1
1
//! Contains initialization utilities for `#[pyclass]`.
2
2
use crate :: ffi_ptr_ext:: FfiPtrExt ;
3
- use crate :: internal:: get_slot:: TP_ALLOC ;
4
- use crate :: types:: PyType ;
5
- use crate :: { ffi, Borrowed , PyErr , PyResult , Python } ;
3
+ use crate :: internal:: get_slot:: TP_NEW ;
4
+ use crate :: types:: { PyDict , PyTuple , PyType } ;
5
+ use crate :: { ffi, Borrowed , Bound , PyErr , PyResult , Python } ;
6
6
use crate :: { ffi:: PyTypeObject , sealed:: Sealed , type_object:: PyTypeInfo } ;
7
7
use std:: marker:: PhantomData ;
8
8
9
+ use super :: pyclass:: PyClassBaseType ;
10
+
9
11
/// Initializer for Python types.
10
12
///
11
13
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
@@ -17,69 +19,70 @@ pub trait PyObjectInit<T>: Sized + Sealed {
17
19
self ,
18
20
py : Python < ' _ > ,
19
21
subtype : * mut PyTypeObject ,
22
+ args : & Bound < ' _ , PyTuple > ,
23
+ kwargs : Option < & Bound < ' _ , PyDict > > ,
20
24
) -> PyResult < * mut ffi:: PyObject > ;
21
25
22
26
#[ doc( hidden) ]
23
27
fn can_be_subclassed ( & self ) -> bool ;
24
28
}
25
29
26
- /// Initializer for Python native types, like ` PyDict` .
27
- pub struct PyNativeTypeInitializer < T : PyTypeInfo > ( pub PhantomData < T > ) ;
30
+ /// Initializer for Python native types, like [ PyDict] .
31
+ pub struct PyNativeTypeInitializer < T : PyTypeInfo + PyClassBaseType > ( pub PhantomData < T > ) ;
28
32
29
- impl < T : PyTypeInfo > PyObjectInit < T > for PyNativeTypeInitializer < T > {
33
+ impl < T : PyTypeInfo + PyClassBaseType > PyObjectInit < T > for PyNativeTypeInitializer < T > {
34
+ /// call `__new__` ([ffi::PyTypeObject::tp_new]) for the native base type.
35
+ /// This will allocate a new python object and initialize the part relating to the native base type.
30
36
unsafe fn into_new_object (
31
37
self ,
32
38
py : Python < ' _ > ,
33
39
subtype : * mut PyTypeObject ,
40
+ args : & Bound < ' _ , PyTuple > ,
41
+ kwargs : Option < & Bound < ' _ , PyDict > > ,
34
42
) -> PyResult < * mut ffi:: PyObject > {
35
43
unsafe fn inner (
36
44
py : Python < ' _ > ,
37
- type_object : * mut PyTypeObject ,
45
+ native_base_type : * mut PyTypeObject ,
38
46
subtype : * mut PyTypeObject ,
47
+ args : & Bound < ' _ , PyTuple > ,
48
+ kwargs : Option < & Bound < ' _ , PyDict > > ,
49
+ new_accepts_arguments : bool ,
39
50
) -> PyResult < * mut ffi:: PyObject > {
40
- // HACK (due to FIXME below): PyBaseObject_Type's tp_new isn't happy with NULL arguments
41
- let is_base_object = type_object == std:: ptr:: addr_of_mut!( ffi:: PyBaseObject_Type ) ;
42
- let subtype_borrowed: Borrowed < ' _ , ' _ , PyType > = subtype
51
+ let native_base_type_borrowed: Borrowed < ' _ , ' _ , PyType > = native_base_type
43
52
. cast :: < ffi:: PyObject > ( )
44
53
. assume_borrowed_unchecked ( py)
45
54
. downcast_unchecked ( ) ;
55
+ let tp_new = native_base_type_borrowed
56
+ . get_slot ( TP_NEW )
57
+ . unwrap_or ( ffi:: PyType_GenericNew ) ;
46
58
47
- if is_base_object {
48
- let alloc = subtype_borrowed
49
- . get_slot ( TP_ALLOC )
50
- . unwrap_or ( ffi:: PyType_GenericAlloc ) ;
51
-
52
- let obj = alloc ( subtype, 0 ) ;
53
- return if obj. is_null ( ) {
54
- Err ( PyErr :: fetch ( py) )
55
- } else {
56
- Ok ( obj)
57
- } ;
58
- }
59
-
60
- #[ cfg( Py_LIMITED_API ) ]
61
- unreachable ! ( "subclassing native types is not possible with the `abi3` feature" ) ;
59
+ let obj = if new_accepts_arguments {
60
+ tp_new (
61
+ subtype,
62
+ args. as_ptr ( ) ,
63
+ kwargs
64
+ . map ( |obj| obj. as_ptr ( ) )
65
+ . unwrap_or ( std:: ptr:: null_mut ( ) ) ,
66
+ )
67
+ } else {
68
+ let args = PyTuple :: empty ( py) ;
69
+ tp_new ( subtype, args. as_ptr ( ) , std:: ptr:: null_mut ( ) )
70
+ } ;
62
71
63
- #[ cfg( not( Py_LIMITED_API ) ) ]
64
- {
65
- match ( * type_object) . tp_new {
66
- // FIXME: Call __new__ with actual arguments
67
- Some ( newfunc) => {
68
- let obj = newfunc ( subtype, std:: ptr:: null_mut ( ) , std:: ptr:: null_mut ( ) ) ;
69
- if obj. is_null ( ) {
70
- Err ( PyErr :: fetch ( py) )
71
- } else {
72
- Ok ( obj)
73
- }
74
- }
75
- None => Err ( crate :: exceptions:: PyTypeError :: new_err (
76
- "base type without tp_new" ,
77
- ) ) ,
78
- }
72
+ if obj. is_null ( ) {
73
+ Err ( PyErr :: fetch ( py) )
74
+ } else {
75
+ Ok ( obj)
79
76
}
80
77
}
81
- let type_object = T :: type_object_raw ( py) ;
82
- inner ( py, type_object, subtype)
78
+ inner (
79
+ py,
80
+ T :: type_object_raw ( py) ,
81
+ subtype,
82
+ args,
83
+ kwargs,
84
+ T :: NEW_ACCEPTS_ARGUMENTS ,
85
+ )
83
86
}
84
87
85
88
#[ inline]
0 commit comments