1
1
//! `PyClass` and related traits.
2
2
use crate :: {
3
- class:: impl_:: { fallback_new, tp_dealloc, PyClassImpl } ,
3
+ class:: impl_:: { fallback_new, tp_dealloc, PyClassImpl , PyBufferProcs } ,
4
4
ffi,
5
5
pyclass_slots:: { PyClassDict , PyClassWeakRef } ,
6
6
PyCell , PyErr , PyMethodDefType , PyNativeType , PyResult , PyTypeInfo , Python ,
@@ -35,11 +35,53 @@ fn into_raw<T>(vec: Vec<T>) -> *mut c_void {
35
35
36
36
pub ( crate ) fn create_type_object < T > (
37
37
py : Python ,
38
- module_name : Option < & str > ,
39
- ) -> PyResult < * mut ffi:: PyTypeObject >
38
+ ) -> * mut ffi:: PyTypeObject
40
39
where
41
40
T : PyClass ,
42
41
{
42
+ match unsafe { create_type_object_impl (
43
+ py,
44
+ T :: DOC ,
45
+ T :: MODULE ,
46
+ T :: NAME ,
47
+ T :: BaseType :: type_object_raw ( py) ,
48
+ std:: mem:: size_of :: < T :: Layout > ( ) ,
49
+ T :: get_new ( ) ,
50
+ tp_dealloc :: < T > ,
51
+ T :: get_alloc ( ) ,
52
+ T :: get_free ( ) ,
53
+ PyCell :: < T > :: dict_offset ( ) ,
54
+ PyCell :: < T > :: weakref_offset ( ) ,
55
+ & T :: for_each_method_def,
56
+ & T :: for_each_proto_slot,
57
+ T :: IS_GC ,
58
+ T :: IS_BASETYPE ,
59
+ T :: get_buffer ( ) ,
60
+ ) } {
61
+ Ok ( type_object) => type_object,
62
+ Err ( e) => type_object_creation_failed ( py, e, T :: NAME ) ,
63
+ }
64
+ }
65
+
66
+ unsafe fn create_type_object_impl (
67
+ py : Python ,
68
+ tp_doc : & str ,
69
+ module_name : Option < & str > ,
70
+ name : & str ,
71
+ base_type_object : * mut ffi:: PyTypeObject ,
72
+ basicsize : usize ,
73
+ tp_new : Option < ffi:: newfunc > ,
74
+ tp_dealloc : ffi:: destructor ,
75
+ tp_alloc : Option < ffi:: allocfunc > ,
76
+ tp_free : Option < ffi:: freefunc > ,
77
+ dict_offset : Option < ffi:: Py_ssize_t > ,
78
+ weakref_offset : Option < ffi:: Py_ssize_t > ,
79
+ for_each_method_def : & dyn Fn ( & mut dyn FnMut ( & [ PyMethodDefType ] ) ) ,
80
+ for_each_proto_slot : & dyn Fn ( & mut dyn FnMut ( & [ ffi:: PyType_Slot ] ) ) ,
81
+ is_gc : bool ,
82
+ is_basetype : bool ,
83
+ buffer_procs : Option < & PyBufferProcs > ,
84
+ ) -> PyResult < * mut ffi:: PyTypeObject > {
43
85
let mut slots = Vec :: new ( ) ;
44
86
45
87
fn push_slot ( slots : & mut Vec < ffi:: PyType_Slot > , slot : c_int , pfunc : * mut c_void ) {
@@ -49,49 +91,49 @@ where
49
91
push_slot (
50
92
& mut slots,
51
93
ffi:: Py_tp_base ,
52
- T :: BaseType :: type_object_raw ( py ) as _ ,
94
+ base_type_object as _ ,
53
95
) ;
54
- if let Some ( doc) = py_class_doc ( T :: DOC ) {
96
+ if let Some ( doc) = py_class_doc ( tp_doc ) {
55
97
push_slot ( & mut slots, ffi:: Py_tp_doc , doc as _ ) ;
56
98
}
57
99
58
100
push_slot (
59
101
& mut slots,
60
102
ffi:: Py_tp_new ,
61
- T :: get_new ( ) . unwrap_or ( fallback_new) as _ ,
103
+ tp_new . unwrap_or ( fallback_new) as _ ,
62
104
) ;
63
- push_slot ( & mut slots, ffi:: Py_tp_dealloc , tp_dealloc :: < T > as _ ) ;
105
+ push_slot ( & mut slots, ffi:: Py_tp_dealloc , tp_dealloc as _ ) ;
64
106
65
- if let Some ( alloc) = T :: get_alloc ( ) {
107
+ if let Some ( alloc) = tp_alloc {
66
108
push_slot ( & mut slots, ffi:: Py_tp_alloc , alloc as _ ) ;
67
109
}
68
- if let Some ( free) = T :: get_free ( ) {
110
+ if let Some ( free) = tp_free {
69
111
push_slot ( & mut slots, ffi:: Py_tp_free , free as _ ) ;
70
112
}
71
113
72
114
#[ cfg( Py_3_9 ) ]
73
115
{
74
- let members = py_class_members ( PyCell :: < T > :: dict_offset ( ) , PyCell :: < T > :: weakref_offset ( ) ) ;
116
+ let members = py_class_members ( dict_offset, weakref_offset) ;
75
117
if !members. is_empty ( ) {
76
118
push_slot ( & mut slots, ffi:: Py_tp_members , into_raw ( members) )
77
119
}
78
120
}
79
121
80
122
// normal methods
81
- let methods = py_class_method_defs ( & T :: for_each_method_def) ;
123
+ let methods = py_class_method_defs ( for_each_method_def) ;
82
124
if !methods. is_empty ( ) {
83
125
push_slot ( & mut slots, ffi:: Py_tp_methods , into_raw ( methods) ) ;
84
126
}
85
127
86
128
// properties
87
- let props = py_class_properties ( T :: Dict :: IS_DUMMY , & T :: for_each_method_def) ;
129
+ let props = py_class_properties ( dict_offset . is_none ( ) , for_each_method_def) ;
88
130
if !props. is_empty ( ) {
89
131
push_slot ( & mut slots, ffi:: Py_tp_getset , into_raw ( props) ) ;
90
132
}
91
133
92
134
// protocol methods
93
135
let mut has_gc_methods = false ;
94
- T :: for_each_proto_slot ( & mut |proto_slots| {
136
+ for_each_proto_slot ( & mut |proto_slots| {
95
137
has_gc_methods |= proto_slots
96
138
. iter ( )
97
139
. any ( |slot| slot. slot == ffi:: Py_tp_clear || slot. slot == ffi:: Py_tp_traverse ) ;
@@ -100,41 +142,47 @@ where
100
142
101
143
push_slot ( & mut slots, 0 , ptr:: null_mut ( ) ) ;
102
144
let mut spec = ffi:: PyType_Spec {
103
- name : py_class_qualified_name ( module_name, T :: NAME ) ?,
104
- basicsize : std :: mem :: size_of :: < T :: Layout > ( ) as c_int ,
145
+ name : py_class_qualified_name ( module_name, name ) ?,
146
+ basicsize : basicsize as c_int ,
105
147
itemsize : 0 ,
106
- flags : py_class_flags ( has_gc_methods, T :: IS_GC , T :: IS_BASETYPE ) ,
148
+ flags : py_class_flags ( has_gc_methods, is_gc , is_basetype ) ,
107
149
slots : slots. as_mut_ptr ( ) ,
108
150
} ;
109
151
110
- let type_object = unsafe { ffi:: PyType_FromSpec ( & mut spec) } ;
152
+ let type_object = ffi:: PyType_FromSpec ( & mut spec) ;
111
153
if type_object. is_null ( ) {
112
154
Err ( PyErr :: fetch ( py) )
113
155
} else {
114
- tp_init_additional :: < T > ( type_object as _ ) ;
156
+ tp_init_additional ( type_object as _ , tp_doc , buffer_procs , dict_offset , weakref_offset ) ;
115
157
Ok ( type_object as _ )
116
158
}
117
159
}
118
160
161
+ #[ cold]
162
+ fn type_object_creation_failed ( py : Python , e : PyErr , name : & ' static str ) -> ! {
163
+ e. print ( py) ;
164
+ panic ! ( "An error occurred while initializing class {}" , name)
165
+ }
166
+
119
167
/// Additional type initializations necessary before Python 3.10
120
168
#[ cfg( all( not( Py_LIMITED_API ) , not( Py_3_10 ) ) ) ]
121
- fn tp_init_additional < T : PyClass > ( type_object : * mut ffi:: PyTypeObject ) {
169
+ fn tp_init_additional ( type_object : * mut ffi:: PyTypeObject , tp_doc : & str , buffer_procs : Option < & PyBufferProcs > , dict_offset : Option < ffi :: Py_ssize_t > , weakref_offset : Option < ffi :: Py_ssize_t > ) {
122
170
// Just patch the type objects for the things there's no
123
171
// PyType_FromSpec API for... there's no reason this should work,
124
172
// except for that it does and we have tests.
125
173
126
174
// Running this causes PyPy to segfault.
127
175
#[ cfg( all( not( PyPy ) , not( Py_3_10 ) ) ) ]
128
176
{
129
- if T :: DOC != "\0 " {
177
+ if tp_doc != "\0 " {
130
178
unsafe {
131
179
// Until CPython 3.10, tp_doc was treated specially for
132
180
// heap-types, and it removed the text_signature value from it.
133
181
// We go in after the fact and replace tp_doc with something
134
182
// that _does_ include the text_signature value!
135
183
ffi:: PyObject_Free ( ( * type_object) . tp_doc as _ ) ;
136
- let data = ffi:: PyObject_Malloc ( T :: DOC . len ( ) ) ;
137
- data. copy_from ( T :: DOC . as_ptr ( ) as _ , T :: DOC . len ( ) ) ;
184
+ let data = ffi:: PyObject_Malloc ( tp_doc . len ( ) ) ;
185
+ data. copy_from ( tp_doc . as_ptr ( ) as _ , tp_doc . len ( ) ) ;
138
186
( * type_object) . tp_doc = data as _ ;
139
187
}
140
188
}
@@ -144,7 +192,7 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
144
192
// must manually fixup the type object.
145
193
#[ cfg( not( Py_3_9 ) ) ]
146
194
{
147
- if let Some ( buffer) = T :: get_buffer ( ) {
195
+ if let Some ( buffer) = buffer_procs {
148
196
unsafe {
149
197
( * ( * type_object) . tp_as_buffer ) . bf_getbuffer = buffer. bf_getbuffer ;
150
198
( * ( * type_object) . tp_as_buffer ) . bf_releasebuffer = buffer. bf_releasebuffer ;
@@ -157,13 +205,13 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
157
205
#[ cfg( not( Py_3_9 ) ) ]
158
206
{
159
207
// __dict__ support
160
- if let Some ( dict_offset) = PyCell :: < T > :: dict_offset ( ) {
208
+ if let Some ( dict_offset) = dict_offset {
161
209
unsafe {
162
210
( * type_object) . tp_dictoffset = dict_offset;
163
211
}
164
212
}
165
213
// weakref support
166
- if let Some ( weakref_offset) = PyCell :: < T > :: weakref_offset ( ) {
214
+ if let Some ( weakref_offset) = weakref_offset {
167
215
unsafe {
168
216
( * type_object) . tp_weaklistoffset = weakref_offset;
169
217
}
@@ -172,7 +220,7 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
172
220
}
173
221
174
222
#[ cfg( any( Py_LIMITED_API , Py_3_10 ) ) ]
175
- fn tp_init_additional < T : PyClass > ( _type_object : * mut ffi:: PyTypeObject ) { }
223
+ fn tp_init_additional ( _type_object : * mut ffi:: PyTypeObject , tp_doc : & str , buffer_procs : Option < & PyBufferProcs > , dict_offset : Option < ffi :: Py_ssize_t > , weakref_offset : Option < ffi :: Py_ssize_t > ) { }
176
224
177
225
fn py_class_doc ( class_doc : & str ) -> Option < * mut c_char > {
178
226
match class_doc {
0 commit comments