Skip to content

Commit 4d38167

Browse files
committed
update things
2 parents fb5cecb + 20d576b commit 4d38167

Some content is hidden

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

81 files changed

+2976
-2245
lines changed

.github/workflows/ci.yml

Lines changed: 107 additions & 87 deletions
Large diffs are not rendered by default.

.github/workflows/codspeed.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
python-version: ${{ env.UV_PYTHON}}
2727

2828
- name: install uv
29-
uses: astral-sh/setup-uv@v6
29+
uses: astral-sh/setup-uv@v7
3030
with:
3131
enable-cache: true
3232

Cargo.lock

Lines changed: 10 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pydantic-core"
3-
version = "2.40.1"
3+
version = "2.41.4"
44
edition = "2021"
55
license = "MIT"
66
homepage = "https://github.com/pydantic/pydantic-core"
@@ -28,7 +28,7 @@ rust-version = "1.75"
2828
# TODO it would be very nice to remove the "py-clone" feature as it can panic,
2929
# but needs a bit of work to make sure it's not used in the codebase
3030
pyo3 = { version = "0.26", features = ["generate-import-lib", "num-bigint", "py-clone"] }
31-
regex = "1.11.3"
31+
regex = "1.12.2"
3232
strum = { version = "0.27", features = ["derive"] }
3333
strum_macros = "0.27"
3434
serde_json = { version = "1.0.145", features = ["arbitrary_precision"] }
@@ -44,8 +44,9 @@ base64 = "0.22.1"
4444
num-bigint = "0.4.6"
4545
num-traits = "0.2.19"
4646
uuid = "1.18.1"
47-
jiter = { version = "0.11.0", features = ["python"] }
47+
jiter = { version = "0.11.1", features = ["python"] }
4848
hex = "0.4.3"
49+
percent-encoding = "2.3.2"
4950

5051
[lib]
5152
name = "_pydantic_core"
@@ -90,9 +91,7 @@ doc_markdown = "allow"
9091
float_cmp = "allow"
9192
fn_params_excessive_bools = "allow"
9293
if_not_else = "allow"
93-
manual_let_else = "allow"
9494
match_bool = "allow"
95-
match_same_arms = "allow"
9695
missing_errors_doc = "allow"
9796
missing_panics_doc = "allow"
9897
module_name_repetitions = "allow"

