Merge pull request #770 from kngwyu/pycell

Rename PyClassShell with `PyCell` and do mutability checking
This commit is contained in:
Yuji Kanagawa 2020-03-02 14:56:51 +09:00 committed by GitHub
commit fee755adbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 1818 additions and 1195 deletions

View File

@ -16,14 +16,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* The implementation for `IntoPy<U> for T` where `U: FromPy<T>` is no longer specializable. Control the behavior of this via the implementation of `FromPy`. [#713](https://github.com/PyO3/pyo3/pull/713)
* Use `parking_lot::Mutex` instead of `spin::Mutex`. [#734](https://github.com/PyO3/pyo3/pull/734)
* Bumped minimum Rust version to `1.42.0-nightly 2020-01-21`. [#761](https://github.com/PyO3/pyo3/pull/761)
* `PyRef` and `PyRefMut` are renewed for `PyCell`. [#770](https://github.com/PyO3/pyo3/pull/770)
### Added
* `PyClass`, `PyClassShell`, `PyObjectLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683)
* `PyCell`, which has RefCell-like features. [#770](https://github.com/PyO3/pyo3/pull/770)
* `PyClass`, `PyLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683)
* Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716)
* `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730)
* `#[pyo3(get)]` and `#[pyo3(set)]` will now use the Rust doc-comment from the field for the Python property. [#755](https://github.com/PyO3/pyo3/pull/755)
* `#[setter]` functions may now take an argument of `Pyo3::Python`. [#760](https://github.com/PyO3/pyo3/pull/760)
* `PyTypeInfo::BaseLayout` and `PyClass::BaseNativeType`. [#770](https://github.com/PyO3/pyo3/pull/770)
* `PyDowncastImpl`. [#770](https://github.com/PyO3/pyo3/pull/770)
### Fixed
@ -35,9 +38,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Removed
* `PyRef`, `PyRefMut`, `PyRawObject`. [#683](https://github.com/PyO3/pyo3/pull/683)
* `PyRawObject`. [#683](https://github.com/PyO3/pyo3/pull/683)
* `PyNoArgsFunction`. [#741](https://github.com/PyO3/pyo3/pull/741)
* `initialize_type()`. To set the module name for a `#[pyclass]`, use the `module` argument to the macro. #[751](https://github.com/PyO3/pyo3/pull/751)
* `AsPyRef::as_mut/with/with_mut/into_py/into_mut_py`. [#770](https://github.com/PyO3/pyo3/pull/770)
* `PyTryFrom::try_from_mut/try_from_mut_exact/try_from_mut_unchecked`. [#770](https://github.com/PyO3/pyo3/pull/770)
* `Python::mut_from_owned_ptr/mut_from_borrowed_ptr`. [#770](https://github.com/PyO3/pyo3/pull/770)
* `ObjectProtocol::get_base/get_mut_base`. [#770](https://github.com/PyO3/pyo3/pull/770)
## [0.8.5]

View File

@ -22,6 +22,7 @@ Specifically, the following implementation is generated:
```rust
use pyo3::prelude::*;
use pyo3::types::PyAny;
/// Class for demonstration
struct MyClass {
@ -33,9 +34,10 @@ impl pyo3::pyclass::PyClassAlloc for MyClass {}
unsafe impl pyo3::PyTypeInfo for MyClass {
type Type = MyClass;
type BaseType = pyo3::types::PyAny;
type ConcreteLayout = pyo3::PyClassShell<Self>;
type Initializer = pyo3::PyClassInitializer<Self>;
type BaseType = PyAny;
type BaseLayout = pyo3::pycell::PyCellBase<PyAny>;
type Layout = PyCell<Self>;
type Initializer = PyClassInitializer<Self>;
const NAME: &'static str = "MyClass";
const MODULE: Option<&'static str> = None;
@ -53,6 +55,7 @@ unsafe impl pyo3::PyTypeInfo for MyClass {
impl pyo3::pyclass::PyClass for MyClass {
type Dict = pyo3::pyclass_slots::PyClassDummySlot;
type WeakRef = pyo3::pyclass_slots::PyClassDummySlot;
type BaseNativeType = PyAny;
}
impl pyo3::IntoPy<PyObject> for MyClass {
@ -105,47 +108,56 @@ fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> {
}
```
## Get Python objects from `pyclass`
You sometimes need to convert your `pyclass` into a Python object in Rust code (e.g., for testing it).
## PyCell and interior mutability
You sometimes need to convert your `pyclass` into a Python object and access it
from Rust code (e.g., for testing it).
[`PyCell`](https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html) is our primary interface for that.
For getting *GIL-bounded* (i.e., with `'py` lifetime) references of `pyclass`,
you can use `PyClassShell<T>`.
Or you can use `Py<T>` directly, for *not-GIL-bounded* references.
`PyCell<T: PyClass>` is always allocated in the Python heap, so we don't have the ownership of it.
We can only get `&PyCell<T>`, not `PyCell<T>`.
### `PyClassShell`
`PyClassShell` represents the actual layout of `pyclass` on the Python heap.
Thus, to mutate data behind `&PyCell` safely, we employ the
[Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html)
like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html).
If you want to instantiate `pyclass` in Python and get the reference,
you can use `PyClassShell::new_ref` or `PyClassShell::new_mut`.
Users who are familiar with `RefCell` can use `PyCell` just like `RefCell`.
For users who are not very familiar with `RefCell`, here is a reminder of Rust's rules of borrowing:
- At any given time, you can have either (but not both of) one mutable reference or any number of immutable references.
- References must always be valid.
`PyCell` ensures these borrowing rules by tracking references at runtime.
```rust
# use pyo3::prelude::*;
# use pyo3::types::PyDict;
# use pyo3::PyClassShell;
#[pyclass]
struct MyClass {
#[pyo3(get)]
num: i32,
debug: bool,
}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyClassShell::new_ref(py, MyClass { num: 3, debug: true }).unwrap();
// You can use deref
assert_eq!(obj.num, 3);
let dict = PyDict::new(py);
// You can treat a `&PyClassShell` as a normal Python object
dict.set_item("obj", obj).unwrap();
// return &mut PyClassShell<MyClass>
let obj = PyClassShell::new_mut(py, MyClass { num: 3, debug: true }).unwrap();
obj.num = 5;
let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap();
{
let obj_ref = obj.borrow(); // Get PyRef
assert_eq!(obj_ref.num, 3);
// You cannot get PyRefMut unless all PyRefs are dropped
assert!(obj.try_borrow_mut().is_err());
}
{
let mut obj_mut = obj.borrow_mut(); // Get PyRefMut
obj_mut.num = 5;
// You cannot get any other refs until the PyRefMut is dropped
assert!(obj.try_borrow().is_err());
assert!(obj.try_borrow_mut().is_err());
}
// You can convert `&PyCell` to Python object
pyo3::py_run!(py, obj, "assert obj.num == 5")
```
### `Py`
`Py` is an object wrapper which stores an object longer than the GIL lifetime.
You can use it to avoid lifetime problems.
`&PyCell<T>` is bounded by the same lifetime as `GILGuard`.
To avoid this you can use `Py<T>`, which stores an object longer than the GIL lifetime.
```rust
# use pyo3::prelude::*;
#[pyclass]
@ -159,7 +171,9 @@ fn return_myclass() -> Py<MyClass> {
}
let gil = Python::acquire_gil();
let obj = return_myclass();
assert_eq!(obj.as_ref(gil.python()).num, 1);
let cell = obj.as_ref(gil.python()); // AsPyRef::as_ref returns &PyCell
let obj_ref = cell.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1);
```
## Customizing the class
@ -228,9 +242,14 @@ baseclass of `T`.
But for more deeply nested inheritance, you have to return `PyClassInitializer<T>`
explicitly.
To get a parent class from a child, use `PyRef<T>` instead of `&self`,
or `PyRefMut<T>` instead of `&mut self`.
Then you can access a parent class by `self_.as_ref()` as `&Self::BaseClass`,
or by `self_.into_super()` as `PyRef<Self::BaseClass>`.
```rust
# use pyo3::prelude::*;
use pyo3::PyClassShell;
use pyo3::PyCell;
#[pyclass]
struct BaseClass {
@ -261,8 +280,9 @@ impl SubClass {
(SubClass{ val2: 15}, BaseClass::new())
}
fn method2(self_: &PyClassShell<Self>) -> PyResult<usize> {
self_.get_super().method().map(|x| x * self_.val2)
fn method2(self_: PyRef<Self>) -> PyResult<usize> {
let super_ = self_.as_ref(); // Get &BaseClass
super_.method().map(|x| x * self_.val2)
}
}
@ -279,29 +299,24 @@ impl SubSubClass {
.add_subclass(SubSubClass{val3: 20})
}
fn method3(self_: &PyClassShell<Self>) -> PyResult<usize> {
let super_ = self_.get_super();
SubClass::method2(super_).map(|x| x * self_.val3)
fn method3(self_: PyRef<Self>) -> PyResult<usize> {
let v = self_.val3;
let super_ = self_.into_super(); // Get PyRef<SubClass>
SubClass::method2(super_).map(|x| x * v)
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let subsub = pyo3::PyClassShell::new_ref(py, SubSubClass::new()).unwrap();
# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000")
```
To access the super class, you can use either of these two ways:
- Use `self_: &PyClassShell<Self>` instead of `self`, and call `get_super()`
- `ObjectProtocol::get_base`
We recommend `PyClassShell` here, since it makes the context much clearer.
If `SubClass` does not provide a baseclass initialization, the compilation fails.
```compile_fail
# use pyo3::prelude::*;
use pyo3::PyClassShell;
use pyo3::PyCell;
#[pyclass]
struct BaseClass {
@ -761,8 +776,8 @@ struct GCTracked {} // Fails because it does not implement PyGCProtocol
Iterators can be defined using the
[`PyIterProtocol`](https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html) trait.
It includes two methods `__iter__` and `__next__`:
* `fn __iter__(slf: &mut PyClassShell<Self>) -> PyResult<impl IntoPy<PyObject>>`
* `fn __next__(slf: &mut PyClassShell<Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`
* `fn __iter__(slf: PyRefMut<Self>) -> PyResult<impl IntoPy<PyObject>>`
* `fn __next__(slf: PyRefMut<Self>) -> PyResult<Option<impl IntoPy<PyObject>>>`
Returning `Ok(None)` from `__next__` indicates that that there are no further items.
@ -770,7 +785,7 @@ Example:
```rust
use pyo3::prelude::*;
use pyo3::{PyIterProtocol, PyClassShell};
use pyo3::{PyIterProtocol, PyCell};
#[pyclass]
struct MyIterator {
@ -779,19 +794,15 @@ struct MyIterator {
#[pyproto]
impl PyIterProtocol for MyIterator {
fn __iter__(slf: &mut PyClassShell<Self>) -> PyResult<Py<MyIterator>> {
fn __iter__(mut slf: PyRefMut<Self>) -> PyResult<Py<MyIterator>> {
Ok(slf.into())
}
fn __next__(slf: &mut PyClassShell<Self>) -> PyResult<Option<PyObject>> {
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<PyObject>> {
Ok(slf.iter.next())
}
}
```
## Manually implementing pyclass
TODO: Which traits to implement (basically `PyTypeCreate: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol + Sized`) and what they mean.
## How methods are implemented
Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a

View File

@ -36,7 +36,7 @@ your Python extensions quickly.
```rust
use pyo3::prelude::*;
use pyo3::{PyClassShell, PyObjectProtocol, py_run};
use pyo3::{PyCell, PyObjectProtocol, py_run};
# fn main() {
#[pyclass]
struct UserData {
@ -61,7 +61,7 @@ let userdata = UserData {
id: 34,
name: "Yu".to_string(),
};
let userdata = PyClassShell::new_ref(py, userdata).unwrap();
let userdata = PyCell::new(py, userdata).unwrap();
let userdata_as_tuple = (34, "Yu");
py_run!(py, userdata userdata_as_tuple, r#"
assert repr(userdata) == "User Yu(id: 34)"

View File

@ -29,12 +29,16 @@ pub enum FnType {
FnCall,
FnClass,
FnStatic,
PySelf(syn::TypeReference),
/// For methods taht have `self_: &PyCell<Self>` instead of self receiver
PySelfRef(syn::TypeReference),
/// For methods taht have `self_: PyRef<Self>` or `PyRefMut<Self>` instead of self receiver
PySelfPath(syn::TypePath),
}
#[derive(Clone, PartialEq, Debug)]
pub struct FnSpec<'a> {
pub tp: FnType,
pub self_: Option<bool>,
// Rust function name
pub name: &'a syn::Ident,
// Wrapped python name. This should not have any leading r#.
@ -54,6 +58,14 @@ pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
}
impl<'a> FnSpec<'a> {
/// Generate the code for borrowing self
pub(crate) fn borrow_self(&self) -> TokenStream {
let is_mut = self
.self_
.expect("impl_borrow_self is called for non-self fn");
crate::utils::borrow_self(is_mut, true)
}
/// Parser function signature and function attributes
pub fn parse(
sig: &'a syn::Signature,
@ -67,19 +79,19 @@ impl<'a> FnSpec<'a> {
mut python_name,
} = parse_method_attributes(meth_attrs, allow_custom_name)?;
let mut has_self = false;
let mut self_ = None;
let mut arguments = Vec::new();
for input in sig.inputs.iter() {
match input {
syn::FnArg::Receiver(_) => {
has_self = true;
syn::FnArg::Receiver(recv) => {
self_ = Some(recv.mutability.is_some());
}
syn::FnArg::Typed(syn::PatType {
ref pat, ref ty, ..
}) => {
// skip first argument (cls)
if fn_type == FnType::FnClass && !has_self {
has_self = true;
if fn_type == FnType::FnClass && self_.is_none() {
self_ = Some(false);
continue;
}
@ -114,18 +126,18 @@ impl<'a> FnSpec<'a> {
let ty = get_return_info(&sig.output);
if fn_type == FnType::Fn && !has_self {
if fn_type == FnType::Fn && self_.is_none() {
if arguments.is_empty() {
return Err(syn::Error::new_spanned(
name,
"Static method needs #[staticmethod] attribute",
));
}
let tp = match arguments.remove(0).ty {
syn::Type::Reference(r) => replace_self(r)?,
fn_type = match arguments.remove(0).ty {
syn::Type::Reference(r) => FnType::PySelfRef(replace_self_in_ref(r)?),
syn::Type::Path(p) => FnType::PySelfPath(replace_self_in_path(p)),
x => return Err(syn::Error::new_spanned(x, "Invalid type as custom self")),
};
fn_type = FnType::PySelf(tp);
}
// "Tweak" getter / setter names: strip off set_ and get_ if needed
@ -158,9 +170,11 @@ impl<'a> FnSpec<'a> {
};
let text_signature = match &fn_type {
FnType::Fn | FnType::PySelf(_) | FnType::FnClass | FnType::FnStatic => {
utils::parse_text_signature_attrs(&mut *meth_attrs, name)?
}
FnType::Fn
| FnType::PySelfRef(_)
| FnType::PySelfPath(_)
| FnType::FnClass
| FnType::FnStatic => utils::parse_text_signature_attrs(&mut *meth_attrs, name)?,
FnType::FnNew => parse_erroneous_text_signature(
"text_signature not allowed on __new__; if you want to add a signature on \
__new__, put it on the struct definition instead",
@ -174,6 +188,7 @@ impl<'a> FnSpec<'a> {
Ok(FnSpec {
tp: fn_type,
self_,
name,
python_name,
attrs: fn_attrs,
@ -514,17 +529,24 @@ fn parse_method_name_attribute(
}
// Replace &A<Self> with &A<_>
fn replace_self(refn: &syn::TypeReference) -> syn::Result<syn::TypeReference> {
fn infer(span: proc_macro2::Span) -> syn::GenericArgument {
syn::GenericArgument::Type(syn::Type::Infer(syn::TypeInfer {
underscore_token: syn::token::Underscore { spans: [span] },
}))
}
fn replace_self_in_ref(refn: &syn::TypeReference) -> syn::Result<syn::TypeReference> {
let mut res = refn.to_owned();
let tp = match &mut *res.elem {
syn::Type::Path(p) => p,
_ => return Err(syn::Error::new_spanned(refn, "unsupported argument")),
};
replace_self_impl(tp);
res.lifetime = None;
Ok(res)
}
fn replace_self_in_path(tp: &syn::TypePath) -> syn::TypePath {
let mut res = tp.to_owned();
replace_self_impl(&mut res);
res
}
fn replace_self_impl(tp: &mut syn::TypePath) {
for seg in &mut tp.path.segments {
if let syn::PathArguments::AngleBracketed(ref mut g) = seg.arguments {
let mut args = syn::punctuated::Punctuated::new();
@ -546,6 +568,9 @@ fn replace_self(refn: &syn::TypeReference) -> syn::Result<syn::TypeReference> {
g.args = args;
}
}
res.lifetime = None;
Ok(res)
fn infer(span: proc_macro2::Span) -> syn::GenericArgument {
syn::GenericArgument::Type(syn::Type::Infer(syn::TypeInfer {
underscore_token: syn::token::Underscore { spans: [span] },
}))
}
}

View File

@ -28,7 +28,7 @@ pub fn py_init(fnname: &Ident, name: &Ident, doc: syn::LitStr) -> TokenStream {
}
/// Finds and takes care of the #[pyfn(...)] in `#[pymodule]`
pub fn process_functions_in_module(func: &mut syn::ItemFn) {
pub fn process_functions_in_module(func: &mut syn::ItemFn) -> syn::Result<()> {
let mut stmts: Vec<syn::Stmt> = Vec::new();
for stmt in func.block.stmts.iter_mut() {
@ -36,7 +36,7 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
if let Some((module_name, python_name, pyfn_attrs)) =
extract_pyfn_attrs(&mut func.attrs)
{
let function_to_python = add_fn_to_module(func, python_name, pyfn_attrs);
let function_to_python = add_fn_to_module(func, python_name, pyfn_attrs)?;
let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
let item: syn::ItemFn = syn::parse_quote! {
fn block_wrapper() {
@ -51,31 +51,27 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
}
func.block.stmts = stmts;
Ok(())
}
/// Transforms a rust fn arg parsed with syn into a method::FnArg
fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method::FnArg<'a>> {
match input {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(ref cap) => {
let (mutability, by_ref, ident) = match *cap.pat {
syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident),
_ => panic!("unsupported argument: {:?}", cap.pat),
};
fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> syn::Result<method::FnArg<'a>> {
let (mutability, by_ref, ident) = match *cap.pat {
syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident),
_ => return Err(syn::Error::new_spanned(&cap.pat, "Unsupported argument")),
};
let py = crate::utils::if_type_is_python(&cap.ty);
let opt = method::check_arg_ty_and_optional(&name, &cap.ty);
Some(method::FnArg {
name: ident,
mutability,
by_ref,
ty: &cap.ty,
optional: opt,
py,
reference: method::is_ref(&name, &cap.ty),
})
}
}
let py = crate::utils::if_type_is_python(&cap.ty);
let opt = method::check_arg_ty_and_optional(&name, &cap.ty);
Ok(method::FnArg {
name: ident,
mutability,
by_ref,
ty: &cap.ty,
optional: opt,
py,
reference: method::is_ref(&name, &cap.ty),
})
}
/// Extracts the data from the #[pyfn(...)] attribute of a function
@ -136,30 +132,31 @@ pub fn add_fn_to_module(
func: &mut syn::ItemFn,
python_name: Ident,
pyfn_attrs: Vec<pyfunction::Argument>,
) -> TokenStream {
) -> syn::Result<TokenStream> {
let mut arguments = Vec::new();
let mut self_ = None;
for input in func.sig.inputs.iter() {
if let Some(fn_arg) = wrap_fn_argument(input, &func.sig.ident) {
arguments.push(fn_arg);
match input {
syn::FnArg::Receiver(recv) => {
self_ = Some(recv.mutability.is_some());
}
syn::FnArg::Typed(ref cap) => {
arguments.push(wrap_fn_argument(cap, &func.sig.ident)?);
}
}
}
let ty = method::get_return_info(&func.sig.output);
let text_signature = match utils::parse_text_signature_attrs(&mut func.attrs, &python_name) {
Ok(text_signature) => text_signature,
Err(err) => return err.to_compile_error(),
};
let doc = match utils::get_doc(&func.attrs, text_signature, true) {
Ok(doc) => doc,
Err(err) => return err.to_compile_error(),
};
let text_signature = utils::parse_text_signature_attrs(&mut func.attrs, &python_name)?;
let doc = utils::get_doc(&func.attrs, text_signature, true)?;
let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
let spec = method::FnSpec {
tp: method::FnType::Fn,
self_,
name: &function_wrapper_ident,
python_name,
attrs: pyfn_attrs,
@ -174,7 +171,7 @@ pub fn add_fn_to_module(
let wrapper = function_c_wrapper(&func.sig.ident, &spec);
let tokens = quote! {
Ok(quote! {
fn #function_wrapper_ident(py: pyo3::Python) -> pyo3::PyObject {
#wrapper
@ -197,9 +194,7 @@ pub fn add_fn_to_module(
function
}
};
tokens
})
}
/// Generate static function wrapper (PyCFunction, PyCFunctionWithKeywords)
@ -226,8 +221,7 @@ fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>) -> TokenStream {
#body
pyo3::callback::cb_convert(
pyo3::callback::PyObjectCallbackConverter, _py, _result)
pyo3::callback::cb_obj_convert(_py, _result)
}
}
}

View File

@ -317,14 +317,18 @@ fn impl_class(
}
let weakref = if has_weakref {
quote! { type WeakRef = pyo3::pyclass_slots::PyClassWeakRefSlot; }
quote! { pyo3::pyclass_slots::PyClassWeakRefSlot }
} else if attr.has_extends {
quote! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::WeakRef }
} else {
quote! { type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; }
quote! { pyo3::pyclass_slots::PyClassDummySlot }
};
let dict = if has_dict {
quote! { type Dict = pyo3::pyclass_slots::PyClassDictSlot; }
quote! { pyo3::pyclass_slots::PyClassDictSlot }
} else if attr.has_extends {
quote! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::Dict }
} else {
quote! { type Dict = pyo3::pyclass_slots::PyClassDummySlot; }
quote! { pyo3::pyclass_slots::PyClassDummySlot }
};
let module = if let Some(m) = &attr.module {
quote! { Some(#m) }
@ -357,6 +361,16 @@ fn impl_class(
} else {
quote! { 0 }
};
let base_layout = if attr.has_extends {
quote! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::LayoutAsBase }
} else {
quote! { pyo3::pycell::PyCellBase<pyo3::types::PyAny> }
};
let base_nativetype = if attr.has_extends {
quote! { <Self::BaseType as pyo3::derive_utils::PyBaseTypeUtils>::BaseNativeType }
} else {
quote! { pyo3::types::PyAny }
};
// If #cls is not extended type, we allow Self->PyObject conversion
let into_pyobject = if !attr.has_extends {
@ -375,7 +389,8 @@ fn impl_class(
unsafe impl pyo3::type_object::PyTypeInfo for #cls {
type Type = #cls;
type BaseType = #base;
type ConcreteLayout = pyo3::pyclass::PyClassShell<Self>;
type Layout = pyo3::pycell::PyCell<Self>;
type BaseLayout = #base_layout;
type Initializer = pyo3::pyclass_init::PyClassInitializer<Self>;
const NAME: &'static str = #cls_name;
@ -392,20 +407,9 @@ fn impl_class(
}
impl pyo3::PyClass for #cls {
#dict
#weakref
}
impl pyo3::conversion::FromPyObjectImpl for #cls {
type Impl = pyo3::conversion::extract_impl::Cloned;
}
impl pyo3::conversion::FromPyObjectImpl for &'_ #cls {
type Impl = pyo3::conversion::extract_impl::Reference;
}
impl pyo3::conversion::FromPyObjectImpl for &'_ mut #cls {
type Impl = pyo3::conversion::extract_impl::MutReference;
type Dict = #dict;
type WeakRef = #weakref;
type BaseNativeType = #base_nativetype;
}
#into_pyobject

View File

@ -234,7 +234,7 @@ pub fn parse_name_attribute(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Opti
pub fn build_py_function(ast: &mut syn::ItemFn, args: PyFunctionAttr) -> syn::Result<TokenStream> {
let python_name =
parse_name_attribute(&mut ast.attrs)?.unwrap_or_else(|| ast.sig.ident.unraw());
Ok(add_fn_to_module(ast, python_name, args.arguments))
add_fn_to_module(ast, python_name, args.arguments)
}
#[cfg(test)]

View File

@ -20,7 +20,10 @@ pub fn gen_py_method(
Ok(match spec.tp {
FnType::Fn => impl_py_method_def(&spec, &impl_wrap(cls, &spec, true)),
FnType::PySelf(ref self_ty) => {
FnType::PySelfRef(ref self_ty) => {
impl_py_method_def(&spec, &impl_wrap_pyslf(cls, &spec, self_ty, true))
}
FnType::PySelfPath(ref self_ty) => {
impl_py_method_def(&spec, &impl_wrap_pyslf(cls, &spec, self_ty, true))
}
FnType::FnNew => impl_py_method_def_new(&spec, &impl_wrap_new(cls, &spec)),
@ -59,14 +62,18 @@ fn check_generic(sig: &syn::Signature) -> syn::Result<()> {
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap(cls: &syn::Type, spec: &FnSpec<'_>, noargs: bool) -> TokenStream {
let body = impl_call(cls, &spec);
let slf = impl_self(&quote! { &mut #cls });
let borrow_self = spec.borrow_self();
let slf = quote! {
let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
#borrow_self
};
impl_wrap_common(cls, spec, noargs, slf, body)
}
pub fn impl_wrap_pyslf(
cls: &syn::Type,
spec: &FnSpec<'_>,
self_ty: &syn::TypeReference,
self_ty: impl quote::ToTokens,
noargs: bool,
) -> TokenStream {
let names = get_arg_names(spec);
@ -74,7 +81,13 @@ pub fn impl_wrap_pyslf(
let body = quote! {
#cls::#name(_slf, #(#names),*)
};
let slf = impl_self(self_ty);
let slf = quote! {
let _cell = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
let _slf: #self_ty = match std::convert::TryFrom::try_from(_cell) {
Ok(_slf) => _slf,
Err(e) => return pyo3::PyErr::from(e).restore_and_null(_py),
};
};
impl_wrap_common(cls, spec, noargs, slf, body)
}
@ -102,8 +115,7 @@ fn impl_wrap_common(
pyo3::derive_utils::IntoPyResult::into_py_result(#body)
};
pyo3::callback::cb_convert(
pyo3::callback::PyObjectCallbackConverter, _py, _result)
pyo3::callback::cb_obj_convert(_py, _result)
}
}
} else {
@ -125,8 +137,7 @@ fn impl_wrap_common(
#body
pyo3::callback::cb_convert(
pyo3::callback::PyObjectCallbackConverter, _py, _result)
pyo3::callback::cb_obj_convert(_py, _result)
}
}
}
@ -137,6 +148,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
let python_name = &spec.python_name;
let cb = impl_call(cls, &spec);
let body = impl_arg_params(&spec, cb);
let borrow_self = spec.borrow_self();
quote! {
#[allow(unused_mut)]
@ -148,14 +160,14 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
let _py = pyo3::Python::assume_gil_acquired();
let _pool = pyo3::GILPool::new(_py);
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
#borrow_self
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body
pyo3::callback::cb_convert(
pyo3::callback::PyObjectCallbackConverter, _py, _result)
pyo3::callback::cb_obj_convert(_py, _result)
}
}
}
@ -189,7 +201,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
#body
match _result.and_then(|init| pyo3::PyClassInitializer::from(init).create_shell(_py)) {
match _result.and_then(|init| pyo3::PyClassInitializer::from(init).create_cell(_py)) {
Ok(slf) => slf as _,
Err(e) => e.restore_and_null(_py),
}
@ -222,8 +234,7 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
#body
pyo3::callback::cb_convert(
pyo3::callback::PyObjectCallbackConverter, _py, _result)
pyo3::callback::cb_obj_convert(_py, _result)
}
}
}
@ -252,8 +263,7 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
#body
pyo3::callback::cb_convert(
pyo3::callback::PyObjectCallbackConverter, _py, _result)
pyo3::callback::cb_obj_convert(_py, _result)
}
}
}
@ -273,7 +283,6 @@ fn impl_call_getter(spec: &FnSpec) -> syn::Result<TokenStream> {
} else {
quote! { _slf.#name() }
};
Ok(fncall)
}
@ -296,6 +305,7 @@ pub(crate) fn impl_wrap_getter(
PropertyType::Function(spec) => (spec.python_name.clone(), impl_call_getter(&spec)?),
};
let borrow_self = crate::utils::borrow_self(false, true);
Ok(quote! {
unsafe extern "C" fn __wrap(
_slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject
@ -304,18 +314,14 @@ pub(crate) fn impl_wrap_getter(
let _py = pyo3::Python::assume_gil_acquired();
let _pool = pyo3::GILPool::new(_py);
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
#borrow_self
let result = pyo3::derive_utils::IntoPyResult::into_py_result(#getter_impl);
match result {
Ok(val) => {
pyo3::IntoPyPointer::into_ptr(pyo3::IntoPy::<PyObject>::into_py(val, _py))
}
Err(e) => {
e.restore(_py);
::std::ptr::null_mut()
}
Ok(val) => pyo3::IntoPyPointer::into_ptr(pyo3::IntoPy::<PyObject>::into_py(val, _py)),
Err(e) => e.restore_and_null(_py),
}
}
})
@ -359,6 +365,7 @@ pub(crate) fn impl_wrap_setter(
PropertyType::Function(spec) => (spec.python_name.clone(), impl_call_setter(&spec)?),
};
let borrow_self = crate::utils::borrow_self(true, false);
Ok(quote! {
#[allow(unused_mut)]
unsafe extern "C" fn __wrap(
@ -368,7 +375,8 @@ pub(crate) fn impl_wrap_setter(
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
let _py = pyo3::Python::assume_gil_acquired();
let _pool = pyo3::GILPool::new(_py);
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
let _slf = _py.from_borrowed_ptr::<pyo3::PyCell<#cls>>(_slf);
#borrow_self
let _value = _py.from_borrowed_ptr(_value);
let _result = match pyo3::FromPyObject::extract(_value) {
@ -401,12 +409,6 @@ fn impl_call(_cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
quote! { _slf.#fname(#(#names),*) }
}
fn impl_self<T: quote::ToTokens>(self_ty: &T) -> TokenStream {
quote! {
let _slf: #self_ty = pyo3::FromPyPointer::from_borrowed_ptr(_py, _slf);
}
}
/// Converts a bool to "true" or "false"
fn bool_to_ident(condition: bool) -> syn::Ident {
if condition {

View File

@ -1,9 +1,32 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::quote;
use std::fmt::Display;
pub(crate) fn borrow_self(is_mut: bool, return_null: bool) -> TokenStream {
let ret = if return_null {
quote! { restore_and_null }
} else {
quote! { restore_and_minus1 }
};
if is_mut {
quote! {
let mut _slf = match _slf.try_borrow_mut() {
Ok(ref_) => ref_,
Err(e) => return pyo3::PyErr::from(e).#ret(_py),
};
}
} else {
quote! {
let _slf = match _slf.try_borrow() {
Ok(ref_) => ref_,
Err(e) => return pyo3::PyErr::from(e).#ret(_py),
};
}
}
}
pub fn print_err(msg: String, t: TokenStream) {
println!("Error: {} in '{}'", msg, t.to_string());
}

View File

@ -23,7 +23,9 @@ pub fn pymodule(attr: TokenStream, input: TokenStream) -> TokenStream {
parse_macro_input!(attr as syn::Ident)
};
process_functions_in_module(&mut ast);
if let Err(err) = process_functions_in_module(&mut ast) {
return err.to_compile_error().into();
}
let doc = match get_doc(&ast.attrs, None, false) {
Ok(doc) => doc,

View File

@ -10,53 +10,64 @@ use crate::{IntoPy, PyObject, Python};
use std::os::raw::c_int;
use std::{isize, ptr};
pub trait CallbackConverter<S> {
type R;
/// Convert the result of callback function into the appropriate return value.
///
/// Used by PyO3 macros.
pub trait CallbackConverter {
type Source;
type Result: Copy;
const ERR_VALUE: Self::Result;
fn convert(s: S, p: Python) -> Self::R;
fn error_value() -> Self::R;
}
pub struct PyObjectCallbackConverter;
impl<S> CallbackConverter<S> for PyObjectCallbackConverter
where
S: IntoPy<PyObject>,
{
type R = *mut ffi::PyObject;
fn convert(val: S, py: Python) -> *mut ffi::PyObject {
val.into_py(py).into_ptr()
}
fn convert(s: Self::Source, py: Python) -> Self::Result;
#[inline]
fn error_value() -> *mut ffi::PyObject {
ptr::null_mut()
fn convert_result(py: Python, value: PyResult<Self::Source>) -> Self::Result {
match value {
Ok(val) => Self::convert(val, py),
Err(e) => {
e.restore(py);
Self::ERR_VALUE
}
}
}
}
pub struct PyObjectCallbackConverter<T>(pub std::marker::PhantomData<T>);
impl<T> CallbackConverter for PyObjectCallbackConverter<T>
where
T: IntoPy<PyObject>,
{
type Source = T;
type Result = *mut ffi::PyObject;
const ERR_VALUE: Self::Result = ptr::null_mut();
fn convert(s: Self::Source, py: Python) -> Self::Result {
s.into_py(py).into_ptr()
}
}
pub struct BoolCallbackConverter;
impl CallbackConverter<bool> for BoolCallbackConverter {
type R = c_int;
impl CallbackConverter for BoolCallbackConverter {
type Source = bool;
type Result = c_int;
const ERR_VALUE: Self::Result = -1;
#[inline]
fn convert(val: bool, _py: Python) -> c_int {
val as c_int
}
#[inline]
fn error_value() -> c_int {
-1
fn convert(s: Self::Source, _py: Python) -> Self::Result {
s as c_int
}
}
pub struct LenResultConverter;
impl CallbackConverter<usize> for LenResultConverter {
type R = isize;
impl CallbackConverter for LenResultConverter {
type Source = usize;
type Result = isize;
const ERR_VALUE: Self::Result = -1;
fn convert(val: usize, py: Python) -> isize {
fn convert(val: Self::Source, py: Python) -> Self::Result {
if val <= (isize::MAX as usize) {
val as isize
} else {
@ -64,27 +75,19 @@ impl CallbackConverter<usize> for LenResultConverter {
-1
}
}
#[inline]
fn error_value() -> isize {
-1
}
}
pub struct UnitCallbackConverter;
impl CallbackConverter<()> for UnitCallbackConverter {
type R = c_int;
impl CallbackConverter for UnitCallbackConverter {
type Source = ();
type Result = c_int;
const ERR_VALUE: Self::Result = -1;
#[inline]
fn convert(_: (), _: Python) -> c_int {
fn convert(_s: Self::Source, _py: Python) -> Self::Result {
0
}
#[inline]
fn error_value() -> c_int {
-1
}
}
pub trait WrappingCastTo<T> {
@ -112,13 +115,15 @@ wrapping_cast!(i32, Py_hash_t);
wrapping_cast!(isize, Py_hash_t);
wrapping_cast!(i64, Py_hash_t);
pub struct HashConverter;
pub struct HashConverter<T>(pub std::marker::PhantomData<T>);
impl<T> CallbackConverter<T> for HashConverter
impl<T> CallbackConverter for HashConverter<T>
where
T: WrappingCastTo<Py_hash_t>,
{
type R = Py_hash_t;
type Source = T;
type Result = Py_hash_t;
const ERR_VALUE: Self::Result = -1;
#[inline]
fn convert(val: T, _py: Python) -> Py_hash_t {
@ -129,23 +134,31 @@ where
hash
}
}
}
#[inline]
fn error_value() -> Py_hash_t {
-1
}
// Short hands methods for macros
#[inline]
pub fn cb_convert<C, T>(_c: C, py: Python, value: PyResult<T>) -> C::Result
where
C: CallbackConverter<Source = T>,
{
C::convert_result(py, value)
}
// Same as cb_convert(PyObjectCallbackConverter<T>, py, value)
#[inline]
pub fn cb_obj_convert<T: IntoPy<PyObject>>(
py: Python,
value: PyResult<T>,
) -> <PyObjectCallbackConverter<T> as CallbackConverter>::Result {
PyObjectCallbackConverter::<T>::convert_result(py, value)
}
#[inline]
pub unsafe fn cb_convert<C, T>(_c: C, py: Python, value: PyResult<T>) -> C::R
pub unsafe fn cb_err<C>(_c: C, py: Python, err: impl Into<crate::PyErr>) -> C::Result
where
C: CallbackConverter<T>,
C: CallbackConverter,
{
match value {
Ok(val) => C::convert(val, py),
Err(e) => {
e.restore(py);
C::error_value()
}
}
err.into().restore(py);
C::ERR_VALUE
}

View File

@ -11,16 +11,10 @@
use crate::callback::{BoolCallbackConverter, HashConverter, PyObjectCallbackConverter};
use crate::class::methods::PyMethodDef;
use crate::err::{PyErr, PyResult};
use crate::ffi;
use crate::objectprotocol::ObjectProtocol;
use crate::type_object::PyTypeInfo;
use crate::types::PyAny;
use crate::FromPyObject;
use crate::IntoPyPointer;
use crate::Python;
use crate::{exceptions, IntoPy, PyObject};
use crate::{exceptions, ffi, FromPyObject, IntoPy, IntoPyPointer, PyClass, PyObject, Python};
use std::os::raw::c_int;
use std::ptr;
/// Operators for the __richcmp__ method
#[derive(Debug)]
@ -35,7 +29,7 @@ pub enum CompareOp {
/// Basic python class customization
#[allow(unused_variables)]
pub trait PyObjectProtocol<'p>: PyTypeInfo {
pub trait PyObjectProtocol<'p>: PyClass {
fn __getattr__(&'p self, name: Self::Name) -> Self::Result
where
Self: PyObjectGetAttrProtocol<'p>,
@ -237,14 +231,15 @@ where
return existing;
}
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<crate::types::PyAny>(arg);
let result = match arg.extract() {
Ok(arg) => slf.__getattr__(arg).into(),
Err(e) => Err(e),
};
crate::callback::cb_convert(PyObjectCallbackConverter, py, result)
call_ref_with_converter!(
slf,
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData),
py,
__getattr__,
arg
)
}
Some(wrap::<T>)
}
@ -365,8 +360,7 @@ where
py_unary_func!(
PyObjectStrProtocol,
T::__str__,
<T as PyObjectStrProtocol>::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -390,8 +384,7 @@ where
py_unary_func!(
PyObjectReprProtocol,
T::__repr__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -454,8 +447,7 @@ where
py_unary_func!(
PyObjectHashProtocol,
T::__hash__,
isize,
HashConverter,
HashConverter::<isize>(std::marker::PhantomData),
ffi::Py_hash_t
)
}
@ -480,7 +472,6 @@ where
py_unary_func!(
PyObjectBoolProtocol,
T::__bool__,
bool,
BoolCallbackConverter,
c_int
)
@ -513,22 +504,24 @@ where
{
let py = Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = py.from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(arg);
let res = match extract_op(op) {
Ok(op) => match arg.extract() {
Ok(arg) => slf.__richcmp__(arg, op).into(),
Err(e) => Err(e),
},
Err(e) => Err(e),
};
match res {
Ok(val) => val.into_py(py).into_ptr(),
Err(e) => {
e.restore(py);
ptr::null_mut()
match slf.try_borrow() {
Ok(borrowed_slf) => {
let res = match extract_op(op) {
Ok(op) => match arg.extract() {
Ok(arg) => borrowed_slf.__richcmp__(arg, op).into(),
Err(e) => Err(e),
},
Err(e) => Err(e),
};
match res {
Ok(val) => val.into_py(py).into_ptr(),
Err(e) => e.restore_and_null(py),
}
}
Err(e) => PyErr::from(e).restore_and_null(py),
}
}
Some(wrap::<T>)

View File

@ -6,8 +6,7 @@
//! c-api
use crate::callback::UnitCallbackConverter;
use crate::err::PyResult;
use crate::ffi;
use crate::{PyClass, PyClassShell};
use crate::{ffi, PyClass, PyRefMut};
use std::os::raw::c_int;
/// Buffer protocol interface
@ -16,18 +15,14 @@ use std::os::raw::c_int;
/// c-api
#[allow(unused_variables)]
pub trait PyBufferProtocol<'p>: PyClass {
fn bf_getbuffer(
slf: &mut PyClassShell<Self>,
view: *mut ffi::Py_buffer,
flags: c_int,
) -> Self::Result
fn bf_getbuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result
where
Self: PyBufferGetBufferProtocol<'p>,
{
unimplemented!()
}
fn bf_releasebuffer(slf: &mut PyClassShell<Self>, view: *mut ffi::Py_buffer) -> Self::Result
fn bf_releasebuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer) -> Self::Result
where
Self: PyBufferReleaseBufferProtocol<'p>,
{
@ -98,9 +93,11 @@ where
{
let py = crate::Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = &mut *(slf as *mut PyClassShell<T>);
let result = T::bf_getbuffer(slf, arg1, arg2).into();
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let result = slf
.try_borrow_mut()
.map_err(crate::PyErr::from)
.and_then(|slf_mut| T::bf_getbuffer(slf_mut, arg1, arg2).into());
crate::callback::cb_convert(UnitCallbackConverter, py, result)
}
Some(wrap::<T>)
@ -132,9 +129,11 @@ where
{
let py = crate::Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = &mut *(slf as *mut PyClassShell<T>);
let result = T::bf_releasebuffer(slf, arg1).into();
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let result = slf
.try_borrow_mut()
.map_err(crate::PyErr::from)
.and_then(|slf_mut| T::bf_releasebuffer(slf_mut, arg1).into());
crate::callback::cb_convert(UnitCallbackConverter, py, result);
}
Some(wrap::<T>)

View File

@ -6,12 +6,11 @@
use crate::class::methods::PyMethodDef;
use crate::err::PyResult;
use crate::type_object::PyTypeInfo;
use crate::PyObject;
use crate::{PyClass, PyObject};
/// Context manager interface
#[allow(unused_variables)]
pub trait PyContextProtocol<'p>: PyTypeInfo {
pub trait PyContextProtocol<'p>: PyClass {
fn __enter__(&'p mut self) -> Self::Result
where
Self: PyContextEnterProtocol<'p>,

View File

@ -8,15 +8,13 @@
use crate::callback::{PyObjectCallbackConverter, UnitCallbackConverter};
use crate::class::methods::PyMethodDef;
use crate::err::PyResult;
use crate::type_object::PyTypeInfo;
use crate::types::{PyAny, PyType};
use crate::FromPyObject;
use crate::{ffi, IntoPy, PyObject};
use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject};
use std::os::raw::c_int;
/// Descriptor interface
#[allow(unused_variables)]
pub trait PyDescrProtocol<'p>: PyTypeInfo {
pub trait PyDescrProtocol<'p>: PyClass {
fn __get__(&'p self, instance: &'p PyAny, owner: Option<&'p PyType>) -> Self::Result
where
Self: PyDescrGetProtocol<'p>,
@ -89,8 +87,7 @@ where
py_ternary_func!(
PyDescrGetProtocol,
T::__get__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -111,13 +108,7 @@ where
T: for<'p> PyDescrSetProtocol<'p>,
{
fn tp_descr_set() -> Option<ffi::descrsetfunc> {
py_ternary_func!(
PyDescrSetProtocol,
T::__set__,
(),
UnitCallbackConverter,
c_int
)
py_ternary_func!(PyDescrSetProtocol, T::__set__, UnitCallbackConverter, c_int)
}
}

View File

@ -3,17 +3,14 @@
//! Python GC support
//!
use crate::ffi;
use crate::type_object::PyTypeInfo;
use crate::AsPyPointer;
use crate::Python;
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python};
use std::os::raw::{c_int, c_void};
#[repr(transparent)]
pub struct PyTraverseError(c_int);
/// GC support
pub trait PyGCProtocol<'p>: PyTypeInfo {
pub trait PyGCProtocol<'p>: PyClass {
fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>;
fn __clear__(&'p mut self);
}
@ -94,14 +91,14 @@ where
{
let py = Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
let visit = PyVisit {
visit,
arg,
_py: py,
};
match slf.__traverse__(visit) {
match slf.borrow().__traverse__(visit) {
Ok(()) => 0,
Err(PyTraverseError(code)) => code,
}
@ -136,9 +133,9 @@ where
{
let py = Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
slf.__clear__();
slf.borrow_mut().__clear__();
0
}
Some(tp_clear::<T>)

View File

@ -4,24 +4,23 @@
use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
use crate::err::PyResult;
use crate::{ffi, pyclass::PyClassShell, IntoPy, PyClass, PyObject};
use crate::{IntoPyPointer, Python};
use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, PyRefMut, Python};
use std::ptr;
/// Python Iterator Interface.
///
/// more information
/// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter`
/// Check [CPython doc](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter)
/// for more.
#[allow(unused_variables)]
pub trait PyIterProtocol<'p>: PyClass {
fn __iter__(slf: &mut PyClassShell<Self>) -> Self::Result
fn __iter__(slf: PyRefMut<Self>) -> Self::Result
where
Self: PyIterIterProtocol<'p>,
{
unimplemented!()
}
fn __next__(slf: &mut PyClassShell<Self>) -> Self::Result
fn __next__(slf: PyRefMut<Self>) -> Self::Result
where
Self: PyIterNextProtocol<'p>,
{
@ -78,11 +77,10 @@ where
{
#[inline]
fn tp_iter() -> Option<ffi::getiterfunc> {
py_unary_pyref_func!(
py_unary_refmut_func!(
PyIterIterProtocol,
T::__iter__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -106,24 +104,25 @@ where
{
#[inline]
fn tp_iternext() -> Option<ffi::iternextfunc> {
py_unary_pyref_func!(
py_unary_refmut_func!(
PyIterNextProtocol,
T::__next__,
Option<T::Success>,
IterNextConverter
IterNextConverter::<T::Success>(std::marker::PhantomData)
)
}
}
struct IterNextConverter;
struct IterNextConverter<T>(std::marker::PhantomData<T>);
impl<T> CallbackConverter<Option<T>> for IterNextConverter
impl<T> CallbackConverter for IterNextConverter<T>
where
T: IntoPy<PyObject>,
{
type R = *mut ffi::PyObject;
type Source = Option<T>;
type Result = *mut ffi::PyObject;
const ERR_VALUE: Self::Result = ptr::null_mut();
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
fn convert(val: Self::Source, py: Python) -> Self::Result {
match val {
Some(val) => val.into_py(py).into_ptr(),
None => unsafe {
@ -132,9 +131,4 @@ where
},
}
}
#[inline]
fn error_value() -> *mut ffi::PyObject {
ptr::null_mut()
}
}

View File

@ -3,25 +3,33 @@
#[macro_export]
#[doc(hidden)]
macro_rules! py_unary_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {
($trait:ident, $class:ident :: $f:ident, $conv: expr) => {
py_unary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject);
};
// Use call_ref! by default
($trait:ident, $class:ident :: $f:ident, $conv: expr, $ret_type:ty) => {
py_unary_func!(
$trait,
$class::$f,
$res_type,
$conv,
*mut $crate::ffi::PyObject
$ret_type,
call_ref_with_converter
);
};
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $ret_type:ty) => {{
($trait: ident,
$class:ident :: $f:ident,
$conv: expr,
$ret_type: ty,
$call: ident
) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> $ret_type
where
T: for<'p> $trait<'p>,
{
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let res = slf.$f().into();
$crate::callback::cb_convert($conv, py, res.map(|x| x))
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
$call!(slf, $conv, py, $f)
}
Some(wrap::<$class>)
}};
@ -29,17 +37,16 @@ macro_rules! py_unary_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_unary_pyref_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
macro_rules! py_unary_refmut_func {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject
where
T: for<'p> $trait<'p>,
{
use $crate::pyclass::PyClassShell;
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf: &mut PyClassShell<T> = &mut *(slf as *mut PyClassShell<T>);
let res = $class::$f(slf).into();
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let res = $class::$f(slf.borrow_mut()).into();
$crate::callback::cb_convert($conv, py, res)
}
Some(wrap::<$class>)
@ -56,9 +63,8 @@ macro_rules! py_len_func {
{
let py = Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let result = slf.$f().into();
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let result = call_ref!(slf, $f);
$crate::callback::cb_convert($conv, py, result)
}
Some(wrap::<$class>)
@ -68,17 +74,14 @@ macro_rules! py_len_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_binary_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {
py_binary_func!(
$trait,
$class::$f,
$res_type,
$conv,
*mut $crate::ffi::PyObject
)
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
py_binary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject)
};
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return:ty) => {{
#[allow(unused_mut)]
// Use call_ref! by default
($trait:ident, $class:ident :: $f:ident, $conv:expr, $return:ty) => {{
py_binary_func!($trait, $class::$f, $conv, $return, call_ref_with_converter)
}};
($trait:ident, $class:ident :: $f:ident, $conv:expr, $return:ty, $call:ident) => {{
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject, arg: *mut ffi::PyObject) -> $return
where
T: for<'p> $trait<'p>,
@ -86,14 +89,9 @@ macro_rules! py_binary_func {
use $crate::ObjectProtocol;
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg);
let result = match arg.extract() {
Ok(arg) => slf.$f(arg).into(),
Err(e) => Err(e.into()),
};
$crate::callback::cb_convert($conv, py, result)
$call!(slf, $conv, py, $f, arg)
}
Some(wrap::<$class>)
}};
@ -102,8 +100,7 @@ macro_rules! py_binary_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_binary_num_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
#[allow(unused_mut)]
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(
lhs: *mut ffi::PyObject,
rhs: *mut ffi::PyObject,
@ -130,11 +127,11 @@ macro_rules! py_binary_num_func {
}};
}
// NOTE(kngwyu): This macro is used only for inplace operations, so I used call_mut here.
#[macro_export]
#[doc(hidden)]
macro_rules! py_binary_self_func {
($trait:ident, $class:ident :: $f:ident) => {{
#[allow(unused_mut)]
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: *mut ffi::PyObject,
@ -146,22 +143,15 @@ macro_rules! py_binary_self_func {
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf1 = py.mut_from_borrowed_ptr::<T>(slf);
let slf_ = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg);
let result = match arg.extract() {
Ok(arg) => slf1.$f(arg).into(),
Err(e) => Err(e.into()),
};
let result = call_mut!(slf_, $f, arg);
match result {
Ok(_) => {
ffi::Py_INCREF(slf);
slf
}
Err(e) => {
e.restore(py);
std::ptr::null_mut()
}
Err(e) => e.restore_and_null(py),
}
}
Some(wrap::<$class>)
@ -171,8 +161,16 @@ macro_rules! py_binary_self_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_ssizearg_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
#[allow(unused_mut)]
// Use call_ref! by default
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
py_ssizearg_func!(
$trait,
$class::$f,
$conv,
call_ref_with_converter
)
};
($trait:ident, $class:ident :: $f:ident, $conv:expr, $call:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut ffi::PyObject,
arg: $crate::ffi::Py_ssize_t,
@ -182,9 +180,8 @@ macro_rules! py_ssizearg_func {
{
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let result = slf.$f(arg.into()).into();
$crate::callback::cb_convert($conv, py, result)
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
$call!(slf, $conv, py, $f ;arg.into())
}
Some(wrap::<$class>)
}};
@ -193,16 +190,10 @@ macro_rules! py_ssizearg_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_ternary_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {
py_ternary_func!(
$trait,
$class::$f,
$res_type,
$conv,
*mut $crate::ffi::PyObject
);
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {
py_ternary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject);
};
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return_type:ty) => {{
($trait:ident, $class:ident :: $f:ident, $conv:expr, $return_type:ty) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
arg1: *mut $crate::ffi::PyObject,
@ -215,18 +206,11 @@ macro_rules! py_ternary_func {
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1);
let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2);
let result = match arg1.extract() {
Ok(arg1) => match arg2.extract() {
Ok(arg2) => slf.$f(arg1, arg2).into(),
Err(e) => Err(e.into()),
},
Err(e) => Err(e.into()),
};
$crate::callback::cb_convert($conv, py, result)
call_ref_with_converter!(slf, $conv, py, $f, arg1, arg2)
}
Some(wrap::<T>)
@ -236,7 +220,7 @@ macro_rules! py_ternary_func {
#[macro_export]
#[doc(hidden)]
macro_rules! py_ternary_num_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(
arg1: *mut $crate::ffi::PyObject,
arg2: *mut $crate::ffi::PyObject,
@ -286,31 +270,19 @@ macro_rules! py_ternary_self_func {
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf1 = py.mut_from_borrowed_ptr::<T>(slf);
let slf_cell = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1);
let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2);
let result = match arg1.extract() {
Ok(arg1) => match arg2.extract() {
Ok(arg2) => slf1.$f(arg1, arg2).into(),
Err(e) => Err(e.into()),
},
Err(e) => Err(e.into()),
};
let result = call_mut!(slf_cell, $f, arg1, arg2);
match result {
Ok(_) => slf,
Err(e) => {
e.restore(py);
std::ptr::null_mut()
}
Err(e) => e.restore_and_null(py),
}
}
Some(wrap::<T>)
}};
}
#[doc(hidden)]
macro_rules! py_func_set {
($trait_name:ident, $generic:ident, $fn_set:ident) => {{
unsafe extern "C" fn wrap<$generic>(
@ -325,7 +297,7 @@ macro_rules! py_func_set {
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<$generic>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let result = if value.is_null() {
Err($crate::PyErr::new::<exceptions::NotImplementedError, _>(
@ -335,22 +307,13 @@ macro_rules! py_func_set {
),
))
} else {
let name = py.mut_from_borrowed_ptr::<$crate::types::PyAny>(name);
let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name);
let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value);
match name.extract() {
Ok(name) => match value.extract() {
Ok(value) => slf.$fn_set(name, value).into(),
Err(e) => Err(e.into()),
},
Err(e) => Err(e.into()),
}
call_mut!(slf, $fn_set, name, value)
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
Err(e) => e.restore_and_minus1(py),
}
}
@ -358,7 +321,6 @@ macro_rules! py_func_set {
}};
}
#[doc(hidden)]
macro_rules! py_func_del {
($trait_name:ident, $generic:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<U>(
@ -375,13 +337,10 @@ macro_rules! py_func_del {
let _pool = $crate::GILPool::new(py);
let result = if value.is_null() {
let slf = py.mut_from_borrowed_ptr::<U>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name);
match name.extract() {
Ok(name) => slf.$fn_del(name).into(),
Err(e) => Err(e.into()),
}
call_mut!(slf, $fn_del, name)
} else {
Err(PyErr::new::<exceptions::NotImplementedError, _>(
"Subscript assignment not supported",
@ -389,10 +348,7 @@ macro_rules! py_func_del {
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
Err(e) => e.restore_and_minus1(py),
}
}
@ -400,7 +356,6 @@ macro_rules! py_func_del {
}};
}
#[doc(hidden)]
macro_rules! py_func_set_del {
($trait1:ident, $trait2:ident, $generic:ident, $fn_set:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<$generic>(
@ -415,32 +370,70 @@ macro_rules! py_func_set_del {
let py = $crate::Python::assume_gil_acquired();
let _pool = $crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<$generic>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name);
let result = if value.is_null() {
match name.extract() {
Ok(name) => slf.$fn_del(name).into(),
Err(e) => Err(e.into()),
}
call_mut!(slf, $fn_del, name)
} else {
let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value);
match name.extract() {
Ok(name) => match value.extract() {
Ok(value) => slf.$fn_set(name, value).into(),
Err(e) => Err(e.into()),
},
Err(e) => Err(e.into()),
}
call_mut!(slf, $fn_set, name, value)
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
Err(e) => e.restore_and_minus1(py),
}
}
Some(wrap::<$generic>)
}};
}
macro_rules! _call_impl {
($slf: ident, $fn: ident $(; $args: expr)*) => { $slf.$fn($($args,)*).into() };
($slf: ident, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => {
match $raw_arg.extract() {
Ok(arg) => _call_impl!($slf, $fn $(,$raw_args)* $(;$args)* ;arg),
Err(e) => Err(e.into()),
}
};
}
/// Call `slf.try_borrow()?.$fn(...)`
macro_rules! call_ref {
($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
match $slf.try_borrow() {
Ok(slf) => _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*),
Err(e) => Err(e.into()),
}
};
}
/// Call `slf.try_borrow()?.$fn(...)` and returns the result using the given CallbackConverter
macro_rules! call_ref_with_converter {
($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
match $slf.try_borrow() {
Ok(slf) => $crate::callback::cb_convert($conv, $py, _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*)),
Err(e) => $crate::callback::cb_err($conv, $py, e)
}
};
}
/// Call `slf.try_borrow_mut()?.$fn(...)`
macro_rules! call_mut {
($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
match $slf.try_borrow_mut() {
Ok(mut slf) => _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*),
Err(e) => Err(e.into()),
}
};
}
/// Call `slf.try_borrow_mut()?.$fn(...)` and returns the result using the given CallbackConverter
macro_rules! call_mut_with_converter {
($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => {
match $slf.try_borrow_mut() {
Ok(mut slf) => $crate::callback::cb_convert($conv, $py, _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*)),
Err(e) => $crate::callback::cb_err($conv, $py, e)
}
};
}

View File

@ -6,15 +6,11 @@
use crate::callback::{LenResultConverter, PyObjectCallbackConverter};
use crate::class::methods::PyMethodDef;
use crate::err::{PyErr, PyResult};
use crate::ffi;
use crate::type_object::PyTypeInfo;
use crate::FromPyObject;
use crate::Python;
use crate::{exceptions, IntoPy, PyObject};
use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject, Python};
/// Mapping interface
#[allow(unused_variables)]
pub trait PyMappingProtocol<'p>: PyTypeInfo {
pub trait PyMappingProtocol<'p>: PyClass {
fn __len__(&'p self) -> Self::Result
where
Self: PyMappingLenProtocol<'p>,
@ -171,8 +167,7 @@ where
py_binary_func!(
PyMappingGetItemProtocol,
T::__getitem__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}

View File

@ -7,13 +7,11 @@ use crate::callback::PyObjectCallbackConverter;
use crate::class::basic::PyObjectProtocolImpl;
use crate::class::methods::PyMethodDef;
use crate::err::PyResult;
use crate::type_object::PyTypeInfo;
use crate::FromPyObject;
use crate::{ffi, IntoPy, PyObject};
use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject};
/// Number interface
#[allow(unused_variables)]
pub trait PyNumberProtocol<'p>: PyTypeInfo {
pub trait PyNumberProtocol<'p>: PyClass {
fn __add__(lhs: Self::Left, rhs: Self::Right) -> Self::Result
where
Self: PyNumberAddProtocol<'p>,
@ -766,8 +764,7 @@ where
py_binary_num_func!(
PyNumberAddProtocol,
T::__add__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -793,8 +790,7 @@ where
py_binary_num_func!(
PyNumberSubProtocol,
T::__sub__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -820,8 +816,7 @@ where
py_binary_num_func!(
PyNumberMulProtocol,
T::__mul__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -847,8 +842,7 @@ where
py_binary_num_func!(
PyNumberMatmulProtocol,
T::__matmul__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -874,8 +868,7 @@ where
py_binary_num_func!(
PyNumberTruedivProtocol,
T::__truediv__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -901,8 +894,7 @@ where
py_binary_num_func!(
PyNumberFloordivProtocol,
T::__floordiv__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -928,8 +920,7 @@ where
py_binary_num_func!(
PyNumberModProtocol,
T::__mod__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -955,8 +946,7 @@ where
py_binary_num_func!(
PyNumberDivmodProtocol,
T::__divmod__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -982,8 +972,7 @@ where
py_ternary_num_func!(
PyNumberPowProtocol,
T::__pow__,
<T as PyNumberPowProtocol>::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1009,8 +998,7 @@ where
py_binary_num_func!(
PyNumberLShiftProtocol,
T::__lshift__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1036,8 +1024,7 @@ where
py_binary_num_func!(
PyNumberRShiftProtocol,
T::__rshift__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1063,8 +1050,7 @@ where
py_binary_num_func!(
PyNumberAndProtocol,
T::__and__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1090,8 +1076,7 @@ where
py_binary_num_func!(
PyNumberXorProtocol,
T::__xor__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1117,8 +1102,7 @@ where
py_binary_num_func!(
PyNumberOrProtocol,
T::__or__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1622,8 +1606,7 @@ where
py_unary_func!(
PyNumberNegProtocol,
T::__neg__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1649,8 +1632,7 @@ where
py_unary_func!(
PyNumberPosProtocol,
T::__pos__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1676,8 +1658,7 @@ where
py_unary_func!(
PyNumberAbsProtocol,
T::__abs__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1703,8 +1684,7 @@ where
py_unary_func!(
PyNumberInvertProtocol,
T::__invert__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1730,8 +1710,7 @@ where
py_unary_func!(
PyNumberIntProtocol,
T::__int__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1757,8 +1736,7 @@ where
py_unary_func!(
PyNumberFloatProtocol,
T::__float__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -1784,8 +1762,7 @@ where
py_unary_func!(
PyNumberIndexProtocol,
T::__index__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}

View File

@ -11,15 +11,13 @@
use crate::callback::PyObjectCallbackConverter;
use crate::class::methods::PyMethodDef;
use crate::err::PyResult;
use crate::ffi;
use crate::type_object::PyTypeInfo;
use crate::PyObject;
use crate::{ffi, PyClass, PyObject};
/// Python Async/Await support interface.
///
/// Each method in this trait corresponds to Python async/await implementation.
#[allow(unused_variables)]
pub trait PyAsyncProtocol<'p>: PyTypeInfo {
pub trait PyAsyncProtocol<'p>: PyClass {
fn __await__(&'p self) -> Self::Result
where
Self: PyAsyncAwaitProtocol<'p>,
@ -155,8 +153,7 @@ where
py_unary_func!(
PyAsyncAwaitProtocol,
T::__await__,
<T as PyAsyncAwaitProtocol>::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -183,8 +180,7 @@ where
py_unary_func!(
PyAsyncAiterProtocol,
T::__aiter__,
<T as PyAsyncAiterProtocol>::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -208,17 +204,20 @@ mod anext {
use crate::IntoPyPointer;
use crate::Python;
use crate::{ffi, IntoPy, PyObject};
use std::marker::PhantomData;
use std::ptr;
pub struct IterANextResultConverter;
struct IterANextResultConverter<T>(PhantomData<T>);
impl<T> CallbackConverter<Option<T>> for IterANextResultConverter
impl<T> CallbackConverter for IterANextResultConverter<T>
where
T: IntoPy<PyObject>,
{
type R = *mut ffi::PyObject;
type Source = Option<T>;
type Result = *mut ffi::PyObject;
const ERR_VALUE: Self::Result = ptr::null_mut();
fn convert(val: Option<T>, py: Python) -> *mut ffi::PyObject {
fn convert(val: Self::Source, py: Python) -> Self::Result {
match val {
Some(val) => val.into_py(py).into_ptr(),
None => unsafe {
@ -227,11 +226,6 @@ mod anext {
},
}
}
#[inline]
fn error_value() -> *mut ffi::PyObject {
ptr::null_mut()
}
}
impl<T> PyAsyncAnextProtocolImpl for T
@ -243,8 +237,9 @@ mod anext {
py_unary_func!(
PyAsyncAnextProtocol,
T::__anext__,
Option<T::Success>,
IterANextResultConverter
IterANextResultConverter::<T::Success>(std::marker::PhantomData),
*mut crate::ffi::PyObject,
call_mut_with_converter
)
}
}

View File

@ -5,18 +5,14 @@
use crate::callback::{BoolCallbackConverter, LenResultConverter, PyObjectCallbackConverter};
use crate::err::{PyErr, PyResult};
use crate::ffi;
use crate::objectprotocol::ObjectProtocol;
use crate::type_object::PyTypeInfo;
use crate::types::PyAny;
use crate::FromPyObject;
use crate::Python;
use crate::{exceptions, IntoPy, PyObject};
use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject, Python};
use std::os::raw::c_int;
/// Sequence interface
#[allow(unused_variables)]
pub trait PySequenceProtocol<'p>: PyTypeInfo + Sized {
pub trait PySequenceProtocol<'p>: PyClass + Sized {
fn __len__(&'p self) -> Self::Result
where
Self: PySequenceLenProtocol<'p>,
@ -206,8 +202,7 @@ where
py_ssizearg_func!(
PySequenceGetItemProtocol,
T::__getitem__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -267,27 +262,29 @@ mod sq_ass_item_impl {
{
let py = Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let result = if value.is_null() {
Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
if value.is_null() {
return PyErr::new::<exceptions::NotImplementedError, _>(format!(
"Item deletion is not supported by {:?}",
stringify!(T)
)))
} else {
let value = py.from_borrowed_ptr::<PyAny>(value);
match value.extract() {
Ok(value) => slf.__setitem__(key.into(), value).into(),
Err(e) => Err(e),
}
};
))
.restore_and_minus1(py);
}
let result = match slf.try_borrow_mut() {
Ok(mut slf) => {
let value = py.from_borrowed_ptr::<PyAny>(value);
match value.extract() {
Ok(value) => slf.__setitem__(key.into(), value).into(),
Err(e) => e.into(),
}
}
Err(e) => Err(PyErr::from(e)),
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
Err(e) => e.restore_and_minus1(py),
}
}
Some(wrap::<T>)
@ -322,10 +319,10 @@ mod sq_ass_item_impl {
{
let py = Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let result = if value.is_null() {
slf.__delitem__(key.into()).into()
slf.borrow_mut().__delitem__(key.into()).into()
} else {
Err(PyErr::new::<exceptions::NotImplementedError, _>(format!(
"Item assignment not supported by {:?}",
@ -335,10 +332,7 @@ mod sq_ass_item_impl {
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
Err(e) => e.restore_and_minus1(py),
}
}
Some(wrap::<T>)
@ -373,23 +367,23 @@ mod sq_ass_item_impl {
{
let py = Python::assume_gil_acquired();
let _pool = crate::GILPool::new(py);
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let slf = py.from_borrowed_ptr::<crate::PyCell<T>>(slf);
let result = if value.is_null() {
slf.__delitem__(key.into()).into()
call_mut!(slf, __delitem__; key.into())
} else {
let value = py.from_borrowed_ptr::<PyAny>(value);
match value.extract() {
Ok(value) => slf.__setitem__(key.into(), value).into(),
Err(e) => Err(e),
match slf.try_borrow_mut() {
Ok(mut slf_) => match value.extract() {
Ok(value) => slf_.__setitem__(key.into(), value).into(),
Err(e) => Err(e),
},
Err(e) => Err(e.into()),
}
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
Err(e) => e.restore_and_minus1(py),
}
}
Some(wrap::<T>)
@ -418,7 +412,6 @@ where
py_binary_func!(
PySequenceContainsProtocol,
T::__contains__,
bool,
BoolCallbackConverter,
c_int
)
@ -446,8 +439,7 @@ where
py_binary_func!(
PySequenceConcatProtocol,
T::__concat__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -473,8 +465,7 @@ where
py_ssizearg_func!(
PySequenceRepeatProtocol,
T::__repeat__,
T::Success,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T::Success>(std::marker::PhantomData)
)
}
}
@ -500,8 +491,9 @@ where
py_binary_func!(
PySequenceInplaceConcatProtocol,
T::__inplace_concat__,
T,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T>(std::marker::PhantomData),
*mut crate::ffi::PyObject,
call_mut_with_converter
)
}
}
@ -527,8 +519,8 @@ where
py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
T::__inplace_repeat__,
T,
PyObjectCallbackConverter
PyObjectCallbackConverter::<T>(std::marker::PhantomData),
call_mut_with_converter
)
}
}

View File

@ -3,10 +3,10 @@
//! Conversions between various states of rust and python types and their wrappers.
use crate::err::{self, PyDowncastError, PyResult};
use crate::object::PyObject;
use crate::type_object::{PyObjectLayout, PyTypeInfo};
use crate::type_object::{PyDowncastImpl, PyTypeInfo};
use crate::types::PyAny;
use crate::types::PyTuple;
use crate::{ffi, gil, Py, Python};
use crate::{ffi, gil, Py, PyCell, PyClass, PyNativeType, PyRef, PyRefMut, Python};
use std::ptr::NonNull;
/// This trait represents that, **we can do zero-cost conversion from the object to FFI pointer**.
@ -244,80 +244,42 @@ where
}
}
#[doc(hidden)]
pub mod extract_impl {
use super::*;
pub trait ExtractImpl<'a, Target>: Sized {
fn extract(source: &'a PyAny) -> PyResult<Target>;
impl<'a, T> FromPyObject<'a> for &'a PyCell<T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
PyTryFrom::try_from(obj).map_err(Into::into)
}
pub struct Cloned;
pub struct Reference;
pub struct MutReference;
impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned
where
T: Clone,
Reference: ExtractImpl<'a, &'a T>,
{
fn extract(source: &'a PyAny) -> PyResult<T> {
Ok(Reference::extract(source)?.clone())
}
}
impl<'a, T> ExtractImpl<'a, &'a T> for Reference
where
T: PyTryFrom<'a>,
{
fn extract(source: &'a PyAny) -> PyResult<&'a T> {
Ok(T::try_from(source)?)
}
}
impl<'a, T> ExtractImpl<'a, &'a mut T> for MutReference
where
T: PyTryFrom<'a>,
{
fn extract(source: &'a PyAny) -> PyResult<&'a mut T> {
Ok(T::try_from_mut(source)?)
}
}
}
use extract_impl::ExtractImpl;
/// This is an internal trait for re-using `FromPyObject` implementations for many pyo3 types.
///
/// Users should implement `FromPyObject` directly instead of via this trait.
pub trait FromPyObjectImpl {
// Implement this trait with to specify the implementor of `extract_impl::ExtractImpl` to use for
// extracting this type from Python objects.
//
// Example valid implementations are `extract_impl::Cloned`, `extract_impl::Reference`, and
// `extract_impl::MutReference`, which are for extracting `T`, `&T` and `&mut T` respectively via
// PyTryFrom.
//
// We deliberately don't require Impl: ExtractImpl here because we allow #[pyclass]
// to specify an Impl which doesn't satisfy the ExtractImpl constraints.
//
// e.g. non-clone #[pyclass] can still have Impl: Cloned.
//
// We catch invalid Impls in the blanket impl for FromPyObject, which only
// complains when .extract() is actually used.
/// The type which implements `ExtractImpl`.
type Impl;
}
impl<'a, T> FromPyObject<'a> for T
where
T: FromPyObjectImpl,
<T as FromPyObjectImpl>::Impl: ExtractImpl<'a, Self>,
T: PyClass + Clone,
{
#[inline]
fn extract(ob: &'a PyAny) -> PyResult<T> {
<T as FromPyObjectImpl>::Impl::extract(ob)
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<Self> = PyTryFrom::try_from(obj)?;
Ok(unsafe { cell.try_borrow_unguarded()?.clone() })
}
}
impl<'a, T> FromPyObject<'a> for PyRef<'a, T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;
cell.try_borrow().map_err(Into::into)
}
}
impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T>
where
T: PyClass,
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
let cell: &PyCell<T> = PyTryFrom::try_from(obj)?;
cell.try_borrow_mut().map_err(Into::into)
}
}
@ -337,6 +299,22 @@ where
}
}
/// Trait implemented by Python object types that allow a checked downcast.
/// If `T` implements `PyTryFrom`, we can convert `&PyAny` to `&T`.
///
/// This trait is similar to `std::convert::TryFrom`
pub trait PyTryFrom<'v>: Sized + PyDowncastImpl {
/// Cast from a concrete Python object type to PyObject.
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;
/// Cast a PyAny to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self;
}
/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryInto`
pub trait PyTryInto<T>: Sized {
@ -345,37 +323,6 @@ pub trait PyTryInto<T>: Sized {
/// Cast from PyObject to a concrete Python object type. With exact type check.
fn try_into_exact(&self) -> Result<&T, PyDowncastError>;
/// Cast from PyObject to a concrete Python object type.
fn try_into_mut(&self) -> Result<&mut T, PyDowncastError>;
/// Cast from PyObject to a concrete Python object type. With exact type check.
fn try_into_mut_exact(&self) -> Result<&mut T, PyDowncastError>;
}
/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryFrom`
pub trait PyTryFrom<'v>: Sized {
/// Cast from a concrete Python object type to PyObject.
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject.
fn try_from_mut<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut Self, PyDowncastError>;
/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_mut_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut Self, PyDowncastError>;
/// Cast a PyAny to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self;
/// Cast a PyAny to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
#[allow(clippy::mut_from_ref)]
unsafe fn try_from_mut_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v mut Self;
}
// TryFrom implies TryInto
@ -389,56 +336,28 @@ where
fn try_into_exact(&self) -> Result<&U, PyDowncastError> {
U::try_from_exact(self)
}
fn try_into_mut(&self) -> Result<&mut U, PyDowncastError> {
U::try_from_mut(self)
}
fn try_into_mut_exact(&self) -> Result<&mut U, PyDowncastError> {
U::try_from_mut_exact(self)
}
}
impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo,
T: PyDowncastImpl + PyTypeInfo + PyNativeType,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v T, PyDowncastError> {
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(PyTryFrom::try_from_unchecked(value))
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v T, PyDowncastError> {
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(PyTryFrom::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_mut<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(PyTryFrom::try_from_mut_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_mut_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(PyTryFrom::try_from_mut_unchecked(value))
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
@ -446,15 +365,38 @@ where
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v T {
let value = value.into();
T::ConcreteLayout::internal_ref_cast(value)
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
Self::unchecked_downcast(value.into())
}
}
#[inline]
unsafe fn try_from_mut_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v mut T {
impl<'v, T> PyTryFrom<'v> for PyCell<T>
where
T: 'v + PyClass,
{
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
T::ConcreteLayout::internal_mut_cast(value)
unsafe {
if T::is_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(Self::try_from_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self {
Self::unchecked_downcast(value.into())
}
}
@ -467,33 +409,37 @@ impl FromPy<()> for Py<PyTuple> {
/// Raw level conversion between `*mut ffi::PyObject` and PyO3 types.
pub unsafe trait FromPyPointer<'p>: Sized {
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self>;
unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self>;
unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
match Self::from_owned_ptr_or_opt(py, ptr) {
Some(s) => s,
None => err::panic_after_error(),
}
}
unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
Self::from_owned_ptr_or_panic(py, ptr)
}
unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<Self> {
unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<&'p Self> {
match Self::from_owned_ptr_or_opt(py, ptr) {
Some(s) => Ok(s),
None => Err(err::PyErr::fetch(py)),
}
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self>;
unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject)
-> Option<&'p Self>;
unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(s) => s,
None => err::panic_after_error(),
}
}
unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self {
unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self {
Self::from_borrowed_ptr_or_panic(py, ptr)
}
unsafe fn from_borrowed_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<Self> {
unsafe fn from_borrowed_ptr_or_err(
py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> PyResult<&'p Self> {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(s) => Ok(s),
None => Err(err::PyErr::fetch(py)),
@ -501,29 +447,33 @@ pub unsafe trait FromPyPointer<'p>: Sized {
}
}
unsafe impl<'p, T> FromPyPointer<'p> for &'p T
unsafe impl<'p, T> FromPyPointer<'p> for T
where
T: PyTypeInfo,
T: 'p + crate::PyNativeType,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| T::ConcreteLayout::internal_ref_cast(gil::register_owned(py, p)))
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| Self::unchecked_downcast(gil::register_owned(py, p)))
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr)
.map(|p| T::ConcreteLayout::internal_ref_cast(gil::register_borrowed(py, p)))
unsafe fn from_borrowed_ptr_or_opt(
py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| Self::unchecked_downcast(gil::register_borrowed(py, p)))
}
}
unsafe impl<'p, T> FromPyPointer<'p> for &'p mut T
unsafe impl<'p, T> FromPyPointer<'p> for PyCell<T>
where
T: PyTypeInfo,
T: PyClass,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| T::ConcreteLayout::internal_mut_cast(gil::register_owned(py, p)))
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell<T>))
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr)
.map(|p| T::ConcreteLayout::internal_mut_cast(gil::register_borrowed(py, p)))
unsafe fn from_borrowed_ptr_or_opt(
py: Python<'p>,
ptr: *mut ffi::PyObject,
) -> Option<&'p Self> {
NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell<T>))
}
}

View File

@ -182,8 +182,8 @@ impl<T: IntoPy<PyObject>> IntoPyResult<T> for PyResult<T> {
}
}
/// Variant of IntoPyResult for the specific case of #[new]. In the case of returning (Sub, Base)
/// from #[new], IntoPyResult can't apply because (Sub, Base) doesn't implement IntoPy<PyObject>.
/// Variant of IntoPyResult for the specific case of `#[new]`. In the case of returning (Sub, Base)
/// from `#[new]`, IntoPyResult can't apply because (Sub, Base) doesn't implement IntoPy<PyObject>.
pub trait IntoPyNewResult<T: PyClass, I: Into<PyClassInitializer<T>>> {
fn into_pynew_result(self) -> PyResult<I>;
}
@ -218,3 +218,18 @@ impl GetPropertyValue for PyObject {
self.clone_ref(py)
}
}
/// Utilities for basetype
pub trait PyBaseTypeUtils {
type Dict;
type WeakRef;
type LayoutAsBase;
type BaseNativeType;
}
impl<T: PyClass> PyBaseTypeUtils for T {
type Dict = T::Dict;
type WeakRef = T::WeakRef;
type LayoutAsBase = crate::pycell::PyCellInner<T>;
type BaseNativeType = T::BaseNativeType;
}

View File

@ -331,13 +331,20 @@ impl PyErr {
unsafe { ffi::PyErr_Restore(ptype.into_ptr(), pvalue, ptraceback.into_ptr()) }
}
#[doc(hidden)]
/// Utility method for proc-macro code
#[doc(hidden)]
pub fn restore_and_null<T>(self, py: Python) -> *mut T {
self.restore(py);
std::ptr::null_mut()
}
/// Utility method for proc-macro code
#[doc(hidden)]
pub fn restore_and_minus1(self, py: Python) -> crate::libc::c_int {
self.restore(py);
-1
}
/// Issue a warning message.
/// May return a PyErr if warnings-as-errors is enabled.
pub fn warn(py: Python, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
@ -401,7 +408,7 @@ impl<'a> IntoPy<PyObject> for &'a PyErr {
}
}
/// Converts `PyDowncastError` to Python `TypeError`.
/// Convert `PyDowncastError` to Python `TypeError`.
impl std::convert::From<PyDowncastError> for PyErr {
fn from(_err: PyDowncastError) -> PyErr {
exceptions::TypeError.into()

View File

@ -21,6 +21,7 @@ pub struct setentry {
}
#[repr(C)]
#[derive(Debug)]
pub struct PySetObject {
pub ob_base: PyObject,
pub fill: Py_ssize_t,

View File

@ -4,7 +4,7 @@
use crate::ffi;
use crate::pyclass::{tp_free_fallback, PyClassAlloc};
use crate::type_object::{PyObjectLayout, PyTypeInfo};
use crate::type_object::{PyLayout, PyTypeInfo};
use crate::Python;
use std::mem;
use std::os::raw::c_void;
@ -72,7 +72,7 @@ impl<T> PyClassAlloc for T
where
T: PyTypeInfo + PyClassWithFreeList,
{
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
unsafe fn alloc(_py: Python) -> *mut Self::Layout {
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list().pop() {
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::type_object() as *const _ as _);
obj as _
@ -81,7 +81,7 @@ where
}
}
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
unsafe fn dealloc(py: Python, self_: *mut Self::Layout) {
(*self_).py_drop(py);
let obj = self_ as _;

View File

@ -3,12 +3,12 @@ use crate::err::{PyErr, PyResult};
use crate::gil;
use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::pyclass::{PyClass, PyClassShell};
use crate::pyclass_init::PyClassInitializer;
use crate::type_object::{PyObjectLayout, PyTypeInfo};
use crate::type_object::{PyBorrowFlagLayout, PyDowncastImpl};
use crate::types::PyAny;
use crate::{ffi, IntoPy};
use crate::{AsPyPointer, FromPyObject, IntoPyPointer, Python, ToPyObject};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyCell, PyClass, PyClassInitializer,
PyRef, PyRefMut, Python, ToPyObject,
};
use std::marker::PhantomData;
use std::mem;
use std::ptr::NonNull;
@ -35,15 +35,18 @@ unsafe impl<T> Send for Py<T> {}
unsafe impl<T> Sync for Py<T> {}
impl<T> Py<T> {
/// Create new instance of T and move it under python management
/// Create a new instance `Py<T>`.
///
/// This method is **soft-duplicated** since PyO3 0.9.0.
/// Use [`PyCell::new`](../pycell/struct.PyCell.html#method.new) and
/// `Py::from` instead.
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<Py<T>>
where
T: PyClass,
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
let initializer = value.into();
let obj = unsafe { initializer.create_shell(py)? };
let obj = unsafe { initializer.create_cell(py)? };
let ob = unsafe { Py::from_owned_ptr(obj as _) };
Ok(ob)
}
@ -119,15 +122,47 @@ impl<T> Py<T> {
}
}
pub trait AsPyRef<T: PyTypeInfo>: Sized {
/// Retrives `&'py` types from `Py<T>` or `PyObject`.
///
/// # Examples
/// `PyObject::as_ref` returns `&PyAny`.
/// ```
/// # use pyo3::prelude::*;
/// use pyo3::ObjectProtocol;
/// let obj: PyObject = {
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// py.eval("[]", None, None).unwrap().to_object(py)
/// };
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// assert_eq!(obj.as_ref(py).len().unwrap(), 0); // PyAny implements ObjectProtocol
/// ```
/// `Py<T>::as_ref` returns `&PyDict`, `&PyList` or so for native types, and `&PyCell<T>`
/// for `#[pyclass]`.
/// ```
/// # use pyo3::prelude::*;
/// use pyo3::ObjectProtocol;
/// let obj: PyObject = {
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// py.eval("[]", None, None).unwrap().to_object(py)
/// };
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// assert_eq!(obj.as_ref(py).len().unwrap(), 0); // PyAny implements ObjectProtocol
/// ```
pub trait AsPyRef: Sized {
type Target;
/// Return reference to object.
fn as_ref(&self, py: Python) -> &T;
fn as_ref(&self, py: Python<'_>) -> &Self::Target;
}
impl<T: PyTypeInfo> AsPyRef<T> for Py<T> {
fn as_ref(&self, _py: Python) -> &T {
impl<'p, T: PyClass> AsPyRef for Py<T> {
type Target = PyCell<T>;
fn as_ref(&self, _py: Python) -> &PyCell<T> {
let any = self as *const Py<T> as *const PyAny;
unsafe { T::ConcreteLayout::internal_ref_cast(&*any) }
unsafe { PyDowncastImpl::unchecked_downcast(&*any) }
}
}
@ -174,22 +209,31 @@ where
}
}
// `&PyClassShell<T>` can be converted to `Py<T>`
impl<'a, T> std::convert::From<&PyClassShell<T>> for Py<T>
// `&PyCell<T>` can be converted to `Py<T>`
impl<'a, T> std::convert::From<&PyCell<T>> for Py<T>
where
T: PyClass,
{
fn from(shell: &PyClassShell<T>) -> Self {
unsafe { Py::from_borrowed_ptr(shell.as_ptr()) }
fn from(cell: &PyCell<T>) -> Self {
unsafe { Py::from_borrowed_ptr(cell.as_ptr()) }
}
}
impl<'a, T> std::convert::From<&mut PyClassShell<T>> for Py<T>
impl<'a, T> std::convert::From<PyRef<'a, T>> for Py<T>
where
T: PyClass,
{
fn from(shell: &mut PyClassShell<T>) -> Self {
unsafe { Py::from_borrowed_ptr(shell.as_ptr()) }
fn from(pyref: PyRef<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.as_ptr()) }
}
}
impl<'a, T> std::convert::From<PyRefMut<'a, T>> for Py<T>
where
T: PyClass,
{
fn from(pyref: PyRefMut<'a, T>) -> Self {
unsafe { Py::from_borrowed_ptr(pyref.as_ptr()) }
}
}

View File

@ -23,3 +23,10 @@ macro_rules! private_impl {
}
}
}
macro_rules! pyo3_exception {
($name: ident, $base: ty) => {
$crate::impl_exception_boilerplate!($name);
$crate::create_exception_type_object!(pyo3_runtime, $name, $base);
};
}

View File

@ -128,7 +128,8 @@ pub use crate::gil::{init_once, GILGuard, GILPool};
pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType};
pub use crate::object::PyObject;
pub use crate::objectprotocol::ObjectProtocol;
pub use crate::pyclass::{PyClass, PyClassShell};
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass::PyClass;
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::python::{prepare_freethreaded_python, Python};
pub use crate::type_object::{type_flags, PyTypeInfo};
@ -171,6 +172,7 @@ pub mod marshal;
mod object;
mod objectprotocol;
pub mod prelude;
pub mod pycell;
pub mod pyclass;
pub mod pyclass_init;
pub mod pyclass_slots;
@ -222,7 +224,7 @@ macro_rules! wrap_pymodule {
///
/// # Example
/// ```
/// use pyo3::{prelude::*, py_run, PyClassShell};
/// use pyo3::{prelude::*, py_run, PyCell};
/// #[pyclass]
/// #[derive(Debug)]
/// struct Time {
@ -245,7 +247,7 @@ macro_rules! wrap_pymodule {
/// }
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let time = PyClassShell::new_ref(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
/// let time_as_tuple = (8, 43, 16);
/// py_run!(py, time time_as_tuple, r#"
/// assert time.hour == 8

View File

@ -149,16 +149,16 @@ impl PyObject {
}
/// Casts the PyObject to a concrete Python object type.
pub fn cast_as<D>(&self, py: Python) -> Result<&D, PyDowncastError>
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError>
where
D: for<'v> PyTryFrom<'v>,
D: PyTryFrom<'p>,
{
D::try_from(self.as_ref(py))
}
/// Extracts some type from the Python object.
/// This is a wrapper function around `FromPyObject::extract()`.
pub fn extract<'p, D>(&'p self, py: Python) -> PyResult<D>
pub fn extract<'p, D>(&'p self, py: Python<'p>) -> PyResult<D>
where
D: FromPyObject<'p>,
{
@ -250,7 +250,8 @@ impl PyObject {
}
}
impl AsPyRef<PyAny> for PyObject {
impl AsPyRef for PyObject {
type Target = PyAny;
fn as_ref(&self, _py: Python) -> &PyAny {
unsafe { &*(self as *const _ as *const PyAny) }
}

View File

@ -3,16 +3,11 @@
use crate::class::basic::CompareOp;
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::exceptions::TypeError;
use crate::ffi;
use crate::instance::PyNativeType;
use crate::object::PyObject;
use crate::type_object::PyTypeInfo;
use crate::types::{PyAny, PyDict, PyIterator, PyString, PyTuple, PyType};
use crate::AsPyPointer;
use crate::IntoPyPointer;
use crate::Py;
use crate::Python;
use crate::{FromPyObject, IntoPy, PyTryFrom, ToBorrowedObject, ToPyObject};
use crate::{
ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyNativeType, PyObject, PyTryFrom,
Python, ToBorrowedObject, ToPyObject,
};
use std::cmp::Ordering;
use std::os::raw::c_int;
@ -175,17 +170,6 @@ pub trait ObjectProtocol {
/// Gets the Python type pointer for this object.
fn get_type_ptr(&self) -> *mut ffi::PyTypeObject;
/// Gets the Python base object for this object.
fn get_base(&self) -> &<Self as PyTypeInfo>::BaseType
where
Self: PyTypeInfo;
/// Gets the Python base object for this object.
fn get_mut_base(&mut self) -> &mut <Self as PyTypeInfo>::BaseType
where
Self: PyTypeInfo;
/// Casts the PyObject to a concrete Python object type.
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
@ -455,20 +439,6 @@ where
unsafe { (*self.as_ptr()).ob_type }
}
fn get_base(&self) -> &<Self as PyTypeInfo>::BaseType
where
Self: PyTypeInfo,
{
unsafe { self.py().from_borrowed_ptr(self.as_ptr()) }
}
fn get_mut_base(&mut self) -> &mut <Self as PyTypeInfo>::BaseType
where
Self: PyTypeInfo,
{
unsafe { self.py().mut_from_borrowed_ptr(self.as_ptr()) }
}
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError>
where
D: PyTryFrom<'a>,

View File

@ -15,6 +15,7 @@ pub use crate::gil::GILGuard;
pub use crate::instance::{AsPyRef, Py};
pub use crate::object::PyObject;
pub use crate::objectprotocol::ObjectProtocol;
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::python::Python;
pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};

678
src/pycell.rs Normal file
View File

@ -0,0 +1,678 @@
//! Includes `PyCell` implementation.
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
use crate::pyclass_init::PyClassInitializer;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{PyBorrowFlagLayout, PyDowncastImpl, PyLayout, PySizedLayout, PyTypeInfo};
use crate::types::PyAny;
use crate::{ffi, FromPy, PyClass, PyErr, PyNativeType, PyObject, PyResult, Python};
use std::cell::{Cell, UnsafeCell};
use std::fmt;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
/// Base layout of PyCell.
/// This is necessary for sharing BorrowFlag between parents and children.
#[doc(hidden)]
#[repr(C)]
pub struct PyCellBase<T: PyTypeInfo> {
ob_base: T::Layout,
borrow_flag: Cell<BorrowFlag>,
}
unsafe impl<T> PyLayout<T> for PyCellBase<T>
where
T: PyTypeInfo + PyNativeType,
T::Layout: PySizedLayout<T>,
{
const IS_NATIVE_TYPE: bool = true;
unsafe fn get_ptr(&self) -> *mut T {
(&self) as *const &Self as *const _ as *mut _
}
}
// Thes impls ensures `PyCellBase` can be a base type.
impl<T> PySizedLayout<T> for PyCellBase<T>
where
T: PyTypeInfo + PyNativeType,
T::Layout: PySizedLayout<T>,
{
}
unsafe impl<T> PyBorrowFlagLayout<T> for PyCellBase<T>
where
T: PyTypeInfo + PyNativeType,
T::Layout: PySizedLayout<T>,
{
}
/// Inner type of `PyCell` without dict slots and reference counter.
/// This struct has two usages:
/// 1. As an inner type of `PyRef` and `PyRefMut`.
/// 2. When `#[pyclass(extends=Base)]` is specified, `PyCellInner<Base>` is used as a base layout.
#[doc(hidden)]
#[repr(C)]
pub struct PyCellInner<T: PyClass> {
ob_base: T::BaseLayout,
value: ManuallyDrop<UnsafeCell<T>>,
}
impl<T: PyClass> AsPyPointer for PyCellInner<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _
}
}
unsafe impl<T: PyClass> PyLayout<T> for PyCellInner<T> {
const IS_NATIVE_TYPE: bool = false;
fn get_super(&mut self) -> Option<&mut T::BaseLayout> {
Some(&mut self.ob_base)
}
unsafe fn get_ptr(&self) -> *mut T {
self.value.get()
}
unsafe fn py_init(&mut self, value: T) {
self.value = ManuallyDrop::new(UnsafeCell::new(value));
}
unsafe fn py_drop(&mut self, py: Python) {
ManuallyDrop::drop(&mut self.value);
self.ob_base.py_drop(py);
}
}
// These impls ensures `PyCellInner` can be a base type.
impl<T: PyClass> PySizedLayout<T> for PyCellInner<T> {}
unsafe impl<T: PyClass> PyBorrowFlagLayout<T> for PyCellInner<T> {}
impl<T: PyClass> PyCellInner<T> {
fn get_borrow_flag(&self) -> BorrowFlag {
let base = (&self.ob_base) as *const _ as *const PyCellBase<T::BaseNativeType>;
unsafe { (*base).borrow_flag.get() }
}
fn set_borrow_flag(&self, flag: BorrowFlag) {
let base = (&self.ob_base) as *const _ as *const PyCellBase<T::BaseNativeType>;
unsafe { (*base).borrow_flag.set(flag) }
}
}
/// `PyCell` is the container type for [`PyClass`](../pyclass/trait.PyClass.html).
///
/// From Python side, `PyCell<T>` is the concrete layout of `T: PyClass` in the Python heap,
/// which means we can convert `*const PyClass<T>` to `*mut ffi::PyObject`.
///
/// From Rust side, `PyCell<T>` is the mutable container of `T`.
/// Since `PyCell<T: PyClass>` is always on the Python heap, we don't have the ownership of it.
/// Thus, to mutate the data behind `&PyCell<T>` safely, we employ the
/// [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html)
/// like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html).
///
/// # Examples
///
/// In most cases, `PyCell` is hidden behind `#[pymethods]`.
/// However, you can construct `&PyCell` directly to test your pyclass in Rust code.
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Book {
/// #[pyo3(get)]
/// name: &'static str,
/// author: &'static str,
/// }
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let book = Book {
/// name: "The Man in the High Castle",
/// author: "Philip Kindred Dick",
/// };
/// let book_cell = PyCell::new(py, book).unwrap();
/// // you can expose PyCell to Python snippets
/// pyo3::py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'");
/// ```
/// You can use `slf: &PyCell<Self>` as an alternative `self` receiver of `#[pymethod]`,
/// though you rarely need it.
/// ```
/// # use pyo3::prelude::*;
/// use std::collections::HashMap;
/// #[pyclass]
/// #[derive(Default)]
/// struct Counter {
/// counter: HashMap<String, usize>
/// }
/// #[pymethods]
/// impl Counter {
/// // You can use &mut self here, but now we use &PyCell for demonstration
/// fn increment(slf: &PyCell<Self>, name: String) -> PyResult<usize> {
/// let mut slf_mut = slf.try_borrow_mut()?;
/// // Now a mutable reference exists so we cannot get another one
/// assert!(slf.try_borrow().is_err());
/// assert!(slf.try_borrow_mut().is_err());
/// let counter = slf_mut.counter.entry(name).or_insert(0);
/// *counter += 1;
/// Ok(*counter)
/// }
/// }
/// # let gil = Python::acquire_gil();
/// # let py = gil.python();
/// # let counter = PyCell::new(py, Counter::default()).unwrap();
/// # pyo3::py_run!(py, counter, "assert counter.increment('cat') == 1");
/// ```
#[repr(C)]
pub struct PyCell<T: PyClass> {
inner: PyCellInner<T>,
dict: T::Dict,
weakref: T::WeakRef,
}
impl<T: PyClass> PyCell<T> {
/// Make new `PyCell` on the Python heap and returns the reference of it.
///
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self>
where
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
unsafe {
let initializer = value.into();
let self_ = initializer.create_cell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
}
}
/// Immutably borrows the value `T`. This borrow lasts untill the returned `PyRef` exists.
///
/// # Panics
///
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow`](#method.try_borrow).
pub fn borrow(&self) -> PyRef<'_, T> {
self.try_borrow().expect("Already mutably borrowed")
}
/// Mutably borrows the value `T`. This borrow lasts untill the returned `PyRefMut` exists.
///
/// # Panics
///
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
pub fn borrow_mut(&self) -> PyRefMut<'_, T> {
self.try_borrow_mut().expect("Already borrowed")
}
/// Immutably borrows the value `T`, returning an error if the value is currently
/// mutably borrowed. This borrow lasts untill the returned `PyRef` exists.
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
///
/// # Examples
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let c = PyCell::new(py, Class {}).unwrap();
/// {
/// let m = c.borrow_mut();
/// assert!(c.try_borrow().is_err());
/// }
///
/// {
/// let m = c.borrow();
/// assert!(c.try_borrow().is_ok());
/// }
/// ```
pub fn try_borrow(&self) -> Result<PyRef<'_, T>, PyBorrowError> {
let flag = self.inner.get_borrow_flag();
if flag == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
} else {
self.inner.set_borrow_flag(flag.increment());
Ok(PyRef { inner: &self.inner })
}
}
/// Mutably borrows the value `T`, returning an error if the value is currently borrowed.
/// This borrow lasts untill the returned `PyRefMut` exists.
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
///
/// # Examples
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let c = PyCell::new(py, Class {}).unwrap();
/// {
/// let m = c.borrow();
/// assert!(c.try_borrow_mut().is_err());
/// }
///
/// assert!(c.try_borrow_mut().is_ok());
/// ```
pub fn try_borrow_mut(&self) -> Result<PyRefMut<'_, T>, PyBorrowMutError> {
if self.inner.get_borrow_flag() != BorrowFlag::UNUSED {
Err(PyBorrowMutError { _private: () })
} else {
self.inner.set_borrow_flag(BorrowFlag::HAS_MUTABLE_BORROW);
Ok(PyRefMut { inner: &self.inner })
}
}
/// Immutably borrows the value `T`, returning an error if the value is
/// currently mutably borrowed.
///
/// # Safety
///
/// This method is unsafe because it does not return a `PyRef`,
/// thus leaving the borrow flag untouched. Mutably borrowing the `PyCell`
/// while the reference returned by this method is alive is undefined behaviour.
///
/// # Examples
///
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Class {}
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let c = PyCell::new(py, Class {}).unwrap();
///
/// {
/// let m = c.borrow_mut();
/// assert!(unsafe { c.try_borrow_unguarded() }.is_err());
/// }
///
/// {
/// let m = c.borrow();
/// assert!(unsafe { c.try_borrow_unguarded() }.is_ok());
/// }
/// ```
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> {
if self.inner.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW {
Err(PyBorrowError { _private: () })
} else {
Ok(&*self.inner.value.get())
}
}
/// Replaces the wrapped value with a new one, returning the old value,
///
/// # Panics
///
/// Panics if the value is currently borrowed.
#[inline]
pub fn replace(&self, t: T) -> T {
std::mem::replace(&mut *self.borrow_mut(), t)
}
/// Replaces the wrapped value with a new one computed from `f`, returning the old value.
///
/// # Panics
///
/// Panics if the value is currently borrowed.
pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T {
let mut_borrow = &mut *self.borrow_mut();
let replacement = f(mut_borrow);
std::mem::replace(mut_borrow, replacement)
}
/// Swaps the wrapped value of `self` with the wrapped value of `other`.
///
/// # Panics
///
/// Panics if the value in either `PyCell` is currently borrowed.
#[inline]
pub fn swap(&self, other: &Self) {
std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
}
/// Allocates new PyCell without initilizing value.
/// Requires `T::BaseLayout: PyBorrowFlagLayout<T::BaseType>` to ensure `self` has a borrow flag.
pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self>
where
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
let base = T::alloc(py);
if base.is_null() {
return Err(PyErr::fetch(py));
}
let base = base as *mut PyCellBase<T::BaseNativeType>;
(*base).borrow_flag = Cell::new(BorrowFlag::UNUSED);
let self_ = base as *mut Self;
(*self_).dict = T::Dict::new();
(*self_).weakref = T::WeakRef::new();
Ok(self_)
}
}
unsafe impl<T: PyClass> PyLayout<T> for PyCell<T> {
const IS_NATIVE_TYPE: bool = false;
fn get_super(&mut self) -> Option<&mut T::BaseLayout> {
Some(&mut self.inner.ob_base)
}
unsafe fn get_ptr(&self) -> *mut T {
self.inner.get_ptr()
}
unsafe fn py_init(&mut self, value: T) {
self.inner.value = ManuallyDrop::new(UnsafeCell::new(value));
}
unsafe fn py_drop(&mut self, py: Python) {
ManuallyDrop::drop(&mut self.inner.value);
self.dict.clear_dict(py);
self.weakref.clear_weakrefs(self.as_ptr(), py);
self.inner.ob_base.py_drop(py);
}
}
unsafe impl<T: PyClass> PyDowncastImpl for PyCell<T> {
unsafe fn unchecked_downcast(obj: &PyAny) -> &Self {
&*(obj.as_ptr() as *const Self)
}
private_impl! {}
}
impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
self.inner.as_ptr()
}
}
impl<T: PyClass> ToPyObject for &PyCell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_borrow() {
Ok(borrow) => f.debug_struct("RefCell").field("value", &borrow).finish(),
Err(_) => {
struct BorrowedPlaceholder;
impl fmt::Debug for BorrowedPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<borrowed>")
}
}
f.debug_struct("RefCell")
.field("value", &BorrowedPlaceholder)
.finish()
}
}
}
}
/// Wraps a borrowed reference to a value in a `PyCell<T>`.
///
/// See the [`PyCell`](struct.PyCell.html) documentation for more.
/// # Example
/// You can use `PyRef` as an alternative of `&self` receiver when
/// - You need to access the pointer of `PyCell`.
/// - You want to get super class.
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Parent {
/// basename: &'static str,
/// }
/// #[pyclass(extends=Parent)]
/// struct Child {
/// name: &'static str,
/// }
/// #[pymethods]
/// impl Child {
/// #[new]
/// fn new() -> (Self, Parent) {
/// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" })
/// }
/// fn format(slf: PyRef<Self>) -> String {
/// // We can get *mut ffi::PyObject from PyRef
/// use pyo3::AsPyPointer;
/// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) };
/// // We can get &Self::BaseType by as_ref
/// let basename = slf.as_ref().basename;
/// format!("{}(base: {}, cnt: {})", slf.name, basename, refcnt)
/// }
/// }
/// # let gil = Python::acquire_gil();
/// # let py = gil.python();
/// # let sub = PyCell::new(py, Child::new()).unwrap();
/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)'");
/// ```
pub struct PyRef<'p, T: PyClass> {
inner: &'p PyCellInner<T>,
}
impl<'p, T: PyClass> AsRef<T::BaseType> for PyRef<'p, T> {
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
}
}
impl<'p, T, U> PyRef<'p, T>
where
T: PyClass + PyTypeInfo<BaseType = U, BaseLayout = PyCellInner<U>>,
U: PyClass,
{
/// Get `PyRef<T::BaseType>`.
/// You can use this method to get super class of super class.
///
/// # Examples
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct Base1 {
/// name1: &'static str,
/// }
/// #[pyclass(extends=Base1)]
/// struct Base2 {
/// name2: &'static str,
/// }
/// #[pyclass(extends=Base2)]
/// struct Sub {
/// name3: &'static str,
/// }
/// #[pymethods]
/// impl Sub {
/// #[new]
/// fn new() -> PyClassInitializer<Self> {
/// PyClassInitializer::from(Base1{ name1: "base1" })
/// .add_subclass(Base2 { name2: "base2" })
/// .add_subclass(Self { name3: "sub" })
/// }
/// fn name(slf: PyRef<Self>) -> String {
/// let subname = slf.name3;
/// let super_ = slf.into_super();
/// format!("{} {} {}", super_.as_ref().name1, super_.name2, subname)
/// }
/// }
/// # let gil = Python::acquire_gil();
/// # let py = gil.python();
/// # let sub = PyCell::new(py, Sub::new()).unwrap();
/// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'")
/// ```
pub fn into_super(self) -> PyRef<'p, U> {
let PyRef { inner } = self;
std::mem::forget(self);
PyRef {
inner: &inner.ob_base,
}
}
}
impl<'p, T: PyClass> Deref for PyRef<'p, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.inner.get_ptr() }
}
}
impl<'p, T: PyClass> Drop for PyRef<'p, T> {
fn drop(&mut self) {
let flag = self.inner.get_borrow_flag();
self.inner.set_borrow_flag(flag.decrement())
}
}
impl<'p, T: PyClass> FromPy<PyRef<'p, T>> for PyObject {
fn from_py(pyref: PyRef<'p, T>, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, pyref.inner.as_ptr()) }
}
}
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> {
type Error = PyBorrowError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
cell.try_borrow()
}
}
impl<'a, T: PyClass> AsPyPointer for PyRef<'a, T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
self.inner.as_ptr()
}
}
impl<T: PyClass + fmt::Debug> fmt::Debug for PyRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
/// Wraps a mutable borrowed reference to a value in a `PyCell<T>`.
///
/// See the [`PyCell`](struct.PyCell.html) and [`PyRef`](struct.PyRef.html) documentations for more.
pub struct PyRefMut<'p, T: PyClass> {
inner: &'p PyCellInner<T>,
}
impl<'p, T: PyClass> AsRef<T::BaseType> for PyRefMut<'p, T> {
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
}
}
impl<'p, T: PyClass> AsMut<T::BaseType> for PyRefMut<'p, T> {
fn as_mut(&mut self) -> &mut T::BaseType {
unsafe { &mut *self.inner.ob_base.get_ptr() }
}
}
impl<'p, T, U> PyRefMut<'p, T>
where
T: PyClass + PyTypeInfo<BaseType = U, BaseLayout = PyCellInner<U>>,
U: PyClass,
{
/// Get `PyRef<T::BaseType>`.
/// See [`PyRef::into_super`](struct.PyRef.html#method.into_super) for more.
pub fn into_super(self) -> PyRefMut<'p, U> {
let PyRefMut { inner } = self;
std::mem::forget(self);
PyRefMut {
inner: &inner.ob_base,
}
}
}
impl<'p, T: PyClass> Deref for PyRefMut<'p, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.inner.get_ptr() }
}
}
impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.inner.get_ptr() }
}
}
impl<'p, T: PyClass> Drop for PyRefMut<'p, T> {
fn drop(&mut self) {
self.inner.set_borrow_flag(BorrowFlag::UNUSED)
}
}
impl<'p, T: PyClass> FromPy<PyRefMut<'p, T>> for PyObject {
fn from_py(pyref: PyRefMut<'p, T>, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, pyref.inner.as_ptr()) }
}
}
impl<'a, T: PyClass> AsPyPointer for PyRefMut<'a, T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
self.inner.as_ptr()
}
}
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRefMut<'a, T> {
type Error = PyBorrowMutError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
cell.try_borrow_mut()
}
}
impl<T: PyClass + fmt::Debug> fmt::Debug for PyRefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&*(self.deref()), f)
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
struct BorrowFlag(usize);
impl BorrowFlag {
const UNUSED: BorrowFlag = BorrowFlag(0);
const HAS_MUTABLE_BORROW: BorrowFlag = BorrowFlag(usize::max_value());
const fn increment(self) -> Self {
Self(self.0 + 1)
}
const fn decrement(self) -> Self {
Self(self.0 - 1)
}
}
/// An error returned by [`PyCell::try_borrow`](struct.PyCell.html#method.try_borrow).
pub struct PyBorrowError {
_private: (),
}
impl fmt::Debug for PyBorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PyBorrowError").finish()
}
}
impl fmt::Display for PyBorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("Already mutably borrowed", f)
}
}
/// An error returned by [`PyCell::try_borrow_mut`](struct.PyCell.html#method.try_borrow_mut).
pub struct PyBorrowMutError {
_private: (),
}
impl fmt::Debug for PyBorrowMutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PyBorrowMutError").finish()
}
}
impl fmt::Display for PyBorrowMutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("Already borrowed", f)
}
}
pyo3_exception!(PyBorrowError, crate::exceptions::Exception);
pyo3_exception!(PyBorrowMutError, crate::exceptions::Exception);

View File

@ -1,22 +1,17 @@
//! Traits and structs for `#[pyclass]`.
//! `PyClass` trait
use crate::class::methods::{PyMethodDefType, PyMethodsProtocol};
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
use crate::pyclass_init::PyClassInitializer;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{type_flags, PyObjectLayout, PyObjectSizedLayout};
use crate::types::PyAny;
use crate::{class, ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python};
use crate::type_object::{type_flags, PyLayout};
use crate::{class, ffi, gil, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python};
use std::ffi::CString;
use std::mem::ManuallyDrop;
use std::os::raw::c_void;
use std::ptr::{self, NonNull};
use std::ptr;
#[inline]
pub(crate) unsafe fn default_alloc<T: PyTypeInfo>() -> *mut ffi::PyObject {
let type_obj = T::type_object();
if T::FLAGS & type_flags::EXTENDED != 0
&& <T::BaseType as PyTypeInfo>::ConcreteLayout::IS_NATIVE_TYPE
{
// if the class derives native types(e.g., PyDict), call special new
if T::FLAGS & type_flags::EXTENDED != 0 && T::BaseLayout::IS_NATIVE_TYPE {
let base_tp = <T::BaseType as PyTypeInfo>::type_object();
if let Some(base_new) = base_tp.tp_new {
return base_new(type_obj as *const _ as _, ptr::null_mut(), ptr::null_mut());
@ -32,7 +27,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized {
///
/// # Safety
/// This function must return a valid pointer to the Python heap.
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
unsafe fn alloc(_py: Python) -> *mut Self::Layout {
default_alloc::<Self>() as _
}
@ -40,7 +35,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized {
///
/// # Safety
/// `self_` must be a valid pointer to the Python heap.
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
unsafe fn dealloc(py: Python, self_: *mut Self::Layout) {
(*self_).py_drop(py);
let obj = self_ as _;
if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 {
@ -71,185 +66,20 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) {
}
/// If `PyClass` is implemented for `T`, then we can use `T` in the Python world,
/// via `PyClassShell`.
/// via `PyCell`.
///
/// The `#[pyclass]` attribute automatically implements this trait for your Rust struct,
/// so you don't have to use this trait directly.
pub trait PyClass:
PyTypeInfo<ConcreteLayout = PyClassShell<Self>> + Sized + PyClassAlloc + PyMethodsProtocol
PyTypeInfo<Layout = PyCell<Self>> + Sized + PyClassAlloc + PyMethodsProtocol
{
/// Specify this class has `#[pyclass(dict)]` or not.
type Dict: PyClassDict;
/// Specify this class has `#[pyclass(weakref)]` or not.
type WeakRef: PyClassWeakRef;
}
/// `PyClassShell` represents the concrete layout of `T: PyClass` when it is converted
/// to a Python class.
///
/// You can use it to test your `#[pyclass]` correctly works.
///
/// ```
/// # use pyo3::prelude::*;
/// # use pyo3::{py_run, PyClassShell};
/// #[pyclass]
/// struct Book {
/// #[pyo3(get)]
/// name: &'static str,
/// author: &'static str,
/// }
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let book = Book {
/// name: "The Man in the High Castle",
/// author: "Philip Kindred Dick",
/// };
/// let book_shell = PyClassShell::new_ref(py, book).unwrap();
/// py_run!(py, book_shell, "assert book_shell.name[-6:] == 'Castle'");
/// ```
#[repr(C)]
pub struct PyClassShell<T: PyClass> {
ob_base: <T::BaseType as PyTypeInfo>::ConcreteLayout,
pyclass: ManuallyDrop<T>,
dict: T::Dict,
weakref: T::WeakRef,
}
impl<T: PyClass> PyClassShell<T> {
/// Make new `PyClassShell` on the Python heap and returns the reference of it.
pub fn new_ref(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self>
where
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
unsafe {
let initializer = value.into();
let self_ = initializer.create_shell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
}
}
/// Make new `PyClassShell` on the Python heap and returns the mutable reference of it.
pub fn new_mut(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&mut Self>
where
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
unsafe {
let initializer = value.into();
let self_ = initializer.create_shell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
}
}
/// Get the reference of base object.
pub fn get_super(&self) -> &<T::BaseType as PyTypeInfo>::ConcreteLayout {
&self.ob_base
}
/// Get the mutable reference of base object.
pub fn get_super_mut(&mut self) -> &mut <T::BaseType as PyTypeInfo>::ConcreteLayout {
&mut self.ob_base
}
pub(crate) unsafe fn new(py: Python) -> PyResult<*mut Self>
where
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
let base = T::alloc(py);
if base.is_null() {
return Err(PyErr::fetch(py));
}
let self_ = base as *mut Self;
(*self_).dict = T::Dict::new();
(*self_).weakref = T::WeakRef::new();
Ok(self_)
}
}
impl<T: PyClass> PyObjectLayout<T> for PyClassShell<T> {
const IS_NATIVE_TYPE: bool = false;
fn get_super_or(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::ConcreteLayout> {
Some(&mut self.ob_base)
}
unsafe fn internal_ref_cast(obj: &PyAny) -> &T {
let shell = obj.as_ptr() as *const Self;
&(*shell).pyclass
}
unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T {
let shell = obj.as_ptr() as *const _ as *mut Self;
&mut (*shell).pyclass
}
unsafe fn py_drop(&mut self, py: Python) {
ManuallyDrop::drop(&mut self.pyclass);
self.dict.clear_dict(py);
self.weakref.clear_weakrefs(self.as_ptr(), py);
self.ob_base.py_drop(py);
}
unsafe fn py_init(&mut self, value: T) {
self.pyclass = ManuallyDrop::new(value);
}
}
impl<T: PyClass> PyObjectSizedLayout<T> for PyClassShell<T> {}
impl<T: PyClass> AsPyPointer for PyClassShell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _
}
}
impl<T: PyClass> std::ops::Deref for PyClassShell<T> {
type Target = T;
fn deref(&self) -> &T {
self.pyclass.deref()
}
}
impl<T: PyClass> std::ops::DerefMut for PyClassShell<T> {
fn deref_mut(&mut self) -> &mut T {
self.pyclass.deref_mut()
}
}
impl<T: PyClass> ToPyObject for &PyClassShell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
impl<T: PyClass> ToPyObject for &mut PyClassShell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
unsafe impl<'p, T> FromPyPointer<'p> for &'p PyClassShell<T>
where
T: PyClass,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyClassShell<T>))
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr)
.map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyClassShell<T>))
}
}
unsafe impl<'p, T> FromPyPointer<'p> for &'p mut PyClassShell<T>
where
T: PyClass,
{
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| {
&mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyClassShell<T>)
})
}
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|p| {
&mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyClassShell<T>)
})
}
/// The closest native ancestor. This is `PyAny` by default, and when you declare
/// `#[pyclass(extends=PyDict)]`, it's `PyDict`.
type BaseNativeType: PyTypeInfo + PyNativeType;
}
#[cfg(not(Py_LIMITED_API))]
@ -285,12 +115,12 @@ where
{
let py = Python::assume_gil_acquired();
let _pool = gil::GILPool::new_no_pointers(py);
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::ConcreteLayout) as _)
<T as PyClassAlloc>::dealloc(py, (obj as *mut T::Layout) as _)
}
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
// type size
type_object.tp_basicsize = std::mem::size_of::<T::ConcreteLayout>() as ffi::Py_ssize_t;
type_object.tp_basicsize = std::mem::size_of::<T::Layout>() as ffi::Py_ssize_t;
let mut offset = type_object.tp_basicsize;

View File

@ -1,7 +1,6 @@
//! Initialization utilities for `#[pyclass]`.
use crate::pyclass::{PyClass, PyClassShell};
use crate::type_object::{PyObjectLayout, PyObjectSizedLayout, PyTypeInfo};
use crate::{PyResult, Python};
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
use crate::{PyCell, PyClass, PyResult, Python};
use std::marker::PhantomData;
/// Initializer for Python types.
@ -9,7 +8,7 @@ use std::marker::PhantomData;
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
/// Python native types.
pub trait PyObjectInit<T: PyTypeInfo>: Sized {
fn init_class(self, shell: &mut T::ConcreteLayout);
fn init_class<L: PyLayout<T>>(self, layout: &mut L);
private_decl! {}
}
@ -17,7 +16,7 @@ pub trait PyObjectInit<T: PyTypeInfo>: Sized {
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
fn init_class(self, _shell: &mut T::ConcreteLayout) {}
fn init_class<L: PyLayout<T>>(self, _layout: &mut L) {}
private_impl! {}
}
@ -109,30 +108,32 @@ impl<T: PyClass> PyClassInitializer<T> {
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = T>,
S::BaseLayout: PySizedLayout<T>,
S::BaseType: PyTypeInfo<Initializer = Self>,
{
PyClassInitializer::new(subclass_value, self)
}
// Create a new PyCell + initialize it
#[doc(hidden)]
pub unsafe fn create_shell(self, py: Python) -> PyResult<*mut PyClassShell<T>>
pub unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell<T>>
where
T: PyClass,
<T::BaseType as PyTypeInfo>::ConcreteLayout: PyObjectSizedLayout<T::BaseType>,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
let shell = PyClassShell::new(py)?;
self.init_class(&mut *shell);
Ok(shell)
let cell = PyCell::internal_new(py)?;
self.init_class(&mut *cell);
Ok(cell)
}
}
impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
fn init_class(self, obj: &mut T::ConcreteLayout) {
fn init_class<L: PyLayout<T>>(self, layout: &mut L) {
let Self { init, super_init } = self;
unsafe {
obj.py_init(init);
layout.py_init(init);
}
if let Some(super_obj) = obj.get_super_or() {
if let Some(super_obj) = layout.get_super() {
super_init.init_class(super_obj);
}
}
@ -152,6 +153,7 @@ where
impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = B>,
S::BaseLayout: PySizedLayout<B>,
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{

View File

@ -7,7 +7,7 @@ use crate::ffi;
use crate::gil::{self, GILGuard};
use crate::instance::AsPyRef;
use crate::object::PyObject;
use crate::type_object::{PyObjectLayout, PyTypeInfo, PyTypeObject};
use crate::type_object::{PyDowncastImpl, PyTypeInfo, PyTypeObject};
use crate::types::{PyAny, PyDict, PyModule, PyType};
use crate::AsPyPointer;
use crate::{FromPyPointer, IntoPyPointer, PyTryFrom};
@ -177,7 +177,7 @@ impl<'p> Python<'p> {
/// Some(locals),
/// ).unwrap();
/// let ret = locals.get_item("ret").unwrap();
/// let b64: &PyBytes = ret.downcast_ref().unwrap();
/// let b64: &PyBytes = ret.downcast().unwrap();
/// assert_eq!(b64.as_bytes(), b"SGVsbG8gUnVzdCE=");
/// ```
pub fn run(
@ -275,19 +275,19 @@ impl<'p> Python<'p> {
/// Register object in release pool, and try to downcast to specific type.
pub fn checked_cast_as<T>(self, obj: PyObject) -> Result<&'p T, PyDowncastError>
where
T: PyTypeInfo,
T: PyTryFrom<'p>,
{
let p = unsafe { gil::register_owned(self, obj.into_nonnull()) };
<T as PyTryFrom>::try_from(p)
let obj = unsafe { gil::register_owned(self, obj.into_nonnull()) };
<T as PyTryFrom>::try_from(obj)
}
/// Register object in release pool, and do unchecked downcast to specific type.
pub unsafe fn cast_as<T>(self, obj: PyObject) -> &'p T
where
T: PyTypeInfo,
T: PyDowncastImpl + PyTypeInfo,
{
let p = gil::register_owned(self, obj.into_nonnull());
T::ConcreteLayout::internal_ref_cast(p)
let obj = gil::register_owned(self, obj.into_nonnull());
T::unchecked_downcast(obj)
}
/// Register `ffi::PyObject` pointer in release pool
@ -304,16 +304,7 @@ impl<'p> Python<'p> {
#[allow(clippy::wrong_self_convention)]
pub unsafe fn from_owned_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
where
T: PyTypeInfo,
{
FromPyPointer::from_owned_ptr(self, ptr)
}
/// Register `ffi::PyObject` pointer in release pool,
/// Do unchecked downcast to specific type. Returns mutable reference.
pub unsafe fn mut_from_owned_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p mut T
where
T: PyTypeInfo,
T: FromPyPointer<'p>,
{
FromPyPointer::from_owned_ptr(self, ptr)
}
@ -324,7 +315,7 @@ impl<'p> Python<'p> {
#[allow(clippy::wrong_self_convention)]
pub unsafe fn from_owned_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
where
T: PyTypeInfo,
T: FromPyPointer<'p>,
{
FromPyPointer::from_owned_ptr_or_err(self, ptr)
}
@ -335,7 +326,7 @@ impl<'p> Python<'p> {
#[allow(clippy::wrong_self_convention)]
pub unsafe fn from_owned_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
where
T: PyTypeInfo,
T: FromPyPointer<'p>,
{
FromPyPointer::from_owned_ptr_or_opt(self, ptr)
}
@ -346,17 +337,7 @@ impl<'p> Python<'p> {
#[allow(clippy::wrong_self_convention)]
pub unsafe fn from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
where
T: PyTypeInfo,
{
FromPyPointer::from_borrowed_ptr(self, ptr)
}
/// Register borrowed `ffi::PyObject` pointer in release pool.
/// Panics if the pointer is `null`.
/// do unchecked downcast to specific type.
pub unsafe fn mut_from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p mut T
where
T: PyTypeInfo,
T: FromPyPointer<'p>,
{
FromPyPointer::from_borrowed_ptr(self, ptr)
}
@ -367,7 +348,7 @@ impl<'p> Python<'p> {
#[allow(clippy::wrong_self_convention)]
pub unsafe fn from_borrowed_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
where
T: PyTypeInfo,
T: FromPyPointer<'p>,
{
FromPyPointer::from_borrowed_ptr_or_err(self, ptr)
}
@ -378,7 +359,7 @@ impl<'p> Python<'p> {
#[allow(clippy::wrong_self_convention)]
pub unsafe fn from_borrowed_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
where
T: PyTypeInfo,
T: FromPyPointer<'p>,
{
FromPyPointer::from_borrowed_ptr_or_opt(self, ptr)
}

View File

@ -1,46 +1,49 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//! Python type object information
use crate::instance::Py;
use crate::pyclass::{initialize_type_object, PyClass};
use crate::pyclass_init::PyObjectInit;
use crate::types::{PyAny, PyType};
use crate::{ffi, AsPyPointer, Python};
use crate::{ffi, AsPyPointer, Py, Python};
use std::cell::UnsafeCell;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicBool, Ordering};
/// `T: PyObjectLayout<U>` represents that `T` is a concrete representaion of `U` in Python heap.
/// E.g., `PyClassShell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject`
/// `T: PyLayout<U>` represents that `T` is a concrete representaion of `U` in Python heap.
/// E.g., `PyCell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject`
/// is of `PyAny`.
///
/// This trait is intended to be used internally.
pub trait PyObjectLayout<T: PyTypeInfo> {
pub unsafe trait PyLayout<T: PyTypeInfo> {
const IS_NATIVE_TYPE: bool = true;
fn get_super_or(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::ConcreteLayout> {
fn get_super(&mut self) -> Option<&mut T::BaseLayout> {
None
}
unsafe fn internal_ref_cast(obj: &PyAny) -> &T {
&*(obj as *const _ as *const T)
}
#[allow(clippy::mut_from_ref)]
unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T {
&mut *(obj as *const _ as *const T as *mut T)
}
unsafe fn py_init(&mut self, _value: T) {}
unsafe fn py_drop(&mut self, _py: Python) {}
unsafe fn get_ptr(&self) -> *mut T;
}
/// `T: PyObjectSizedLayout<U>` represents `T` is not a instance of
/// `T: PySizedLayout<U>` represents `T` is not a instance of
/// [`PyVarObject`](https://docs.python.org/3.8/c-api/structures.html?highlight=pyvarobject#c.PyVarObject).
/// , in addition that `T` is a concrete representaion of `U`.
pub trait PySizedLayout<T: PyTypeInfo>: PyLayout<T> + Sized {}
/// Marker type indicates that `Self` can be a base layout of `PyClass`.
///
/// `pyclass`es need this trait for their base class.
pub trait PyObjectSizedLayout<T: PyTypeInfo>: PyObjectLayout<T> + Sized {}
/// # Safety
///
/// Self should be laid out as follows:
/// ```ignore
/// #[repr(C)]
/// struct Self {
/// obj: ffi::PyObject,
/// borrow_flag: u64,
/// ...
/// }
/// ```
/// Otherwise, implementing this trait is undefined behavior.
pub unsafe trait PyBorrowFlagLayout<T: PyTypeInfo>: PyLayout<T> + Sized {}
/// Our custom type flags
#[doc(hidden)]
@ -61,6 +64,32 @@ pub mod type_flags {
pub const EXTENDED: usize = 1 << 4;
}
/// Reference abstraction for `PyClass` and `PyNativeType`. Used internaly.
// NOTE(kngwyu):
// `&PyCell` is a pointer of `ffi::PyObject` but `&PyAny` is a pointer of a pointer,
// so we need abstraction.
// This mismatch eventually should be fixed(e.g., https://github.com/PyO3/pyo3/issues/679).
pub unsafe trait PyDowncastImpl {
/// Cast `&PyAny` to `&Self` without no type checking.
///
/// # Safety
///
/// Unless obj is not an instance of a type corresponding to Self,
/// this method causes undefined behavior.
unsafe fn unchecked_downcast(obj: &PyAny) -> &Self;
private_decl! {}
}
unsafe impl<'py, T> PyDowncastImpl for T
where
T: 'py + crate::PyNativeType,
{
unsafe fn unchecked_downcast(obj: &PyAny) -> &Self {
&*(obj as *const _ as *const Self)
}
private_impl! {}
}
/// Python type information.
/// All Python native types(e.g., `PyDict`) and `#[pyclass]` structs implement this trait.
///
@ -87,7 +116,10 @@ pub unsafe trait PyTypeInfo: Sized {
type BaseType: PyTypeInfo + PyTypeObject;
/// Layout
type ConcreteLayout: PyObjectLayout<Self>;
type Layout: PyLayout<Self>;
/// Layout of Basetype.
type BaseLayout: PySizedLayout<Self::BaseType>;
/// Initializer for layout
type Initializer: PyObjectInit<Self>;

View File

@ -18,13 +18,17 @@ use crate::{ffi, PyObject};
/// let dict = PyDict::new(gil.python());
/// assert!(gil.python().is_instance::<PyAny, _>(dict).unwrap());
/// let any = dict.as_ref();
/// assert!(any.downcast_ref::<PyDict>().is_ok());
/// assert!(any.downcast_ref::<PyList>().is_err());
/// assert!(any.downcast::<PyDict>().is_ok());
/// assert!(any.downcast::<PyList>().is_err());
/// ```
#[repr(transparent)]
pub struct PyAny(PyObject, Unsendable);
impl crate::type_object::PyObjectLayout<PyAny> for ffi::PyObject {}
impl crate::type_object::PyObjectSizedLayout<PyAny> for ffi::PyObject {}
unsafe impl crate::type_object::PyLayout<PyAny> for ffi::PyObject {
unsafe fn get_ptr(&self) -> *mut PyAny {
(&self) as *const &Self as *const _ as *mut _
}
}
impl crate::type_object::PySizedLayout<PyAny> for ffi::PyObject {}
pyobject_native_type_named!(PyAny);
pyobject_native_type_convert!(
PyAny,
@ -36,17 +40,10 @@ pyobject_native_type_convert!(
pyobject_native_type_extract!(PyAny);
impl PyAny {
pub fn downcast_ref<T>(&self) -> Result<&T, PyDowncastError>
pub fn downcast<T>(&self) -> Result<&T, PyDowncastError>
where
T: for<'gil> PyTryFrom<'gil>,
for<'py> T: PyTryFrom<'py>,
{
T::try_from(self)
}
pub fn downcast_mut<T>(&self) -> Result<&mut T, PyDowncastError>
where
T: for<'gil> PyTryFrom<'gil>,
{
T::try_from_mut(self)
<T as PyTryFrom>::try_from(self)
}
}

View File

@ -56,11 +56,27 @@ macro_rules! pyobject_native_type_named (
};
);
macro_rules! impl_layout {
($name: ty, $layout: path) => {
unsafe impl $crate::type_object::PyLayout<$name> for $layout {
unsafe fn get_ptr(&self) -> *mut $name {
(&self) as *const &Self as *const _ as *mut _
}
}
};
}
#[macro_export]
macro_rules! pyobject_native_type {
($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
impl $crate::type_object::PyObjectLayout<$name> for $layout {}
impl $crate::type_object::PyObjectSizedLayout<$name> for $layout {}
impl_layout!($name, $layout);
impl $crate::type_object::PySizedLayout<$name> for $layout {}
impl $crate::derive_utils::PyBaseTypeUtils for $name {
type Dict = $crate::pyclass_slots::PyClassDummySlot;
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
type LayoutAsBase = $crate::pycell::PyCellBase<$name>;
type BaseNativeType = $name;
}
pyobject_native_type_named!($name $(,$type_param)*);
pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*);
pyobject_native_type_extract!($name $(,$type_param)*);
@ -81,7 +97,7 @@ macro_rules! pyobject_native_type {
#[macro_export]
macro_rules! pyobject_native_var_type {
($name: ty, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
impl $crate::type_object::PyObjectLayout<$name> for $crate::ffi::PyObject {}
impl_layout!($name, $crate::ffi::PyObject);
pyobject_native_type_named!($name $(,$type_param)*);
pyobject_native_type_convert!($name, $crate::ffi::PyObject,
$typeobject, $module, $checkfunction $(,$type_param)*);
@ -104,8 +120,10 @@ macro_rules! pyobject_native_var_type {
// because rust-numpy has a special implementation.
macro_rules! pyobject_native_type_extract {
($name: ty $(,$type_param: ident)*) => {
impl<$($type_param,)*> $crate::conversion::FromPyObjectImpl for &'_ $name {
type Impl = $crate::conversion::extract_impl::Reference;
impl<'py, $($type_param,)*> $crate::FromPyObject<'py> for &'py $name {
fn extract(obj: &'py crate::types::PyAny) -> $crate::PyResult<Self> {
$crate::PyTryFrom::try_from(obj).map_err(Into::into)
}
}
}
}
@ -117,7 +135,8 @@ macro_rules! pyobject_native_type_convert(
unsafe impl<$($type_param,)*> $crate::type_object::PyTypeInfo for $name {
type Type = ();
type BaseType = $crate::types::PyAny;
type ConcreteLayout = $layout;
type Layout = $layout;
type BaseLayout = ffi::PyObject;
type Initializer = $crate::pyclass_init::PyNativeTypeInitializer<Self>;
const NAME: &'static str = stringify!($name);
@ -144,6 +163,14 @@ macro_rules! pyobject_native_type_convert(
}
}
impl $crate::AsPyRef for $crate::Py<$name> {
type Target = $name;
fn as_ref(&self, _py: $crate::Python) -> &$name {
let any = self as *const $crate::Py<$name> as *const $crate::types::PyAny;
unsafe { $crate::type_object::PyDowncastImpl::unchecked_downcast(&*any) }
}
}
impl<$($type_param,)*> ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter)
-> Result<(), ::std::fmt::Error>

View File

@ -79,7 +79,7 @@ impl PyModule {
/// Return the index (`__all__`) of the module, creating one if needed.
pub fn index(&self) -> PyResult<&PyList> {
match self.getattr("__all__") {
Ok(idx) => idx.downcast_ref().map_err(PyErr::from),
Ok(idx) => idx.downcast().map_err(PyErr::from),
Err(err) => {
if err.is_instance::<exceptions::AttributeError>(self.py()) {
let l = PyList::empty(self.py());

View File

@ -298,34 +298,11 @@ impl<'v> PyTryFrom<'v> for PySequence {
<PySequence as PyTryFrom>::try_from(value)
}
fn try_from_mut<V: Into<&'v PyAny>>(value: V) -> Result<&'v mut PySequence, PyDowncastError> {
let value = value.into();
unsafe {
if ffi::PySequence_Check(value.as_ptr()) != 0 {
Ok(<PySequence as PyTryFrom>::try_from_mut_unchecked(value))
} else {
Err(PyDowncastError)
}
}
}
fn try_from_mut_exact<V: Into<&'v PyAny>>(
value: V,
) -> Result<&'v mut PySequence, PyDowncastError> {
<PySequence as PyTryFrom>::try_from_mut(value)
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PySequence {
let ptr = value.into() as *const _ as *const PySequence;
&*ptr
}
#[inline]
unsafe fn try_from_mut_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v mut PySequence {
let ptr = value.into() as *const _ as *mut PySequence;
&mut *ptr
}
}
#[cfg(test)]

View File

@ -3,15 +3,11 @@
use crate::conversion::FromPyObject;
use crate::conversion::{PyTryFrom, ToPyObject};
use crate::err::{PyErr, PyResult};
use crate::gil;
use crate::instance::PyNativeType;
use crate::internal_tricks::Unsendable;
use crate::object::PyObject;
use crate::types::PyAny;
use crate::AsPyPointer;
use crate::IntoPy;
use crate::Python;
use crate::{ffi, FromPy};
use crate::{ffi, gil, AsPyPointer, FromPy, IntoPy, Python};
use std::borrow::Cow;
use std::ffi::CStr;
use std::os::raw::c_char;

View File

@ -4,7 +4,7 @@ use pyo3::exceptions::BufferError;
use pyo3::ffi;
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::{AsPyPointer, PyClassShell};
use pyo3::AsPyPointer;
use std::ffi::CStr;
use std::os::raw::{c_int, c_void};
use std::ptr;
@ -19,11 +19,7 @@ struct TestBufferClass {
#[pyproto]
impl PyBufferProtocol for TestBufferClass {
fn bf_getbuffer(
slf: &mut PyClassShell<Self>,
view: *mut ffi::Py_buffer,
flags: c_int,
) -> PyResult<()> {
fn bf_getbuffer(slf: PyRefMut<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> PyResult<()> {
if view.is_null() {
return Err(BufferError::py_err("View is null"));
}
@ -69,7 +65,7 @@ impl PyBufferProtocol for TestBufferClass {
Ok(())
}
fn bf_releasebuffer(_slf: &mut PyClassShell<Self>, _view: *mut ffi::Py_buffer) -> PyResult<()> {
fn bf_releasebuffer(_slf: PyRefMut<Self>, _view: *mut ffi::Py_buffer) -> PyResult<()> {
Ok(())
}
}

View File

@ -16,10 +16,12 @@ fn test_cloneable_pyclass() {
let py_c = Py::new(py, c.clone()).unwrap().to_object(py);
let c2: Cloneable = py_c.extract(py).unwrap();
let rc: &Cloneable = py_c.extract(py).unwrap();
let mrc: &mut Cloneable = py_c.extract(py).unwrap();
assert_eq!(c, c2);
assert_eq!(&c, rc);
assert_eq!(&c, mrc);
{
let rc: PyRef<Cloneable> = py_c.extract(py).unwrap();
assert_eq!(&c, &*rc);
// Drops PyRef before taking PyRefMut
}
let mrc: PyRefMut<Cloneable> = py_c.extract(py).unwrap();
assert_eq!(&c, &*mrc);
}

View File

@ -19,7 +19,7 @@ fn empty_class_with_new() {
assert!(typeobj
.call((), None)
.unwrap()
.cast_as::<EmptyClassWithNew>()
.cast_as::<PyCell<EmptyClassWithNew>>()
.is_ok());
}
@ -43,8 +43,9 @@ fn new_with_one_arg() {
let py = gil.python();
let typeobj = py.get_type::<NewWithOneArg>();
let wrp = typeobj.call((42,), None).unwrap();
let obj = wrp.cast_as::<NewWithOneArg>().unwrap();
assert_eq!(obj._data, 42);
let obj = wrp.cast_as::<PyCell<NewWithOneArg>>().unwrap();
let obj_ref = obj.borrow();
assert_eq!(obj_ref._data, 42);
}
#[pyclass]
@ -73,7 +74,8 @@ fn new_with_two_args() {
.call((10, 20), None)
.map_err(|e| e.print(py))
.unwrap();
let obj = wrp.cast_as::<NewWithTwoArgs>().unwrap();
assert_eq!(obj._data1, 10);
assert_eq!(obj._data2, 20);
let obj = wrp.cast_as::<PyCell<NewWithTwoArgs>>().unwrap();
let obj_ref = obj.borrow();
assert_eq!(obj_ref._data1, 10);
assert_eq!(obj_ref._data2, 20);
}

View File

@ -6,7 +6,7 @@ use pyo3::class::{
use pyo3::exceptions::{IndexError, ValueError};
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyAny, PyBytes, PySlice, PyType};
use pyo3::{ffi, py_run, AsPyPointer, PyClassShell};
use pyo3::{ffi, py_run, AsPyPointer, PyCell};
use std::convert::TryFrom;
use std::{isize, iter};
@ -53,11 +53,11 @@ struct Iterator {
#[pyproto]
impl<'p> PyIterProtocol for Iterator {
fn __iter__(slf: &mut PyClassShell<Self>) -> PyResult<Py<Iterator>> {
fn __iter__(slf: PyRefMut<Self>) -> PyResult<Py<Iterator>> {
Ok(slf.into())
}
fn __next__(slf: &mut PyClassShell<Self>) -> PyResult<Option<i32>> {
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<i32>> {
Ok(slf.iter.next())
}
}
@ -231,6 +231,7 @@ fn callable() {
}
#[pyclass]
#[derive(Debug)]
struct SetItem {
key: i32,
val: i32,
@ -250,10 +251,13 @@ fn setitem() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = PyClassShell::new_ref(py, SetItem { key: 0, val: 0 }).unwrap();
let c = PyCell::new(py, SetItem { key: 0, val: 0 }).unwrap();
py_run!(py, c, "c[1] = 2");
assert_eq!(c.key, 1);
assert_eq!(c.val, 2);
{
let c = c.borrow();
assert_eq!(c.key, 1);
assert_eq!(c.val, 2);
}
py_expect_exception!(py, c, "del c[1]", NotImplementedError);
}
@ -275,9 +279,12 @@ fn delitem() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = PyClassShell::new_ref(py, DelItem { key: 0 }).unwrap();
let c = PyCell::new(py, DelItem { key: 0 }).unwrap();
py_run!(py, c, "del c[1]");
assert_eq!(c.key, 1);
{
let c = c.borrow();
assert_eq!(c.key, 1);
}
py_expect_exception!(py, c, "c[1] = 2", NotImplementedError);
}
@ -304,10 +311,14 @@ fn setdelitem() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = PyClassShell::new_ref(py, SetDelItem { val: None }).unwrap();
let c = PyCell::new(py, SetDelItem { val: None }).unwrap();
py_run!(py, c, "c[1] = 2");
assert_eq!(c.val, Some(2));
{
let c = c.borrow();
assert_eq!(c.val, Some(2));
}
py_run!(py, c, "del c[1]");
let c = c.borrow();
assert_eq!(c.val, None);
}
@ -383,21 +394,26 @@ fn context_manager() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = PyClassShell::new_mut(py, ContextManager { exit_called: false }).unwrap();
let c = PyCell::new(py, ContextManager { exit_called: false }).unwrap();
py_run!(py, c, "with c as x: assert x == 42");
assert!(c.exit_called);
c.exit_called = false;
{
let mut c = c.borrow_mut();
assert!(c.exit_called);
c.exit_called = false;
}
py_run!(py, c, "with c as x: raise ValueError");
assert!(c.exit_called);
c.exit_called = false;
{
let mut c = c.borrow_mut();
assert!(c.exit_called);
c.exit_called = false;
}
py_expect_exception!(
py,
c,
"with c as x: raise NotImplementedError",
NotImplementedError
);
let c = c.borrow();
assert!(c.exit_called);
}
@ -455,7 +471,7 @@ struct DunderDictSupport {}
fn dunder_dict_support() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_ref(py, DunderDictSupport {}).unwrap();
let inst = PyCell::new(py, DunderDictSupport {}).unwrap();
py_run!(
py,
inst,
@ -470,7 +486,28 @@ fn dunder_dict_support() {
fn access_dunder_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_ref(py, DunderDictSupport {}).unwrap();
let inst = PyCell::new(py, DunderDictSupport {}).unwrap();
py_run!(
py,
inst,
r#"
inst.a = 1
assert inst.__dict__ == {'a': 1}
"#
);
}
// If the base class has dict support, child class also has dict
#[pyclass(extends=DunderDictSupport)]
struct InheritDict {
_value: usize,
}
#[test]
fn inherited_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyCell::new(py, (InheritDict { _value: 0 }, DunderDictSupport {})).unwrap();
py_run!(
py,
inst,
@ -488,7 +525,7 @@ struct WeakRefDunderDictSupport {}
fn weakref_dunder_dict_support() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_ref(py, WeakRefDunderDictSupport {}).unwrap();
let inst = PyCell::new(py, WeakRefDunderDictSupport {}).unwrap();
py_run!(
py,
inst,
@ -513,7 +550,7 @@ impl PyObjectProtocol for ClassWithGetAttr {
fn getattr_doesnt_override_member() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_ref(py, ClassWithGetAttr { data: 4 }).unwrap();
let inst = PyCell::new(py, ClassWithGetAttr { data: 4 }).unwrap();
py_assert!(py, inst, "inst.data == 4");
py_assert!(py, inst, "inst.a == 8");
}

View File

@ -3,7 +3,7 @@ use pyo3::class::PyTraverseError;
use pyo3::class::PyVisit;
use pyo3::prelude::*;
use pyo3::types::{PyAny, PyTuple};
use pyo3::{ffi, py_run, AsPyPointer, PyClassShell};
use pyo3::{ffi, py_run, AsPyPointer, PyCell, PyTryInto};
use std::cell::RefCell;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@ -153,7 +153,7 @@ fn gc_integration() {
{
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_mut(
let inst = PyCell::new(
py,
GCIntegration {
self_ref: RefCell::new(py.None()),
@ -164,7 +164,8 @@ fn gc_integration() {
)
.unwrap();
*inst.self_ref.borrow_mut() = inst.to_object(py);
let mut borrow = inst.borrow_mut();
*borrow.self_ref.borrow_mut() = inst.to_object(py);
}
let gil = Python::acquire_gil();
@ -188,7 +189,7 @@ impl PyGCProtocol for GCIntegration2 {
fn gc_integration2() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_ref(py, GCIntegration2 {}).unwrap();
let inst = PyCell::new(py, GCIntegration2 {}).unwrap();
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
}
@ -199,7 +200,25 @@ struct WeakRefSupport {}
fn weakref_support() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyClassShell::new_ref(py, WeakRefSupport {}).unwrap();
let inst = PyCell::new(py, WeakRefSupport {}).unwrap();
py_run!(
py,
inst,
"import weakref; assert weakref.ref(inst)() is inst"
);
}
// If the base class has weakref support, child class also has weakref.
#[pyclass(extends=WeakRefSupport)]
struct InheritWeakRef {
_value: usize,
}
#[test]
fn inherited_weakref() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = PyCell::new(py, (InheritWeakRef { _value: 0 }, WeakRefSupport {})).unwrap();
py_run!(
py,
inst,
@ -264,12 +283,10 @@ fn inheritance_with_new_methods_with_drop() {
let typeobj = py.get_type::<SubClassWithDrop>();
let inst = typeobj.call((), None).unwrap();
let obj = SubClassWithDrop::try_from_mut(inst).unwrap();
obj.data = Some(Arc::clone(&drop_called1));
let base: &mut <SubClassWithDrop as pyo3::PyTypeInfo>::BaseType =
unsafe { py.mut_from_borrowed_ptr(inst.as_ptr()) };
base.data = Some(Arc::clone(&drop_called2));
let obj: &PyCell<SubClassWithDrop> = inst.try_into().unwrap();
let mut obj_ref_mut = obj.borrow_mut();
obj_ref_mut.data = Some(Arc::clone(&drop_called1));
obj_ref_mut.as_mut().data = Some(Arc::clone(&drop_called2));
}
assert!(drop_called1.load(Ordering::Relaxed));

View File

@ -36,6 +36,14 @@ impl BaseClass {
fn new() -> Self {
BaseClass { val1: 10 }
}
fn base_method(&self, x: usize) -> usize {
x * self.val1
}
fn base_set(&mut self, fn_: &pyo3::types::PyAny) -> PyResult<()> {
let value: usize = fn_.call0()?.extract()?;
self.val1 = value;
Ok(())
}
}
#[pyclass(extends=BaseClass)]
@ -50,6 +58,13 @@ impl SubClass {
fn new() -> (Self, BaseClass) {
(SubClass { val2: 5 }, BaseClass { val1: 10 })
}
fn sub_method(&self, x: usize) -> usize {
x * self.val2
}
fn sub_set_and_ret(&mut self, x: usize) -> usize {
self.val2 = x;
x
}
}
#[test]
@ -61,6 +76,33 @@ fn inheritance_with_new_methods() {
py_run!(py, inst, "assert inst.val1 == 10; assert inst.val2 == 5");
}
#[test]
fn call_base_and_sub_methods() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyCell::new(py, SubClass::new()).unwrap();
py_run!(
py,
obj,
r#"
assert obj.base_method(10) == 100
assert obj.sub_method(10) == 50
"#
);
}
#[test]
fn mutation_fails() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyCell::new(py, SubClass::new()).unwrap();
let global = Some([("obj", obj)].into_py_dict(py));
let e = py
.run("obj.base_set(lambda: obj.sub_set_and_ret(1))", global, None)
.unwrap_err();
assert!(e.is_instance::<pyo3::pycell::PyBorrowMutError>(py))
}
#[pyclass]
struct BaseClassWithResult {
_val: usize,
@ -109,6 +151,7 @@ except Exception as e:
}
#[pyclass(extends=PySet)]
#[derive(Debug)]
struct SetWithName {
#[pyo3(get(name))]
_name: &'static str,
@ -126,7 +169,7 @@ impl SetWithName {
fn inherit_set() {
let gil = Python::acquire_gil();
let py = gil.python();
let set_sub = pyo3::pyclass::PyClassShell::new_ref(py, SetWithName::new()).unwrap();
let set_sub = pyo3::PyCell::new(py, SetWithName::new()).unwrap();
py_run!(
py,
set_sub,
@ -135,6 +178,7 @@ fn inherit_set() {
}
#[pyclass(extends=PyDict)]
#[derive(Debug)]
struct DictWithName {
#[pyo3(get(name))]
_name: &'static str,
@ -152,7 +196,7 @@ impl DictWithName {
fn inherit_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let dict_sub = pyo3::pyclass::PyClassShell::new_ref(py, DictWithName::new()).unwrap();
let dict_sub = pyo3::PyCell::new(py, DictWithName::new()).unwrap();
py_run!(
py,
dict_sub,

View File

@ -1,7 +1,7 @@
use pyo3::prelude::*;
use pyo3::py_run;
use pyo3::types::{IntoPyDict, PyDict, PyList, PySet, PyString, PyTuple, PyType};
use pyo3::PyClassShell;
use pyo3::PyCell;
mod common;
@ -23,8 +23,9 @@ fn instance_method() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyClassShell::new_mut(py, InstanceMethod { member: 42 }).unwrap();
assert_eq!(obj.method().unwrap(), 42);
let obj = PyCell::new(py, InstanceMethod { member: 42 }).unwrap();
let obj_ref = obj.borrow();
assert_eq!(obj_ref.method().unwrap(), 42);
let d = [("obj", obj)].into_py_dict(py);
py.run("assert obj.method() == 42", None, Some(d)).unwrap();
py.run("assert obj.method.__doc__ == 'Test method'", None, Some(d))
@ -49,8 +50,9 @@ fn instance_method_with_args() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyClassShell::new_mut(py, InstanceMethodWithArgs { member: 7 }).unwrap();
assert_eq!(obj.method(6).unwrap(), 42);
let obj = PyCell::new(py, InstanceMethodWithArgs { member: 7 }).unwrap();
let obj_ref = obj.borrow();
assert_eq!(obj_ref.method(6).unwrap(), 42);
let d = [("obj", obj)].into_py_dict(py);
py.run("assert obj.method(3) == 21", None, Some(d)).unwrap();
py.run("assert obj.method(multiplier=6) == 42", None, Some(d))
@ -395,7 +397,7 @@ impl MethodWithLifeTime {
fn method_with_lifetime() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyClassShell::new_ref(py, MethodWithLifeTime {}).unwrap();
let obj = PyCell::new(py, MethodWithLifeTime {}).unwrap();
py_run!(
py,
obj,

View File

@ -2,7 +2,7 @@
use pyo3;
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyString};
use pyo3::{AsPyRef, PyClassShell, PyIterProtocol};
use pyo3::{AsPyRef, PyCell, PyIterProtocol};
use std::collections::HashMap;
mod common;
@ -17,16 +17,13 @@ struct Reader {
#[pymethods]
impl Reader {
fn clone_ref(slf: &PyClassShell<Self>) -> &PyClassShell<Self> {
fn clone_ref(slf: &PyCell<Self>) -> &PyCell<Self> {
slf
}
fn clone_ref_with_py<'py>(
slf: &'py PyClassShell<Self>,
_py: Python<'py>,
) -> &'py PyClassShell<Self> {
fn clone_ref_with_py<'py>(slf: &'py PyCell<Self>, _py: Python<'py>) -> &'py PyCell<Self> {
slf
}
fn get_iter(slf: &PyClassShell<Self>, keys: Py<PyBytes>) -> PyResult<Iter> {
fn get_iter(slf: &PyCell<Self>, keys: Py<PyBytes>) -> PyResult<Iter> {
Ok(Iter {
reader: slf.into(),
keys,
@ -34,7 +31,7 @@ impl Reader {
})
}
fn get_iter_and_reset(
slf: &mut PyClassShell<Self>,
mut slf: PyRefMut<Self>,
keys: Py<PyBytes>,
py: Python,
) -> PyResult<Iter> {
@ -49,6 +46,7 @@ impl Reader {
}
#[pyclass]
#[derive(Debug)]
struct Iter {
reader: Py<Reader>,
keys: Py<PyBytes>,
@ -57,23 +55,23 @@ struct Iter {
#[pyproto]
impl PyIterProtocol for Iter {
fn __iter__(slf: &mut PyClassShell<Self>) -> PyResult<PyObject> {
fn __iter__(slf: PyRefMut<Self>) -> PyResult<PyObject> {
let py = unsafe { Python::assume_gil_acquired() };
Ok(slf.to_object(py))
Ok(slf.into_py(py))
}
fn __next__(slf: &mut PyClassShell<Self>) -> PyResult<Option<PyObject>> {
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<PyObject>> {
let py = unsafe { Python::assume_gil_acquired() };
let bytes = slf.keys.as_ref(py).as_bytes();
match bytes.get(slf.idx) {
Some(&b) => {
let res = slf
.reader
.as_ref(py)
slf.idx += 1;
let reader = slf.reader.as_ref(py);
let reader_ref = reader.try_borrow()?;
let res = reader_ref
.inner
.get(&b)
.map(|s| PyString::new(py, s).into());
slf.idx += 1;
Ok(res)
}
None => Ok(None),
@ -89,7 +87,7 @@ fn reader() -> Reader {
}
#[test]
fn test_nested_iter1() {
fn test_nested_iter() {
let gil = Python::acquire_gil();
let py = gil.python();
let reader: PyObject = reader().into_py(py);
@ -113,11 +111,12 @@ fn test_clone_ref() {
fn test_nested_iter_reset() {
let gil = Python::acquire_gil();
let py = gil.python();
let reader = PyClassShell::new_ref(py, reader()).unwrap();
let reader = PyCell::new(py, reader()).unwrap();
py_assert!(
py,
reader,
"list(reader.get_iter_and_reset(bytes([3, 5, 2]))) == ['c', 'e', 'b']"
);
assert!(reader.inner.is_empty());
let reader_ref = reader.borrow();
assert!(reader_ref.inner.is_empty());
}

View File

@ -64,7 +64,7 @@ impl PySequenceProtocol for ByteSequence {
}
}
fn __concat__(&self, other: &Self) -> PyResult<Self> {
fn __concat__(&self, other: PyRef<'p, Self>) -> PyResult<Self> {
let mut elements = self.elements.clone();
elements.extend_from_slice(&other.elements);
Ok(Self { elements })

View File

@ -1,5 +1,5 @@
use pyo3::prelude::*;
use pyo3::{types::PyType, wrap_pyfunction, wrap_pymodule, PyClassShell};
use pyo3::{types::PyType, wrap_pyfunction, wrap_pymodule, PyCell};
mod common;
@ -144,7 +144,7 @@ fn test_methods() {
let _ = a;
}
#[text_signature = "($self, b)"]
fn pyself_method(_this: &PyClassShell<Self>, b: i32) {
fn pyself_method(_this: &PyCell<Self>, b: i32) {
let _ = b;
}
#[classmethod]

View File

@ -1,7 +1,7 @@
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::types::{PyDict, PyTuple};
use pyo3::{py_run, wrap_pyfunction, AsPyRef, PyClassShell};
use pyo3::{py_run, wrap_pyfunction, AsPyRef, PyCell};
mod common;
@ -15,7 +15,7 @@ impl MutRefArg {
fn get(&self) -> PyResult<i32> {
Ok(self.n)
}
fn set_other(&self, other: &mut MutRefArg) -> PyResult<()> {
fn set_other(&self, mut other: PyRefMut<MutRefArg>) -> PyResult<()> {
other.n = 100;
Ok(())
}
@ -31,7 +31,8 @@ fn mut_ref_arg() {
let d = [("inst1", &inst1), ("inst2", &inst2)].into_py_dict(py);
py.run("inst1.set_other(inst2)", None, Some(d)).unwrap();
assert_eq!(inst2.as_ref(py).n, 100);
let inst2 = inst2.as_ref(py).borrow();
assert_eq!(inst2.n, 100);
}
#[pyclass]
@ -81,8 +82,8 @@ fn intopytuple_pyclass() {
let py = gil.python();
let tup = (
PyClassShell::new_ref(py, SimplePyClass {}).unwrap(),
PyClassShell::new_ref(py, SimplePyClass {}).unwrap(),
PyCell::new(py, SimplePyClass {}).unwrap(),
PyCell::new(py, SimplePyClass {}).unwrap(),
);
py_assert!(py, tup, "type(tup[0]).__name__ == 'SimplePyClass'");
py_assert!(py, tup, "type(tup[0]).__name__ == type(tup[1]).__name__");
@ -106,8 +107,8 @@ fn pytuple_pyclass_iter() {
let tup = PyTuple::new(
py,
[
PyClassShell::new_ref(py, SimplePyClass {}).unwrap(),
PyClassShell::new_ref(py, SimplePyClass {}).unwrap(),
PyCell::new(py, SimplePyClass {}).unwrap(),
PyCell::new(py, SimplePyClass {}).unwrap(),
]
.iter(),
);
@ -127,7 +128,7 @@ impl PickleSupport {
}
pub fn __reduce__<'py>(
slf: &'py PyClassShell<Self>,
slf: &'py PyCell<Self>,
py: Python<'py>,
) -> PyResult<(PyObject, &'py PyTuple, PyObject)> {
let cls = slf.to_object(py).getattr(py, "__class__")?;
@ -141,7 +142,7 @@ fn add_module(py: Python, module: &PyModule) -> PyResult<()> {
.dict()
.get_item("modules")
.unwrap()
.downcast_mut::<PyDict>()?
.downcast::<PyDict>()?
.set_item(module.name()?, module)
}
@ -152,7 +153,7 @@ fn test_pickle() {
let module = PyModule::new(py, "test_module").unwrap();
module.add_class::<PickleSupport>().unwrap();
add_module(py, module).unwrap();
let inst = PyClassShell::new_ref(py, PickleSupport {}).unwrap();
let inst = PyCell::new(py, PickleSupport {}).unwrap();
py_run!(
py,
inst,

View File

@ -4,5 +4,4 @@ error[E0277]: the trait bound `TestClass: std::clone::Clone` is not satisfied
15 | let t: TestClass = pyvalue.extract(py).unwrap();
| ^^^^^^^ the trait `std::clone::Clone` is not implemented for `TestClass`
|
= note: required because of the requirements on the impl of `pyo3::conversion::extract_impl::ExtractImpl<'_, TestClass>` for `pyo3::conversion::extract_impl::Cloned`
= note: required because of the requirements on the impl of `pyo3::conversion::FromPyObject<'_>` for `TestClass`