rustpython_vm/builtins/
function.rs

1#[cfg(feature = "jit")]
2mod jitfunc;
3
4use super::{
5    tuple::PyTupleTyped, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStr, PyStrRef,
6    PyTupleRef, PyType, PyTypeRef,
7};
8#[cfg(feature = "jit")]
9use crate::common::lock::OnceCell;
10use crate::common::lock::PyMutex;
11use crate::convert::ToPyObject;
12use crate::function::ArgMapping;
13use crate::object::{Traverse, TraverseFn};
14use crate::{
15    bytecode,
16    class::PyClassImpl,
17    frame::Frame,
18    function::{FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
19    scope::Scope,
20    types::{
21        Callable, Comparable, Constructor, GetAttr, GetDescriptor, PyComparisonOp, Representable,
22    },
23    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
24};
25use itertools::Itertools;
26#[cfg(feature = "jit")]
27use rustpython_jit::CompiledCode;
28
29#[pyclass(module = false, name = "function", traverse = "manual")]
30#[derive(Debug)]
31pub struct PyFunction {
32    code: PyRef<PyCode>,
33    globals: PyDictRef,
34    closure: Option<PyTupleTyped<PyCellRef>>,
35    defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>,
36    name: PyMutex<PyStrRef>,
37    qualname: PyMutex<PyStrRef>,
38    type_params: PyMutex<PyTupleRef>,
39    #[cfg(feature = "jit")]
40    jitted_code: OnceCell<CompiledCode>,
41}
42
43unsafe impl Traverse for PyFunction {
44    fn traverse(&self, tracer_fn: &mut TraverseFn) {
45        self.globals.traverse(tracer_fn);
46        self.closure.traverse(tracer_fn);
47        self.defaults_and_kwdefaults.traverse(tracer_fn);
48    }
49}
50
51impl PyFunction {
52    pub(crate) fn new(
53        code: PyRef<PyCode>,
54        globals: PyDictRef,
55        closure: Option<PyTupleTyped<PyCellRef>>,
56        defaults: Option<PyTupleRef>,
57        kw_only_defaults: Option<PyDictRef>,
58        qualname: PyStrRef,
59        type_params: PyTupleRef,
60    ) -> Self {
61        let name = PyMutex::new(code.obj_name.to_owned());
62        PyFunction {
63            code,
64            globals,
65            closure,
66            defaults_and_kwdefaults: PyMutex::new((defaults, kw_only_defaults)),
67            name,
68            qualname: PyMutex::new(qualname),
69            type_params: PyMutex::new(type_params),
70            #[cfg(feature = "jit")]
71            jitted_code: OnceCell::new(),
72        }
73    }
74
75    fn fill_locals_from_args(
76        &self,
77        frame: &Frame,
78        func_args: FuncArgs,
79        vm: &VirtualMachine,
80    ) -> PyResult<()> {
81        let code = &*self.code;
82        let nargs = func_args.args.len();
83        let nexpected_args = code.arg_count as usize;
84        let total_args = code.arg_count as usize + code.kwonlyarg_count as usize;
85        // let arg_names = self.code.arg_names();
86
87        // This parses the arguments from args and kwargs into
88        // the proper variables keeping into account default values
89        // and starargs and kwargs.
90        // See also: PyEval_EvalCodeWithName in cpython:
91        // https://github.com/python/cpython/blob/main/Python/ceval.c#L3681
92
93        let mut fastlocals = frame.fastlocals.lock();
94
95        let mut args_iter = func_args.args.into_iter();
96
97        // Copy positional arguments into local variables
98        // zip short-circuits if either iterator returns None, which is the behavior we want --
99        // only fill as much as there is to fill with as much as we have
100        for (local, arg) in Iterator::zip(
101            fastlocals.iter_mut().take(nexpected_args),
102            args_iter.by_ref().take(nargs),
103        ) {
104            *local = Some(arg);
105        }
106
107        let mut vararg_offset = total_args;
108        // Pack other positional arguments in to *args:
109        if code.flags.contains(bytecode::CodeFlags::HAS_VARARGS) {
110            let vararg_value = vm.ctx.new_tuple(args_iter.collect());
111            fastlocals[vararg_offset] = Some(vararg_value.into());
112            vararg_offset += 1;
113        } else {
114            // Check the number of positional arguments
115            if nargs > nexpected_args {
116                return Err(vm.new_type_error(format!(
117                    "{}() takes {} positional arguments but {} were given",
118                    self.qualname(),
119                    nexpected_args,
120                    nargs
121                )));
122            }
123        }
124
125        // Do we support `**kwargs` ?
126        let kwargs = if code.flags.contains(bytecode::CodeFlags::HAS_VARKEYWORDS) {
127            let d = vm.ctx.new_dict();
128            fastlocals[vararg_offset] = Some(d.clone().into());
129            Some(d)
130        } else {
131            None
132        };
133
134        let argpos = |range: std::ops::Range<_>, name: &str| {
135            code.varnames
136                .iter()
137                .enumerate()
138                .skip(range.start)
139                .take(range.end - range.start)
140                .find(|(_, s)| s.as_str() == name)
141                .map(|(p, _)| p)
142        };
143
144        let mut posonly_passed_as_kwarg = Vec::new();
145        // Handle keyword arguments
146        for (name, value) in func_args.kwargs {
147            // Check if we have a parameter with this name:
148            if let Some(pos) = argpos(code.posonlyarg_count as usize..total_args, &name) {
149                let slot = &mut fastlocals[pos];
150                if slot.is_some() {
151                    return Err(vm.new_type_error(format!(
152                        "{}() got multiple values for argument '{}'",
153                        self.qualname(),
154                        name
155                    )));
156                }
157                *slot = Some(value);
158            } else if let Some(kwargs) = kwargs.as_ref() {
159                kwargs.set_item(&name, value, vm)?;
160            } else if argpos(0..code.posonlyarg_count as usize, &name).is_some() {
161                posonly_passed_as_kwarg.push(name);
162            } else {
163                return Err(vm.new_type_error(format!(
164                    "{}() got an unexpected keyword argument '{}'",
165                    self.qualname(),
166                    name
167                )));
168            }
169        }
170        if !posonly_passed_as_kwarg.is_empty() {
171            return Err(vm.new_type_error(format!(
172                "{}() got some positional-only arguments passed as keyword arguments: '{}'",
173                self.qualname(),
174                posonly_passed_as_kwarg.into_iter().format(", "),
175            )));
176        }
177
178        let mut defaults_and_kwdefaults = None;
179        // can't be a closure cause it returns a reference to a captured variable :/
180        macro_rules! get_defaults {
181            () => {{
182                defaults_and_kwdefaults
183                    .get_or_insert_with(|| self.defaults_and_kwdefaults.lock().clone())
184            }};
185        }
186
187        // Add missing positional arguments, if we have fewer positional arguments than the
188        // function definition calls for
189        if nargs < nexpected_args {
190            let defaults = get_defaults!().0.as_ref().map(|tup| tup.as_slice());
191            let ndefs = defaults.map_or(0, |d| d.len());
192
193            let nrequired = code.arg_count as usize - ndefs;
194
195            // Given the number of defaults available, check all the arguments for which we
196            // _don't_ have defaults; if any are missing, raise an exception
197            let mut missing: Vec<_> = (nargs..nrequired)
198                .filter_map(|i| {
199                    if fastlocals[i].is_none() {
200                        Some(&code.varnames[i])
201                    } else {
202                        None
203                    }
204                })
205                .collect();
206            let missing_args_len = missing.len();
207
208            if !missing.is_empty() {
209                let last = if missing.len() > 1 {
210                    missing.pop()
211                } else {
212                    None
213                };
214
215                let (and, right) = if let Some(last) = last {
216                    (
217                        if missing.len() == 1 {
218                            "' and '"
219                        } else {
220                            "', and '"
221                        },
222                        last.as_str(),
223                    )
224                } else {
225                    ("", "")
226                };
227
228                return Err(vm.new_type_error(format!(
229                    "{}() missing {} required positional argument{}: '{}{}{}'",
230                    self.qualname(),
231                    missing_args_len,
232                    if missing_args_len == 1 { "" } else { "s" },
233                    missing.iter().join("', '"),
234                    and,
235                    right,
236                )));
237            }
238
239            if let Some(defaults) = defaults {
240                let n = std::cmp::min(nargs, nexpected_args);
241                let i = n.saturating_sub(nrequired);
242
243                // We have sufficient defaults, so iterate over the corresponding names and use
244                // the default if we don't already have a value
245                for i in i..defaults.len() {
246                    let slot = &mut fastlocals[nrequired + i];
247                    if slot.is_none() {
248                        *slot = Some(defaults[i].clone());
249                    }
250                }
251            }
252        };
253
254        if code.kwonlyarg_count > 0 {
255            // TODO: compile a list of missing arguments
256            // let mut missing = vec![];
257            // Check if kw only arguments are all present:
258            for (slot, kwarg) in fastlocals
259                .iter_mut()
260                .zip(&*code.varnames)
261                .skip(code.arg_count as usize)
262                .take(code.kwonlyarg_count as usize)
263                .filter(|(slot, _)| slot.is_none())
264            {
265                if let Some(defaults) = &get_defaults!().1 {
266                    if let Some(default) = defaults.get_item_opt(&**kwarg, vm)? {
267                        *slot = Some(default);
268                        continue;
269                    }
270                }
271
272                // No default value and not specified.
273                return Err(
274                    vm.new_type_error(format!("Missing required kw only argument: '{kwarg}'"))
275                );
276            }
277        }
278
279        if let Some(cell2arg) = code.cell2arg.as_deref() {
280            for (cell_idx, arg_idx) in cell2arg.iter().enumerate().filter(|(_, i)| **i != -1) {
281                let x = fastlocals[*arg_idx as usize].take();
282                frame.cells_frees[cell_idx].set(x);
283            }
284        }
285
286        Ok(())
287    }
288
289    pub fn invoke_with_locals(
290        &self,
291        func_args: FuncArgs,
292        locals: Option<ArgMapping>,
293        vm: &VirtualMachine,
294    ) -> PyResult {
295        #[cfg(feature = "jit")]
296        if let Some(jitted_code) = self.jitted_code.get() {
297            match jitfunc::get_jit_args(self, &func_args, jitted_code, vm) {
298                Ok(args) => {
299                    return Ok(args.invoke().to_pyobject(vm));
300                }
301                Err(err) => info!(
302                    "jit: function `{}` is falling back to being interpreted because of the \
303                    error: {}",
304                    self.code.obj_name, err
305                ),
306            }
307        }
308
309        let code = &self.code;
310
311        let locals = if self.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) {
312            ArgMapping::from_dict_exact(vm.ctx.new_dict())
313        } else if let Some(locals) = locals {
314            locals
315        } else {
316            ArgMapping::from_dict_exact(self.globals.clone())
317        };
318
319        // Construct frame:
320        let frame = Frame::new(
321            code.clone(),
322            Scope::new(Some(locals), self.globals.clone()),
323            vm.builtins.dict(),
324            self.closure.as_ref().map_or(&[], |c| c.as_slice()),
325            vm,
326        )
327        .into_ref(&vm.ctx);
328
329        self.fill_locals_from_args(&frame, func_args, vm)?;
330
331        // If we have a generator, create a new generator
332        let is_gen = code.flags.contains(bytecode::CodeFlags::IS_GENERATOR);
333        let is_coro = code.flags.contains(bytecode::CodeFlags::IS_COROUTINE);
334        match (is_gen, is_coro) {
335            (true, false) => Ok(PyGenerator::new(frame, self.name()).into_pyobject(vm)),
336            (false, true) => Ok(PyCoroutine::new(frame, self.name()).into_pyobject(vm)),
337            (true, true) => Ok(PyAsyncGen::new(frame, self.name()).into_pyobject(vm)),
338            (false, false) => vm.run_frame(frame),
339        }
340    }
341
342    #[inline(always)]
343    pub fn invoke(&self, func_args: FuncArgs, vm: &VirtualMachine) -> PyResult {
344        self.invoke_with_locals(func_args, None, vm)
345    }
346}
347
348impl PyPayload for PyFunction {
349    fn class(ctx: &Context) -> &'static Py<PyType> {
350        ctx.types.function_type
351    }
352}
353
354#[pyclass(
355    with(GetDescriptor, Callable, Representable),
356    flags(HAS_DICT, METHOD_DESCRIPTOR)
357)]
358impl PyFunction {
359    #[pygetset(magic)]
360    fn code(&self) -> PyRef<PyCode> {
361        self.code.clone()
362    }
363
364    #[pygetset(magic)]
365    fn defaults(&self) -> Option<PyTupleRef> {
366        self.defaults_and_kwdefaults.lock().0.clone()
367    }
368    #[pygetset(magic, setter)]
369    fn set_defaults(&self, defaults: Option<PyTupleRef>) {
370        self.defaults_and_kwdefaults.lock().0 = defaults
371    }
372
373    #[pygetset(magic)]
374    fn kwdefaults(&self) -> Option<PyDictRef> {
375        self.defaults_and_kwdefaults.lock().1.clone()
376    }
377    #[pygetset(magic, setter)]
378    fn set_kwdefaults(&self, kwdefaults: Option<PyDictRef>) {
379        self.defaults_and_kwdefaults.lock().1 = kwdefaults
380    }
381
382    // {"__closure__",   T_OBJECT,     OFF(func_closure), READONLY},
383    // {"__doc__",       T_OBJECT,     OFF(func_doc), 0},
384    // {"__globals__",   T_OBJECT,     OFF(func_globals), READONLY},
385    // {"__module__",    T_OBJECT,     OFF(func_module), 0},
386    // {"__builtins__",  T_OBJECT,     OFF(func_builtins), READONLY},
387    #[pymember(magic)]
388    fn globals(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
389        let zelf = Self::_as_pyref(&zelf, vm)?;
390        Ok(zelf.globals.clone().into())
391    }
392
393    #[pymember(magic)]
394    fn closure(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
395        let zelf = Self::_as_pyref(&zelf, vm)?;
396        Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.to_pyobject(vm))))
397    }
398
399    #[pygetset(magic)]
400    fn name(&self) -> PyStrRef {
401        self.name.lock().clone()
402    }
403
404    #[pygetset(magic, setter)]
405    fn set_name(&self, name: PyStrRef) {
406        *self.name.lock() = name;
407    }
408
409    #[pygetset(magic)]
410    fn qualname(&self) -> PyStrRef {
411        self.qualname.lock().clone()
412    }
413
414    #[pygetset(magic, setter)]
415    fn set_qualname(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
416        match value {
417            PySetterValue::Assign(value) => {
418                let Ok(qualname) = value.downcast::<PyStr>() else {
419                    return Err(vm.new_type_error(
420                        "__qualname__ must be set to a string object".to_string(),
421                    ));
422                };
423                *self.qualname.lock() = qualname;
424            }
425            PySetterValue::Delete => {
426                return Err(
427                    vm.new_type_error("__qualname__ must be set to a string object".to_string())
428                );
429            }
430        }
431        Ok(())
432    }
433
434    #[pygetset(magic)]
435    fn type_params(&self) -> PyTupleRef {
436        self.type_params.lock().clone()
437    }
438
439    #[pygetset(magic, setter)]
440    fn set_type_params(
441        &self,
442        value: PySetterValue<PyTupleRef>,
443        vm: &VirtualMachine,
444    ) -> PyResult<()> {
445        match value {
446            PySetterValue::Assign(value) => {
447                *self.type_params.lock() = value;
448            }
449            PySetterValue::Delete => {
450                return Err(
451                    vm.new_type_error("__type_params__ must be set to a tuple object".to_string())
452                );
453            }
454        }
455        Ok(())
456    }
457
458    #[cfg(feature = "jit")]
459    #[pymethod(magic)]
460    fn jit(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<()> {
461        zelf.jitted_code
462            .get_or_try_init(|| {
463                let arg_types = jitfunc::get_jit_arg_types(&zelf, vm)?;
464                rustpython_jit::compile(&zelf.code.code, &arg_types)
465                    .map_err(|err| jitfunc::new_jit_error(err.to_string(), vm))
466            })
467            .map(drop)
468    }
469}
470
471impl GetDescriptor for PyFunction {
472    fn descr_get(
473        zelf: PyObjectRef,
474        obj: Option<PyObjectRef>,
475        cls: Option<PyObjectRef>,
476        vm: &VirtualMachine,
477    ) -> PyResult {
478        let (_zelf, obj) = Self::_unwrap(&zelf, obj, vm)?;
479        let obj = if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) {
480            zelf
481        } else {
482            PyBoundMethod::new_ref(obj, zelf, &vm.ctx).into()
483        };
484        Ok(obj)
485    }
486}
487
488impl Callable for PyFunction {
489    type Args = FuncArgs;
490    #[inline]
491    fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
492        zelf.invoke(args, vm)
493    }
494}
495
496impl Representable for PyFunction {
497    #[inline]
498    fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
499        Ok(format!(
500            "<function {} at {:#x}>",
501            zelf.qualname(),
502            zelf.get_id()
503        ))
504    }
505}
506
507#[pyclass(module = false, name = "method", traverse)]
508#[derive(Debug)]
509pub struct PyBoundMethod {
510    object: PyObjectRef,
511    function: PyObjectRef,
512}
513
514impl Callable for PyBoundMethod {
515    type Args = FuncArgs;
516    #[inline]
517    fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
518        args.prepend_arg(zelf.object.clone());
519        zelf.function.call(args, vm)
520    }
521}
522
523impl Comparable for PyBoundMethod {
524    fn cmp(
525        zelf: &Py<Self>,
526        other: &PyObject,
527        op: PyComparisonOp,
528        _vm: &VirtualMachine,
529    ) -> PyResult<PyComparisonValue> {
530        op.eq_only(|| {
531            let other = class_or_notimplemented!(Self, other);
532            Ok(PyComparisonValue::Implemented(
533                zelf.function.is(&other.function) && zelf.object.is(&other.object),
534            ))
535        })
536    }
537}
538
539impl GetAttr for PyBoundMethod {
540    fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
541        let class_attr = vm
542            .ctx
543            .interned_str(name)
544            .and_then(|attr_name| zelf.get_class_attr(attr_name));
545        if let Some(obj) = class_attr {
546            return vm.call_if_get_descriptor(&obj, zelf.to_owned().into());
547        }
548        zelf.function.get_attr(name, vm)
549    }
550}
551
552#[derive(FromArgs)]
553pub struct PyBoundMethodNewArgs {
554    #[pyarg(positional)]
555    function: PyObjectRef,
556    #[pyarg(positional)]
557    object: PyObjectRef,
558}
559
560impl Constructor for PyBoundMethod {
561    type Args = PyBoundMethodNewArgs;
562
563    fn py_new(
564        cls: PyTypeRef,
565        Self::Args { function, object }: Self::Args,
566        vm: &VirtualMachine,
567    ) -> PyResult {
568        PyBoundMethod::new(object, function)
569            .into_ref_with_type(vm, cls)
570            .map(Into::into)
571    }
572}
573
574impl PyBoundMethod {
575    fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
576        PyBoundMethod { object, function }
577    }
578
579    pub fn new_ref(object: PyObjectRef, function: PyObjectRef, ctx: &Context) -> PyRef<Self> {
580        PyRef::new_ref(
581            Self::new(object, function),
582            ctx.types.bound_method_type.to_owned(),
583            None,
584        )
585    }
586}
587
588#[pyclass(
589    with(Callable, Comparable, GetAttr, Constructor, Representable),
590    flags(HAS_DICT)
591)]
592impl PyBoundMethod {
593    #[pymethod(magic)]
594    fn reduce(
595        &self,
596        vm: &VirtualMachine,
597    ) -> (Option<PyObjectRef>, (PyObjectRef, Option<PyObjectRef>)) {
598        let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok();
599        let funcself = self.object.clone();
600        let funcname = self.function.get_attr("__name__", vm).ok();
601        (builtins_getattr, (funcself, funcname))
602    }
603
604    #[pygetset(magic)]
605    fn doc(&self, vm: &VirtualMachine) -> PyResult {
606        self.function.get_attr("__doc__", vm)
607    }
608
609    #[pygetset(magic)]
610    fn func(&self) -> PyObjectRef {
611        self.function.clone()
612    }
613
614    #[pygetset(name = "__self__")]
615    fn get_self(&self) -> PyObjectRef {
616        self.object.clone()
617    }
618
619    #[pygetset(magic)]
620    fn module(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
621        self.function.get_attr("__module__", vm).ok()
622    }
623
624    #[pygetset(magic)]
625    fn qualname(&self, vm: &VirtualMachine) -> PyResult {
626        if self
627            .function
628            .fast_isinstance(vm.ctx.types.builtin_function_or_method_type)
629        {
630            // Special case: we work with `__new__`, which is not really a method.
631            // It is a function, so its `__qualname__` is just `__new__`.
632            // We need to add object's part manually.
633            let obj_name = vm.get_attribute_opt(self.object.clone(), "__qualname__")?;
634            let obj_name: Option<PyStrRef> = obj_name.and_then(|o| o.downcast().ok());
635            return Ok(vm
636                .ctx
637                .new_str(format!(
638                    "{}.__new__",
639                    obj_name.as_ref().map_or("?", |s| s.as_str())
640                ))
641                .into());
642        }
643        self.function.get_attr("__qualname__", vm)
644    }
645}
646
647impl PyPayload for PyBoundMethod {
648    fn class(ctx: &Context) -> &'static Py<PyType> {
649        ctx.types.bound_method_type
650    }
651}
652
653impl Representable for PyBoundMethod {
654    #[inline]
655    fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
656        #[allow(clippy::needless_match)] // False positive on nightly
657        let funcname =
658            if let Some(qname) = vm.get_attribute_opt(zelf.function.clone(), "__qualname__")? {
659                Some(qname)
660            } else {
661                vm.get_attribute_opt(zelf.function.clone(), "__name__")?
662            };
663        let funcname: Option<PyStrRef> = funcname.and_then(|o| o.downcast().ok());
664        Ok(format!(
665            "<bound method {} of {}>",
666            funcname.as_ref().map_or("?", |s| s.as_str()),
667            &zelf.object.repr(vm)?.as_str(),
668        ))
669    }
670}
671
672#[pyclass(module = false, name = "cell", traverse)]
673#[derive(Debug, Default)]
674pub(crate) struct PyCell {
675    contents: PyMutex<Option<PyObjectRef>>,
676}
677pub(crate) type PyCellRef = PyRef<PyCell>;
678
679impl PyPayload for PyCell {
680    fn class(ctx: &Context) -> &'static Py<PyType> {
681        ctx.types.cell_type
682    }
683}
684
685impl Constructor for PyCell {
686    type Args = OptionalArg;
687
688    fn py_new(cls: PyTypeRef, value: Self::Args, vm: &VirtualMachine) -> PyResult {
689        Self::new(value.into_option())
690            .into_ref_with_type(vm, cls)
691            .map(Into::into)
692    }
693}
694
695#[pyclass(with(Constructor))]
696impl PyCell {
697    pub fn new(contents: Option<PyObjectRef>) -> Self {
698        Self {
699            contents: PyMutex::new(contents),
700        }
701    }
702
703    pub fn get(&self) -> Option<PyObjectRef> {
704        self.contents.lock().clone()
705    }
706    pub fn set(&self, x: Option<PyObjectRef>) {
707        *self.contents.lock() = x;
708    }
709
710    #[pygetset]
711    fn cell_contents(&self, vm: &VirtualMachine) -> PyResult {
712        self.get()
713            .ok_or_else(|| vm.new_value_error("Cell is empty".to_owned()))
714    }
715    #[pygetset(setter)]
716    fn set_cell_contents(&self, x: PyObjectRef) {
717        self.set(Some(x))
718    }
719}
720
721pub fn init(context: &Context) {
722    PyFunction::extend_class(context, context.types.function_type);
723    PyBoundMethod::extend_class(context, context.types.bound_method_type);
724    PyCell::extend_class(context, context.types.cell_type);
725}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.