python/pydantic_core/core_schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class CoreConfig(TypedDict, total=False):
8080
validate_by_alias: Whether to use the field's alias when validating against the provided input data. Default is `True`.
8181
validate_by_name: Whether to use the field's name when validating against the provided input data. Default is `False`. Replacement for `populate_by_name`.
8282
serialize_by_alias: Whether to serialize by alias. Default is `False`, expected to change to `True` in V3.
83+
url_preserve_empty_path: Whether to preserve empty URL paths when validating values for a URL type. Defaults to `False`.
8384
"""
8485

8586
title: str
@@ -120,6 +121,7 @@ class CoreConfig(TypedDict, total=False):
120121
validate_by_alias: bool # default: True
121122
validate_by_name: bool # default: False
122123
serialize_by_alias: bool # default: False
124+
url_preserve_empty_path: bool # default: False
123125

124126

125127
IncExCall: TypeAlias = 'set[int | str] | dict[int | str, IncExCall] | None'
@@ -4294,6 +4296,7 @@ def definition_reference_schema(
42944296
'model_attributes_type',
42954297
'dataclass_type',
42964298
'dataclass_exact_type',
4299+
'default_factory_not_called',
42974300
'none_required',
42984301
'greater_than',
42994302
'greater_than_equal',

src/argument_markers.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@ impl PydanticUndefinedType {
6060
}
6161

6262
#[staticmethod]
63-
pub fn new(py: Python) -> Py<Self> {
64-
UNDEFINED_CELL
65-
.get_or_init(py, || Py::new(py, PydanticUndefinedType {}).unwrap())
66-
.clone_ref(py)
63+
#[pyo3(name = "new")]
64+
pub fn get(py: Python<'_>) -> &Py<Self> {
65+
UNDEFINED_CELL.get_or_init(py, || Py::new(py, PydanticUndefinedType {}).unwrap())
6766
}
6867

6968
fn __repr__(&self) -> &'static str {

src/errors/location.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl From<String> for LocItem {
4141

4242
impl From<&String> for LocItem {
4343
fn from(s: &String) -> Self {
44-
s.to_string().into()
44+
s.clone().into()
4545
}
4646
}
4747

@@ -87,22 +87,17 @@ impl Serialize for LocItem {
8787
/// Note: location in List is stored in **REVERSE** so adding an "outer" item to location involves
8888
/// pushing to the vec which is faster than inserting and shifting everything along.
8989
/// Then when "using" location in `Display` and `ToPyObject` order has to be reversed
90-
#[derive(Clone)]
90+
#[derive(Clone, Default)]
9191
#[cfg_attr(debug_assertions, derive(Debug))]
9292
pub enum Location {
9393
// no location, avoid creating an unnecessary vec
94+
#[default]
9495
Empty,
9596
// store the in a vec of LocItems, Note: this is the REVERSE of location, see above
9697
// we could perhaps use a smallvec or similar here, probably only worth it if we store a Cow in LocItem
9798
List(Vec<LocItem>),
9899
}
99100

100-
impl Default for Location {
101-
fn default() -> Self {
102-
Self::Empty
103-
}
104-
}
105-
106101
static EMPTY_TUPLE: PyOnceLock<Py<PyTuple>> = PyOnceLock::new();
107102

108103
impl<'py> IntoPyObject<'py> for &'_ Location {

src/errors/types.rs

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ error_types! {
196196
class_name: {ctx_type: String, ctx_fn: field_from_context},
197197
},
198198
// ---------------------
199+
// Default factory not called (happens when there's already an error and the factory takes data)
200+
DefaultFactoryNotCalled {},
201+
// ---------------------
199202
// None errors
200203
NoneRequired {},
201204
// ---------------------
@@ -480,6 +483,7 @@ impl ErrorType {
480483
}
481484

482485
pub fn message_template_python(&self) -> &'static str {
486+
#[allow(clippy::match_same_arms)] // much nicer to have the messages explicitly listed
483487
match self {
484488
Self::NoSuchAttribute {..} => "Object has no attribute '{attribute}'",
485489
Self::JsonInvalid {..} => "Invalid JSON: {error}",
@@ -496,6 +500,7 @@ impl ErrorType {
496500
Self::ModelAttributesType {..} => "Input should be a valid dictionary or object to extract fields from",
497501
Self::DataclassType {..} => "Input should be a dictionary or an instance of {class_name}",
498502
Self::DataclassExactType {..} => "Input should be an instance of {class_name}",
503+
Self::DefaultFactoryNotCalled {..} => "The default factory uses validated data, but at least one validation error occurred",
499504
Self::NoneRequired {..} => "Input should be None",
500505
Self::GreaterThan {..} => "Input should be greater than {gt}",
501506
Self::GreaterThanEqual {..} => "Input should be greater than or equal to {ge}",
@@ -637,12 +642,24 @@ impl ErrorType {
637642
};
638643
match self {
639644
Self::NoSuchAttribute { attribute, .. } => render!(tmpl, attribute),
640-
Self::JsonInvalid { error, .. } => render!(tmpl, error),
645+
Self::JsonInvalid { error, .. }
646+
| Self::GetAttributeError { error, .. }
647+
| Self::IterationError { error, .. }
648+
| Self::DatetimeObjectInvalid { error, .. }
649+
| Self::UrlParsing { error, .. }
650+
| Self::UuidParsing { error, .. } => render!(tmpl, error),
651+
Self::MappingType { error, .. }
652+
| Self::DateParsing { error, .. }
653+
| Self::DateFromDatetimeParsing { error, .. }
654+
| Self::TimeParsing { error, .. }
655+
| Self::DatetimeParsing { error, .. }
656+
| Self::DatetimeFromDateParsing { error, .. }
657+
| Self::TimeDeltaParsing { error, .. }
658+
| Self::UrlSyntaxViolation { error, .. } => render!(tmpl, error),
641659
Self::NeedsPythonObject { method_name, .. } => render!(tmpl, method_name),
642-
Self::GetAttributeError { error, .. } => render!(tmpl, error),
643-
Self::ModelType { class_name, .. } => render!(tmpl, class_name),
644-
Self::DataclassType { class_name, .. } => render!(tmpl, class_name),
645-
Self::DataclassExactType { class_name, .. } => render!(tmpl, class_name),
660+
Self::ModelType { class_name, .. }
661+
| Self::DataclassType { class_name, .. }
662+
| Self::DataclassExactType { class_name, .. } => render!(tmpl, class_name),
646663
Self::GreaterThan { gt, .. } => to_string_render!(tmpl, gt),
647664
Self::GreaterThanEqual { ge, .. } => to_string_render!(tmpl, ge),
648665
Self::LessThan { lt, .. } => to_string_render!(tmpl, lt),
@@ -667,26 +684,18 @@ impl ErrorType {
667684
let actual_length = actual_length.map_or(Cow::Borrowed("more"), |v| Cow::Owned(v.to_string()));
668685
to_string_render!(tmpl, field_type, max_length, actual_length, expected_plural,)
669686
}
670-
Self::IterationError { error, .. } => render!(tmpl, error),
671-
Self::StringTooShort { min_length, .. } => {
687+
Self::StringTooShort { min_length, .. } | Self::BytesTooShort { min_length, .. } => {
672688
let expected_plural = plural_s(*min_length);
673689
to_string_render!(tmpl, min_length, expected_plural)
674690
}
675-
Self::StringTooLong { max_length, .. } => {
691+
Self::StringTooLong { max_length, .. }
692+
| Self::BytesTooLong { max_length, .. }
693+
| Self::UrlTooLong { max_length, .. } => {
676694
let expected_plural = plural_s(*max_length);
677695
to_string_render!(tmpl, max_length, expected_plural)
678696
}
679697
Self::StringPatternMismatch { pattern, .. } => render!(tmpl, pattern),
680698
Self::Enum { expected, .. } => to_string_render!(tmpl, expected),
681-
Self::MappingType { error, .. } => render!(tmpl, error),
682-
Self::BytesTooShort { min_length, .. } => {
683-
let expected_plural = plural_s(*min_length);
684-
to_string_render!(tmpl, min_length, expected_plural)
685-
}
686-
Self::BytesTooLong { max_length, .. } => {
687-
let expected_plural = plural_s(*max_length);
688-
to_string_render!(tmpl, max_length, expected_plural)
689-
}
690699
Self::BytesInvalidEncoding {
691700
encoding,
692701
encoding_error,
@@ -710,33 +719,18 @@ impl ErrorType {
710719
..
711720
} => PydanticCustomError::format_message(message_template, context.as_ref().map(|c| c.bind(py))),
712721
Self::LiteralError { expected, .. } => render!(tmpl, expected),
713-
Self::DateParsing { error, .. } => render!(tmpl, error),
714-
Self::DateFromDatetimeParsing { error, .. } => render!(tmpl, error),
715-
Self::TimeParsing { error, .. } => render!(tmpl, error),
716-
Self::DatetimeParsing { error, .. } => render!(tmpl, error),
717-
Self::DatetimeFromDateParsing { error, .. } => render!(tmpl, error),
718-
Self::DatetimeObjectInvalid { error, .. } => render!(tmpl, error),
719722
Self::TimezoneOffset {
720723
tz_expected, tz_actual, ..
721724
} => to_string_render!(tmpl, tz_expected, tz_actual),
722-
Self::TimeDeltaParsing { error, .. } => render!(tmpl, error),
723-
Self::IsInstanceOf { class, .. } => render!(tmpl, class),
724-
Self::IsSubclassOf { class, .. } => render!(tmpl, class),
725+
Self::IsInstanceOf { class, .. } | Self::IsSubclassOf { class, .. } => render!(tmpl, class),
725726
Self::UnionTagInvalid {
726727
discriminator,
727728
tag,
728729
expected_tags,
729730
..
730731
} => render!(tmpl, discriminator, tag, expected_tags),
731732
Self::UnionTagNotFound { discriminator, .. } => render!(tmpl, discriminator),
732-
Self::UrlParsing { error, .. } => render!(tmpl, error),
733-
Self::UrlSyntaxViolation { error, .. } => render!(tmpl, error),
734-
Self::UrlTooLong { max_length, .. } => {
735-
let expected_plural = plural_s(*max_length);
736-
to_string_render!(tmpl, max_length, expected_plural)
737-
}
738733
Self::UrlScheme { expected_schemes, .. } => render!(tmpl, expected_schemes),
739-
Self::UuidParsing { error, .. } => render!(tmpl, error),
740734
Self::UuidVersion { expected_version, .. } => to_string_render!(tmpl, expected_version),
741735
Self::DecimalMaxDigits { max_digits, .. } => {
742736
let expected_plural = plural_s(*max_digits);

0 commit comments

Comments
 (0)