Merge pull request #1245 from alex/abi3-merge-master

Merge master into abi3 branch, resolving conflicts
This commit is contained in:
David Hewitt 2020-10-19 22:11:47 +01:00 committed by GitHub
commit f74b64993e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 222 additions and 206 deletions

View file

@ -45,7 +45,7 @@ jobs:
fail-fast: false # If one platform fails, allow the rest to keep testing. fail-fast: false # If one platform fails, allow the rest to keep testing.
matrix: matrix:
rust: [stable] 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: [ platform: [
{ os: "macOS-latest", python-architecture: "x64", rust-target: "x86_64-apple-darwin" }, { 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" }, { os: "ubuntu-latest", python-architecture: "x64", rust-target: "x86_64-unknown-linux-gnu" },

View file

@ -6,14 +6,24 @@ 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). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
### Packaging
- Drop support for Python 3.5 (as it is now end-of-life). [#1250](https://github.com/PyO3/pyo3/pull/1250)
### Added ### 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 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)
### Changed ### Changed
- Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152) - Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
- `#[pyclass(subclass)]` is now required for subclassing from Rust (was previously just required for subclassing from Python). [#1152](https://github.com/PyO3/pyo3/pull/1152) - `#[pyclass(subclass)]` is now required for subclassing from Rust (was previously just required for subclassing from Python). [#1152](https://github.com/PyO3/pyo3/pull/1152)
- 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)
## [Unreleased] ### 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)
- Remove unused `python3` feature. [#1235](https://github.com/PyO3/pyo3/pull/1235)
## [0.12.3] - 2020-10-12
### Fixed ### 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) - 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)
@ -523,7 +533,8 @@ Yanked
### Added ### Added
- Initial release - 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.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.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 [0.12.0]: https://github.com/pyo3/pyo3/compare/v0.11.1...v0.12.0

View file

@ -1,6 +1,6 @@
[package] [package]
name = "pyo3" name = "pyo3"
version = "0.12.2" version = "0.12.3"
description = "Bindings to Python interpreter" description = "Bindings to Python interpreter"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"] authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
readme = "README.md" readme = "README.md"
@ -23,7 +23,7 @@ parking_lot = "0.11.0"
num-bigint = { version = "0.3", optional = true } num-bigint = { version = "0.3", optional = true }
num-complex = { version = "0.3", optional = true } num-complex = { version = "0.3", optional = true }
paste = { version = "0.1.6", 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 } unindent = { version = "0.1.4", optional = true }
hashbrown = { version = "0.9", optional = true } hashbrown = { version = "0.9", optional = true }
@ -42,9 +42,6 @@ abi3 = []
# Optimizes PyObject to Vec conversion and so on. # Optimizes PyObject to Vec conversion and so on.
nightly = [] nightly = []
# this is no longer needed internally, but setuptools-rust assumes this feature
python3 = []
# Use this feature when building an extension module. # Use this feature when building an extension module.
# It tells the linker to keep the python symbols unresolved, # It tells the linker to keep the python symbols unresolved,
# so that the module can also be used with statically linked python interpreters. # so that the module can also be used with statically linked python interpreters.

View file

@ -20,5 +20,7 @@ lint: fmt clippy
publish: test publish: test
cargo publish --manifest-path pyo3-derive-backend/Cargo.toml cargo publish --manifest-path pyo3-derive-backend/Cargo.toml
sleep 10 # wait for crates.io to update
cargo publish --manifest-path pyo3cls/Cargo.toml cargo publish --manifest-path pyo3cls/Cargo.toml
sleep 10 # wait for crates.io to update
cargo publish cargo publish

View file

@ -18,7 +18,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste
## Usage ## 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+. 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). Please refer to the [pypy section in the guide](https://pyo3.rs/master/pypy.html).
@ -48,7 +48,7 @@ name = "string_sum"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies.pyo3] [dependencies.pyo3]
version = "0.12.2" version = "0.12.3"
features = ["extension-module"] features = ["extension-module"]
``` ```
@ -99,7 +99,7 @@ use it to run Python code, add `pyo3` to your `Cargo.toml` like this:
```toml ```toml
[dependencies] [dependencies]
pyo3 = "0.12.2" pyo3 = "0.12.3"
``` ```
Example program displaying the value of `sys.version` and the current user name: Example program displaying the value of `sys.version` and the current user name:
@ -155,6 +155,7 @@ about this topic.
* [wasmer-python](https://github.com/wasmerio/wasmer-python) _Python library to run WebAssembly binaries_ * [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_ * [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_ * [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 ## License

View file

@ -775,7 +775,7 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<String> {
} }
if let Some(minor) = interpreter_config.version.minor { 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); println!("cargo:rustc-cfg=Py_3_{}", i);
flags += format!("CFG_Py_3_{},", i).as_ref(); flags += format!("CFG_Py_3_{},", i).as_ref();
} }

View file

@ -59,7 +59,7 @@ class CargoModifiedSdist(SdistCommand):
def get_py_version_cfgs(): def get_py_version_cfgs():
# For now each Cfg Py_3_X flag is interpreted as "at least 3.X" # For now each Cfg Py_3_X flag is interpreted as "at least 3.X"
version = sys.version_info[0:2] version = sys.version_info[0:2]
py3_min = 5 py3_min = 6
out_cfg = [] out_cfg = []
for minor in range(py3_min, version[1] + 1): for minor in range(py3_min, version[1] + 1):
out_cfg.append("--cfg=Py_3_%d" % minor) out_cfg.append("--cfg=Py_3_%d" % minor)

View file

