Merge branch 'main' into docsrs

This commit is contained in:
mejrs 2021-05-04 12:34:38 +02:00 committed by GitHub
commit 5f51520559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 651 additions and 231 deletions

View File

@ -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

View File

@ -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)

View File

@ -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(())
}

View File

@ -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}

View File

@ -1,4 +1,3 @@
pip>=19.1
pytest>=3.5.0
setuptools-rust>=0.10.2
pytest-benchmark~=3.2

View File

@ -1,4 +1,3 @@
pip>=19.1
hypothesis>=3.55
pytest>=3.5.0
psutil>=5.6

View File

@ -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}

View File

@ -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}

View File

@ -1,4 +1,3 @@
pip>=19.1
pytest>=3.5.0
setuptools-rust>=0.10.2
pytest-benchmark>=3.1.1

View File

@ -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);

288
src/conversions/array.rs Normal file
View File

@ -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)"
);
})
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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"
);
})
}
}

View File

@ -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"))]

View File

@ -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);

View File

@ -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`.

View File

@ -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.

View File

@ -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.

View File

@ -13,7 +13,7 @@ pyobject_native_type!(
PyComplex,
ffi::PyComplexObject,
ffi::PyComplex_Type,
ffi::PyComplex_Check
#checkfunction=ffi::PyComplex_Check
);
impl PyComplex {

View File

@ -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 {

View File

@ -20,7 +20,7 @@ pyobject_native_type!(
PyDict,
ffi::PyDictObject,
ffi::PyDict_Type,
ffi::PyDict_Check
#checkfunction=ffi::PyDict_Check
);
impl PyDict {

View File

@ -20,7 +20,7 @@ pyobject_native_type!(
PyFloat,
ffi::PyFloatObject,
ffi::PyFloat_Type,
ffi::PyFloat_Check
#checkfunction=ffi::PyFloat_Check
);
impl PyFloat {

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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.

View File

@ -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) => {

View File

@ -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();

View File

@ -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 {

View File

@ -16,7 +16,7 @@ pyobject_native_type!(
PySlice,
ffi::PySliceObject,
ffi::PySlice_Type,
ffi::PySlice_Check
#checkfunction=ffi::PySlice_Check
);
/// Represents Python `slice` indices.

View File

@ -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.

View File

@ -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.

View File

@ -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.