Skip to content

Commit 486f964

Browse files
committed
implement Input for str
1 parent ffe3d05 commit 486f964

Some content is hidden

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

51 files changed

+441
-417
lines changed

src/errors/line_error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ impl<'a, T: BorrowInput<'a>> ToErrorValue for T {
2323
}
2424
}
2525

26+
impl ToErrorValue for &'_ dyn ToErrorValue {
27+
fn to_error_value(&self) -> InputValue {
28+
(**self).to_error_value()
29+
}
30+
}
31+
2632
#[cfg_attr(debug_assertions, derive(Debug))]
2733
pub enum ValError {
2834
LineErrors(Vec<ValLineError>),

src/input/datetime.rs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::hash::Hasher;
1414
use strum::EnumMessage;
1515

1616
use super::Input;
17+
use crate::errors::ToErrorValue;
1718
use crate::errors::{ErrorType, ValError, ValResult};
1819
use crate::tools::py_err;
1920

@@ -285,7 +286,7 @@ impl<'a> EitherDateTime<'a> {
285286
}
286287
}
287288

288-
pub fn bytes_as_date<'a>(input: &'a impl Input<'a>, bytes: &[u8]) -> ValResult<EitherDate<'a>> {
289+
pub fn bytes_as_date<'py>(input: &(impl Input<'py> + ?Sized), bytes: &[u8]) -> ValResult<EitherDate<'py>> {
289290
match Date::parse_bytes(bytes) {
290291
Ok(date) => Ok(date.into()),
291292
Err(err) => Err(ValError::new(
@@ -298,11 +299,11 @@ pub fn bytes_as_date<'a>(input: &'a impl Input<'a>, bytes: &[u8]) -> ValResult<E
298299
}
299300
}
300301

301-
pub fn bytes_as_time<'a>(
302-
input: &'a impl Input<'a>,
302+
pub fn bytes_as_time<'py>(
303+
input: &(impl Input<'py> + ?Sized),
303304
bytes: &[u8],
304305
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
305-
) -> ValResult<EitherTime<'a>> {
306+
) -> ValResult<EitherTime<'py>> {
306307
match Time::parse_bytes_with_config(
307308
bytes,
308309
&TimeConfig {
@@ -321,11 +322,11 @@ pub fn bytes_as_time<'a>(
321322
}
322323
}
323324

324-
pub fn bytes_as_datetime<'a, 'b>(
325-
input: &'a impl Input<'a>,
326-
bytes: &'b [u8],
325+
pub fn bytes_as_datetime<'py>(
326+
input: &(impl Input<'py> + ?Sized),
327+
bytes: &[u8],
327328
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
328-
) -> ValResult<EitherDateTime<'a>> {
329+
) -> ValResult<EitherDateTime<'py>> {
329330
match DateTime::parse_bytes_with_config(
330331
bytes,
331332
&TimeConfig {
@@ -344,11 +345,11 @@ pub fn bytes_as_datetime<'a, 'b>(
344345
}
345346
}
346347

347-
pub fn int_as_datetime<'a>(
348-
input: &'a impl Input<'a>,
348+
pub fn int_as_datetime<'py>(
349+
input: &(impl Input<'py> + ?Sized),
349350
timestamp: i64,
350351
timestamp_microseconds: u32,
351-
) -> ValResult<EitherDateTime> {
352+
) -> ValResult<EitherDateTime<'py>> {
352353
match DateTime::from_timestamp_with_config(
353354
timestamp,
354355
timestamp_microseconds,
@@ -382,7 +383,7 @@ macro_rules! nan_check {
382383
};
383384
}
384385

385-
pub fn float_as_datetime<'a>(input: &'a impl Input<'a>, timestamp: f64) -> ValResult<EitherDateTime> {
386+
pub fn float_as_datetime<'py>(input: &(impl Input<'py> + ?Sized), timestamp: f64) -> ValResult<EitherDateTime<'py>> {
386387
nan_check!(input, timestamp, DatetimeParsing);
387388
let microseconds = timestamp.fract().abs() * 1_000_000.0;
388389
// checking for extra digits in microseconds is unreliable with large floats,
@@ -408,11 +409,11 @@ pub fn date_as_datetime<'py>(date: &Bound<'py, PyDate>) -> PyResult<EitherDateTi
408409

