Skip to main content

pyclass

Attribute Macro pyclass 

Source
#[pyclass]
Expand description

The attribute can be applied either to a struct, trait, or impl.

§Struct

This implements MaybeTraverse, PyClassDef, and StaticType for the struct. Consider deriving Traverse to implement it.

§Arguments

  • module: the module which contains the class – can be omitted if in a #[pymodule].
  • name: the name of the Python class, by default it is the name of the struct.
  • base: the base class of the Python class. This does not cause inheritance of functions or attributes that must be done by a separate trait. The native payload must be a struct with the base as its first field. The macro adds repr(C) if no explicit representation is present and checks the base field offset, the payload offset in Py<T>, and object alignment.

§Impl

This part implements PyClassImpl for the struct. This includes methods, getters/setters, etc.; only annotated methods will be included. Common functions and abilities like instantiation and __call__ are often implemented by traits rather than in the impl itself; see Constructor and Callable respectively for those.

§Arguments

  • name: the name of the Python class, when no name is provided the struct name is used.
  • flags: the flags of the class, see PyTypeFlags.
    • BASETYPE: allows the class to be inheritable.
    • IMMUTABLETYPE: class attributes are immutable.
  • with: which trait implementations are to be included in the python class.
ⓘ
#[pyclass(module = "my_module", name = "MyClass", base = BaseClass)]
struct MyStruct {
   x: i32,
}

impl Constructor for MyStruct {
    ...
}

#[pyclass(with(Constructor))]
impl MyStruct {
   ...
}

§Docstrings

A /// doc comment on the struct, or on a #[pymethod] / #[pyclassmethod] / #[pystaticmethod] / #[pygetset] / #[pymember] item, is the Python docstring and overrides the stored documentation. The stored documentation is used only when that item has no /// comment. Use // for developer notes.

§Inner markers

§pymethod/pyclassmethod/pystaticmethod

pymethod is used to mark a method of the Python class. pyclassmethod is used to mark a class method. pystaticmethod is used to mark a static method.

§Method signature

The first parameter can be either &self or <var>: PyRef<Self> for pymethod. The first parameter can be cls: PyTypeRef for pyclassmethod. There is no mandatory parameter for pystaticmethod. Both are valid and essentially the same, but the latter can yield more control. The last parameter can optionally be of the type &VirtualMachine to access the VM. All other values must implement IntoPyResult. Numeric types, String, bool, and PyObjectRef implement this trait, but so does any object that implements PyValue. Consider using OptionalArg for optional arguments.

§Arguments
  • name: the name of the method in Python, by default it is the same as the Rust method, or surrounded by double underscores if magic is present. This overrides magic and the default name and cannot be used with magic to prevent ambiguity.

§pygetset

This is used to mark a getter/setter pair.

§Arguments
  • setter: marks the method as a setter, it acts as a getter by default. Setter method names should be prefixed with set_.
  • name: the name of the attribute in Python, by default it is the same as the Rust method.
  • magic: marks the method as a magic method: the method name is surrounded with double underscores. This cannot be used with name to prevent ambiguity.

Ensure both the getter and setter are marked with name and magic in the same manner.

§Examples
ⓘ
#[pyclass]
impl MyStruct {
   #[pygetset]
   fn x(&self) -> PyResult<i32> {
      Ok(self.x.lock())
    }
   #[pygetset(setter)]
  fn set_x(&mut self, value: i32) -> PyResult<()> {
     self.x.set(value);
    Ok(())
    }
}

§pyslot

This is used to mark a slot method it should be marked by prefixing the method in rust with slot_.

§Arguments
  • name: the name of the slot method.

§pyattr

§extend_class

This helps inherit attributes from a parent class. The method this is applied on should be called extend_class_with_fields.

§Examples
ⓘ
#[extend_class]
fn extend_class_with_fields(ctx: &Context, class: &'static Py<PyType>) {
    class.set_attr(
        identifier!(ctx, _fields),
        ctx.new_tuple(vec![
            ctx.new_str(ascii!("body")).into(),
            ctx.new_str(ascii!("type_ignores")).into(),
        ])
        .into(),
    );
    class.set_attr(identifier!(ctx, _attributes), ctx.new_list(vec![]).into());
}

§pymember

Declares an offset member on a payload field. The struct #[pyclass] builds a PyClassDef::MEMBERS table and extend_class registers one member_descriptor per entry. The member kind is inferred from the field type: bool / AtomicBool, u8 / AtomicU8, i32 / AtomicI32, u32 / AtomicU32, isize / AtomicIsize, f64 / AtomicF64, or an object pointer (PyObjectRef, PyRef<T>, Option of those, PyObjectCell, PyAtomicRef<PyObject>, PyAtomicRef<Option<PyObject>>, PyAtomicRef<Option<T>>, &'static Py<T>, &'static PyStrInterned).

  • type: only "object_ex", on an object field or a struct-level offset member. "object", "bool", "int", "uint", "double", and "py_ssize_t" are rejected because those kinds are inferred.
  • writable: accept stores. Members are readonly without it. A writable object member must be PyAtomicRef<PyObject> (never null) or PyAtomicRef<Option<PyObject>> (nullable), a writable bool must be AtomicBool, a writable unsigned byte must be AtomicU8, a writable int must be AtomicI32, a writable uint must be AtomicU32, a writable double must be AtomicF64, and a writable py_ssize_t must be AtomicIsize.
  • audit_read: audit object.__getattr__ before the load.
  • name: Python attribute name. Defaults to the field name.
  • path: subfield of the annotated field (value with path = "re").
  • doc: only doc = false, which stores no docstring. Otherwise the docstring is the field’s /// comment when present, and the stored attribute documentation when the field has no /// comment. Every #[pymember] on one field shares that comment. A string doc is rejected.

A struct-level #[pymember] (after #[pyclass]) has no field. offset is required there and rejected on a field. /// lines placed immediately above that attribute are its docstring; otherwise the stored attribute documentation is used. One field may carry several #[pymember] attributes. A #[cfg] on the field gates that entry.

ⓘ
#[pymember(name = "fget")]
getter: PyAtomicRef<Option<PyObject>>,

§Trait

#[pyclass] on traits functions a lot like #[pyclass] on impl blocks. Note that associated functions that are annotated with #[pymethod] or similar must have a body, abstract functions should be wrapped before applying an annotation.