@@ -8,7 +8,7 @@ use super::{
88#[ cfg( feature = "jit" ) ]
99use crate :: common:: lock:: OnceCell ;
1010use crate :: common:: lock:: PyMutex ;
11- use crate :: convert:: ToPyObject ;
11+ use crate :: convert:: { ToPyObject , TryFromObject } ;
1212use crate :: function:: ArgMapping ;
1313use crate :: object:: { Traverse , TraverseFn } ;
1414use crate :: {
@@ -31,6 +31,7 @@ use rustpython_jit::CompiledCode;
3131pub 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
5455impl 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) ]
368388impl PyFunction {
@@ -409,12 +429,7 @@ impl PyFunction {
409429 #[ pymember( magic) ]
410430 fn builtins ( vm : & VirtualMachine , zelf : PyObjectRef ) -> PyResult {
411431 let zelf = Self :: _as_pyref ( & zelf, vm) ?;
412- // Get __builtins__ from the function's globals dict
413- let builtins = zelf
414- . globals
415- . get_item ( "__builtins__" , vm)
416- . unwrap_or_else ( |_| vm. builtins . clone ( ) . into ( ) ) ;
417- Ok ( builtins)
432+ Ok ( zelf. builtins . clone ( ) )
418433 }
419434
420435 #[ pygetset( magic) ]
@@ -561,6 +576,81 @@ impl Representable for PyFunction {
561576 }
562577}
563578
579+ #[ derive( FromArgs ) ]
580+ pub struct PyFunctionNewArgs {
581+ #[ pyarg( positional) ]
582+ code : PyRef < PyCode > ,
583+ #[ pyarg( positional) ]
584+ globals : PyDictRef ,
585+ #[ pyarg( any, optional) ]
586+ name : OptionalArg < PyStrRef > ,
587+ #[ pyarg( any, optional) ]
588+ defaults : OptionalArg < PyTupleRef > ,
589+ #[ pyarg( any, optional) ]
590+ closure : OptionalArg < PyTupleRef > ,
591+ #[ pyarg( any, optional) ]
592+ kwdefaults : OptionalArg < PyDictRef > ,
593+ }
594+
595+ impl Constructor for PyFunction {
596+ type Args = PyFunctionNewArgs ;
597+
598+ fn py_new ( cls : PyTypeRef , args : Self :: Args , vm : & VirtualMachine ) -> PyResult {
599+ // Handle closure - must be a tuple of cells
600+ let closure = if let Some ( closure_tuple) = args. closure . into_option ( ) {
601+ // Check that closure length matches code's free variables
602+ if closure_tuple. len ( ) != args. code . freevars . len ( ) {
603+ return Err ( vm. new_value_error ( format ! (
604+ "{} requires closure of length {}, not {}" ,
605+ args. code. obj_name,
606+ args. code. freevars. len( ) ,
607+ closure_tuple. len( )
608+ ) ) ) ;
609+ }
610+
611+ // Validate that all items are cells and create typed tuple
612+ let typed_closure =
613+ PyTupleTyped :: < PyCellRef > :: try_from_object ( vm, closure_tuple. into ( ) ) ?;
614+ Some ( typed_closure)
615+ } else if !args. code . freevars . is_empty ( ) {
616+ return Err ( vm. new_type_error ( "arg 5 (closure) must be tuple" . to_owned ( ) ) ) ;
617+ } else {
618+ None
619+ } ;
620+
621+ // Get function name - use provided name or default to code object name
622+ let name = args
623+ . name
624+ . into_option ( )
625+ . unwrap_or_else ( || PyStr :: from ( args. code . obj_name . as_str ( ) ) . into_ref ( & vm. ctx ) ) ;
626+
627+ // Get qualname - for now just use the name
628+ let qualname = name. clone ( ) ;
629+
630+ // Create empty type_params and annotations
631+ let type_params = vm. ctx . new_tuple ( vec ! [ ] ) ;
632+ let annotations = vm. ctx . new_dict ( ) ;
633+
634+ // Get doc from code object - for now just use None
635+ let doc = vm. ctx . none ( ) ;
636+
637+ let func = PyFunction :: new (
638+ args. code ,
639+ args. globals ,
640+ closure,
641+ args. defaults . into_option ( ) ,
642+ args. kwdefaults . into_option ( ) ,
643+ qualname,
644+ type_params,
645+ annotations,
646+ doc,
647+ vm,
648+ ) ?;
649+
650+ func. into_ref_with_type ( vm, cls) . map ( Into :: into)
651+ }
652+ }
653+
564654#[ pyclass( module = false , name = "method" , traverse) ]
565655#[ derive( Debug ) ]
566656pub struct PyBoundMethod {
0 commit comments