Skip to content

Commit

Permalink
bump pyo3 to 0.17.1 (#236)
Browse files Browse the repository at this point in the history
* build(deps): bump pyo3 to 0.17.1

* fix dict set_item

cannot infer type of the type parameter `V` declared on the associated function `set_item`

* fix getattr PyAny

the trait bound `&pyo3::PyAny: pyo3::IntoPy<pyo3::Py<PyString>>` is not satisfied

* getattr needs IntoPy<Py<PyString>> now

* fix PyTzInfo

* fix error message

* fix pypy

* remove _pyo3_dict

* simplify code
  • Loading branch information
PrettyWood committed Aug 30, 2022
1 parent 72b9cad commit 784c9e0
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 90 deletions.
30 changes: 20 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Expand Up @@ -8,7 +8,7 @@ repository = "https://github.com/pydantic/pydantic-core.git"
readme = "README.md"

[dependencies]
pyo3 = "0.16.5"
pyo3 = "0.17.1"
regex = "1.5.5"
strum = { version = "0.24", features = ["derive"] }
strum_macros = "0.24"
Expand Down Expand Up @@ -43,4 +43,4 @@ panic = "abort"
[build-dependencies]
version_check = "0.9.4"
# used where logic has to be version/distrobution specific, e.g. pypy
pyo3-build-config = "0.16.5"
pyo3-build-config = "0.17.1"
2 changes: 1 addition & 1 deletion src/errors/kinds.rs
Expand Up @@ -366,7 +366,7 @@ macro_rules! py_dict {
($py:ident, $($value:expr),* $(,)?) => {{
let dict = PyDict::new($py);
$(
dict.set_item(stringify!($value), $value.into_py($py))?;
dict.set_item::<&str, Py<PyAny>>(stringify!($value), $value.into_py($py))?;
)*
Ok(Some(dict.into_py($py)))
}};
Expand Down
39 changes: 0 additions & 39 deletions src/input/_pyo3_dict.rs

This file was deleted.

31 changes: 19 additions & 12 deletions src/input/datetime.rs
Expand Up @@ -219,15 +219,22 @@ impl<'a> EitherDateTime<'a> {

pub fn try_into_py(self, py: Python<'a>) -> PyResult<PyObject> {
let dt = match self {
Self::Raw(datetime) => {
let tz: Option<PyObject> = match datetime.offset {
Some(offset) => {
let tz_info = TzInfo::new(offset);
Some(Py::new(py, tz_info)?.to_object(py))
}
None => None,
};
PyDateTime::new(
Self::Raw(datetime) => match datetime.offset {
Some(offset) => {
let tz_info = TzInfo::new(offset);
PyDateTime::new(
py,
datetime.date.year as i32,
datetime.date.month,
datetime.date.day,
datetime.time.hour,
datetime.time.minute,
datetime.time.second,
datetime.time.microsecond,
Some(Py::new(py, tz_info)?.to_object(py).extract(py)?),
)?
}
None => PyDateTime::new(
py,
datetime.date.year as i32,
datetime.date.month,
Expand All @@ -236,9 +243,9 @@ impl<'a> EitherDateTime<'a> {
datetime.time.minute,
datetime.time.second,
datetime.time.microsecond,
tz.as_ref(),
)?
}
None,
)?,
},
Self::Py(dt) => dt,
};
Ok(dt.into_py(py))
Expand Down
4 changes: 2 additions & 2 deletions src/input/input_python.rs
Expand Up @@ -7,12 +7,12 @@ use pyo3::types::{
PyBool, PyByteArray, PyBytes, PyDate, PyDateTime, PyDelta, PyDict, PyFrozenSet, PyIterator, PyList, PyMapping,
PySequence, PySet, PyString, PyTime, PyTuple, PyType,
};
#[cfg(not(PyPy))]
use pyo3::types::{PyDictItems, PyDictKeys, PyDictValues};
use pyo3::{intern, AsPyPointer};

use crate::errors::{py_err_string, ErrorKind, InputValue, LocItem, ValError, ValResult};

#[cfg(not(PyPy))]
use super::_pyo3_dict::{PyDictItems, PyDictKeys, PyDictValues};
use super::datetime::{
bytes_as_date, bytes_as_datetime, bytes_as_time, bytes_as_timedelta, date_as_datetime, float_as_datetime,
float_as_duration, float_as_time, int_as_datetime, int_as_duration, int_as_time, EitherDate, EitherDateTime,
Expand Down
2 changes: 0 additions & 2 deletions src/input/mod.rs
@@ -1,7 +1,5 @@
use pyo3::prelude::*;

#[cfg(not(PyPy))]
mod _pyo3_dict;
mod datetime;
mod input_abstract;
mod input_json;
Expand Down
13 changes: 5 additions & 8 deletions src/lookup_key.rs
Expand Up @@ -126,13 +126,13 @@ impl LookupKey {

pub fn py_get_attr<'data, 's>(&'s self, obj: &'data PyAny) -> PyResult<Option<(&'s str, &'data PyAny)>> {
match self {
LookupKey::Simple(key, py_key) => match py_get_attrs(obj, &py_key)? {
LookupKey::Simple(key, py_key) => match py_get_attrs(obj, py_key)? {
Some(value) => Ok(Some((key, value))),
None => Ok(None),
},
LookupKey::Choice(key1, key2, py_key1, py_key2) => match py_get_attrs(obj, &py_key1)? {
LookupKey::Choice(key1, key2, py_key1, py_key2) => match py_get_attrs(obj, py_key1)? {
Some(value) => Ok(Some((key1, value))),
None => match py_get_attrs(obj, &py_key2)? {
None => match py_get_attrs(obj, py_key2)? {
Some(value) => Ok(Some((key2, value))),
None => Ok(None),
},
Expand Down Expand Up @@ -302,11 +302,8 @@ impl PathItem {

/// wrapper around `getattr` that returns `Ok(None)` for attribute errors, but returns other errors
/// We dont check `try_from_attributes` because that check was performed on the top level object before we got here
fn py_get_attrs<N>(obj: &PyAny, attr_name: N) -> PyResult<Option<&PyAny>>
where
N: ToPyObject,
{
match obj.getattr(attr_name) {
fn py_get_attrs<'a, 'b>(obj: &'a PyAny, attr_name: &'b Py<PyString>) -> PyResult<Option<&'a PyAny>> {
match obj.getattr(attr_name.extract::<&PyString>(obj.py())?) {
Ok(attr) => Ok(Some(attr)),
Err(err) => {
if err.get_type(obj.py()).is_subclass_of::<PyAttributeError>()? {
Expand Down
27 changes: 15 additions & 12 deletions src/validators/typed_dict.rs
@@ -1,8 +1,12 @@
use std::borrow::Cow;

use pyo3::intern;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyFunction, PyList, PySet, PyString};
use pyo3::{intern, PyTypeInfo};
#[cfg(not(PyPy))]
use pyo3::types::PyFunction;
use pyo3::types::{PyDict, PyList, PySet, PyString};
#[cfg(not(PyPy))]
use pyo3::PyTypeInfo;

use ahash::AHashSet;

Expand Down Expand Up @@ -500,23 +504,22 @@ impl<'a> Iterator for AttributesIterator<'a> {
.to_string_lossy();
if !name_cow.as_ref().starts_with('_') {
// getattr is most likely to fail due to an exception in a @property, skip
if let Ok(attr) = self.object.getattr(name) {
if let Ok(attr) = self.object.getattr(name_cow.as_ref()) {
// we don't want bound methods to be included, is there a better way to check?
// ref https://stackoverflow.com/a/18955425/949890
let is_bound = matches!(attr.hasattr(intern!(attr.py(), "__self__")), Ok(true));
// the PyFunction::is_type_of(attr) catches `staticmethod`, but also any other function,
// I think that's better than including static methods in the yielded attributes,
// if someone really wants fields, they can use an explicit field, or a function to modify input
#[cfg(not(PyPy))]
if !is_bound && !PyFunction::is_type_of(attr) {
// MASSIVE HACK! PyFunction::is_type_of(attr) doesn't detect staticmethod on PyPy,
// is_instance_of::<PyFunction> crashes with a null pointer, hence this hack, see
// https://github.com/pydantic/pydantic-core/pull/161#discussion_r917257635
#[cfg(PyPy)]
if attr.get_type().to_string() != "<class 'function'>" {
return Some((name, attr));
}

#[cfg(not(PyPy))]
return Some((name, attr));
}
// MASSIVE HACK! PyFunction doesn't exist for PyPy,
// is_instance_of::<PyFunction> crashes with a null pointer, hence this hack, see
// https://github.com/pydantic/pydantic-core/pull/161#discussion_r917257635
#[cfg(PyPy)]
if !is_bound && attr.get_type().to_string() != "<class 'function'>" {
return Some((name, attr));
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/validators/test_dict.py
Expand Up @@ -183,10 +183,10 @@ def __len__(self):
'loc': [],
'message': (
'Unable to convert mapping to a dictionary, error: '
'ValueError: Expected tuple of length 2, but got tuple of length 1.'
'ValueError: expected tuple of length 2, but got tuple of length 1'
),
'input_value': HasRepr(IsStr(regex='.+BadMapping object at.+')),
'context': {'error': 'ValueError: Expected tuple of length 2, but got tuple of length 1.'},
'context': {'error': 'ValueError: expected tuple of length 2, but got tuple of length 1'},
}
]

Expand Down

0 comments on commit 784c9e0

Please sign in to comment.