pyderive/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! This library provides derive macros of Python spacial methods and a class attributes for [PyO3].
4//!
5//! The field attribute `#[pyderive(..)]` helps to customize implementations,
6//! like [`dataclasses.field()`][dataclasses-field] of Python.
7//!
8//! It requires to enable `multiple-pymethods` feature of PyO3
9//! because the derive macros that this library provides may implement multiple `#[pymethods]`.
10//!
11//! [dataclasses-field]: https://docs.python.org/3/library/dataclasses.html#dataclasses.field
12//! [PyO3]: https://github.com/PyO3/pyo3
13//!
14//! # Example
15//!
16//! ```
17//! // Enable `multiple-pymethods` feature of PyO3
18//! use pyo3::prelude::*;
19//! use pyderive::*;
20//!
21//! // Place #[derive(PyNew, ...)] before #[pyclass]
22//! #[derive(PyNew, PyMatchArgs, PyRepr, PyEq)]
23//! #[pyclass(get_all)]
24//! #[derive(PartialEq, Hash)]
25//! struct MyClass {
26//! string: String,
27//! integer: i64,
28//! option: Option<i64>
29//! }
30//! ```
31//! ```python
32//! # Python script
33//! from rust_module import MyClass
34//!
35//!
36//! # Derives __new__()
37//! m = MyClass("a", 1, None)
38//!
39//! # Derives __match_args__ (supports Pattern Matching by positional arguments)
40//! match m:
41//! case MyClass(a, b, c):
42//! assert a == "a"
43//! assert b == 1
44//! assert c is None
45//! case _:
46//! raise AssertionError
47//!
48//! # Derives __repr__(), calls Python repr() recursively
49//! assert str(m) == "MyClass(string='a', integer=1, option=None)"
50//! assert repr(m) == "MyClass(string='a', integer=1, option=None)"
51//!
52//! # Derives __eq__() that depends on PartialEq trait
53//! assert m == MyClass("a", 1, None)
54//! ```
55//!
56//! # Detail
57//!
58//! Some macros change implementations depend on `#[pyclass(..)]` and `#[pyo3(..)]` arguments,
59//! hence it should place `#[derive(PyNew)]` etc. before `#[pyclass(..)]` and `#[pyo3(..)]`.
60//!
61//! We list the default implementations that the macros generate.
62//!
63//! | Derive Macro | Derives |
64//! | --------------------- | ---------------------------------------------------- |
65//! | [`PyNew`] | `__new__()` with all fields |
66//! | [`PyMatchArgs`] | `__match_args__` class attr. with `get` fields |
67//! | [`PyRepr`] | `__repr__()` returns `get` and `set` fields |
68//! | [`PyStr`] | `__str__()` returns `get` and `set` fields |
69//! | [`PyIter`] | `__iter__()` returns an iterator of `get` fields |
70//! | [`PyReversed`] | `__reversed__()` returns an iterator of `get` fields |
71//! | [`PyLen`] | `__len__()` returns number of `get` fields |
72//! | [`PyDataclassFields`] | `__dataclass_fields__` class attr. with all fields |
73//!
74//! Notes, methods implemented by [`PyRepr`] and [`PyStr`] are recursively calls `repr()` or `str()` like a Python `dataclass`.
75//!
76//! We call the field is *`get` (or `set`) field*
77//! if the field has a `#[pyclass/pyo3(get)]` (or `#[pyclass/pyo3(set)]`) attribute or
78//! its struct has a `#[pyclass/pyo3(get_all)]` (or `#[pyclass/pyo3(set_all)]`) attribute.
79//!
80//! The following derive macros depend on traits.
81//!
82//! | Derive Macro | Derives |
83//! | --------------- | -------------------------------------------------------------------------------------------------- |
84//! | [`PyEq`] | `__eq__()` and `__ne__()`, depends on [`PartialEq`] |
85//! | [`PyOrd`] | `__lt__()`, `__le__()`, `__gt__()` and `__ge__()`, depend on [`PartialOrd`] |
86//! | [`PyRichCmp`] | `==`, `!=`, `>`, `>=`, `<` and `<=` by `__richcmp__()`, depend on [`PartialEq`] and [`PartialOrd`] |
87//! | [`PyNumeric`] | Numeric op traits (`__add__()` etc.) |
88//! | [`PyBitwise`] | Bitwise op traits (`__and__()` etc.) |
89//!
90//! Notes, implementation of [`PyEq`] and [`PyOrd`] does not use `__richcmp__()`.
91//!
92//! Module [`pyderive::ops`](mod@ops) and [`pyderive::convert`](mod@convert) provides
93//! derive macros that implement individual method that enumerating numeric type (`__add__()` etc.) and
94//! called by builtin functions (`__int__()` etc.).
95//!
96//! # Notes on `PyNamedTuple` family
97//!
98//! It experimentally provides the derive macors
99//! implement methods that the `namedtuple()` generates
100//!
101//! | Derive Macro | Derives |
102//! | ----------------------------- | ----------------------------- |
103//! | [`PyNamedTupleAsdict`] | `_asdit()` instance method |
104//! | [`PyNamedTupleFieldDefaults`] | `_field_defaults` class attr. |
105//! | [`PyNamedTupleFields`] | `_fields` class attr. |
106//! | [`PyNamedTupleMake`] | `_make()` class method |
107//! | [`PyNamedTupleReplace`] | `_replace()` instance method |
108//!
109//! It is designed to mimic `namedtuple()`.
110//! Thus, It may case unexpected behavior when you use the macros with non-`namedtuple()`-lish structs.
111//! For example, all fields should be in the constructor, the struct should be `get-all`,
112//! and once a field is decorated by `#[pyderive(default=...)]`, all subsequent fields should be too.
113//!
114//! [pyo3_IntoPy]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPy.html
115//! [pyo3_pyclass]: https://docs.rs/pyo3/latest/pyo3/attr.pyclass.html
116//!
117//! # Customize Implementation
118//!
119//! The field attributes `#[pyderive(..)]` is used to customize implementations
120//! produced by [pyderive](crate)'s derive.
121//!
122//! ```
123//! # use pyo3::prelude::*;
124//! use pyderive::*;
125//!
126//! #[derive(PyNew, PyRepr)]
127//! #[pyclass]
128//! struct MyClass {
129//! string: String,
130//! #[pyderive(repr=false)]
131//! #[pyo3(get)]
132//! integer: i64,
133//! #[pyderive(default=10)]
134//! option: Option<i64>
135//! }
136//! ```
137//!
138//! It allows to omit the right-hand side,
139//! and it evaluates to the right-hand as `true`
140//! except `default` , for example,
141//! `#[pyderive(repr)]` is equivalent to `#[pyderive(repr=true)]`.
142//!
143//! - `#[pyderive(repr=<bool>)]`
144//!
145//! If `repr=true`,
146//! the field is included in the string that the `__repr__()` method returns;
147//! if `repr=false`, it isn't.
148//!
149//! The derive macro [`PyDataclassFields`] reads this attribute also,
150//! see [`PyDataclassFields`] for detail.
151//!
152//! - `#[pyderive(str=<bool>)]`
153//!
154//! If `str=true`,
155//! the field is included in the string that the `__str__()` method returns;
156//! if `str=false`, it isn't.
157//!
158//! - `#[pyderive(new=<bool>)]`
159//!
160//! If `new=false`,
161//! the field is excluded from the arguments of the `__new__()` method.
162//! Notes, `new=true` has no effect.
163//!
164//! The derive macro [`PyDataclassFields`] and [`PyNamedTupleFieldDefaults`] read this attribute also,
165//! see [`PyDataclassFields`] and [`PyNamedTupleFieldDefaults`] for detail.
166//!
167//! - `#[pyderive(default=<expr>)]`
168//!
169//! This is used to customize default value for the `__new__()` method.
170//! It supports any rust expression which PyO3 supports, e.g.,
171//!
172//! ```
173//! # use pyderive::*;
174//! # use pyo3::prelude::*;
175//! #
176//! #[derive(PyNew)]
177//! #[pyclass]
178//! struct PyClass {
179//! #[pyderive(default = Some("str".to_string()))]
180//! field: Option<String>,
181//! }
182//! ```
183//!
184//! We note that this internally produces `#[pyo3(signature = ..)]` attribute.
185//!
186//! 1. No `#[pyderive(..)]` (for example, just `field: i64`)
187//!
188//! Pseudocode:
189//!
190//! ```python
191//! def __new__(cls, field):
192//! self = super().__new__(cls)
193//! self.field = field
194//! return self
195//! ```
196//!
197//! 2. `#[pyderive(new=false)]`
198//!
199//! The field is excluded from the arguments,
200//! and initialized by [`Default::default()`] in the `__new__()` method.
201//! We note that it is evaluated on every `__new__()` call.
202//!
203//! Pseudocode:
204//!
205//! ```python
206//! def __new__(cls):
207//! self = super().__new__(cls)
208//! self.field = field::default() # call rust fn
209//! return self
210//! ```
211//!
212//! 3. `#[pyderive(default=<expr>)]`
213//!
214//! The field is included to the arguments with default value `<expr>`.
215//! We note that `<expr>` (rust code) is evaluated on every `__new__()` call (PyO3 feature).
216//!
217//! Pseudocode:
218//!
219//! ```python
220//! def __new__(cls, field=<expr>):
221//! self = super().__new__(cls)
222//! self.field = field
223//! return self
224//! ```
225//!
226//! 4. `#[pyderive(new=false, default=<expr>)]`
227//!
228//! The field is excluded from the arguments,
229//! and initialized with `<expr>` in the `__new__()` method.
230//! We note that `<expr>` (rust code) is evaluated on every `__new__()` call.
231//!
232//! Pseudocode:
233//!
234//! ```python
235//! def __new__(cls):
236//! self = super().__new__(cls)
237//! self.field = <expr>
238//! return self
239//! ```
240//!
241//! The derive macro [`PyDataclassFields`] and [`PyNamedTupleFieldDefaults`] read this attribute also,
242//! see [`PyDataclassFields`] and [`PyNamedTupleFieldDefaults`] for detail.
243//!
244//! - `#[pyderive(default_factory=true)]`
245//!
246//! If `default_factory=true`,
247//! let the `default_factory` attribute of `Field`obj be `lambda: <expr>`,
248//! and let the `default` attribute be [`dataclasses.MISSING`][MISSING],
249//! where `<expr>` is given by `#[pyderive(default=<expr>)]`.
250//! Notes, `default_factory=false` has no effect,
251//! If the field is not marked by `#[pyderive(default=<expr>)]`, this ignores.
252//!
253//! See [`PyDataclassFields`] for detail.
254//!
255//! - `#[pyderive(kw_only=true)]`
256//!
257//! If `kw_only=true`,
258//! the following fields are keyword only arguments in the `__new__()` method,
259//! like [`*`][keyword-only-arguments] and [`dataclasses.KW_ONLY`][KW_ONLY].
260//! Note, `kw_only=false` has no effect.
261//!
262//! The derive macro [`PyDataclassFields`] reads this attribute also,
263//! see [`PyDataclassFields`] for detail.
264//!
265//! - `#[pyderive(match_args=<bool>)]`
266//!
267//! If `match_args=true`,
268//! the field is included in the `__match_args__` class attribute;
269//! if `match_args=false`, it isn't.
270//!
271//! We note that, as far as I know,
272//! the field must be accessible on the pattern matching.
273//! For example,
274//! pattern matching does *not* work with *not `get` field without a getter*
275//! (even if `match_args=true`), but it does work if the field has a getter.
276//!
277//! - `#[pyderive(iter=<bool>)]`
278//!
279//! If `iter=true`,
280//! the field is included in the iterator that `__iter__()` and `__reversed__()` return;
281//! if `iter=false`, it isn't.
282//!
283//! - `#[pyderive(len=<bool>)]`
284//!
285//! If `len=true`,
286//! the field is counted by the `__len__()`;
287//! if `len=false`, it isn't.
288//!
289//! - `#[pyderive(dataclass_field=false)]`
290//!
291//! If `dataclass_field=false`,
292//! the field is excluded from the `__dataclass_fields__` dict.
293//! Notes, `dataclass_field=true` has no effect.
294//!
295//! See [`PyDataclassFields`] for detail.
296//!
297//! - `#[pyderive(annotation=<str>)]`
298//!
299//! The derive macro [`PyDataclassFields`] reads this attribute,
300//! see [`PyDataclassFields`] for detail.
301//!
302//! [keyword-only-arguments]: https://docs.python.org/3/tutorial/controlflow.html#keyword-only-arguments
303//! [KW_ONLY]: https://docs.python.org/3/library/dataclasses.html#dataclasses.KW_ONLY
304//! [MISSING]: https://docs.python.org/3/library/dataclasses.html#dataclasses.MISSING
305
306pub mod convert;
307pub mod ops;
308
309/// Derive macro generating a `__dataclass_fields__` fn/Python class attribute.
310///
311/// It returns a [`dataclasses.Field`][Field] dict that helper functions of the [dataclasses] module read.
312/// It supports [`is_dataclass()`][is_dataclass], [`fields()`][fields],
313/// [`asdict()`][asdict] (include nest), [`astuple()`][astuple] (include nest)
314/// and [`replace()`][replace] of the dataclasses module.
315///
316/// The resulting dict contains all fields as default.
317///
318/// If the filed is marked by `#[pyderive(dataclass_field=false)]` attribute,
319/// the field is excluded from the dict that `__dataclass_fields__` returns.
320/// Notes, `dataclass_field=true` has no effect.
321///
322/// - It should place `#[derive(PyDataclassField)]` before `#[pyclass]`.
323/// - All fields in the arguments of the `__new__()` method should be `get` field, like `dataclass` does.
324/// - It requires [`IntoPyObject`][pyo3_IntoPyObject] trait for fields.
325///
326/// This does not generate other fn/method,
327/// use [`PyNew`] etc. to implement `__new__()` etc.
328///
329/// [pyo3_IntoPyObject]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPyObject.html
330/// [pyo3_pyclass]: https://docs.rs/pyo3/latest/pyo3/attr.pyclass.html
331///
332/// # Example
333///
334/// ```
335/// use pyo3::{prelude::*, py_run};
336/// use pyderive::*;
337///
338/// // Place before `#[pyclass]`
339/// #[derive(PyNew, PyDataclassFields)]
340/// #[pyclass(get_all)]
341/// struct PyClass {
342/// string: String,
343/// integer: i64,
344/// float: f64,
345/// tuple: (String, i64, f64),
346/// option: Option<String>,
347/// #[pyderive(dataclass_field=false)]
348/// excluded: String,
349/// }
350///
351/// Python::attach(|py| -> PyResult<()> {
352/// let a = Py::new(py, PyClass {
353/// string: "s".to_string(),
354/// integer: 1,
355/// float: 1.0,
356/// tuple: ("s".to_string(), 1, 1.0),
357/// option: None,
358/// excluded: "s".to_string(),
359/// })?;
360///
361/// let test = "
362/// from dataclasses import is_dataclass, asdict, astuple
363///
364/// assert is_dataclass(a) is True
365/// assert asdict(a) == {'string': 's', 'integer': 1, 'float': 1.0, 'tuple': ('s', 1, 1.0), 'option': None}
366/// assert astuple(a) == ('s', 1, 1.0, ('s', 1, 1.0), None)
367/// ";
368/// py_run!(py, a, test);
369///
370/// Ok(())
371/// });
372/// ```
373///
374/// # Implementation Notes
375///
376/// | `dataclasses.Field` Attribute | Compatibility |
377/// | ----------------------------- | ---------------------------------- |
378/// | `name` | ✅ |
379/// | `type` | ❌ (✅ if `annotation` given) |
380/// | `default` | ✅ (`<expr>` or `MISSING`) |
381/// | `default_factory` | ✅ (`lambda: <expr>` or `MISSING`) |
382/// | `new` | ✅ |
383/// | `repr` | ✅ |
384/// | `hash` | ❌ (`None` for pyderive) |
385/// | `compare` | ❌ (`None` for pyderive) |
386/// | `metadata` | ✅ (empty for pyderive) |
387/// | `kw_only` | ✅ |
388///
389/// 1. The `type` attribute of `Field` is `None` as default.
390/// If the field is marked by `#[pyderive(annotation=<type>)]`,
391/// this uses the given `<type>` as `type` attribute.
392/// 2. If the field is marked by `#[pyderive(default_factory=true)]`,
393/// the `default` attribute of the resulting `Field` obj is [`MISSING`][MISSING]
394/// and the `default_factory` is `lambda: <expr>`.
395/// Notes, it evaluates `<expr>` on every `Field.default_factory` call.
396///
397/// | Rust Field Attribute | Python `default` Attribute | Python `default_factory` Attribute |
398/// | ----------------------------------- | -------------------------- | ---------------------------------- |
399/// | `#[pyderive(default_factory=true)]` | `MISSING` | `lambda: <expr>` |
400/// | Other | `<expr>` | `MISSING` |
401/// 3. Attributes `hash` and `compare` are `None`.
402/// 4. This marks `new=false` field as a [`ClassVar` field][dataclass_ClassVar].
403///
404/// | Field Attribute | Result |
405/// | ---------------------- | -------------------------------------- |
406/// |`new=true` (default) | Dataclass field |
407/// |`new=false` | [`ClassVar` field][dataclass_ClassVar] |
408/// |`dataclass_field=false` | Exclude from `__dataclass_fields__` |
409/// 5. The [PEP 487][PEP487] ([`__set_name__()`][set_name] hook) is not supported
410/// (The default value of `__dataclass_fields__` is a different object
411/// from `__new__()`'s one, that is, they have different object IDs.
412/// This calls `__set_name__()` of `__dataclass_fields__` only,
413/// but not `__new__()`'s one).
414///
415/// [dataclasses]: https://docs.python.org/3/library/dataclasses.html
416/// [dataclass]: https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass
417/// [Field]: https://docs.python.org/3/library/dataclasses.html#dataclasses.Field
418/// [fields]: https://docs.python.org/3/library/dataclasses.html#dataclasses.fields
419/// [asdict]: https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict
420/// [astuple]: https://docs.python.org/3/library/dataclasses.html#dataclasses.astuple
421/// [replace]: https://docs.python.org/3/library/dataclasses.html#dataclasses.replace
422/// [is_dataclass]: https://docs.python.org/3/library/dataclasses.html#dataclasses.is_dataclass
423/// [ClassVar]: https://docs.python.org/3/library/typing.html#typing.ClassVar
424/// [dataclass_ClassVar]: https://docs.python.org/3/library/dataclasses.html#class-variables
425/// [MISSING]: https://docs.python.org/3/library/dataclasses.html#dataclasses.MISSING
426/// [PEP487]: https://peps.python.org/pep-0487/
427/// [set_name]: https://docs.python.org/3/reference/datamodel.html#object.__set_name__
428pub use pyderive_macros::PyDataclassFields;
429/// Derive macro generating a [`__eq__()`][__eq__] and [`__ne__()`][__ne__] fn/Python methods.
430///
431/// The implementation requires [`PartialEq`] impl.
432///
433/// *Note that implementing `__eq__()` and `__ne__()` methods will cause
434/// Python not to generate a default `__hash__()` implementation,
435/// so consider also implementing `__hash__()`.*
436///
437/// # Expansion
438///
439/// This implements, for example;
440///
441/// ```
442/// # use pyo3::prelude::*;
443/// # #[pyclass]
444/// # #[derive(PartialEq)]
445/// # struct PyClass {}
446/// #[pymethods]
447/// impl PyClass {
448/// pub fn __eq__(&self, other: &Self) -> bool {
449/// self.eq(other)
450/// }
451/// pub fn __ne__(&self, other: &Self) -> bool {
452/// self.ne(other)
453/// }
454/// }
455/// ```
456///
457/// [__eq__]: https://docs.python.org/reference/datamodel.html#object.__eq__
458/// [__ne__]: https://docs.python.org/reference/datamodel.html#object.__ne__
459///
460/// # Example
461///
462/// ```
463/// use pyo3::{prelude::*, py_run};
464/// use pyderive::*;
465///
466/// #[derive(PyEq)]
467/// #[pyclass]
468/// #[derive(PartialEq)]
469/// struct PyClass {
470/// field: f64,
471/// }
472///
473/// Python::attach(|py| -> PyResult<()> {
474/// let a = Py::new(py, PyClass { field: 0.0 })?;
475/// let b = Py::new(py, PyClass { field: 1.0 })?;
476/// let c = Py::new(py, PyClass { field: f64::NAN })?;
477///
478/// py_run!(py, a b, "assert a == a");
479/// py_run!(py, a b, "assert a != b");
480/// py_run!(py, c, "assert c != c");
481/// py_run!(py, a, "assert a != 1");
482///
483/// Ok(())
484/// });
485/// ```
486pub use pyderive_macros::PyEq;
487
488/// Derive macro generating a [`__iter__()`][__iter__] fn/Python method.
489///
490/// It returns an iterator of `get` fields as default,
491/// in the order of declaration.
492///
493/// If the filed is marked by `#[pyderive(iter=true)]` attribute,
494/// the field is included to the iterator that `__iter__()` returns;
495/// if `#[pyderive(iter=false)]`, it isn't.
496///
497/// - It should place `#[derive(PyIter)]` before `#[pyclass]`.
498/// - It requires [`IntoPyObject`][pyo3_IntoPyObject] trait for fields.
499/// - Calling `__next__()` is thread-safe, it raises `PyRuntimeError` when it fails to take a lock.
500///
501/// [pyo3_IntoPyObject]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPyObject.html
502/// [pyo3_pyclass]: https://docs.rs/pyo3/latest/pyo3/attr.pyclass.html
503/// [__iter__]: https://docs.python.org/reference/datamodel.html#object.__iter__
504///
505/// # Example
506///
507/// ```
508/// use pyo3::{prelude::*, py_run};
509/// use pyderive::*;
510///
511/// // Place before `#[pyclass]`
512/// #[derive(PyIter)]
513/// #[pyclass(get_all)]
514/// struct PyClass {
515/// string: String,
516/// integer: i64,
517/// float: f64,
518/// tuple: (String, i64, f64),
519/// option: Option<String>,
520/// #[pyderive(iter=false)]
521/// excluded: String,
522/// }
523///
524/// Python::attach(|py| -> PyResult<()> {
525/// let a = Py::new(py, PyClass {
526/// string: "s".to_string(),
527/// integer: 1,
528/// float: 1.0,
529/// tuple: ("s".to_string(), 1, 1.0),
530/// option: None,
531/// excluded: "excluded".to_string(),
532/// })?;
533///
534/// py_run!(py, a, "assert tuple(a) == ('s', 1, 1.0, ('s', 1, 1.0), None)");
535///
536/// Ok(())
537/// });
538/// ```
539pub use pyderive_macros::PyIter;
540/// Derive macro generating a [`__len__()`][__len__] fn/Python method.
541///
542/// That returns number of `get` fields as default.
543///
544/// If the filed is marked by `#[pyderive(len=true)]` attribute,
545/// the field is counted by the `__len__()`; if `#[pyderive(len=false)]`, it isn't.
546///
547/// - It should place `#[derive(PyLen)]` before `#[pyclass]`.
548///
549/// [__len__]: https://docs.python.org/reference/datamodel.html#object.__len__
550///
551/// # Example
552///
553/// ```
554/// use pyo3::{prelude::*, py_run};
555/// use pyderive::*;
556///
557/// // Place before `#[pyclass]`
558/// #[derive(PyLen)]
559/// #[pyclass(get_all)]
560/// struct PyClass {
561/// string: String,
562/// integer: i64,
563/// float: f64,
564/// tuple: (String, i64, f64),
565/// option: Option<String>,
566/// #[pyderive(len=false)]
567/// excluded: String,
568/// }
569///
570/// Python::attach(|py| -> PyResult<()> {
571/// let a = Py::new(py, PyClass {
572/// string: "s".to_string(),
573/// integer: 1,
574/// float: 1.0,
575/// tuple: ("s".to_string(), 1, 1.0),
576/// option: None,
577/// excluded: "excluded".to_string(),
578/// })?;
579///
580/// py_run!(py, a, "assert len(a) == 5");
581///
582/// Ok(())
583/// });
584/// ```
585pub use pyderive_macros::PyLen;
586/// Derive macro generating a [`__match_args__`][__match_args__] const/Python class attribute.
587///
588/// It contains `get` fields as default,
589/// in the order of declaration.
590///
591/// If the filed is marked by `#[pyderive(match_args=true)]` attribute,
592/// the field is included to the `__match_args__`;
593/// if `#[pyderive(match_args=false)]`, it isn't.
594///
595/// - It should place `#[derive(PyMatchArgs)]` before `#[pyclass]`.
596///
597/// [__match_args__]: https://docs.python.org/reference/datamodel.html#object.__match_args__
598///
599/// # Example
600///
601/// ```
602/// use pyo3::{prelude::*, py_run};
603/// use pyderive::*;
604///
605/// // Place before `#[pyclass]`
606/// #[derive(PyNew, PyMatchArgs)]
607/// #[pyclass(get_all)]
608/// struct PyClass {
609/// string: String,
610/// integer: i64,
611/// float: f64,
612/// tuple: (String, i64, f64),
613/// option: Option<String>,
614/// #[pyderive(match_args=false)]
615/// excluded: String,
616/// }
617///
618/// let test = "
619/// match PyClass('s', 1, 1.0, ('s', 1, 1.0), None, 's'):
620/// case PyClass(a, b, c, d, e):
621/// assert a == 's'
622/// assert b == 1
623/// assert c == 1.0
624/// assert d == ('s', 1, 1.0)
625/// assert e is None
626/// case _:
627/// raise AssertionError
628/// ";
629///
630/// Python::attach(|py| {
631/// if py.version_info() >= (3, 10) {
632/// let PyClass = py.get_type::<PyClass>();
633///
634/// py_run!(py, PyClass, test)
635/// }
636/// });
637/// ```
638pub use pyderive_macros::PyMatchArgs;
639/// Derive macro generating a [`__new__()`][__new__] Python method.
640///
641/// It has all fields as the arguments as default,
642/// in the order of declaration.
643///
644/// If the filed is marked by `#[pyderive(new=false)]` attribute,
645/// the field is excluded from the arguments of the `__new__()` method.
646/// Notes, `new=true` has no effect.
647///
648/// - It should place `#[derive(PyNew)]` before `#[pyclass]`.
649///
650/// See the [Customize Implementation](crate) section of the crate doc for detail.
651///
652/// [__new__]: https://docs.python.org/reference/datamodel.html#object.__new__
653///
654/// # Example
655///
656/// ```
657/// use pyo3::{prelude::*, py_run};
658/// use pyderive::*;
659///
660/// // Place before `#[pyclass]`
661/// #[derive(PyNew)]
662/// #[pyclass(get_all)]
663/// struct PyClass {
664/// string: String,
665/// integer: i64,
666/// float: f64,
667/// tuple: (String, i64, f64),
668/// option: Option<String>,
669/// #[pyderive(new=false)]
670/// excluded: String,
671/// }
672///
673/// let test = "
674/// a = PyClass('s', 1, 1.0, ('s', 1, 1.0), None)
675/// assert a.string == 's'
676/// assert a.integer == 1
677/// assert a.float == 1.0
678/// assert a.tuple == ('s', 1, 1.0)
679/// assert a.option is None
680/// assert a.excluded == ''
681/// ";
682///
683/// Python::attach(|py| {
684/// let PyClass = py.get_type::<PyClass>();
685///
686/// py_run!(py, PyClass, test)
687/// });
688/// ```
689pub use pyderive_macros::PyNew;
690/// Derive macro generating [`__lt__()`][__lt__], [`__le__()`][__le__], [`__gt__()`][__gt__] and [`__ge__()`][__ge__] fn/Python methods.
691///
692/// The implementation requires [`PartialOrd`] impl.
693///
694/// <section class="warning">
695/// PyO3 supports <code>#[pyclass(ord)]</code> since 0.22.
696/// </section>
697///
698/// The generated methods return `False` when [`PartialOrd::partial_cmp`] returns [`None`].
699///
700/// *Note that implementing `__lt__()`, `__le__()`, `__gt__()` and `__ge__()` methods
701/// will cause Python not to generate a default `__hash__()` implementation,
702/// so consider also implementing `__hash__()`.*
703///
704/// # Expansion
705///
706/// This implements, for example;
707///
708/// ```
709/// # use std::cmp::Ordering;
710/// # use pyo3::prelude::*;
711/// # #[pyclass]
712/// # #[derive(PartialOrd, PartialEq)]
713/// # struct PyClass {}
714/// #[pymethods]
715/// impl PyClass {
716/// pub fn __lt__(&self, other: &Self) -> bool {
717/// matches!(self.partial_cmp(other), Some(Ordering::Less))
718/// }
719/// // and __le__, __gt__ and __ge__
720/// }
721/// ```
722///
723/// [__lt__]: https://docs.python.org/reference/datamodel.html#object.__lt__
724/// [__le__]: https://docs.python.org/reference/datamodel.html#object.__le__
725/// [__gt__]: https://docs.python.org/reference/datamodel.html#object.__gt__
726/// [__ge__]: https://docs.python.org/reference/datamodel.html#object.__ge__
727///
728/// # Example
729///
730/// ```
731/// use pyo3::{prelude::*, py_run};
732/// use pyderive::*;
733///
734/// #[derive(PyOrd)]
735/// #[pyclass]
736/// #[derive(PartialOrd, PartialEq)]
737/// struct PyClass {
738/// field: f64,
739/// }
740///
741/// Python::attach(|py| -> PyResult<()> {
742/// let a = Py::new(py, PyClass { field: 0.0 })?;
743/// let b = Py::new(py, PyClass { field: 1.0 })?;
744/// let c = Py::new(py, PyClass { field: f64::NAN })?;
745///
746/// py_run!(py, a b, "assert a < b");
747/// py_run!(py, a b, "assert a <= b");
748/// py_run!(py, a b, "assert not a > b");
749/// py_run!(py, a b, "assert not a >= b");
750/// py_run!(py, c, "assert not c < c");
751///
752/// let test = "
753/// try:
754/// a < 1
755/// except TypeError:
756/// pass
757/// else:
758/// raise AssertionError
759/// ";
760/// py_run!(py, a, test);
761///
762/// Ok(())
763/// });
764/// ```
765pub use pyderive_macros::PyOrd;
766/// Derive macro generating a [`__repr__()`][__repr__] fn/Python method.
767///
768/// It returns the string that contains `get` and `set` fields as default,
769/// in the order of declaration.
770///
771/// If the filed is marked by `#[pyderive(repr=true)]` attribute,
772/// the field is included in the string that `__str__()` returns;
773/// if `#[pyderive(repr=false)]`, it isn't.
774///
775/// - It should place `#[derive(PyRepr)]` before `#[pyclass]`.
776/// - It requires [`IntoPyObject`][pyo3_IntoPyObject] trait for fields.
777/// - This recursively calls `repr()` like a dataclass.
778///
779/// [pyo3_IntoPyObject]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPyObject.html
780/// [pyo3_pyclass]: https://docs.rs/pyo3/latest/pyo3/attr.pyclass.html
781/// [__repr__]: https://docs.python.org/reference/datamodel.html#object.__repr__
782/// [repr]: https://docs.python.org/library/functions.html#repr
783///
784/// # Example
785///
786/// ```
787/// use pyo3::{prelude::*, py_run};
788/// use pyderive::*;
789///
790/// // Place before `#[pyclass]`
791/// #[derive(PyRepr)]
792/// #[pyclass(get_all)]
793/// struct PyClass {
794/// string: String,
795/// integer: i64,
796/// float: f64,
797/// tuple: (String, i64, f64),
798/// option: Option<String>,
799/// #[pyderive(repr=false)]
800/// excluded: String,
801/// }
802///
803/// Python::attach(|py| -> PyResult<()> {
804/// let a = Py::new(py, PyClass {
805/// string: "s".to_string(),
806/// integer: 1,
807/// float: 1.0,
808/// tuple: ("s".to_string(), 1, 1.0),
809/// option: None,
810/// excluded: "excluded".to_string(),
811/// })?;
812///
813/// py_run!(py, a, r#"assert repr(a) == "PyClass(string='s', integer=1, float=1.0, tuple=('s', 1, 1.0), option=None)""#);
814///
815/// Ok(())
816/// });
817/// ```
818pub use pyderive_macros::PyRepr;
819/// Derive macro generating a [`__reversed__()`][__reversed__] fn/Python method.
820///
821/// It returns an iterator of `get` fields as default,
822/// in the reverse order of declaration.
823///
824/// This is a reversed one of a derive macro, [`PyIter`].
825///
826/// If the filed is marked by `#[pyderive(iter=true)]` attribute,
827/// the field is included to the iterator that `__reversed__()` returns;
828/// if `#[pyderive(iter=false)]`, it isn't.
829///
830/// - It should place `#[derive(PyReversed)]` before `#[pyclass]`.
831/// - It requires [`IntoPyObject`][pyo3_IntoPyObject] trait for fields.
832/// - Calling `__next__()` is thread-safe, it raises `PyRuntimeError` when it fails to take a lock.
833///
834/// [pyo3_IntoPyObject]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPyObject.html
835/// [pyo3_pyclass]: https://docs.rs/pyo3/latest/pyo3/attr.pyclass.html
836/// [__reversed__]: https://docs.python.org/reference/datamodel.html#object.__reversed__
837///
838/// # Example
839///
840/// ```
841/// use pyo3::{prelude::*, py_run};
842/// use pyderive::*;
843///
844/// // Place before `#[pyclass]`
845/// #[derive(PyReversed)]
846/// #[pyclass(get_all)]
847/// struct PyClass {
848/// string: String,
849/// integer: i64,
850/// float: f64,
851/// tuple: (String, i64, f64),
852/// option: Option<String>,
853/// #[pyderive(iter=false)]
854/// excluded: String,
855/// }
856///
857/// Python::attach(|py| -> PyResult<()> {
858/// let a = Py::new(py, PyClass {
859/// string: "s".to_string(),
860/// integer: 1,
861/// float: 1.0,
862/// tuple: ("s".to_string(), 1, 1.0),
863/// option: None,
864/// excluded: "excluded".to_string(),
865/// })?;
866///
867/// py_run!(py, a, "assert tuple(reversed(a)) == (None, ('s', 1, 1.0), 1.0, 1, 's')");
868///
869/// Ok(())
870/// });
871/// ```
872pub use pyderive_macros::PyReversed;
873/// Derive macro generating `__richcmp__` fn that provides Python comparison operations (`==`, `!=`, `<`, `<=`, `>`, and `>=`).
874///
875/// The implementation requires [`PartialEq`] and [`PartialOrd`] impl.
876///
877/// <section class="warning">
878/// PyO3 supports <code>#[pyclass(ord)]</code> since 0.22, it is recommended to use it.
879/// </section>
880///
881/// The generated methods return `False` when [`PartialOrd::partial_cmp`] returns [`None`].
882///
883/// *Note that implementing `__richcmp__` will cause Python not to generate
884/// a default `__hash__` implementation, so consider implementing `__hash__`
885/// when implementing `__richcmp__`.*
886///
887/// # Expansion
888///
889/// This implements, for example;
890///
891/// ```
892/// # use std::cmp::Ordering;
893/// # use pyo3::prelude::*;
894/// # use pyo3::pyclass::CompareOp;
895/// # #[pyclass]
896/// # #[derive(PartialOrd, PartialEq)]
897/// # struct PyClass {}
898/// #[pymethods]
899/// impl PyClass {
900/// pub fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
901/// match op {
902/// CompareOp::Eq => self.eq(other),
903/// CompareOp::Ne => self.ne(other),
904/// CompareOp::Lt => matches!(self.partial_cmp(other), Some(Ordering::Less)),
905/// CompareOp::Le => matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal)),
906/// CompareOp::Gt => matches!(self.partial_cmp(other), Some(Ordering::Greater)),
907/// CompareOp::Ge => matches!(self.partial_cmp(other), Some(Ordering::Greater | Ordering::Equal))
908/// }
909/// }
910/// }
911/// ```
912///
913/// # Example
914///
915/// ```
916/// use pyo3::{prelude::*, py_run};
917/// use pyderive::*;
918///
919/// #[derive(PyRichCmp)]
920/// #[pyclass]
921/// #[derive(PartialOrd, PartialEq)]
922/// struct PyClass {
923/// field: f64,
924/// }
925///
926/// Python::attach(|py| -> PyResult<()> {
927/// let a = Py::new(py, PyClass { field: 0.0 })?;
928/// let b = Py::new(py, PyClass { field: 1.0 })?;
929/// let c = Py::new(py, PyClass { field: f64::NAN })?;
930///
931/// py_run!(py, a b, "assert a == a");
932/// py_run!(py, a b, "assert a != b");
933/// py_run!(py, a b, "assert a < b");
934/// py_run!(py, a b, "assert a <= b");
935/// py_run!(py, a b, "assert not a > b");
936/// py_run!(py, a b, "assert not a >= b");
937/// py_run!(py, c, "assert not c < c");
938///
939/// let test = "
940/// try:
941/// a < 1
942/// except TypeError:
943/// pass
944/// else:
945/// raise AssertionError
946/// ";
947/// py_run!(py, a, test);
948///
949/// Ok(())
950/// });
951/// ```
952pub use pyderive_macros::PyRichCmp;
953/// Derive macro generating a [`__str__()`][__str__] fn/Python method.
954///
955/// It returns the string that contains `get` and `set` fields as default,
956/// in the order of declaration.
957///
958/// If the filed is marked by `#[pyderive(str=true)]` attribute,
959/// the field is included in the string that `__str__()` returns;
960/// if `#[pyderive(str=false)]`, it isn't.
961///
962/// - It should place `#[derive(PyStr)]` before `#[pyclass]`.
963/// - It requires [`IntoPyObject`][pyo3_IntoPyObject] trait for fields.
964/// - recursively calls `str()` like a dataclass.
965///
966/// [pyo3_IntoPyObject]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPyObject.html
967/// [pyo3_pyclass]: https://docs.rs/pyo3/latest/pyo3/attr.pyclass.html
968/// [__str__]: https://docs.python.org/reference/datamodel.html#object.__str__
969/// [str]: https://docs.python.org/library/functions.html#str
970///
971/// # Example
972///
973/// ```
974/// use pyo3::{prelude::*, py_run};
975/// use pyderive::*;
976///
977/// // Place before `#[pyclass]`
978/// #[derive(PyStr)]
979/// #[pyclass(get_all)]
980/// struct PyClass {
981/// string: String,
982/// integer: i64,
983/// float: f64,
984/// tuple: (String, i64, f64),
985/// option: Option<String>,
986/// #[pyderive(str=false)]
987/// excluded: String,
988/// }
989///
990/// Python::attach(|py| -> PyResult<()> {
991/// let a = Py::new(py, PyClass {
992/// string: "s".to_string(),
993/// integer: 1,
994/// float: 1.0,
995/// tuple: ("s".to_string(), 1, 1.0),
996/// option: None,
997/// excluded: "excluded".to_string(),
998/// })?;
999///
1000/// py_run!(py, a, r#"assert str(a) == "PyClass(string='s', integer=1, float=1.0, tuple=('s', 1, 1.0), option=None)""#);
1001///
1002/// Ok(())
1003/// });
1004/// ```
1005pub use pyderive_macros::PyStr;
1006
1007/// Derive macro generating a [`_asdict()`][_asdict] fn/Python method.
1008///
1009/// It assumes all fields are `get` (e.g. `get_all`).
1010///
1011/// - It should place `#[derive(PyNamedTupleAsdict)]` before `#[pyclass]`.
1012/// - It requires [`IntoPyObject`][pyo3_IntoPyObject] trait for fields.
1013///
1014/// [pyo3_IntoPyObject]: https://docs.rs/pyo3/latest/pyo3/conversion/trait.IntoPyObject.html
1015/// [_asdict]: https://docs.python.org/3/library/collections.html#collections.somenamedtuple._asdict
1016///
1017/// # Implementation Note
1018///
1019/// This is experimental. Behavior may change in future releases.
1020///
1021/// # Example
1022///
1023/// ```
1024/// use pyo3::{prelude::*, py_run};
1025/// use pyderive::*;
1026///
1027/// // Place before `#[pyclass]`
1028/// #[derive(PyNamedTupleAsdict)]
1029/// #[pyclass(get_all)]
1030/// struct PyClass {
1031/// string: String,
1032/// integer: i64,
1033/// float: f64,
1034/// tuple: (String, i64, f64),
1035/// option: Option<String>,
1036/// }
1037///
1038/// Python::attach(|py| -> PyResult<()> {
1039/// let a = Py::new(py, PyClass {
1040/// string: "s".to_string(),
1041/// integer: 1,
1042/// float: 1.0,
1043/// tuple: ("s".to_string(), 1, 1.0),
1044/// option: None,
1045/// })?;
1046///
1047/// py_run!(py, a, r#"assert a._asdict() == {'string': 's', 'integer': 1, 'float': 1.0, 'tuple': ('s', 1, 1.0), 'option': None}"#);
1048///
1049/// Ok(())
1050/// });
1051/// ```
1052pub use pyderive_macros::PyNamedTupleAsdict;
1053
1054/// Derive macro generating a [`_field_defaults`][_field_defaults] fn/Python class attribute.
1055///
1056/// It assumes all fields are `get` (e.g. `get_all`).
1057///
1058/// It contains `get` fields with default values, and:
1059///
1060/// 1. `#[pyderive(defualt=xxx)]` field with value `xxx`
1061/// 2. `#[pyderive(new=false)]` field with value `Default::default()`
1062/// 3. `#[pyderive(new=false, defualt=xxx)]` field with value `xxx`
1063///
1064/// - It should place `#[derive(PyNamedTupleFieldDefaults)]` before `#[pyclass]`.
1065///
1066/// [_field_defaults]: https://docs.python.org/3/library/collections.html#collections.somenamedtuple._field_defaults
1067///
1068/// # Implementation Note
1069///
1070/// This is experimental. Behavior may change in future releases.
1071///
1072/// # Example
1073///
1074/// ```
1075/// use pyo3::{prelude::*, py_run};
1076/// use pyderive::*;
1077///
1078/// // Place before `#[pyclass]`
1079/// #[derive(PyNamedTupleFieldDefaults)]
1080/// #[pyclass(get_all)]
1081/// struct PyClass {
1082/// a: i64,
1083/// #[pyderive(default=1)]
1084/// b: i64,
1085/// #[pyderive(new=false)]
1086/// c: i64,
1087/// #[pyderive(new=false, default=2)]
1088/// d: i64,
1089/// }
1090///
1091/// Python::attach(|py| -> PyResult<()> {
1092/// let Class = py.get_type::<PyClass>();
1093///
1094/// py_run!(py, Class, r#"assert Class._field_defaults == {'b': 1, 'c': 0, 'd': 2}"#);
1095///
1096/// Ok(())
1097/// });
1098/// ```
1099pub use pyderive_macros::PyNamedTupleFieldDefaults;
1100
1101/// Derive macro generating a [`_fields`][_fields] fn/Python class attribute.
1102///
1103/// It assumes all fields are `get` (e.g. `get_all`).
1104///
1105/// - It should place `#[derive(PyNamedTupleFields)]` before `#[pyclass]`.
1106///
1107/// [_fields]: https://docs.python.org/3/library/collections.html#collections.somenamedtuple._fields
1108///
1109/// # Implementation Note
1110///
1111/// This is experimental. Behavior may change in future releases.
1112///
1113/// # Example
1114///
1115/// ```
1116/// use pyo3::{prelude::*, py_run};
1117/// use pyderive::*;
1118///
1119/// // Place before `#[pyclass]`
1120/// #[derive(PyNamedTupleFields)]
1121/// #[pyclass(get_all)]
1122/// struct PyClass {
1123/// string: String,
1124/// integer: i64,
1125/// float: f64,
1126/// tuple: (String, i64, f64),
1127/// }
1128///
1129/// Python::attach(|py| -> PyResult<()> {
1130/// let Class = py.get_type::<PyClass>();
1131///
1132/// py_run!(py, Class, r#"assert Class._fields == ('string', 'integer', 'float', 'tuple')"#);
1133///
1134/// Ok(())
1135/// });
1136/// ```
1137pub use pyderive_macros::PyNamedTupleFields;
1138
1139/// Derive macro generating a [`_make`][_make] fn/Python class method.
1140///
1141/// It constructs `Self` from the argument `iterable`, doesn't use any other value.
1142///
1143/// - It should place `#[derive(PyNamedTupleMake)]` before `#[pyclass]`.
1144///
1145/// [_make]: https://docs.python.org/3/library/collections.html#collections.somenamedtuple._make
1146///
1147/// # Implementation Note
1148///
1149/// This is experimental. Behavior may change in future releases.
1150///
1151/// # Example
1152///
1153/// ```
1154/// use pyo3::{prelude::*, py_run};
1155/// use pyderive::*;
1156///
1157/// // Place before `#[pyclass]`
1158/// #[derive(PyNamedTupleMake)]
1159/// #[pyclass(get_all)]
1160/// struct PyClass {
1161/// string: String,
1162/// integer: i64,
1163/// float: f64,
1164/// tuple: (String, i64, f64),
1165/// }
1166///
1167/// Python::attach(|py| -> PyResult<()> {
1168/// let Class = py.get_type::<PyClass>();
1169///
1170/// py_run!(py, Class, r#"
1171/// a = Class._make(['a', 1, 2.0, ('a', 1, 2.0)])
1172///
1173/// assert a.string == 'a'
1174/// assert a.integer == 1
1175/// assert a.float == 2.0
1176/// assert a.tuple == ('a', 1, 2.0)
1177/// "#);
1178///
1179/// Ok(())
1180/// });
1181/// ```
1182pub use pyderive_macros::PyNamedTupleMake;
1183
1184/// Derive macro generating a [`_replace`][_replace] fn/Python method.
1185///
1186/// It assumes all fields are `get` (e.g. `get_all`).
1187///
1188/// - It should place `#[derive(PyNamedTupleMake)]` before `#[pyclass]`.
1189/// - It requires [`Clone`] for non-`Py` field
1190///
1191/// [_replace]: https://docs.python.org/3/library/collections.html#collections.somenamedtuple._replace
1192///
1193/// # Implementation Note
1194///
1195/// This is experimental. Behavior may change in future releases.
1196///
1197/// # Example
1198///
1199/// ```
1200/// use pyo3::{prelude::*, py_run};
1201/// use pyderive::*;
1202///
1203/// // Place before `#[pyclass]`
1204/// #[derive(PyNamedTupleReplace)]
1205/// #[pyclass(get_all)]
1206/// struct PyClass {
1207/// string: String,
1208/// integer: i64,
1209/// float: f64,
1210/// tuple: (String, i64, f64),
1211/// }
1212///
1213/// Python::attach(|py| -> PyResult<()> {
1214/// let a = Py::new(py, PyClass {
1215/// string: "s".to_string(),
1216/// integer: 1,
1217/// float: 1.0,
1218/// tuple: ("s".to_string(), 1, 1.0),
1219/// })?;
1220///
1221/// py_run!(py, a, r#"b = a._replace(integer=2, tuple=("", 0, 0.0))
1222/// assert b.string == "s"
1223/// assert b.integer == 2
1224/// assert b.float == 1.0
1225/// assert b.tuple == ("", 0, 0.0)
1226/// "#);
1227///
1228/// Ok(())
1229/// });
1230/// ```
1231pub use pyderive_macros::PyNamedTupleReplace;
1232
1233/// Derive macro generating an impl of bitwise op methods/fns base on [std::ops] traits.
1234///
1235/// This derives;
1236///
1237/// | Python method | Required Trait |
1238/// |------------------------------|-----------------------------------|
1239/// | [`__invert__()`][__invert__] | `Not for &Class` |
1240/// | [`__and__()`][__and__] | `BitAnd<&Class> for &Class` |
1241/// | [`__or__()`][__or__] | `BitOr<&Class> for &Class` |
1242/// | [`__xor__()`][__xor__] | `BitXor<&Class> for &Class` |
1243/// | [`__iand__()`][__iand__] | `BitAndAssign<&Class> for &Class` |
1244/// | [`__ior__()`][__ior__] | `BitOrAssign<&Class> for &Class` |
1245/// | [`__ixor__()`][__ixor__] | `BitXorAssign<&Class> for &Class` |
1246///
1247/// [__invert__]: https://docs.python.org/3/reference/datamodel.html#object.__invert__
1248/// [__and__]: https://docs.python.org/3/reference/datamodel.html#object.__and__
1249/// [__or__]: https://docs.python.org/3/reference/datamodel.html#object.__or__
1250/// [__xor__]: https://docs.python.org/3/reference/datamodel.html#object.__xor__
1251/// [__iand__]: https://docs.python.org/3/reference/datamodel.html#object.__iand__
1252/// [__ior__]: https://docs.python.org/3/reference/datamodel.html#object.__ior__
1253/// [__ixor__]: https://docs.python.org/3/reference/datamodel.html#object.__ixor__
1254pub use pyderive_macros::PyBitwise;
1255/// Derive macro generating an impl of numeric op methods/fns base on [std::ops] traits.
1256///
1257/// This derives;
1258///
1259/// | Python method | Required Trait |
1260/// |----------------------------------|-----------------------------------------|
1261/// | [`__pos__()`][__pos__] | -- |
1262/// | [`__neg__()`][__neg__] | `Neg<&Class> for &Class` |
1263/// | [`__add__()`][__add__] | `Add<&Class> for &Class` |
1264/// | [`__sub__()`][__sub__] | `Sub<&Class> for &Class` |
1265/// | [`__mul__()`][__mul__] | `Mul<&Class> for &Class` |
1266/// | [`__truediv__()`][__truediv__] | `Div<&Class> for &Class` |
1267/// | [`__mod__()`][__mod__] | `Rem<&Class> for &Class` |
1268/// | [`__iadd__()`][__iadd__] | `AddAssign<&Class> for &Class` |
1269/// | [`__isub__()`][__isub__] | `SubAssign<&Class> for &Class` |
1270/// | [`__imul__()`][__imul__] | `MulAssign<&Class> for &Class` |
1271/// | [`__itruediv__()`][__itruediv__] | `DivAssign<&Class> for &Class` |
1272/// | [`__imod__()`][__imod__] | `RemAssign<&Class> for &Class` |
1273/// | [`__divmod__()`][__divmod__] | Same as `__truediv__()` and `__mod__()` |
1274///
1275/// [__pos__]: https://docs.python.org/3/reference/datamodel.html#object.__pos__
1276/// [__neg__]: https://docs.python.org/3/reference/datamodel.html#object.__neg__
1277/// [__add__]: https://docs.python.org/3/reference/datamodel.html#object.__add__
1278/// [__sub__]: https://docs.python.org/3/reference/datamodel.html#object.__sub__
1279/// [__mul__]: https://docs.python.org/3/reference/datamodel.html#object.__mul__
1280/// [__truediv__]: https://docs.python.org/3/reference/datamodel.html#object.__truediv__
1281/// [__mod__]: https://docs.python.org/3/reference/datamodel.html#object.__mod__
1282/// [__iadd__]: https://docs.python.org/3/reference/datamodel.html#object.__iadd__
1283/// [__isub__]: https://docs.python.org/3/reference/datamodel.html#object.__isub__
1284/// [__imul__]: https://docs.python.org/3/reference/datamodel.html#object.__imul__
1285/// [__itruediv__]: https://docs.python.org/3/reference/datamodel.html#object.__itruediv__
1286/// [__imod__]: https://docs.python.org/3/reference/datamodel.html#object.__imod__
1287/// [__divmod__]: https://docs.python.org/3/reference/datamodel.html#object.__divmod__
1288pub use pyderive_macros::PyNumeric;