Skip to content

Commit 98f20c4

Browse files
committed
Native subclassing part 2
1 parent 3def623 commit 98f20c4

Some content is hidden

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

92 files changed

+682
-1097
lines changed

derive-impl/src/pyclass.rs

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,10 @@ fn generate_class_def(
307307
ident: &Ident,
308308
name: &str,
309309
module_name: Option<&str>,
310-
base: Option<&Ident>,
310+
base: Option<&syn::ExprPath>,
311311
metaclass: Option<String>,
312312
unhashable: bool,
313-
no_payload: bool,
313+
is_pystruct: bool,
314314
attrs: &[Attribute],
315315
) -> Result<TokenStream> {
316316
let doc = attrs.doc().or_else(|| {
@@ -341,16 +341,6 @@ fn generate_class_def(
341341
quote!(false)
342342
};
343343
let basicsize = quote!(std::mem::size_of::<#ident>());
344-
let is_pystruct = attrs.iter().any(|attr| {
345-
attr.path().is_ident("derive")
346-
&& if let Ok(Meta::List(l)) = attr.parse_meta() {
347-
l.nested
348-
.into_iter()
349-
.any(|n| n.get_ident().is_some_and(|p| p == "PyStructSequence"))
350-
} else {
351-
false
352-
}
353-
});
354344
if base.is_some() && is_pystruct {
355345
bail_span!(ident, "PyStructSequence cannot have `base` class attr",);
356346
}
@@ -384,7 +374,7 @@ fn generate_class_def(
384374
quote! { ::rustpython_vm::builtins::PyBaseObject }
385375
};
386376

387-
let type_id = if no_payload || is_pystruct {
377+
let type_id = if is_pystruct {
388378
quote!(::std::option::Option::None)
389379
} else {
390380
quote! {
@@ -438,16 +428,27 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<Tok
438428
let base = class_meta.base()?;
439429
let metaclass = class_meta.metaclass()?;
440430
let unhashable = class_meta.unhashable()?;
441-
let no_payload = class_meta.no_payload()?;
431+
let manual_payload = class_meta.manual_payload()?;
432+
433+
let is_pystruct = attrs.iter().any(|attr| {
434+
attr.path().is_ident("derive")
435+
&& if let Ok(Meta::List(l)) = attr.parse_meta() {
436+
l.nested
437+
.into_iter()
438+
.any(|n| n.get_ident().is_some_and(|p| p == "PyStructSequence"))
439+
} else {
440+
false
441+
}
442+
});
442443

443444
let class_def = generate_class_def(
444445
ident,
445446
&class_name,
446447
module_name.as_deref(),
447-
base.as_ref(),
448+
base,
448449
metaclass,
449450
unhashable,
450-
no_payload,
451+
is_pystruct,
451452
attrs,
452453
)?;
453454

@@ -500,19 +501,23 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result<Tok
500501
}
501502
};
502503

503-
let impl_payload = if let Some(ctx_type_name) = class_meta.ctx_name()? {
504-
let ctx_type_ident = Ident::new(&ctx_type_name, ident.span()); // FIXME span
505-
506-
let base = base
504+
let impl_payload = if !(is_pystruct || manual_payload) {
505+
let base_ty = base
507506
.as_ref()
508507
.map(|t| quote!(#t))
509508
.unwrap_or_else(|| quote!(::rustpython_vm::builtins::PyBaseObject));
510-
// We need this to make extend mechanism work:
509+
let get_class = if let Some(ctx_type_ident) = class_meta.ctx_name()? {
510+
quote!(ctx.types.#ctx_type_ident)
511+
} else if let Some(ctx_type_ident) = class_meta.exception_ctx_name()? {
512+
quote!(ctx.exceptions.#ctx_type_ident)
513+
} else {
514+
quote!(<Self as ::rustpython_vm::class::StaticType>::static_type())
515+
};
511516
quote! {
512517
impl ::rustpython_vm::PyPayload for #ident {
513-
type Super = #base;
518+
type Super = #base_ty;
514519
fn class(ctx: &::rustpython_vm::vm::Context) -> &'static ::rustpython_vm::Py<::rustpython_vm::builtins::PyType> {
515-
ctx.types.#ctx_type_ident
520+
#get_class
516521
}
517522
}
518523
}
@@ -558,21 +563,9 @@ pub(crate) fn impl_pyexception(attr: PunctuatedNestedMeta, item: Item) -> Result
558563
let base_class_name = class_meta
559564
.base()?
560565
.ok_or_else(|| syn::Error::new(Span::call_site(), "pyexception must specify base"))?;
561-
let impl_payload = if let Some(ctx_type_name) = class_meta.ctx_name()? {
562-
let ctx_type_ident = Ident::new(&ctx_type_name, ident.span()); // FIXME span
563-
564-
// We need this to make extend mechanism work:
565-
quote! {
566-
impl ::rustpython_vm::PyPayload for #ident {
567-
type Super = #base_class_name;
568-
fn class(ctx: &::rustpython_vm::vm::Context) -> &'static ::rustpython_vm::Py<::rustpython_vm::builtins::PyType> {
569-
ctx.exceptions.#ctx_type_ident
570-
}
571-
}
572-
}
573-
} else {
574-
quote! {}
575-
};
566+
let ctx_type_ident = class_meta
567+
.ctx_name()?
568+
.ok_or_else(|| syn::Error::new(Span::call_site(), "pyexception must specify ctx"))?;
576569
let impl_pyclass = if class_meta.has_impl()? {
577570
quote! {
578571
#[pyexception]
@@ -582,12 +575,9 @@ pub(crate) fn impl_pyexception(attr: PunctuatedNestedMeta, item: Item) -> Result
582575
quote! {}
583576
};
584577

585-
let base_class_name = base_class_name.to_string();
586-
587578
let ret = quote! {
588-
#[pyclass(module = false, name = #class_name, base = #base_class_name)]
579+
#[pyclass(module = false, name = #class_name, base = #base_class_name, exception_ctx = #ctx_type_ident)]
589580
#item
590-
#impl_payload
591581
#impl_pyclass
592582
};
593583
Ok(ret)

derive-impl/src/util.rs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -187,31 +187,51 @@ impl ItemMetaInner {
187187
Ok(value)
188188
}
189189

190-
pub fn _optional_parse<T: syn::parse::Parse>(&self, key: &str) -> Result<Option<T>> {
190+
pub fn _optional_path(&self, key: &str) -> Result<Option<&syn::ExprPath>> {
191191
let value = if let Some((_, meta)) = self.meta_map.get(key) {
192192
let Meta::NameValue(syn::MetaNameValue {
193-
value:
194-
syn::Expr::Lit(syn::ExprLit {
195-
lit: syn::Lit::Str(lit),
196-
..
197-
}),
193+
value: syn::Expr::Path(path),
198194
..
199195
}) = meta
200196
else {
201197
bail_span!(
202198
meta,
203-
"#[{}({} = ...)] must exist as a string",
199+
"#[{}({} = ...)] must exist as a path",
204200
self.meta_name(),
205201
key
206202
)
207203
};
208-
Some(lit.parse()?)
204+
Some(path)
209205
} else {
210206
None
211207
};
212208
Ok(value)
213209
}
214210

211+
pub fn _optional_ident(&self, key: &str) -> Result<Option<&Ident>> {
212+
let Some((_, meta)) = self.meta_map.get(key) else {
213+
return Ok(None);
214+
};
215+
if let Meta::NameValue(syn::MetaNameValue {
216+
value:
217+
syn::Expr::Path(syn::ExprPath {
218+
qself: None, path, ..
219+
}),
220+
..
221+
}) = meta
222+
{
223+
if let Some(ident) = path.get_ident() {
224+
return Ok(Some(ident));
225+
}
226+
}
227+
bail_span!(
228+
meta,
229+
"#[{}({} = ...)] must exist as an ident",
230+
self.meta_name(),
231+
key
232+
)
233+
}
234+
215235
pub fn _has_key(&self, key: &str) -> Result<bool> {
216236
Ok(matches!(self.meta_map.get(key), Some((_, _))))
217237
}
@@ -361,8 +381,9 @@ impl ItemMeta for ClassItemMeta {
361381
"base",
362382
"metaclass",
363383
"unhashable",
364-
"no_payload",
384+
"manual_payload",
365385
"ctx",
386+
"exception_ctx",
366387
"impl",
367388
"traverse",
368389
];
@@ -401,20 +422,24 @@ impl ClassItemMeta {
401422
)
402423
}
403424

404-
pub fn ctx_name(&self) -> Result<Option<String>> {
405-
self.inner()._optional_str("ctx")
425+
pub fn ctx_name(&self) -> Result<Option<&Ident>> {
426+
self.inner()._optional_ident("ctx")
427+
}
428+
429+
pub fn exception_ctx_name(&self) -> Result<Option<&Ident>> {
430+
self.inner()._optional_ident("exception_ctx")
406431
}
407432

408-
pub fn base(&self) -> Result<Option<Ident>> {
409-
self.inner()._optional_parse("base")
433+
pub fn base(&self) -> Result<Option<&syn::ExprPath>> {
434+
self.inner()._optional_path("base")
410435
}
411436

412437
pub fn unhashable(&self) -> Result<bool> {
413438
self.inner()._bool("unhashable")
414439
}
415440

416-
pub fn no_payload(&self) -> Result<bool> {
417-
self.inner()._bool("no_payload")
441+
pub fn manual_payload(&self) -> Result<bool> {
442+
self.inner()._bool("manual_payload")
418443
}
419444

420445
pub fn metaclass(&self) -> Result<Option<String>> {
@@ -471,8 +496,7 @@ impl ClassItemMeta {
471496
pub(crate) struct ExceptionItemMeta(ClassItemMeta);
472497

473498
impl ItemMeta for ExceptionItemMeta {
474-
const ALLOWED_NAMES: &'static [&'static str] =
475-
&["name", "base", "unhashable", "no_payload", "ctx", "impl"];
499+
const ALLOWED_NAMES: &'static [&'static str] = &["name", "base", "unhashable", "ctx", "impl"];
476500

477501
fn from_inner(inner: ItemMetaInner) -> Self {
478502
Self(ClassItemMeta(inner))

derive/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub fn derive_from_args(input: TokenStream) -> TokenStream {
3434
/// - `IMMUTABLETYPE`: class attributes are immutable.
3535
/// - `with`: which trait implementations are to be included in the python class.
3636
/// ```rust, ignore
37-
/// #[pyclass(module = "my_module", name = "MyClass", base = "BaseClass")]
37+
/// #[pyclass(module = "my_module", name = "MyClass", base = BaseClass)]
3838
/// struct MyStruct {
3939
/// x: i32,
4040
/// }

examples/call_between_rust_and_python.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustpython::vm::{
2-
PyObject, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine, pyclass, pymodule,
2+
PyObject, PyResult, TryFromBorrowedObject, VirtualMachine, pyclass, pymodule,
33
};
44

55
pub fn main() {
@@ -64,7 +64,7 @@ python_person.name: {}",
6464

6565
#[pyattr]
6666
#[pyclass(module = "rust_py_module", name = "RustStruct")]
67-
#[derive(Debug, PyPayload)]
67+
#[derive(Debug)]
6868
struct RustStruct {
6969
numbers: NumVec,
7070
}

stdlib/src/array.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ mod array {
622622
#[pyattr]
623623
#[pyattr(name = "ArrayType")]
624624
#[pyclass(name = "array")]
625-
#[derive(Debug, PyPayload)]
625+
#[derive(Debug)]
626626
pub struct PyArray {
627627
array: PyRwLock<ArrayContentType>,
628628
exports: AtomicUsize,
@@ -1387,7 +1387,7 @@ mod array {
13871387

13881388
#[pyattr]
13891389
#[pyclass(name = "arrayiterator", traverse)]
1390-
#[derive(Debug, PyPayload)]
1390+
#[derive(Debug)]
13911391
pub struct PyArrayIter {
13921392
internal: PyMutex<PositionIterInternal<PyArrayRef>>,
13931393
}

stdlib/src/bz2.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod _bz2 {
2727

2828
#[pyattr]
2929
#[pyclass(name = "BZ2Decompressor")]
30-
#[derive(PyPayload)]
30+
#[derive()]
3131
struct BZ2Decompressor {
3232
state: PyMutex<DecompressorState>,
3333
}
@@ -172,7 +172,7 @@ mod _bz2 {
172172

173173
#[pyattr]
174174
#[pyclass(name = "BZ2Compressor")]
175-
#[derive(PyPayload)]
175+
#[derive()]
176176
struct BZ2Compressor {
177177
state: PyMutex<CompressorState>,
178178
}

stdlib/src/contextvars.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ mod _contextvars {
4343
type Hamt = IndexMap<PyRef<ContextVar>, PyObjectRef, ahash::RandomState>;
4444

4545
#[pyclass(no_attr, name = "Hamt", module = "contextvars")]
46-
#[derive(Debug, PyPayload)]
46+
#[derive(Debug)]
4747
pub(crate) struct HamtObject {
4848
hamt: RefCell<Hamt>,
4949
}
@@ -73,7 +73,7 @@ mod _contextvars {
7373

7474
#[pyattr]
7575
#[pyclass(name = "Context")]
76-
#[derive(Debug, PyPayload)]
76+
#[derive(Debug)]
7777
pub(crate) struct PyContext {
7878
// not to confuse with vm::Context
7979
inner: ContextInner,
@@ -287,7 +287,7 @@ mod _contextvars {
287287

288288
#[pyattr]
289289
#[pyclass(name, traverse)]
290-
#[derive(PyPayload)]
290+
#[derive()]
291291
struct ContextVar {
292292
#[pytraverse(skip)]
293293
name: String,
@@ -537,7 +537,7 @@ mod _contextvars {
537537

538538
#[pyattr]
539539
#[pyclass(name = "Token")]
540-
#[derive(Debug, PyPayload)]
540+
#[derive(Debug)]
541541
struct ContextToken {
542542
ctx: PyRef<PyContext>, // tok_ctx in CPython
543543
var: PyRef<ContextVar>, // tok_var in CPython
@@ -585,7 +585,7 @@ mod _contextvars {
585585
}
586586

587587
#[pyclass(no_attr, name = "Token.MISSING")]
588-
#[derive(Debug, PyPayload)]
588+
#[derive(Debug)]
589589
pub(super) struct ContextTokenMissing {}
590590

591591
#[pyclass(with(Representable))]

stdlib/src/csv.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ mod _csv {
5353

5454
#[pyattr]
5555
#[pyclass(module = "csv", name = "Dialect")]
56-
#[derive(Debug, PyPayload, Clone, Copy)]
56+
#[derive(Debug, Clone, Copy)]
5757
struct PyDialect {
5858
delimiter: u8,
5959
quotechar: Option<u8>,
@@ -900,7 +900,7 @@ mod _csv {
900900
}
901901

902902
#[pyclass(no_attr, module = "_csv", name = "reader", traverse)]
903-
#[derive(PyPayload)]
903+
#[derive()]
904904
pub(super) struct Reader {
905905
iter: PyIter,
906906
#[pytraverse(skip)]
@@ -1054,7 +1054,7 @@ mod _csv {
10541054
}
10551055

10561056
#[pyclass(no_attr, module = "_csv", name = "writer", traverse)]
1057-
#[derive(PyPayload)]
1057+
#[derive()]
10581058
pub(super) struct Writer {
10591059
write: PyObjectRef,
10601060
#[pytraverse(skip)]

0 commit comments

Comments
 (0)