Skip to main content

rustpython_vm/builtins/
singletons.rs

1use super::{PyStrRef, PyType, PyTypeRef};
2use crate::{
3    Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
4    class::PyClassImpl,
5    common::hash::PyHash,
6    convert::ToPyObject,
7    function::{FuncArgs, PyComparisonValue},
8    protocol::PyNumberMethods,
9    types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable},
10};
11
12#[pyclass(module = false, name = "NoneType")]
13#[derive(Debug)]
14pub struct PyNone;
15
16impl PyPayload for PyNone {
17    #[inline]
18    fn class(ctx: &Context) -> &'static Py<PyType> {
19        ctx.types.none_type
20    }
21}
22
23// This allows a built-in function to not return a value, mapping to
24// Python's behavior of returning `None` in this situation.
25impl ToPyObject for () {
26    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
27        vm.ctx.none()
28    }
29}
30
31impl<T: ToPyObject> ToPyObject for Option<T> {
32    fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
33        match self {
34            Some(x) => x.to_pyobject(vm),
35            None => vm.ctx.none(),
36        }
37    }
38}
39
40impl Constructor for PyNone {
41    type Args = ();
42
43    fn slot_new(_cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
44        let _: () = args.bind(vm)?;
45        Ok(vm.ctx.none.clone().into())
46    }
47
48    fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
49        unreachable!("None is a singleton")
50    }
51}
52
53#[pyclass(with(Constructor, AsNumber, Comparable, Hashable, Representable))]
54impl PyNone {}
55
56impl Representable for PyNone {
57    #[inline]
58    fn repr(_zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
59        Ok(vm.ctx.names.None.to_owned())
60    }
61
62    #[cold]
63    fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
64        unreachable!("use repr instead")
65    }
66}
67
68impl AsNumber for PyNone {
69    fn as_number() -> &'static PyNumberMethods {
70        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
71            boolean: Some(|_number, _vm| Ok(false)),
72            ..PyNumberMethods::NOT_IMPLEMENTED
73        };
74        &AS_NUMBER
75    }
76}
77
78impl Comparable for PyNone {
79    fn cmp(
80        zelf: &Py<Self>,
81        other: &crate::PyObject,
82        op: PyComparisonOp,
83        _vm: &VirtualMachine,
84    ) -> PyResult<PyComparisonValue> {
85        Ok(op
86            .identical_optimization(zelf, other)
87            .map(PyComparisonValue::Implemented)
88            .unwrap_or(PyComparisonValue::NotImplemented))
89    }
90}
91
92impl Hashable for PyNone {
93    fn hash(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyHash> {
94        Ok(0xFCA86420)
95    }
96}
97
98#[pyclass(module = false, name = "NotImplementedType")]
99#[derive(Debug)]
100pub struct PyNotImplemented;
101
102impl PyPayload for PyNotImplemented {
103    #[inline]
104    fn class(ctx: &Context) -> &'static Py<PyType> {
105        ctx.types.not_implemented_type
106    }
107}
108
109impl Constructor for PyNotImplemented {
110    type Args = ();
111
112    fn slot_new(_cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
113        let _: () = args.bind(vm)?;
114        Ok(vm.ctx.not_implemented.clone().into())
115    }
116
117    fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
118        unreachable!("PyNotImplemented is a singleton")
119    }
120}
121
122#[pyclass(with(Constructor, AsNumber, Representable), flags(IMMUTABLETYPE))]
123impl PyNotImplemented {
124    #[pymethod]
125    fn __reduce__(&self, vm: &VirtualMachine) -> PyStrRef {
126        vm.ctx.names.NotImplemented.to_owned()
127    }
128}
129
130impl AsNumber for PyNotImplemented {
131    fn as_number() -> &'static PyNumberMethods {
132        static AS_NUMBER: PyNumberMethods = PyNumberMethods {
133            boolean: Some(|_number, vm| {
134                Err(vm.new_type_error("NotImplemented should not be used in a boolean context"))
135            }),
136            ..PyNumberMethods::NOT_IMPLEMENTED
137        };
138        &AS_NUMBER
139    }
140}
141
142impl Representable for PyNotImplemented {
143    #[inline]
144    fn repr(_zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
145        Ok(vm.ctx.names.NotImplemented.to_owned())
146    }
147
148    #[cold]
149    fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
150        unreachable!("use repr instead")
151    }
152}
153
154pub fn init(context: &'static Context) {
155    PyNone::extend_class(context, context.types.none_type);
156    PyNotImplemented::extend_class(context, context.types.not_implemented_type);
157}