Skip to content

Commit 6220455

Browse files
authored
adding tools.rs, add extract_i64 (#635)
1 parent 7dc970f commit 6220455

Some content is hidden

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

71 files changed

+316
-241
lines changed

src/argument_markers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use pyo3::basic::CompareOp;
22
use pyo3::prelude::*;
33
use pyo3::types::{PyDict, PyTuple};
44

5-
use crate::build_tools::safe_repr;
5+
use crate::tools::safe_repr;
66

77
#[pyclass(module = "pydantic_core._pydantic_core", get_all, frozen, freelist = 100)]
88
#[derive(Debug, Clone)]

src/build_tools.rs

Lines changed: 11 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,15 @@
1-
use std::borrow::Cow;
21
use std::error::Error;
32
use std::fmt;
43

5-
use pyo3::exceptions::{PyException, PyKeyError};
4+
use pyo3::exceptions::PyException;
65
use pyo3::prelude::*;
76
use pyo3::types::{PyDict, PyList, PyString};
87
use pyo3::{intern, FromPyObject, PyErrArguments};
98

109
use crate::errors::{ErrorMode, ValError};
10+
use crate::tools::SchemaDict;
1111
use crate::ValidationError;
1212

13-
pub trait SchemaDict<'py> {
14-
fn get_as<T>(&'py self, key: &PyString) -> PyResult<Option<T>>
15-
where
16-
T: FromPyObject<'py>;
17-
18-
fn get_as_req<T>(&'py self, key: &PyString) -> PyResult<T>
19-
where
20-
T: FromPyObject<'py>;
21-
}
22-
23-
impl<'py> SchemaDict<'py> for PyDict {
24-
fn get_as<T>(&'py self, key: &PyString) -> PyResult<Option<T>>
25-
where
26-
T: FromPyObject<'py>,
27-
{
28-
match self.get_item(key) {
29-
Some(t) => Ok(Some(<T>::extract(t)?)),
30-
None => Ok(None),
31-
}
32-
}
33-
34-
fn get_as_req<T>(&'py self, key: &PyString) -> PyResult<T>
35-
where
36-
T: FromPyObject<'py>,
37-
{
38-
match self.get_item(key) {
39-
Some(t) => <T>::extract(t),
40-
None => py_err!(PyKeyError; "{}", key),
41-
}
42-
}
43-
}
44-
45-
impl<'py> SchemaDict<'py> for Option<&PyDict> {
46-
fn get_as<T>(&'py self, key: &PyString) -> PyResult<Option<T>>
47-
where
48-
T: FromPyObject<'py>,
49-
{
50-
match self {
51-
Some(d) => d.get_as(key),
52-
None => Ok(None),
53-
}
54-
}
55-
56-
#[cfg_attr(has_no_coverage, no_coverage)]
57-
fn get_as_req<T>(&'py self, key: &PyString) -> PyResult<T>
58-
where
59-
T: FromPyObject<'py>,
60-
{
61-
match self {
62-
Some(d) => d.get_as_req(key),
63-
None => py_err!(PyKeyError; "{}", key),
64-
}
65-
}
66-
}
67-
6813
pub fn schema_or_config<'py, T>(
6914
schema: &'py PyDict,
7015
config: Option<&'py PyDict>,
@@ -197,58 +142,25 @@ impl SchemaError {
197142
}
198143
}
199144

200-
macro_rules! py_error_type {
145+
macro_rules! py_schema_error_type {
201146
($msg:expr) => {
202-
crate::build_tools::py_error_type!(crate::build_tools::SchemaError; $msg)
147+
crate::tools::py_error_type!(crate::build_tools::SchemaError; $msg)
203148
};
204149
($msg:expr, $( $msg_args:expr ),+ ) => {
205-
crate::build_tools::py_error_type!(crate::build_tools::SchemaError; $msg, $( $msg_args ),+)
206-
};
207-
208-
($error_type:ty; $msg:expr) => {
209-
<$error_type>::new_err($msg)
210-
};
211-
212-
($error_type:ty; $msg:expr, $( $msg_args:expr ),+ ) => {
213-
<$error_type>::new_err(format!($msg, $( $msg_args ),+))
150+
crate::tools::py_error_type!(crate::build_tools::SchemaError; $msg, $( $msg_args ),+)
214151
};
215152
}
216-
pub(crate) use py_error_type;
153+
pub(crate) use py_schema_error_type;
217154

218-
macro_rules! py_err {
155+
macro_rules! py_schema_err {
219156
($msg:expr) => {
220-
Err(crate::build_tools::py_error_type!($msg))
157+
Err(crate::build_tools::py_schema_error_type!($msg))
221158
};
222159
($msg:expr, $( $msg_args:expr ),+ ) => {
223-
Err(crate::build_tools::py_error_type!($msg, $( $msg_args ),+))
224-
};
225-
226-
($error_type:ty; $msg:expr) => {
227-
Err(crate::build_tools::py_error_type!($error_type; $msg))
160+
Err(crate::build_tools::py_schema_error_type!($msg, $( $msg_args ),+))
228161
};
229-
230-
($error_type:ty; $msg:expr, $( $msg_args:expr ),+ ) => {
231-
Err(crate::build_tools::py_error_type!($error_type; $msg, $( $msg_args ),+))
232-
};
233-
}
234-
pub(crate) use py_err;
235-
236-
pub fn function_name(f: &PyAny) -> PyResult<String> {
237-
match f.getattr(intern!(f.py(), "__name__")) {
238-
Ok(name) => name.extract(),
239-
_ => f.repr()?.extract(),
240-
}
241-
}
242-
243-
pub fn safe_repr(v: &PyAny) -> Cow<str> {
244-
if let Ok(s) = v.repr() {
245-
s.to_string_lossy()
246-
} else if let Ok(name) = v.get_type().name() {
247-
format!("<unprintable {name} object>").into()
248-
} else {
249-
"<unprintable object>".into()
250-
}
251162
}
163+
pub(crate) use py_schema_err;
252164

253165
#[derive(Debug, Clone)]
254166
pub(crate) enum ExtraBehavior {
@@ -275,7 +187,7 @@ impl ExtraBehavior {
275187
Some("allow") => Self::Allow,
276188
Some("ignore") => Self::Ignore,
277189
Some("forbid") => Self::Forbid,
278-
Some(v) => return py_err!("Invalid extra_behavior: `{}`", v),
190+
Some(v) => return py_schema_err!("Invalid extra_behavior: `{}`", v),
279191
None => default,
280192
};
281193
Ok(res)

src/definitions.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use pyo3::prelude::*;
99

1010
use ahash::AHashMap;
1111

12-
use crate::build_tools::py_err;
12+
use crate::build_tools::py_schema_err;
1313

1414
// An integer id for the reference
1515
pub type ReferenceId = usize;
@@ -68,7 +68,7 @@ impl<T: Clone + std::fmt::Debug> DefinitionsBuilder<T> {
6868
let next_id = self.definitions.len();
6969
match self.definitions.entry(reference.clone()) {
7070
Entry::Occupied(mut entry) => match entry.get_mut().value.replace(value) {
71-
Some(_) => py_err!("Duplicate ref: `{}`", reference),
71+
Some(_) => py_schema_err!("Duplicate ref: `{}`", reference),
7272
None => Ok(entry.get().id),
7373
},
7474
Entry::Vacant(entry) => {
@@ -86,11 +86,11 @@ impl<T: Clone + std::fmt::Debug> DefinitionsBuilder<T> {
8686
pub fn get_definition(&self, reference_id: ReferenceId) -> PyResult<&T> {
8787
let (reference, def) = match self.definitions.iter().find(|(_, def)| def.id == reference_id) {
8888
Some(v) => v,
89-
None => return py_err!("Definitions error: no definition for ReferenceId `{}`", reference_id),
89+
None => return py_schema_err!("Definitions error: no definition for ReferenceId `{}`", reference_id),
9090
};
9191
match def.value.as_ref() {
9292
Some(v) => Ok(v),
93-
None => py_err!(
93+
None => py_schema_err!(
9494
"Definitions error: attempted to use `{}` before it was filled",
9595
reference
9696
),
@@ -103,7 +103,7 @@ impl<T: Clone + std::fmt::Debug> DefinitionsBuilder<T> {
103103
let mut defs: Vec<(usize, T)> = Vec::new();
104104
for (reference, def) in self.definitions.into_iter() {
105105
match def.value {
106-
None => return py_err!("Definitions error: definition {} was never filled", reference),
106+
None => return py_schema_err!("Definitions error: definition {} was never filled", reference),
107107
Some(v) => defs.push((def.id, v)),
108108
}
109109
}

src/errors/location.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use serde::ser::SerializeSeq;
88
use serde::{Serialize, Serializer};
99

1010
use crate::lookup_key::{LookupPath, PathItem};
11+
use crate::tools::extract_i64;
1112

1213
/// Used to store individual items of the error location, e.g. a string for key/field names
1314
/// or a number for array indices.
@@ -88,7 +89,7 @@ impl TryFrom<&PyAny> for LocItem {
8889
if let Ok(py_str) = loc_item.downcast::<PyString>() {
8990
let str = py_str.to_str()?.to_string();
9091
Ok(Self::S(str))
91-
} else if let Ok(int) = loc_item.extract::<i64>() {
92+
} else if let Ok(int) = extract_i64(loc_item) {
9293
Ok(Self::I(int))
9394
} else {
9495
Err(PyTypeError::new_err("Item in a location must be a string or int"))

src/errors/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use pyo3::once_cell::GILOnceCell;
77
use pyo3::prelude::*;
88
use pyo3::types::{PyDict, PyList};
99

10-
use crate::build_tools::{py_err, py_error_type};
10+
use crate::tools::{extract_i64, py_err, py_error_type};
1111
use strum::{Display, EnumMessage, IntoEnumIterator};
1212
use strum_macros::EnumIter;
1313

@@ -729,7 +729,7 @@ impl From<String> for Number {
729729

730730
impl FromPyObject<'_> for Number {
731731
fn extract(obj: &PyAny) -> PyResult<Self> {
732-
if let Ok(int) = obj.extract::<i64>() {
732+
if let Ok(int) = extract_i64(obj) {
733733
Ok(Number::Int(int))
734734
} else if let Ok(float) = obj.extract::<f64>() {
735735
Ok(Number::Float(float))

src/errors/validation_exception.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use serde::{Serialize, Serializer};
1313

1414
use serde_json::ser::PrettyFormatter;
1515

16-
use crate::build_tools::{py_error_type, safe_repr, SchemaDict};
16+
use crate::build_tools::py_schema_error_type;
1717
use crate::errors::LocItem;
1818
use crate::get_version;
1919
use crate::serializers::{SerMode, SerializationState};
20+
use crate::tools::{safe_repr, SchemaDict};
2021

2122
use super::line_error::ValLineError;
2223
use super::location::Location;
@@ -97,7 +98,7 @@ impl ValidationError {
9798
}
9899

99100
pub fn omit_error() -> PyErr {
100-
py_error_type!("Uncaught Omit error, please check your usage of `default` validators.")
101+
py_schema_error_type!("Uncaught Omit error, please check your usage of `default` validators.")
101102
}
102103
}
103104

src/errors/value_exception.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use pyo3::prelude::*;
44
use pyo3::types::{PyDict, PyString};
55

66
use crate::input::Input;
7+
use crate::tools::extract_i64;
78

89
use super::{ErrorType, ValError};
910

@@ -74,7 +75,7 @@ impl PydanticCustomError {
7475
let key: &PyString = key.downcast()?;
7576
if let Ok(py_str) = value.downcast::<PyString>() {
7677
message = message.replace(&format!("{{{}}}", key.to_str()?), py_str.to_str()?);
77-
} else if let Ok(value_int) = value.extract::<i64>() {
78+
} else if let Ok(value_int) = extract_i64(value) {
7879
message = message.replace(&format!("{{{}}}", key.to_str()?), &value_int.to_string());
7980
} else {
8081
// fallback for anything else just in case

src/input/input_python.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use pyo3::types::{
1010
use pyo3::types::{PyDictItems, PyDictKeys, PyDictValues};
1111
use pyo3::{ffi, intern, AsPyPointer, PyTypeInfo};
1212

13-
use crate::build_tools::safe_repr;
1413
use crate::errors::{ErrorType, InputValue, LocItem, ValError, ValResult};
14+
use crate::tools::{extract_i64, safe_repr};
1515
use crate::{ArgsKwargs, PyMultiHostUrl, PyUrl};
1616

1717
use super::datetime::{
@@ -89,7 +89,7 @@ impl<'a> Input<'a> for PyAny {
8989
fn as_loc_item(&self) -> LocItem {
9090
if let Ok(py_str) = self.downcast::<PyString>() {
9191
py_str.to_string_lossy().as_ref().into()
92-
} else if let Ok(key_int) = self.extract::<i64>() {
92+
} else if let Ok(key_int) = extract_i64(self) {
9393
key_int.into()
9494
} else {
9595
safe_repr(self).to_string().into()
@@ -244,19 +244,19 @@ impl<'a> Input<'a> for PyAny {
244244
}
245245

246246
fn strict_bool(&self) -> ValResult<bool> {
247-
if let Ok(bool) = self.extract::<bool>() {
248-
Ok(bool)
247+
if let Ok(bool) = self.downcast::<PyBool>() {
248+
Ok(bool.is_true())
249249
} else {
250250
Err(ValError::new(ErrorType::BoolType, self))
251251
}
252252
}
253253

254254
fn lax_bool(&self) -> ValResult<bool> {
255-
if let Ok(bool) = self.extract::<bool>() {
256-
Ok(bool)
255+
if let Ok(bool) = self.downcast::<PyBool>() {
256+
Ok(bool.is_true())
257257
} else if let Some(cow_str) = maybe_as_string(self, ErrorType::BoolParsing)? {
258258
str_as_bool(self, &cow_str)
259-
} else if let Ok(int) = self.extract::<i64>() {
259+
} else if let Ok(int) = extract_i64(self) {
260260
int_as_bool(self, int)
261261
} else if let Ok(float) = self.extract::<f64>() {
262262
match float_as_int(self, float) {
@@ -547,7 +547,7 @@ impl<'a> Input<'a> for PyAny {
547547
bytes_as_time(self, py_bytes.as_bytes())
548548
} else if PyBool::is_exact_type_of(self) {
549549
Err(ValError::new(ErrorType::TimeType, self))
550-
} else if let Ok(int) = self.extract::<i64>() {
550+
} else if let Ok(int) = extract_i64(self) {
551551
int_as_time(self, int, 0)
552552
} else if let Ok(float) = self.extract::<f64>() {
553553
float_as_time(self, float)
@@ -574,7 +574,7 @@ impl<'a> Input<'a> for PyAny {
574574
bytes_as_datetime(self, py_bytes.as_bytes())
575575
} else if PyBool::is_exact_type_of(self) {
576576
Err(ValError::new(ErrorType::DatetimeType, self))
577-
} else if let Ok(int) = self.extract::<i64>() {
577+
} else if let Ok(int) = extract_i64(self) {
578578
int_as_datetime(self, int, 0)
579579
} else if let Ok(float) = self.extract::<f64>() {
580580
float_as_datetime(self, float)
@@ -601,7 +601,7 @@ impl<'a> Input<'a> for PyAny {
601601
bytes_as_timedelta(self, str.as_bytes())
602602
} else if let Ok(py_bytes) = self.downcast::<PyBytes>() {
603603
bytes_as_timedelta(self, py_bytes.as_bytes())
604-
} else if let Ok(int) = self.extract::<i64>() {
604+
} else if let Ok(int) = extract_i64(self) {
605605
Ok(int_as_duration(self, int)?.into())
606606
} else if let Ok(float) = self.extract::<f64>() {
607607
Ok(float_as_duration(self, float)?.into())

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod lazy_index_map;
1818
mod lookup_key;
1919
mod recursion_guard;
2020
mod serializers;
21+
mod tools;
2122
mod url;
2223
mod validators;
2324

src/lookup_key.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use pyo3::exceptions::{PyAttributeError, PyTypeError};
55
use pyo3::prelude::*;
66
use pyo3::types::{PyDict, PyList, PyMapping, PyString};
77

8-
use crate::build_tools::py_err;
8+
use crate::build_tools::py_schema_err;
99
use crate::errors::{ErrorType, ValLineError};
1010
use crate::input::{Input, JsonInput, JsonObject};
11+
use crate::tools::{extract_i64, py_err};
1112

1213
/// Used got getting items from python dicts, python objects, or JSON objects, in different ways
1314
#[derive(Debug, Clone)]
@@ -73,7 +74,7 @@ impl LookupKey {
7374
let list: &PyList = value.downcast()?;
7475
let first = match list.get_item(0) {
7576
Ok(v) => v,
76-
Err(_) => return py_err!("Lookup paths should have at least one element"),
77+
Err(_) => return py_schema_err!("Lookup paths should have at least one element"),
7778
};
7879
let mut locs: Vec<LookupPath> = if first.downcast::<PyString>().is_ok() {
7980
// list of strings rather than list of lists
@@ -331,7 +332,7 @@ impl LookupPath {
331332
.collect::<PyResult<Vec<PathItem>>>()?;
332333

333334
if v.is_empty() {
334-
py_err!("Each alias path should have at least one element")
335+
py_schema_err!("Each alias path should have at least one element")
335336
} else {
336337
Ok(Self(v))
337338
}
@@ -408,7 +409,7 @@ impl PathItem {
408409
} else {
409410
Ok(Self::Pos(usize_key))
410411
}
411-
} else if let Ok(int_key) = obj.extract::<i64>() {
412+
} else if let Ok(int_key) = extract_i64(obj) {
412413
if index == 0 {
413414
py_err!(PyTypeError; "The first item in an alias path should be a string")
414415
} else {

0 commit comments

Comments
 (0)