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;