409410
const MAX_U32: i64 = u32::MAX as i64;
410411

411-
pub fn int_as_time<'a>(
412-
input: &'a impl Input<'a>,
412+
pub fn int_as_time<'py>(
413+
input: &(impl Input<'py> + ?Sized),
413414
timestamp: i64,
414415
timestamp_microseconds: u32,
415-
) -> ValResult<EitherTime> {
416+
) -> ValResult<EitherTime<'py>> {
416417
let time_timestamp: u32 = match timestamp {
417418
t if t < 0_i64 => {
418419
return Err(ValError::new(
@@ -447,14 +448,14 @@ pub fn int_as_time<'a>(
447448
}
448449
}
449450

450-
pub fn float_as_time<'a>(input: &'a impl Input<'a>, timestamp: f64) -> ValResult<EitherTime> {
451+
pub fn float_as_time<'py>(input: &(impl Input<'py> + ?Sized), timestamp: f64) -> ValResult<EitherTime<'py>> {
451452
nan_check!(input, timestamp, TimeParsing);
452453
let microseconds = timestamp.fract().abs() * 1_000_000.0;
453454
// round for same reason as above
454455
int_as_time(input, timestamp.floor() as i64, microseconds.round() as u32)
455456
}
456457

457-
fn map_timedelta_err<'a>(input: &'a impl Input<'a>, err: ParseError) -> ValError {
458+
fn map_timedelta_err(input: impl ToErrorValue, err: ParseError) -> ValError {
458459
ValError::new(
459460
ErrorType::TimeDeltaParsing {
460461
error: Cow::Borrowed(err.get_documentation().unwrap_or_default()),
@@ -464,11 +465,11 @@ fn map_timedelta_err<'a>(input: &'a impl Input<'a>, err: ParseError) -> ValError
464465
)
465466
}
466467

467-
pub fn bytes_as_timedelta<'a, 'b>(
468-
input: &'a impl Input<'a>,
469-
bytes: &'b [u8],
468+
pub fn bytes_as_timedelta<'py>(
469+
input: &(impl Input<'py> + ?Sized),
470+
bytes: &[u8],
470471
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
471-
) -> ValResult<EitherTimedelta<'a>> {
472+
) -> ValResult<EitherTimedelta<'py>> {
472473
match Duration::parse_bytes_with_config(
473474
bytes,
474475
&TimeConfig {
@@ -481,7 +482,7 @@ pub fn bytes_as_timedelta<'a, 'b>(
481482
}
482483
}
483484

484-
pub fn int_as_duration<'a>(input: &'a impl Input<'a>, total_seconds: i64) -> ValResult<Duration> {
485+
pub fn int_as_duration(input: impl ToErrorValue, total_seconds: i64) -> ValResult<Duration> {
485486
let positive = total_seconds >= 0;
486487
let total_seconds = total_seconds.unsigned_abs();
487488
// we can safely unwrap here since we've guaranteed seconds and microseconds can't cause overflow
@@ -490,7 +491,7 @@ pub fn int_as_duration<'a>(input: &'a impl Input<'a>, total_seconds: i64) -> Val
490491
Duration::new(positive, days, seconds, 0).map_err(|err| map_timedelta_err(input, err))
491492
}
492493

493-
pub fn float_as_duration<'a>(input: &'a impl Input<'a>, total_seconds: f64) -> ValResult<Duration> {
494+
pub fn float_as_duration(input: impl ToErrorValue, total_seconds: f64) -> ValResult<Duration> {
494495
nan_check!(input, total_seconds, TimeDeltaParsing);
495496
let positive = total_seconds >= 0_f64;
496497
let total_seconds = total_seconds.abs();

src/input/input_abstract.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use pyo3::exceptions::PyValueError;
44
use pyo3::types::{PyDict, PyType};
55
use pyo3::{intern, prelude::*};
66

7-
use crate::errors::{ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
7+
use crate::errors::{ErrorTypeDefaults, InputValue, ValError, ValResult};
88
use crate::tools::py_err;
99
use crate::{PyMultiHostUrl, PyUrl};
1010

@@ -46,7 +46,7 @@ impl TryFrom<&str> for InputType {
4646
/// the convention is to either implement:
4747
/// * `strict_*` & `lax_*` if they have different behavior
4848
/// * or, `validate_*` and `strict_*` to just call `validate_*` if the behavior for strict and lax is the same
49-
pub trait Input<'py>: fmt::Debug + ToPyObject + Into<LocItem> + Sized {
49+
pub trait Input<'py>: fmt::Debug + ToPyObject {
5050
fn as_error_value(&self) -> InputValue;
5151

5252
fn identity(&self) -> Option<usize> {
@@ -83,9 +83,9 @@ pub trait Input<'py>: fmt::Debug + ToPyObject + Into<LocItem> + Sized {
8383
false
8484
}
8585

86-
fn validate_args(&self) -> ValResult<GenericArguments<'_>>;
86+
fn validate_args(&self) -> ValResult<GenericArguments<'_, 'py>>;
8787

88-
fn validate_dataclass_args<'a>(&'a self, dataclass_name: &str) -> ValResult<GenericArguments<'a>>;
88+
fn validate_dataclass_args<'a>(&'a self, dataclass_name: &str) -> ValResult<GenericArguments<'a, 'py>>;
8989

9090
fn validate_str(&self, strict: bool, coerce_numbers_to_str: bool) -> ValResult<ValidationMatch<EitherString<'_>>>;
9191

@@ -201,25 +201,25 @@ pub trait Input<'py>: fmt::Debug + ToPyObject + Into<LocItem> + Sized {
201201

202202
fn validate_iter(&self) -> ValResult<GenericIterator>;
203203

204-
fn validate_date(&self, strict: bool) -> ValResult<ValidationMatch<EitherDate>>;
204+
fn validate_date(&self, strict: bool) -> ValResult<ValidationMatch<EitherDate<'py>>>;
205205

206206
fn validate_time(
207207
&self,
208208
strict: bool,
209209
microseconds_overflow_behavior: speedate::MicrosecondsPrecisionOverflowBehavior,
210-
) -> ValResult<ValidationMatch<EitherTime>>;
210+
) -> ValResult<ValidationMatch<EitherTime<'py>>>;
211211

212212
fn validate_datetime(
213213
&self,
214214
strict: bool,
215215
microseconds_overflow_behavior: speedate::MicrosecondsPrecisionOverflowBehavior,
216-
) -> ValResult<ValidationMatch<EitherDateTime>>;
216+
) -> ValResult<ValidationMatch<EitherDateTime<'py>>>;
217217

218218
fn validate_timedelta(
219219
&self,
220220
strict: bool,
221221
microseconds_overflow_behavior: speedate::MicrosecondsPrecisionOverflowBehavior,
222-
) -> ValResult<ValidationMatch<EitherTimedelta>>;
222+
) -> ValResult<ValidationMatch<EitherTimedelta<'py>>>;
223223
}
224224

