From 0518af741b4c962220c89b0b5cbfbe1d39c5a65f Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Sun, 14 Jan 2024 15:03:43 +0000 Subject: [PATCH 1/8] using PyLong_AsLongLong --- Cargo.toml | 3 ++- src/input/return_enums.rs | 19 ++++++++++++++++++- tests/validators/test_int.py | 15 +++++++++++---- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 283e364f9..69667d976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,8 @@ extension-module = ["pyo3/extension-module"] [profile.release] lto = "fat" codegen-units = 1 -strip = true +#strip = true +debug = true [profile.bench] debug = true diff --git a/src/input/return_enums.rs b/src/input/return_enums.rs index fa70880ca..87191026a 100644 --- a/src/input/return_enums.rs +++ b/src/input/return_enums.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::cmp::Ordering; +use std::ffi::c_longlong; use std::ops::Rem; use std::slice::Iter as SliceIter; use std::str::FromStr; @@ -1019,9 +1020,25 @@ impl<'a> Rem for &'a Int { } } +/// Extract an i64 from a python object, note that contrary to what +/// https://docs.python.org/3/c-api/long.html#c.PyLong_AsLongLong suggests, we are not calling +/// `__index__()` (`ffi::PyNumber_Index`) first, as it seems to make no difference +fn extract_i64(obj: &PyAny) -> Option { + let val: c_longlong = unsafe { ffi::PyLong_AsLongLong(obj.as_ptr()) }; + if unsafe { ffi::PyErr_Occurred() }.is_null() { + Some(val) + } else { + let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); + let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut(); + let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut(); + unsafe { ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback) }; + None + } +} + impl<'a> FromPyObject<'a> for Int { fn extract(obj: &'a PyAny) -> PyResult { - if let Ok(i) = obj.extract::() { + if let Some(i) = extract_i64(obj) { Ok(Int::I64(i)) } else if let Ok(b) = obj.extract::() { Ok(Int::Big(b)) diff --git a/tests/validators/test_int.py b/tests/validators/test_int.py index 80dd1cf73..35a13f6a7 100644 --- a/tests/validators/test_int.py +++ b/tests/validators/test_int.py @@ -29,6 +29,8 @@ ('123456789123456.00001', Err('Input should be a valid integer, unable to parse string as an integer')), (int(1e10), int(1e10)), (i64_max, i64_max), + (i64_max + 1, i64_max + 1), + (i64_max * 2, i64_max * 2), pytest.param( 12.5, Err('Input should be a valid integer, got a number with a fractional part [type=int_from_float'), @@ -106,10 +108,15 @@ def test_int(input_value, expected): @pytest.mark.parametrize( 'input_value,expected', [ - (Decimal('1'), 1), - (Decimal('1.0'), 1), - (i64_max, i64_max), - (i64_max + 1, i64_max + 1), + pytest.param(Decimal('1'), 1), + pytest.param(Decimal('1.0'), 1), + pytest.param(i64_max, i64_max, id='i64_max'), + pytest.param(i64_max + 1, i64_max + 1, id='i64_max+1'), + pytest.param( + -1, + Err('Input should be greater than 0 [type=greater_than, input_value=-1, input_type=int]'), + id='-1', + ), ( -i64_max + 1, Err('Input should be greater than 0 [type=greater_than, input_value=-9223372036854775806, input_type=int]'), From c9d748cd757f5a5f99bf268ff3833da574f934a0 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Sun, 14 Jan 2024 16:52:57 +0000 Subject: [PATCH 2/8] remove PyNumber_Index stuff --- Cargo.toml | 4 +- src/input/return_enums.rs | 48 ++++++++++++++--------- src/serializers/infer.rs | 7 +++- tests/benchmarks/test_micro_benchmarks.py | 12 ++++++ 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 69667d976..b16a9e856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,8 +56,8 @@ extension-module = ["pyo3/extension-module"] [profile.release] lto = "fat" codegen-units = 1 -#strip = true -debug = true +strip = true +#debug = true [profile.bench] debug = true diff --git a/src/input/return_enums.rs b/src/input/return_enums.rs index 87191026a..67115c43d 100644 --- a/src/input/return_enums.rs +++ b/src/input/return_enums.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; use std::cmp::Ordering; -use std::ffi::c_longlong; +use std::ffi::c_long; use std::ops::Rem; use std::slice::Iter as SliceIter; use std::str::FromStr; @@ -864,9 +864,12 @@ pub enum EitherInt<'a> { impl<'a> EitherInt<'a> { pub fn upcast(py_any: &'a PyAny) -> ValResult { // Safety: we know that py_any is a python int - if let Ok(int_64) = py_any.extract::() { + if let Some(int_64) = extract_i64(py_any) { Ok(Self::I64(int_64)) } else { + // In theory we could copy a lot of code from pyo3, and make big_int extraction + // faster by skipping the downcast and PyNumber_Index calls, but it would add A LOT + // of complexity let big_int: BigInt = py_any.extract()?; Ok(Self::BigInt(big_int)) } @@ -1020,22 +1023,6 @@ impl<'a> Rem for &'a Int { } } -/// Extract an i64 from a python object, note that contrary to what -/// https://docs.python.org/3/c-api/long.html#c.PyLong_AsLongLong suggests, we are not calling -/// `__index__()` (`ffi::PyNumber_Index`) first, as it seems to make no difference -fn extract_i64(obj: &PyAny) -> Option { - let val: c_longlong = unsafe { ffi::PyLong_AsLongLong(obj.as_ptr()) }; - if unsafe { ffi::PyErr_Occurred() }.is_null() { - Some(val) - } else { - let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); - let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut(); - let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut(); - unsafe { ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback) }; - None - } -} - impl<'a> FromPyObject<'a> for Int { fn extract(obj: &'a PyAny) -> PyResult { if let Some(i) = extract_i64(obj) { @@ -1056,3 +1043,28 @@ impl ToPyObject for Int { } } } + +/// Extract an i64 from a python object more quickly, see +/// https://github.com/PyO3/pyo3/pull/3742#discussion_r1451763928 +fn extract_i64(obj: &PyAny) -> Option { + let val: c_long = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; + if val == -1 && PyErr::occurred(obj.py()) { + _take_err(obj.py()); + None + } else { + Some(val) + } +} + +#[cfg(not(Py_3_12))] +fn _take_err(_: Python) { + let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); + let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut(); + let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut(); + unsafe { ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback) }; +} + +#[cfg(Py_3_12)] +fn _take_err(_: Python) { + unsafe { ffi::PyErr_GetRaisedException() } +} diff --git a/src/serializers/infer.rs b/src/serializers/infer.rs index 13c20062b..9bf1777f7 100644 --- a/src/serializers/infer.rs +++ b/src/serializers/infer.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::str::FromStr; use pyo3::exceptions::PyTypeError; use pyo3::intern; @@ -421,7 +422,11 @@ pub(crate) fn infer_serialize_known( let ser_result = match ob_type { ObType::None => serializer.serialize_none(), - ObType::Int | ObType::IntSubclass => serialize!(Int), + ObType::Int => { + let n = serde_json::Number::from_str(&value.to_string()).map_err(S::Error::custom)?; + n.serialize(serializer) + } + ObType::IntSubclass => serialize!(Int), ObType::Bool => serialize!(bool), ObType::Float | ObType::FloatSubclass => serialize!(f64), ObType::Decimal => value.to_string().serialize(serializer), diff --git a/tests/benchmarks/test_micro_benchmarks.py b/tests/benchmarks/test_micro_benchmarks.py index f1ec32eef..c2320427c 100644 --- a/tests/benchmarks/test_micro_benchmarks.py +++ b/tests/benchmarks/test_micro_benchmarks.py @@ -1232,6 +1232,18 @@ def test_strict_int(benchmark): benchmark(v.validate_python, 42) +@pytest.mark.benchmark(group='strict_int') +def test_strict_int_fails(benchmark): + v = SchemaValidator(core_schema.int_schema(strict=True)) + + @benchmark + def t(): + try: + v.validate_python(()) + except ValidationError: + pass + + @pytest.mark.benchmark(group='int_range') def test_int_range(benchmark): v = SchemaValidator(core_schema.int_schema(gt=0, lt=100)) From d3d5bc7b64433f4c7c82e4371159dc7e7ffbb467 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Sun, 14 Jan 2024 17:21:11 +0000 Subject: [PATCH 3/8] improve extract_i64 impl --- src/errors/types.rs | 2 +- src/errors/value_exception.rs | 2 +- src/input/input_python.rs | 10 +++---- src/input/return_enums.rs | 28 +----------------- src/lookup_key.rs | 2 +- src/serializers/infer.rs | 12 ++++---- src/serializers/type_serializers/literal.rs | 4 +-- src/tools.rs | 32 ++++++++++++++++----- 8 files changed, 41 insertions(+), 51 deletions(-) diff --git a/src/errors/types.rs b/src/errors/types.rs index cfa96221e..eddd7dbaa 100644 --- a/src/errors/types.rs +++ b/src/errors/types.rs @@ -786,7 +786,7 @@ impl From for Number { impl FromPyObject<'_> for Number { fn extract(obj: &PyAny) -> PyResult { - if let Ok(int) = extract_i64(obj) { + if let Some(int) = extract_i64(obj) { Ok(Number::Int(int)) } else if let Ok(float) = obj.extract::() { Ok(Number::Float(float)) diff --git a/src/errors/value_exception.rs b/src/errors/value_exception.rs index a88610eef..68f93d463 100644 --- a/src/errors/value_exception.rs +++ b/src/errors/value_exception.rs @@ -122,7 +122,7 @@ impl PydanticCustomError { let key: &PyString = key.downcast()?; if let Ok(py_str) = value.downcast::() { message = message.replace(&format!("{{{}}}", key.to_str()?), py_str.to_str()?); - } else if let Ok(value_int) = extract_i64(value) { + } else if let Some(value_int) = extract_i64(value) { message = message.replace(&format!("{{{}}}", key.to_str()?), &value_int.to_string()); } else { // fallback for anything else just in case diff --git a/src/input/input_python.rs b/src/input/input_python.rs index 5d4d4826c..2eaaebe10 100644 --- a/src/input/input_python.rs +++ b/src/input/input_python.rs @@ -96,7 +96,7 @@ impl AsLocItem for PyAny { fn as_loc_item(&self) -> LocItem { if let Ok(py_str) = self.downcast::() { py_str.to_string_lossy().as_ref().into() - } else if let Ok(key_int) = extract_i64(self) { + } else if let Some(key_int) = extract_i64(self) { key_int.into() } else { safe_repr(self).to_string().into() @@ -292,7 +292,7 @@ impl<'a> Input<'a> for PyAny { if !strict { if let Some(cow_str) = maybe_as_string(self, ErrorTypeDefaults::BoolParsing)? { return str_as_bool(self, &cow_str).map(ValidationMatch::lax); - } else if let Ok(int) = extract_i64(self) { + } else if let Some(int) = extract_i64(self) { return int_as_bool(self, int).map(ValidationMatch::lax); } else if let Ok(float) = self.extract::() { if let Ok(int) = float_as_int(self, float) { @@ -635,7 +635,7 @@ impl<'a> Input<'a> for PyAny { bytes_as_time(self, py_bytes.as_bytes(), microseconds_overflow_behavior) } else if PyBool::is_exact_type_of(self) { Err(ValError::new(ErrorTypeDefaults::TimeType, self)) - } else if let Ok(int) = extract_i64(self) { + } else if let Some(int) = extract_i64(self) { int_as_time(self, int, 0) } else if let Ok(float) = self.extract::() { float_as_time(self, float) @@ -669,7 +669,7 @@ impl<'a> Input<'a> for PyAny { bytes_as_datetime(self, py_bytes.as_bytes(), microseconds_overflow_behavior) } else if PyBool::is_exact_type_of(self) { Err(ValError::new(ErrorTypeDefaults::DatetimeType, self)) - } else if let Ok(int) = extract_i64(self) { + } else if let Some(int) = extract_i64(self) { int_as_datetime(self, int, 0) } else if let Ok(float) = self.extract::() { float_as_datetime(self, float) @@ -706,7 +706,7 @@ impl<'a> Input<'a> for PyAny { bytes_as_timedelta(self, str.as_bytes(), microseconds_overflow_behavior) } else if let Ok(py_bytes) = self.downcast::() { bytes_as_timedelta(self, py_bytes.as_bytes(), microseconds_overflow_behavior) - } else if let Ok(int) = extract_i64(self) { + } else if let Some(int) = extract_i64(self) { Ok(int_as_duration(self, int)?.into()) } else if let Ok(float) = self.extract::() { Ok(float_as_duration(self, float)?.into()) diff --git a/src/input/return_enums.rs b/src/input/return_enums.rs index 67115c43d..95a0e698a 100644 --- a/src/input/return_enums.rs +++ b/src/input/return_enums.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::cmp::Ordering; -use std::ffi::c_long; use std::ops::Rem; use std::slice::Iter as SliceIter; use std::str::FromStr; @@ -24,7 +23,7 @@ use pyo3::PyTypeInfo; use serde::{ser::Error, Serialize, Serializer}; use crate::errors::{py_err_string, ErrorType, ErrorTypeDefaults, InputValue, ValError, ValLineError, ValResult}; -use crate::tools::py_err; +use crate::tools::{extract_i64, py_err}; use crate::validators::{CombinedValidator, Exactness, ValidationState, Validator}; use super::input_string::StringMapping; @@ -1043,28 +1042,3 @@ impl ToPyObject for Int { } } } - -/// Extract an i64 from a python object more quickly, see -/// https://github.com/PyO3/pyo3/pull/3742#discussion_r1451763928 -fn extract_i64(obj: &PyAny) -> Option { - let val: c_long = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; - if val == -1 && PyErr::occurred(obj.py()) { - _take_err(obj.py()); - None - } else { - Some(val) - } -} - -#[cfg(not(Py_3_12))] -fn _take_err(_: Python) { - let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); - let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut(); - let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut(); - unsafe { ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback) }; -} - -#[cfg(Py_3_12)] -fn _take_err(_: Python) { - unsafe { ffi::PyErr_GetRaisedException() } -} diff --git a/src/lookup_key.rs b/src/lookup_key.rs index e145c1f41..e4d2dcce7 100644 --- a/src/lookup_key.rs +++ b/src/lookup_key.rs @@ -429,7 +429,7 @@ impl PathItem { } else { Ok(Self::Pos(usize_key)) } - } else if let Ok(int_key) = extract_i64(obj) { + } else if let Some(int_key) = extract_i64(obj) { if index == 0 { py_err!(PyTypeError; "The first item in an alias path should be a string") } else { diff --git a/src/serializers/infer.rs b/src/serializers/infer.rs index 9bf1777f7..e39ca38f8 100644 --- a/src/serializers/infer.rs +++ b/src/serializers/infer.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::str::FromStr; use pyo3::exceptions::PyTypeError; use pyo3::intern; @@ -124,7 +123,10 @@ pub(crate) fn infer_to_python_known( // `bool` and `None` can't be subclasses, `ObType::Int`, `ObType::Float`, `ObType::Str` refer to exact types ObType::None | ObType::Bool | ObType::Int | ObType::Str => value.into_py(py), // have to do this to make sure subclasses of for example str are upcast to `str` - ObType::IntSubclass => extract_i64(value)?.into_py(py), + ObType::IntSubclass => match extract_i64(value) { + Some(v) => v.into_py(py), + None => return py_err!(PyTypeError; "expected int, got {}", safe_repr(value)), + }, ObType::Float | ObType::FloatSubclass => { let v = value.extract::()?; if (v.is_nan() || v.is_infinite()) && extra.config.inf_nan_mode == InfNanMode::Null { @@ -422,11 +424,7 @@ pub(crate) fn infer_serialize_known( let ser_result = match ob_type { ObType::None => serializer.serialize_none(), - ObType::Int => { - let n = serde_json::Number::from_str(&value.to_string()).map_err(S::Error::custom)?; - n.serialize(serializer) - } - ObType::IntSubclass => serialize!(Int), + ObType::Int | ObType::IntSubclass => serialize!(Int), ObType::Bool => serialize!(bool), ObType::Float | ObType::FloatSubclass => serialize!(f64), ObType::Decimal => value.to_string().serialize(serializer), diff --git a/src/serializers/type_serializers/literal.rs b/src/serializers/type_serializers/literal.rs index 846b8843f..d6b08afa5 100644 --- a/src/serializers/type_serializers/literal.rs +++ b/src/serializers/type_serializers/literal.rs @@ -46,7 +46,7 @@ impl BuildSerializer for LiteralSerializer { repr_args.push(item.repr()?.extract()?); if let Ok(bool) = item.downcast::() { expected_py.append(bool)?; - } else if let Ok(int) = extract_i64(item) { + } else if let Some(int) = extract_i64(item) { expected_int.insert(int); } else if let Ok(py_str) = item.downcast::() { expected_str.insert(py_str.to_str()?.to_string()); @@ -79,7 +79,7 @@ impl LiteralSerializer { fn check<'a>(&self, value: &'a PyAny, extra: &Extra) -> PyResult> { if extra.check.enabled() { if !self.expected_int.is_empty() && !PyBool::is_type_of(value) { - if let Ok(int) = extract_i64(value) { + if let Some(int) = extract_i64(value) { if self.expected_int.contains(&int) { return Ok(OutputValue::OkInt(int)); } diff --git a/src/tools.rs b/src/tools.rs index af58131f5..ab1a4b364 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,9 +1,10 @@ use std::borrow::Cow; +use std::ffi::c_long; -use pyo3::exceptions::{PyKeyError, PyTypeError}; +use pyo3::exceptions::PyKeyError; use pyo3::prelude::*; -use pyo3::types::{PyDict, PyInt, PyString}; -use pyo3::{intern, FromPyObject, PyTypeInfo}; +use pyo3::types::{PyDict, PyString}; +use pyo3::{ffi, intern, FromPyObject}; pub trait SchemaDict<'py> { fn get_as(&'py self, key: &PyString) -> PyResult> @@ -99,10 +100,27 @@ pub fn safe_repr(v: &PyAny) -> Cow { } } -pub fn extract_i64(v: &PyAny) -> PyResult { - if PyInt::is_type_of(v) { - v.extract() +/// Extract an i64 from a python object more quickly, see +/// https://github.com/PyO3/pyo3/pull/3742#discussion_r1451763928 +pub fn extract_i64(obj: &PyAny) -> Option { + let val: c_long = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; + if val == -1 && PyErr::occurred(obj.py()) { + _take_err(obj.py()); + None } else { - py_err!(PyTypeError; "expected int, got {}", safe_repr(v)) + Some(val) } } + +#[cfg(not(Py_3_12))] +fn _take_err(_: Python) { + let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); + let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut(); + let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut(); + unsafe { ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback) }; +} + +#[cfg(Py_3_12)] +fn _take_err(_: Python) { + unsafe { ffi::PyErr_GetRaisedException() } +} From d0239feb90d7365c654d18e3f7144d56402459e7 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Sun, 14 Jan 2024 17:23:47 +0000 Subject: [PATCH 4/8] cleanup --- src/input/return_enums.rs | 3 --- src/tools.rs | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/input/return_enums.rs b/src/input/return_enums.rs index 95a0e698a..905b895f9 100644 --- a/src/input/return_enums.rs +++ b/src/input/return_enums.rs @@ -866,9 +866,6 @@ impl<'a> EitherInt<'a> { if let Some(int_64) = extract_i64(py_any) { Ok(Self::I64(int_64)) } else { - // In theory we could copy a lot of code from pyo3, and make big_int extraction - // faster by skipping the downcast and PyNumber_Index calls, but it would add A LOT - // of complexity let big_int: BigInt = py_any.extract()?; Ok(Self::BigInt(big_int)) } diff --git a/src/tools.rs b/src/tools.rs index ab1a4b364..973998127 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::ffi::c_long; use pyo3::exceptions::PyKeyError; use pyo3::prelude::*; @@ -103,7 +102,7 @@ pub fn safe_repr(v: &PyAny) -> Cow { /// Extract an i64 from a python object more quickly, see /// https://github.com/PyO3/pyo3/pull/3742#discussion_r1451763928 pub fn extract_i64(obj: &PyAny) -> Option { - let val: c_long = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; + let val = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; if val == -1 && PyErr::occurred(obj.py()) { _take_err(obj.py()); None From 48d9c33197e14bc579f95320b17833b612ed64b7 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Sun, 14 Jan 2024 17:31:29 +0000 Subject: [PATCH 5/8] windows support --- src/tools.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/tools.rs b/src/tools.rs index 973998127..c7dd833d2 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -101,6 +101,7 @@ pub fn safe_repr(v: &PyAny) -> Cow { /// Extract an i64 from a python object more quickly, see /// https://github.com/PyO3/pyo3/pull/3742#discussion_r1451763928 +#[cfg(not(any(target_pointer_width = "32", target_os = "windows")))] pub fn extract_i64(obj: &PyAny) -> Option { let val = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; if val == -1 && PyErr::occurred(obj.py()) { @@ -111,6 +112,15 @@ pub fn extract_i64(obj: &PyAny) -> Option { } } +#[cfg(any(target_pointer_width = "32", target_os = "windows"))] +pub fn extract_i64(v: &PyAny) -> Option { + if v.is_instance_of::() { + v.extract().ok() + } else { + None + } +} + #[cfg(not(Py_3_12))] fn _take_err(_: Python) { let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); @@ -121,5 +131,7 @@ fn _take_err(_: Python) { #[cfg(Py_3_12)] fn _take_err(_: Python) { - unsafe { ffi::PyErr_GetRaisedException() } + unsafe { + ffi::PyErr_GetRaisedException(); + } } From 82101878b238e16fad7ea5943ccd85bef54768ac Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Sun, 14 Jan 2024 18:10:17 +0000 Subject: [PATCH 6/8] pypy check --- src/tools.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools.rs b/src/tools.rs index c7dd833d2..2ee24c063 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -101,7 +101,7 @@ pub fn safe_repr(v: &PyAny) -> Cow { /// Extract an i64 from a python object more quickly, see /// https://github.com/PyO3/pyo3/pull/3742#discussion_r1451763928 -#[cfg(not(any(target_pointer_width = "32", target_os = "windows")))] +#[cfg(not(any(target_pointer_width = "32", windows, PyPy)))] pub fn extract_i64(obj: &PyAny) -> Option { let val = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; if val == -1 && PyErr::occurred(obj.py()) { @@ -112,7 +112,7 @@ pub fn extract_i64(obj: &PyAny) -> Option { } } -#[cfg(any(target_pointer_width = "32", target_os = "windows"))] +#[cfg(any(target_pointer_width = "32", windows, PyPy))] pub fn extract_i64(v: &PyAny) -> Option { if v.is_instance_of::() { v.extract().ok() From d8111f7d742f67aeffae80660e698b261cc41fc3 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Mon, 15 Jan 2024 10:33:21 +0000 Subject: [PATCH 7/8] use PyErr_Clear --- Cargo.toml | 1 - src/tools.rs | 17 +---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b16a9e856..283e364f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,6 @@ extension-module = ["pyo3/extension-module"] lto = "fat" codegen-units = 1 strip = true -#debug = true [profile.bench] debug = true diff --git a/src/tools.rs b/src/tools.rs index 2ee24c063..bdc41583c 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -105,7 +105,7 @@ pub fn safe_repr(v: &PyAny) -> Cow { pub fn extract_i64(obj: &PyAny) -> Option { let val = unsafe { ffi::PyLong_AsLong(obj.as_ptr()) }; if val == -1 && PyErr::occurred(obj.py()) { - _take_err(obj.py()); + unsafe { ffi::PyErr_Clear() }; None } else { Some(val) @@ -120,18 +120,3 @@ pub fn extract_i64(v: &PyAny) -> Option { None } } - -#[cfg(not(Py_3_12))] -fn _take_err(_: Python) { - let mut ptype: *mut ffi::PyObject = std::ptr::null_mut(); - let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut(); - let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut(); - unsafe { ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback) }; -} - -#[cfg(Py_3_12)] -fn _take_err(_: Python) { - unsafe { - ffi::PyErr_GetRaisedException(); - } -} From 16a3c25eb0b44632971f4413f4a378db28092f13 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Mon, 15 Jan 2024 10:48:03 +0000 Subject: [PATCH 8/8] add profile.json to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6c9ace5f4..efffcbf69 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ node_modules/ /foobar.py /python/pydantic_core/*.so /src/self_schema.py + +# samply +/profile.json