Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
Ashwin Naren edited this pageFeb 21, 2025 ·11 revisions

RustPython has special attributes to support easy python object building.

pyfunction, pymethod

These attributes are very common for every code chunk. They eventually turn intobuiltin_function_or_method asFn(&VirtualMachine, FuncArgs) -> PyResult.

The common form looks like this:

#[pyfunction]pubfnascii(obj:PyObjectRef,vm:&VirtualMachine) ->PyResult<String>{let repr = vm.to_repr(&obj)?;let ascii =to_ascii(repr.as_str());Ok(ascii)}

Thevm paramter is just suffix. We add it as the last parameter unless we don't usevm at all - very rare case. It takes an objectobj asPyObjectRef, which is a general python object. It returnsPyResult<String>, which will turn intoPyResult<PyObjectRef> the same representation ofPyResult.

Every return value must be convertible toPyResult. This is defined asIntoPyResult trait. So any return value of them must implementIntoPyResult. It will bePyResult<PyObjectRef>,PyObjectRef and anyPyResult<T> when T implementsIntoPyObject. Practically we can list them like:

  • AnyT whenPyResult<T> is possible
  • PyObjectRef
  • PyResult<()> and() asNone
  • PyRef<T: PyValue> likePyIntRef,PyStrRef
  • T: PyValue likePyInt,PyStr
  • Numbers likeusize orf64 forPyInt andPyFloat
  • String forPyStr
  • And more types implementingIntoPyObject.

Just like the return type, parameters are also described in similar way.

fnmath_comb(n:PyIntRef,k:PyIntRef,vm:&VirtualMachine) ->PyResult<BigInt>{    ...}

For this function,n andk are defined asPyIntRef. This conversion is supported byTryFromObject trait. When any parameter type isT: TryFromObject instead ofPyObjectRef, it will call the conversion function and returnTypeError during the conversion. Practically, they arePyRef<T: PyValue> and a few more types like numbers and strings.

Note that all those conversions are done by runtime, which are identical as manual conversions when we call the conversion function manually fromPyObjectRef.

#[pyfunction] is used to create a free function.#[pymethod] is used to create a method which can be bound. So here can be usually another prefix parameterself forPyRef<T: PyValue> and&self forT: PyValue.

#[pyclass(...)]implPyStr{    ...#[pymethod(magic)]fncontains(&self,needle:PyStrRef) ->bool{self.value.contains(needle.as_str())}    ...}

These parameters are mostly treated just same as other parameter, especially whenSelf isPyRef<T>.&self forT is sometimes a bit different. The actual object forself is alwaysPyRef<Self>, but the features are limited when it is represented as just&Self. Then there will be a special pattern likezelf: PyRef<Self>.

...#[pymethod(magic)]fnmul(zelf:PyRef<Self>,value:isize,vm:&VirtualMachine) ->PyRef<Self>{        ...}...

This pattern is just same asself forPyRef<T>. So nothing special. Just a different notation.

pyclass

pyclass when applied to astruct and its' associatedimpl is used to create a python type in Rust side. For now, they essentially consists of 2 steps:

  1. A data type with#[pyclass] attribute.
  2. pyclass on animpl block gathering various stuff related to python attributes.

DirEntry type invm/src/stdlib/os.rs is a nice and compact example of small class.

#[pyclass(name)]#[derive(Debug)]structDirEntry{entry: fs::DirEntry,}#[pyclass]implDirEntry{        ...}

#[pyclass] onstruct and data type

The data type is rust-side payload for the type. For simple usage, check forPyInt andPyStr. There are even empty types likePyNone. For complex types,PyDict will be interesting.pyclass macro helps to implement a few traits with given attrs.

  • module:false for builtins and a string for others. This field will be automatically filled when defined in#[pymodule] macro.
  • name: The class name.
  • base: A rust type name of base class for inheritance. Mostly empty. SeePyBool for an example.
  • metaclass: A rust type name of metaclass when required. Mostly empty.

#[pyclass] onimpl and python attributes

This part is the most interesting part. Basically#[pyclass] collects python attributes. A class can contains#[pymethod],#[pyclassmethod],#[pygetset] and#[pyslot]. These attributes will be covered in next sections.

One of important feature of#[pyclass] is filling slots ofPyType. Typically - but not necessarily - a group of slots are defiend as a trait in RustPython.with(...) will collect python attributes from the traits. Additionallyflags set the type flags. SeePyStr andHashable for the slot traits. See alsoPyFunction andHAS_DICT for flags.

pymethod, pyclassmethod

This article already coveredpymethod withpyfunction.

pygetset

Sometimes it is required to expose attributes of a class#[pygetset] allows this to happen

#[pygetset]fnco_posonlyargcount(&self) ->usize{self.code.posonlyarg_countasusize}

If it is desirable to make the variable editable, consider returning andAtomicCell,RwLock, orMutex.

If this is not feasible, or if it is desired to run some checks when writing to the attribute, using#[pygetset] coupled with#[pygetset(setter)] allows for separate get and set functions.

#[pygetset]fnname(&self) ->PyStrRef{self.inner.name()}#[pygetset(setter)]fnset_name(&self,name:PyStrRef){self.inner.set_name(name)}

pyslot

slots provide fast path for a few frequently-accessed type attributes.#[pyslot] connects the annotated function to each slot. The function name must be same as slot name ortp_ prefixed slot name.

In RustPython, most of them are conventionally implemented through a trait.

  • Hashable:__hash__
  • Callable:__call__
  • Comparable:__eq__,__ne__,__ge__,__ge__,__le__,__lt__
  • Buffer:tp_as_buffer

...

Note: For now, non-zero-sized payload(#[pyclass]) withouttp_new slot will make payload error after creating the instance.

pymodule

Clone this wiki locally

[8]ページ先頭

©2009-2025 Movatter.jp