Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3566dca

Browse files
authored
Pyfunction builtins and constructor (#5823)
* func builtins * PyFunction constructor
1 parent 0624ca6 commit 3566dca

File tree

3 files changed

+111
-19
lines changed

3 files changed

+111
-19
lines changed

Lib/test/test_collections.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,6 @@ class NewPoint(tuple):
698698
self.assertEqual(np.x, 1)
699699
self.assertEqual(np.y, 2)
700700

701-
# TODO: RUSTPYTHON
702-
@unittest.expectedFailure
703701
def test_new_builtins_issue_43102(self):
704702
obj = namedtuple('C', ())
705703
new_func = obj.__new__

vm/src/builtins/function.rs

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use super::{
88
#[cfg(feature = "jit")]
99
use crate::common::lock::OnceCell;
1010
use crate::common::lock::PyMutex;
11-
use crate::convert::ToPyObject;
11+
use crate::convert::{ToPyObject, TryFromObject};
1212
use crate::function::ArgMapping;
1313
use crate::object::{Traverse, TraverseFn};
1414
use crate::{
@@ -31,6 +31,7 @@ use rustpython_jit::CompiledCode;
3131
pub struct PyFunction {
3232
code: PyRef<PyCode>,
3333
globals: PyDictRef,
34+
builtins: PyObjectRef,
3435
closure: Option<PyTupleTyped<PyCellRef>>,
3536
defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>,
3637
name: PyMutex<PyStrRef>,
@@ -53,6 +54,7 @@ unsafe impl Traverse for PyFunction {
5354

5455
impl PyFunction {
5556
#[allow(clippy::too_many_arguments)]
57+
#[inline]
5658
pub(crate) fn new(
5759
code: PyRef<PyCode>,
5860
globals: PyDictRef,
@@ -62,24 +64,42 @@ impl PyFunction {
6264
qualname: PyStrRef,
6365
type_params: PyTupleRef,
6466
annotations: PyDictRef,
65-
module: PyObjectRef,
6667
doc: PyObjectRef,
67-
) -> Self {
68+
vm: &VirtualMachine,
69+
) -> PyResult<Self> {
6870
let name = PyMutex::new(code.obj_name.to_owned());
69-
PyFunction {
71+
let module = vm.unwrap_or_none(globals.get_item_opt(identifier!(vm, __name__), vm)?);
72+
let builtins = globals.get_item("__builtins__", vm).unwrap_or_else(|_| {
73+
// If not in globals, inherit from current execution context
74+
if let Some(frame) = vm.current_frame() {
75+
frame.builtins.clone().into()
76+
} else {
77+
vm.builtins.clone().into()
78+
}
79+
});
80+
81+
let func = PyFunction {
7082
code,
7183
globals,
84+
builtins,
7285
closure,
7386
defaults_and_kwdefaults: PyMutex::new((defaults, kw_only_defaults)),
7487
name,
7588
qualname: PyMutex::new(qualname),
7689
type_params: PyMutex::new(type_params),
77-
#[cfg(feature = "jit")]
78-
jitted_code: OnceCell::new(),
7990
annotations: PyMutex::new(annotations),
8091
module: PyMutex::new(module),
8192
doc: PyMutex::new(doc),
82-
}
93+
#[cfg(feature = "jit")]
94+
jitted_code: OnceCell::new(),
95+
};
96+
97+
// let name = qualname.as_str().split('.').next_back().unwrap();
98+
// func.set_attr(identifier!(vm, __name__), vm.new_pyobj(name), vm)?;
99+
// func.set_attr(identifier!(vm, __qualname__), qualname, vm)?;
100+
// func.set_attr(identifier!(vm, __doc__), doc, vm)?;
101+
102+
Ok(func)
83103
}
84104

85105
fn fill_locals_from_args(
@@ -362,7 +382,7 @@ impl PyPayload for PyFunction {
362382
}
363383

364384
#[pyclass(
365-
with(GetDescriptor, Callable, Representable),
385+
with(GetDescriptor, Callable, Representable, Constructor),
366386
flags(HAS_DICT, METHOD_DESCRIPTOR)
367387
)]
368388
impl PyFunction {
@@ -406,6 +426,12 @@ impl PyFunction {
406426
Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.to_pyobject(vm))))
407427
}
408428

429+
#[pymember(magic)]
430+
fn builtins(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
431+
let zelf = Self::_as_pyref(&zelf, vm)?;
432+
Ok(zelf.builtins.clone())
433+
}
434+
409435
#[pygetset(magic)]
410436
fn name(&self) -> PyStrRef {
411437
self.name.lock().clone()
@@ -555,6 +581,81 @@ impl Representable for PyFunction {
555581
}
556582
}
557583

584+
#[derive(FromArgs)]
585+
pub struct PyFunctionNewArgs {
586+
#[pyarg(positional)]
587+
code: PyRef<PyCode>,
588+
#[pyarg(positional)]
589+
globals: PyDictRef,
590+
#[pyarg(any, optional)]
591+
name: OptionalArg<PyStrRef>,
592+
#[pyarg(any, optional)]
593+
defaults: OptionalArg<PyTupleRef>,
594+
#[pyarg(any, optional)]
595+
closure: OptionalArg<PyTupleRef>,
596+
#[pyarg(any, optional)]
597+
kwdefaults: OptionalArg<PyDictRef>,
598+
}
599+
600+
impl Constructor for PyFunction {
601+
type Args = PyFunctionNewArgs;
602+
603+
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
604+
// Handle closure - must be a tuple of cells
605+
let closure = if let Some(closure_tuple) = args.closure.into_option() {
606+
// Check that closure length matches code's free variables
607+
if closure_tuple.len() != args.code.freevars.len() {
608+
return Err(vm.new_value_error(format!(
609+
"{} requires closure of length {}, not {}",
610+
args.code.obj_name,
611+
args.code.freevars.len(),
612+
closure_tuple.len()
613+
)));
614+
}
615+
616+
// Validate that all items are cells and create typed tuple
617+
let typed_closure =
618+
PyTupleTyped::<PyCellRef>::try_from_object(vm, closure_tuple.into())?;
619+
Some(typed_closure)
620+
} else if !args.code.freevars.is_empty() {
621+
return Err(vm.new_type_error("arg 5 (closure) must be tuple".to_owned()));
622+
} else {
623+
None
624+
};
625+
626+
// Get function name - use provided name or default to code object name
627+
let name = args
628+
.name
629+
.into_option()
630+
.unwrap_or_else(|| PyStr::from(args.code.obj_name.as_str()).into_ref(&vm.ctx));
631+
632+
// Get qualname - for now just use the name
633+
let qualname = name.clone();
634+
635+
// Create empty type_params and annotations
636+
let type_params = vm.ctx.new_tuple(vec![]);
637+
let annotations = vm.ctx.new_dict();
638+
639+
// Get doc from code object - for now just use None
640+
let doc = vm.ctx.none();
641+
642+
let func = PyFunction::new(
643+
args.code,
644+
args.globals,
645+
closure,
646+
args.defaults.into_option(),
647+
args.kwdefaults.into_option(),
648+
qualname,
649+
type_params,
650+
annotations,
651+
doc,
652+
vm,
653+
)?;
654+
655+
func.into_ref_with_type(vm, cls).map(Into::into)
656+
}
657+
}
658+
558659
#[pyclass(module = false, name = "method", traverse)]
559660
#[derive(Debug)]
560661
pub struct PyBoundMethod {

vm/src/frame.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,8 +1905,6 @@ impl ExecutingFrame<'_> {
19051905
None
19061906
};
19071907

1908-
let module = vm.unwrap_or_none(self.globals.get_item_opt(identifier!(vm, __name__), vm)?);
1909-
19101908
// pop argc arguments
19111909
// argument: name, args, globals
19121910
// let scope = self.scope.clone();
@@ -1919,16 +1917,11 @@ impl ExecutingFrame<'_> {
19191917
qualified_name.clone(),
19201918
type_params,
19211919
annotations.downcast().unwrap(),
1922-
module,
19231920
vm.ctx.none(),
1924-
)
1921+
vm,
1922+
)?
19251923
.into_pyobject(vm);
19261924

1927-
let name = qualified_name.as_str().split('.').next_back().unwrap();
1928-
func_obj.set_attr(identifier!(vm, __name__), vm.new_pyobj(name), vm)?;
1929-
func_obj.set_attr(identifier!(vm, __qualname__), qualified_name, vm)?;
1930-
func_obj.set_attr(identifier!(vm, __doc__), vm.ctx.none(), vm)?;
1931-
19321925
self.push_value(func_obj);
19331926
Ok(None)
19341927
}

0 commit comments

Comments
 (0)