Skip to content

Commit b1226d5

Browse files
committed
Merge branch 'main' into allow_partial
2 parents 833ddfb + 5e95c05 commit b1226d5

File tree

14 files changed

+95
-152
lines changed

14 files changed

+95
-152
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ rust-version = "1.75"
3030
# TODO it would be very nice to remove the "py-clone" feature as it can panic,
3131
# but needs a bit of work to make sure it's not used in the codebase
3232
pyo3 = { version = "0.22.5", features = ["generate-import-lib", "num-bigint", "py-clone"] }
33-
regex = "1.11.0"
33+
regex = "1.11.1"
3434
strum = { version = "0.26.3", features = ["derive"] }
3535
strum_macros = "0.26.4"
3636
serde_json = {version = "1.0.132", features = ["arbitrary_precision", "preserve_order"]}
3737
enum_dispatch = "0.3.13"
38-
serde = { version = "1.0.213", features = ["derive"] }
38+
serde = { version = "1.0.214", features = ["derive"] }
3939
speedate = "0.14.4"
4040
smallvec = "1.13.2"
4141
ahash = "0.8.10"
@@ -46,7 +46,7 @@ base64 = "0.22.1"
4646
num-bigint = "0.4.6"
4747
python3-dll-a = "0.2.10"
4848
uuid = "1.11.0"
49-
jiter = { version = "0.7.0", features = ["python"] }
49+
jiter = { version = "0.7", features = ["python"] }
5050
hex = "0.4.3"
5151

5252
[lib]

python/pydantic_core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class ErrorTypeInfo(_TypedDict):
124124
"""Example of context values."""
125125

126126

127-
class MultiHostHost(_TypedDict, total=False):
127+
class MultiHostHost(_TypedDict):
128128
"""
129129
A host part of a multi-host URL.
130130
"""

python/pydantic_core/_pydantic_core.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ class Url(SupportsAllComparisons):
598598
scheme: str,
599599
username: str | None = None,
600600
password: str | None = None,
601-
host: str | None = None,
601+
host: str,
602602
port: int | None = None,
603603
path: str | None = None,
604604
query: str | None = None,
@@ -611,7 +611,7 @@ class Url(SupportsAllComparisons):
611611
scheme: The scheme part of the URL.
612612
username: The username part of the URL, or omit for no username.
613613
password: The password part of the URL, or omit for no password.
614-
host: The host part of the URL, or omit for no host.
614+
host: The host part of the URL.
615615
port: The port part of the URL, or omit for no port.
616616
path: The path part of the URL, or omit for no path.
617617
query: The query part of the URL, or omit for no query.

python/pydantic_core/core_schema.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3655,7 +3655,6 @@ class MyModel:
36553655

36563656
class UrlSchema(TypedDict, total=False):
36573657
type: Required[Literal['url']]
3658-
cls: Type[Any]
36593658
max_length: int
36603659
allowed_schemes: List[str]
36613660
host_required: bool # default False
@@ -3670,7 +3669,6 @@ class UrlSchema(TypedDict, total=False):
36703669

