Skip to content

Commit c46b5b5

Browse files
committed
Fix weirdness with type.__new__()/type()/metaclass.__new__()
1 parent e5bbe82 commit c46b5b5

41 files changed

Lines changed: 184 additions & 98 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

derive/src/pyclass.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ enum ClassItem {
2020
py_name: String,
2121
setter: bool,
2222
},
23+
Slot {
24+
slot_ident: Ident,
25+
item_ident: Ident,
26+
},
2327
}
2428

2529
fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
@@ -192,6 +196,28 @@ impl ClassItem {
192196
setter,
193197
});
194198
attr_idx = Some(i);
199+
} else if name == "pyslot" {
200+
if item.is_some() {
201+
bail_span!(
202+
sig.ident,
203+
"You can only have one #[py*] attribute on an impl item"
204+
)
205+
}
206+
let pyslot_err = "#[pyslot] must be of the form #[pyslot(slotname)]";
207+
let nesteds =
208+
meta_to_vec(meta).map_err(|meta| err_span!(meta, "{}", pyslot_err))?;
209+
if nesteds.len() != 1 {
210+
return Err(Diagnostic::spanned_error(&quote!(#(#nesteds)*), pyslot_err));
211+
}
212+
let slot_ident = match nesteds.into_iter().next().unwrap() {
213+
NestedMeta::Meta(Meta::Word(ident)) => ident,
214+
bad => bail_span!(bad, "{}", pyslot_err),
215+
};
216+
item = Some(ClassItem::Slot {
217+
slot_ident,
218+
item_ident: sig.ident.clone(),
219+
});
220+
attr_idx = Some(i);
195221
}
196222
}
197223
if let Some(attr_idx) = attr_idx {
@@ -257,6 +283,14 @@ pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
257283
} => Some(quote! {
258284
class.set_str_attr(#py_name, ctx.new_classmethod(Self::#item_ident));
259285
}),
286+
ClassItem::Slot {
287+
slot_ident,
288+
item_ident,
289+
} => Some(quote! {
290+
class.slots.borrow_mut().#slot_ident = Some(
291+
::rustpython_vm::function::IntoPyNativeFunc::into_func(Self::#item_ident)
292+
);
293+
}),
260294
_ => None,
261295
});
262296
let properties = properties

vm/src/macros.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,25 +137,36 @@ macro_rules! extend_module {
137137

138138
#[macro_export]
139139
macro_rules! py_class {
140-
( $ctx:expr, $class_name:expr, $class_base:expr, { $($name:expr => $value:expr),* $(,)* }) => {
140+
( $ctx:expr, $class_name:expr, $class_base:expr, { $($name:tt => $value:expr),* $(,)* }) => {
141141
{
142142
let py_class = $ctx.new_class($class_name, $class_base);
143-
$(
144-
py_class.set_str_attr($name, $value);
145-
)*
143+
$crate::extend_class!($ctx, &py_class, { $($name => $value),* });
146144
py_class
147145
}
148146
}
149147
}
150148

151149
#[macro_export]
152150
macro_rules! extend_class {
153-
( $ctx:expr, $class:expr, { $($name:expr => $value:expr),* $(,)* }) => {
154-
let class = $class;
151+
( $ctx:expr, $class:expr, { $($name:tt => $value:expr),* $(,)* }) => {
155152
$(
156-
class.set_str_attr($name, $value);
153+
$crate::extend_class!(@set_attr($ctx, $class, $name, $value));
157154
)*
158-
}
155+
if $class.slots.borrow().new.is_some() {
156+
$class.attributes.borrow_mut().entry("__new__".into()).or_insert_with(|| {
157+
$ctx.new_classmethod($crate::obj::objtype::type_new)
158+
});
159+
}
160+
};
161+
162+
(@set_attr($ctx:expr, $class:expr, (slot $slot_name:ident), $value:expr)) => {
163+
$class.slots.borrow_mut().$slot_name = Some(
164+
$crate::function::IntoPyNativeFunc::into_func($value)
165+
);
166+
};
167+
(@set_attr($ctx:expr, $class:expr, $name:expr, $value:expr)) => {
168+
$class.set_str_attr($name, $value);
169+
};
159170
}
160171

161172
#[macro_export]

vm/src/obj/objbool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ The class bool is a subclass of the class int, and cannot be subclassed.";
8181

8282
let bool_type = &context.types.bool_type;
8383
extend_class!(context, bool_type, {
84-
"__new__" => context.new_rustfunc(bool_new),
84+
(slot new) => bool_new,
8585
"__repr__" => context.new_rustfunc(bool_repr),
8686
"__format__" => context.new_rustfunc(bool_format),
8787
"__or__" => context.new_rustfunc(bool_or),

vm/src/obj/objbytearray.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn init(context: &PyContext) {
9090

9191
#[pyimpl]
9292
impl PyByteArrayRef {
93-
#[pymethod(name = "__new__")]
93+
#[pyslot(new)]
9494
fn bytearray_new(
9595
cls: PyClassRef,
9696
options: ByteInnerNewOptions,

vm/src/obj/objbytes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub fn init(context: &PyContext) {
9797

9898
#[pyimpl]
9999
impl PyBytesRef {
100-
#[pymethod(name = "__new__")]
100+
#[pyslot(new)]
101101
fn bytes_new(
102102
cls: PyClassRef,
103103
options: ByteInnerNewOptions,

vm/src/obj/objclassmethod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl PyValue for PyClassMethod {
3939

4040
#[pyimpl]
4141
impl PyClassMethod {
42-
#[pymethod(name = "__new__")]
42+
#[pyslot(new)]
4343
fn new(
4444
cls: PyClassRef,
4545
callable: PyObjectRef,

vm/src/obj/objcode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl PyCodeRef {
8282

8383
pub fn init(context: &PyContext) {
8484
extend_class!(context, &context.types.code_type, {
85-
"__new__" => context.new_rustfunc(PyCodeRef::new),
85+
(slot new) => PyCodeRef::new,
8686
"__repr__" => context.new_rustfunc(PyCodeRef::repr),
8787

8888
"co_argcount" => context.new_property(PyCodeRef::co_argcount),

vm/src/obj/objcomplex.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ impl PyComplex {
230230
!Complex64::is_zero(&self.value)
231231
}
232232

233-
#[pymethod(name = "__new__")]
233+
#[pyslot(new)]
234234
fn complex_new(
235235
cls: PyClassRef,
236236
real: OptionalArg<IntoPyFloat>,

vm/src/obj/objdict.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ pub fn init(context: &PyContext) {
577577
"__eq__" => context.new_rustfunc(PyDictRef::eq),
578578
"__getitem__" => context.new_rustfunc(PyDictRef::inner_getitem),
579579
"__iter__" => context.new_rustfunc(PyDictRef::iter),
580-
"__new__" => context.new_rustfunc(PyDictRef::new),
580+
(slot new) => PyDictRef::new,
581581
"__repr__" => context.new_rustfunc(PyDictRef::repr),
582582
"__setitem__" => context.new_rustfunc(PyDictRef::inner_setitem),
583583
"__hash__" => context.new_rustfunc(PyDictRef::hash),

vm/src/obj/objellipsis.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::vm::VirtualMachine;
44

55
pub fn init(context: &PyContext) {
66
extend_class!(context, &context.ellipsis_type, {
7-
"__new__" => context.new_rustfunc(ellipsis_new),
7+
(slot new) => ellipsis_new,
88
"__repr__" => context.new_rustfunc(ellipsis_repr),
99
"__reduce__" => context.new_rustfunc(ellipsis_reduce),
1010
});

0 commit comments

Comments
 (0)