225225
/// The problem to solve here is that iterating collections often returns owned
@@ -228,11 +228,11 @@ pub trait Input<'py>: fmt::Debug + ToPyObject + Into<LocItem> + Sized {
228228
/// or borrowed; all we care about is that we can borrow it again with `borrow_input`
229229
/// for some lifetime 'a.
230230
pub trait BorrowInput<'py> {
231-
type Input: Input<'py>;
231+
type Input: Input<'py> + ?Sized;
232232
fn borrow_input(&self) -> &Self::Input;
233233
}
234234

235-
impl<'py, T: Input<'py>> BorrowInput<'py> for &'_ T {
235+
impl<'py, T: Input<'py> + ?Sized> BorrowInput<'py> for &'_ T {
236236
type Input = T;
237237
fn borrow_input(&self) -> &Self::Input {
238238
self

src/input/input_json.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ impl<'py> Input<'py> for JsonValue {
6060
}
6161
}
6262

63-
fn validate_args(&self) -> ValResult<GenericArguments<'_>> {
63+
fn validate_args(&self) -> ValResult<GenericArguments<'_, 'py>> {
6464
match self {
6565
JsonValue::Object(object) => Ok(JsonArgs::new(None, Some(object)).into()),
6666
JsonValue::Array(array) => Ok(JsonArgs::new(Some(array), None).into()),
6767
_ => Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self)),
6868
}
6969
}
7070

