Merge pull request #770 from kngwyu/pycell
Rename PyClassShell with `PyCell` and do mutability checking
This commit is contained in:
commit
fee755adbe
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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] },
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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("e! { &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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
133
src/callback.rs
133
src/callback.rs
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
11
src/err.rs
11
src/err.rs
|
@ -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()
|
||||
|
|
|
@ -21,6 +21,7 @@ pub struct setentry {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct PySetObject {
|
||||
pub ob_base: PyObject,
|
||||
pub fill: Py_ssize_t,
|
||||
|
|
|
@ -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 _;
|
||||
|
|
|
@ -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()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
204
src/pyclass.rs
204
src/pyclass.rs
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>>,
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`
|
||||
|
|
Loading…
Reference in New Issue