From 38af32e33ef9878217384bebab7e9df25233c4e5 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 12 Oct 2020 16:18:25 +0100 Subject: [PATCH 01/18] Release 0.12.3 --- CHANGELOG.md | 5 +++-- Cargo.toml | 4 ++-- Makefile | 2 ++ README.md | 4 ++-- pyo3-derive-backend/Cargo.toml | 2 +- pyo3cls/Cargo.toml | 4 ++-- src/lib.rs | 4 ++-- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d59efef..61fc0415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ PyO3 versions, please see the [migration guide](https://pyo3.rs/master/migration The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.12.3] - 2020-10-12 ### Fixed - Fix support for Rust versions 1.39 to 1.44, broken by an incorrect internal update to paste 1.0 which was done in PyO3 0.12.2. [#1234](https://github.com/PyO3/pyo3/pull/1234) @@ -515,7 +515,8 @@ Yanked ### Added - Initial release -[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.12.2...HEAD +[Unreleased]: https://github.com/pyo3/pyo3/compare/v0.12.3...HEAD +[0.12.3]: https://github.com/pyo3/pyo3/compare/v0.12.2...v0.12.3 [0.12.2]: https://github.com/pyo3/pyo3/compare/v0.12.1...v0.12.2 [0.12.1]: https://github.com/pyo3/pyo3/compare/v0.12.0...v0.12.1 [0.12.0]: https://github.com/pyo3/pyo3/compare/v0.11.1...v0.12.0 diff --git a/Cargo.toml b/Cargo.toml index e8a5b23b..c7f9f55e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3" -version = "0.12.2" +version = "0.12.3" description = "Bindings to Python interpreter" authors = ["PyO3 Project and Contributors "] readme = "README.md" @@ -23,7 +23,7 @@ parking_lot = "0.11.0" num-bigint = { version = "0.3", optional = true } num-complex = { version = "0.3", optional = true } paste = { version = "0.1.6", optional = true } -pyo3cls = { path = "pyo3cls", version = "=0.12.2", optional = true } +pyo3cls = { path = "pyo3cls", version = "=0.12.3", optional = true } unindent = { version = "0.1.4", optional = true } hashbrown = { version = "0.9", optional = true } diff --git a/Makefile b/Makefile index 87b67ea8..1fe0fee3 100644 --- a/Makefile +++ b/Makefile @@ -20,5 +20,7 @@ lint: fmt clippy publish: test cargo publish --manifest-path pyo3-derive-backend/Cargo.toml + sleep 10 # wait for crates.io to update cargo publish --manifest-path pyo3cls/Cargo.toml + sleep 10 # wait for crates.io to update cargo publish diff --git a/README.md b/README.md index 5a8eff94..b42156ce 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ name = "string_sum" crate-type = ["cdylib"] [dependencies.pyo3] -version = "0.12.2" +version = "0.12.3" features = ["extension-module"] ``` @@ -99,7 +99,7 @@ use it to run Python code, add `pyo3` to your `Cargo.toml` like this: ```toml [dependencies] -pyo3 = "0.12.2" +pyo3 = "0.12.3" ``` Example program displaying the value of `sys.version` and the current user name: diff --git a/pyo3-derive-backend/Cargo.toml b/pyo3-derive-backend/Cargo.toml index d7f29492..92bfead9 100644 --- a/pyo3-derive-backend/Cargo.toml +++ b/pyo3-derive-backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3-derive-backend" -version = "0.12.2" +version = "0.12.3" description = "Code generation for PyO3 package" authors = ["PyO3 Project and Contributors "] keywords = ["pyo3", "python", "cpython", "ffi"] diff --git a/pyo3cls/Cargo.toml b/pyo3cls/Cargo.toml index ece0719f..23df9fdf 100644 --- a/pyo3cls/Cargo.toml +++ b/pyo3cls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3cls" -version = "0.12.2" +version = "0.12.3" description = "Proc macros for PyO3 package" authors = ["PyO3 Project and Contributors "] keywords = ["pyo3", "python", "cpython", "ffi"] @@ -16,4 +16,4 @@ proc-macro = true [dependencies] quote = "1" syn = { version = "1", features = ["full", "extra-traits"] } -pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.12.2" } +pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.12.3" } diff --git a/src/lib.rs b/src/lib.rs index 4009887b..bc14deab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ //! crate-type = ["cdylib"] //! //! [dependencies.pyo3] -//! version = "0.12.2" +//! version = "0.12.3" //! features = ["extension-module"] //! ``` //! @@ -109,7 +109,7 @@ //! //! ```toml //! [dependencies] -//! pyo3 = "0.12.2" +//! pyo3 = "0.12.3" //! ``` //! //! Example program displaying the value of `sys.version`: From 1539db762b51b46bd684a56e06cc09fcc7f8d8ca Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 12 Oct 2020 16:02:34 +0100 Subject: [PATCH 02/18] Remove unused `python3` feature --- CHANGELOG.md | 4 ++++ Cargo.toml | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61fc0415..60918324 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ PyO3 versions, please see the [migration guide](https://pyo3.rs/master/migration The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Removed +- Remove unused `python3` feature. [#1209](https://github.com/PyO3/pyo3/pull/1209) + ## [0.12.3] - 2020-10-12 ### Fixed - Fix support for Rust versions 1.39 to 1.44, broken by an incorrect internal update to paste 1.0 which was done in PyO3 0.12.2. [#1234](https://github.com/PyO3/pyo3/pull/1234) diff --git a/Cargo.toml b/Cargo.toml index c7f9f55e..776d9e87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,9 +38,6 @@ macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] # Optimizes PyObject to Vec conversion and so on. nightly = [] -# this is no longer needed internally, but setuptools-rust assumes this feature -python3 = [] - # Use this feature when building an extension module. # It tells the linker to keep the python symbols unresolved, # so that the module can also be used with statically linked python interpreters. From 70a378538345bf3317d5c149a8fd0f88fc341ddb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 11 Sep 2020 18:51:57 +0200 Subject: [PATCH 03/18] improve PyIterator docs a little --- src/types/iterator.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 0a5c3a80..4dac1f08 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -9,6 +9,10 @@ use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python}; /// Unlike other Python objects, this class includes a `Python<'p>` token /// so that `PyIterator` can implement the Rust `Iterator` trait. /// +/// This means that you can't use `PyIterator` in many places where other +/// types like `PyList` can automatically be extracted from objects, such +/// as function arguments. +/// /// # Example /// /// ```rust @@ -29,7 +33,9 @@ use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python}; pub struct PyIterator<'p>(&'p PyAny); impl<'p> PyIterator<'p> { - /// Constructs a `PyIterator` from a Python iterator object. + /// Constructs a `PyIterator` from a Python iterable object. + /// + /// Equivalent to Python's built-in `iter` function. pub fn from_object(py: Python<'p>, obj: &T) -> PyResult> where T: AsPyPointer, From 7b90a9b13ec0984dc3af889363430919b5890f20 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 11 Sep 2020 19:03:01 +0200 Subject: [PATCH 04/18] implement PyIterator without additional lifetime This lets us treat it no different from other types like PySequence. --- src/types/any.rs | 2 +- src/types/iterator.rs | 58 +++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/types/any.rs b/src/types/any.rs index cd973b00..e5f4ae84 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -362,7 +362,7 @@ impl PyAny { /// /// This is typically a new iterator but if the argument is an iterator, /// this returns itself. - pub fn iter(&self) -> PyResult { + pub fn iter(&self) -> PyResult<&PyIterator> { PyIterator::from_object(self.py(), self) } diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 4dac1f08..59506012 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -2,17 +2,12 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython -use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python}; +use crate::{ + ffi, AsPyPointer, PyAny, PyDowncastError, PyErr, PyNativeType, PyResult, PyTryFrom, Python, +}; /// A Python iterator object. /// -/// Unlike other Python objects, this class includes a `Python<'p>` token -/// so that `PyIterator` can implement the Rust `Iterator` trait. -/// -/// This means that you can't use `PyIterator` in many places where other -/// types like `PyList` can automatically be extracted from objects, such -/// as function arguments. -/// /// # Example /// /// ```rust @@ -30,31 +25,24 @@ use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python}; /// # } /// ``` #[derive(Debug)] -pub struct PyIterator<'p>(&'p PyAny); +#[repr(transparent)] +pub struct PyIterator(PyAny); +pyobject_native_type_named!(PyIterator); +pyobject_native_type_extract!(PyIterator); -impl<'p> PyIterator<'p> { +impl PyIterator { /// Constructs a `PyIterator` from a Python iterable object. /// /// Equivalent to Python's built-in `iter` function. - pub fn from_object(py: Python<'p>, obj: &T) -> PyResult> + pub fn from_object<'p, T>(py: Python<'p>, obj: &T) -> PyResult<&'p PyIterator> where T: AsPyPointer, { - let iter = unsafe { - // This looks suspicious, but is actually correct. Even though ptr is an owned - // reference, PyIterator takes ownership of the reference and decreases the count - // in its Drop implementation. - // - // Therefore we must use from_borrowed_ptr_or_err instead of from_owned_ptr_or_err so - // that the GILPool does not take ownership of the reference. - py.from_borrowed_ptr_or_err(ffi::PyObject_GetIter(obj.as_ptr()))? - }; - - Ok(PyIterator(iter)) + unsafe { py.from_owned_ptr_or_err(ffi::PyObject_GetIter(obj.as_ptr())) } } } -impl<'p> Iterator for PyIterator<'p> { +impl<'p> Iterator for &'p PyIterator { type Item = PyResult<&'p PyAny>; /// Retrieves the next item from an iterator. @@ -79,10 +67,26 @@ impl<'p> Iterator for PyIterator<'p> { } } -/// Dropping a `PyIterator` instance decrements the reference count on the object by 1. -impl<'p> Drop for PyIterator<'p> { - fn drop(&mut self) { - unsafe { ffi::Py_DECREF(self.0.as_ptr()) } +impl<'v> PyTryFrom<'v> for PyIterator { + fn try_from>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> { + let value = value.into(); + unsafe { + if ffi::PyIter_Check(value.as_ptr()) != 0 { + Ok(::try_from_unchecked(value)) + } else { + Err(PyDowncastError::new(value, "Iterator")) + } + } + } + + fn try_from_exact>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> { + ::try_from(value) + } + + #[inline] + unsafe fn try_from_unchecked>(value: V) -> &'v PyIterator { + let ptr = value.into() as *const _ as *const PyIterator; + &*ptr } } From 482ee3a8b202b824e3f8b85fa31e11cca94d42fa Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 12 Oct 2020 14:35:27 +0100 Subject: [PATCH 05/18] Add changelog entry and test --- CHANGELOG.md | 3 +++ src/types/iterator.rs | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60918324..7b4774cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- Change `PyIterator` to be consistent with other native types: it is now used as `&PyIterator` instead of `PyIterator<'a>`. [#1176](https://github.com/PyO3/pyo3/pull/1176) + ### Removed - Remove unused `python3` feature. [#1209](https://github.com/PyO3/pyo3/pull/1209) diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 59506012..4666d39e 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -96,8 +96,7 @@ mod tests { use crate::exceptions::PyTypeError; use crate::gil::GILPool; use crate::types::{PyDict, PyList}; - use crate::Python; - use crate::ToPyObject; + use crate::{Python, Py, PyAny, ToPyObject, PyTryFrom}; use indoc::indoc; #[test] @@ -199,4 +198,13 @@ mod tests { assert!(err.is_instance::(py)) } + + #[test] + fn iterator_try_from() { + let gil_guard = Python::acquire_gil(); + let py = gil_guard.python(); + let obj: Py = vec![10, 20].to_object(py).as_ref(py).iter().unwrap().into(); + let iter: &PyIterator = PyIterator::try_from(obj.as_ref(py)).unwrap(); + assert_eq!(obj, iter.into()); + } } From 1c84539ef9a84a5d2fdb66c6849c00322363fa17 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 12 Oct 2020 16:22:11 +0100 Subject: [PATCH 06/18] rustfmt --- src/types/iterator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 4666d39e..cc798ebf 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -96,7 +96,7 @@ mod tests { use crate::exceptions::PyTypeError; use crate::gil::GILPool; use crate::types::{PyDict, PyList}; - use crate::{Python, Py, PyAny, ToPyObject, PyTryFrom}; + use crate::{Py, PyAny, PyTryFrom, Python, ToPyObject}; use indoc::indoc; #[test] From 5f109743a765b2f3e9e4cffe8f37da22631cb4de Mon Sep 17 00:00:00 2001 From: konstin Date: Fri, 9 Oct 2020 14:52:07 +0200 Subject: [PATCH 07/18] Remove symbols that are removed in python 3.10 Those functions and variable are listed in https://docs.python.org/3.10/whatsnew/3.10.html#id4 --- CHANGELOG.md | 3 +++ src/ffi/ceval.rs | 5 ----- src/ffi/objectabstract.rs | 20 -------------------- src/ffi/unicodeobject.rs | 5 ----- 4 files changed, 3 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60918324..2ac87fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Hide docs of PyO3 private implementation details in `pyo3::class::methods`. [#1169](https://github.com/PyO3/pyo3/pull/1169) - Fix unnecessary rebuild on PATH changes when the python interpreter is provided by PYO3_PYTHON. [#1231](https://github.com/PyO3/pyo3/pull/1231) +### Removed +- Removed `PyUnicode_AsUnicodeCopy`, `PyUnicode_GetMax`, `_Py_CheckRecursionLimit`, `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_CheckReadBuffer` and `PyObject_AsWriteBuffer` which have been removed in python 3.10 + ## [0.12.1] - 2020-09-16 ### Fixed - Fix building for a 32-bit Python on 64-bit Windows with a 64-bit Rust toolchain. [#1179](https://github.com/PyO3/pyo3/pull/1179) diff --git a/src/ffi/ceval.rs b/src/ffi/ceval.rs index 20b4d0b3..21f4445f 100644 --- a/src/ffi/ceval.rs +++ b/src/ffi/ceval.rs @@ -49,11 +49,6 @@ extern "C" { fn _Py_CheckRecursiveCall(_where: *mut c_char) -> c_int; } -#[cfg_attr(windows, link(name = "pythonXY"))] -extern "C" { - static mut _Py_CheckRecursionLimit: c_int; -} - // TODO: Py_EnterRecursiveCall etc. #[cfg(Py_3_6)] pub type _PyFrameEvalFunction = diff --git a/src/ffi/objectabstract.rs b/src/ffi/objectabstract.rs index ce2f744d..76154c33 100644 --- a/src/ffi/objectabstract.rs +++ b/src/ffi/objectabstract.rs @@ -71,26 +71,6 @@ extern "C" { pub fn PyObject_SetItem(o: *mut PyObject, key: *mut PyObject, v: *mut PyObject) -> c_int; pub fn PyObject_DelItemString(o: *mut PyObject, key: *const c_char) -> c_int; pub fn PyObject_DelItem(o: *mut PyObject, key: *mut PyObject) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_AsCharBuffer")] - pub fn PyObject_AsCharBuffer( - obj: *mut PyObject, - buffer: *mut *const c_char, - buffer_len: *mut Py_ssize_t, - ) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_CheckReadBuffer")] - pub fn PyObject_CheckReadBuffer(obj: *mut PyObject) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_AsReadBuffer")] - pub fn PyObject_AsReadBuffer( - obj: *mut PyObject, - buffer: *mut *const c_void, - buffer_len: *mut Py_ssize_t, - ) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_AsWriteBuffer")] - pub fn PyObject_AsWriteBuffer( - obj: *mut PyObject, - buffer: *mut *mut c_void, - buffer_len: *mut Py_ssize_t, - ) -> c_int; } #[cfg(not(Py_LIMITED_API))] diff --git a/src/ffi/unicodeobject.rs b/src/ffi/unicodeobject.rs index 715bcf97..b454c4f4 100644 --- a/src/ffi/unicodeobject.rs +++ b/src/ffi/unicodeobject.rs @@ -96,9 +96,6 @@ extern "C" { index: Py_ssize_t, character: Py_UCS4, ) -> c_int; - #[cfg(not(Py_LIMITED_API))] - #[cfg_attr(PyPy, link_name = "PyPyUnicode_GetMax")] - pub fn PyUnicode_GetMax() -> Py_UNICODE; #[cfg_attr(PyPy, link_name = "PyPyUnicode_Resize")] pub fn PyUnicode_Resize(unicode: *mut *mut PyObject, length: Py_ssize_t) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyUnicode_FromEncodedObject")] @@ -462,6 +459,4 @@ extern "C" { pub fn PyUnicode_Format(format: *mut PyObject, args: *mut PyObject) -> *mut PyObject; pub fn PyUnicode_Contains(container: *mut PyObject, element: *mut PyObject) -> c_int; pub fn PyUnicode_IsIdentifier(s: *mut PyObject) -> c_int; - #[cfg(not(Py_LIMITED_API))] - pub fn PyUnicode_AsUnicodeCopy(unicode: *mut PyObject) -> *mut Py_UNICODE; } From 2c3ee2c97a0660b3d06691bcdd514b85ffc361e1 Mon Sep 17 00:00:00 2001 From: konstin Date: Fri, 9 Oct 2020 23:25:15 +0200 Subject: [PATCH 08/18] Update CHANGELOG.md Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com> --- CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac87fb8..8bea0fa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Removed -- Remove unused `python3` feature. [#1209](https://github.com/PyO3/pyo3/pull/1209) +- Remove deprecated ffi definitions `PyUnicode_AsUnicodeCopy`, `PyUnicode_GetMax`, `_Py_CheckRecursionLimit`, `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_CheckReadBuffer` and `PyObject_AsWriteBuffer`, which will be removed in Python 3.10. [#1217](https://github.com/PyO3/pyo3/pull/1217) +- Remove unused `python3` feature. [#1235](https://github.com/PyO3/pyo3/pull/1235) ## [0.12.3] - 2020-10-12 ### Fixed @@ -23,9 +24,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Hide docs of PyO3 private implementation details in `pyo3::class::methods`. [#1169](https://github.com/PyO3/pyo3/pull/1169) - Fix unnecessary rebuild on PATH changes when the python interpreter is provided by PYO3_PYTHON. [#1231](https://github.com/PyO3/pyo3/pull/1231) -### Removed -- Removed `PyUnicode_AsUnicodeCopy`, `PyUnicode_GetMax`, `_Py_CheckRecursionLimit`, `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_CheckReadBuffer` and `PyObject_AsWriteBuffer` which have been removed in python 3.10 - ## [0.12.1] - 2020-09-16 ### Fixed - Fix building for a 32-bit Python on 64-bit Windows with a 64-bit Rust toolchain. [#1179](https://github.com/PyO3/pyo3/pull/1179) From 007bfb7ab061cfd3285006a9b059bf967e128c9f Mon Sep 17 00:00:00 2001 From: Askaholic Date: Tue, 13 Oct 2020 14:02:14 -0800 Subject: [PATCH 09/18] Refactor py_expect_exception to also verify error string representation --- tests/common.rs | 15 ++++++++++++++- tests/test_arithmetics.rs | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/common.rs b/tests/common.rs index 4cd3c084..c110f5d4 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -16,9 +16,22 @@ macro_rules! py_expect_exception { let d = [(stringify!($val), &$val)].into_py_dict($py); let res = $py.run($code, None, Some(d)); - let err = res.unwrap_err(); + let err = res.expect_err(&format!("Did not raise {}", stringify!($err))); if !err.matches($py, $py.get_type::()) { panic!("Expected {} but got {:?}", stringify!($err), err) } + err + }}; + ($py:expr, $val:ident, $code:expr, $err:ident, $err_msg:expr) => {{ + let err = py_expect_exception!($py, $val, $code, $err); + assert_eq!( + err.instance($py) + .str() + .expect("error str() failed") + .to_str() + .expect("message was not valid utf8"), + $err_msg + ); + err }}; } diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs index f0c38724..5fd81952 100644 --- a/tests/test_arithmetics.rs +++ b/tests/test_arithmetics.rs @@ -604,7 +604,7 @@ mod return_not_implemented { c2, &format!("class Other: pass\nc2 {} Other()", operator), PyTypeError - ) + ); } fn _test_inplace_binary_operator(operator: &str, dunder: &str) { From 19889bc6b9a9c79d03b6b4a11293faf4aa19b73c Mon Sep 17 00:00:00 2001 From: Askaholic Date: Tue, 13 Oct 2020 13:38:21 -0800 Subject: [PATCH 10/18] Add argument name to TypeError messages caused during argument conversion --- pyo3-derive-backend/src/pymethod.rs | 10 ++++-- src/derive_utils.rs | 15 ++++++++- tests/test_pyfunction.rs | 51 +++++++++++++++++++++++++++++ tests/test_string.rs | 13 +++----- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 6afb66ef..a3d2f4ad 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -475,10 +475,14 @@ fn impl_arg_param( let ty = arg.ty; let name = arg.name; + let transform_error = quote! { + |e| pyo3::derive_utils::argument_extraction_error(_py, stringify!(#name), e) + }; if spec.is_args(&name) { return quote! { - let #arg_name = <#ty as pyo3::FromPyObject>::extract(_args.as_ref())?; + let #arg_name = <#ty as pyo3::FromPyObject>::extract(_args.as_ref()) + .map_err(#transform_error)?; }; } else if spec.is_kwargs(&name) { return quote! { @@ -518,7 +522,7 @@ fn impl_arg_param( quote! { let #mut_ _tmp: #target_ty = match #arg_value { - Some(_obj) => _obj.extract()?, + Some(_obj) => _obj.extract().map_err(#transform_error)?, None => #default, }; let #arg_name = #borrow_tmp; @@ -526,7 +530,7 @@ fn impl_arg_param( } else { quote! { let #arg_name = match #arg_value { - Some(_obj) => _obj.extract()?, + Some(_obj) => _obj.extract().map_err(#transform_error)?, None => #default, }; } diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 52e22fac..cd0f5bde 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -8,7 +8,7 @@ use crate::err::{PyErr, PyResult}; use crate::exceptions::PyTypeError; use crate::instance::PyNativeType; use crate::pyclass::{PyClass, PyClassThreadChecker}; -use crate::types::{PyAny, PyDict, PyModule, PyTuple}; +use crate::types::{PyAny, PyDict, PyModule, PyString, PyTuple}; use crate::{ffi, GILPool, IntoPy, PyCell, Python}; use std::cell::UnsafeCell; @@ -111,6 +111,19 @@ pub fn parse_fn_args<'p>( Ok((args, kwargs)) } +/// Add the argument name to the error message of an error which occurred during argument extraction +pub fn argument_extraction_error(py: Python, arg_name: &str, error: PyErr) -> PyErr { + if error.ptype(py) == py.get_type::() { + let reason = error + .instance(py) + .str() + .unwrap_or_else(|_| PyString::new(py, "")); + PyTypeError::new_err(format!("argument '{}': {}", arg_name, reason)) + } else { + error + } +} + /// `Sync` wrapper of `ffi::PyModuleDef`. #[doc(hidden)] pub struct ModuleDef(UnsafeCell); diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index 6f6bf44c..affb768a 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -119,3 +119,54 @@ fn test_raw_function() { .unwrap(); assert_eq!(res, "Some(true)"); } + +#[pyfunction] +fn conversion_error(str_arg: &str, int_arg: i64, tuple_arg: (&str, f64), option_arg: Option) { + println!( + "{:?} {:?} {:?} {:?}", + str_arg, int_arg, tuple_arg, option_arg + ); +} + +#[test] +fn test_conversion_error() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let conversion_error = wrap_pyfunction!(conversion_error)(py).unwrap(); + py_expect_exception!( + py, + conversion_error, + "conversion_error(None, None, None, None)", + PyTypeError, + "argument 'str_arg': Can't convert None to PyString" + ); + py_expect_exception!( + py, + conversion_error, + "conversion_error(100, None, None, None)", + PyTypeError, + "argument 'str_arg': Can't convert 100 to PyString" + ); + py_expect_exception!( + py, + conversion_error, + "conversion_error('string1', 'string2', None, None)", + PyTypeError, + "argument 'int_arg': 'str' object cannot be interpreted as an integer" + ); + py_expect_exception!( + py, + conversion_error, + "conversion_error('string1', -100, 'string2', None)", + PyTypeError, + "argument 'tuple_arg': Can't convert 'string2' to PyTuple" + ); + py_expect_exception!( + py, + conversion_error, + "conversion_error('string1', -100, ('string2', 10.), 'string3')", + PyTypeError, + "argument 'option_arg': 'str' object cannot be interpreted as an integer" + ); +} diff --git a/tests/test_string.rs b/tests/test_string.rs index 38d375b5..cde88592 100644 --- a/tests/test_string.rs +++ b/tests/test_string.rs @@ -1,5 +1,4 @@ use pyo3::prelude::*; -use pyo3::py_run; use pyo3::wrap_pyfunction; mod common; @@ -15,15 +14,11 @@ fn test_unicode_encode_error() { let py = gil.python(); let take_str = wrap_pyfunction!(take_str)(py).unwrap(); - py_run!( + py_expect_exception!( py, take_str, - r#" - try: - take_str('\ud800') - except UnicodeEncodeError as e: - error_msg = "'utf-8' codec can't encode character '\\ud800' in position 0: surrogates not allowed" - assert str(e) == error_msg - "# + "take_str('\\ud800')", + PyUnicodeEncodeError, + "'utf-8' codec can't encode character '\\ud800' in position 0: surrogates not allowed" ); } From 6724783395ddc3e85d2a223be7257ab108df4ac3 Mon Sep 17 00:00:00 2001 From: Askaholic Date: Tue, 13 Oct 2020 13:40:03 -0800 Subject: [PATCH 11/18] Change wording of PyDowncastError display implementation Displays type(obj) instead of repr(obj) and uses `cannot` instead of `can't` to be more consistent with existing python error messages. See discussion at #1212. --- guide/src/conversions/traits.md | 3 +-- pyo3-derive-backend/src/from_pyobject.rs | 6 +----- src/err/mod.rs | 7 ++----- tests/test_frompyobject.rs | 2 +- tests/test_pyfunction.rs | 6 +++--- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index 7d997538..d09a684f 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -171,8 +171,7 @@ enum RustyEnum { ``` If the input is neither a string nor an integer, the error message will be: -`"Can't convert to Union[str, int]"`, where `` is replaced by the type name and -`repr()` of the input object. +`"'' cannot be converted to 'Union[str, int]'"`. #### `#[derive(FromPyObject)]` Container Attributes - `pyo3(transparent)` diff --git a/pyo3-derive-backend/src/from_pyobject.rs b/pyo3-derive-backend/src/from_pyobject.rs index 8d8dc956..5ea0754d 100644 --- a/pyo3-derive-backend/src/from_pyobject.rs +++ b/pyo3-derive-backend/src/from_pyobject.rs @@ -73,11 +73,7 @@ impl<'a> Enum<'a> { quote!( #(#var_extracts)* let type_name = obj.get_type().name(); - let from = obj - .repr() - .map(|s| format!("{} ({})", s.to_string_lossy(), type_name)) - .unwrap_or_else(|_| type_name.to_string()); - let err_msg = format!("Can't convert {} to {}", from, #error_names); + let err_msg = format!("'{}' object cannot be converted to '{}'", type_name, #error_names); Err(::pyo3::exceptions::PyTypeError::new_err(err_msg)) ) } diff --git a/src/err/mod.rs b/src/err/mod.rs index 294cbe9b..461a1ad4 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -490,11 +490,8 @@ impl<'a> std::fmt::Display for PyDowncastError<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { write!( f, - "Can't convert {} to {}", - self.from - .repr() - .map(|s| s.to_string_lossy()) - .unwrap_or_else(|_| self.from.get_type().name()), + "'{}' object cannot be converted to '{}'", + self.from.get_type().name(), self.to ) } diff --git a/tests/test_frompyobject.rs b/tests/test_frompyobject.rs index 98f2d2b9..24a7322b 100644 --- a/tests/test_frompyobject.rs +++ b/tests/test_frompyobject.rs @@ -296,6 +296,6 @@ fn test_err_rename() { assert!(f.is_err()); assert_eq!( f.unwrap_err().to_string(), - "TypeError: Can't convert {} (dict) to Union[str, uint, int]" + "TypeError: 'dict' object cannot be converted to 'Union[str, uint, int]'" ); } diff --git a/tests/test_pyfunction.rs b/tests/test_pyfunction.rs index affb768a..1e088955 100644 --- a/tests/test_pyfunction.rs +++ b/tests/test_pyfunction.rs @@ -139,14 +139,14 @@ fn test_conversion_error() { conversion_error, "conversion_error(None, None, None, None)", PyTypeError, - "argument 'str_arg': Can't convert None to PyString" + "argument 'str_arg': 'NoneType' object cannot be converted to 'PyString'" ); py_expect_exception!( py, conversion_error, "conversion_error(100, None, None, None)", PyTypeError, - "argument 'str_arg': Can't convert 100 to PyString" + "argument 'str_arg': 'int' object cannot be converted to 'PyString'" ); py_expect_exception!( py, @@ -160,7 +160,7 @@ fn test_conversion_error() { conversion_error, "conversion_error('string1', -100, 'string2', None)", PyTypeError, - "argument 'tuple_arg': Can't convert 'string2' to PyTuple" + "argument 'tuple_arg': 'str' object cannot be converted to 'PyTuple'" ); py_expect_exception!( py, From 1d7034478c91951bd1ae35178c937b6397843d97 Mon Sep 17 00:00:00 2001 From: Askaholic Date: Thu, 15 Oct 2020 12:02:58 -0800 Subject: [PATCH 12/18] Add entries to changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6175856..98fceb83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Add argument names to `TypeError` messages generated by pymethod wrappers. [#1212](https://github.com/PyO3/pyo3/pull/1212) + ### Changed - Change `PyIterator` to be consistent with other native types: it is now used as `&PyIterator` instead of `PyIterator<'a>`. [#1176](https://github.com/PyO3/pyo3/pull/1176) +- Change formatting of `PyDowncastError` messages to be closer to Python's builtin error messages. [#1212](https://github.com/PyO3/pyo3/pull/1212) ### Removed - Remove deprecated ffi definitions `PyUnicode_AsUnicodeCopy`, `PyUnicode_GetMax`, `_Py_CheckRecursionLimit`, `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_CheckReadBuffer` and `PyObject_AsWriteBuffer`, which will be removed in Python 3.10. [#1217](https://github.com/PyO3/pyo3/pull/1217) From 4a916eb3ed3252de28785fc25caa721d30f3ef3e Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Fri, 16 Oct 2020 09:00:46 +0100 Subject: [PATCH 13/18] guide: fix broken link --- guide/src/conversions/tables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/conversions/tables.md b/guide/src/conversions/tables.md index be1750af..72768a6f 100644 --- a/guide/src/conversions/tables.md +++ b/guide/src/conversions/tables.md @@ -49,7 +49,7 @@ There are also a few special types related to the GIL and Rust-defined `#[pyclas | `PyRef` | A `#[pyclass]` borrowed immutably. | | `PyRefMut` | A `#[pyclass]` borrowed mutably. | -For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](class.md). +For more detail on accepting `#[pyclass]` values as function arguments, see [the section of this guide on Python Classes](../class.md). #### Using Rust library types vs Python-native types From d463d2dfd26afba5c254cbf9e0e19fddf0a91741 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 18 Oct 2020 10:14:06 +0100 Subject: [PATCH 14/18] examples: add Pyre --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b42156ce..def67db2 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ about this topic. * [wasmer-python](https://github.com/wasmerio/wasmer-python) _Python library to run WebAssembly binaries_ * [mocpy](https://github.com/cds-astro/mocpy) _Astronomical Python library offering data structures for describing any arbitrary coverage regions on the unit sphere_ * [tokenizers](https://github.com/huggingface/tokenizers/tree/master/bindings/python) _Python bindings to the Hugging Face tokenizers (NLP) written in Rust_ + * [pyre](https://github.com/Project-Dream-Weaver/Pyre) _Fast Python HTTP server written in Rust_ ## License From 265db337c2c5047c06eb0dd9b7a4ef55a1950028 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 18 Oct 2020 11:06:21 -0400 Subject: [PATCH 15/18] Fixes for PyIterator --- CHANGELOG.md | 1 + src/ffi/objectabstract.rs | 5 +++++ src/types/iterator.rs | 14 ++++++++++---- src/types/set.rs | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd5203f..0c4f4d7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added - Add support for building for CPython limited API. This required a few minor changes to runtime behaviour of of pyo3 `#[pyclass]` types. See the migration guide for full details. [#1152](https://github.com/PyO3/pyo3/pull/1152) - Add argument names to `TypeError` messages generated by pymethod wrappers. [#1212](https://github.com/PyO3/pyo3/pull/1212) diff --git a/src/ffi/objectabstract.rs b/src/ffi/objectabstract.rs index 76154c33..bead5ebf 100644 --- a/src/ffi/objectabstract.rs +++ b/src/ffi/objectabstract.rs @@ -143,6 +143,11 @@ pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int { }) as c_int } +#[cfg(all(Py_LIMITED_API, Py_3_8))] +extern "C" { + pub fn PyIter_Check(obj: *mut PyObject) -> c_int; +} + extern "C" { #[cfg_attr(PyPy, link_name = "PyPyIter_Next")] pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject; diff --git a/src/types/iterator.rs b/src/types/iterator.rs index cc798ebf..ea0f2dc9 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -2,9 +2,9 @@ // // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython -use crate::{ - ffi, AsPyPointer, PyAny, PyDowncastError, PyErr, PyNativeType, PyResult, PyTryFrom, Python, -}; +use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python}; +#[cfg(any(not(Py_LIMITED_API), Py_3_8))] +use crate::{PyDowncastError, PyTryFrom}; /// A Python iterator object. /// @@ -28,6 +28,7 @@ use crate::{ #[repr(transparent)] pub struct PyIterator(PyAny); pyobject_native_type_named!(PyIterator); +#[cfg(any(not(Py_LIMITED_API), Py_3_8))] pyobject_native_type_extract!(PyIterator); impl PyIterator { @@ -67,6 +68,8 @@ impl<'p> Iterator for &'p PyIterator { } } +// PyIter_Check does not exist in the limited API until 3.8 +#[cfg(any(not(Py_LIMITED_API), Py_3_8))] impl<'v> PyTryFrom<'v> for PyIterator { fn try_from>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> { let value = value.into(); @@ -96,7 +99,9 @@ mod tests { use crate::exceptions::PyTypeError; use crate::gil::GILPool; use crate::types::{PyDict, PyList}; - use crate::{Py, PyAny, PyTryFrom, Python, ToPyObject}; + #[cfg(any(not(Py_LIMITED_API), Py_3_8))] + use crate::{Py, PyAny, PyTryFrom}; + use crate::{Python, ToPyObject}; use indoc::indoc; #[test] @@ -200,6 +205,7 @@ mod tests { } #[test] + #[cfg(any(not(Py_LIMITED_API), Py_3_8))] fn iterator_try_from() { let gil_guard = Python::acquire_gil(); let py = gil_guard.python(); diff --git a/src/types/set.rs b/src/types/set.rs index d631c080..5593490a 100644 --- a/src/types/set.rs +++ b/src/types/set.rs @@ -118,8 +118,8 @@ impl PySet { } #[cfg(Py_LIMITED_API)] -pub struct PySetIterator<'py> { - it: PyIterator<'py>, +pub struct PySetIterator<'p> { + it: &'p PyIterator, } #[cfg(Py_LIMITED_API)] From 7ddcf45ec5dabc43b1ad7b6a91d219bc9f8225c6 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 18 Oct 2020 18:36:31 +0100 Subject: [PATCH 16/18] Fix lifetime soundness in freelist --- pyo3-derive-backend/src/pyclass.rs | 2 +- src/freelist.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index 558a6f6e..032935d5 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -267,7 +267,7 @@ fn impl_class( quote! { impl pyo3::freelist::PyClassWithFreeList for #cls { #[inline] - fn get_free_list(_py: pyo3::Python) -> &'static mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> { + fn get_free_list(_py: pyo3::Python) -> &mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> { static mut FREELIST: *mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> = 0 as *mut _; unsafe { if FREELIST.is_null() { diff --git a/src/freelist.rs b/src/freelist.rs index 65331773..5b56c337 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -12,7 +12,7 @@ use std::os::raw::c_void; /// The performance improvement applies to types that are often created and deleted in a row, /// so that they can benefit from a freelist. pub trait PyClassWithFreeList { - fn get_free_list(py: Python) -> &'static mut FreeList<*mut ffi::PyObject>; + fn get_free_list(py: Python) -> &mut FreeList<*mut ffi::PyObject>; } pub enum Slot { From bb3b8122aab1f945a6fcb3c11665cf236b70346e Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sun, 18 Oct 2020 13:41:33 -0400 Subject: [PATCH 17/18] Make get_free_list API sound Without having a Py (which ensures the GIL is held) multiple thraeds could call get_free_list concurrently. --- pyo3-derive-backend/src/pyclass.rs | 2 +- src/freelist.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index bc2fd66e..558a6f6e 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -267,7 +267,7 @@ fn impl_class( quote! { impl pyo3::freelist::PyClassWithFreeList for #cls { #[inline] - fn get_free_list() -> &'static mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> { + fn get_free_list(_py: pyo3::Python) -> &'static mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> { static mut FREELIST: *mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> = 0 as *mut _; unsafe { if FREELIST.is_null() { diff --git a/src/freelist.rs b/src/freelist.rs index 2f78e900..65331773 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -12,7 +12,7 @@ use std::os::raw::c_void; /// The performance improvement applies to types that are often created and deleted in a row, /// so that they can benefit from a freelist. pub trait PyClassWithFreeList { - fn get_free_list() -> &'static mut FreeList<*mut ffi::PyObject>; + fn get_free_list(py: Python) -> &'static mut FreeList<*mut ffi::PyObject>; } pub enum Slot { @@ -74,7 +74,7 @@ where unsafe fn new(py: Python, subtype: *mut ffi::PyTypeObject) -> *mut Self::Layout { // if subtype is not equal to this type, we cannot use the freelist if subtype == Self::type_object_raw(py) { - if let Some(obj) = ::get_free_list().pop() { + if let Some(obj) = ::get_free_list(py).pop() { ffi::PyObject_Init(obj, subtype); return obj as _; } @@ -91,7 +91,7 @@ where return; } - if let Some(obj) = ::get_free_list().insert(obj.as_ptr()) { + if let Some(obj) = ::get_free_list(py).insert(obj.as_ptr()) { match (*ffi::Py_TYPE(obj)).tp_free { Some(free) => free(obj as *mut c_void), None => tp_free_fallback(obj), From afd9b823d2a7d099af400354aaed38e5c90f6b2a Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 18 Oct 2020 19:02:27 +0100 Subject: [PATCH 18/18] Drop support for Python 3.5 --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 3 +++ README.md | 2 +- build.rs | 2 +- examples/rustapi_module/setup.py | 2 +- examples/rustapi_module/src/datetime.rs | 17 +++++------- .../rustapi_module/tests/test_datetime.py | 23 +++------------- examples/word-count/tox.ini | 6 ++--- src/exceptions.rs | 1 - src/ffi/ceval.rs | 6 +---- src/ffi/code.rs | 8 ------ src/ffi/datetime.rs | 26 +++++++++---------- src/ffi/dictobject.rs | 1 - src/ffi/fileobject.rs | 1 - src/ffi/genobject.rs | 8 ------ src/ffi/mod.rs | 2 -- src/ffi/osmodule.rs | 1 - src/ffi/pydebug.rs | 2 +- src/ffi/pyerrors.rs | 2 -- src/ffi/pylifecycle.rs | 1 - src/ffi/pystate.rs | 3 --- src/ffi/warnings.rs | 1 - src/types/datetime.rs | 12 ++++----- tests/test_datetime.rs | 8 ------ 24 files changed, 40 insertions(+), 100 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2886d489..4c4caaa4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: fail-fast: false # If one platform fails, allow the rest to keep testing. matrix: rust: [stable] - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, pypy3] + python-version: [3.6, 3.7, 3.8, 3.9, pypy3] platform: [ { os: "macOS-latest", python-architecture: "x64", rust-target: "x86_64-apple-darwin" }, { os: "ubuntu-latest", python-architecture: "x64", rust-target: "x86_64-unknown-linux-gnu" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 98fceb83..6c41bcf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Packaging +- Drop support for Python 3.5 (as it is now end-of-life). [#1250](https://github.com/PyO3/pyo3/pull/1250) + ### Added - Add argument names to `TypeError` messages generated by pymethod wrappers. [#1212](https://github.com/PyO3/pyo3/pull/1212) diff --git a/README.md b/README.md index def67db2..53ace4f3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste ## Usage -PyO3 supports Python 3.5 and up. The minimum required Rust version is 1.39.0. +PyO3 supports Python 3.6 and up. The minimum required Rust version is 1.39.0. Building with PyPy is also possible (via cpyext) for Python 3.6, targeted PyPy version is 7.3+. Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html). diff --git a/build.rs b/build.rs index 05d90f47..b77e4ad6 100644 --- a/build.rs +++ b/build.rs @@ -770,7 +770,7 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result { } if let Some(minor) = interpreter_config.version.minor { - for i in 5..(minor + 1) { + for i in 6..(minor + 1) { println!("cargo:rustc-cfg=Py_3_{}", i); flags += format!("CFG_Py_3_{},", i).as_ref(); } diff --git a/examples/rustapi_module/setup.py b/examples/rustapi_module/setup.py index f1fe9002..c4a51c58 100644 --- a/examples/rustapi_module/setup.py +++ b/examples/rustapi_module/setup.py @@ -59,7 +59,7 @@ class CargoModifiedSdist(SdistCommand): def get_py_version_cfgs(): # For now each Cfg Py_3_X flag is interpreted as "at least 3.X" version = sys.version_info[0:2] - py3_min = 5 + py3_min = 6 out_cfg = [] for minor in range(py3_min, version[1] + 1): out_cfg.append("--cfg=Py_3_%d" % minor) diff --git a/examples/rustapi_module/src/datetime.rs b/examples/rustapi_module/src/datetime.rs index 062dc958..72fba0f0 100644 --- a/examples/rustapi_module/src/datetime.rs +++ b/examples/rustapi_module/src/datetime.rs @@ -42,7 +42,7 @@ fn make_time<'p>( ) } -#[cfg(Py_3_6)] +#[cfg(not(PyPy))] #[pyfunction] fn time_with_fold<'p>( py: Python<'p>, @@ -77,7 +77,7 @@ fn get_time_tuple<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple { ) } -#[cfg(all(Py_3_6, not(PyPy)))] +#[cfg(not(PyPy))] #[pyfunction] fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple { PyTuple::new( @@ -156,7 +156,7 @@ fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple { ) } -#[cfg(all(Py_3_6, not(PyPy)))] +#[cfg(not(PyPy))] #[pyfunction] fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple { PyTuple::new( @@ -226,15 +226,12 @@ fn datetime(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(get_datetime_tuple, m)?)?; m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?; - // Python 3.6+ functions - #[cfg(Py_3_6)] + // Functions not supported by PyPy + #[cfg(not(PyPy))] { m.add_function(wrap_pyfunction!(time_with_fold, m)?)?; - #[cfg(not(PyPy))] - { - m.add_function(wrap_pyfunction!(get_time_tuple_fold, m)?)?; - m.add_function(wrap_pyfunction!(get_datetime_tuple_fold, m)?)?; - } + m.add_function(wrap_pyfunction!(get_time_tuple_fold, m)?)?; + m.add_function(wrap_pyfunction!(get_datetime_tuple_fold, m)?)?; } m.add_function(wrap_pyfunction!(issue_219, m)?)?; diff --git a/examples/rustapi_module/tests/test_datetime.py b/examples/rustapi_module/tests/test_datetime.py index fa9cb89a..421dec70 100644 --- a/examples/rustapi_module/tests/test_datetime.py +++ b/examples/rustapi_module/tests/test_datetime.py @@ -69,18 +69,6 @@ else: MAX_DATETIME = pdt.datetime(9999, 12, 31, 18, 59, 59) PYPY = platform.python_implementation() == "PyPy" -HAS_FOLD = getattr(pdt.datetime, "fold", False) and not PYPY - - -xfail_date_bounds = pytest.mark.xfail( - sys.version_info < (3, 6), - reason="Date bounds were not checked in the C constructor prior to version 3.6", -) - -xfail_macos_datetime_bounds = pytest.mark.xfail( - sys.version_info < (3, 6) and platform.system() == "Darwin", - reason="Unclearly failing. See https://github.com/PyO3/pyo3/pull/830 for more.", -) # Tests @@ -96,13 +84,11 @@ def test_date_accessors(d): assert act == exp -@xfail_date_bounds def test_invalid_date_fails(): with pytest.raises(ValueError): rdt.make_date(2017, 2, 30) -@xfail_macos_datetime_bounds @given(d=st.dates(MIN_DATETIME.date(), MAX_DATETIME.date())) def test_date_from_timestamp(d): if PYPY and d < pdt.date(1900, 1, 1): @@ -136,7 +122,7 @@ def test_time(t): assert act == exp -@pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") +@pytest.mark.xfail(PYPY, reason="Feature not available on PyPy") @given(t=st.times()) def test_time_fold(t): t_nofold = t.replace(fold=0) @@ -149,7 +135,7 @@ def test_time_fold(t): assert act == exp -@pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") +@pytest.mark.xfail(PYPY, reason="Feature not available on PyPy") @pytest.mark.parametrize("fold", [False, True]) def test_time_fold(fold): t = rdt.time_with_fold(0, 0, 0, 0, None, fold) @@ -165,7 +151,6 @@ def test_invalid_time_fails_xfail(args): rdt.make_time(*args) -@xfail_date_bounds @pytest.mark.parametrize( "args", [ @@ -218,7 +203,7 @@ def test_datetime_tuple(dt): assert act == exp -@pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") +@pytest.mark.xfail(PYPY, reason="Feature not available on PyPy") @given(dt=st.datetimes()) def test_datetime_tuple_fold(dt): dt_fold = dt.replace(fold=1) @@ -231,7 +216,6 @@ def test_datetime_tuple_fold(dt): assert act == exp -@xfail_date_bounds def test_invalid_datetime_fails(): with pytest.raises(ValueError): rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0) @@ -242,7 +226,6 @@ def test_datetime_typeerror(): rdt.make_datetime("2011", 1, 1, 0, 0, 0, 0) -@xfail_macos_datetime_bounds @given(dt=st.datetimes(MIN_DATETIME, MAX_DATETIME)) @example(dt=pdt.datetime(1970, 1, 2, 0, 0)) def test_datetime_from_timestamp(dt): diff --git a/examples/word-count/tox.ini b/examples/word-count/tox.ini index 7a7d545d..1bc625bf 100644 --- a/examples/word-count/tox.ini +++ b/examples/word-count/tox.ini @@ -1,10 +1,10 @@ [tox] -envlist = py35, - py36, +envlist = py36, py37, py38, + py39, pypy3 -minversion = 3.4.0 +minversion = 3.6.0 skip_missing_interpreters = true isolated_build = true diff --git a/src/exceptions.rs b/src/exceptions.rs index 8bed1fb0..97483d41 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -308,7 +308,6 @@ impl_native_exception!( impl_native_exception!(PyOSError, OSError, PyExc_OSError, ffi::PyOSErrorObject); impl_native_exception!(PyImportError, ImportError, PyExc_ImportError); -#[cfg(Py_3_6)] impl_native_exception!( PyModuleNotFoundError, ModuleNotFoundError, diff --git a/src/ffi/ceval.rs b/src/ffi/ceval.rs index 21f4445f..31b00d7b 100644 --- a/src/ffi/ceval.rs +++ b/src/ffi/ceval.rs @@ -1,4 +1,3 @@ -#[cfg(Py_3_6)] use crate::ffi::code::FreeFunc; use crate::ffi::object::PyObject; use crate::ffi::pystate::PyThreadState; @@ -49,8 +48,7 @@ extern "C" { fn _Py_CheckRecursiveCall(_where: *mut c_char) -> c_int; } -// TODO: Py_EnterRecursiveCall etc. -#[cfg(Py_3_6)] +// TODO: Py_EnterRecursiveCall etc pub type _PyFrameEvalFunction = extern "C" fn(*mut crate::ffi::PyFrameObject, c_int) -> *mut PyObject; @@ -59,12 +57,10 @@ extern "C" { pub fn PyEval_GetFuncDesc(arg1: *mut PyObject) -> *const c_char; pub fn PyEval_GetCallStats(arg1: *mut PyObject) -> *mut PyObject; pub fn PyEval_EvalFrame(arg1: *mut crate::ffi::PyFrameObject) -> *mut PyObject; - #[cfg(Py_3_6)] pub fn _PyEval_EvalFrameDefault( arg1: *mut crate::ffi::PyFrameObject, exc: c_int, ) -> *mut PyObject; - #[cfg(Py_3_6)] pub fn _PyEval_RequestCodeExtraIndex(func: FreeFunc) -> c_int; pub fn PyEval_EvalFrameEx(f: *mut crate::ffi::PyFrameObject, exc: c_int) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")] diff --git a/src/ffi/code.rs b/src/ffi/code.rs index aadc835f..643e0652 100644 --- a/src/ffi/code.rs +++ b/src/ffi/code.rs @@ -14,7 +14,6 @@ pub struct PyCodeObject { pub co_nlocals: c_int, pub co_stacksize: c_int, pub co_flags: c_int, - #[cfg(Py_3_6)] pub co_firstlineno: c_int, pub co_code: *mut PyObject, pub co_consts: *mut PyObject, @@ -25,12 +24,9 @@ pub struct PyCodeObject { pub co_cell2arg: *mut c_uchar, pub co_filename: *mut PyObject, pub co_name: *mut PyObject, - #[cfg(not(Py_3_6))] - pub co_firstlineno: c_int, pub co_lnotab: *mut PyObject, pub co_zombieframe: *mut c_void, pub co_weakreflist: *mut PyObject, - #[cfg(Py_3_6)] pub co_extra: *mut c_void, #[cfg(Py_3_8)] pub co_opcache_map: *mut c_uchar, @@ -70,8 +66,6 @@ pub const CO_FUTURE_BARRY_AS_BDFL: c_int = 0x4_0000; pub const CO_FUTURE_GENERATOR_STOP: c_int = 0x8_0000; pub const CO_MAXBLOCKS: usize = 20; - -#[cfg(Py_3_6)] pub type FreeFunc = extern "C" fn(*mut c_void) -> c_void; #[cfg_attr(windows, link(name = "pythonXY"))] @@ -80,13 +74,11 @@ extern "C" { } extern "C" { - #[cfg(Py_3_6)] pub fn _PyCode_GetExtra( code: *mut PyObject, index: Py_ssize_t, extra: *const *mut c_void, ) -> c_int; - #[cfg(Py_3_6)] pub fn _PyCode_SetExtra(code: *mut PyObject, index: Py_ssize_t, extra: *mut c_void) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyCode_New")] diff --git a/src/ffi/datetime.rs b/src/ffi/datetime.rs index e26dc034..8ed85eaa 100644 --- a/src/ffi/datetime.rs +++ b/src/ffi/datetime.rs @@ -29,7 +29,7 @@ pub struct PyDateTime_CAPI { pub TimeType: *mut PyTypeObject, pub DeltaType: *mut PyTypeObject, pub TZInfoType: *mut PyTypeObject, - #[cfg(Py_3_7)] + #[cfg(all(Py_3_7, not(PyPy)))] pub TimeZone_UTC: *mut PyObject, #[cfg_attr(PyPy, link_name = "_PyPyDate_FromDate")] pub Date_FromDate: unsafe extern "C" fn( @@ -67,7 +67,7 @@ pub struct PyDateTime_CAPI { normalize: c_int, cls: *mut PyTypeObject, ) -> *mut PyObject, - #[cfg(Py_3_7)] + #[cfg(all(Py_3_7, not(PyPy)))] pub TimeZone_FromTimeZone: unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, @@ -80,7 +80,7 @@ pub struct PyDateTime_CAPI { // Defined for PyPy as `PyDate_FromTimestamp` pub Date_FromTimestamp: unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, - #[cfg(Py_3_6)] + #[cfg(not(PyPy))] pub DateTime_FromDateAndTimeAndFold: unsafe extern "C" fn( year: c_int, month: c_int, @@ -93,7 +93,7 @@ pub struct PyDateTime_CAPI { fold: c_int, cls: *mut PyTypeObject, ) -> *mut PyObject, - #[cfg(Py_3_6)] + #[cfg(not(PyPy))] pub Time_FromTimeAndFold: unsafe extern "C" fn( hour: c_int, minute: c_int, @@ -154,6 +154,7 @@ const _PyDateTime_DATETIME_DATASIZE: usize = 10; /// Structure representing a `datetime.date` pub struct PyDateTime_Date { pub ob_base: PyObject, + #[cfg(not(PyPy))] pub hashcode: Py_hash_t, pub hastzinfo: c_char, pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], @@ -164,10 +165,12 @@ pub struct PyDateTime_Date { /// Structure representing a `datetime.time` pub struct PyDateTime_Time { pub ob_base: PyObject, + #[cfg(not(PyPy))] pub hashcode: Py_hash_t, pub hastzinfo: c_char, + #[cfg(not(PyPy))] pub data: [c_uchar; _PyDateTime_TIME_DATASIZE], - #[cfg(Py_3_6)] + #[cfg(not(PyPy))] pub fold: c_uchar, pub tzinfo: *mut PyObject, } @@ -177,10 +180,12 @@ pub struct PyDateTime_Time { /// Structure representing a `datetime.datetime` pub struct PyDateTime_DateTime { pub ob_base: PyObject, + #[cfg(not(PyPy))] pub hashcode: Py_hash_t, pub hastzinfo: c_char, + #[cfg(not(PyPy))] pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], - #[cfg(Py_3_6)] + #[cfg(not(PyPy))] pub fold: c_uchar, pub tzinfo: *mut PyObject, } @@ -190,6 +195,7 @@ pub struct PyDateTime_DateTime { /// Structure representing a `datetime.timedelta` pub struct PyDateTime_Delta { pub ob_base: PyObject, + #[cfg(not(PyPy))] pub hashcode: Py_hash_t, pub days: c_int, pub seconds: c_int, @@ -381,7 +387,6 @@ macro_rules! _PyDateTime_GET_MICROSECOND { }; } -#[cfg(Py_3_6)] #[cfg(not(PyPy))] macro_rules! _PyDateTime_GET_FOLD { ($o: expr) => { @@ -429,13 +434,10 @@ pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int { _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) } -#[cfg(Py_3_6)] #[inline] #[cfg(not(PyPy))] /// Retrieve the fold component of a `PyDateTime_DateTime`. /// Returns a signed integer in the interval `[0, 1]` -/// -/// Added in version Python 3.6 pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar { _PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime) } @@ -482,12 +484,10 @@ pub unsafe fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int { _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_Time), 0) } -#[cfg(all(Py_3_6, not(PyPy)))] +#[cfg(not(PyPy))] #[inline] /// Retrieve the fold component of a `PyDateTime_Time`. /// Returns a signed integer in the interval `[0, 1]` -/// -/// Added in version Python 3.6 pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar { _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time) } diff --git a/src/ffi/dictobject.rs b/src/ffi/dictobject.rs index 4a1ba8e1..fa37ed1e 100644 --- a/src/ffi/dictobject.rs +++ b/src/ffi/dictobject.rs @@ -30,7 +30,6 @@ pub struct PyDictKeysObject { pub struct PyDictObject { pub ob_base: PyObject, pub ma_used: Py_ssize_t, - #[cfg(Py_3_6)] pub ma_version_tag: u64, pub ma_keys: *mut PyDictKeysObject, pub ma_values: *mut *mut PyObject, diff --git a/src/ffi/fileobject.rs b/src/ffi/fileobject.rs index 1a2562b6..61307496 100644 --- a/src/ffi/fileobject.rs +++ b/src/ffi/fileobject.rs @@ -27,7 +27,6 @@ extern "C" { #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { pub static mut Py_FileSystemDefaultEncoding: *const c_char; - #[cfg(Py_3_6)] pub static mut Py_FileSystemDefaultEncodeErrors: *const c_char; pub static mut Py_HasFileSystemDefaultEncoding: c_int; } diff --git a/src/ffi/genobject.rs b/src/ffi/genobject.rs index f67fe54c..af7a936b 100644 --- a/src/ffi/genobject.rs +++ b/src/ffi/genobject.rs @@ -58,20 +58,12 @@ pub unsafe fn PyCoroWrapper_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut _PyCoroWrapper_Type) } -#[cfg(Py_3_6)] #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { pub static mut PyAsyncGen_Type: PyTypeObject; } -#[cfg(Py_3_6)] #[inline] pub unsafe fn PyAsyncGen_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyAsyncGen_Type) } - -#[cfg(not(Py_3_6))] -#[inline] -pub unsafe fn PyAsyncGen_Check(_op: *mut PyObject) -> c_int { - 0 -} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 4015b2cd..5182a695 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -34,7 +34,6 @@ pub use self::moduleobject::*; pub use self::object::*; pub use self::objectabstract::*; pub use self::objimpl::*; -#[cfg(Py_3_6)] pub use self::osmodule::*; pub use self::pyarena::*; pub use self::pycapsule::*; @@ -124,7 +123,6 @@ mod pythonrun; // TODO some functions need to be moved to pylifecycle mod ceval; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod import; mod intrcheck; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 -#[cfg(Py_3_6)] mod osmodule; mod sysmodule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 diff --git a/src/ffi/osmodule.rs b/src/ffi/osmodule.rs index 35e4c3f7..f15682bd 100644 --- a/src/ffi/osmodule.rs +++ b/src/ffi/osmodule.rs @@ -1,4 +1,3 @@ -// This header is new in Python 3.6 use crate::ffi::object::PyObject; extern "C" { diff --git a/src/ffi/pydebug.rs b/src/ffi/pydebug.rs index 6422716d..1ebda05e 100644 --- a/src/ffi/pydebug.rs +++ b/src/ffi/pydebug.rs @@ -32,6 +32,6 @@ extern "C" { #[cfg_attr(PyPy, link_name = "PyPy_HashRandomizationFlag")] pub static mut Py_HashRandomizationFlag: c_int; pub static mut Py_IsolatedFlag: c_int; - #[cfg(all(Py_3_6, windows))] + #[cfg(windows)] pub static mut Py_LegacyWindowsStdioFlag: c_int; } diff --git a/src/ffi/pyerrors.rs b/src/ffi/pyerrors.rs index e88fc240..7a36bcb0 100644 --- a/src/ffi/pyerrors.rs +++ b/src/ffi/pyerrors.rs @@ -201,7 +201,6 @@ extern "C" { pub static mut PyExc_OSError: *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyExc_ImportError")] pub static mut PyExc_ImportError: *mut PyObject; - #[cfg(Py_3_6)] #[cfg_attr(PyPy, link_name = "PyPyExc_ModuleNotFoundError")] pub static mut PyExc_ModuleNotFoundError: *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyExc_IndexError")] @@ -339,7 +338,6 @@ extern "C" { ) -> *mut PyObject; #[cfg_attr(PyPy, link_name = "PyPyErr_Format")] pub fn PyErr_Format(exception: *mut PyObject, format: *const c_char, ...) -> *mut PyObject; - #[cfg(Py_3_6)] pub fn PyErr_SetImportErrorSubclass( arg1: *mut PyObject, arg2: *mut PyObject, diff --git a/src/ffi/pylifecycle.rs b/src/ffi/pylifecycle.rs index c5e36138..d3c92385 100644 --- a/src/ffi/pylifecycle.rs +++ b/src/ffi/pylifecycle.rs @@ -6,7 +6,6 @@ extern "C" { pub fn Py_Initialize(); pub fn Py_InitializeEx(arg1: c_int); pub fn Py_Finalize(); - #[cfg(Py_3_6)] pub fn Py_FinalizeEx() -> c_int; #[cfg_attr(PyPy, link_name = "PyPy_IsInitialized")] diff --git a/src/ffi/pystate.rs b/src/ffi/pystate.rs index 2b883d97..1eaad51d 100644 --- a/src/ffi/pystate.rs +++ b/src/ffi/pystate.rs @@ -1,17 +1,14 @@ -#[cfg(Py_3_6)] use crate::ffi::ceval::_PyFrameEvalFunction; use crate::ffi::moduleobject::PyModuleDef; use crate::ffi::object::PyObject; use std::os::raw::{c_int, c_long}; -#[cfg(Py_3_6)] pub const MAX_CO_EXTRA_USERS: c_int = 255; #[repr(C)] #[derive(Copy, Clone)] pub struct PyInterpreterState { pub ob_base: PyObject, - #[cfg(Py_3_6)] pub eval_frame: _PyFrameEvalFunction, } diff --git a/src/ffi/warnings.rs b/src/ffi/warnings.rs index c70dfccd..2e2731cc 100644 --- a/src/ffi/warnings.rs +++ b/src/ffi/warnings.rs @@ -16,7 +16,6 @@ extern "C" { format: *const c_char, ... ) -> c_int; - #[cfg(Py_3_6)] pub fn PyErr_ResourceWarning( source: *mut PyObject, stack_level: Py_ssize_t, diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 6d2696ab..89dbeb20 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -11,7 +11,7 @@ use crate::ffi; use crate::ffi::datetime::{PyDateTime_FromTimestamp, PyDate_FromTimestamp}; use crate::ffi::PyDateTimeAPI; use crate::ffi::{PyDateTime_Check, PyDate_Check, PyDelta_Check, PyTZInfo_Check, PyTime_Check}; -#[cfg(all(Py_3_6, not(PyPy)))] +#[cfg(not(PyPy))] use crate::ffi::{PyDateTime_DATE_GET_FOLD, PyDateTime_TIME_GET_FOLD}; use crate::ffi::{ PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MICROSECOND, PyDateTime_DATE_GET_MINUTE, @@ -57,7 +57,7 @@ pub trait PyTimeAccess { fn get_minute(&self) -> u8; fn get_second(&self) -> u8; fn get_microsecond(&self) -> u32; - #[cfg(all(Py_3_6, not(PyPy)))] + #[cfg(not(PyPy))] fn get_fold(&self) -> u8; } @@ -223,7 +223,7 @@ impl PyTimeAccess for PyDateTime { unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 } } - #[cfg(all(Py_3_6, not(PyPy)))] + #[cfg(not(PyPy))] fn get_fold(&self) -> u8 { unsafe { PyDateTime_DATE_GET_FOLD(self.as_ptr()) as u8 } } @@ -262,10 +262,8 @@ impl PyTime { } } - #[cfg(Py_3_6)] + #[cfg(not(PyPy))] /// Alternate constructor that takes a `fold` argument - /// - /// First available in Python 3.6. pub fn new_with_fold<'p>( py: Python<'p>, hour: u8, @@ -307,7 +305,7 @@ impl PyTimeAccess for PyTime { unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 } } - #[cfg(all(Py_3_6, not(PyPy)))] + #[cfg(not(PyPy))] fn get_fold(&self) -> u8 { unsafe { PyDateTime_TIME_GET_FOLD(self.as_ptr()) as u8 } } diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index f3114838..bbdff7fa 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -121,7 +121,6 @@ fn test_datetime_utc() { assert_approx_eq!(offset, 0f32); } -#[cfg(Py_3_6)] static INVALID_DATES: &[(i32, u8, u8)] = &[ (-1, 1, 1), (0, 1, 1), @@ -134,16 +133,13 @@ static INVALID_DATES: &[(i32, u8, u8)] = &[ (2018, 1, 32), ]; -#[cfg(Py_3_6)] static INVALID_TIMES: &[(u8, u8, u8, u32)] = &[(25, 0, 0, 0), (255, 0, 0, 0), (0, 60, 0, 0), (0, 0, 61, 0)]; -#[cfg(Py_3_6)] #[test] fn test_pydate_out_of_bounds() { use pyo3::types::PyDate; - // This test is an XFAIL on Python < 3.6 until bounds checking is implemented let gil = Python::acquire_gil(); let py = gil.python(); for val in INVALID_DATES { @@ -153,12 +149,10 @@ fn test_pydate_out_of_bounds() { } } -#[cfg(Py_3_6)] #[test] fn test_pytime_out_of_bounds() { use pyo3::types::PyTime; - // This test is an XFAIL on Python < 3.6 until bounds checking is implemented let gil = Python::acquire_gil(); let py = gil.python(); for val in INVALID_TIMES { @@ -168,13 +162,11 @@ fn test_pytime_out_of_bounds() { } } -#[cfg(Py_3_6)] #[test] fn test_pydatetime_out_of_bounds() { use pyo3::types::PyDateTime; use std::iter; - // This test is an XFAIL on Python < 3.6 until bounds checking is implemented let gil = Python::acquire_gil(); let py = gil.python(); let valid_time = (0, 0, 0, 0);