Skip to content

Commit e875aaf

Browse files
committed
updates for new PyO3 API
1 parent 605e883 commit e875aaf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+2349
-2099
lines changed

benches/main.rs

Lines changed: 106 additions & 98 deletions
Large diffs are not rendered by default.

src/argument_markers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ impl ArgsKwargs {
5555
}
5656

5757
pub fn __repr__(&self, py: Python) -> String {
58-
let args = safe_repr(self.args.as_ref(py));
58+
let args = safe_repr(self.args.bind(py));
5959
match self.kwargs {
60-
Some(ref d) => format!("ArgsKwargs({args}, {})", safe_repr(d.as_ref(py))),
60+
Some(ref d) => format!("ArgsKwargs({args}, {})", safe_repr(d.bind(py))),
6161
None => format!("ArgsKwargs({args})"),
6262
}
6363
}

src/build_tools.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ use crate::tools::SchemaDict;
1212
use crate::ValidationError;
1313

1414
pub fn schema_or_config<'py, T>(
15-
schema: &'py PyDict,
16-
config: Option<&'py PyDict>,
17-
schema_key: &PyString,
18-
config_key: &PyString,
15+
schema: &Bound<'py, PyDict>,
16+
config: Option<&Bound<'py, PyDict>>,
17+
schema_key: &Bound<'_, PyString>,
18+
config_key: &Bound<'_, PyString>,
1919
) -> PyResult<Option<T>>
2020
where
2121
T: FromPyObject<'py>,
@@ -30,17 +30,17 @@ where
3030
}
3131

3232
pub fn schema_or_config_same<'py, T>(
33-
schema: &'py PyDict,
34-
config: Option<&'py PyDict>,
35-
key: &PyString,
33+
schema: &Bound<'py, PyDict>,
34+
config: Option<&Bound<'py, PyDict>>,
35+
key: &Bound<'_, PyString>,
3636
) -> PyResult<Option<T>>
3737
where
3838
T: FromPyObject<'py>,
3939
{
4040
schema_or_config(schema, config, key, key)
4141
}
4242

