Skip to main content

rustpython_derive/
lib.rs

1#![recursion_limit = "128"]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/main/logo.png")]
3#![doc(html_root_url = "https://docs.rs/rustpython-derive/")]
4
5use proc_macro::TokenStream;
6use rustpython_derive_impl as derive_impl;
7use syn::parse_macro_input;
8use syn::punctuated::Punctuated;
9
10/// Derive `FromArgs` for a struct whose fields are Python arguments.
11///
12/// Each field takes one `#[pyarg(...)]`. The first item is the parameter kind.
13/// # Arguments
14/// - `positional`: positional-only.
15/// - `any`: positional or keyword.
16/// - `named`: keyword-only.
17/// - `flatten`: take this field from the same argument list. No other keys.
18/// - `name = "..."`: Python parameter name. The field name is used when omitted.
19/// - `default`: missing argument stores `Default::default()`. Affects parsing.
20///   The signature text is `0`, `False` or `0.0` for a primitive integer, `bool`
21///   or float field, and `<unrepresentable>` otherwise, unless `py_default` is set.
22/// - `default = <expr>`: missing argument stores that Rust value. Affects parsing.
23///   A string, byte-string, integer (optionally negated), float, or bool literal
24///   on any field that is not a Rust primitive (`i8`..`i128`, `u8`..`u128`,
25///   `isize`, `usize`, `f32`, `f64`, `bool`), `&'static str`, `Option<T>`, or
26///   `OptionalArg<T>` is converted only when the argument is missing:
27///   `<FieldTy as TryFromObject>::try_from_object(vm, ToPyObject::to_pyobject(LIT, vm))?`.
28///   A byte-string literal becomes a Python `bytes` object.
29/// - `default = ::NAME`: `NAME` is one identifier. The signature copies that
30///   name, and the missing argument stores `Into::into(NAME)` (the leading
31///   `::` is not Rust syntax for a local constant). A longer `::` path is a
32///   compile error. An explicit `py_default` still wins. A path without a
33///   leading `::` stays a typed value.
34/// - `optional`: same parsing as a bare `default`. The field type must implement
35///   `OptionalArgDefault`. `Option<T>` renders `None`; `OptionalArg<T>` renders
36///   `<unrepresentable>`. Any other type is rejected.
37/// - `py_default = "<python source>"`: text copied verbatim into `__text_signature__`.
38///   Never affects parsing. Overrides every other signature default.
39///   `py_default = "<unrepresentable>"` is a compile error. Use `OptionalArg`
40///   when a missing argument is a distinct state, or give the default's type a
41///   real `py_default()`.
42/// - `error_msg = "..."`: type-error text when conversion fails.
43/// # Signature default
44/// An explicit `py_default` is used as written. Otherwise:
45/// - a literal, a negative literal, or the path `None` becomes a typed default
46///   (`None`, `True`/`False`, an int, a quoted str, a bytes literal, a char;
47///   a float literal keeps its source text);
48/// - a non-literal on a primitive integer field becomes that value as a decimal int;
49/// - a path on a `bool` field becomes `True` or `False`;
50/// - `::NAME` is the name, verbatim;
51/// - any other expression uses `const V: FieldTy = <expr>; V.py_default()`.
52///
53/// A function argument whose pattern is a one-field tuple struct takes the
54/// parameter name from that field. `Fildes(fd): Fildes` is `fd`. A reference
55/// or parentheses around the inner pattern are skipped. When the argument
56/// type supplies parameters, this name is ignored.
57///
58/// `py_default` is an inherent `pub const fn py_default(&self) -> DefaultRepr`.
59/// A type defines it once, and every `default = <expr>` of that type reuses it:
60///
61/// ```rust, ignore
62/// impl ArgByteOrder {
63///     pub const fn py_default(&self) -> DefaultRepr {
64///         match self {
65///             Self::Big => DefaultRepr::Str("big"),
66///             Self::Little => DefaultRepr::Str("little"),
67///         }
68///     }
69/// }
70///
71/// #[pyarg(any, default = ArgByteOrder::Big)]
72/// byteorder: ArgByteOrder, // signature shows 'big'
73/// ```
74///
75/// A bare `optional` renders the field type's default:
76///
77/// | Rust type | Meaning | Clinic equivalent | Signature default |
78/// | --- | --- | --- | --- |
79/// | `OptionalArg<T>` | the argument may be omitted (`Missing`). That is distinct from every Python value, including `None` | `= NULL` | `<unrepresentable>` |
80/// | `Option<T>` | `None` or a value. A missing argument and an explicit `None` are the same | `= None` | `None` |
81/// | `OptionalOption<T>` (`OptionalArg<Option<T>>`) | missing, `None`, and a value are all distinct | `= NULL`, and `None` is accepted | `<unrepresentable>` |
82///
83/// Keep `py_default` when the expression needs `vm` (so it is not const), or
84/// when an `OptionalArg` is missing in the body and the shown default is a
85/// concrete value the Rust type cannot store.
86/// ```rust, ignore
87/// #[derive(FromArgs)]
88/// struct OpenArgs {
89///     #[pyarg(any, default = 0o777)]
90///     mode: i32, // signature shows 511
91///     #[pyarg(named, default = "main")]
92///     name: PyStrRef, // signature shows 'main'
93/// }
94///
95/// #[derive(FromArgs)]
96/// struct PrintOptions {
97///     // None means a space; the string is filled in when printing.
98///     #[pyarg(named, default, py_default = "' '")]
99///     sep: Option<PyStrRef>,
100///     #[pyarg(named, default = None)]
101///     file: Option<PyObjectRef>,
102/// }
103/// ```
104#[proc_macro_derive(FromArgs, attributes(pyarg))]
105pub fn derive_from_args(input: TokenStream) -> TokenStream {
106    let input = parse_macro_input!(input);
107    derive_impl::derive_from_args(input).into()
108}
109
110/// The attribute can be applied either to a struct, trait, or impl.
111/// # Struct
112/// This implements `MaybeTraverse`, `PyClassDef`, and `StaticType` for the struct.
113/// Consider deriving `Traverse` to implement it.
114/// ## Arguments
115/// - `module`: the module which contains the class --  can be omitted if in a `#[pymodule]`.
116/// - `name`: the name of the Python class, by default it is the name of the struct.
117/// - `base`: the base class of the Python class.
118///   This does not cause inheritance of functions or attributes that must be done by a separate trait.
119///   The native payload must be a struct with the base as its first field.
120///   The macro adds `repr(C)` if no explicit representation is present and
121///   checks the base field offset, the payload offset in `Py<T>`, and object alignment.
122/// # Impl
123/// This part implements `PyClassImpl` for the struct.
124/// This includes methods, getters/setters, etc.; only annotated methods will be included.
125/// Common functions and abilities like instantiation and `__call__` are often implemented by
126/// traits rather than in the `impl` itself; see `Constructor` and `Callable` respectively for those.
127/// ## Arguments
128/// - `name`: the name of the Python class, when no name is provided the struct name is used.
129/// - `flags`: the flags of the class, see `PyTypeFlags`.
130///     - `BASETYPE`: allows the class to be inheritable.
131///     - `IMMUTABLETYPE`: class attributes are immutable.
132/// - `with`: which trait implementations are to be included in the python class.
133/// ```rust, ignore
134/// #[pyclass(module = "my_module", name = "MyClass", base = BaseClass)]
135/// struct MyStruct {
136///    x: i32,
137/// }
138///
139/// impl Constructor for MyStruct {
140///     ...
141/// }
142///
143/// #[pyclass(with(Constructor))]
144/// impl MyStruct {
145///    ...
146/// }
147/// ```
148/// ## Docstrings
149/// A `///` doc comment on the struct, or on a `#[pymethod]` / `#[pyclassmethod]` /
150/// `#[pystaticmethod]` / `#[pygetset]` / `#[pymember]` item, is the Python
151/// docstring and overrides the stored documentation. The stored documentation
152/// is used only when that item has no `///` comment. Use `//` for developer notes.
153/// ## Inner markers
154/// ### pymethod/pyclassmethod/pystaticmethod
155/// `pymethod` is used to mark a method of the Python class.
156/// `pyclassmethod` is used to mark a class method.
157/// `pystaticmethod` is used to mark a static method.
158/// #### Method signature
159/// The first parameter can be either `&self` or `<var>: PyRef<Self>` for `pymethod`.
160/// The first parameter can be `cls: PyTypeRef` for `pyclassmethod`.
161/// There is no mandatory parameter for `pystaticmethod`.
162/// Both are valid and essentially the same, but the latter can yield more control.
163/// The last parameter can optionally be of the type `&VirtualMachine` to access the VM.
164/// All other values must implement `IntoPyResult`.
165/// Numeric types, `String`, `bool`, and `PyObjectRef` implement this trait,
166/// but so does any object that implements `PyValue`.
167/// Consider using `OptionalArg` for optional arguments.
168/// #### Arguments
169/// - `name`: the name of the method in Python,
170///   by default it is the same as the Rust method, or surrounded by double underscores if magic is present.
171///   This overrides `magic` and the default name and cannot be used with `magic` to prevent ambiguity.
172/// ### pygetset
173/// This is used to mark a getter/setter pair.
174/// #### Arguments
175/// - `setter`: marks the method as a setter, it acts as a getter by default.
176///   Setter method names should be prefixed with `set_`.
177/// - `name`: the name of the attribute in Python, by default it is the same as the Rust method.
178/// - `magic`: marks the method as a magic method: the method name is surrounded with double underscores.
179///   This cannot be used with `name` to prevent ambiguity.
180///
181/// Ensure both the getter and setter are marked with `name` and `magic` in the same manner.
182/// #### Examples
183/// ```rust, ignore
184/// #[pyclass]
185/// impl MyStruct {
186///    #[pygetset]
187///    fn x(&self) -> PyResult<i32> {
188///       Ok(self.x.lock())
189///     }
190///    #[pygetset(setter)]
191///   fn set_x(&mut self, value: i32) -> PyResult<()> {
192///      self.x.set(value);
193///     Ok(())
194///     }
195/// }
196/// ```
197/// ### pyslot
198/// This is used to mark a slot method it should be marked by prefixing the method in rust with `slot_`.
199/// #### Arguments
200/// - name: the name of the slot method.
201/// ### pyattr
202/// ### extend_class
203/// This helps inherit attributes from a parent class.
204/// The method this is applied on should be called `extend_class_with_fields`.
205/// #### Examples
206/// ```rust, ignore
207/// #[extend_class]
208/// fn extend_class_with_fields(ctx: &Context, class: &'static Py<PyType>) {
209///     class.set_attr(
210///         identifier!(ctx, _fields),
211///         ctx.new_tuple(vec![
212///             ctx.new_str(ascii!("body")).into(),
213///             ctx.new_str(ascii!("type_ignores")).into(),
214///         ])
215///         .into(),
216///     );
217///     class.set_attr(identifier!(ctx, _attributes), ctx.new_list(vec![]).into());
218/// }
219/// ```
220/// ### pymember
221/// Declares an offset member on a payload field. The struct `#[pyclass]` builds
222/// a `PyClassDef::MEMBERS` table and `extend_class` registers one
223/// `member_descriptor` per entry. The member kind is inferred from the field
224/// type: `bool` / `AtomicBool`, `u8` / `AtomicU8`, `i32` / `AtomicI32`, `u32` / `AtomicU32`,
225/// `isize` / `AtomicIsize`, `f64` / `AtomicF64`, or an object pointer
226/// (`PyObjectRef`, `PyRef<T>`, `Option` of those, `PyObjectCell`, `PyAtomicRef<PyObject>`,
227/// `PyAtomicRef<Option<PyObject>>`, `PyAtomicRef<Option<T>>`,
228/// `&'static Py<T>`, `&'static PyStrInterned`).
229///
230/// - `type`: only `"object_ex"`, on an object field or a struct-level offset
231///   member. `"object"`, `"bool"`, `"int"`, `"uint"`, `"double"`, and
232///   `"py_ssize_t"` are rejected because those kinds are inferred.
233/// - `writable`: accept stores. Members are readonly without it. A writable
234///   object member must be `PyAtomicRef<PyObject>` (never null) or
235///   `PyAtomicRef<Option<PyObject>>` (nullable), a writable bool must be
236///   `AtomicBool`, a writable unsigned byte must be `AtomicU8`, a writable int
237///   must be `AtomicI32`, a writable uint must be
238///   `AtomicU32`, a writable double must be `AtomicF64`, and a writable
239///   py_ssize_t must be `AtomicIsize`.
240/// - `audit_read`: audit `object.__getattr__` before the load.
241/// - `name`: Python attribute name. Defaults to the field name.
242/// - `path`: subfield of the annotated field (`value` with `path = "re"`).
243/// - `doc`: only `doc = false`, which stores no docstring. Otherwise the
244///   docstring is the field's `///` comment when present, and the stored
245///   attribute documentation when the field has no `///` comment. Every
246///   `#[pymember]` on one field shares that comment. A string `doc` is rejected.
247///
248/// A struct-level `#[pymember]` (after `#[pyclass]`) has no field. `offset` is
249/// required there and rejected on a field. `///` lines placed immediately above
250/// that attribute are its docstring; otherwise the stored attribute
251/// documentation is used. One field may carry several `#[pymember]` attributes.
252/// A `#[cfg]` on the field gates that entry.
253/// ```rust, ignore
254/// #[pymember(name = "fget")]
255/// getter: PyAtomicRef<Option<PyObject>>,
256/// ```
257/// # Trait
258/// `#[pyclass]` on traits functions a lot like `#[pyclass]` on `impl` blocks.
259/// Note that associated functions that are annotated with `#[pymethod]` or similar **must**
260/// have a body, abstract functions should be wrapped before applying an annotation.
261#[proc_macro_attribute]
262pub fn pyclass(attr: TokenStream, item: TokenStream) -> TokenStream {
263    let attr = parse_macro_input!(attr with Punctuated::parse_terminated);
264    let item = parse_macro_input!(item);
265    derive_impl::pyclass(attr, item).into()
266}
267
268/// Helper macro to define `Exception` types.
269/// More-or-less is an alias to `pyclass` macro.
270/// A `///` comment on the exception type is its docstring and overrides the
271/// stored documentation. Use `//` for developer notes.
272///
273/// This macro serves a goal of generating multiple
274/// `BaseException` / `Exception`
275/// subtypes in a uniform and convenient manner.
276/// It looks like `SimpleExtendsException` in `CPython`.
277/// <https://github.com/python/cpython/blob/main/Objects/exceptions.c>
278#[proc_macro_attribute]
279pub fn pyexception(attr: TokenStream, item: TokenStream) -> TokenStream {
280    let attr = parse_macro_input!(attr with Punctuated::parse_terminated);
281    let item = parse_macro_input!(item);
282    derive_impl::pyexception(attr, item).into()
283}
284
285/// This attribute must be applied to an inline module.
286/// It defines a Python module in the form of a `module_def` function in the module;
287/// this has to be used in a `add_native_module` to properly register the module.
288/// Additionally, this macro defines 'MODULE_NAME' and 'DOC' in the module.
289/// A `///` comment on the module is its docstring and overrides the stored
290/// module documentation. `#[pyfunction]` works the same way: its `///` comment
291/// is the function docstring and overrides the stored function documentation.
292/// Use `//` for developer notes.
293/// # Arguments
294/// - `name`: the name of the python module,
295///   by default, it is the name of the module, but this can be configured.
296/// ```rust, ignore
297/// // This will create a module named `my_module`
298/// #[pymodule(name = "my_module")]
299/// mod module {
300/// }
301/// ```
302/// - `sub`: declare the module as a submodule merged into another module.
303///   `name` is that Python module. Doc lookup uses it; without `name`, the
304///   doc DB is not consulted.
305/// ```rust, ignore
306/// #[pymodule(sub, name = "my_module")]
307/// mod submodule {
308/// }
309///
310/// #[pymodule(with(submodule))]
311/// mod my_module {
312/// }
313/// ```
314/// - `with`: declare the list of submodules that this module contains (see `sub` for example).
315/// ## Inner markers
316/// ### pyattr
317/// `pyattr` is a multipurpose marker that can be used in a pymodule.
318/// The most common use is to mark a function or class as a part of the module.
319/// This can be done by applying it to a function or struct prior to the `#[pyfunction]` or `#[pyclass]` macro.
320/// If applied to a constant, it will be added to the module as an attribute.
321/// If applied to a function not marked with `pyfunction`,
322/// it will also be added to the module as an attribute but the value is the result of the function.
323/// If `#[pyattr(once)]` is used in this case, the function will be called once
324/// and the result will be stored using a `static_cell`.
325/// #### Examples
326/// ```rust, ignore
327/// #[pymodule]
328/// mod my_module {
329///     #[pyattr]
330///     const MY_CONSTANT: i32 = 42;
331///     #[pyattr]
332///    fn another_constant() -> PyResult<i32> {
333///       Ok(42)
334///    }
335///   #[pyattr(once)]
336///   fn once() -> PyResult<i32> {
337///     // This will only be called once and the result will be stored.
338///     Ok(2 ** 24)
339///  }
340///
341///     #[pyattr]
342///     #[pyfunction]
343///     fn my_function(vm: &VirtualMachine) -> PyResult<()> {
344///         ...
345///     }
346/// }
347/// ```
348/// ### pyfunction
349/// This is used to create a python function.
350/// #### Function signature
351/// The last argument can optionally be of the type `&VirtualMachine` to access the VM.
352/// Refer to the `pymethod` documentation (located in the `pyclass` macro documentation)
353/// for more information on what regular argument types are permitted.
354/// #### Arguments
355/// - `name`: the name of the function in Python, by default it is the same as the associated Rust function.
356#[proc_macro_attribute]
357pub fn pymodule(attr: TokenStream, item: TokenStream) -> TokenStream {
358    let attr = parse_macro_input!(attr as derive_impl::PyModuleArgs);
359    let item = parse_macro_input!(item);
360    derive_impl::pymodule(attr, item).into()
361}
362
363/// Attribute macro for defining Python struct sequence types.
364///
365/// This macro is applied to an empty struct to create a Python type
366/// that wraps a Data struct. A `///` comment on that struct is the type's
367/// docstring and overrides the stored documentation. Use `//` for developer notes.
368///
369/// # Example
370/// ```ignore
371/// #[pystruct_sequence_data]
372/// struct StructTimeData {
373///     pub tm_year: PyObjectRef,
374///     #[pystruct_sequence(skip)]
375///     pub tm_gmtoff: PyObjectRef,
376/// }
377///
378/// #[pystruct_sequence(name = "struct_time", module = "time", data = "StructTimeData")]
379/// struct PyStructTime;
380/// ```
381#[proc_macro_attribute]
382pub fn pystruct_sequence(attr: TokenStream, item: TokenStream) -> TokenStream {
383    let attr = parse_macro_input!(attr with Punctuated::parse_terminated);
384    let item = parse_macro_input!(item);
385    derive_impl::pystruct_sequence(attr, item).into()
386}
387
388/// Attribute macro for struct sequence Data structs.
389///
390/// Generates field name constants, index constants, and `into_tuple()` method.
391///
392/// # Example
393/// ```ignore
394/// #[pystruct_sequence_data]
395/// struct StructTimeData {
396///     pub tm_year: PyObjectRef,
397///     pub tm_mon: PyObjectRef,
398///     #[pystruct_sequence(skip)]  // optional field, not included in tuple
399///     pub tm_gmtoff: PyObjectRef,
400/// }
401/// ```
402///
403/// # Options
404/// - `try_from_object`: Generate `try_from_elements()` method and `TryFromObject` impl
405///
406/// ```ignore
407/// #[pystruct_sequence_data(try_from_object)]
408/// struct StructTimeData { ... }
409/// ```
410#[proc_macro_attribute]
411pub fn pystruct_sequence_data(attr: TokenStream, item: TokenStream) -> TokenStream {
412    let attr = parse_macro_input!(attr with Punctuated::parse_terminated);
413    let item = parse_macro_input!(item);
414    derive_impl::pystruct_sequence_data(attr, item).into()
415}
416
417struct Compiler;
418impl derive_impl::Compiler for Compiler {
419    fn compile(
420        &self,
421        source: &str,
422        mode: rustpython_compiler::Mode,
423        module_name: &str,
424    ) -> Result<rustpython_compiler::CodeObject, Box<dyn core::error::Error>> {
425        use rustpython_compiler::{CompileOpts, compile};
426        Ok(compile(source, mode, module_name, CompileOpts::default())?)
427    }
428}
429
430#[proc_macro]
431pub fn py_compile(input: TokenStream) -> TokenStream {
432    derive_impl::py_compile(input.into(), &Compiler).into()
433}
434
435#[proc_macro]
436pub fn py_freeze(input: TokenStream) -> TokenStream {
437    derive_impl::py_freeze(input.into(), &Compiler).into()
438}
439
440#[proc_macro_derive(PyPayload)]
441pub fn pypayload(input: TokenStream) -> TokenStream {
442    let input = parse_macro_input!(input);
443    derive_impl::pypayload(input).into()
444}
445
446/// use on struct with named fields like `struct A{x:PyRef<B>, y:PyRef<C>}` to impl `Traverse` for datatype.
447///
448/// use `#[pytraverse(skip)]` on fields you wish not to trace
449///
450/// add `trace` attr to `#[pyclass]` to make it impl `MaybeTraverse` that will call `Traverse`'s `traverse` method so make it
451/// traceable(Even from type-erased PyObject)(i.e. write `#[pyclass(trace)]`).
452/// # Example
453/// ```rust, ignore
454/// #[pyclass(module = false, traverse)]
455/// #[derive(Default, Traverse)]
456/// pub struct PyList {
457///     elements: PyRwLock<Vec<PyObjectRef>>,
458///     #[pytraverse(skip)]
459///     len: AtomicCell<usize>,
460/// }
461/// ```
462/// This create both `MaybeTraverse` that call `Traverse`'s `traverse` method and `Traverse` that impl `Traverse`
463/// for `PyList` which call elements' `traverse` method and ignore `len` field.
464#[proc_macro_derive(Traverse, attributes(pytraverse))]
465pub fn pytraverse(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
466    let item = parse_macro_input!(item);
467    derive_impl::pytraverse(item).into()
468}