36713670
def url_schema(
36723671
*,
3673-
cls: Type[Any] | None = None,
36743672
max_length: int | None = None,
36753673
allowed_schemes: list[str] | None = None,
36763674
host_required: bool | None = None,
@@ -3695,7 +3693,6 @@ def url_schema(
36953693
```
36963694
36973695
Args:
3698-
cls: The class to use for the URL build (a subclass of `pydantic_core.Url`)
36993696
max_length: The maximum length of the URL
37003697
allowed_schemes: The allowed URL schemes
37013698
host_required: Whether the URL must have a host
@@ -3709,7 +3706,6 @@ def url_schema(
37093706
"""
37103707
return _dict_not_none(
37113708
type='url',
3712-
cls=cls,
37133709
max_length=max_length,
37143710
allowed_schemes=allowed_schemes,
37153711
host_required=host_required,
@@ -3725,7 +3721,6 @@ def url_schema(
37253721

37263722
class MultiHostUrlSchema(TypedDict, total=False):
37273723
type: Required[Literal['multi-host-url']]
3728-
cls: Type[Any]
37293724
max_length: int
37303725
allowed_schemes: List[str]
37313726
host_required: bool # default False
@@ -3740,7 +3735,6 @@ class MultiHostUrlSchema(TypedDict, total=False):
37403735

37413736
def multi_host_url_schema(
37423737
*,
3743-
cls: Type[Any] | None = None,
37443738
max_length: int | None = None,
37453739
allowed_schemes: list[str] | None = None,
37463740
host_required: bool | None = None,
@@ -3765,7 +3759,6 @@ def multi_host_url_schema(
37653759
```
37663760
37673761
Args:
3768-
cls: The class to use for the URL build (a subclass of `pydantic_core.MultiHostUrl`)
37693762
max_length: The maximum length of the URL
37703763
allowed_schemes: The allowed URL schemes
37713764
host_required: Whether the URL must have a host
@@ -3779,7 +3772,6 @@ def multi_host_url_schema(
37793772
"""
37803773
return _dict_not_none(
37813774
type='multi-host-url',
3782-
cls=cls,
37833775
max_length=max_length,
37843776
allowed_schemes=allowed_schemes,
37853777
host_required=host_required,

src/serializers/infer.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,9 @@ pub(crate) fn infer_to_python_known(
231231
PyList::new_bound(py, items).into_py(py)
232232
}
233233
ObType::Complex => {
234-
let dict = value.downcast::<PyDict>()?;
235-
let new_dict = PyDict::new_bound(py);
236-
let _ = new_dict.set_item("real", dict.get_item("real")?);
237-
let _ = new_dict.set_item("imag", dict.get_item("imag")?);
238-
new_dict.into_py(py)
234+
let v = value.downcast::<PyComplex>()?;
235+
let complex_str = type_serializers::complex::complex_to_str(v);
236+
complex_str.into_py(py)
239237
}
240238
ObType::Path => value.str()?.into_py(py),
241239
ObType::Pattern => value.getattr(intern!(py, "pattern"))?.into_py(py),
@@ -286,11 +284,9 @@ pub(crate) fn infer_to_python_known(
286284
iter.into_py(py)
287285
}
288286
ObType::Complex => {
289-
let dict = value.downcast::<PyDict>()?;
290-
let new_dict = PyDict::new_bound(py);
291-
let _ = new_dict.set_item("real", dict.get_item("real")?);
292-
let _ = new_dict.set_item("imag", dict.get_item("imag")?);
293-
new_dict.into_py(py)
287+
let v = value.downcast::<PyComplex>()?;
288+
let complex_str = type_serializers::complex::complex_to_str(v);
289+
complex_str.into_py(py)
294290
}
295291
ObType::Unknown => {
296292
if let Some(fallback) = extra.fallback {
@@ -422,10 +418,8 @@ pub(crate) fn infer_serialize_known<S: Serializer>(
422418
ObType::Bool => serialize!(bool),
423419
ObType::Complex => {
424420
let v = value.downcast::<PyComplex>().map_err(py_err_se_err)?;
425-
let mut map = serializer.serialize_map(Some(2))?;
426-
map.serialize_entry(&"real", &v.real())?;
427-
map.serialize_entry(&"imag", &v.imag())?;
428-
map.end()
421+
let complex_str = type_serializers::complex::complex_to_str(v);
422+
Ok(serializer.collect_str::<String>(&complex_str)?)
429423
}
430424
ObType::Float | ObType::FloatSubclass => {
431425
let v = value.extract::<f64>().map_err(py_err_se_err)?;
@@ -672,7 +666,7 @@ pub(crate) fn infer_json_key_known<'a>(
672666
}
673667
Ok(Cow::Owned(key_build.finish()))
674668
}
675-
ObType::List | ObType::Set | ObType::Frozenset | ObType::Dict | ObType::Generator | ObType::Complex => {
669+
ObType::List | ObType::Set | ObType::Frozenset | ObType::Dict | ObType::Generator => {
676670
py_err!(PyTypeError; "`{}` not valid as object key", ob_type)
677671
}
678672
ObType::Dataclass | ObType::PydanticSerializable => {
@@ -689,6 +683,10 @@ pub(crate) fn infer_json_key_known<'a>(
689683
// FIXME it would be nice to have a "PyCow" which carries ownership of the Python type too
690684
Ok(Cow::Owned(key.str()?.to_string_lossy().into_owned()))
691685
}
686+
ObType::Complex => {
687+
let v = key.downcast::<PyComplex>()?;
688+
Ok(type_serializers::complex::complex_to_str(v).into())
689+
}
692690
ObType::Pattern => Ok(Cow::Owned(
693691
key.getattr(intern!(key.py(), "pattern"))?
694692
.str()?

src/serializers/ob_type.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ impl ObTypeLookup {
252252
ObType::Url
253253
} else if ob_type == self.multi_host_url {
254254
ObType::MultiHostUrl
255+
} else if ob_type == self.complex {
256+
ObType::Complex
255257
} else if ob_type == self.uuid_object.as_ptr() as usize {
256258
ObType::Uuid
257259
} else if is_pydantic_serializable(op_value) {

src/serializers/type_serializers/complex.rs

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,10 @@ impl TypeSerializer for ComplexSerializer {
3333
) -> PyResult<PyObject> {
3434
let py = value.py();
3535
match value.downcast::<PyComplex>() {
36-
Ok(py_complex) => match extra.mode {
37-
SerMode::Json => {
38-
let re = py_complex.real();
39-
let im = py_complex.imag();
40-
let mut s = format!("{im}j");
41-
if re != 0.0 {
42-
let mut sign = "";
43-
if im >= 0.0 {
44-
sign = "+";
45-
}
46-
s = format!("{re}{sign}{s}");
47-
}
48-
Ok(s.into_py(py))
49-
}
50-
_ => Ok(value.into_py(py)),
51-
},
36+
Ok(py_complex) => Ok(match extra.mode {
37+
SerMode::Json => complex_to_str(py_complex).into_py(py),
38+
_ => value.into_py(py),
39+
}),
5240
Err(_) => {
5341
extra.warnings.on_fallback_py(self.get_name(), value, extra)?;
5442
infer_to_python(value, include, exclude, extra)
@@ -70,16 +58,7 @@ impl TypeSerializer for ComplexSerializer {
7058
) -> Result<S::Ok, S::Error> {
7159
match value.downcast::<PyComplex>() {
7260
Ok(py_complex) => {
73-
let re = py_complex.real();
74-
let im = py_complex.imag();
75-
let mut s = format!("{im}j");
76-
if re != 0.0 {
77-
let mut sign = "";
78-
if im >= 0.0 {
79-
sign = "+";
80-
}
81-
s = format!("{re}{sign}{s}");
82-
}
61+
let s = complex_to_str(py_complex);
8362
Ok(serializer.collect_str::<String>(&s)?)
8463
}
8564
Err(_) => {
@@ -93,3 +72,17 @@ impl TypeSerializer for ComplexSerializer {
9372
"complex"
9473
}
9574
}
75+
76+
pub fn complex_to_str(py_complex: &Bound<'_, PyComplex>) -> String {
77+
let re = py_complex.real();
78+
let im = py_complex.imag();
79+
let mut s = format!("{im}j");
80+
if re != 0.0 {
81+
let mut sign = "";
82+
if im >= 0.0 {
83+
sign = "+";
84+
}
85+
s = format!("{re}{sign}{s}");
86+
}
87+
s
88+
}

src/url.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,12 @@ impl PyUrl {
156156
}
157157

158158
#[classmethod]
159-
#[pyo3(signature=(*, scheme, host=None, username=None, password=None, port=None, path=None, query=None, fragment=None))]
159+
#[pyo3(signature=(*, scheme, host, username=None, password=None, port=None, path=None, query=None, fragment=None))]
160160
#[allow(clippy::too_many_arguments)]
161161
pub fn build<'py>(
162162
cls: &Bound<'py, PyType>,
163163
scheme: &str,
164-
host: Option<&str>,
164+
host: &str,
165165
username: Option<&str>,
166166
password: Option<&str>,
167167
port: Option<u16>,
@@ -172,7 +172,7 @@ impl PyUrl {
172172
let url_host = UrlHostParts {
173173
username: username.map(Into::into),
174174
password: password.map(Into::into),
175-
host: host.map(Into::into),
175+
host: Some(host.into()),
176176
port,
177177
};
178178
let mut url = format!("{scheme}://{url_host}");
@@ -423,7 +423,6 @@ impl PyMultiHostUrl {
423423
}
424424
}
425425

426-
#[cfg_attr(debug_assertions, derive(Debug))]
427426
pub struct UrlHostParts {
428427
username: Option<String>,
429428
password: Option<String>,
@@ -441,12 +440,11 @@ impl FromPyObject<'_> for UrlHostParts {
441440
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
442441
let py = ob.py();
443442
let dict = ob.downcast::<PyDict>()?;
444-
445443
Ok(UrlHostParts {
446-
username: dict.get_as::<Option<_>>(intern!(py, "username"))?.flatten(),
447-
password: dict.get_as::<Option<_>>(intern!(py, "password"))?.flatten(),
448-
host: dict.get_as::<Option<_>>(intern!(py, "host"))?.flatten(),
449-
port: dict.get_as::<Option<_>>(intern!(py, "port"))?.flatten(),
444+
username: dict.get_as(intern!(py, "username"))?,
445+
password: dict.get_as(intern!(py, "password"))?,
446+
host: dict.get_as(intern!(py, "host"))?,
447+
port: dict.get_as(intern!(py, "port"))?,
450448
})
451449
}
452450
}

src/validators/string.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,14 @@ impl StrConstrainedValidator {
200200
}
201201

202202
// whether any of the constraints/customisations are actually enabled
203-
// except strict which can be set on StrValidator
203+
// except strict and coerce_numbers_to_str which can be set on StrValidator
204204
fn has_constraints_set(&self) -> bool {
205205
self.pattern.is_some()
206206
|| self.max_length.is_some()
207207
|| self.min_length.is_some()
208208
|| self.strip_whitespace
209209
|| self.to_lower
210210
|| self.to_upper
211-
|| self.coerce_numbers_to_str
212211
}
213212
}
214213

0 commit comments

Comments
 (0)