43-
pub fn is_strict(schema: &PyDict, config: Option<&PyDict>) -> PyResult<bool> {
43+
pub fn is_strict(schema: &Bound<'_, PyDict>, config: Option<&Bound<'_, PyDict>>) -> PyResult<bool> {
4444
let py = schema.py();
4545
Ok(schema_or_config_same(schema, config, intern!(py, "strict"))?.unwrap_or(false))
4646
}
@@ -90,7 +90,7 @@ impl SchemaError {
9090
ValidationError::new(line_errors, "Schema".to_object(py), InputType::Python, false);
9191
let schema_error = SchemaError(SchemaErrorEnum::ValidationError(validation_error));
9292
match Py::new(py, schema_error) {
93-
Ok(err) => PyErr::from_value(err.into_ref(py)),
93+
Ok(err) => PyErr::from_value(err.as_ref(py)),
9494
Err(err) => err,
9595
}
9696
}
@@ -124,7 +124,7 @@ impl SchemaError {
124124

125125
fn errors(&self, py: Python) -> PyResult<Py<PyList>> {
126126
match &self.0 {
127-
SchemaErrorEnum::Message(_) => Ok(PyList::empty(py).into_py(py)),
127+
SchemaErrorEnum::Message(_) => Ok(PyList::empty_bound(py).unbind()),
128128
SchemaErrorEnum::ValidationError(error) => error.errors(py, false, false, true),
129129
}
130130
}
@@ -174,18 +174,18 @@ pub(crate) enum ExtraBehavior {
174174
impl ExtraBehavior {
175175
pub fn from_schema_or_config(
176176
py: Python,
177-
schema: &PyDict,
178-
config: Option<&PyDict>,
177+
schema: &Bound<'_, PyDict>,
178+
config: Option<&Bound<'_, PyDict>>,
179179
default: Self,
180180
) -> PyResult<Self> {
181-
let extra_behavior = schema_or_config::<Option<&str>>(
181+
let extra_behavior = schema_or_config::<Option<Bound<'_, PyString>>>(
182182
schema,
183183
config,
184184
intern!(py, "extra_behavior"),
185185
intern!(py, "extra_fields_behavior"),
186186
)?
187187
.flatten();
188-
let res = match extra_behavior {
188+
let res = match extra_behavior.as_ref().map(|s| s.to_str()).transpose()? {
189189
Some("allow") => Self::Allow,
190190
Some("ignore") => Self::Ignore,
191191
Some("forbid") => Self::Forbid,

src/errors/line_error.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use pyo3::exceptions::PyTypeError;
22
use pyo3::prelude::*;
3-
use pyo3::PyDowncastError;
3+
use pyo3::DowncastError;
4+
use pyo3::DowncastIntoError;
45

56
use jiter::JsonValue;
67

@@ -35,8 +36,14 @@ impl From<PyErr> for ValError {
3536
}
3637
}
3738

38-
impl From<PyDowncastError<'_>> for ValError {
39-
fn from(py_downcast: PyDowncastError) -> Self {
39+
impl From<DowncastError<'_, '_>> for ValError {
40+
fn from(py_downcast: DowncastError) -> Self {
41+
Self::InternalErr(PyTypeError::new_err(py_downcast.to_string()))
42+
}
43+
}
44+
45+
impl From<DowncastIntoError<'_>> for ValError {
46+
fn from(py_downcast: DowncastIntoError) -> Self {
4047
Self::InternalErr(PyTypeError::new_err(py_downcast.to_string()))
4148
}
4249
}

src/errors/location.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ static EMPTY_TUPLE: GILOnceCell<PyObject> = GILOnceCell::new();
126126
impl ToPyObject for Location {
127127
fn to_object(&self, py: Python<'_>) -> PyObject {
128128
match self {
129-
Self::List(loc) => PyTuple::new(py, loc.iter().rev()).to_object(py),
129+
Self::List(loc) => PyTuple::new_bound(py, loc.iter().rev()).to_object(py),
130130
Self::Empty => EMPTY_TUPLE
131-
.get_or_init(py, || PyTuple::empty(py).to_object(py))
131+
.get_or_init(py, || PyTuple::empty_bound(py).to_object(py))
132132
.clone_ref(py),
133133
}
134134
}
@@ -193,12 +193,12 @@ impl Serialize for Location {
193193
}
194194
}
195195

196-
impl TryFrom<Option<&PyAny>> for Location {
196+
impl TryFrom<Option<&Bound<'_, PyAny>>> for Location {
197197
type Error = PyErr;
198198

199199
/// Only ever called by ValidationError -> PyLineError to convert user input to our internal Location
200200
/// Thus this expects the location to *not* be reversed and reverses it before storing it.
201-
fn try_from(location: Option<&PyAny>) -> PyResult<Self> {
201+
fn try_from(location: Option<&Bound<'_, PyAny>>) -> PyResult<Self> {
202202
if let Some(location) = location {
203203
let mut loc_vec: Vec<LocItem> = if let Ok(tuple) = location.downcast::<PyTuple>() {
204204
tuple.iter().map(Into::into).collect()

src/errors/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub use self::value_exception::{PydanticCustomError, PydanticKnownError, Pydanti
1414

1515
pub fn py_err_string(py: Python, err: PyErr) -> String {
1616
let value = err.value(py);
17-
match value.get_type().name() {
17+
match value.get_type().qualname() {
1818
Ok(type_name) => match value.str() {
1919
Ok(py_str) => {
2020
let str_cow = py_str.to_string_lossy();

src/errors/types.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ use crate::tools::{extract_i64, py_err, py_error_type};
1818
use super::PydanticCustomError;
1919

2020
#[pyfunction]
21-
pub fn list_all_errors(py: Python) -> PyResult<&PyList> {
22-
let mut errors: Vec<&PyDict> = Vec::with_capacity(100);
21+
pub fn list_all_errors(py: Python) -> PyResult<Bound<'_, PyList>> {
22+
let mut errors: Vec<Bound<'_, PyDict>> = Vec::with_capacity(100);
2323
for error_type in ErrorType::iter() {
2424
if !matches!(error_type, ErrorType::CustomError { .. }) {
25-
let d = PyDict::new(py);
25+
let d = PyDict::new_bound(py);
2626
d.set_item("type", error_type.to_string())?;
2727
let message_template_python = error_type.message_template_python();
2828
d.set_item("message_template_python", message_template_python)?;
@@ -39,7 +39,7 @@ pub fn list_all_errors(py: Python) -> PyResult<&PyList> {
3939
errors.push(d);
4040
}
4141
}
42-
Ok(PyList::new(py, errors))
42+
Ok(PyList::new_bound(py, errors))
4343
}
4444

4545
fn field_from_context<'py, T: FromPyObject<'py>>(
@@ -121,15 +121,16 @@ macro_rules! error_types {
121121
}
122122
}
123123

124-
fn py_dict_update_ctx(&self, py: Python, dict: &PyDict) -> PyResult<bool> {
124+
fn py_dict_update_ctx(&self, py: Python, dict: &Bound<'_, PyDict>) -> PyResult<bool> {
125+
use pyo3::types::PyMapping;
125126
match self {
126127
$(
127128
Self::$item { context, $($key,)* } => {
128129
$(
129130
dict.set_item::<&str, Py<PyAny>>(stringify!($key), $key.to_object(py))?;
130131
)*
131132
if let Some(ctx) = context {
132-
dict.update(ctx.as_ref(py).downcast()?)?;
133+
dict.update(ctx.bind(py).downcast::<PyMapping>()?)?;
133134
Ok(true)
134135
} else {
135136
Ok(false)
@@ -682,7 +683,7 @@ impl ErrorType {
682683
message_template,
683684
context,
684685
..
685-
} => PydanticCustomError::format_message(message_template, context.as_ref().map(|c| c.as_ref(py))),
686+
} => PydanticCustomError::format_message(message_template, context.as_ref().map(|c| c.bind(py))),
686687
Self::LiteralError { expected, .. } => render!(tmpl, expected),
687688
Self::DateParsing { error, .. } => render!(tmpl, error),
688689
Self::DateFromDatetimeParsing { error, .. } => render!(tmpl, error),
@@ -729,8 +730,8 @@ impl ErrorType {
729730
}
730731

731732
pub fn py_dict(&self, py: Python) -> PyResult<Option<Py<PyDict>>> {
732-
let dict = PyDict::new(py);
733-
let custom_ctx_used = self.py_dict_update_ctx(py, dict)?;
733+
let dict = PyDict::new_bound(py);
734+
let custom_ctx_used = self.py_dict_update_ctx(py, &dict)?;
734735

735736
if let Self::CustomError { .. } = self {
736737
if custom_ctx_used {
@@ -785,7 +786,7 @@ impl From<Int> for Number {
785786
}
786787

787788
impl FromPyObject<'_> for Number {
788-
fn extract(obj: &PyAny) -> PyResult<Self> {
789+
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
789790
if let Some(int) = extract_i64(obj) {
790791
Ok(Number::Int(int))
791792
} else if let Ok(float) = obj.extract::<f64>() {

src/errors/validation_exception.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -253,14 +253,17 @@ impl ValidationError {
253253
fn from_exception_data(
254254
py: Python,
255255
title: PyObject,
256-
line_errors: &PyList,
256+
line_errors: Bound<'_, PyList>,
257257
input_type: &str,
258258
hide_input: bool,
259259
) -> PyResult<Py<Self>> {
260260
Py::new(
261261
py,
262262
Self {
263-
line_errors: line_errors.iter().map(PyLineError::try_from).collect::<PyResult<_>>()?,
263+
line_errors: line_errors
264+
.iter()
265+
.map(|error| PyLineError::try_from(&error))
266+
.collect::<PyResult<_>>()?,
264267
title,
265268
input_type: InputType::try_from(input_type)?,
266269
hide_input,
@@ -287,7 +290,7 @@ impl ValidationError {
287290
) -> PyResult<Py<PyList>> {
288291
let url_prefix = get_url_prefix(py, include_url);
289292
let mut iteration_error = None;
290-
let list = PyList::new(
293+
let list = PyList::new_bound(
291294
py,
292295
// PyList::new takes ExactSizeIterator, so if an error occurs during iteration we
293296
// fill the list with None before returning the error; the list will then be thrown
@@ -318,7 +321,7 @@ impl ValidationError {
318321
include_url: bool,
319322
include_context: bool,
320323
include_input: bool,
321-
) -> PyResult<&'py PyString> {
324+
) -> PyResult<Bound<'py, PyString>> {
322325
let state = SerializationState::new("iso8601", "utf8", "constants")?;
323326
let extra = state.extra(py, &SerMode::Json, true, false, false, true, None);
324327
let serializer = ValidationErrorSerializer {
@@ -347,7 +350,7 @@ impl ValidationError {
347350
}
348351
};
349352
let s = from_utf8(&bytes).map_err(json_py_err)?;
350-
Ok(PyString::new(py, s))
353+
Ok(PyString::new_bound(py, s))
351354
}
352355

353356
fn __repr__(&self, py: Python) -> String {
@@ -463,11 +466,11 @@ impl From<PyLineError> for ValLineError {
463466
}
464467
}
465468

466-
impl TryFrom<&PyAny> for PyLineError {
469+
impl TryFrom<&Bound<'_, PyAny>> for PyLineError {
467470
type Error = PyErr;
468471

469-
fn try_from(value: &PyAny) -> PyResult<Self> {
470-
let dict: &PyDict = value.downcast()?;
472+
fn try_from(value: &Bound<'_, PyAny>) -> PyResult<Self> {
473+
let dict = value.downcast::<PyDict>()?;
471474
let py = value.py();
472475

473476
let type_raw = dict
@@ -485,7 +488,7 @@ impl TryFrom<&PyAny> for PyLineError {
485488
));
486489
};
487490

488-
let location = Location::try_from(dict.get_item("loc")?)?;
491+
let location = Location::try_from(dict.get_item("loc")?.as_ref())?;
489492

490493
let input_value = match dict.get_item("input")? {
491494
Some(i) => i.into_py(py),
@@ -513,7 +516,7 @@ impl PyLineError {
513516
input_type: InputType,
514517
include_input: bool,
515518
) -> PyResult<PyObject> {
516-
let dict = PyDict::new(py);
519+
let dict = PyDict::new_bound(py);
517520
dict.set_item("type", self.error_type.type_string())?;
518521
dict.set_item("loc", self.location.to_object(py))?;
519522
dict.set_item("msg", self.error_type.render_message(py, input_type)?)?;
@@ -555,7 +558,7 @@ impl PyLineError {
555558
write!(output, " {message} [type={}", self.error_type.type_string())?;
556559

557560
if !hide_input {
558-
let input_value = self.input_value.as_ref(py);
561+
let input_value = self.input_value.bind(py);
559562
let input_str = safe_repr(input_value);
560563
truncate_input_value!(output, &input_str);
561564

@@ -663,13 +666,13 @@ impl<'py> Serialize for PyLineErrorSerializer<'py> {
663666
if self.include_input {
664667
map.serialize_entry(
665668
"input",
666-
&self.extra.serialize_infer(self.line_error.input_value.as_ref(py)),
669+
&self.extra.serialize_infer(self.line_error.input_value.bind(py)),
667670
)?;
668671
}
669672

670673
if self.include_context {
671674
if let Some(context) = self.line_error.error_type.py_dict(py).map_err(py_err_json::<S>)? {
672-
map.serialize_entry("ctx", &self.extra.serialize_infer(context.as_ref(py)))?;
675+
map.serialize_entry("ctx", &self.extra.serialize_infer(context.bind(py)))?;
673676
}
674677
}
675678
if let Some(url_prefix) = self.url_prefix {

src/errors/value_exception.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl PydanticCustomError {
8989
}
9090

9191
pub fn message(&self, py: Python) -> PyResult<String> {
92-
Self::format_message(&self.message_template, self.context.as_ref().map(|c| c.as_ref(py)))
92+
Self::format_message(&self.message_template, self.context.as_ref().map(|c| c.bind(py)))
9393
}
9494

9595
fn __str__(&self, py: Python) -> PyResult<String> {
@@ -115,14 +115,14 @@ impl PydanticCustomError {
115115
ValError::new(error_type, input)
116116
}
117117

118-
pub fn format_message(message_template: &str, context: Option<&PyDict>) -> PyResult<String> {
118+
pub fn format_message(message_template: &str, context: Option<&Bound<'_, PyDict>>) -> PyResult<String> {
119119
let mut message = message_template.to_string();
120120
if let Some(ctx) = context {
121-
for (key, value) in ctx {
122-
let key: &PyString = key.downcast()?;
121+
for (key, value) in ctx.iter() {
122+
let key = key.downcast::<PyString>()?;
123123
if let Ok(py_str) = value.downcast::<PyString>() {
124124
message = message.replace(&format!("{{{}}}", key.to_str()?), py_str.to_str()?);
125-
} else if let Some(value_int) = extract_i64(value) {
125+
} else if let Some(value_int) = extract_i64(&value) {
126126
message = message.replace(&format!("{{{}}}", key.to_str()?), &value_int.to_string());
127127
} else {
128128
// fallback for anything else just in case

0 commit comments

Comments
 (0)