@ -42,7 +42,7 @@ fn make_time<'p>(
) )
} }
#[cfg(Py_3_6)] #[cfg(not(PyPy))]
#[pyfunction] #[pyfunction]
fn time_with_fold<'p>( fn time_with_fold<'p>(
py: Python<'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] #[pyfunction]
fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple { fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> &'p PyTuple {
PyTuple::new( 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] #[pyfunction]
fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple { fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> &'p PyTuple {
PyTuple::new( 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!(get_datetime_tuple, m)?)?;
m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?; m.add_function(wrap_pyfunction!(datetime_from_timestamp, m)?)?;
// Python 3.6+ functions // Functions not supported by PyPy
#[cfg(Py_3_6)] #[cfg(not(PyPy))]
{ {
m.add_function(wrap_pyfunction!(time_with_fold, m)?)?; 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)?)?; m.add_function(wrap_pyfunction!(issue_219, m)?)?;

View file

@ -70,18 +70,6 @@ else:
MAX_DATETIME = pdt.datetime(9999, 12, 31, 18, 59, 59) MAX_DATETIME = pdt.datetime(9999, 12, 31, 18, 59, 59)
PYPY = platform.python_implementation() == "PyPy" 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 # Tests
@ -97,13 +85,11 @@ def test_date_accessors(d):
assert act == exp assert act == exp
@xfail_date_bounds
def test_invalid_date_fails(): def test_invalid_date_fails():
with pytest.raises(ValueError): with pytest.raises(ValueError):
rdt.make_date(2017, 2, 30) rdt.make_date(2017, 2, 30)
@xfail_macos_datetime_bounds
@given(d=st.dates(MIN_DATETIME.date(), MAX_DATETIME.date())) @given(d=st.dates(MIN_DATETIME.date(), MAX_DATETIME.date()))
def test_date_from_timestamp(d): def test_date_from_timestamp(d):
if PYPY and d < pdt.date(1900, 1, 1): if PYPY and d < pdt.date(1900, 1, 1):
@ -137,7 +123,7 @@ def test_time(t):
assert act == exp 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()) @given(t=st.times())
def test_time_fold(t): def test_time_fold(t):
t_nofold = t.replace(fold=0) t_nofold = t.replace(fold=0)
@ -150,7 +136,7 @@ def test_time_fold(t):
assert act == exp 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]) @pytest.mark.parametrize("fold", [False, True])
def test_time_fold(fold): def test_time_fold(fold):
t = rdt.time_with_fold(0, 0, 0, 0, None, fold) t = rdt.time_with_fold(0, 0, 0, 0, None, fold)
@ -166,7 +152,6 @@ def test_invalid_time_fails_xfail(args):
rdt.make_time(*args) rdt.make_time(*args)
@xfail_date_bounds
@pytest.mark.parametrize( @pytest.mark.parametrize(
"args", "args",
[ [
@ -219,7 +204,7 @@ def test_datetime_tuple(dt):
assert act == exp 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()) @given(dt=st.datetimes())
def test_datetime_tuple_fold(dt): def test_datetime_tuple_fold(dt):
dt_fold = dt.replace(fold=1) dt_fold = dt.replace(fold=1)
@ -232,7 +217,6 @@ def test_datetime_tuple_fold(dt):
assert act == exp assert act == exp
@xfail_date_bounds
def test_invalid_datetime_fails(): def test_invalid_datetime_fails():
with pytest.raises(ValueError): with pytest.raises(ValueError):
rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0) rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0)
@ -243,7 +227,6 @@ def test_datetime_typeerror():
rdt.make_datetime("2011", 1, 1, 0, 0, 0, 0) rdt.make_datetime("2011", 1, 1, 0, 0, 0, 0)
@xfail_macos_datetime_bounds
@given(dt=st.datetimes(MIN_DATETIME, MAX_DATETIME)) @given(dt=st.datetimes(MIN_DATETIME, MAX_DATETIME))
@example(dt=pdt.datetime(1970, 1, 2, 0, 0)) @example(dt=pdt.datetime(1970, 1, 2, 0, 0))
def test_datetime_from_timestamp(dt): def test_datetime_from_timestamp(dt):

View file

@ -1,10 +1,10 @@
[tox] [tox]
envlist = py35, envlist = py36,
py36,
py37, py37,
py38, py38,
py39,
pypy3 pypy3
minversion = 3.4.0 minversion = 3.6.0
skip_missing_interpreters = true skip_missing_interpreters = true
isolated_build = true isolated_build = true

View file

@ -49,7 +49,7 @@ There are also a few special types related to the GIL and Rust-defined `#[pyclas
| `PyRef<T>` | A `#[pyclass]` borrowed immutably. | | `PyRef<T>` | A `#[pyclass]` borrowed immutably. |
| `PyRefMut<T>` | A `#[pyclass]` borrowed mutably. | | `PyRefMut<T>` | 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 #### Using Rust library types vs Python-native types

View file

@ -171,8 +171,7 @@ enum RustyEnum {
``` ```
If the input is neither a string nor an integer, the error message will be: If the input is neither a string nor an integer, the error message will be:
`"Can't convert <INPUT> to Union[str, int]"`, where `<INPUT>` is replaced by the type name and `"'<INPUT_TYPE>' cannot be converted to 'Union[str, int]'"`.
`repr()` of the input object.
#### `#[derive(FromPyObject)]` Container Attributes #### `#[derive(FromPyObject)]` Container Attributes
- `pyo3(transparent)` - `pyo3(transparent)`

View file

@ -1,6 +1,6 @@
[package] [package]
name = "pyo3-derive-backend" name = "pyo3-derive-backend"
version = "0.12.2" version = "0.12.3"
description = "Code generation for PyO3 package" description = "Code generation for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"] authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"] keywords = ["pyo3", "python", "cpython", "ffi"]

View file

@ -73,11 +73,7 @@ impl<'a> Enum<'a> {
quote!( quote!(
#(#var_extracts)* #(#var_extracts)*
let type_name = obj.get_type().name()?; let type_name = obj.get_type().name()?;
let from = obj let err_msg = format!("'{}' object cannot be converted to '{}'", type_name, #error_names);
.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);
Err(::pyo3::exceptions::PyTypeError::new_err(err_msg)) Err(::pyo3::exceptions::PyTypeError::new_err(err_msg))
) )
} }

View file

