Merge branch 'main' into docsrs
This commit is contained in:
commit
5f51520559
|
@ -61,9 +61,9 @@ jobs:
|
|||
# There is no 64-bit pypy on windows for pypy-3.6
|
||||
- python-version: pypy-3.6
|
||||
platform: { os: "windows-latest", python-architecture: "x64" }
|
||||
# pypy on windows for pypy-3.7 coming in next PyPy release
|
||||
# PyPy 3.7 on Windows doesn't release 32-bit builds any more
|
||||
- python-version: pypy-3.7
|
||||
platform: { os: "windows-latest", python-architecture: "x64" }
|
||||
platform: { os: "windows-latest", python-architecture: "x86" }
|
||||
include:
|
||||
# Test minimal supported Rust version
|
||||
- rust: 1.41.1
|
||||
|
|
|
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Support PyPy 3.7. [#1538](https://github.com/PyO3/pyo3/pull/1538)
|
||||
|
||||
### Added
|
||||
- Add conversions for `[T; N]` for all `N` on Rust 1.51 and up. [#1128](https://github.com/PyO3/pyo3/pull/1128)
|
||||
- Add conversions between `OsStr`/`OsString`/`Path`/`PathBuf` and Python strings. [#1379](https://github.com/PyO3/pyo3/pull/1379)
|
||||
- Add `#[pyo3(from_py_with = "...")]` attribute for function arguments and struct fields to override the default from-Python conversion. [#1411](https://github.com/PyO3/pyo3/pull/1411)
|
||||
- Add FFI definition `PyCFunction_CheckExact` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
|
||||
|
@ -20,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Add FFI definition `_Py_InitializeMain`. [#1473](https://github.com/PyO3/pyo3/pull/1473)
|
||||
- Add FFI definitions from `cpython/import.h`.[#1475](https://github.com/PyO3/pyo3/pull/1475)
|
||||
- Add tuple and unit struct support for `#[pyclass]` macro. [#1504](https://github.com/PyO3/pyo3/pull/1504)
|
||||
- Add FFI definition `PyDateTime_TimeZone_UTC`. [#1572](https://github.com/PyO3/pyo3/pull/1572)
|
||||
|
||||
### Changed
|
||||
- Change `PyTimeAcces::get_fold()` to return a `bool` instead of a `u8`. [#1397](https://github.com/PyO3/pyo3/pull/1397)
|
||||
|
|
17
build.rs
17
build.rs
|
@ -746,6 +746,17 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn rustc_minor_version() -> Option<u32> {
|
||||
let rustc = env::var_os("RUSTC")?;
|
||||
let output = Command::new(rustc).arg("--version").output().ok()?;
|
||||
let version = core::str::from_utf8(&output.stdout).ok()?;
|
||||
let mut pieces = version.split('.');
|
||||
if pieces.next() != Some("rustc 1") {
|
||||
return None;
|
||||
}
|
||||
pieces.next()?.parse().ok()
|
||||
}
|
||||
|
||||
fn emit_cargo_configuration(interpreter_config: &InterpreterConfig) -> Result<()> {
|
||||
let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let is_extension_module = cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some();
|
||||
|
@ -850,6 +861,12 @@ fn emit_cargo_configuration(interpreter_config: &InterpreterConfig) -> Result<()
|
|||
println!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag)
|
||||
}
|
||||
|
||||
// Enable use of const generics on Rust 1.51 and greater
|
||||
|
||||
if rustc_minor_version().unwrap_or(0) >= 51 {
|
||||
println!("cargo:rustc-cfg=min_const_generics");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,11 @@ skipsdist = true
|
|||
|
||||
[testenv]
|
||||
description = Run the unit tests under {basepython}
|
||||
deps = -rrequirements-dev.txt
|
||||
deps =
|
||||
pip>=21.1 # for in-tree-build
|
||||
-rrequirements-dev.txt
|
||||
commands =
|
||||
# Use pip master with in-tree-build feature (to be released in pip 21.0)
|
||||
python -m pip install --upgrade git+https://github.com/pypa/pip.git
|
||||
# --use-feature=in-tree-build is necessary because this example is inside
|
||||
# the PyO3 repo.
|
||||
python -m pip install . --use-feature=in-tree-build
|
||||
pytest {posargs}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pip>=19.1
|
||||
pytest>=3.5.0
|
||||
setuptools-rust>=0.10.2
|
||||
pytest-benchmark~=3.2
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pip>=19.1
|
||||
hypothesis>=3.55
|
||||
pytest>=3.5.0
|
||||
psutil>=5.6
|
||||
|
|
|
@ -4,9 +4,11 @@ skipsdist = true
|
|||
|
||||
[testenv]
|
||||
description = Run the unit tests under {basepython}
|
||||
deps = -rrequirements-dev.txt
|
||||
deps =
|
||||
pip>=21.1 # for in-tree-build
|
||||
-rrequirements-dev.txt
|
||||
commands =
|
||||
# Use pip master with in-tree-build feature (to be released in pip 21.1)
|
||||
python -m pip install --upgrade git+https://github.com/pypa/pip.git
|
||||
# --use-feature=in-tree-build is necessary because this example is inside
|
||||
# the PyO3 repo.
|
||||
python -m pip install . --use-feature=in-tree-build
|
||||
pytest {posargs}
|
||||
|
|
|
@ -6,7 +6,5 @@ skipsdist = true
|
|||
description = Run the unit tests under {basepython}
|
||||
deps = -rrequirements-dev.txt
|
||||
commands =
|
||||
# Use pip master with in-tree-build feature (to be released in pip 21.1)
|
||||
python -m pip install --upgrade git+https://github.com/pypa/pip.git
|
||||
python -m pip install . --use-feature=in-tree-build
|
||||
python setup.py install
|
||||
pytest {posargs}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pip>=19.1
|
||||
pytest>=3.5.0
|
||||
setuptools-rust>=0.10.2
|
||||
pytest-benchmark>=3.1.1
|
||||
|
|
|
@ -14,8 +14,10 @@ use pyo3_macros_backend::{
|
|||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// Internally, this proc macro create a new c function called `PyInit_{my_module}`
|
||||
/// that then calls the init function you provided
|
||||
/// A proc macro used to implement Python modules.
|
||||
///
|
||||
/// For more on creating Python modules
|
||||
/// see the [module section of the guide](https://pyo3.rs/main/module.html).
|
||||
#[proc_macro_attribute]
|
||||
pub fn pymodule(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut ast = parse_macro_input!(input as syn::ItemFn);
|
||||
|
@ -44,6 +46,16 @@ pub fn pymodule(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
.into()
|
||||
}
|
||||
|
||||
/// A proc macro used to implement Python's [dunder methods][1].
|
||||
///
|
||||
/// This atribute is required on blocks implementing [`PyObjectProtocol`][2],
|
||||
/// [`PyNumberProtocol`][3], [`PyGCProtocol`][4] and [`PyIterProtocol`][5].
|
||||
///
|
||||
/// [1]: https://docs.python.org/3/reference/datamodel.html#special-method-names
|
||||
/// [2]: ../class/basic/trait.PyObjectProtocol.html
|
||||
/// [3]: ../class/number/trait.PyNumberProtocol.html
|
||||
/// [4]: ../class/gc/trait.PyGCProtocol.html
|
||||
/// [5]: ../class/iter/trait.PyIterProtocol.html
|
||||
#[proc_macro_attribute]
|
||||
pub fn pyproto(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut ast = parse_macro_input!(input as syn::ItemImpl);
|
||||
|
@ -56,26 +68,152 @@ pub fn pyproto(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
.into()
|
||||
}
|
||||
|
||||
/// A proc macro used to expose Rust structs as Python objects.
|
||||
///
|
||||
/// `#[pyclass]` accepts the following [parameters][2]:
|
||||
///
|
||||
/// | Parameter | Description |
|
||||
/// | :- | :- |
|
||||
/// | <span style="white-space: pre">`name = "python_name"`</span> | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
|
||||
/// | <span style="white-space: pre">`freelist = N`</span> | Implements a [free list][10] of size N. This can improve performance for types that are often created and deleted in quick succession. Profile your code to see whether `freelist` is right for you. |
|
||||
/// | `gc` | Participate in Python's [garbage collection][5]. Required if your type contains references to other Python objects. If you don't (or incorrectly) implement this, contained Python objects may be hidden from Python's garbage collector and you may leak memory. Note that leaking memory, while undesirable, [is safe behavior][7].|
|
||||
/// | `weakref` | Allows this class to be [weakly referenceable][6]. |
|
||||
/// | <span style="white-space: pre">`extends = BaseType`</span> | Use a custom baseclass. Defaults to [`PyAny`][4] |
|
||||
/// | `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. |
|
||||
/// | `unsendable` | Required if your struct is not [`Send`][3]. Rather than using `unsendable`, consider implementing your struct in a threadsafe way by e.g. substituting [`Rc`][8] with [`Arc`][9]. By using `unsendable`, your class will panic when accessed by another thread.|
|
||||
/// | <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
|
||||
///
|
||||
/// For more on creating Python classes,
|
||||
/// see the [class section of the guide][1].
|
||||
///
|
||||
/// [1]: https://pyo3.rs/main/class.html
|
||||
/// [2]: https://pyo3.rs/main/class.html#customizing-the-class
|
||||
/// [3]: std::marker::Send
|
||||
/// [4]: ../prelude/struct.PyAny.html
|
||||
/// [5]: https://pyo3.rs/main/class/protocols.html#garbage-collector-integration
|
||||
/// [6]: https://docs.python.org/3/library/weakref.html
|
||||
/// [7]: https://doc.rust-lang.org/nomicon/leaking.html
|
||||
/// [8]: std::rc::Rc
|
||||
/// [9]: std::sync::Arc
|
||||
/// [10]: https://en.wikipedia.org/wiki/Free_list
|
||||
#[proc_macro_attribute]
|
||||
pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pyclass_impl(attr, input, PyClassMethodsType::Specialization)
|
||||
}
|
||||
|
||||
/// A proc macro used to expose Rust structs as Python objects.
|
||||
///
|
||||
/// `#[pyclass]` accepts the following [parameters][2]:
|
||||
///
|
||||
/// | Parameter | Description |
|
||||
/// | :- | :- |
|
||||
/// | <span style="white-space: pre">`name = "python_name"`</span> | Sets the name that Python sees this class as. Defaults to the name of the Rust struct. |
|
||||
/// | <span style="white-space: pre">`freelist = N`</span> | Implements a [free list][10] of size N. This can improve performance for types that are often created and deleted in quick succession. Profile your code to see whether `freelist` is right for you. |
|
||||
/// | `gc` | Participate in Python's [garbage collection][5]. Required if your type contains references to other Python objects. If you don't (or incorrectly) implement this, contained Python objects may be hidden from Python's garbage collector and you may leak memory. Note that leaking memory, while undesirable, [is safe behavior][7].|
|
||||
/// | `weakref` | Allows this class to be [weakly referenceable][6]. |
|
||||
/// | <span style="white-space: pre">`extends = BaseType`</span> | Use a custom baseclass. Defaults to [`PyAny`][4] |
|
||||
/// | `subclass` | Allows other Python classes and `#[pyclass]` to inherit from this class. |
|
||||
/// | `unsendable` | Required if your struct is not [`Send`][3]. Rather than using `unsendable`, consider implementing your struct in a threadsafe way by e.g. substituting [`Rc`][8] with [`Arc`][9]. By using `unsendable`, your class will panic when accessed by another thread.|
|
||||
/// | <span style="white-space: pre">`module = "module_name"`</span> | Python code will see the class as being defined in this module. Defaults to `builtins`. |
|
||||
///
|
||||
/// For more on creating Python classes,
|
||||
/// see the [class section of the guide][1].
|
||||
///
|
||||
/// [1]: https://pyo3.rs/main/class.html
|
||||
/// [2]: https://pyo3.rs/main/class.html#customizing-the-class
|
||||
/// [3]: std::marker::Send
|
||||
/// [4]: ../prelude/struct.PyAny.html
|
||||
/// [5]: https://pyo3.rs/main/class/protocols.html#garbage-collector-integration
|
||||
/// [6]: https://docs.python.org/3/library/weakref.html
|
||||
/// [7]: https://doc.rust-lang.org/nomicon/leaking.html
|
||||
/// [8]: std::rc::Rc
|
||||
/// [9]: std::sync::Arc
|
||||
/// [10]: https://en.wikipedia.org/wiki/Free_list
|
||||
#[proc_macro_attribute]
|
||||
pub fn pyclass_with_inventory(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pyclass_impl(attr, input, PyClassMethodsType::Inventory)
|
||||
}
|
||||
|
||||
/// A proc macro used to expose methods to Python.
|
||||
///
|
||||
/// Methods within a `#[pymethods]` block can be annotated with the following:
|
||||
///
|
||||
/// | Annotation | Description |
|
||||
/// | :- | :- |
|
||||
/// | [`#[new]`][4] | Defines the class constructor, like Python's `__new__` method. |
|
||||
/// | [`#[getter]`][5] and [`#[setter]`][5] | These define getters and setters, similar to Python's `@property` decorator. This is useful for getters/setters that require computation or side effects; if that is not the case consider using [`#[pyo3(get, set)]`][11] on the struct's field(s).|
|
||||
/// | [`#[staticmethod]`][6]| Defines the method as a staticmethod, like Python's `@staticmethod` decorator.|
|
||||
/// | [`#[classmethod]`][7] | Defines the method as a classmethod, like Python's `@classmethod` decorator.|
|
||||
/// | [`#[call]`][8] | Allows Python code to call a class instance as a function, like Python's `__call__` method. |
|
||||
/// | [`#[classattr]`][9] | Defines a class variable. |
|
||||
/// | [`#[args]`][10] | Define a method's default arguments and allows the function to receive `*args` and `**kwargs`. |
|
||||
///
|
||||
/// For more on creating class methods,
|
||||
/// see the [class section of the guide][1].
|
||||
///
|
||||
/// If the [`multiple-pymethods`][2] feature is enabled, it is possible to implement
|
||||
/// multiple `#[pymethods]` blocks for a single `#[pyclass]`.
|
||||
/// This will add a transitive dependency on the [`inventory`][3] crate.
|
||||
///
|
||||
/// [1]: https://pyo3.rs/main/class.html#instance-methods
|
||||
/// [2]: https://pyo3.rs/main/features.html#multiple-pymethods
|
||||
/// [3]: https://docs.rs/inventory/
|
||||
/// [4]: https://pyo3.rs/main/class.html#constructor
|
||||
/// [5]: https://pyo3.rs/main/class.html#object-properties-using-getter-and-setter
|
||||
/// [6]: https://pyo3.rs/main/class.html#static-methods
|
||||
/// [7]: https://pyo3.rs/main/class.html#class-methods
|
||||
/// [8]: https://pyo3.rs/main/class.html#callable-objects
|
||||
/// [9]: https://pyo3.rs/main/class.html#class-attributes
|
||||
/// [10]: https://pyo3.rs/main/class.html#method-arguments
|
||||
/// [11]: https://pyo3.rs/main/class.html#object-properties-using-pyo3get-set
|
||||
#[proc_macro_attribute]
|
||||
pub fn pymethods(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pymethods_impl(input, PyClassMethodsType::Specialization)
|
||||
}
|
||||
|
||||
/// A proc macro used to expose methods to Python.
|
||||
///
|
||||
/// Methods within a `#[pymethods]` block can be annotated with the following:
|
||||
///
|
||||
/// | Annotation | Description |
|
||||
/// | :- | :- |
|
||||
/// | [`#[new]`][4] | Defines the class constructor, like Python's `__new__` method. |
|
||||
/// | [`#[getter]`][5] and [`#[setter]`][5] | These define getters and setters, similar to Python's `@property` decorator. This is useful for getters/setters that require computation or side effects; if that is not the case consider using [`#[pyo3(get, set)]`][11] on the struct's field(s).|
|
||||
/// | [`#[staticmethod]`][6]| Defines the method as a staticmethod, like Python's `@staticmethod` decorator.|
|
||||
/// | [`#[classmethod]`][7] | Defines the method as a classmethod, like Python's `@classmethod` decorator.|
|
||||
/// | [`#[call]`][8] | Allows Python code to call a class instance as a function, like Python's `__call__` method. |
|
||||
/// | [`#[classattr]`][9] | Defines a class variable. |
|
||||
/// | [`#[args]`][10] | Define a method's default arguments and allows the function to receive `*args` and `**kwargs`. |
|
||||
///
|
||||
/// For more on creating class methods,
|
||||
/// see the [class section of the guide][1].
|
||||
///
|
||||
/// If the [`multiple-pymethods`][2] feature is enabled, it is possible to implement
|
||||
/// multiple `#[pymethods]` blocks for a single `#[pyclass]`.
|
||||
/// This will add a transitive dependency on the [`inventory`][3] crate.
|
||||
///
|
||||
/// [1]: https://pyo3.rs/main/class.html#instance-methods
|
||||
/// [2]: https://pyo3.rs/main/features.html#multiple-pymethods
|
||||
/// [3]: https://docs.rs/inventory/
|
||||
/// [4]: https://pyo3.rs/main/class.html#constructor
|
||||
/// [5]: https://pyo3.rs/main/class.html#object-properties-using-getter-and-setter
|
||||
/// [6]: https://pyo3.rs/main/class.html#static-methods
|
||||
/// [7]: https://pyo3.rs/main/class.html#class-methods
|
||||
/// [8]: https://pyo3.rs/main/class.html#callable-objects
|
||||
/// [9]: https://pyo3.rs/main/class.html#class-attributes
|
||||
/// [10]: https://pyo3.rs/main/class.html#method-arguments
|
||||
/// [11]: https://pyo3.rs/main/class.html#object-properties-using-pyo3get-set
|
||||
#[proc_macro_attribute]
|
||||
pub fn pymethods_with_inventory(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pymethods_impl(input, PyClassMethodsType::Inventory)
|
||||
}
|
||||
|
||||
/// A proc macro used to expose Rust functions to Python.
|
||||
///
|
||||
/// For more on exposing functions,
|
||||
/// see the [function section of the guide][1].
|
||||
///
|
||||
/// [1]: https://pyo3.rs/main/function.html
|
||||
#[proc_macro_attribute]
|
||||
pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut ast = parse_macro_input!(input as syn::ItemFn);
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
use crate::{exceptions, PyErr};
|
||||
|
||||
#[cfg(min_const_generics)]
|
||||
mod min_const_generics {
|
||||
use super::invalid_sequence_length;
|
||||
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, ToPyObject};
|
||||
|
||||
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
|
||||
where
|
||||
T: ToPyObject,
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.as_ref().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
{
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
create_array_from_obj(obj)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
default fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
create_array_from_obj(obj)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<'source, T, const N: usize> FromPyObject<'source> for [T; N]
|
||||
where
|
||||
for<'a> T: Default + FromPyObject<'a> + crate::buffer::Element,
|
||||
{
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
use crate::{AsPyPointer, PyNativeType};
|
||||
// first try buffer protocol
|
||||
if unsafe { crate::ffi::PyObject_CheckBuffer(obj.as_ptr()) } == 1 {
|
||||
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
|
||||
let mut array = [T::default(); N];
|
||||
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
|
||||
buf.release(obj.py());
|
||||
return Ok(array);
|
||||
}
|
||||
buf.release(obj.py());
|
||||
}
|
||||
}
|
||||
create_array_from_obj(obj)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
{
|
||||
let seq = <crate::types::PySequence as PyTryFrom>::try_from(obj)?;
|
||||
let seq_len = seq.len()? as usize;
|
||||
if seq_len != N {
|
||||
return Err(invalid_sequence_length(N, seq_len));
|
||||
}
|
||||
array_try_from_fn(|idx| seq.get_item(idx as isize).and_then(PyAny::extract))
|
||||
}
|
||||
|
||||
// TODO use std::array::try_from_fn, if that stabilises:
|
||||
// (https://github.com/rust-lang/rust/pull/75644)
|
||||
fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
|
||||
where
|
||||
F: FnMut(usize) -> Result<T, E>,
|
||||
{
|
||||
// Helper to safely create arrays since the standard library doesn't
|
||||
// provide one yet. Shouldn't be necessary in the future.
|
||||
struct ArrayGuard<T, const N: usize> {
|
||||
dst: *mut T,
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.initialized <= N);
|
||||
let initialized_part =
|
||||
core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
|
||||
unsafe {
|
||||
core::ptr::drop_in_place(initialized_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
|
||||
// APIs which would make this easier.
|
||||
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
|
||||
let mut guard: ArrayGuard<T, N> = ArrayGuard {
|
||||
dst: array.as_mut_ptr() as _,
|
||||
initialized: 0,
|
||||
};
|
||||
unsafe {
|
||||
let mut value_ptr = array.as_mut_ptr() as *mut T;
|
||||
for i in 0..N {
|
||||
core::ptr::write(value_ptr, cb(i)?);
|
||||
value_ptr = value_ptr.offset(1);
|
||||
guard.initialized += 1;
|
||||
}
|
||||
core::mem::forget(guard);
|
||||
Ok(array.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::{
|
||||
panic,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn array_try_from_fn() {
|
||||
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
struct CountDrop;
|
||||
impl Drop for CountDrop {
|
||||
fn drop(&mut self) {
|
||||
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
let _ = catch_unwind_silent(move || {
|
||||
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
|
||||
if idx == 2 {
|
||||
panic!("peek a boo");
|
||||
}
|
||||
Ok(CountDrop)
|
||||
});
|
||||
});
|
||||
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bytearray_to_array() {
|
||||
Python::with_gil(|py| {
|
||||
let v: [u8; 33] = py
|
||||
.eval(
|
||||
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
|
||||
})
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/59211505
|
||||
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
|
||||
where
|
||||
F: FnOnce() -> R + panic::UnwindSafe,
|
||||
{
|
||||
let prev_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {}));
|
||||
let result = panic::catch_unwind(f);
|
||||
panic::set_hook(prev_hook);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(min_const_generics))]
|
||||
mod array_impls {
|
||||
use super::invalid_sequence_length;
|
||||
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python, ToPyObject};
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr),+) => {
|
||||
$(
|
||||
impl<T> IntoPy<PyObject> for [T; $N]
|
||||
where
|
||||
T: ToPyObject
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.as_ref().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for [T; $N]
|
||||
where
|
||||
T: Copy + Default + FromPyObject<'a>,
|
||||
{
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
default fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<'source, T> FromPyObject<'source> for [T; $N]
|
||||
where
|
||||
for<'a> T: Default + FromPyObject<'a> + crate::buffer::Element,
|
||||
{
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
// first try buffer protocol
|
||||
if unsafe { crate::ffi::PyObject_CheckBuffer(obj.as_ptr()) } == 1 {
|
||||
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
|
||||
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
|
||||
buf.release(obj.py());
|
||||
return Ok(array);
|
||||
}
|
||||
buf.release(obj.py());
|
||||
}
|
||||
}
|
||||
// fall back to sequence protocol
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(min_const_generics))]
|
||||
array_impls!(
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32
|
||||
);
|
||||
|
||||
#[cfg(not(min_const_generics))]
|
||||
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
{
|
||||
let seq = <crate::types::PySequence as PyTryFrom>::try_from(obj)?;
|
||||
let seq_len = seq.len()? as usize;
|
||||
if seq_len != slice.len() {
|
||||
return Err(invalid_sequence_length(slice.len(), seq_len));
|
||||
}
|
||||
for (value, item) in slice.iter_mut().zip(seq.iter()?) {
|
||||
*value = item?.extract::<T>()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
|
||||
exceptions::PyValueError::new_err(format!(
|
||||
"expected a sequence of length {} (got {})",
|
||||
expected, actual
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{PyResult, Python};
|
||||
|
||||
#[test]
|
||||
fn test_extract_small_bytearray_to_array() {
|
||||
Python::with_gil(|py| {
|
||||
let v: [u8; 3] = py
|
||||
.eval("bytearray(b'abc')", None, None)
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert!(&v == b"abc");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_invalid_sequence_length() {
|
||||
Python::with_gil(|py| {
|
||||
let v: PyResult<[u8; 3]> = py
|
||||
.eval("bytearray(b'abcdefg')", None, None)
|
||||
.unwrap()
|
||||
.extract();
|
||||
assert_eq!(
|
||||
v.unwrap_err().to_string(),
|
||||
"ValueError: expected a sequence of length 3 (got 7)"
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//! This module contains conversions between non-String Rust object and their string representation
|
||||
//! in Python
|
||||
//! This module contains conversions between various Rust object and their representation in Python.
|
||||
|
||||
mod array;
|
||||
mod osstr;
|
||||
mod path;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
//! Exception types defined by Python.
|
||||
|
||||
use crate::type_object::PySizedLayout;
|
||||
use crate::{ffi, PyResult, Python};
|
||||
use std::ffi::CStr;
|
||||
use std::ops;
|
||||
|
@ -78,9 +77,8 @@ macro_rules! import_exception {
|
|||
|
||||
$crate::pyobject_native_type_core!(
|
||||
$name,
|
||||
$crate::ffi::PyBaseExceptionObject,
|
||||
*$name::type_object_raw($crate::Python::assume_gil_acquired()),
|
||||
Some(stringify!($module))
|
||||
#module=Some(stringify!($module))
|
||||
);
|
||||
|
||||
impl $name {
|
||||
|
@ -164,9 +162,8 @@ macro_rules! create_exception_type_object {
|
|||
($module: ident, $name: ident, $base: ty) => {
|
||||
$crate::pyobject_native_type_core!(
|
||||
$name,
|
||||
$crate::ffi::PyBaseExceptionObject,
|
||||
*$name::type_object_raw($crate::Python::assume_gil_acquired()),
|
||||
Some(stringify!($module))
|
||||
#module=Some(stringify!($module))
|
||||
);
|
||||
|
||||
impl $name {
|
||||
|
@ -201,15 +198,13 @@ macro_rules! impl_native_exception (
|
|||
pub struct $name($crate::PyAny);
|
||||
|
||||
$crate::impl_exception_boilerplate!($name);
|
||||
$crate::pyobject_native_type_core!($name, $layout, *(ffi::$exc_name as *mut ffi::PyTypeObject), Some("builtins"));
|
||||
$crate::pyobject_native_type!($name, $layout, *(ffi::$exc_name as *mut ffi::PyTypeObject));
|
||||
);
|
||||
($name:ident, $exc_name:ident) => (
|
||||
impl_native_exception!($name, $exc_name, ffi::PyBaseExceptionObject);
|
||||
)
|
||||
);
|
||||
|
||||
impl PySizedLayout<PyBaseException> for ffi::PyBaseExceptionObject {}
|
||||
|
||||
impl_native_exception!(PyBaseException, PyExc_BaseException);
|
||||
impl_native_exception!(PyException, PyExc_Exception);
|
||||
impl_native_exception!(PyStopAsyncIteration, PyExc_StopAsyncIteration);
|
||||
|
|
|
@ -399,13 +399,11 @@ pub struct PyDateTime_CAPI {
|
|||
pub TimeZone_FromTimeZone:
|
||||
unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject,
|
||||
|
||||
// Defined for PyPy as `PyDateTime_FromTimestamp`
|
||||
pub DateTime_FromTimestamp: unsafe extern "C" fn(
|
||||
cls: *mut PyTypeObject,
|
||||
args: *mut PyObject,
|
||||
kwargs: *mut PyObject,
|
||||
) -> *mut PyObject,
|
||||
// Defined for PyPy as `PyDate_FromTimestamp`
|
||||
pub Date_FromTimestamp:
|
||||
unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject,
|
||||
#[cfg(not(PyPy))]
|
||||
|
@ -447,6 +445,17 @@ pub static PyDateTimeAPI: _PyDateTimeAPI_impl = _PyDateTimeAPI_impl {
|
|||
inner: GILOnceCell::new(),
|
||||
};
|
||||
|
||||
/// Safe wrapper around the Python C-API global `PyDateTime_TimeZone_UTC`. This follows a similar
|
||||
/// strategy as [`PyDateTimeAPI`]: the Python datetime C-API will automatically be imported if this
|
||||
/// type is deferenced.
|
||||
///
|
||||
/// The type obtained by dereferencing this object is `&'static PyObject`. This may change in the
|
||||
/// future to be a more specific type representing that this is a `datetime.timezone` object.
|
||||
#[cfg(all(Py_3_7, not(PyPy)))]
|
||||
pub static PyDateTime_TimeZone_UTC: _PyDateTime_TimeZone_UTC_impl = _PyDateTime_TimeZone_UTC_impl {
|
||||
inner: &PyDateTimeAPI,
|
||||
};
|
||||
|
||||
/// Populates the `PyDateTimeAPI` object
|
||||
///
|
||||
/// Unlike in C, this does *not* need to be actively invoked in Rust, which
|
||||
|
@ -559,6 +568,16 @@ pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
|
|||
// skipped non-limited PyTimeZone_FromOffset
|
||||
// skipped non-limited PyTimeZone_FromOffsetAndName
|
||||
|
||||
#[cfg(not(PyPy))]
|
||||
pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
|
||||
(PyDateTimeAPI.DateTime_FromTimestamp)(PyDateTimeAPI.DateTimeType, args, std::ptr::null_mut())
|
||||
}
|
||||
|
||||
#[cfg(not(PyPy))]
|
||||
pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
|
||||
(PyDateTimeAPI.Date_FromTimestamp)(PyDateTimeAPI.DateType, args)
|
||||
}
|
||||
|
||||
#[cfg(PyPy)]
|
||||
extern "C" {
|
||||
#[link_name = "PyPyDate_FromTimestamp"]
|
||||
|
@ -566,6 +585,7 @@ extern "C" {
|
|||
#[link_name = "PyPyDateTime_FromTimestamp"]
|
||||
pub fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
|
||||
}
|
||||
|
||||
#[cfg(PyPy)]
|
||||
extern "C" {
|
||||
#[link_name = "_PyPyDateTime_Import"]
|
||||
|
@ -587,3 +607,70 @@ impl Deref for _PyDateTimeAPI_impl {
|
|||
unsafe { PyDateTime_IMPORT() }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(all(Py_3_7, not(PyPy)))]
|
||||
pub struct _PyDateTime_TimeZone_UTC_impl {
|
||||
inner: &'static _PyDateTimeAPI_impl,
|
||||
}
|
||||
|
||||
#[cfg(all(Py_3_7, not(PyPy)))]
|
||||
impl Deref for _PyDateTime_TimeZone_UTC_impl {
|
||||
type Target = crate::PyObject;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &crate::PyObject {
|
||||
unsafe {
|
||||
&*((&self.inner.TimeZone_UTC) as *const *mut crate::ffi::PyObject
|
||||
as *const crate::PyObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{py_run, AsPyPointer, IntoPy, Py, PyAny, Python};
|
||||
|
||||
#[test]
|
||||
fn test_datetime_fromtimestamp() {
|
||||
Python::with_gil(|py| {
|
||||
let args: Py<PyAny> = (100,).into_py(py);
|
||||
unsafe { PyDateTime_IMPORT() };
|
||||
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDateTime_FromTimestamp(args.as_ptr())) };
|
||||
py_run!(
|
||||
py,
|
||||
dt,
|
||||
"import datetime; assert dt == datetime.datetime.fromtimestamp(100)"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_fromtimestamp() {
|
||||
Python::with_gil(|py| {
|
||||
let args: Py<PyAny> = (100,).into_py(py);
|
||||
dbg!(args.as_ref(py));
|
||||
unsafe { PyDateTime_IMPORT() };
|
||||
let dt: &PyAny = unsafe { py.from_owned_ptr(PyDate_FromTimestamp(args.as_ptr())) };
|
||||
py_run!(
|
||||
py,
|
||||
dt,
|
||||
"import datetime; assert dt == datetime.date.fromtimestamp(100)"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(Py_3_7, not(PyPy)))]
|
||||
fn test_utc_timezone() {
|
||||
Python::with_gil(|py| {
|
||||
let utc_timezone = PyDateTime_TimeZone_UTC.as_ref(py);
|
||||
py_run!(
|
||||
py,
|
||||
utc_timezone,
|
||||
"import datetime; assert utc_timezone is datetime.timezone.utc"
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,11 +197,13 @@ pub mod types;
|
|||
#[cfg(feature = "serde")]
|
||||
pub mod serde;
|
||||
|
||||
/// The proc macros, which are also part of the prelude.
|
||||
/// The proc macros, all of which are part of the prelude.
|
||||
///
|
||||
/// Import these with `use pyo3::prelude::*;`
|
||||
#[cfg(feature = "macros")]
|
||||
pub mod proc_macro {
|
||||
pub use pyo3_macros::pymodule;
|
||||
/// The proc macro attributes
|
||||
|
||||
pub use pyo3_macros::{pyfunction, pyproto};
|
||||
|
||||
#[cfg(not(feature = "multiple-pymethods"))]
|
||||
|
|
|
@ -61,7 +61,7 @@ pyobject_native_type_info!(
|
|||
ffi::PyObject,
|
||||
ffi::PyBaseObject_Type,
|
||||
Some("builtins"),
|
||||
PyObject_Check
|
||||
#checkfunction=PyObject_Check
|
||||
);
|
||||
|
||||
pyobject_native_type_extract!(PyAny);
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
#[repr(transparent)]
|
||||
pub struct PyBool(PyAny);
|
||||
|
||||
pyobject_native_type!(PyBool, ffi::PyObject, ffi::PyBool_Type, ffi::PyBool_Check);
|
||||
pyobject_native_type!(PyBool, ffi::PyObject, ffi::PyBool_Type, #checkfunction=ffi::PyBool_Check);
|
||||
|
||||
impl PyBool {
|
||||
/// Depending on `val`, returns `true` or `false`.
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::slice;
|
|||
#[repr(transparent)]
|
||||
pub struct PyByteArray(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyByteArray, ffi::PyByteArray_Type, ffi::PyByteArray_Check);
|
||||
pyobject_native_type_core!(PyByteArray, ffi::PyByteArray_Type, #checkfunction=ffi::PyByteArray_Check);
|
||||
|
||||
impl PyByteArray {
|
||||
/// Creates a new Python bytearray object.
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::str;
|
|||
#[repr(transparent)]
|
||||
pub struct PyBytes(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyBytes, ffi::PyBytes_Type, ffi::PyBytes_Check);
|
||||
pyobject_native_type_core!(PyBytes, ffi::PyBytes_Type, #checkfunction=ffi::PyBytes_Check);
|
||||
|
||||
impl PyBytes {
|
||||
/// Creates a new Python bytestring object.
|
||||
|
|
|
@ -13,7 +13,7 @@ pyobject_native_type!(
|
|||
PyComplex,
|
||||
ffi::PyComplexObject,
|
||||
ffi::PyComplex_Type,
|
||||
ffi::PyComplex_Check
|
||||
#checkfunction=ffi::PyComplex_Check
|
||||
);
|
||||
|
||||
impl PyComplex {
|
||||
|
|
|
@ -66,8 +66,8 @@ pyobject_native_type!(
|
|||
PyDate,
|
||||
crate::ffi::PyDateTime_Date,
|
||||
*PyDateTimeAPI.DateType,
|
||||
Some("datetime"),
|
||||
PyDate_Check
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyDate_Check
|
||||
);
|
||||
|
||||
impl PyDate {
|
||||
|
@ -123,8 +123,8 @@ pyobject_native_type!(
|
|||
PyDateTime,
|
||||
crate::ffi::PyDateTime_DateTime,
|
||||
*PyDateTimeAPI.DateTimeType,
|
||||
Some("datetime"),
|
||||
PyDateTime_Check
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyDateTime_Check
|
||||
);
|
||||
|
||||
impl PyDateTime {
|
||||
|
@ -268,8 +268,8 @@ pyobject_native_type!(
|
|||
PyTime,
|
||||
crate::ffi::PyDateTime_Time,
|
||||
*PyDateTimeAPI.TimeType,
|
||||
Some("datetime"),
|
||||
PyTime_Check
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyTime_Check
|
||||
);
|
||||
|
||||
impl PyTime {
|
||||
|
@ -352,8 +352,8 @@ pyobject_native_type!(
|
|||
PyTzInfo,
|
||||
crate::ffi::PyObject,
|
||||
*PyDateTimeAPI.TZInfoType,
|
||||
Some("datetime"),
|
||||
PyTZInfo_Check
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyTZInfo_Check
|
||||
);
|
||||
|
||||
/// Bindings for `datetime.timedelta`
|
||||
|
@ -363,8 +363,8 @@ pyobject_native_type!(
|
|||
PyDelta,
|
||||
crate::ffi::PyDateTime_Delta,
|
||||
*PyDateTimeAPI.DeltaType,
|
||||
Some("datetime"),
|
||||
PyDelta_Check
|
||||
#module=Some("datetime"),
|
||||
#checkfunction=PyDelta_Check
|
||||
);
|
||||
|
||||
impl PyDelta {
|
||||
|
|
|
@ -20,7 +20,7 @@ pyobject_native_type!(
|
|||
PyDict,
|
||||
ffi::PyDictObject,
|
||||
ffi::PyDict_Type,
|
||||
ffi::PyDict_Check
|
||||
#checkfunction=ffi::PyDict_Check
|
||||
);
|
||||
|
||||
impl PyDict {
|
||||
|
|
|
@ -20,7 +20,7 @@ pyobject_native_type!(
|
|||
PyFloat,
|
||||
ffi::PyFloatObject,
|
||||
ffi::PyFloat_Type,
|
||||
ffi::PyFloat_Check
|
||||
#checkfunction=ffi::PyFloat_Check
|
||||
);
|
||||
|
||||
impl PyFloat {
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
#[repr(transparent)]
|
||||
pub struct PyCFunction(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
|
||||
pyobject_native_type_core!(PyCFunction, ffi::PyCFunction_Type, #checkfunction=ffi::PyCFunction_Check);
|
||||
|
||||
impl PyCFunction {
|
||||
/// Create a new built-in function with keywords.
|
||||
|
@ -73,4 +73,4 @@ impl PyCFunction {
|
|||
pub struct PyFunction(PyAny);
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pyobject_native_var_type!(PyFunction, ffi::PyFunction_Type, ffi::PyFunction_Check);
|
||||
pyobject_native_type_core!(PyFunction, ffi::PyFunction_Type, #checkfunction=ffi::PyFunction_Check);
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
#[repr(transparent)]
|
||||
pub struct PyList(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyList, ffi::PyList_Type, ffi::PyList_Check);
|
||||
pyobject_native_type_core!(PyList, ffi::PyList_Type, #checkfunction=ffi::PyList_Check);
|
||||
|
||||
impl PyList {
|
||||
/// Constructs a new list with the given elements.
|
||||
|
@ -178,26 +178,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr),+) => {
|
||||
$(
|
||||
impl<T> IntoPy<PyObject> for [T; $N]
|
||||
where
|
||||
T: ToPyObject
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
self.as_ref().to_object(py)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
array_impls!(
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 29, 30, 31, 32
|
||||
);
|
||||
|
||||
impl<T> ToPyObject for Vec<T>
|
||||
where
|
||||
T: ToPyObject,
|
||||
|
|
136
src/types/mod.rs
136
src/types/mod.rs
|
@ -30,7 +30,7 @@ pub use self::typeobject::PyType;
|
|||
// Implementations core to all native types
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_base(
|
||||
($name: ty $(;$generics: ident)* ) => {
|
||||
($name:ty $(;$generics:ident)* ) => {
|
||||
unsafe impl<$($generics,)*> $crate::PyNativeType for $name {}
|
||||
|
||||
impl<$($generics,)*> std::fmt::Debug for $name {
|
||||
|
@ -75,7 +75,7 @@ macro_rules! pyobject_native_type_base(
|
|||
// make sense on PyAny / have different implementations).
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_named (
|
||||
($name: ty $(;$generics: ident)*) => {
|
||||
($name:ty $(;$generics:ident)*) => {
|
||||
$crate::pyobject_native_type_base!($name $(;$generics)*);
|
||||
|
||||
impl<$($generics,)*> std::convert::AsRef<$crate::PyAny> for $name {
|
||||
|
@ -127,76 +127,10 @@ macro_rules! pyobject_native_type_named (
|
|||
};
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_core {
|
||||
($name: ty, $layout: path, $typeobject: expr, $module: expr $(, $checkfunction:path)? $(;$generics: ident)*) => {
|
||||
unsafe impl $crate::type_object::PyLayout<$name> for $layout {}
|
||||
$crate::pyobject_native_type_named!($name $(;$generics)*);
|
||||
$crate::pyobject_native_type_info!($name, $layout, $typeobject, $module $(, $checkfunction)? $(;$generics)*);
|
||||
$crate::pyobject_native_type_extract!($name $(;$generics)*);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_sized {
|
||||
($name: ty, $layout: path $(;$generics: ident)*) => {
|
||||
// To prevent inheriting native types with ABI3
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))]
|
||||
impl $crate::type_object::PySizedLayout<$name> for $layout {}
|
||||
impl<'a, $($generics,)*> $crate::derive_utils::PyBaseTypeUtils for $name {
|
||||
type Dict = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type LayoutAsBase = $crate::pycell::PyCellBase<$name>;
|
||||
type BaseNativeType = $name;
|
||||
type ThreadChecker = $crate::class::impl_::ThreadCheckerStub<$crate::PyObject>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type {
|
||||
($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction:path $(;$generics: ident)*) => {
|
||||
$crate::pyobject_native_type_core!($name, $layout, $typeobject, $module, $checkfunction $(;$generics)*);
|
||||
$crate::pyobject_native_type_sized!($name, $layout $(;$generics)*);
|
||||
};
|
||||
($name: ty, $layout: path, $typeobject: expr, $checkfunction:path $(;$generics: ident)*) => {
|
||||
$crate::pyobject_native_type! {
|
||||
$name, $layout, $typeobject, Some("builtins"), $checkfunction $(;$generics)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_var_type {
|
||||
($name: ty, $typeobject: expr, $module: expr, $checkfunction:path $(;$generics: ident)*) => {
|
||||
$crate::pyobject_native_type_core!(
|
||||
$name, $crate::ffi::PyObject, $typeobject, Some("builtins"), $checkfunction $(;$generics)*);
|
||||
};
|
||||
($name: ty, $typeobject: expr, $checkfunction: path $(;$generics: ident)*) => {
|
||||
$crate::pyobject_native_var_type! {
|
||||
$name, $typeobject, Some("builtins"), $checkfunction $(;$generics)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// NOTE: This macro is not included in pyobject_native_type_base!
|
||||
// because rust-numpy has a special implementation.
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_extract {
|
||||
($name: ty $(;$generics: ident)*) => {
|
||||
impl<'py, $($generics,)*> $crate::FromPyObject<'py> for &'py $name {
|
||||
fn extract(obj: &'py $crate::PyAny) -> $crate::PyResult<Self> {
|
||||
$crate::PyTryFrom::try_from(obj).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_info(
|
||||
($name: ty, $layout: path, $typeobject: expr,
|
||||
$module: expr $(, $checkfunction:path)? $(;$generics: ident)*) => {
|
||||
($name:ty, $layout:path, $typeobject:expr,
|
||||
$module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => {
|
||||
unsafe impl<$($generics,)*> $crate::type_object::PyTypeInfo for $name {
|
||||
type BaseType = $crate::PyAny;
|
||||
type Layout = $layout;
|
||||
|
@ -226,6 +160,66 @@ macro_rules! pyobject_native_type_info(
|
|||
};
|
||||
);
|
||||
|
||||
// NOTE: This macro is not included in pyobject_native_type_base!
|
||||
// because rust-numpy has a special implementation.
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_extract {
|
||||
($name:ty $(;$generics:ident)*) => {
|
||||
impl<'py, $($generics,)*> $crate::FromPyObject<'py> for &'py $name {
|
||||
fn extract(obj: &'py $crate::PyAny) -> $crate::PyResult<Self> {
|
||||
$crate::PyTryFrom::try_from(obj).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares all of the boilerplate for Python types.
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_core {
|
||||
($name:ty, $typeobject:expr, #module=$module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => {
|
||||
$crate::pyobject_native_type_core!(@impl $name, $crate::PyAny, $typeobject, #module=$module $(, #checkfunction=$checkfunction)? $(;$generics)*);
|
||||
};
|
||||
($name:ty, $typeobject:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => {
|
||||
$crate::pyobject_native_type_core!(@impl $name, $crate::PyAny, $typeobject, #module=Some("builtins") $(, #checkfunction=$checkfunction)? $(;$generics)*);
|
||||
};
|
||||
|
||||
(@impl $name:ty, $layout:path, $typeobject:expr, #module=$module:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => {
|
||||
unsafe impl $crate::type_object::PyLayout<$name> for $layout {}
|
||||
$crate::pyobject_native_type_named!($name $(;$generics)*);
|
||||
$crate::pyobject_native_type_info!($name, $layout, $typeobject, $module $(, #checkfunction=$checkfunction)? $(;$generics)*);
|
||||
$crate::pyobject_native_type_extract!($name $(;$generics)*);
|
||||
};
|
||||
(@impl $name:ty, $layout:path, $typeobject:expr $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => {
|
||||
$crate::pyobject_native_type_core!(@impl $name, $layout, $typeobject, #module=Some("builtins") $(, #checkfunction=$checkfunction)? $(;$generics)*);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type_sized {
|
||||
($name:ty, $layout:path $(;$generics:ident)*) => {
|
||||
// To prevent inheriting native types with ABI3
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
impl $crate::type_object::PySizedLayout<$name> for $layout {}
|
||||
impl<'a, $($generics,)*> $crate::derive_utils::PyBaseTypeUtils for $name {
|
||||
type Dict = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type WeakRef = $crate::pyclass_slots::PyClassDummySlot;
|
||||
type LayoutAsBase = $crate::pycell::PyCellBase<$name>;
|
||||
type BaseNativeType = $name;
|
||||
type ThreadChecker = $crate::class::impl_::ThreadCheckerStub<$crate::PyObject>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares all of the boilerplate for Python types which can be inherited from (because the exact
|
||||
/// Python layout is known).
|
||||
#[macro_export]
|
||||
macro_rules! pyobject_native_type {
|
||||
($name:ty, $layout:path, $typeobject:expr $(, #module=$module:expr)? $(, #checkfunction=$checkfunction:path)? $(;$generics:ident)*) => {
|
||||
$crate::pyobject_native_type_core!(@impl $name, $layout, $typeobject $(, #module=$module)? $(, #checkfunction=$checkfunction)? $(;$generics)*);
|
||||
$crate::pyobject_native_type_sized!($name, $layout $(;$generics)*);
|
||||
};
|
||||
}
|
||||
|
||||
mod any;
|
||||
mod boolobject;
|
||||
mod bytearray;
|
||||
|
@ -246,4 +240,4 @@ mod set;
|
|||
mod slice;
|
||||
mod string;
|
||||
mod tuple;
|
||||
mod typeobject;
|
||||
mod typeobject;
|
|
@ -19,7 +19,7 @@ use std::str;
|
|||
#[repr(transparent)]
|
||||
pub struct PyModule(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyModule, ffi::PyModule_Type, ffi::PyModule_Check);
|
||||
pyobject_native_type_core!(PyModule, ffi::PyModule_Type, #checkfunction=ffi::PyModule_Check);
|
||||
|
||||
impl PyModule {
|
||||
/// Creates a new module object with the `__name__` attribute set to name.
|
||||
|
|
|
@ -55,7 +55,7 @@ macro_rules! int_fits_larger_int {
|
|||
#[repr(transparent)]
|
||||
pub struct PyLong(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyLong, ffi::PyLong_Type, ffi::PyLong_Check);
|
||||
pyobject_native_type_core!(PyLong, ffi::PyLong_Type, #checkfunction=ffi::PyLong_Check);
|
||||
|
||||
macro_rules! int_fits_c_long {
|
||||
($rust_type:ty) => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use crate::err::{self, PyDowncastError, PyErr, PyResult};
|
||||
use crate::exceptions;
|
||||
use crate::ffi::{self, Py_ssize_t};
|
||||
use crate::instance::PyNativeType;
|
||||
use crate::types::{PyAny, PyList, PyTuple};
|
||||
|
@ -258,59 +257,6 @@ impl PySequence {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr),+) => {
|
||||
$(
|
||||
impl<'a, T> FromPyObject<'a> for [T; $N]
|
||||
where
|
||||
T: Copy + Default + FromPyObject<'a>,
|
||||
{
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
default fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<'source, T> FromPyObject<'source> for [T; $N]
|
||||
where
|
||||
for<'a> T: Default + FromPyObject<'a> + crate::buffer::Element,
|
||||
{
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
// first try buffer protocol
|
||||
if unsafe { ffi::PyObject_CheckBuffer(obj.as_ptr()) } == 1 {
|
||||
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
|
||||
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
|
||||
buf.release(obj.py());
|
||||
return Ok(array);
|
||||
}
|
||||
buf.release(obj.py());
|
||||
}
|
||||
}
|
||||
// fall back to sequence protocol
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
array_impls!(
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 29, 30, 31, 32
|
||||
);
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for Vec<T>
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
|
@ -358,22 +304,6 @@ where
|
|||
Ok(v)
|
||||
}
|
||||
|
||||
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
{
|
||||
let seq = <PySequence as PyTryFrom>::try_from(obj)?;
|
||||
if seq.len()? as usize != slice.len() {
|
||||
return Err(exceptions::PyBufferError::new_err(
|
||||
"Slice length does not match buffer length.",
|
||||
));
|
||||
}
|
||||
for (value, item) in slice.iter_mut().zip(seq.iter()?) {
|
||||
*value = item?.extract::<T>()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'v> PyTryFrom<'v> for PySequence {
|
||||
fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> {
|
||||
let value = value.into();
|
||||
|
@ -707,18 +637,6 @@ mod test {
|
|||
assert!(v == [1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bytearray_to_array() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v: [u8; 3] = py
|
||||
.eval("bytearray(b'abc')", None, None)
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert!(&v == b"abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bytearray_to_vec() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
|
@ -20,12 +20,12 @@ pub struct PySet(PyAny);
|
|||
#[repr(transparent)]
|
||||
pub struct PyFrozenSet(PyAny);
|
||||
|
||||
pyobject_native_type!(PySet, ffi::PySetObject, ffi::PySet_Type, ffi::PySet_Check);
|
||||
pyobject_native_type!(PySet, ffi::PySetObject, ffi::PySet_Type, #checkfunction=ffi::PySet_Check);
|
||||
pyobject_native_type!(
|
||||
PyFrozenSet,
|
||||
ffi::PySetObject,
|
||||
ffi::PyFrozenSet_Type,
|
||||
ffi::PyFrozenSet_Check
|
||||
#checkfunction=ffi::PyFrozenSet_Check
|
||||
);
|
||||
|
||||
impl PySet {
|
||||
|
|
|
@ -16,7 +16,7 @@ pyobject_native_type!(
|
|||
PySlice,
|
||||
ffi::PySliceObject,
|
||||
ffi::PySlice_Type,
|
||||
ffi::PySlice_Check
|
||||
#checkfunction=ffi::PySlice_Check
|
||||
);
|
||||
|
||||
/// Represents Python `slice` indices.
|
||||
|
|
|
@ -15,7 +15,7 @@ use std::str;
|
|||
#[repr(transparent)]
|
||||
pub struct PyString(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyString, ffi::PyUnicode_Type, ffi::PyUnicode_Check);
|
||||
pyobject_native_type_core!(PyString, ffi::PyUnicode_Type, #checkfunction=ffi::PyUnicode_Check);
|
||||
|
||||
impl PyString {
|
||||
/// Creates a new Python string object.
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
#[repr(transparent)]
|
||||
pub struct PyTuple(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyTuple, ffi::PyTuple_Type, ffi::PyTuple_Check);
|
||||
pyobject_native_type_core!(PyTuple, ffi::PyTuple_Type, #checkfunction=ffi::PyTuple_Check);
|
||||
|
||||
impl PyTuple {
|
||||
/// Constructs a new tuple with the given elements.
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{ffi, AsPyPointer, PyAny, Python};
|
|||
#[repr(transparent)]
|
||||
pub struct PyType(PyAny);
|
||||
|
||||
pyobject_native_var_type!(PyType, ffi::PyType_Type, ffi::PyType_Check);
|
||||
pyobject_native_type_core!(PyType, ffi::PyType_Type, #checkfunction=ffi::PyType_Check);
|
||||
|
||||
impl PyType {
|
||||
/// Creates a new type object.
|
||||
|
|
Loading…
Reference in New Issue