From 41ffe1ecdd4befd33c80d6fedc8e1a701958dcfe Mon Sep 17 00:00:00 2001 From: konstin Date: Mon, 4 Mar 2019 23:17:48 +0100 Subject: [PATCH 01/17] Add notes about the c api and stacktraces to the guide --- guide/src/SUMMARY.md | 1 + guide/src/advanced.md | 7 +++++++ guide/src/debugging.md | 9 +++++++++ 3 files changed, 17 insertions(+) create mode 100644 guide/src/advanced.md diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md index 24bc9689..64515c6e 100644 --- a/guide/src/SUMMARY.md +++ b/guide/src/SUMMARY.md @@ -8,5 +8,6 @@ - [Python Class](class.md) - [Parallelism](parallelism.md) - [Debugging](debugging.md) +- [Advanced Topics](advanced.md) - [Building and Distribution](building-and-distribution.md) - [Appendix: Pyo3 and rust-cpython](rust-cpython.md) diff --git a/guide/src/advanced.md b/guide/src/advanced.md new file mode 100644 index 00000000..7c47505a --- /dev/null +++ b/guide/src/advanced.md @@ -0,0 +1,7 @@ +# Advanced topics + +## ffi + +pyo3 exposes much of python's c api through the `ffi`. + +The c api is naturually unsafe and requires you to manage reference counts, errors and specific invariants yourself. Please refer to the [C API Reference Manual](https://docs.python.org/3/c-api/) and [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/ffi.html) before using any function from that api. \ No newline at end of file diff --git a/guide/src/debugging.md b/guide/src/debugging.md index b268a155..c1699922 100644 --- a/guide/src/debugging.md +++ b/guide/src/debugging.md @@ -29,3 +29,12 @@ Activate an environment with the debug interpreter and recompile. If you're on l [Download the suppressions file for cpython](https://raw.githubusercontent.com/python/cpython/master/Misc/valgrind-python.supp). Run valgrind with `valgrind --suppressions=valgrind-python.supp ./my-command --with-options` + +## Getting a stacktrace + +The best start to investigate a crash such as an segmentation fault is a backtrace. + + * Link against a debug build of python as described in the previous chapter + * Run `gdb ` + * Enter `r` to run + * After the crash occured, enter `bt` or `bt full` to print the stacktrace \ No newline at end of file From f68ae0cfae46adc9846525b20d46fc6069aefaad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Tue, 5 Mar 2019 21:52:44 +0100 Subject: [PATCH 02/17] Fix flaky tests The tests were flaky because they took references to the empty tuple which is a singleton. Use a unique object for each test to make them independent. --- src/gil.rs | 59 +++++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/gil.rs b/src/gil.rs index 7f04b511..a6a1ba76 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -334,14 +334,21 @@ mod test { use crate::ToPyObject; use crate::{ffi, gil}; - fn get_object() -> PyObject { + fn get_object() -> (PyObject, *mut ffi::PyObject) { // Convenience function for getting a single unique object let gil = Python::acquire_gil(); let py = gil.python(); - let obj = py.eval("object()", None, None).unwrap(); + let obj = py.eval("object()", None, None).unwrap().to_object(py); + let obj_ptr = obj.as_ptr(); - obj.to_object(py) + // The refcount would become 0 after dropping, which means the + // GC can free the pointer and getting the refcount would be UB. + // This increfs ensures that it remains 1. + unsafe { + ffi::Py_INCREF(obj_ptr); + } + (obj, obj_ptr) } #[test] @@ -352,21 +359,20 @@ mod test { let p: &'static mut ReleasePool = &mut *POOL; let cnt; - let empty; + let (_obj, obj_ptr) = get_object(); { let gil = Python::acquire_gil(); let py = gil.python(); - empty = ffi::PyTuple_New(0); - cnt = ffi::Py_REFCNT(empty) - 1; - let _ = gil::register_owned(py, NonNull::new(empty).unwrap()); + cnt = ffi::Py_REFCNT(obj_ptr) - 1; + let _ = gil::register_owned(py, NonNull::new(obj_ptr).unwrap()); assert_eq!(p.owned.len(), 1); } { let _gil = Python::acquire_gil(); assert_eq!(p.owned.len(), 0); - assert_eq!(cnt, ffi::Py_REFCNT(empty)); + assert_eq!(cnt, ffi::Py_REFCNT(obj_ptr)); } } } @@ -381,30 +387,28 @@ mod test { let p: &'static mut ReleasePool = &mut *POOL; let cnt; - let empty; + let (_obj, obj_ptr) = get_object(); { let _pool = GILPool::new(); assert_eq!(p.owned.len(), 0); - // empty tuple is singleton - empty = ffi::PyTuple_New(0); - cnt = ffi::Py_REFCNT(empty) - 1; + cnt = ffi::Py_REFCNT(obj_ptr) - 1; - let _ = gil::register_owned(py, NonNull::new(empty).unwrap()); + let _ = gil::register_owned(py, NonNull::new(obj_ptr).unwrap()); assert_eq!(p.owned.len(), 1); { let _pool = GILPool::new(); - let empty = ffi::PyTuple_New(0); - let _ = gil::register_owned(py, NonNull::new(empty).unwrap()); + let (_obj, obj_ptr) = get_object(); + let _ = gil::register_owned(py, NonNull::new(obj_ptr).unwrap()); assert_eq!(p.owned.len(), 2); } assert_eq!(p.owned.len(), 1); } { assert_eq!(p.owned.len(), 0); - assert_eq!(cnt, ffi::Py_REFCNT(empty)); + assert_eq!(cnt, ffi::Py_REFCNT(obj_ptr)); } } } @@ -416,8 +420,7 @@ mod test { unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let obj = get_object(); - let obj_ptr = obj.as_ptr(); + let (_obj, obj_ptr) = get_object(); let cnt; { let gil = Python::acquire_gil(); @@ -445,8 +448,7 @@ mod test { unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let obj = get_object(); - let obj_ptr = obj.as_ptr(); + let (_obj, obj_ptr) = get_object(); let cnt; { let gil = Python::acquire_gil(); @@ -484,26 +486,19 @@ mod test { unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let ob; let cnt; - let empty; + let (obj, obj_ptr) = get_object(); { - let gil = Python::acquire_gil(); - let py = gil.python(); assert_eq!(p.owned.len(), 0); - - // empty tuple is singleton - empty = ffi::PyTuple_New(0); - cnt = ffi::Py_REFCNT(empty); - ob = PyObject::from_owned_ptr(py, empty); + cnt = ffi::Py_REFCNT(obj_ptr); } - drop(ob); - assert_eq!(cnt, ffi::Py_REFCNT(empty)); + drop(obj); + assert_eq!(cnt, ffi::Py_REFCNT(obj_ptr)); { let _gil = Python::acquire_gil(); } - assert_eq!(cnt - 1, ffi::Py_REFCNT(empty)); + assert_eq!(cnt - 1, ffi::Py_REFCNT(obj_ptr)); } } } From ae93e71a3055b94e6feccb87ff1ad6dfd05748bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Wed, 6 Mar 2019 20:31:02 +0100 Subject: [PATCH 03/17] Fix some typos and grammar in class.md --- guide/src/class.md | 70 ++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index fab6abd0..21ad35b6 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -2,7 +2,8 @@ ## Define new class -To define python custom class, rust struct needs to be annotated with `#[pyclass]` attribute. +To define a custom python class, a rust struct needs to be annotated with the +`#[pyclass]` attribute. ```rust # use pyo3::prelude::*; @@ -19,12 +20,12 @@ The above example generates implementations for `PyTypeInfo` and `PyTypeObject` ## Get Python objects from `pyclass` You can use `pyclass`es like normal rust structs. -However, if instantiate noramlly, you can't treat `pyclass`es as Python objects. +However, if instantiated normally, you can't treat `pyclass`es as Python objects. To get a Python object which includes `pyclass`, we have to use some special methods. ### `PyRef` -`PyRef` is a special reference, which ensures that the reffrered struct is a part of +`PyRef` is a special reference, which ensures that the referred struct is a part of a Python object, and you are also holding the GIL. You can get an instance of `PyRef` by `PyRef::new`, which does 3 things: @@ -219,7 +220,7 @@ Getter or setter function's name is used as property name by default. There are ways how to override name. If function name starts with `get_` or `set_` for getter or setter respectively. -Descriptor name becomes function name with prefix removed. This is useful in case os +Descriptor name becomes function name with prefix removed. This is useful in case of rust's special keywords like `type`. ```rust @@ -248,7 +249,7 @@ impl MyClass { In this case property `num` is defined. And it is available from python code as `self.num`. Also both `#[getter]` and `#[setter]` attributes accepts one parameter. -If parameter is specified, it is used and property name. i.e. +If this parameter is specified, it is used as a property name. i.e. ```rust # use pyo3::prelude::*; @@ -273,7 +274,7 @@ impl MyClass { } ``` -In this case property `number` is defined. And it is available from python code as `self.number`. +In this case the property `number` is defined and is available from python code as `self.number`. For simple cases you can also define getters and setters in your Rust struct field definition, for example: @@ -290,10 +291,9 @@ Then it is available from Python code as `self.num`. ## Instance methods -To define python compatible method, `impl` block for struct has to be annotated -with `#[pymethods]` attribute. `pyo3` library generates python compatible -wrappers for all functions in this block with some variations, like descriptors, -class method static methods, etc. +To define a python compatible method, `impl` block for struct has to be annotated with the +`#[pymethods]` attribute. PyO3 generates python compatible wrappers for all functions in this +block with some variations, like descriptors, class method static methods, etc. ```rust # use pyo3::prelude::*; @@ -342,8 +342,8 @@ From python perspective `method2`, in above example, does not accept any argumen ## Class methods -To specify class method for custom class, method needs to be annotated -with`#[classmethod]` attribute. +To specify a class method for a custom class, the method needs to be annotated +with the `#[classmethod]` attribute. ```rust # use pyo3::prelude::*; @@ -373,9 +373,9 @@ Declares a class method callable from Python. ## Static methods -To specify class method for custom class, method needs to be annotated -with `#[staticmethod]` attribute. The return type must be `PyResult` -for some `T` that implements `IntoPyObject`. +To specify a static method for a custom class, method needs to be annotated with +`#[staticmethod]` attribute. The return type must be `PyResult` for some `T` that implements +`IntoPyObject`. ```rust # use pyo3::prelude::*; @@ -396,8 +396,8 @@ impl MyClass { ## Callable object -To specify custom `__call__` method for custom class, call method needs to be annotated -with `#[call]` attribute. Arguments of the method are specified same as for instance method. +To specify a custom `__call__` method for a custom class, call methods need to be annotated with +the `#[call]` attribute. Arguments of the method are specified same as for instance method. ```rust # use pyo3::prelude::*; @@ -421,14 +421,13 @@ impl MyClass { ## Method arguments -By default pyo3 library uses function signature to determine which arguments are required. -Then it scans incoming `args` parameter and then incoming `kwargs` parameter. If it can not -find all required parameters, it raises `TypeError` exception. -It is possible to override default behavior with `#[args(...)]` attribute. `args` attribute -accept comma separated list of parameters in form `attr_name="default value"`. Each parameter -has to match method parameter by name. +By default PyO3 uses function signatures to determine which arguments are required. Then it scans +incoming `args` parameter and then incoming `kwargs` parameter. If it can not find all required +parameters, it raises a `TypeError` exception. It is possible to override the default behavior +with `#[args(...)]` attribute. `args` attribute accepts a comma separated list of parameters in +form of `attr_name="default value"`. Each parameter has to match the method parameter by name. -Each parameter could one of following type: +Each parameter could be one of following type: * "\*": var arguments separator, each parameter defined after "*" is keyword only parameters. corresponds to python's `def meth(*, arg1.., arg2=..)` @@ -438,7 +437,7 @@ Each parameter could one of following type: Type of `kwargs` parameter has to be `Option<&PyDict>`. * arg="Value": arguments with default value. corresponds to python's `def meth(arg=Value)`. if `arg` argument is defined after var arguments it is treated as keyword argument. - Note that `Value` has to be valid rust code, pyo3 just inserts it into generated + Note that `Value` has to be valid rust code, PyO3 just inserts it into generated code unmodified. Example: @@ -464,11 +463,10 @@ impl MyClass { ## Class customizations -Python object model defines several protocols for different object behavior, -like sequence, mapping or number protocols. pyo3 library defines separate trait for each -of them. To provide specific python object behavior you need to implement specific trait -for your struct. Important note, each protocol implementation block has to be annotated -with `#[pyproto]` attribute. +Python's object model defines several protocols for different object behavior, like sequence, +mapping or number protocols. PyO3 defines separate traits for each of them. To provide specific +python object behavior you need to implement the specific trait for your struct. Important note, +each protocol implementation block has to be annotated with `#[pyproto]` attribute. ### Basic object customization @@ -527,7 +525,7 @@ Each methods corresponds to python's `self.attr`, `self.attr = value` and `del s * `fn __bool__(&self) -> PyResult` - Determines the "truthyness" of the object. + Determines the "truthiness" of the object. This method works for both python 3 and python 2, even on Python 2.7 where the Python spelling was `__nonzero__`. @@ -574,9 +572,9 @@ impl PyGCProtocol for ClassWithGCSupport { } ``` -Special protocol trait implementation has to be annotated with `#[pyproto]` attribute. +Special protocol trait implementations have to be annotated with the `#[pyproto]` attribute. -It is also possible to enable gc for custom class using `gc` parameter for `class` annotation. +It is also possible to enable GC for custom class using `gc` parameter for `class` annotation. i.e. `#[pyclass(gc)]`. In that case instances of custom class participate in python garbage collector, and it is possible to track them with `gc` module methods. @@ -618,6 +616,10 @@ TODO: Which traits to implement (basically `PyTypeCreate: PyObjectAlloc + PyType ## How methods are implemented -Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while pyo3 needs a trait with a function that returns all methods. Since it's impossible make the code generation in pyclass dependent on whether there is an impl block, we'd need to make to implement the trait on `#[pyclass]` and override the implementation in `#[pymethods]`, which is to my best knowledge only possible with the specialization feature, which is can't be used on stable. +Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a +trait with a function that returns all methods. Since it's impossible to make the code generation in +pyclass dependent on whether there is an impl block, we'd need to implement the trait on +`#[pyclass]` and override the implementation in `#[pymethods]`, which is to the best of my knowledge +only possible with the specialization feature, which can't be used on stable. To escape this we use [inventory](https://github.com/dtolnay/inventory), which allows us to collect `impl`s from arbitrary source code by exploiting some binary trick. See [inventory: how it works](https://github.com/dtolnay/inventory#how-it-works) and `pyo3_derive_backend::py_class::impl_inventory` for more details. From e2e4733b828baba027141ad3f1f03bb16b83dffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Thu, 14 Feb 2019 10:41:52 +0100 Subject: [PATCH 04/17] Test examples in user guide with travis Test could only be activated for Python 3.5 and some tests had to be ignored, see #381 and #387. --- .travis.yml | 2 +- Cargo.toml | 3 +++ README.md | 6 ++++++ ci/travis/test.sh | 1 + guide/src/class.md | 34 +++++++++++++++++++++++++++------- guide/src/conversions.md | 2 ++ guide/src/exception.md | 8 ++++++++ guide/src/function.md | 5 ++++- guide/src/get_started.md | 2 ++ guide/src/module.md | 2 ++ guide/src/rust-cpython.md | 1 + tests/test_doc.rs | 4 ++-- 12 files changed, 59 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9b10699e..7d2823ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ matrix: env: FEATURES=python2 - name: Python 3.5 python: "3.5" - env: FEATURES=python3 + env: FEATURES="python3 test-doc" - name: Python 3.6 python: "3.6" env: FEATURES=python3 diff --git a/Cargo.toml b/Cargo.toml index 874df4bd..39d72be7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,9 @@ extension-module = [] # are welcome. # abi3 = [] +# Use this feature to test the examples in the user guide +test-doc = [] + [workspace] members = [ "pyo3cls", diff --git a/README.md b/README.md index 503e7214..5c9ac754 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,9 @@ features = ["extension-module"] **`src/lib.rs`** ```rust +// Not required when using Rust 2018 +extern crate pyo3; + use pyo3::prelude::*; use pyo3::wrap_pyfunction; @@ -95,6 +98,9 @@ pyo3 = "0.6.0-alpha.4" Example program displaying the value of `sys.version`: ```rust +// Not required when using Rust 2018 +extern crate pyo3; + use pyo3::prelude::*; use pyo3::types::PyDict; diff --git a/ci/travis/test.sh b/ci/travis/test.sh index 70ebc801..51011e8c 100755 --- a/ci/travis/test.sh +++ b/ci/travis/test.sh @@ -1,6 +1,7 @@ #!/bin/bash set -ex +cargo clean cargo test --features "$FEATURES num-complex" if [ $TRAVIS_JOB_NAME = 'Minimum nightly' ]; then cargo fmt --all -- --check diff --git a/guide/src/class.md b/guide/src/class.md index 21ad35b6..c4b1323d 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -6,6 +6,7 @@ To define a custom python class, a rust struct needs to be annotated with the `#[pyclass]` attribute. ```rust +# extern crate pyo3; # use pyo3::prelude::*; #[pyclass] @@ -35,6 +36,7 @@ You can get an instance of `PyRef` by `PyRef::new`, which does 3 things: You can use `PyRef` just like `&T`, because it implements `Deref`. ```rust +# extern crate pyo3; # use pyo3::prelude::*; # use pyo3::types::PyDict; #[pyclass] @@ -54,6 +56,7 @@ dict.set_item("obj", obj).unwrap(); ### `PyRefMut` `PyRefMut` is a mutable version of `PyRef`. ```rust +# extern crate pyo3; # use pyo3::prelude::*; #[pyclass] struct MyClass { @@ -71,6 +74,7 @@ obj.num = 5; You can use it to avoid lifetime problems. ```rust +# extern crate pyo3; # use pyo3::prelude::*; #[pyclass] struct MyClass { @@ -110,6 +114,7 @@ To declare a constructor, you need to define a class method and annotate it with attribute. Only the python `__new__` method can be specified, `__init__` is not available. ```rust +# extern crate pyo3; # use pyo3::prelude::*; # use pyo3::PyRawObject; #[pyclass] @@ -150,7 +155,8 @@ By default `PyObject` is used as default base class. To override default base cl `new` method accepts `PyRawObject` object. `obj` instance must be initialized with value of custom class struct. Subclass must call parent's `new` method. -```rust +```rust,ignore +# extern crate pyo3; # use pyo3::prelude::*; # use pyo3::PyRawObject; #[pyclass] @@ -184,7 +190,7 @@ impl SubClass { } fn method2(&self) -> PyResult<()> { - self.get_base().method() + self.get_base().method() } } ``` @@ -200,6 +206,7 @@ Descriptor methods can be defined in attributes. i.e. ```rust +# extern crate pyo3; # use pyo3::prelude::*; # #[pyclass] # struct MyClass { @@ -223,7 +230,8 @@ If function name starts with `get_` or `set_` for getter or setter respectively. Descriptor name becomes function name with prefix removed. This is useful in case of rust's special keywords like `type`. -```rust +```rust,ignore +# extern crate pyo3; # use pyo3::prelude::*; # #[pyclass] # struct MyClass { @@ -251,7 +259,8 @@ In this case property `num` is defined. And it is available from python code as Also both `#[getter]` and `#[setter]` attributes accepts one parameter. If this parameter is specified, it is used as a property name. i.e. -```rust +```rust,ignore +# extern crate pyo3; # use pyo3::prelude::*; # #[pyclass] # struct MyClass { @@ -278,7 +287,8 @@ In this case the property `number` is defined and is available from python code For simple cases you can also define getters and setters in your Rust struct field definition, for example: -```rust +```rust,ignore +# extern crate pyo3; # use pyo3::prelude::*; #[pyclass] struct MyClass { @@ -296,6 +306,7 @@ To define a python compatible method, `impl` block for struct has to be annotate block with some variations, like descriptors, class method static methods, etc. ```rust +# extern crate pyo3; # use pyo3::prelude::*; # #[pyclass] # struct MyClass { @@ -323,6 +334,7 @@ The return type must be `PyResult` for some `T` that implements `IntoPyObject get injected by method wrapper. i.e ```rust +# extern crate pyo3; # use pyo3::prelude::*; # #[pyclass] # struct MyClass { @@ -346,6 +358,7 @@ To specify a class method for a custom class, the method needs to be annotated with the `#[classmethod]` attribute. ```rust +# extern crate pyo3; # use pyo3::prelude::*; # use pyo3::types::PyType; # #[pyclass] @@ -378,6 +391,7 @@ To specify a static method for a custom class, method needs to be annotated with `IntoPyObject`. ```rust +# extern crate pyo3; # use pyo3::prelude::*; # #[pyclass] # struct MyClass { @@ -400,6 +414,7 @@ To specify a custom `__call__` method for a custom class, call methods need to b the `#[call]` attribute. Arguments of the method are specified same as for instance method. ```rust +# extern crate pyo3; # use pyo3::prelude::*; use pyo3::types::PyTuple; # #[pyclass] @@ -442,6 +457,7 @@ Each parameter could be one of following type: Example: ```rust +# extern crate pyo3; # use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; # @@ -543,6 +559,8 @@ These correspond to the slots `tp_traverse` and `tp_clear` in the Python C API. as every cycle must contain at least one mutable reference. Example: ```rust +extern crate pyo3; + use pyo3::prelude::*; use pyo3::PyTraverseError; use pyo3::gc::{PyGCProtocol, PyVisit}; @@ -583,14 +601,16 @@ collector, and it is possible to track them with `gc` module methods. Iterators can be defined using the [`PyIterProtocol`](https://docs.rs/pyo3/0.6.0-alpha.4/class/iter/trait.PyIterProtocol.html) trait. It includes two methods `__iter__` and `__next__`: - * `fn __iter__(&mut self) -> PyResult` - * `fn __next__(&mut self) -> PyResult>` + * `fn __iter__(slf: PyRefMut) -> PyResult` + * `fn __next__(slf: PyRefMut) -> PyResult>` Returning `Ok(None)` from `__next__` indicates that that there are no further items. Example: ```rust +extern crate pyo3; + use pyo3::prelude::*; use pyo3::PyIterProtocol; diff --git a/guide/src/conversions.md b/guide/src/conversions.md index a8b34568..537f86b1 100644 --- a/guide/src/conversions.md +++ b/guide/src/conversions.md @@ -24,6 +24,7 @@ provides two methods: Both methods accept `args` and `kwargs` arguments. ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; @@ -61,6 +62,7 @@ fn main() { [`IntoPyDict`][IntoPyDict] trait to convert other dict-like containers, e.g. `HashMap`, `BTreeMap` as well as tuples with up to 10 elements and `Vec`s where each element is a two element tuple. ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::types::{IntoPyDict, PyDict}; use std::collections::HashMap; diff --git a/guide/src/exception.md b/guide/src/exception.md index a6c4bb3c..de12e83a 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -5,6 +5,7 @@ You can use the `create_exception!` macro to define a new exception type: ```rust +# extern crate pyo3; use pyo3::create_exception; create_exception!(module, MyError, pyo3::exceptions::Exception); @@ -16,6 +17,7 @@ create_exception!(module, MyError, pyo3::exceptions::Exception); For example: ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::create_exception; use pyo3::types::PyDict; @@ -40,6 +42,7 @@ fn main() { To raise an exception, first you need to obtain an exception type and construct a new [`PyErr`](https://docs.rs/pyo3/0.2.7/struct.PyErr.html), then call [`PyErr::restore()`](https://docs.rs/pyo3/0.2.7/struct.PyErr.html#method.restore) method to write the exception back to the Python interpreter's global state. ```rust +# extern crate pyo3; use pyo3::{Python, PyErr}; use pyo3::exceptions; @@ -64,6 +67,7 @@ has corresponding rust type, exceptions defined by `create_exception!` and `impo have rust type as well. ```rust +# extern crate pyo3; # use pyo3::exceptions; # use pyo3::prelude::*; # fn check_for_error() -> bool {false} @@ -82,6 +86,7 @@ Python has an [`isinstance`](https://docs.python.org/3/library/functions.html#is in `PyO3` there is a [`Python::is_instance()`](https://docs.rs/pyo3/0.2.7/struct.Python.html#method.is_instance) method which does the same thing. ```rust +# extern crate pyo3; use pyo3::Python; use pyo3::types::{PyBool, PyList}; @@ -100,6 +105,7 @@ fn main() { To check the type of an exception, you can simply do: ```rust +# extern crate pyo3; # use pyo3::exceptions; # use pyo3::prelude::*; # fn main() { @@ -151,6 +157,7 @@ The code snippet above will raise `OSError` in Python if `TcpListener::bind()` r types so `try!` macro or `?` operator can be used. ```rust +# extern crate pyo3; use pyo3::prelude::*; fn parse_int(s: String) -> PyResult { @@ -168,6 +175,7 @@ It is possible to use exception defined in python code as native rust types. for that exception. ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::import_exception; diff --git a/guide/src/function.md b/guide/src/function.md index 65f06354..836d723f 100644 --- a/guide/src/function.md +++ b/guide/src/function.md @@ -6,6 +6,7 @@ the function to a [module](./module.md) One way is defining the function in the module definition. ```rust +# extern crate pyo3; use pyo3::prelude::*; #[pymodule] @@ -30,6 +31,7 @@ as first parameter, the function name as second and an instance of `Python` as third. ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::wrap_pyfunction; @@ -60,6 +62,7 @@ built-ins are new in Python 3 — in Python 2, it is simply considered to be par of the doc-string. ```rust +# extern crate pyo3; use pyo3::prelude::*; /// add(a, b, /) @@ -73,7 +76,7 @@ fn add(a: u64, b: u64) -> u64 { ``` When annotated like this, signatures are also correctly displayed in IPython. -``` +```ignore >>> pyo3_test.add? Signature: pyo3_test.add(a, b, /) Docstring: This function adds two unsigned 64-bit integers. diff --git a/guide/src/get_started.md b/guide/src/get_started.md index dbddab18..d8d71cfe 100644 --- a/guide/src/get_started.md +++ b/guide/src/get_started.md @@ -45,6 +45,7 @@ features = ["extension-module"] **`src/lib.rs`** ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::wrap_pyfunction; @@ -89,6 +90,7 @@ pyo3 = "0.5" Example program displaying the value of `sys.version`: ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::types::PyDict; diff --git a/guide/src/module.md b/guide/src/module.md index 7a4a2ea8..f18165d3 100644 --- a/guide/src/module.md +++ b/guide/src/module.md @@ -3,6 +3,7 @@ As shown in the Getting Started chapter, you can create a module as follows: ```rust +# extern crate pyo3; use pyo3::prelude::*; // add bindings to the generated python module @@ -52,6 +53,7 @@ Which means that the above Python code will print `This module is implemented in In python, modules are first class objects. This means can store them as values or add them to dicts or other modules: ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::{wrap_pyfunction, wrap_pymodule}; use pyo3::types::PyDict; diff --git a/guide/src/rust-cpython.md b/guide/src/rust-cpython.md index 466dc1a0..8295dc96 100644 --- a/guide/src/rust-cpython.md +++ b/guide/src/rust-cpython.md @@ -25,6 +25,7 @@ py_class!(class MyClass |py| { **pyo3** ```rust +# extern crate pyo3; use pyo3::prelude::*; use pyo3::PyRawObject; diff --git a/tests/test_doc.rs b/tests/test_doc.rs index b5f46225..49688b64 100644 --- a/tests/test_doc.rs +++ b/tests/test_doc.rs @@ -14,8 +14,8 @@ fn assert_file>(path: P) { doc.test_file(path.as_ref()) } -#[ignore] #[test] +#[cfg(feature = "test-doc")] fn test_guide() { let guide_path = PathBuf::from("guide").join("src"); for entry in guide_path.read_dir().unwrap() { @@ -23,8 +23,8 @@ fn test_guide() { } } -#[ignore] #[test] +#[cfg(feature = "test-doc")] fn test_readme() { assert_file("README.md") } From 3a6e0a2724a1b2a1a5e77faeaf766411a8b6098c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Sun, 10 Mar 2019 00:28:25 +0100 Subject: [PATCH 05/17] Remove use of `INCREF` and `cnt` --- src/gil.rs | 81 +++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/src/gil.rs b/src/gil.rs index a6a1ba76..eff1a43a 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -334,45 +334,41 @@ mod test { use crate::ToPyObject; use crate::{ffi, gil}; - fn get_object() -> (PyObject, *mut ffi::PyObject) { + fn get_object() -> PyObject { // Convenience function for getting a single unique object let gil = Python::acquire_gil(); let py = gil.python(); - let obj = py.eval("object()", None, None).unwrap().to_object(py); - let obj_ptr = obj.as_ptr(); + let obj = py.eval("object()", None, None).unwrap(); - // The refcount would become 0 after dropping, which means the - // GC can free the pointer and getting the refcount would be UB. - // This increfs ensures that it remains 1. - unsafe { - ffi::Py_INCREF(obj_ptr); - } - (obj, obj_ptr) + obj.to_object(py) } #[test] fn test_owned() { gil::init_once(); + let gil = Python::acquire_gil(); + let py = gil.python(); + let obj = get_object(); + let obj_ptr = obj.as_ptr(); + // Ensure that obj does not get freed + let _ref = obj.clone_ref(py); unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let cnt; - let (_obj, obj_ptr) = get_object(); { let gil = Python::acquire_gil(); let py = gil.python(); + let _ = gil::register_owned(py, obj.into_nonnull()); - cnt = ffi::Py_REFCNT(obj_ptr) - 1; - let _ = gil::register_owned(py, NonNull::new(obj_ptr).unwrap()); - + assert_eq!(ffi::Py_REFCNT(obj_ptr), 2); assert_eq!(p.owned.len(), 1); } { let _gil = Python::acquire_gil(); assert_eq!(p.owned.len(), 0); - assert_eq!(cnt, ffi::Py_REFCNT(obj_ptr)); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } } } @@ -382,33 +378,33 @@ mod test { gil::init_once(); let gil = Python::acquire_gil(); let py = gil.python(); + let obj = get_object(); + // Ensure that obj does not get freed + let _ref = obj.clone_ref(py); + let obj_ptr = obj.as_ptr(); unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let cnt; - let (_obj, obj_ptr) = get_object(); { let _pool = GILPool::new(); assert_eq!(p.owned.len(), 0); - cnt = ffi::Py_REFCNT(obj_ptr) - 1; - - let _ = gil::register_owned(py, NonNull::new(obj_ptr).unwrap()); + let _ = gil::register_owned(py, obj.into_nonnull()); assert_eq!(p.owned.len(), 1); - + assert_eq!(ffi::Py_REFCNT(obj_ptr), 2); { let _pool = GILPool::new(); - let (_obj, obj_ptr) = get_object(); - let _ = gil::register_owned(py, NonNull::new(obj_ptr).unwrap()); + let obj = get_object(); + let _ = gil::register_owned(py, obj.into_nonnull()); assert_eq!(p.owned.len(), 2); } assert_eq!(p.owned.len(), 1); } { assert_eq!(p.owned.len(), 0); - assert_eq!(cnt, ffi::Py_REFCNT(obj_ptr)); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } } } @@ -420,23 +416,22 @@ mod test { unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let (_obj, obj_ptr) = get_object(); - let cnt; + let obj = get_object(); + let obj_ptr = obj.as_ptr(); { let gil = Python::acquire_gil(); let py = gil.python(); assert_eq!(p.borrowed.len(), 0); - cnt = ffi::Py_REFCNT(obj_ptr); gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap()); assert_eq!(p.borrowed.len(), 1); - assert_eq!(ffi::Py_REFCNT(obj_ptr), cnt); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } { let _gil = Python::acquire_gil(); assert_eq!(p.borrowed.len(), 0); - assert_eq!(ffi::Py_REFCNT(obj_ptr), cnt); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } } } @@ -448,18 +443,17 @@ mod test { unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let (_obj, obj_ptr) = get_object(); - let cnt; + let obj = get_object(); + let obj_ptr = obj.as_ptr(); { let gil = Python::acquire_gil(); let py = gil.python(); assert_eq!(p.borrowed.len(), 0); - cnt = ffi::Py_REFCNT(obj_ptr); gil::register_borrowed(py, NonNull::new(obj_ptr).unwrap()); assert_eq!(p.borrowed.len(), 1); - assert_eq!(ffi::Py_REFCNT(obj_ptr), cnt); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); { let _pool = GILPool::new(); @@ -469,36 +463,41 @@ mod test { } assert_eq!(p.borrowed.len(), 1); - assert_eq!(ffi::Py_REFCNT(obj_ptr), cnt); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } { let _gil = Python::acquire_gil(); assert_eq!(p.borrowed.len(), 0); - assert_eq!(ffi::Py_REFCNT(obj_ptr), cnt); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } } } + #[ignore] #[test] fn test_pyobject_drop() { gil::init_once(); + let gil = Python::acquire_gil(); + let py = gil.python(); + let obj = get_object(); + // Ensure that obj does not get freed + let _ref = obj.clone_ref(py); + let obj_ptr = obj.as_ptr(); unsafe { let p: &'static mut ReleasePool = &mut *POOL; - let cnt; - let (obj, obj_ptr) = get_object(); { assert_eq!(p.owned.len(), 0); - cnt = ffi::Py_REFCNT(obj_ptr); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 2); } drop(obj); - assert_eq!(cnt, ffi::Py_REFCNT(obj_ptr)); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); { let _gil = Python::acquire_gil(); } - assert_eq!(cnt - 1, ffi::Py_REFCNT(obj_ptr)); + assert_eq!(ffi::Py_REFCNT(obj_ptr), 1); } } } From b60b3395f7c0c71de442033ff1d97e16e622b347 Mon Sep 17 00:00:00 2001 From: konstin Date: Sun, 10 Mar 2019 22:16:49 +0100 Subject: [PATCH 06/17] Create pull_request_template.md --- .github/pull_request_template.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..b0d819b6 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +Thank you for contributing to pyo3! + +Here are some things you should check for submitting your pull request: + + - Run `cargo fmt` (This is checked by travis ci) + - Run `cargo clippy` and check there are no hard errors (There are a bunch of existing warnings; This is also checked by travis) + - If applicable, add an entry in the changelog. + - If applicable, add documentation to all new items and extend the guide. + - If applicable, add tests for all new or fixed functions + +You might want to run `tox` (`pip install tox`) locally to check compatibility with all supported python versions. If you're using linux or mac you might find the Makefile helpful for testing. From a9e78b98d1c74fb786b315a482d75b18f2e4df48 Mon Sep 17 00:00:00 2001 From: konstin Date: Mon, 11 Mar 2019 12:19:46 +0100 Subject: [PATCH 07/17] Lower travis log level --- ci/travis/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/travis/setup.sh b/ci/travis/setup.sh index 42a65a92..9ae10672 100755 --- a/ci/travis/setup.sh +++ b/ci/travis/setup.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -ex +set -e ### Setup Rust toolchain ####################################################### From 310e5976936952b07fd8cfb77d3741d3119c157c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Wed, 13 Mar 2019 17:14:17 +0100 Subject: [PATCH 08/17] Fix some typos in user guide --- guide/src/advanced.md | 4 ++-- guide/src/debugging.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/guide/src/advanced.md b/guide/src/advanced.md index 7c47505a..81087b58 100644 --- a/guide/src/advanced.md +++ b/guide/src/advanced.md @@ -2,6 +2,6 @@ ## ffi -pyo3 exposes much of python's c api through the `ffi`. +pyo3 exposes much of python's C api through the `ffi`. -The c api is naturually unsafe and requires you to manage reference counts, errors and specific invariants yourself. Please refer to the [C API Reference Manual](https://docs.python.org/3/c-api/) and [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/ffi.html) before using any function from that api. \ No newline at end of file +The C api is naturally unsafe and requires you to manage reference counts, errors and specific invariants yourself. Please refer to the [C API Reference Manual](https://docs.python.org/3/c-api/) and [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/ffi.html) before using any function from that api. \ No newline at end of file diff --git a/guide/src/debugging.md b/guide/src/debugging.md index c1699922..58b9e0a0 100644 --- a/guide/src/debugging.md +++ b/guide/src/debugging.md @@ -20,9 +20,9 @@ See [cargo expand](https://github.com/dtolnay/cargo-expand) for a more elaborate ## Running with Valgrind -Valgrind is a tool to detect memory managment bugs such as memory leaks. +Valgrind is a tool to detect memory management bugs such as memory leaks. -You first need to installa debug build of python, otherwise valgrind won't produce usable results. In ubuntu there's e.g. a `python3-dbg` package. +You first need to install a debug build of python, otherwise valgrind won't produce usable results. In ubuntu there's e.g. a `python3-dbg` package. Activate an environment with the debug interpreter and recompile. If you're on linux, use `ldd` with the name of you're binary and check that you're linking e.g. `libpython3.6dm.so.1.0` instead of `libpython3.6m.so.1.0`. @@ -37,4 +37,4 @@ The best start to investigate a crash such as an segmentation fault is a backtra * Link against a debug build of python as described in the previous chapter * Run `gdb ` * Enter `r` to run - * After the crash occured, enter `bt` or `bt full` to print the stacktrace \ No newline at end of file + * After the crash occurred, enter `bt` or `bt full` to print the stacktrace \ No newline at end of file From 438bd7f61664681032bd7886e18d4eca9c80d860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Wed, 13 Mar 2019 17:26:48 +0100 Subject: [PATCH 09/17] Refer to PyO3 in a consistent way --- guide/src/advanced.md | 2 +- guide/src/building-and-distribution.md | 6 +++--- guide/src/conversions.md | 4 ++-- guide/src/debugging.md | 2 +- guide/src/exception.md | 2 +- guide/src/get_started.md | 4 ++-- guide/src/module.md | 2 +- guide/src/parallelism.md | 4 ++-- guide/src/rust-cpython.md | 12 ++++++------ 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/guide/src/advanced.md b/guide/src/advanced.md index 81087b58..b6e6cc75 100644 --- a/guide/src/advanced.md +++ b/guide/src/advanced.md @@ -2,6 +2,6 @@ ## ffi -pyo3 exposes much of python's C api through the `ffi`. +PyO3 exposes much of python's C api through the `ffi`. The C api is naturally unsafe and requires you to manage reference counts, errors and specific invariants yourself. Please refer to the [C API Reference Manual](https://docs.python.org/3/c-api/) and [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/ffi.html) before using any function from that api. \ No newline at end of file diff --git a/guide/src/building-and-distribution.md b/guide/src/building-and-distribution.md index 7ef9eb40..eb1cd7b1 100644 --- a/guide/src/building-and-distribution.md +++ b/guide/src/building-and-distribution.md @@ -2,13 +2,13 @@ ## Python version -pyo3 uses a build script to determine the python version and set the correct linker arguments. By default it uses the `python3` executable. With the `python2` feature it uses the `python2` executable. You can override the python interpreter by setting `PYTHON_SYS_EXECUTABLE`. +PyO3 uses a build script to determine the python version and set the correct linker arguments. By default it uses the `python3` executable. With the `python2` feature it uses the `python2` executable. You can override the python interpreter by setting `PYTHON_SYS_EXECUTABLE`. ## Linking Different linker arguments must be set for libraries/extension modules and binaries, which includes both standalone binaries and tests. (More specifically, binaries must be told where to find libpython and libraries must not link to libpython for [manylinux](https://www.python.org/dev/peps/pep-0513/) compliance). -Since pyo3's build script can't know whether you're building a binary or a library, you have to activate the `extension-module` feature to get the build options for a library, or it'll default to binary. +Since PyO3's build script can't know whether you're building a binary or a library, you have to activate the `extension-module` feature to get the build options for a library, or it'll default to binary. If you have e.g. a library crate and a profiling crate alongside, you need to use optional features. E.g. you put the following in the library crate: @@ -40,7 +40,7 @@ There are two ways to distribute your module as python package: The old [setupto ## Cross Compiling -Cross compiling Pyo3 modules is relatively straightforward and requires a few pieces of software: +Cross compiling PyO3 modules is relatively straightforward and requires a few pieces of software: * A toolchain for your target. * The appropriate options in your Cargo `.config` for the platform you're targeting and the toolchain you are using. diff --git a/guide/src/conversions.md b/guide/src/conversions.md index 537f86b1..1673b3b0 100644 --- a/guide/src/conversions.md +++ b/guide/src/conversions.md @@ -1,6 +1,6 @@ # Type Conversions -`PyO3` provides some handy traits to convert between Python types and Rust types. +PyO3 provides some handy traits to convert between Python types and Rust types. ## `.extract()` @@ -104,7 +104,7 @@ fn main() { ## `IntoPy` -Many conversions in pyo3 can't use `std::convert::Into` because they need a gil token. That's why the `IntoPy` trait offers an `into_py` methods that works just like `into` except for taking a `Python<'_>` as argument. +Many conversions in PyO3 can't use `std::convert::Into` because they need a gil token. That's why the `IntoPy` trait offers an `into_py` methods that works just like `into` except for taking a `Python<'_>` as argument. Eventually, traits such as `IntoPyObject` will be replaces by this trait and a `FromPy` trait will be added that will implement `IntoPy`, just like with `From` and `Into`. diff --git a/guide/src/debugging.md b/guide/src/debugging.md index 58b9e0a0..580639f2 100644 --- a/guide/src/debugging.md +++ b/guide/src/debugging.md @@ -2,7 +2,7 @@ ## Macros -Pyo3's attributes, `#[pyclass]`, `#[pymodule]`, etc. are [procedural macros](https://doc.rust-lang.org/unstable-book/language-features/proc-macro.html), which means that rewrite the source of the annotated item. You can view the generated source with the following command, which also expands a few other things: +PyO3's attributes, `#[pyclass]`, `#[pymodule]`, etc. are [procedural macros](https://doc.rust-lang.org/unstable-book/language-features/proc-macro.html), which means that rewrite the source of the annotated item. You can view the generated source with the following command, which also expands a few other things: ```bash cargo rustc --profile=check -- -Z unstable-options --pretty=expanded > expanded.rs; rustfmt expanded.rs diff --git a/guide/src/exception.md b/guide/src/exception.md index de12e83a..b1ab5dca 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -122,7 +122,7 @@ The vast majority of operations in this library will return [`PyResult`](http This is an alias for the type `Result`. A [`PyErr`](https://docs.rs/pyo3/0.2.7/struct.PyErr.html) represents a Python exception. -Errors within the `Pyo3` library are also exposed as Python exceptions. +Errors within the PyO3 library are also exposed as Python exceptions. PyO3 library handles python exception in two stages. During first stage `PyErr` instance get created. At this stage python GIL is not required. During second stage, actual python diff --git a/guide/src/get_started.md b/guide/src/get_started.md index d8d71cfe..53ef35f3 100644 --- a/guide/src/get_started.md +++ b/guide/src/get_started.md @@ -10,7 +10,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste ## Usage -Pyo3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.30.0-nightly 2018-08-18. +PyO3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.30.0-nightly 2018-08-18. You can either write a native python module in rust or use python from a rust binary. @@ -24,7 +24,7 @@ sudo apt install python3-dev python-dev ## Using rust from python -Pyo3 can be used to generate a native python module. +PyO3 can be used to generate a native python module. **`Cargo.toml`:** diff --git a/guide/src/module.md b/guide/src/module.md index f18165d3..00c7e6de 100644 --- a/guide/src/module.md +++ b/guide/src/module.md @@ -12,7 +12,7 @@ use pyo3::prelude::*; #[pymodule] fn rust2py(py: Python, m: &PyModule) -> PyResult<()> { - // pyo3 aware function. All of our python interface could be declared in a separate module. + // PyO3 aware function. All of our python interface could be declared in a separate module. // Note that the `#[pyfn()]` annotation automatically converts the arguments from // Python objects to Rust values; and the Rust return value back into a Python object. #[pyfn(m, "sum_as_string")] diff --git a/guide/src/parallelism.md b/guide/src/parallelism.md index ab0097b1..2da55872 100644 --- a/guide/src/parallelism.md +++ b/guide/src/parallelism.md @@ -1,7 +1,7 @@ # Parallelism CPython has an infamous GIL(Global Interpreter Lock) prevents developers -getting true parallelism. With `pyo3` you can release GIL when executing +getting true parallelism. With PyO3 you can release GIL when executing Rust code to achieve true parallelism. The [`Python::allow_threads`](https://docs.rs/pyo3/0.2.7/struct.Python.html#method.allow_threads) @@ -47,7 +47,7 @@ fn word_count(py: Python, m: &PyModule) -> PyResult<()> { ## Benchmark -Let's benchmark the `word-count` example to verify that we did unlock true parallelism with `pyo3`. +Let's benchmark the `word-count` example to verify that we did unlock true parallelism with PyO3. We are using `pytest-benchmark` to benchmark three word count functions: 1. [Pure Python version](https://github.com/PyO3/pyo3/blob/master/examples/word-count/word_count/__init__.py#L9) diff --git a/guide/src/rust-cpython.md b/guide/src/rust-cpython.md index 8295dc96..25dbc176 100644 --- a/guide/src/rust-cpython.md +++ b/guide/src/rust-cpython.md @@ -1,12 +1,12 @@ -# Appendix: pyo3 and rust-cpython +# Appendix: PyO3 and rust-cpython -Pyo3 began as fork of [rust-cpython](https://github.com/dgrunwald/rust-cpython) when rust-cpython wasn't maintained. Over the time pyo3 has become fundamentally different from rust-cpython. +PyO3 began as fork of [rust-cpython](https://github.com/dgrunwald/rust-cpython) when rust-cpython wasn't maintained. Over the time pyo3 has become fundamentally different from rust-cpython. This chapter is based on the discussion in [PyO3/pyo3#55](https://github.com/PyO3/pyo3/issues/55). ## Macros -While rust-cpython has a macro based dsl for declaring modules and classes, pyo3 use proc macros and specialization. Pyo3 also doesn't change your struct and functions so you can still use them as normal rust functions. The disadvantage is that proc macros and specialization currently only work on nightly. +While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 use proc macros and specialization. PyO3 also doesn't change your struct and functions so you can still use them as normal rust functions. The disadvantage is that proc macros and specialization currently only work on nightly. **rust-cpython** @@ -53,7 +53,7 @@ impl MyClass { ## Ownership and lifetimes -All objects are owned by pyo3 library and all apis available with references, while in rust-cpython, you own python objects. +All objects are owned by PyO3 library and all apis available with references, while in rust-cpython, you own python objects. Here is example of PyList api: @@ -79,10 +79,10 @@ impl PyList { } ``` -Because pyo3 allows only references to python object, all reference have the Gil lifetime. So the python object is not required, and it is safe to have functions like `fn py<'p>(&'p self) -> Python<'p> {}`. +Because PyO3 allows only references to python object, all reference have the Gil lifetime. So the python object is not required, and it is safe to have functions like `fn py<'p>(&'p self) -> Python<'p> {}`. ## Error handling rust-cpython requires a `Python` parameter for `PyErr`, so error handling ergonomics is pretty bad. It is not possible to use `?` with rust errors. -`pyo3` on other hand does not require `Python` for `PyErr`, it is only required if you want to raise an exception in python with the `PyErr::restore()` method. Due to the `std::convert::From for PyErr` trait `?` is supported automatically. +PyO3 on other hand does not require `Python` for `PyErr`, it is only required if you want to raise an exception in python with the `PyErr::restore()` method. Due to the `std::convert::From for PyErr` trait `?` is supported automatically. From 548f3f91cf290cfc020254927a836bb12dea5aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Wed, 13 Mar 2019 17:36:16 +0100 Subject: [PATCH 10/17] Replace Pyo3 with PyO3 in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c9ac754..c6f439c3 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste ## Usage -Pyo3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.34.0-nightly 2019-02-06. +PyO3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.34.0-nightly 2019-02-06. You can either write a native python module in rust or use python from a rust binary. @@ -29,7 +29,7 @@ sudo apt install python3-dev python-dev ## Using rust from python -Pyo3 can be used to generate a native python module. +PyO3 can be used to generate a native python module. **`Cargo.toml`** From 7564f76051ffc181347db5f6f21e13dd973729d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=B6ln?= Date: Fri, 15 Mar 2019 15:54:14 +0100 Subject: [PATCH 11/17] fix #398 --- src/types/list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/list.rs b/src/types/list.rs index baaef9ea..655cb42f 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -191,7 +191,7 @@ where impl IntoPyObject for Vec where - T: IntoPyObject + ToPyObject, + T: IntoPyObject, { fn into_object(self, py: Python) -> PyObject { unsafe { From 6540a374afc82ffd2e6646ca8ad416990c95acbe Mon Sep 17 00:00:00 2001 From: konstin Date: Sat, 16 Mar 2019 11:31:55 +0100 Subject: [PATCH 12/17] Export pyo3_derive_backend::pyimpl::impl_methods --- pyo3-derive-backend/src/lib.rs | 2 +- tests/test_doc.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pyo3-derive-backend/src/lib.rs b/pyo3-derive-backend/src/lib.rs index cedf1dcc..fc652325 100644 --- a/pyo3-derive-backend/src/lib.rs +++ b/pyo3-derive-backend/src/lib.rs @@ -17,6 +17,6 @@ mod utils; pub use module::{add_fn_to_module, process_functions_in_module, py2_init, py3_init}; pub use pyclass::{build_py_class, PyClassArgs}; pub use pyfunction::PyFunctionAttr; -pub use pyimpl::build_py_methods; +pub use pyimpl::{build_py_methods, impl_methods}; pub use pyproto::build_py_proto; pub use utils::get_doc; diff --git a/tests/test_doc.rs b/tests/test_doc.rs index 49688b64..6309d466 100644 --- a/tests/test_doc.rs +++ b/tests/test_doc.rs @@ -1,7 +1,11 @@ -use docmatic; -use std::default::Default; -use std::path::{Path, PathBuf}; +#[cfg(feature = "test-doc")] +use { + docmatic, + std::default::Default, + std::path::{Path, PathBuf}, +}; +#[cfg(feature = "test-doc")] fn assert_file>(path: P) { let mut doc = docmatic::Assert::default(); if cfg!(windows) { From a56147fa522203f7bf6d9e43a1dbaba611914a90 Mon Sep 17 00:00:00 2001 From: konstin Date: Sat, 16 Mar 2019 11:40:56 +0100 Subject: [PATCH 13/17] Replace `::pyo3::` with only `pyo3::` in the proc macros This makes reexporting the macro output possible in the 2018 edition --- pyo3-derive-backend/src/defs.rs | 226 ++++++++++++++-------------- pyo3-derive-backend/src/module.rs | 36 ++--- pyo3-derive-backend/src/pyclass.rs | 66 ++++---- pyo3-derive-backend/src/pyimpl.rs | 6 +- pyo3-derive-backend/src/pymethod.rs | 220 +++++++++++++-------------- pyo3-derive-backend/src/pyproto.rs | 8 +- 6 files changed, 281 insertions(+), 281 deletions(-) diff --git a/pyo3-derive-backend/src/defs.rs b/pyo3-derive-backend/src/defs.rs index 48238bac..7efae828 100644 --- a/pyo3-derive-backend/src/defs.rs +++ b/pyo3-derive-backend/src/defs.rs @@ -19,76 +19,76 @@ pub const OBJECT: Proto = Proto { name: "__getattr__", arg: "Name", pyres: true, - proto: "::pyo3::class::basic::PyObjectGetAttrProtocol", + proto: "pyo3::class::basic::PyObjectGetAttrProtocol", }, MethodProto::Ternary { name: "__setattr__", arg1: "Name", arg2: "Value", pyres: true, - proto: "::pyo3::class::basic::PyObjectSetAttrProtocol", + proto: "pyo3::class::basic::PyObjectSetAttrProtocol", }, MethodProto::Binary { name: "__delattr__", arg: "Name", pyres: true, - proto: "::pyo3::class::basic::PyObjectDelAttrProtocol", + proto: "pyo3::class::basic::PyObjectDelAttrProtocol", }, MethodProto::Unary { name: "__str__", pyres: true, - proto: "::pyo3::class::basic::PyObjectStrProtocol", + proto: "pyo3::class::basic::PyObjectStrProtocol", }, MethodProto::Unary { name: "__repr__", pyres: true, - proto: "::pyo3::class::basic::PyObjectReprProtocol", + proto: "pyo3::class::basic::PyObjectReprProtocol", }, MethodProto::Binary { name: "__format__", arg: "Format", pyres: true, - proto: "::pyo3::class::basic::PyObjectFormatProtocol", + proto: "pyo3::class::basic::PyObjectFormatProtocol", }, MethodProto::Unary { name: "__hash__", pyres: false, - proto: "::pyo3::class::basic::PyObjectHashProtocol", + proto: "pyo3::class::basic::PyObjectHashProtocol", }, MethodProto::Unary { name: "__bytes__", pyres: true, - proto: "::pyo3::class::basic::PyObjectBytesProtocol", + proto: "pyo3::class::basic::PyObjectBytesProtocol", }, MethodProto::Unary { name: "__unicode__", pyres: true, - proto: "::pyo3::class::basic::PyObjectUnicodeProtocol", + proto: "pyo3::class::basic::PyObjectUnicodeProtocol", }, MethodProto::Unary { name: "__bool__", pyres: false, - proto: "::pyo3::class::basic::PyObjectBoolProtocol", + proto: "pyo3::class::basic::PyObjectBoolProtocol", }, MethodProto::Binary { name: "__richcmp__", arg: "Other", pyres: true, - proto: "::pyo3::class::basic::PyObjectRichcmpProtocol", + proto: "pyo3::class::basic::PyObjectRichcmpProtocol", }, ], py_methods: &[ PyMethod { name: "__format__", - proto: "::pyo3::class::basic::FormatProtocolImpl", + proto: "pyo3::class::basic::FormatProtocolImpl", }, PyMethod { name: "__bytes__", - proto: "::pyo3::class::basic::BytesProtocolImpl", + proto: "pyo3::class::basic::BytesProtocolImpl", }, PyMethod { name: "__unicode__", - proto: "::pyo3::class::basic::UnicodeProtocolImpl", + proto: "pyo3::class::basic::UnicodeProtocolImpl", }, ], }; @@ -99,39 +99,39 @@ pub const ASYNC: Proto = Proto { MethodProto::Unary { name: "__await__", pyres: true, - proto: "::pyo3::class::pyasync::PyAsyncAwaitProtocol", + proto: "pyo3::class::pyasync::PyAsyncAwaitProtocol", }, MethodProto::Unary { name: "__aiter__", pyres: true, - proto: "::pyo3::class::pyasync::PyAsyncAiterProtocol", + proto: "pyo3::class::pyasync::PyAsyncAiterProtocol", }, MethodProto::Unary { name: "__anext__", pyres: true, - proto: "::pyo3::class::pyasync::PyAsyncAnextProtocol", + proto: "pyo3::class::pyasync::PyAsyncAnextProtocol", }, MethodProto::Unary { name: "__aenter__", pyres: true, - proto: "::pyo3::class::pyasync::PyAsyncAenterProtocol", + proto: "pyo3::class::pyasync::PyAsyncAenterProtocol", }, MethodProto::Quaternary { name: "__aexit__", arg1: "ExcType", arg2: "ExcValue", arg3: "Traceback", - proto: "::pyo3::class::pyasync::PyAsyncAexitProtocol", + proto: "pyo3::class::pyasync::PyAsyncAexitProtocol", }, ], py_methods: &[ PyMethod { name: "__aenter__", - proto: "::pyo3::class::pyasync::PyAsyncAenterProtocolImpl", + proto: "pyo3::class::pyasync::PyAsyncAenterProtocolImpl", }, PyMethod { name: "__aexit__", - proto: "::pyo3::class::pyasync::PyAsyncAexitProtocolImpl", + proto: "pyo3::class::pyasync::PyAsyncAexitProtocolImpl", }, ], }; @@ -142,12 +142,12 @@ pub const BUFFER: Proto = Proto { MethodProto::Unary { name: "bf_getbuffer", pyres: false, - proto: "::pyo3::class::buffer::PyBufferGetBufferProtocol", + proto: "pyo3::class::buffer::PyBufferGetBufferProtocol", }, MethodProto::Unary { name: "bf_releasebuffer", pyres: false, - proto: "::pyo3::class::buffer::PyBufferReleaseBufferProtocol", + proto: "pyo3::class::buffer::PyBufferReleaseBufferProtocol", }, ], py_methods: &[], @@ -159,24 +159,24 @@ pub const CONTEXT: Proto = Proto { MethodProto::Unary { name: "__enter__", pyres: true, - proto: "::pyo3::class::context::PyContextEnterProtocol", + proto: "pyo3::class::context::PyContextEnterProtocol", }, MethodProto::Quaternary { name: "__exit__", arg1: "ExcType", arg2: "ExcValue", arg3: "Traceback", - proto: "::pyo3::class::context::PyContextExitProtocol", + proto: "pyo3::class::context::PyContextExitProtocol", }, ], py_methods: &[ PyMethod { name: "__enter__", - proto: "::pyo3::class::context::PyContextEnterProtocolImpl", + proto: "pyo3::class::context::PyContextEnterProtocolImpl", }, PyMethod { name: "__exit__", - proto: "::pyo3::class::context::PyContextExitProtocolImpl", + proto: "pyo3::class::context::PyContextExitProtocolImpl", }, ], }; @@ -186,11 +186,11 @@ pub const GC: Proto = Proto { methods: &[ MethodProto::Free { name: "__traverse__", - proto: "::pyo3::class::gc::PyGCTraverseProtocol", + proto: "pyo3::class::gc::PyGCTraverseProtocol", }, MethodProto::Free { name: "__clear__", - proto: "::pyo3::class::gc::PyGCClearProtocol", + proto: "pyo3::class::gc::PyGCClearProtocol", }, ], py_methods: &[], @@ -204,36 +204,36 @@ pub const DESCR: Proto = Proto { arg1: "Inst", arg2: "Owner", pyres: true, - proto: "::pyo3::class::descr::PyDescrGetProtocol", + proto: "pyo3::class::descr::PyDescrGetProtocol", }, MethodProto::Ternary { name: "__set__", arg1: "Inst", arg2: "Value", pyres: true, - proto: "::pyo3::class::descr::PyDescrSetProtocol", + proto: "pyo3::class::descr::PyDescrSetProtocol", }, MethodProto::Binary { name: "__det__", arg: "Inst", pyres: false, - proto: "::pyo3::class::descr::PyDescrDelProtocol", + proto: "pyo3::class::descr::PyDescrDelProtocol", }, MethodProto::Binary { name: "__set_name__", arg: "Inst", pyres: false, - proto: "::pyo3::class::descr::PyDescrSetNameProtocol", + proto: "pyo3::class::descr::PyDescrSetNameProtocol", }, ], py_methods: &[ PyMethod { name: "__del__", - proto: "::pyo3::class::context::PyDescrDelProtocolImpl", + proto: "pyo3::class::context::PyDescrDelProtocolImpl", }, PyMethod { name: "__set_name__", - proto: "::pyo3::class::context::PyDescrNameProtocolImpl", + proto: "pyo3::class::context::PyDescrNameProtocolImpl", }, ], }; @@ -245,12 +245,12 @@ pub const ITER: Proto = Proto { MethodProto::Unary { name: "__iter__", pyres: true, - proto: "::pyo3::class::iter::PyIterIterProtocol", + proto: "pyo3::class::iter::PyIterIterProtocol", }, MethodProto::Unary { name: "__next__", pyres: true, - proto: "::pyo3::class::iter::PyIterNextProtocol", + proto: "pyo3::class::iter::PyIterNextProtocol", }, ], }; @@ -261,56 +261,56 @@ pub const MAPPING: Proto = Proto { MethodProto::Unary { name: "__len__", pyres: false, - proto: "::pyo3::class::mapping::PyMappingLenProtocol", + proto: "pyo3::class::mapping::PyMappingLenProtocol", }, MethodProto::Binary { name: "__getitem__", arg: "Key", pyres: true, - proto: "::pyo3::class::mapping::PyMappingGetItemProtocol", + proto: "pyo3::class::mapping::PyMappingGetItemProtocol", }, MethodProto::Ternary { name: "__setitem__", arg1: "Key", arg2: "Value", pyres: false, - proto: "::pyo3::class::mapping::PyMappingSetItemProtocol", + proto: "pyo3::class::mapping::PyMappingSetItemProtocol", }, MethodProto::Binary { name: "__delitem__", arg: "Key", pyres: false, - proto: "::pyo3::class::mapping::PyMappingDelItemProtocol", + proto: "pyo3::class::mapping::PyMappingDelItemProtocol", }, MethodProto::Binary { name: "__contains__", arg: "Value", pyres: false, - proto: "::pyo3::class::mapping::PyMappingContainsProtocol", + proto: "pyo3::class::mapping::PyMappingContainsProtocol", }, MethodProto::Unary { name: "__reversed__", pyres: true, - proto: "::pyo3::class::mapping::PyMappingReversedProtocol", + proto: "pyo3::class::mapping::PyMappingReversedProtocol", }, MethodProto::Unary { name: "__iter__", pyres: true, - proto: "::pyo3::class::mapping::PyMappingIterProtocol", + proto: "pyo3::class::mapping::PyMappingIterProtocol", }, ], py_methods: &[ PyMethod { name: "__iter__", - proto: "::pyo3::class::mapping::PyMappingIterProtocolImpl", + proto: "pyo3::class::mapping::PyMappingIterProtocolImpl", }, PyMethod { name: "__contains__", - proto: "::pyo3::class::mapping::PyMappingContainsProtocolImpl", + proto: "pyo3::class::mapping::PyMappingContainsProtocolImpl", }, PyMethod { name: "__reversed__", - proto: "::pyo3::class::mapping::PyMappingReversedProtocolImpl", + proto: "pyo3::class::mapping::PyMappingReversedProtocolImpl", }, ], }; @@ -380,56 +380,56 @@ pub const NUM: Proto = Proto { arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberAddProtocol", + proto: "pyo3::class::number::PyNumberAddProtocol", }, MethodProto::BinaryS { name: "__sub__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberSubProtocol", + proto: "pyo3::class::number::PyNumberSubProtocol", }, MethodProto::BinaryS { name: "__mul__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberMulProtocol", + proto: "pyo3::class::number::PyNumberMulProtocol", }, MethodProto::BinaryS { name: "__matmul__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberMatmulProtocol", + proto: "pyo3::class::number::PyNumberMatmulProtocol", }, MethodProto::BinaryS { name: "__truediv__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberTruedivProtocol", + proto: "pyo3::class::number::PyNumberTruedivProtocol", }, MethodProto::BinaryS { name: "__floordiv__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberFloordivProtocol", + proto: "pyo3::class::number::PyNumberFloordivProtocol", }, MethodProto::BinaryS { name: "__mod__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberModProtocol", + proto: "pyo3::class::number::PyNumberModProtocol", }, MethodProto::BinaryS { name: "__divmod__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberDivmodProtocol", + proto: "pyo3::class::number::PyNumberDivmodProtocol", }, MethodProto::TernaryS { name: "__pow__", @@ -437,317 +437,317 @@ pub const NUM: Proto = Proto { arg2: "Right", arg3: "Modulo", pyres: true, - proto: "::pyo3::class::number::PyNumberPowProtocol", + proto: "pyo3::class::number::PyNumberPowProtocol", }, MethodProto::BinaryS { name: "__lshift__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberLShiftProtocol", + proto: "pyo3::class::number::PyNumberLShiftProtocol", }, MethodProto::BinaryS { name: "__rshift__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberRShiftProtocol", + proto: "pyo3::class::number::PyNumberRShiftProtocol", }, MethodProto::BinaryS { name: "__and__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberAndProtocol", + proto: "pyo3::class::number::PyNumberAndProtocol", }, MethodProto::BinaryS { name: "__xor__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberXorProtocol", + proto: "pyo3::class::number::PyNumberXorProtocol", }, MethodProto::BinaryS { name: "__or__", arg1: "Left", arg2: "Right", pyres: true, - proto: "::pyo3::class::number::PyNumberOrProtocol", + proto: "pyo3::class::number::PyNumberOrProtocol", }, MethodProto::Binary { name: "__radd__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRAddProtocol", + proto: "pyo3::class::number::PyNumberRAddProtocol", }, MethodProto::Binary { name: "__rsub__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRSubProtocol", + proto: "pyo3::class::number::PyNumberRSubProtocol", }, MethodProto::Binary { name: "__rmul__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRMulProtocol", + proto: "pyo3::class::number::PyNumberRMulProtocol", }, MethodProto::Binary { name: "__rmatmul__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRMatmulProtocol", + proto: "pyo3::class::number::PyNumberRMatmulProtocol", }, MethodProto::Binary { name: "__rtruediv__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRTruedivProtocol", + proto: "pyo3::class::number::PyNumberRTruedivProtocol", }, MethodProto::Binary { name: "__rfloordiv__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRFloordivProtocol", + proto: "pyo3::class::number::PyNumberRFloordivProtocol", }, MethodProto::Binary { name: "__rmod__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRModProtocol", + proto: "pyo3::class::number::PyNumberRModProtocol", }, MethodProto::Binary { name: "__rdivmod__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRDivmodProtocol", + proto: "pyo3::class::number::PyNumberRDivmodProtocol", }, MethodProto::Ternary { name: "__rpow__", arg1: "Other", arg2: "Modulo", pyres: true, - proto: "::pyo3::class::number::PyNumberRPowProtocol", + proto: "pyo3::class::number::PyNumberRPowProtocol", }, MethodProto::Binary { name: "__rlshift__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRLShiftProtocol", + proto: "pyo3::class::number::PyNumberRLShiftProtocol", }, MethodProto::Binary { name: "__rrshift__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRRShiftProtocol", + proto: "pyo3::class::number::PyNumberRRShiftProtocol", }, MethodProto::Binary { name: "__rand__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRAndProtocol", + proto: "pyo3::class::number::PyNumberRAndProtocol", }, MethodProto::Binary { name: "__rxor__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberRXorProtocol", + proto: "pyo3::class::number::PyNumberRXorProtocol", }, MethodProto::Binary { name: "__ror__", arg: "Other", pyres: true, - proto: "::pyo3::class::number::PyNumberROrProtocol", + proto: "pyo3::class::number::PyNumberROrProtocol", }, MethodProto::Binary { name: "__iadd__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIAddProtocol", + proto: "pyo3::class::number::PyNumberIAddProtocol", }, MethodProto::Binary { name: "__isub__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberISubProtocol", + proto: "pyo3::class::number::PyNumberISubProtocol", }, MethodProto::Binary { name: "__imul__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIMulProtocol", + proto: "pyo3::class::number::PyNumberIMulProtocol", }, MethodProto::Binary { name: "__imatmul__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIMatmulProtocol", + proto: "pyo3::class::number::PyNumberIMatmulProtocol", }, MethodProto::Binary { name: "__itruediv__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberITruedivProtocol", + proto: "pyo3::class::number::PyNumberITruedivProtocol", }, MethodProto::Binary { name: "__ifloordiv__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIFloordivProtocol", + proto: "pyo3::class::number::PyNumberIFloordivProtocol", }, MethodProto::Binary { name: "__imod__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIModProtocol", + proto: "pyo3::class::number::PyNumberIModProtocol", }, MethodProto::Ternary { name: "__ipow__", arg1: "Other", arg2: "Modulo", pyres: false, - proto: "::pyo3::class::number::PyNumberIPowProtocol", + proto: "pyo3::class::number::PyNumberIPowProtocol", }, MethodProto::Binary { name: "__ilshift__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberILShiftProtocol", + proto: "pyo3::class::number::PyNumberILShiftProtocol", }, MethodProto::Binary { name: "__irshift__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIRShiftProtocol", + proto: "pyo3::class::number::PyNumberIRShiftProtocol", }, MethodProto::Binary { name: "__iand__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIAndProtocol", + proto: "pyo3::class::number::PyNumberIAndProtocol", }, MethodProto::Binary { name: "__ixor__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIXorProtocol", + proto: "pyo3::class::number::PyNumberIXorProtocol", }, MethodProto::Binary { name: "__ior__", arg: "Other", pyres: false, - proto: "::pyo3::class::number::PyNumberIOrProtocol", + proto: "pyo3::class::number::PyNumberIOrProtocol", }, MethodProto::Unary { name: "__neg__", pyres: true, - proto: "::pyo3::class::number::PyNumberNegProtocol", + proto: "pyo3::class::number::PyNumberNegProtocol", }, MethodProto::Unary { name: "__pos__", pyres: true, - proto: "::pyo3::class::number::PyNumberPosProtocol", + proto: "pyo3::class::number::PyNumberPosProtocol", }, MethodProto::Unary { name: "__abs__", pyres: true, - proto: "::pyo3::class::number::PyNumberAbsProtocol", + proto: "pyo3::class::number::PyNumberAbsProtocol", }, MethodProto::Unary { name: "__invert__", pyres: true, - proto: "::pyo3::class::number::PyNumberInvertProtocol", + proto: "pyo3::class::number::PyNumberInvertProtocol", }, MethodProto::Unary { name: "__complex__", pyres: true, - proto: "::pyo3::class::number::PyNumberComplexProtocol", + proto: "pyo3::class::number::PyNumberComplexProtocol", }, MethodProto::Unary { name: "__int__", pyres: true, - proto: "::pyo3::class::number::PyNumberIntProtocol", + proto: "pyo3::class::number::PyNumberIntProtocol", }, MethodProto::Unary { name: "__float__", pyres: true, - proto: "::pyo3::class::number::PyNumberFloatProtocol", + proto: "pyo3::class::number::PyNumberFloatProtocol", }, MethodProto::Unary { name: "__round__", pyres: true, - proto: "::pyo3::class::number::PyNumberRoundProtocol", + proto: "pyo3::class::number::PyNumberRoundProtocol", }, MethodProto::Unary { name: "__index__", pyres: true, - proto: "::pyo3::class::number::PyNumberIndexProtocol", + proto: "pyo3::class::number::PyNumberIndexProtocol", }, ], py_methods: &[ PyMethod { name: "__radd__", - proto: "::pyo3::class::number::PyNumberRAddProtocolImpl", + proto: "pyo3::class::number::PyNumberRAddProtocolImpl", }, PyMethod { name: "__rsub__", - proto: "::pyo3::class::number::PyNumberRSubProtocolImpl", + proto: "pyo3::class::number::PyNumberRSubProtocolImpl", }, PyMethod { name: "__rmul__", - proto: "::pyo3::class::number::PyNumberRMulProtocolImpl", + proto: "pyo3::class::number::PyNumberRMulProtocolImpl", }, PyMethod { name: "__rmatmul__", - proto: "::pyo3::class::number::PyNumberRMatmulProtocolImpl", + proto: "pyo3::class::number::PyNumberRMatmulProtocolImpl", }, PyMethod { name: "__rtruediv__", - proto: "::pyo3::class::number::PyNumberRTruedivProtocolImpl", + proto: "pyo3::class::number::PyNumberRTruedivProtocolImpl", }, PyMethod { name: "__rfloordiv__", - proto: "::pyo3::class::number::PyNumberRFloordivProtocolImpl", + proto: "pyo3::class::number::PyNumberRFloordivProtocolImpl", }, PyMethod { name: "__rmod__", - proto: "::pyo3::class::number::PyNumberRModProtocolImpl", + proto: "pyo3::class::number::PyNumberRModProtocolImpl", }, PyMethod { name: "__rdivmod__", - proto: "::pyo3::class::number::PyNumberRDivmodProtocolImpl", + proto: "pyo3::class::number::PyNumberRDivmodProtocolImpl", }, PyMethod { name: "__rpow__", - proto: "::pyo3::class::number::PyNumberRPowProtocolImpl", + proto: "pyo3::class::number::PyNumberRPowProtocolImpl", }, PyMethod { name: "__rlshift__", - proto: "::pyo3::class::number::PyNumberRLShiftProtocolImpl", + proto: "pyo3::class::number::PyNumberRLShiftProtocolImpl", }, PyMethod { name: "__rrshift__", - proto: "::pyo3::class::number::PyNumberRRShiftProtocolImpl", + proto: "pyo3::class::number::PyNumberRRShiftProtocolImpl", }, PyMethod { name: "__rand__", - proto: "::pyo3::class::number::PyNumberRAndProtocolImpl", + proto: "pyo3::class::number::PyNumberRAndProtocolImpl", }, PyMethod { name: "__rxor__", - proto: "::pyo3::class::number::PyNumberRXorProtocolImpl", + proto: "pyo3::class::number::PyNumberRXorProtocolImpl", }, PyMethod { name: "__ror__", - proto: "::pyo3::class::number::PyNumberROrProtocolImpl", + proto: "pyo3::class::number::PyNumberROrProtocolImpl", }, PyMethod { name: "__complex__", - proto: "::pyo3::class::number::PyNumberComplexProtocolImpl", + proto: "pyo3::class::number::PyNumberComplexProtocolImpl", }, PyMethod { name: "__round__", - proto: "::pyo3::class::number::PyNumberRoundProtocolImpl", + proto: "pyo3::class::number::PyNumberRoundProtocolImpl", }, ], }; diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs index cd64de32..b583aaaf 100644 --- a/pyo3-derive-backend/src/module.rs +++ b/pyo3-derive-backend/src/module.rs @@ -21,8 +21,8 @@ pub fn py3_init(fnname: &Ident, name: &Ident, doc: syn::Lit) -> TokenStream { #[allow(non_snake_case)] /// This autogenerated function is called by the python interpreter when importing /// the module. - pub unsafe extern "C" fn #cb_name() -> *mut ::pyo3::ffi::PyObject { - ::pyo3::derive_utils::make_module(concat!(stringify!(#name), "\0"), #doc, #fnname) + pub unsafe extern "C" fn #cb_name() -> *mut pyo3::ffi::PyObject { + pyo3::derive_utils::make_module(concat!(stringify!(#name), "\0"), #doc, #fnname) } } } @@ -34,7 +34,7 @@ pub fn py2_init(fnname: &Ident, name: &Ident, doc: syn::Lit) -> TokenStream { #[no_mangle] #[allow(non_snake_case)] pub unsafe extern "C" fn #cb_name() { - ::pyo3::derive_utils::make_module(concat!(stringify!(#name), "\0"), #doc, #fnname) + pyo3::derive_utils::make_module(concat!(stringify!(#name), "\0"), #doc, #fnname) } } } @@ -190,20 +190,20 @@ pub fn add_fn_to_module( let doc = utils::get_doc(&func.attrs, true); let tokens = quote! { - fn #function_wrapper_ident(py: ::pyo3::Python) -> ::pyo3::PyObject { + fn #function_wrapper_ident(py: pyo3::Python) -> pyo3::PyObject { #wrapper - let _def = ::pyo3::class::PyMethodDef { + let _def = pyo3::class::PyMethodDef { ml_name: stringify!(#python_name), - ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS, + ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS, ml_doc: #doc, }; let function = unsafe { - ::pyo3::PyObject::from_owned_ptr_or_panic( + pyo3::PyObject::from_owned_ptr_or_panic( py, - ::pyo3::ffi::PyCFunction_New( + pyo3::ffi::PyCFunction_New( Box::into_raw(Box::new(_def.as_method_def())), ::std::ptr::null_mut() ) @@ -228,21 +228,21 @@ fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>) -> TokenStream { quote! { unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _slf: *mut pyo3::ffi::PyObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!(stringify!(#name), "()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); + let _args = _py.from_borrowed_ptr::(_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_convert( + pyo3::callback::PyObjectCallbackConverter, _py, _result) } } } diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index f11d44bf..731da712 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -37,7 +37,7 @@ impl Default for PyClassArgs { // We need the 0 as value for the constant we're later building using quote for when there // are no other flags flags: vec![parse_quote! {0}], - base: parse_quote! {::pyo3::types::PyObjectRef}, + base: parse_quote! {pyo3::types::PyObjectRef}, } } } @@ -110,16 +110,16 @@ impl PyClassArgs { let flag = exp.path.segments.first().unwrap().value().ident.to_string(); let path = match flag.as_str() { "gc" => { - parse_quote! {::pyo3::type_object::PY_TYPE_FLAG_GC} + parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC} } "weakref" => { - parse_quote! {::pyo3::type_object::PY_TYPE_FLAG_WEAKREF} + parse_quote! {pyo3::type_object::PY_TYPE_FLAG_WEAKREF} } "subclass" => { - parse_quote! {::pyo3::type_object::PY_TYPE_FLAG_BASETYPE} + parse_quote! {pyo3::type_object::PY_TYPE_FLAG_BASETYPE} } "dict" => { - parse_quote! {::pyo3::type_object::PY_TYPE_FLAG_DICT} + parse_quote! {pyo3::type_object::PY_TYPE_FLAG_DICT} } _ => { return Err(syn::Error::new_spanned( @@ -204,26 +204,26 @@ fn impl_inventory(cls: &syn::Ident) -> TokenStream { quote! { #[doc(hidden)] pub struct #inventory_cls { - methods: &'static [::pyo3::class::PyMethodDefType], + methods: &'static [pyo3::class::PyMethodDefType], } - impl ::pyo3::class::methods::PyMethodsInventory for #inventory_cls { - fn new(methods: &'static [::pyo3::class::PyMethodDefType]) -> Self { + impl pyo3::class::methods::PyMethodsInventory for #inventory_cls { + fn new(methods: &'static [pyo3::class::PyMethodDefType]) -> Self { Self { methods } } - fn get_methods(&self) -> &'static [::pyo3::class::PyMethodDefType] { + fn get_methods(&self) -> &'static [pyo3::class::PyMethodDefType] { self.methods } } - impl ::pyo3::class::methods::PyMethodsInventoryDispatch for #cls { + impl pyo3::class::methods::PyMethodsInventoryDispatch for #cls { type InventoryType = #inventory_cls; } - ::pyo3::inventory::collect!(#inventory_cls); + pyo3::inventory::collect!(#inventory_cls); } } @@ -241,16 +241,16 @@ fn impl_class( let extra = { if let Some(freelist) = &attr.freelist { quote! { - impl ::pyo3::freelist::PyObjectWithFreeList for #cls { + impl pyo3::freelist::PyObjectWithFreeList for #cls { #[inline] - fn get_free_list() -> &'static mut ::pyo3::freelist::FreeList<*mut ::pyo3::ffi::PyObject> { - static mut FREELIST: *mut ::pyo3::freelist::FreeList<*mut ::pyo3::ffi::PyObject> = 0 as *mut _; + fn get_free_list() -> &'static mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> { + static mut FREELIST: *mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> = 0 as *mut _; unsafe { if FREELIST.is_null() { FREELIST = Box::into_raw(Box::new( - ::pyo3::freelist::FreeList::with_capacity(#freelist))); + pyo3::freelist::FreeList::with_capacity(#freelist))); - <#cls as ::pyo3::type_object::PyTypeObject>::init_type(); + <#cls as pyo3::type_object::PyTypeObject>::init_type(); } &mut *FREELIST } @@ -259,7 +259,7 @@ fn impl_class( } } else { quote! { - impl ::pyo3::type_object::PyObjectAlloc for #cls {} + impl pyo3::type_object::PyObjectAlloc for #cls {} } } }; @@ -280,20 +280,20 @@ fn impl_class( let mut has_dict = false; for f in attr.flags.iter() { if let syn::Expr::Path(ref epath) = f { - if epath.path == parse_quote! {::pyo3::type_object::PY_TYPE_FLAG_WEAKREF} { + if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_WEAKREF} { has_weakref = true; - } else if epath.path == parse_quote! {::pyo3::type_object::PY_TYPE_FLAG_DICT} { + } else if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_DICT} { has_dict = true; } } } let weakref = if has_weakref { - quote! {std::mem::size_of::<*const ::pyo3::ffi::PyObject>()} + quote! {std::mem::size_of::<*const pyo3::ffi::PyObject>()} } else { quote! {0} }; let dict = if has_dict { - quote! {std::mem::size_of::<*const ::pyo3::ffi::PyObject>()} + quote! {std::mem::size_of::<*const pyo3::ffi::PyObject>()} } else { quote! {0} }; @@ -304,7 +304,7 @@ fn impl_class( let flags = &attr.flags; quote! { - impl ::pyo3::type_object::PyTypeInfo for #cls { + impl pyo3::type_object::PyTypeInfo for #cls { type Type = #cls; type BaseType = #base; @@ -319,22 +319,22 @@ fn impl_class( const OFFSET: isize = { // round base_size up to next multiple of align ( - (<#base as ::pyo3::type_object::PyTypeInfo>::SIZE + + (<#base as pyo3::type_object::PyTypeInfo>::SIZE + ::std::mem::align_of::<#cls>() - 1) / ::std::mem::align_of::<#cls>() * ::std::mem::align_of::<#cls>() ) as isize }; #[inline] - unsafe fn type_object() -> &'static mut ::pyo3::ffi::PyTypeObject { - static mut TYPE_OBJECT: ::pyo3::ffi::PyTypeObject = ::pyo3::ffi::PyTypeObject_INIT; + unsafe fn type_object() -> &'static mut pyo3::ffi::PyTypeObject { + static mut TYPE_OBJECT: pyo3::ffi::PyTypeObject = pyo3::ffi::PyTypeObject_INIT; &mut TYPE_OBJECT } } - impl ::pyo3::IntoPyObject for #cls { - fn into_object(self, py: ::pyo3::Python) -> ::pyo3::PyObject { - ::pyo3::Py::new(py, self).unwrap().into_object(py) + impl pyo3::IntoPyObject for #cls { + fn into_object(self, py: pyo3::Python) -> pyo3::PyObject { + pyo3::Py::new(py, self).unwrap().into_object(py) } } @@ -356,7 +356,7 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec)> FnType::Getter(_) => { quote! { impl #cls { - fn #name(&self) -> ::pyo3::PyResult<#field_ty> { + fn #name(&self) -> pyo3::PyResult<#field_ty> { Ok(self.#name.clone()) } } @@ -367,7 +367,7 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec)> syn::Ident::new(&format!("set_{}", name), Span::call_site()); quote! { impl #cls { - fn #setter_name(&mut self, value: #field_ty) -> ::pyo3::PyResult<()> { + fn #setter_name(&mut self, value: #field_ty) -> pyo3::PyResult<()> { self.#name = value; Ok(()) } @@ -430,10 +430,10 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec)> quote! { #(#methods)* - ::pyo3::inventory::submit! { + pyo3::inventory::submit! { #![crate = pyo3] { - type ClsInventory = <#cls as ::pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType; - ::new(&[#(#py_methods),*]) + type ClsInventory = <#cls as pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType; + ::new(&[#(#py_methods),*]) } } } diff --git a/pyo3-derive-backend/src/pyimpl.rs b/pyo3-derive-backend/src/pyimpl.rs index 2ca3bf91..431c0744 100644 --- a/pyo3-derive-backend/src/pyimpl.rs +++ b/pyo3-derive-backend/src/pyimpl.rs @@ -36,10 +36,10 @@ pub fn impl_methods(ty: &syn::Type, impls: &mut Vec) -> TokenStre } quote! { - ::pyo3::inventory::submit! { + pyo3::inventory::submit! { #![crate = pyo3] { - type TyInventory = <#ty as ::pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType; - ::new(&[#(#methods),*]) + type TyInventory = <#ty as pyo3::class::methods::PyMethodsInventoryDispatch>::InventoryType; + ::new(&[#(#methods),*]) } } } diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 76bd4eec..010e0c0e 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -52,21 +52,21 @@ pub fn impl_wrap( if spec.args.is_empty() && noargs { quote! { unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject - ) -> *mut ::pyo3::ffi::PyObject + _slf: *mut pyo3::ffi::PyObject + ) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!( stringify!(#cls), ".", stringify!(#name), "()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); let _result = { - ::pyo3::derive_utils::IntoPyResult::into_py_result(#body) + pyo3::derive_utils::IntoPyResult::into_py_result(#body) }; - ::pyo3::callback::cb_convert( - ::pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_convert( + pyo3::callback::PyObjectCallbackConverter, _py, _result) } } } else { @@ -74,22 +74,22 @@ pub fn impl_wrap( quote! { unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _slf: *mut pyo3::ffi::PyObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!( stringify!(#cls), ".", stringify!(#name), "()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _args = _py.from_borrowed_ptr::(_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_convert( + pyo3::callback::PyObjectCallbackConverter, _py, _result) } } } @@ -103,21 +103,21 @@ pub fn impl_proto_wrap(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _slf: *mut pyo3::ffi::PyObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _args = _py.from_borrowed_ptr::(_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_convert( + pyo3::callback::PyObjectCallbackConverter, _py, _result) } } } @@ -132,24 +132,24 @@ pub fn impl_wrap_new(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> T quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - _cls: *mut ::pyo3::ffi::PyTypeObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _cls: *mut pyo3::ffi::PyTypeObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { - use ::pyo3::type_object::PyTypeInfo; + use pyo3::type_object::PyTypeInfo; const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); - match ::pyo3::type_object::PyRawObject::new(_py, #cls::type_object(), _cls) { + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); + match pyo3::type_object::PyRawObject::new(_py, #cls::type_object(), _cls) { Ok(_obj) => { - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _args = _py.from_borrowed_ptr::(_args); + let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); #body match _result { - Ok(_) => ::pyo3::IntoPyPointer::into_ptr(_obj), + Ok(_) => pyo3::IntoPyPointer::into_ptr(_obj), Err(e) => { e.restore(_py); ::std::ptr::null_mut() @@ -180,16 +180,16 @@ fn impl_wrap_init(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> Toke quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> libc::c_int + _slf: *mut pyo3::ffi::PyObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> libc::c_int { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _args = _py.from_borrowed_ptr::(_args); + let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); #body @@ -214,21 +214,21 @@ pub fn impl_wrap_class(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - _cls: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _cls: *mut pyo3::ffi::PyObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); - let _cls = ::pyo3::types::PyType::from_type_ptr(_py, _cls as *mut ::pyo3::ffi::PyTypeObject); - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); + let _cls = pyo3::types::PyType::from_type_ptr(_py, _cls as *mut pyo3::ffi::PyTypeObject); + let _args = _py.from_borrowed_ptr::(_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_convert( + pyo3::callback::PyObjectCallbackConverter, _py, _result) } } } @@ -243,20 +243,20 @@ pub fn impl_wrap_static(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) - quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _args: *mut ::pyo3::ffi::PyObject, - _kwargs: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject + _slf: *mut pyo3::ffi::PyObject, + _args: *mut pyo3::ffi::PyObject, + _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); - let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args); - let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); + let _args = _py.from_borrowed_ptr::(_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_convert( + pyo3::callback::PyObjectCallbackConverter, _py, _result) } } } @@ -265,17 +265,17 @@ pub fn impl_wrap_static(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) - pub(crate) fn impl_wrap_getter(cls: &syn::Type, name: &syn::Ident) -> TokenStream { quote! { unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut ::pyo3::ffi::PyObject + _slf: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> *mut pyo3::ffi::PyObject { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); match _slf.#name() { Ok(val) => { - ::pyo3::IntoPyPointer::into_ptr(val.into_object(_py)) + pyo3::IntoPyPointer::into_ptr(val.into_object(_py)) } Err(e) => { e.restore(_py); @@ -304,16 +304,16 @@ pub(crate) fn impl_wrap_setter( quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - _slf: *mut ::pyo3::ffi::PyObject, - _value: *mut ::pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> libc::c_int + _slf: *mut pyo3::ffi::PyObject, + _value: *mut pyo3::ffi::PyObject, _: *mut ::std::os::raw::c_void) -> libc::c_int { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()"); - let _pool = ::pyo3::GILPool::new(); - let _py = ::pyo3::Python::assume_gil_acquired(); + let _pool = pyo3::GILPool::new(); + let _py = pyo3::Python::assume_gil_acquired(); let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); let _value = _py.from_borrowed_ptr(_value); - let _result = match <#val_ty as ::pyo3::FromPyObject>::extract(_value) { + let _result = match <#val_ty as pyo3::FromPyObject>::extract(_value) { Ok(_val) => _slf.#name(_val), Err(e) => Err(e) }; @@ -355,7 +355,7 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream { if spec.args.is_empty() { return quote! { let _result = { - ::pyo3::derive_utils::IntoPyResult::into_py_result(#body) + pyo3::derive_utils::IntoPyResult::into_py_result(#body) }; }; } @@ -371,7 +371,7 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream { let opt = bool_to_ident(arg.optional.is_some() || spec.default_value(&arg.name).is_some()); params.push(quote! { - ::pyo3::derive_utils::ParamDescription { + pyo3::derive_utils::ParamDescription { name: stringify!(#name), is_optional: #opt, kw_only: #kwonly @@ -394,8 +394,8 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream { // create array of arguments, and then parse quote! { - use ::pyo3::ObjectProtocol; - const PARAMS: &'static [::pyo3::derive_utils::ParamDescription] = &[ + use pyo3::ObjectProtocol; + const PARAMS: &'static [pyo3::derive_utils::ParamDescription] = &[ #(#params),* ]; @@ -403,7 +403,7 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream { // Workaround to use the question mark operator without rewriting everything let _result = (|| { - ::pyo3::derive_utils::parse_fn_args( + pyo3::derive_utils::parse_fn_args( Some(_LOCATION), PARAMS, &_args, @@ -415,7 +415,7 @@ pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream { #(#param_conversion)* - ::pyo3::derive_utils::IntoPyResult::into_py_result(#body) + pyo3::derive_utils::IntoPyResult::into_py_result(#body) })(); } } @@ -443,7 +443,7 @@ fn impl_arg_param( if spec.is_args(&name) { quote! { - let #arg_name = <#ty as ::pyo3::FromPyObject>::extract(_args.as_ref())?; + let #arg_name = <#ty as pyo3::FromPyObject>::extract(_args.as_ref())?; } } else if spec.is_kwargs(&name) { quote! { @@ -496,26 +496,26 @@ pub fn impl_py_method_def( ) -> TokenStream { if spec.args.is_empty() { quote! { - ::pyo3::class::PyMethodDefType::Method({ + pyo3::class::PyMethodDefType::Method({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyNoArgsFunction(__wrap), - ml_flags: ::pyo3::ffi::METH_NOARGS, + ml_meth: pyo3::class::PyMethodType::PyNoArgsFunction(__wrap), + ml_flags: pyo3::ffi::METH_NOARGS, ml_doc: #doc, } }) } } else { quote! { - ::pyo3::class::PyMethodDefType::Method({ + pyo3::class::PyMethodDefType::Method({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS, + ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS, ml_doc: #doc, } }) @@ -529,13 +529,13 @@ pub fn impl_py_method_def_new( wrapper: &TokenStream, ) -> TokenStream { quote! { - ::pyo3::class::PyMethodDefType::New({ + pyo3::class::PyMethodDefType::New({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyNewFunc(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS, + ml_meth: pyo3::class::PyMethodType::PyNewFunc(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS, ml_doc: #doc, } }) @@ -548,13 +548,13 @@ pub fn impl_py_method_def_init( wrapper: &TokenStream, ) -> TokenStream { quote! { - ::pyo3::class::PyMethodDefType::Init({ + pyo3::class::PyMethodDefType::Init({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyInitFunc(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS, + ml_meth: pyo3::class::PyMethodType::PyInitFunc(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS, ml_doc: #doc, } }) @@ -567,14 +567,14 @@ pub fn impl_py_method_def_class( wrapper: &TokenStream, ) -> TokenStream { quote! { - ::pyo3::class::PyMethodDefType::Class({ + pyo3::class::PyMethodDefType::Class({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS | - ::pyo3::ffi::METH_CLASS, + ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | + pyo3::ffi::METH_CLASS, ml_doc: #doc, } }) @@ -587,13 +587,13 @@ pub fn impl_py_method_def_static( wrapper: &TokenStream, ) -> TokenStream { quote! { - ::pyo3::class::PyMethodDefType::Static({ + pyo3::class::PyMethodDefType::Static({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS | ::pyo3::ffi::METH_STATIC, + ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | pyo3::ffi::METH_STATIC, ml_doc: #doc, } }) @@ -606,13 +606,13 @@ pub fn impl_py_method_def_call( wrapper: &TokenStream, ) -> TokenStream { quote! { - ::pyo3::class::PyMethodDefType::Call({ + pyo3::class::PyMethodDefType::Call({ #wrapper - ::pyo3::class::PyMethodDef { + pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS, + ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS, ml_doc: #doc, } }) @@ -637,10 +637,10 @@ pub(crate) fn impl_py_setter_def( }; quote! { - ::pyo3::class::PyMethodDefType::Setter({ + pyo3::class::PyMethodDefType::Setter({ #wrapper - ::pyo3::class::PySetterDef { + pyo3::class::PySetterDef { name: #n, meth: __wrap, doc: #doc, @@ -667,10 +667,10 @@ pub(crate) fn impl_py_getter_def( }; quote! { - ::pyo3::class::PyMethodDefType::Getter({ + pyo3::class::PyMethodDefType::Getter({ #wrapper - ::pyo3::class::PyGetterDef { + pyo3::class::PyGetterDef { name: #n, meth: __wrap, doc: #doc, diff --git a/pyo3-derive-backend/src/pyproto.rs b/pyo3-derive-backend/src/pyproto.rs index a551341f..b74bc26c 100644 --- a/pyo3-derive-backend/src/pyproto.rs +++ b/pyo3-derive-backend/src/pyproto.rs @@ -84,13 +84,13 @@ fn impl_proto_impl( impl #proto for #ty { #[inline] - fn #name() -> Option<::pyo3::class::methods::PyMethodDef> { + fn #name() -> Option { #meth - Some(::pyo3::class::PyMethodDef { + Some(pyo3::class::PyMethodDef { ml_name: stringify!(#name), - ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), - ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS, + ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap), + ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS, ml_doc: "" }) } From f79f84a31b9da6d7d46a0fac6799bdc052828b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Sun, 17 Mar 2019 11:08:05 +0100 Subject: [PATCH 14/17] Complete __dict__ support Add `__dict__` slot to tp_getset of type_object. See https://bugs.python.org/issue16272 for why this is necessary. --- src/ffi3/object.rs | 1 + src/type_object.rs | 16 +++++++++++++++- tests/test_dunder.rs | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/ffi3/object.rs b/src/ffi3/object.rs index ee7b790b..0ccbffff 100644 --- a/src/ffi3/object.rs +++ b/src/ffi3/object.rs @@ -702,6 +702,7 @@ extern "C" { arg2: *mut PyObject, arg3: *mut PyObject, ) -> c_int; + pub fn PyObject_GenericGetDict(arg1: *mut PyObject, arg2: *mut c_void) -> *mut PyObject; pub fn PyObject_GenericSetDict( arg1: *mut PyObject, arg2: *mut PyObject, diff --git a/src/type_object.rs b/src/type_object.rs index 9a30b804..00c28f84 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -14,7 +14,9 @@ use crate::{class, ffi, gil}; use class::methods::PyMethodsProtocol; use std::collections::HashMap; use std::ffi::CString; +use std::os::raw::c_char; use std::os::raw::c_void; +use std::ptr; use std::ptr::NonNull; /// Python type information. @@ -321,7 +323,8 @@ where } // __dict__ support - if T::FLAGS & PY_TYPE_FLAG_DICT != 0 { + let has_dict = T::FLAGS & PY_TYPE_FLAG_DICT != 0; + if has_dict { offset -= std::mem::size_of::<*const ffi::PyObject>(); type_object.tp_dictoffset = offset as isize; } @@ -392,6 +395,17 @@ where // properties let mut props = py_class_properties::(); + + if cfg!(Py_3) && has_dict { + let dict_slot = ffi::PyGetSetDef { + name: "__dict__\0".as_ptr() as *mut c_char, + get: Some(ffi::PyObject_GenericGetDict), + set: Some(ffi::PyObject_GenericSetDict), + doc: ptr::null_mut(), + closure: ptr::null_mut(), + }; + props.push(dict_slot); + } if !props.is_empty() { props.push(ffi::PyGetSetDef_INIT); type_object.tp_getset = Box::into_raw(props.into_boxed_slice()) as *mut _; diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs index 66f97ad7..bb2b99f3 100644 --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -453,6 +453,22 @@ fn dunder_dict_support() { ); } +#[cfg(Py_3)] +#[test] +fn access_dunder_dict() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = PyRef::new(py, DunderDictSupport {}).unwrap(); + py_run!( + py, + inst, + r#" + inst.a = 1 + assert inst.__dict__ == {'a': 1} + "# + ); +} + #[pyclass(weakref, dict)] struct WeakRefDunderDictSupport {} From dc581fa746e2ee703b2759bf7b584b64fcfdb073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Sun, 17 Mar 2019 20:39:38 +0100 Subject: [PATCH 15/17] Update guide --- guide/src/class.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index c4b1323d..89ff14a3 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -104,8 +104,7 @@ python object that can be collected, the `PyGCProtocol` trait has to be implemen * `weakref` - adds support for python weak references * `extends=BaseType` - use a custom base class. The base BaseType must implement `PyTypeInfo`. * `subclass` - Allows Python classes to inherit from this class -* `dict` - adds `__dict__` support, the instances of this type have a dictionary containing instance variables. (Incomplete, see [#123](https://github.com/PyO3/pyo3/issues/123)) - +* `dict` - adds `__dict__` support, the instances of this type have a dictionary containing instance variables. ## Constructor From dd8e6211d5475a37db65b4c9994bd01dc7af3820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Sun, 17 Mar 2019 20:39:51 +0100 Subject: [PATCH 16/17] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9268f240..0e4aa8ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed * A soudness hole where every instances of a `#[pyclass]` struct was considered to be part of a python object, even though you can create instances that are not part of the python heap. This was fixed through `PyRef` and `PyRefMut`. + * Fix kwargs support in [#328]. + * Add full support for `__dict__` in [#403]. ## [0.5.3] - 2019-01-04 From 5a3466d1d7f137459cc2e3dfe51ae234877561a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Niederb=C3=BChl?= Date: Sun, 17 Mar 2019 20:44:44 +0100 Subject: [PATCH 17/17] Fix compilation for Python 2 --- src/ffi2/descrobject.rs | 2 ++ src/ffi3/descrobject.rs | 12 +++++++++++- src/type_object.rs | 11 +---------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/ffi2/descrobject.rs b/src/ffi2/descrobject.rs index 06ace20f..81387624 100644 --- a/src/ffi2/descrobject.rs +++ b/src/ffi2/descrobject.rs @@ -27,6 +27,8 @@ pub const PyGetSetDef_INIT: PyGetSetDef = PyGetSetDef { closure: ptr::null_mut(), }; +pub const PyGetSetDef_DICT: PyGetSetDef = PyGetSetDef_INIT; + impl Clone for PyGetSetDef { #[inline] fn clone(&self) -> PyGetSetDef { diff --git a/src/ffi3/descrobject.rs b/src/ffi3/descrobject.rs index b098660c..6ec5eebe 100644 --- a/src/ffi3/descrobject.rs +++ b/src/ffi3/descrobject.rs @@ -1,5 +1,7 @@ use crate::ffi3::methodobject::PyMethodDef; -use crate::ffi3::object::{PyObject, PyTypeObject}; +use crate::ffi3::object::{ + PyObject, PyObject_GenericGetDict, PyObject_GenericSetDict, PyTypeObject, +}; use crate::ffi3::structmember::PyMemberDef; use std::os::raw::{c_char, c_int, c_void}; use std::ptr; @@ -27,6 +29,14 @@ pub const PyGetSetDef_INIT: PyGetSetDef = PyGetSetDef { closure: ptr::null_mut(), }; +pub const PyGetSetDef_DICT: PyGetSetDef = PyGetSetDef { + name: "__dict__\0".as_ptr() as *mut c_char, + get: Some(PyObject_GenericGetDict), + set: Some(PyObject_GenericSetDict), + doc: ptr::null_mut(), + closure: ptr::null_mut(), +}; + #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { pub static mut PyClassMethodDescr_Type: PyTypeObject; diff --git a/src/type_object.rs b/src/type_object.rs index 00c28f84..010ec97a 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -14,9 +14,7 @@ use crate::{class, ffi, gil}; use class::methods::PyMethodsProtocol; use std::collections::HashMap; use std::ffi::CString; -use std::os::raw::c_char; use std::os::raw::c_void; -use std::ptr; use std::ptr::NonNull; /// Python type information. @@ -397,14 +395,7 @@ where let mut props = py_class_properties::(); if cfg!(Py_3) && has_dict { - let dict_slot = ffi::PyGetSetDef { - name: "__dict__\0".as_ptr() as *mut c_char, - get: Some(ffi::PyObject_GenericGetDict), - set: Some(ffi::PyObject_GenericSetDict), - doc: ptr::null_mut(), - closure: ptr::null_mut(), - }; - props.push(dict_slot); + props.push(ffi::PyGetSetDef_DICT); } if !props.is_empty() { props.push(ffi::PyGetSetDef_INIT);