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.
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" },

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).
## [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 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
- 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)
- 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
- 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
- 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

View file

@ -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 <https://github.com/PyO3>"]
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 }
@ -42,9 +42,6 @@ abi3 = []
# 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.

View file

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

View file

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

View file

@ -775,7 +775,7 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<String> {
}
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();
}

View file

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

View file

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

View file

@ -70,18 +70,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
@ -97,13 +85,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):
@ -137,7 +123,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)
@ -150,7 +136,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)
@ -166,7 +152,6 @@ def test_invalid_time_fails_xfail(args):
rdt.make_time(*args)
@xfail_date_bounds
@pytest.mark.parametrize(
"args",
[
@ -219,7 +204,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)
@ -232,7 +217,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)
@ -243,7 +227,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):

View file

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

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

View file

@ -171,8 +171,7 @@ enum RustyEnum {
```
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
`repr()` of the input object.
`"'<INPUT_TYPE>' cannot be converted to 'Union[str, int]'"`.
#### `#[derive(FromPyObject)]` Container Attributes
- `pyo3(transparent)`

View file

@ -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 <https://github.com/PyO3>"]
keywords = ["pyo3", "python", "cpython", "ffi"]

View file

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

View file

@ -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) -> &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() {

View file

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

View file

@ -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 <https://github.com/PyO3>"]
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" }

View file

@ -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::<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`.
#[doc(hidden)]
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> {
write!(
f,
"Can't convert {} to {}",
self.from
.repr()
.map(|s| s.to_string_lossy())
.or_else(|_| self.from.get_type().name().map(|s| s.into()))
.map_err(|_| std::fmt::Error)?,
"'{}' object cannot be converted to '{}'",
self.from.get_type().name().map_err(|_| std::fmt::Error)?,
self.to
)
}

View file

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

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::object::PyObject;
use crate::ffi::pystate::PyThreadState;
@ -49,13 +49,7 @@ 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)]
// TODO: Py_EnterRecursiveCall etc
pub type _PyFrameEvalFunction =
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_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(all(Py_3_6, not(Py_LIMITED_API)))]
#[cfg(not(Py_LIMITED_API))]
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")]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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_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))]
@ -163,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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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,
/// 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) -> &mut FreeList<*mut ffi::PyObject>;
}
pub enum Slot<T> {
@ -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) = <Self as PyClassWithFreeList>::get_free_list().pop() {
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list(py).pop() {
ffi::PyObject_Init(obj, subtype);
return obj as _;
}
@ -86,7 +86,7 @@ where
(*self_).py_drop(py);
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)) {
Some(free) => {
let ty = ffi::Py_TYPE(obj);

View file

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

View file

@ -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<PyIterator> {
pub fn iter(&self) -> PyResult<&PyIterator> {
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::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 }
}

View file

@ -3,12 +3,11 @@
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
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.
///
/// Unlike other Python objects, this class includes a `Python<'p>` token
/// so that `PyIterator` can implement the Rust `Iterator` trait.
///
/// # Example
///
/// ```rust
@ -26,29 +25,25 @@ 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);
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
pyobject_native_type_extract!(PyIterator);
impl<'p> PyIterator<'p> {
/// Constructs a `PyIterator` from a Python iterator object.
pub fn from_object<T>(py: Python<'p>, obj: &T) -> PyResult<PyIterator<'p>>
impl PyIterator {
/// Constructs a `PyIterator` from a Python iterable object.
///
/// Equivalent to Python's built-in `iter` function.
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.
@ -73,10 +68,28 @@ 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()) }
// 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<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::gil::GILPool;
use crate::types::{PyDict, PyList};
use crate::Python;
use crate::ToPyObject;
#[cfg(any(not(Py_LIMITED_API), Py_3_8))]
use crate::{Py, PyAny, PyTryFrom};
use crate::{Python, ToPyObject};
use indoc::indoc;
#[test]
@ -189,4 +203,14 @@ mod tests {
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)]
pub struct PySetIterator<'py> {
it: PyIterator<'py>,
pub struct PySetIterator<'p> {
it: &'p PyIterator,
}
#[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 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>()) {
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,
&format!("class Other: pass\nc2 {} Other()", operator),
PyTypeError
)
);
}
fn _test_inplace_binary_operator(operator: &str, dunder: &str) {

View file

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

View file

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

View file

@ -135,3 +135,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<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::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"
);
}