@@ -8,7 +8,7 @@ use super::{
8
8
#[ cfg( feature = "jit" ) ]
9
9
use crate :: common:: lock:: OnceCell ;
10
10
use crate :: common:: lock:: PyMutex ;
11
- use crate :: convert:: ToPyObject ;
11
+ use crate :: convert:: { ToPyObject , TryFromObject } ;
12
12
use crate :: function:: ArgMapping ;
13
13
use crate :: object:: { Traverse , TraverseFn } ;
14
14
use crate :: {
@@ -31,6 +31,7 @@ use rustpython_jit::CompiledCode;
31
31
pub struct PyFunction {
32
32
code : PyRef < PyCode > ,
33
33
globals : PyDictRef ,
34
+ builtins : PyObjectRef ,
34
35
closure : Option < PyTupleTyped < PyCellRef > > ,
35
36
defaults_and_kwdefaults : PyMutex < ( Option < PyTupleRef > , Option < PyDictRef > ) > ,
36
37
name : PyMutex < PyStrRef > ,
@@ -53,6 +54,7 @@ unsafe impl Traverse for PyFunction {
53
54
54
55
impl PyFunction {
55
56
#[ allow( clippy:: too_many_arguments) ]
57
+ #[ inline]
56
58
pub ( crate ) fn new (
57
59
code : PyRef < PyCode > ,
58
60
globals : PyDictRef ,
@@ -62,24 +64,42 @@ impl PyFunction {
62
64
qualname : PyStrRef ,
63
65
type_params : PyTupleRef ,
64
66
annotations : PyDictRef ,
65
- module : PyObjectRef ,
66
67
doc : PyObjectRef ,
67
- ) -> Self {
68
+ vm : & VirtualMachine ,
69
+ ) -> PyResult < Self > {
68
70
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 {
70
82
code,
71
83
globals,
84
+ builtins,
72
85
closure,
73
86
defaults_and_kwdefaults : PyMutex :: new ( ( defaults, kw_only_defaults) ) ,
74
87
name,
75
88
qualname : PyMutex :: new ( qualname) ,
76
89
type_params : PyMutex :: new ( type_params) ,
77
- #[ cfg( feature = "jit" ) ]
78
- jitted_code : OnceCell :: new ( ) ,
79
90
annotations : PyMutex :: new ( annotations) ,
80
91
module : PyMutex :: new ( module) ,
81
92
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)
83
103
}
84
104
85
105
fn fill_locals_from_args (
@@ -362,7 +382,7 @@ impl PyPayload for PyFunction {
362
382
}
363
383
364
384
#[ pyclass(
365
- with( GetDescriptor , Callable , Representable ) ,
385
+ with( GetDescriptor , Callable , Representable , Constructor ) ,
366
386
flags( HAS_DICT , METHOD_DESCRIPTOR )
367
387
) ]
368
388
impl PyFunction {
@@ -409,12 +429,7 @@ impl PyFunction {
409
429
#[ pymember( magic) ]
410
430
fn builtins ( vm : & VirtualMachine , zelf : PyObjectRef ) -> PyResult {
411
431
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 ( ) )
418
433
}
419
434
420
435
#[ pygetset( magic) ]
@@ -561,6 +576,81 @@ impl Representable for PyFunction {
561
576
}
562
577
}
563
578
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
+
564
654
#[ pyclass( module = false , name = "method" , traverse) ]
565
655
#[ derive( Debug ) ]
566
656
pub struct PyBoundMethod {
0 commit comments