@ -267,7 +267,7 @@ fn impl_class(
quote! { quote! {
impl pyo3::freelist::PyClassWithFreeList for #cls { impl pyo3::freelist::PyClassWithFreeList for #cls {
#[inline] #[inline]
fn get_free_list() -> &'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 _; static mut FREELIST: *mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> = 0 as *mut _;
unsafe { unsafe {
if FREELIST.is_null() { if FREELIST.is_null() {

View file

@ -475,10 +475,14 @@ fn impl_arg_param(
let ty = arg.ty; let ty = arg.ty;
let name = arg.name; let name = arg.name;
let transform_error = quote! {
|e| pyo3::derive_utils::argument_extraction_error(_py, stringify!(#name), e)
};
if spec.is_args(&name) { if spec.is_args(&name) {
return quote! { 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) { } else if spec.is_kwargs(&name) {
return quote! { return quote! {
@ -518,7 +522,7 @@ fn impl_arg_param(
quote! { quote! {
let #mut_ _tmp: #target_ty = match #arg_value { let #mut_ _tmp: #target_ty = match #arg_value {
Some(_obj) => _obj.extract()?, Some(_obj) => _obj.extract().map_err(#transform_error)?,
None => #default, None => #default,
}; };
let #arg_name = #borrow_tmp; let #arg_name = #borrow_tmp;
@ -526,7 +530,7 @@ fn impl_arg_param(
} else { } else {
quote! { quote! {
let #arg_name = match #arg_value { let #arg_name = match #arg_value {
Some(_obj) => _obj.extract()?, Some(_obj) => _obj.extract().map_err(#transform_error)?,
None => #default, None => #default,
}; };
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "pyo3cls" name = "pyo3cls"
version = "0.12.2" version = "0.12.3"
description = "Proc macros for PyO3 package" description = "Proc macros for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"] authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"] keywords = ["pyo3", "python", "cpython", "ffi"]
@ -16,4 +16,4 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1" quote = "1"
syn = { version = "1", features = ["full", "extra-traits"] } 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" }

View file

@ -8,7 +8,7 @@ use crate::err::{PyErr, PyResult};
use crate::exceptions::PyTypeError; use crate::exceptions::PyTypeError;
use crate::instance::PyNativeType; use crate::instance::PyNativeType;
use crate::pyclass::{PyClass, PyClassThreadChecker}; 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 crate::{ffi, GILPool, IntoPy, PyCell, Python};
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
@ -111,6 +111,19 @@ pub fn parse_fn_args<'p>(
Ok((args, kwargs)) 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::<PyTypeError>() {
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`. /// `Sync` wrapper of `ffi::PyModuleDef`.
#[doc(hidden)] #[doc(hidden)]
pub struct ModuleDef(UnsafeCell<ffi::PyModuleDef>); pub struct ModuleDef(UnsafeCell<ffi::PyModuleDef>);

View file

@ -490,12 +490,8 @@ impl<'a> std::fmt::Display for PyDowncastError<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!( write!(
f, f,
"Can't convert {} to {}", "'{}' object cannot be converted to '{}'",
self.from self.from.get_type().name().map_err(|_| std::fmt::Error)?,
.repr()
.map(|s| s.to_string_lossy())
.or_else(|_| self.from.get_type().name().map(|s| s.into()))
.map_err(|_| std::fmt::Error)?,
self.to self.to
) )
} }

View file

@ -308,7 +308,6 @@ impl_native_exception!(
impl_native_exception!(PyOSError, OSError, PyExc_OSError, ffi::PyOSErrorObject); impl_native_exception!(PyOSError, OSError, PyExc_OSError, ffi::PyOSErrorObject);
impl_native_exception!(PyImportError, ImportError, PyExc_ImportError); impl_native_exception!(PyImportError, ImportError, PyExc_ImportError);
#[cfg(Py_3_6)]
impl_native_exception!( impl_native_exception!(
PyModuleNotFoundError, PyModuleNotFoundError,
ModuleNotFoundError, ModuleNotFoundError,

View file

@ -1,4 +1,4 @@
#[cfg(all(Py_3_6, not(Py_LIMITED_API)))] #[cfg(not(Py_LIMITED_API))]
use crate::ffi::code::FreeFunc; use crate::ffi::code::FreeFunc;
use crate::ffi::object::PyObject; use crate::ffi::object::PyObject;
use crate::ffi::pystate::PyThreadState; use crate::ffi::pystate::PyThreadState;
@ -49,13 +49,7 @@ extern "C" {
fn _Py_CheckRecursiveCall(_where: *mut c_char) -> c_int; fn _Py_CheckRecursiveCall(_where: *mut c_char) -> c_int;
} }
#[cfg_attr(windows, link(name = "pythonXY"))] // TODO: Py_EnterRecursiveCall etc
extern "C" {
static mut _Py_CheckRecursionLimit: c_int;
}
// TODO: Py_EnterRecursiveCall etc.
#[cfg(Py_3_6)]
pub type _PyFrameEvalFunction = pub type _PyFrameEvalFunction =
extern "C" fn(*mut crate::ffi::PyFrameObject, c_int) -> *mut PyObject; extern "C" fn(*mut crate::ffi::PyFrameObject, c_int) -> *mut PyObject;
@ -64,12 +58,11 @@ extern "C" {
pub fn PyEval_GetFuncDesc(arg1: *mut PyObject) -> *const c_char; pub fn PyEval_GetFuncDesc(arg1: *mut PyObject) -> *const c_char;
pub fn PyEval_GetCallStats(arg1: *mut PyObject) -> *mut PyObject; pub fn PyEval_GetCallStats(arg1: *mut PyObject) -> *mut PyObject;
pub fn PyEval_EvalFrame(arg1: *mut crate::ffi::PyFrameObject) -> *mut PyObject; pub fn PyEval_EvalFrame(arg1: *mut crate::ffi::PyFrameObject) -> *mut PyObject;
#[cfg(Py_3_6)]
pub fn _PyEval_EvalFrameDefault( pub fn _PyEval_EvalFrameDefault(
arg1: *mut crate::ffi::PyFrameObject, arg1: *mut crate::ffi::PyFrameObject,
exc: c_int, exc: c_int,
) -> *mut PyObject; ) -> *mut PyObject;
#[cfg(all(Py_3_6, not(Py_LIMITED_API)))] #[cfg(not(Py_LIMITED_API))]
pub fn _PyEval_RequestCodeExtraIndex(func: FreeFunc) -> c_int; pub fn _PyEval_RequestCodeExtraIndex(func: FreeFunc) -> c_int;
pub fn PyEval_EvalFrameEx(f: *mut crate::ffi::PyFrameObject, exc: c_int) -> *mut PyObject; pub fn PyEval_EvalFrameEx(f: *mut crate::ffi::PyFrameObject, exc: c_int) -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")] #[cfg_attr(PyPy, link_name = "PyPyEval_SaveThread")]

View file

@ -14,7 +14,6 @@ pub struct PyCodeObject {
pub co_nlocals: c_int, pub co_nlocals: c_int,
pub co_stacksize: c_int, pub co_stacksize: c_int,
pub co_flags: c_int, pub co_flags: c_int,
#[cfg(Py_3_6)]
pub co_firstlineno: c_int, pub co_firstlineno: c_int,
pub co_code: *mut PyObject, pub co_code: *mut PyObject,
pub co_consts: *mut PyObject, pub co_consts: *mut PyObject,
@ -25,12 +24,9 @@ pub struct PyCodeObject {
pub co_cell2arg: *mut c_uchar, pub co_cell2arg: *mut c_uchar,
pub co_filename: *mut PyObject, pub co_filename: *mut PyObject,
pub co_name: *mut PyObject, pub co_name: *mut PyObject,
#[cfg(not(Py_3_6))]
pub co_firstlineno: c_int,
pub co_lnotab: *mut PyObject, pub co_lnotab: *mut PyObject,
pub co_zombieframe: *mut c_void, pub co_zombieframe: *mut c_void,
pub co_weakreflist: *mut PyObject, pub co_weakreflist: *mut PyObject,
#[cfg(Py_3_6)]
pub co_extra: *mut c_void, pub co_extra: *mut c_void,
#[cfg(Py_3_8)] #[cfg(Py_3_8)]
pub co_opcache_map: *mut c_uchar, 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_FUTURE_GENERATOR_STOP: c_int = 0x8_0000;
pub const CO_MAXBLOCKS: usize = 20; pub const CO_MAXBLOCKS: usize = 20;
#[cfg(Py_3_6)]
pub type FreeFunc = extern "C" fn(*mut c_void) -> c_void; pub type FreeFunc = extern "C" fn(*mut c_void) -> c_void;
#[cfg_attr(windows, link(name = "pythonXY"))] #[cfg_attr(windows, link(name = "pythonXY"))]
@ -80,13 +74,11 @@ extern "C" {
} }
extern "C" { extern "C" {
#[cfg(Py_3_6)]
pub fn _PyCode_GetExtra( pub fn _PyCode_GetExtra(
code: *mut PyObject, code: *mut PyObject,
index: Py_ssize_t, index: Py_ssize_t,
extra: *const *mut c_void, extra: *const *mut c_void,
) -> c_int; ) -> c_int;
#[cfg(Py_3_6)]
pub fn _PyCode_SetExtra(code: *mut PyObject, index: Py_ssize_t, extra: *mut c_void) -> c_int; pub fn _PyCode_SetExtra(code: *mut PyObject, index: Py_ssize_t, extra: *mut c_void) -> c_int;
#[cfg_attr(PyPy, link_name = "PyPyCode_New")] #[cfg_attr(PyPy, link_name = "PyPyCode_New")]

View file

@ -29,7 +29,7 @@ pub struct PyDateTime_CAPI {
pub TimeType: *mut PyTypeObject, pub TimeType: *mut PyTypeObject,
pub DeltaType: *mut PyTypeObject, pub DeltaType: *mut PyTypeObject,
pub TZInfoType: *mut PyTypeObject, pub TZInfoType: *mut PyTypeObject,
#[cfg(Py_3_7)] #[cfg(all(Py_3_7, not(PyPy)))]
pub TimeZone_UTC: *mut PyObject, pub TimeZone_UTC: *mut PyObject,
#[cfg_attr(PyPy, link_name = "_PyPyDate_FromDate")] #[cfg_attr(PyPy, link_name = "_PyPyDate_FromDate")]
pub Date_FromDate: unsafe extern "C" fn( pub Date_FromDate: unsafe extern "C" fn(
@ -67,7 +67,7 @@ pub struct PyDateTime_CAPI {
normalize: c_int, normalize: c_int,
cls: *mut PyTypeObject, cls: *mut PyTypeObject,
) -> *mut PyObject, ) -> *mut PyObject,
#[cfg(Py_3_7)] #[cfg(all(Py_3_7, not(PyPy)))]
pub TimeZone_FromTimeZone: pub TimeZone_FromTimeZone:
unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, 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` // Defined for PyPy as `PyDate_FromTimestamp`
pub Date_FromTimestamp: pub Date_FromTimestamp:
unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, 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( pub DateTime_FromDateAndTimeAndFold: unsafe extern "C" fn(
year: c_int, year: c_int,
month: c_int, month: c_int,
@ -93,7 +93,7 @@ pub struct PyDateTime_CAPI {
fold: c_int, fold: c_int,
cls: *mut PyTypeObject, cls: *mut PyTypeObject,
) -> *mut PyObject, ) -> *mut PyObject,
#[cfg(Py_3_6)] #[cfg(not(PyPy))]
pub Time_FromTimeAndFold: unsafe extern "C" fn( pub Time_FromTimeAndFold: unsafe extern "C" fn(
hour: c_int, hour: c_int,
minute: c_int, minute: c_int,
@ -154,6 +154,7 @@ const _PyDateTime_DATETIME_DATASIZE: usize = 10;
/// Structure representing a `datetime.date` /// Structure representing a `datetime.date`
pub struct PyDateTime_Date { pub struct PyDateTime_Date {
pub ob_base: PyObject, pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t, pub hashcode: Py_hash_t,
pub hastzinfo: c_char, pub hastzinfo: c_char,
pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], pub data: [c_uchar; _PyDateTime_DATE_DATASIZE],
@ -164,10 +165,12 @@ pub struct PyDateTime_Date {
/// Structure representing a `datetime.time` /// Structure representing a `datetime.time`
pub struct PyDateTime_Time { pub struct PyDateTime_Time {
pub ob_base: PyObject, pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t, pub hashcode: Py_hash_t,
pub hastzinfo: c_char, pub hastzinfo: c_char,
#[cfg(not(PyPy))]
pub data: [c_uchar; _PyDateTime_TIME_DATASIZE], pub data: [c_uchar; _PyDateTime_TIME_DATASIZE],
#[cfg(Py_3_6)] #[cfg(not(PyPy))]
pub fold: c_uchar, pub fold: c_uchar,
pub tzinfo: *mut PyObject, pub tzinfo: *mut PyObject,
} }
@ -177,10 +180,12 @@ pub struct PyDateTime_Time {
/// Structure representing a `datetime.datetime` /// Structure representing a `datetime.datetime`
pub struct PyDateTime_DateTime { pub struct PyDateTime_DateTime {
pub ob_base: PyObject, pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t, pub hashcode: Py_hash_t,
pub hastzinfo: c_char, pub hastzinfo: c_char,
#[cfg(not(PyPy))]
pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE],
#[cfg(Py_3_6)] #[cfg(not(PyPy))]
pub fold: c_uchar, pub fold: c_uchar,
pub tzinfo: *mut PyObject, pub tzinfo: *mut PyObject,
} }
@ -190,6 +195,7 @@ pub struct PyDateTime_DateTime {
/// Structure representing a `datetime.timedelta` /// Structure representing a `datetime.timedelta`
pub struct PyDateTime_Delta { pub struct PyDateTime_Delta {
pub ob_base: PyObject, pub ob_base: PyObject,
#[cfg(not(PyPy))]
pub hashcode: Py_hash_t, pub hashcode: Py_hash_t,
pub days: c_int, pub days: c_int,
pub seconds: c_int, pub seconds: c_int,
@ -381,7 +387,6 @@ macro_rules! _PyDateTime_GET_MICROSECOND {
}; };
} }
#[cfg(Py_3_6)]
#[cfg(not(PyPy))] #[cfg(not(PyPy))]
macro_rules! _PyDateTime_GET_FOLD { macro_rules! _PyDateTime_GET_FOLD {
($o: expr) => { ($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) _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE)
} }
#[cfg(Py_3_6)]
#[inline] #[inline]
#[cfg(not(PyPy))] #[cfg(not(PyPy))]
/// Retrieve the fold component of a `PyDateTime_DateTime`. /// Retrieve the fold component of a `PyDateTime_DateTime`.
/// Returns a signed integer in the interval `[0, 1]` /// 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 { pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar {
_PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime) _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) _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_Time), 0)
} }
#[cfg(all(Py_3_6, not(PyPy)))] #[cfg(not(PyPy))]
#[inline] #[inline]
/// Retrieve the fold component of a `PyDateTime_Time`. /// Retrieve the fold component of a `PyDateTime_Time`.
/// Returns a signed integer in the interval `[0, 1]` /// 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 { pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar {
_PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time) _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time)
} }

View file

@ -30,7 +30,6 @@ pub struct PyDictKeysObject {
pub struct PyDictObject { pub struct PyDictObject {
pub ob_base: PyObject, pub ob_base: PyObject,
pub ma_used: Py_ssize_t, pub ma_used: Py_ssize_t,
#[cfg(Py_3_6)]
pub ma_version_tag: u64, pub ma_version_tag: u64,
pub ma_keys: *mut PyDictKeysObject, pub ma_keys: *mut PyDictKeysObject,
pub ma_values: *mut *mut PyObject, pub ma_values: *mut *mut PyObject,

View file

@ -27,7 +27,6 @@ extern "C" {
#[cfg_attr(windows, link(name = "pythonXY"))] #[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" { extern "C" {
pub static mut Py_FileSystemDefaultEncoding: *const c_char; pub static mut Py_FileSystemDefaultEncoding: *const c_char;
#[cfg(Py_3_6)]
pub static mut Py_FileSystemDefaultEncodeErrors: *const c_char; pub static mut Py_FileSystemDefaultEncodeErrors: *const c_char;
pub static mut Py_HasFileSystemDefaultEncoding: c_int; pub static mut Py_HasFileSystemDefaultEncoding: c_int;
} }

View file

@ -58,20 +58,12 @@ pub unsafe fn PyCoroWrapper_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, &mut _PyCoroWrapper_Type) PyObject_TypeCheck(op, &mut _PyCoroWrapper_Type)
} }
#[cfg(Py_3_6)]
#[cfg_attr(windows, link(name = "pythonXY"))] #[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" { extern "C" {
pub static mut PyAsyncGen_Type: PyTypeObject; pub static mut PyAsyncGen_Type: PyTypeObject;
} }
#[cfg(Py_3_6)]
#[inline] #[inline]
pub unsafe fn PyAsyncGen_Check(op: *mut PyObject) -> c_int { pub unsafe fn PyAsyncGen_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, &mut PyAsyncGen_Type) PyObject_TypeCheck(op, &mut PyAsyncGen_Type)
} }
#[cfg(not(Py_3_6))]
#[inline]
pub unsafe fn PyAsyncGen_Check(_op: *mut PyObject) -> c_int {
0
}

View file

@ -34,7 +34,6 @@ pub use self::moduleobject::*;
pub use self::object::*; pub use self::object::*;
pub use self::objectabstract::*; pub use self::objectabstract::*;
pub use self::objimpl::*; pub use self::objimpl::*;
#[cfg(Py_3_6)]
pub use self::osmodule::*; pub use self::osmodule::*;
pub use self::pyarena::*; pub use self::pyarena::*;
pub use self::pycapsule::*; 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 ceval; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
mod import; mod import;
mod intrcheck; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod intrcheck; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5
#[cfg(Py_3_6)]
mod osmodule; mod osmodule;
mod sysmodule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod sysmodule; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5

View file

@ -71,26 +71,6 @@ extern "C" {
pub fn PyObject_SetItem(o: *mut PyObject, key: *mut PyObject, v: *mut PyObject) -> c_int; 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_DelItemString(o: *mut PyObject, key: *const c_char) -> c_int;
pub fn PyObject_DelItem(o: *mut PyObject, key: *mut PyObject) -> 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))] #[cfg(not(Py_LIMITED_API))]
@ -163,6 +143,11 @@ pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
}) as 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" { extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyIter_Next")] #[cfg_attr(PyPy, link_name = "PyPyIter_Next")]
pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject; pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject;

View file

@ -1,4 +1,3 @@
// This header is new in Python 3.6
use crate::ffi::object::PyObject; use crate::ffi::object::PyObject;
extern "C" { extern "C" {

View file

@ -32,6 +32,6 @@ extern "C" {
#[cfg_attr(PyPy, link_name = "PyPy_HashRandomizationFlag")] #[cfg_attr(PyPy, link_name = "PyPy_HashRandomizationFlag")]
pub static mut Py_HashRandomizationFlag: c_int; pub static mut Py_HashRandomizationFlag: c_int;
pub static mut Py_IsolatedFlag: c_int; pub static mut Py_IsolatedFlag: c_int;
#[cfg(all(Py_3_6, windows))] #[cfg(windows)]
pub static mut Py_LegacyWindowsStdioFlag: c_int; pub static mut Py_LegacyWindowsStdioFlag: c_int;
} }

View file

@ -201,7 +201,6 @@ extern "C" {
pub static mut PyExc_OSError: *mut PyObject; pub static mut PyExc_OSError: *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyExc_ImportError")] #[cfg_attr(PyPy, link_name = "PyPyExc_ImportError")]
pub static mut PyExc_ImportError: *mut PyObject; pub static mut PyExc_ImportError: *mut PyObject;
#[cfg(Py_3_6)]
#[cfg_attr(PyPy, link_name = "PyPyExc_ModuleNotFoundError")] #[cfg_attr(PyPy, link_name = "PyPyExc_ModuleNotFoundError")]
pub static mut PyExc_ModuleNotFoundError: *mut PyObject; pub static mut PyExc_ModuleNotFoundError: *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyExc_IndexError")] #[cfg_attr(PyPy, link_name = "PyPyExc_IndexError")]
@ -339,7 +338,6 @@ extern "C" {
) -> *mut PyObject; ) -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyErr_Format")] #[cfg_attr(PyPy, link_name = "PyPyErr_Format")]
pub fn PyErr_Format(exception: *mut PyObject, format: *const c_char, ...) -> *mut PyObject; pub fn PyErr_Format(exception: *mut PyObject, format: *const c_char, ...) -> *mut PyObject;
#[cfg(Py_3_6)]
pub fn PyErr_SetImportErrorSubclass( pub fn PyErr_SetImportErrorSubclass(
arg1: *mut PyObject, arg1: *mut PyObject,
arg2: *mut PyObject, arg2: *mut PyObject,

View file

@ -6,7 +6,6 @@ extern "C" {
pub fn Py_Initialize(); pub fn Py_Initialize();
pub fn Py_InitializeEx(arg1: c_int); pub fn Py_InitializeEx(arg1: c_int);
pub fn Py_Finalize(); pub fn Py_Finalize();
#[cfg(Py_3_6)]
pub fn Py_FinalizeEx() -> c_int; pub fn Py_FinalizeEx() -> c_int;
#[cfg_attr(PyPy, link_name = "PyPy_IsInitialized")] #[cfg_attr(PyPy, link_name = "PyPy_IsInitialized")]

View file

@ -1,17 +1,14 @@
#[cfg(Py_3_6)]
use crate::ffi::ceval::_PyFrameEvalFunction; use crate::ffi::ceval::_PyFrameEvalFunction;
use crate::ffi::moduleobject::PyModuleDef; use crate::ffi::moduleobject::PyModuleDef;
use crate::ffi::object::PyObject; use crate::ffi::object::PyObject;
use std::os::raw::{c_int, c_long}; use std::os::raw::{c_int, c_long};
#[cfg(Py_3_6)]
pub const MAX_CO_EXTRA_USERS: c_int = 255; pub const MAX_CO_EXTRA_USERS: c_int = 255;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct PyInterpreterState { pub struct PyInterpreterState {
pub ob_base: PyObject, pub ob_base: PyObject,
#[cfg(Py_3_6)]
pub eval_frame: _PyFrameEvalFunction, pub eval_frame: _PyFrameEvalFunction,
} }

View file

@ -96,9 +96,6 @@ extern "C" {
index: Py_ssize_t, index: Py_ssize_t,
character: Py_UCS4, character: Py_UCS4,
) -> c_int; ) -> 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")] #[cfg_attr(PyPy, link_name = "PyPyUnicode_Resize")]
pub fn PyUnicode_Resize(unicode: *mut *mut PyObject, length: Py_ssize_t) -> c_int; pub fn PyUnicode_Resize(unicode: *mut *mut PyObject, length: Py_ssize_t) -> c_int;
#[cfg_attr(PyPy, link_name = "PyPyUnicode_FromEncodedObject")] #[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_Format(format: *mut PyObject, args: *mut PyObject) -> *mut PyObject;
pub fn PyUnicode_Contains(container: *mut PyObject, element: *mut PyObject) -> c_int; pub fn PyUnicode_Contains(container: *mut PyObject, element: *mut PyObject) -> c_int;
pub fn PyUnicode_IsIdentifier(s: *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;
} }

View file

@ -16,7 +16,6 @@ extern "C" {
format: *const c_char, format: *const c_char,
... ...
) -> c_int; ) -> c_int;
#[cfg(Py_3_6)]
pub fn PyErr_ResourceWarning( pub fn PyErr_ResourceWarning(
source: *mut PyObject, source: *mut PyObject,
stack_level: Py_ssize_t, stack_level: Py_ssize_t,

View file

@ -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, /// The performance improvement applies to types that are often created and deleted in a row,
/// so that they can benefit from a freelist. /// so that they can benefit from a freelist.
pub trait PyClassWithFreeList { pub trait PyClassWithFreeList {
fn get_free_list() -> &'static mut FreeList<*mut ffi::PyObject>; fn get_free_list(py: Python) -> &mut FreeList<*mut ffi::PyObject>;
} }
pub enum Slot<T> { pub enum Slot<T> {
@ -74,7 +74,7 @@ where
unsafe fn new(py: Python, subtype: *mut ffi::PyTypeObject) -> *mut Self::Layout { 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 is not equal to this type, we cannot use the freelist
if subtype == Self::type_object_raw(py) { if subtype == Self::type_object_raw(py) {
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list().pop() { if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list(py).pop() {
ffi::PyObject_Init(obj, subtype); ffi::PyObject_Init(obj, subtype);
return obj as _; return obj as _;
} }
@ -86,7 +86,7 @@ where
(*self_).py_drop(py); (*self_).py_drop(py);
let obj = PyAny::from_borrowed_ptr_or_panic(py, self_ as _); let obj = PyAny::from_borrowed_ptr_or_panic(py, self_ as _);
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list().insert(obj.as_ptr()) { if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list(py).insert(obj.as_ptr()) {
match get_type_free(ffi::Py_TYPE(obj)) { match get_type_free(ffi::Py_TYPE(obj)) {
Some(free) => { Some(free) => {
let ty = ffi::Py_TYPE(obj); let ty = ffi::Py_TYPE(obj);

View file

@ -52,7 +52,7 @@
//! crate-type = ["cdylib"] //! crate-type = ["cdylib"]
//! //!
//! [dependencies.pyo3] //! [dependencies.pyo3]
//! version = "0.12.2" //! version = "0.12.3"
//! features = ["extension-module"] //! features = ["extension-module"]
//! ``` //! ```
//! //!
@ -109,7 +109,7 @@
//! //!
//! ```toml //! ```toml
//! [dependencies] //! [dependencies]
//! pyo3 = "0.12.2" //! pyo3 = "0.12.3"
//! ``` //! ```
//! //!
//! Example program displaying the value of `sys.version`: //! Example program displaying the value of `sys.version`:

View file

@ -362,7 +362,7 @@ impl PyAny {
/// ///
/// This is typically a new iterator but if the argument is an iterator, /// This is typically a new iterator but if the argument is an iterator,
/// this returns itself. /// this returns itself.
pub fn iter(&self) -> PyResult<PyIterator> { pub fn iter(&self) -> PyResult<&PyIterator> {
PyIterator::from_object(self.py(), self) PyIterator::from_object(self.py(), self)
} }

View file

@ -11,7 +11,7 @@ use crate::ffi;
use crate::ffi::datetime::{PyDateTime_FromTimestamp, PyDate_FromTimestamp}; use crate::ffi::datetime::{PyDateTime_FromTimestamp, PyDate_FromTimestamp};
use crate::ffi::PyDateTimeAPI; use crate::ffi::PyDateTimeAPI;
use crate::ffi::{PyDateTime_Check, PyDate_Check, PyDelta_Check, PyTZInfo_Check, PyTime_Check}; 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_FOLD, PyDateTime_TIME_GET_FOLD};
use crate::ffi::{ use crate::ffi::{
PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MICROSECOND, PyDateTime_DATE_GET_MINUTE, 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_minute(&self) -> u8;
fn get_second(&self) -> u8; fn get_second(&self) -> u8;
fn get_microsecond(&self) -> u32; fn get_microsecond(&self) -> u32;
#[cfg(all(Py_3_6, not(PyPy)))] #[cfg(not(PyPy))]
fn get_fold(&self) -> u8; fn get_fold(&self) -> u8;
} }
@ -223,7 +223,7 @@ impl PyTimeAccess for PyDateTime {
unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 } 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 { fn get_fold(&self) -> u8 {
unsafe { PyDateTime_DATE_GET_FOLD(self.as_ptr()) as 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 /// Alternate constructor that takes a `fold` argument
///
/// First available in Python 3.6.
pub fn new_with_fold<'p>( pub fn new_with_fold<'p>(
py: Python<'p>, py: Python<'p>,
hour: u8, hour: u8,
@ -307,7 +305,7 @@ impl PyTimeAccess for PyTime {
unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 } 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 { fn get_fold(&self) -> u8 {
unsafe { PyDateTime_TIME_GET_FOLD(self.as_ptr()) as u8 } unsafe { PyDateTime_TIME_GET_FOLD(self.as_ptr()) as u8 }
} }

View file

@ -3,12 +3,11 @@
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython // 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, PyErr, PyNativeType, PyResult, Python};
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::{PyDowncastError, PyTryFrom};
/// A Python iterator object. /// A Python iterator object.
/// ///
/// Unlike other Python objects, this class includes a `Python<'p>` token
/// so that `PyIterator` can implement the Rust `Iterator` trait.
///
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
@ -26,29 +25,25 @@ use crate::{ffi, AsPyPointer, PyAny, PyErr, PyNativeType, PyResult, Python};
/// # } /// # }
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct PyIterator<'p>(&'p PyAny); #[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<'p> PyIterator<'p> { impl PyIterator {
/// Constructs a `PyIterator` from a Python iterator object. /// Constructs a `PyIterator` from a Python iterable object.
pub fn from_object<T>(py: Python<'p>, obj: &T) -> PyResult<PyIterator<'p>> ///
/// Equivalent to Python's built-in `iter` function.
pub fn from_object<'p, T>(py: Python<'p>, obj: &T) -> PyResult<&'p PyIterator>
where where
T: AsPyPointer, T: AsPyPointer,
{ {
let iter = unsafe { unsafe { py.from_owned_ptr_or_err(ffi::PyObject_GetIter(obj.as_ptr())) }
// 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))
} }
} }
impl<'p> Iterator for PyIterator<'p> { impl<'p> Iterator for &'p PyIterator {
type Item = PyResult<&'p PyAny>; type Item = PyResult<&'p PyAny>;
/// Retrieves the next item from an iterator. /// Retrieves the next item from an iterator.
@ -73,10 +68,28 @@ impl<'p> Iterator for PyIterator<'p> {
} }
} }
/// Dropping a `PyIterator` instance decrements the reference count on the object by 1. // PyIter_Check does not exist in the limited API until 3.8
impl<'p> Drop for PyIterator<'p> { #[cfg(any(not(Py_LIMITED_API), Py_3_8))]
fn drop(&mut self) { impl<'v> PyTryFrom<'v> for PyIterator {
unsafe { ffi::Py_DECREF(self.0.as_ptr()) } fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> {
let value = value.into();
unsafe {
if ffi::PyIter_Check(value.as_ptr()) != 0 {
Ok(<PyIterator as PyTryFrom>::try_from_unchecked(value))
} else {
Err(PyDowncastError::new(value, "Iterator"))
}
}
}
fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> {
<PyIterator as PyTryFrom>::try_from(value)
}
#[inline]
unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PyIterator {
let ptr = value.into() as *const _ as *const PyIterator;
&*ptr
} }
} }
@ -86,8 +99,9 @@ mod tests {
use crate::exceptions::PyTypeError; use crate::exceptions::PyTypeError;
use crate::gil::GILPool; use crate::gil::GILPool;
use crate::types::{PyDict, PyList}; use crate::types::{PyDict, PyList};
use crate::Python; #[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::ToPyObject; use crate::{Py, PyAny, PyTryFrom};
use crate::{Python, ToPyObject};
use indoc::indoc; use indoc::indoc;
#[test] #[test]
@ -189,4 +203,14 @@ mod tests {
assert!(err.is_instance::<PyTypeError>(py)) assert!(err.is_instance::<PyTypeError>(py))
} }
#[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();
let obj: Py<PyAny> = 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());
}
} }

View file

@ -118,8 +118,8 @@ impl PySet {
} }
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
pub struct PySetIterator<'py> { pub struct PySetIterator<'p> {
it: PyIterator<'py>, it: &'p PyIterator,
} }
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]

View file

@ -16,9 +16,22 @@ macro_rules! py_expect_exception {
let d = [(stringify!($val), &$val)].into_py_dict($py); let d = [(stringify!($val), &$val)].into_py_dict($py);
let res = $py.run($code, None, Some(d)); 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::<pyo3::exceptions::$err>()) { if !err.matches($py, $py.get_type::<pyo3::exceptions::$err>()) {
panic!("Expected {} but got {:?}", stringify!($err), err) 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
}}; }};
} }

View file

@ -604,7 +604,7 @@ mod return_not_implemented {
c2, c2,
&format!("class Other: pass\nc2 {} Other()", operator), &format!("class Other: pass\nc2 {} Other()", operator),
PyTypeError PyTypeError
) );
} }
fn _test_inplace_binary_operator(operator: &str, dunder: &str) { fn _test_inplace_binary_operator(operator: &str, dunder: &str) {

View file

@ -121,7 +121,6 @@ fn test_datetime_utc() {
assert_approx_eq!(offset, 0f32); assert_approx_eq!(offset, 0f32);
} }
#[cfg(Py_3_6)]
static INVALID_DATES: &[(i32, u8, u8)] = &[ static INVALID_DATES: &[(i32, u8, u8)] = &[
(-1, 1, 1), (-1, 1, 1),
(0, 1, 1), (0, 1, 1),
@ -134,16 +133,13 @@ static INVALID_DATES: &[(i32, u8, u8)] = &[
(2018, 1, 32), (2018, 1, 32),
]; ];
#[cfg(Py_3_6)]
static INVALID_TIMES: &[(u8, u8, u8, u32)] = static INVALID_TIMES: &[(u8, u8, u8, u32)] =
&[(25, 0, 0, 0), (255, 0, 0, 0), (0, 60, 0, 0), (0, 0, 61, 0)]; &[(25, 0, 0, 0), (255, 0, 0, 0), (0, 60, 0, 0), (0, 0, 61, 0)];
#[cfg(Py_3_6)]
#[test] #[test]
fn test_pydate_out_of_bounds() { fn test_pydate_out_of_bounds() {
use pyo3::types::PyDate; use pyo3::types::PyDate;
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
for val in INVALID_DATES { for val in INVALID_DATES {
@ -153,12 +149,10 @@ fn test_pydate_out_of_bounds() {
} }
} }
#[cfg(Py_3_6)]
#[test] #[test]
fn test_pytime_out_of_bounds() { fn test_pytime_out_of_bounds() {
use pyo3::types::PyTime; use pyo3::types::PyTime;
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
for val in INVALID_TIMES { for val in INVALID_TIMES {
@ -168,13 +162,11 @@ fn test_pytime_out_of_bounds() {
} }
} }
#[cfg(Py_3_6)]
#[test] #[test]
fn test_pydatetime_out_of_bounds() { fn test_pydatetime_out_of_bounds() {
use pyo3::types::PyDateTime; use pyo3::types::PyDateTime;
use std::iter; use std::iter;
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
let valid_time = (0, 0, 0, 0); let valid_time = (0, 0, 0, 0);

View file

@ -296,6 +296,6 @@ fn test_err_rename() {
assert!(f.is_err()); assert!(f.is_err());
assert_eq!( assert_eq!(
f.unwrap_err().to_string(), 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]'"
); );
} }

View file

@ -135,3 +135,54 @@ fn test_raw_function() {
.unwrap(); .unwrap();
assert_eq!(res, "Some(true)"); assert_eq!(res, "Some(true)");
} }
#[pyfunction]
fn conversion_error(str_arg: &str, int_arg: i64, tuple_arg: (&str, f64), option_arg: Option<i64>) {
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': 'NoneType' object cannot be converted to 'PyString'"
);
py_expect_exception!(
py,
conversion_error,
"conversion_error(100, None, None, None)",
PyTypeError,
"argument 'str_arg': 'int' object cannot be converted 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': 'str' object cannot be converted 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"
);
}

View file

@ -1,5 +1,4 @@
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::py_run;
use pyo3::wrap_pyfunction; use pyo3::wrap_pyfunction;
mod common; mod common;
@ -15,15 +14,11 @@ fn test_unicode_encode_error() {
let py = gil.python(); let py = gil.python();
let take_str = wrap_pyfunction!(take_str)(py).unwrap(); let take_str = wrap_pyfunction!(take_str)(py).unwrap();
py_run!( py_expect_exception!(
py, py,
take_str, take_str,
r#" "take_str('\\ud800')",
try: PyUnicodeEncodeError,
take_str('\ud800') "'utf-8' codec can't encode character '\\ud800' in position 0: surrogates not allowed"
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
"#
); );
} }