71-
fn validate_dataclass_args<'a>(&'a self, class_name: &str) -> ValResult<GenericArguments<'a>> {
71+
fn validate_dataclass_args<'a>(&'a self, class_name: &str) -> ValResult<GenericArguments<'a, 'py>> {
7272
match self {
7373
JsonValue::Object(object) => Ok(JsonArgs::new(None, Some(object)).into()),
7474
_ => {
@@ -241,7 +241,7 @@ impl<'py> Input<'py> for JsonValue {
241241
}
242242
}
243243

244-
fn validate_date(&self, _strict: bool) -> ValResult<ValidationMatch<EitherDate>> {
244+
fn validate_date(&self, _strict: bool) -> ValResult<ValidationMatch<EitherDate<'py>>> {
245245
match self {
246246
JsonValue::Str(v) => bytes_as_date(self, v.as_bytes()).map(ValidationMatch::strict),
247247
_ => Err(ValError::new(ErrorTypeDefaults::DateType, self)),
@@ -251,7 +251,7 @@ impl<'py> Input<'py> for JsonValue {
251251
&self,
252252
strict: bool,
253253
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
254-
) -> ValResult<ValidationMatch<EitherTime>> {
254+
) -> ValResult<ValidationMatch<EitherTime<'py>>> {
255255
match self {
256256
JsonValue::Str(v) => {
257257
bytes_as_time(self, v.as_bytes(), microseconds_overflow_behavior).map(ValidationMatch::strict)
@@ -277,7 +277,7 @@ impl<'py> Input<'py> for JsonValue {
277277
&self,
278278
strict: bool,
279279
microseconds_overflow_behavior: speedate::MicrosecondsPrecisionOverflowBehavior,
280-
) -> ValResult<ValidationMatch<EitherDateTime>> {
280+
) -> ValResult<ValidationMatch<EitherDateTime<'py>>> {
281281
match self {
282282
JsonValue::Str(v) => {
283283
bytes_as_datetime(self, v.as_bytes(), microseconds_overflow_behavior).map(ValidationMatch::strict)
@@ -292,7 +292,7 @@ impl<'py> Input<'py> for JsonValue {
292292
&self,
293293
strict: bool,
294294
microseconds_overflow_behavior: speedate::MicrosecondsPrecisionOverflowBehavior,
295-
) -> ValResult<ValidationMatch<EitherTimedelta>> {
295+
) -> ValResult<ValidationMatch<EitherTimedelta<'py>>> {
296296
match self {
297297
JsonValue::Str(v) => {
298298
bytes_as_timedelta(self, v.as_bytes(), microseconds_overflow_behavior).map(ValidationMatch::strict)
@@ -309,24 +309,24 @@ impl<'py> Input<'py> for JsonValue {
309309
}
310310

311311
/// Required for JSON Object keys so the string can behave like an Input
312-
impl<'py> Input<'py> for String {
312+
impl<'py> Input<'py> for str {
313313
fn as_error_value(&self) -> InputValue {
314314
// Justification for the clone: this is on the error pathway and we are generally ok
315315
// with errors having a performance penalty
316-
InputValue::Json(JsonValue::Str(self.clone()))
316+
InputValue::Json(JsonValue::Str(self.to_owned()))
317317
}
318318

319319
fn as_kwargs(&self, _py: Python<'py>) -> Option<Bound<'py, PyDict>> {
320320
None
321321
}
322322

323323
#[cfg_attr(has_coverage_attribute, coverage(off))]
324-
fn validate_args(&self) -> ValResult<GenericArguments<'_>> {
324+
fn validate_args(&self) -> ValResult<GenericArguments<'_, 'py>> {
325325
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
326326
}
327327

328328
#[cfg_attr(has_coverage_attribute, coverage(off))]
329-
fn validate_dataclass_args<'a>(&'a self, class_name: &str) -> ValResult<GenericArguments<'a>> {
329+
fn validate_dataclass_args<'a>(&'a self, class_name: &str) -> ValResult<GenericArguments<'a, 'py>> {
330330
let class_name = class_name.to_string();
331331
Err(ValError::new(
332332
ErrorType::DataclassType {
@@ -347,7 +347,7 @@ impl<'py> Input<'py> for String {
347347
// converting input
348348
// TODO: in V3 we may want to make JSON str always win if in union, for consistency,
349349
// see https://github.com/pydantic/pydantic-core/pull/867#discussion_r1386582501
350-
Ok(ValidationMatch::strict(self.as_str().into()))
350+
Ok(ValidationMatch::strict(self.into()))
351351
}
352352

353353
fn validate_bytes<'a>(&'a self, _strict: bool) -> ValResult<ValidationMatch<EitherBytes<'a, 'py>>> {
@@ -403,37 +403,44 @@ impl<'py> Input<'py> for String {
403403
Ok(string_to_vec(self).into())
404404
}
405405

406-
fn validate_date(&self, _strict: bool) -> ValResult<ValidationMatch<EitherDate>> {
406+
fn validate_date(&self, _strict: bool) -> ValResult<ValidationMatch<EitherDate<'py>>> {
407407
bytes_as_date(self, self.as_bytes()).map(ValidationMatch::lax)
408408
}
409409

410410
fn validate_time(
411411
&self,
412412
_strict: bool,
413413
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
414-
) -> ValResult<ValidationMatch<EitherTime>> {
414+
) -> ValResult<ValidationMatch<EitherTime<'py>>> {
415415
bytes_as_time(self, self.as_bytes(), microseconds_overflow_behavior).map(ValidationMatch::lax)
416416
}
417417

418418
fn validate_datetime(
419419
&self,
420420
_strict: bool,
421421
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
422-
) -> ValResult<ValidationMatch<EitherDateTime>> {
422+
) -> ValResult<ValidationMatch<EitherDateTime<'py>>> {
423423
bytes_as_datetime(self, self.as_bytes(), microseconds_overflow_behavior).map(ValidationMatch::lax)
424424
}
425425

426426
fn validate_timedelta(
427427
&self,
428428
_strict: bool,
429429
microseconds_overflow_behavior: MicrosecondsPrecisionOverflowBehavior,
430-
) -> ValResult<ValidationMatch<EitherTimedelta>> {
430+
) -> ValResult<ValidationMatch<EitherTimedelta<'py>>> {
431431
bytes_as_timedelta(self, self.as_bytes(), microseconds_overflow_behavior).map(ValidationMatch::lax)
432432
}
433433
}
434434

435+
impl BorrowInput<'_> for &'_ String {
436+
type Input = str;
437+
fn borrow_input(&self) -> &Self::Input {
438+
self
439+
}
440+
}
441+
435442
impl BorrowInput<'_> for String {
436-
type Input = String;
443+
type Input = str;
437444
fn borrow_input(&self) -> &Self::Input {
438445
self
439446
}

0 commit comments

Comments
 (0)