Skip to content

Commit 53b46a7

Browse files
committed
Split some of #[pyclass] off into #[pyimpl]
1 parent 15cffc4 commit 53b46a7

File tree

4 files changed

+91
-53
lines changed

4 files changed

+91
-53
lines changed

derive/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,12 @@ pub fn derive_from_args(input: TokenStream) -> TokenStream {
5252
pub fn pyclass(attr: TokenStream, item: TokenStream) -> TokenStream {
5353
let attr = parse_macro_input!(attr as AttributeArgs);
5454
let item = parse_macro_input!(item as Item);
55-
pyclass::impl_py_class(attr, item).into()
55+
pyclass::impl_pyclass(attr, item).into()
56+
}
57+
58+
#[proc_macro_attribute]
59+
pub fn pyimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
60+
let attr = parse_macro_input!(attr as AttributeArgs);
61+
let item = parse_macro_input!(item as Item);
62+
pyclass::impl_pyimpl(attr, item).into()
5663
}

derive/src/pyclass.rs

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,62 @@ impl Method {
8989
}
9090
}
9191

92-
pub fn impl_py_class(attr: AttributeArgs, item: Item) -> TokenStream2 {
92+
pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> TokenStream2 {
9393
let mut imp = if let Item::Impl(imp) = item {
9494
imp
9595
} else {
9696
return quote!(#item);
9797
};
98+
99+
let rp_path = rustpython_path_attr(&attr);
100+
101+
let methods = imp
102+
.items
103+
.iter_mut()
104+
.filter_map(|item| {
105+
if let ImplItem::Method(meth) = item {
106+
Method::from_syn(&mut meth.attrs, &meth.sig)
107+
} else {
108+
None
109+
}
110+
})
111+
.collect::<Vec<_>>();
112+
let ty = &imp.self_ty;
113+
let methods = methods.iter().map(
114+
|Method {
115+
py_name,
116+
fn_name,
117+
kind,
118+
}| {
119+
let constructor_fn = kind.to_ctx_constructor_fn();
120+
quote! {
121+
ctx.set_attr(class, #py_name, ctx.#constructor_fn(Self::#fn_name));
122+
}
123+
},
124+
);
125+
126+
quote! {
127+
#imp
128+
impl #rp_path::pyobject::PyClassImpl for #ty {
129+
fn impl_extend_class(
130+
ctx: &#rp_path::pyobject::PyContext,
131+
class: &#rp_path::obj::objtype::PyClassRef,
132+
) {
133+
#(#methods)*
134+
}
135+
}
136+
}
137+
}
138+
139+
pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> TokenStream2 {
140+
let struc = if let Item::Struct(struc) = item {
141+
struc
142+
} else {
143+
panic!("#[pyclass] can only be on a struct declaration");
144+
};
145+
98146
let rp_path = rustpython_path_attr(&attr);
147+
99148
let mut class_name = None;
100149
for attr in attr {
101150
if let NestedMeta::Meta(meta) = attr {
@@ -110,9 +159,10 @@ pub fn impl_py_class(attr: AttributeArgs, item: Item) -> TokenStream2 {
110159
}
111160
}
112161
}
113-
let class_name = class_name.expect("#[pyclass] must have a name");
162+
let class_name = class_name.unwrap_or_else(|| struc.ident.to_string());
163+
114164
let mut doc: Option<Vec<String>> = None;
115-
for attr in imp.attrs.iter() {
165+
for attr in struc.attrs.iter() {
116166
if attr.path.is_ident("doc") {
117167
let meta = attr.parse_meta().expect("expected doc attr to be a meta");
118168
if let Meta::NameValue(name_value) = meta {
@@ -125,8 +175,6 @@ pub fn impl_py_class(attr: AttributeArgs, item: Item) -> TokenStream2 {
125175
} else {
126176
panic!("expected #[doc = ...] to be a string")
127177
}
128-
} else {
129-
panic!("expected #[doc] to be a NameValue, e.g. #[doc = \"...\"");
130178
}
131179
}
132180
}
@@ -137,42 +185,14 @@ pub fn impl_py_class(attr: AttributeArgs, item: Item) -> TokenStream2 {
137185
}
138186
None => quote!(None),
139187
};
140-
let methods = imp
141-
.items
142-
.iter_mut()
143-
.filter_map(|item| {
144-
if let ImplItem::Method(meth) = item {
145-
Method::from_syn(&mut meth.attrs, &meth.sig)
146-
} else {
147-
None
148-
}
149-
})
150-
.collect::<Vec<_>>();
151-
let ty = &imp.self_ty;
152-
let methods = methods.iter().map(
153-
|Method {
154-
py_name,
155-
fn_name,
156-
kind,
157-
}| {
158-
let constructor_fn = kind.to_ctx_constructor_fn();
159-
quote! {
160-
ctx.set_attr(class, #py_name, ctx.#constructor_fn(Self::#fn_name));
161-
}
162-
},
163-
);
188+
189+
let ty = &struc.ident;
164190

165191
quote! {
166-
#imp
167-
impl #rp_path::pyobject::IntoPyClass for #ty {
192+
#struc
193+
impl #rp_path::pyobject::PyClassDef for #ty {
168194
const NAME: &'static str = #class_name;
169195
const DOC: Option<&'static str> = #doc;
170-
fn _extend_class(
171-
ctx: &#rp_path::pyobject::PyContext,
172-
class: &#rp_path::obj::objtype::PyClassRef,
173-
) {
174-
#(#methods)*
175-
}
176196
}
177197
}
178198
}

vm/src/obj/objstr.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use unicode_segmentation::UnicodeSegmentation;
1010
use crate::format::{FormatParseError, FormatPart, FormatString};
1111
use crate::function::{OptionalArg, PyFuncArgs};
1212
use crate::pyobject::{
13-
IdProtocol, IntoPyClass, IntoPyObject, PyContext, PyIterable, PyObjectRef, PyRef, PyResult,
13+
IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult,
1414
PyValue, TryFromObject, TryIntoRef, TypeProtocol,
1515
};
1616
use crate::vm::VirtualMachine;
@@ -20,6 +20,17 @@ use super::objsequence::PySliceableSequence;
2020
use super::objslice::PySlice;
2121
use super::objtype::{self, PyClassRef};
2222

23+
/// str(object='') -> str
24+
/// str(bytes_or_buffer[, encoding[, errors]]) -> str
25+
///
26+
/// Create a new string object from the given object. If encoding or
27+
/// errors is specified, then the object must expose a data buffer
28+
/// that will be decoded using the given encoding and error handler.
29+
/// Otherwise, returns the result of object.__str__() (if defined)
30+
/// or repr(object).
31+
/// encoding defaults to sys.getdefaultencoding().
32+
/// errors defaults to 'strict'."
33+
#[pyclass(name = "str", __inside_vm)]
2334
#[derive(Clone, Debug)]
2435
pub struct PyString {
2536
// TODO: shouldn't be public
@@ -48,17 +59,7 @@ impl TryIntoRef<PyString> for &str {
4859
}
4960
}
5061

51-
#[pyclass(__inside_vm, name = "str")]
52-
/// str(object='') -> str
53-
/// str(bytes_or_buffer[, encoding[, errors]]) -> str
54-
///
55-
/// Create a new string object from the given object. If encoding or
56-
/// errors is specified, then the object must expose a data buffer
57-
/// that will be decoded using the given encoding and error handler.
58-
/// Otherwise, returns the result of object.__str__() (if defined)
59-
/// or repr(object).
60-
/// encoding defaults to sys.getdefaultencoding().
61-
/// errors defaults to 'strict'."
62+
#[pyimpl(__inside_vm)]
6263
impl PyStringRef {
6364
// TODO: should with following format
6465
// class str(object='')

vm/src/pyobject.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,14 +1259,24 @@ where
12591259
}
12601260
}
12611261

1262-
pub trait IntoPyClass {
1262+
pub trait PyClassDef {
12631263
const NAME: &'static str;
12641264
const DOC: Option<&'static str> = None;
1265+
}
1266+
1267+
impl<T> PyClassDef for PyRef<T>
1268+
where
1269+
T: PyClassDef,
1270+
{
1271+
const NAME: &'static str = T::NAME;
1272+
const DOC: Option<&'static str> = T::DOC;
1273+
}
12651274

1266-
fn _extend_class(ctx: &PyContext, class: &PyClassRef);
1275+
pub trait PyClassImpl: PyClassDef {
1276+
fn impl_extend_class(ctx: &PyContext, class: &PyClassRef);
12671277

12681278
fn extend_class(ctx: &PyContext, class: &PyClassRef) {
1269-
Self::_extend_class(ctx, class);
1279+
Self::impl_extend_class(ctx, class);
12701280
if let Some(doc) = Self::DOC {
12711281
ctx.set_attr(class, "__doc__", ctx.new_str(doc.into()));
12721282
}

0 commit comments

Comments
 (0)