Skip to content

Commit 8727e51

Browse files
committed
chore: Merge branch 'main' of github.com:ollz272/pydantic-core into add-build-method-to-multihosturl
2 parents 52dd633 + 1a17d42 commit 8727e51

39 files changed

+469
-851
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ jobs:
103103

104104
- run: pytest
105105
env:
106-
BENCHMARK_VS_PYDANTIC: 1
107106
HYPOTHESIS_PROFILE: slow
108107

109108
test-os:
@@ -142,17 +141,25 @@ jobs:
142141

143142
# test with a debug build as it picks up errors which optimised release builds do not
144143
test-debug:
144+
name: test-debug ${{ matrix.python-version }}
145145
runs-on: ubuntu-latest
146146

147+
strategy:
148+
fail-fast: false
149+
matrix:
150+
python-version:
151+
- '3.11'
152+
- 'pypy3.9'
153+
147154
steps:
148155
- uses: actions/checkout@v3
149156
- name: set up python
150157
uses: actions/setup-python@v4
151158
with:
152-
python-version: '3.11'
159+
python-version: ${{ matrix.python-version }}
153160

154161
- run: pip install -r tests/requirements.txt
155-
- run: pip install -e . --config-settings=build-args='--profile dev'
162+
- run: make build-dev
156163

157164
- run: pip freeze
158165
- run: pytest
@@ -194,7 +201,7 @@ jobs:
194201
- run: pip install -r tests/requirements-linting.txt
195202
if: steps.cache-py.outputs.cache-hit != 'true'
196203

197-
- run: pip install .
204+
- run: make build-dev
198205

199206
- run: pip freeze
200207

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pydantic-core"
3-
version = "2.1.2"
3+
version = "2.1.3"
44
edition = "2021"
55
license = "MIT"
66
homepage = "https://github.com/pydantic/pydantic-core"
@@ -35,7 +35,7 @@ enum_dispatch = "0.3.8"
3535
serde = "1.0.147"
3636
# disabled for benchmarks since it makes microbenchmark performance more flakey
3737
mimalloc = { version = "0.1.30", optional = true, default-features = false, features = ["local_dynamic_tls"] }
38-
speedate = "0.9.0"
38+
speedate = "0.9.1"
3939
ahash = "0.8.0"
4040
url = "2.3.1"
4141
# idna is already required by url, added here to be explicit

Makefile

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ black = black python/pydantic_core tests generate_self_schema.py wasm-preview/ru
33
ruff = ruff python/pydantic_core tests generate_self_schema.py wasm-preview/run_tests.py
44
mypy-stubtest = python -m mypy.stubtest pydantic_core._pydantic_core --allowlist .mypy-stubtest-allowlist
55

6+
# using pip install cargo (via maturin via pip) doesn't get the tty handle
7+
# so doesn't render color without some help
8+
export CARGO_TERM_COLOR=$(shell (test -t 0 && echo "always") || echo "auto")
9+
# maturin develop only makes sense inside a virtual env, is otherwise
10+
# more or less equivalent to pip install -e just a little nicer
11+
USE_MATURIN = $(shell [ "$$VIRTUAL_ENV" != "" ] && (which maturin))
12+
613
.PHONY: install
714
install:
815
pip install -U pip wheel pre-commit
@@ -16,23 +23,55 @@ install-rust-coverage:
1623
cargo install rustfilt coverage-prepare
1724
rustup component add llvm-tools-preview
1825

26+
.PHONY: install-pgo
27+
rustup component add llvm-tools-preview
28+
1929
.PHONY: build-dev
2030
build-dev:
2131
@rm -f python/pydantic_core/*.so
22-
cargo build --features extension-module
23-
@rm -f target/debug/lib_pydantic_core.d
24-
@rm -f target/debug/lib_pydantic_core.rlib
25-
@mv target/debug/lib_pydantic_core.* python/pydantic_core/_pydantic_core.so
32+
ifneq ($(USE_MATURIN),)
33+
maturin develop
34+
else
35+
pip install -v -e . --config-settings=build-args='--profile dev'
36+
endif
2637

2738
.PHONY: build-prod
2839
build-prod:
2940
@rm -f python/pydantic_core/*.so
41+
ifneq ($(USE_MATURIN),)
3042
maturin develop --release
43+
else
44+
pip install -v -e .
45+
endif
3146

3247
.PHONY: build-coverage
3348
build-coverage:
34-
rm -f python/pydantic_core/*.so
35-
maturin develop -- -C instrument-coverage
49+
@rm -f python/pydantic_core/*.so
50+
ifneq ($(USE_MATURIN),)
51+
RUSTFLAGS='-C instrument-coverage' maturin develop --release
52+
else
53+
RUSTFLAGS='-C instrument-coverage' pip install -v -e .
54+
endif
55+
56+
.PHONY: build-pgo
57+
build-pgo:
58+
@rm -f python/pydantic_core/*.so
59+
$(eval PROFDATA := $(shell mktemp -d))
60+
ifneq ($(USE_MATURIN),)
61+
RUSTFLAGS='-Cprofile-generate=$(PROFDATA)' maturin develop --release
62+
else
63+
RUSTFLAGS='-Cprofile-generate=$(PROFDATA)' pip install -v -e .
64+
endif
65+
pytest tests/benchmarks
66+
$(eval LLVM_PROFDATA := $(shell rustup run stable bash -c 'echo $$RUSTUP_HOME/toolchains/$$RUSTUP_TOOLCHAIN/lib/rustlib/$$(rustc -Vv | grep host | cut -d " " -f 2)/bin/llvm-profdata'))
67+
$(LLVM_PROFDATA) merge -o $(PROFDATA)/merged.profdata $(PROFDATA)
68+
ifneq ($(USE_MATURIN),)
69+
RUSTFLAGS='-Cprofile-use=$(PROFDATA)/merged.profdata' maturin develop --release
70+
else
71+
RUSTFLAGS='-Cprofile-use=$(PROFDATA)/merged.profdata' pip install -v -e .
72+
endif
73+
@rm -rf $(PROFDATA)
74+
3675

3776
.PHONY: build-wasm
3877
build-wasm:

python/pydantic_core/_pydantic_core.pyi

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ from _typeshed import SupportsAllComparisons
2222
__all__ = [
2323
'__version__',
2424
'build_profile',
25+
'build_info',
26+
'_recursion_limit',
2527
'ArgsKwargs',
2628
'SchemaValidator',
2729
'SchemaSerializer',
@@ -44,6 +46,8 @@ __all__ = [
4446
]
4547
__version__: str
4648
build_profile: str
49+
build_info: str
50+
_recursion_limit: int
4751

4852
_T = TypeVar('_T', default=Any, covariant=True)
4953

@@ -58,7 +62,7 @@ class Some(Generic[_T]):
5862

5963
@final
6064
class SchemaValidator:
61-
def __init__(self, schema: CoreSchema, config: CoreConfig | None = None) -> None: ...
65+
def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None) -> Self: ...
6266
@property
6367
def title(self) -> str: ...
6468
def validate_python(
@@ -103,7 +107,7 @@ _IncEx: TypeAlias = set[int] | set[str] | dict[int, _IncEx] | dict[str, _IncEx]
103107

104108
@final
105109
class SchemaSerializer:
106-
def __init__(self, schema: CoreSchema, config: CoreConfig | None = None) -> None: ...
110+
def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None) -> Self: ...
107111
def to_python(
108112
self,
109113
value: Any,
@@ -164,7 +168,7 @@ def to_jsonable_python(
164168
) -> Any: ...
165169

166170
class Url(SupportsAllComparisons):
167-
def __init__(self, url: str) -> None: ...
171+
def __new__(cls, url: str) -> Self: ...
168172
@property
169173
def scheme(self) -> str: ...
170174
@property
@@ -202,7 +206,7 @@ class Url(SupportsAllComparisons):
202206
) -> str: ...
203207

204208
class MultiHostUrl(SupportsAllComparisons):
205-
def __init__(self, url: str) -> None: ...
209+
def __new__(cls, url: str) -> Self: ...
206210
@property
207211
def scheme(self) -> str: ...
208212
@property
@@ -260,9 +264,9 @@ class ValidationError(ValueError):
260264

261265
@final
262266
class PydanticCustomError(ValueError):
263-
def __init__(
264-
self, error_type: LiteralString, message_template: LiteralString, context: dict[str, Any] | None = None
265-
) -> None: ...
267+
def __new__(
268+
cls, error_type: LiteralString, message_template: LiteralString, context: dict[str, Any] | None = None
269+
) -> Self: ...
266270
@property
267271
def context(self) -> dict[str, Any] | None: ...
268272
@property
@@ -273,9 +277,9 @@ class PydanticCustomError(ValueError):
273277

274278
@final
275279
class PydanticKnownError(ValueError):
276-
def __init__(
277-
self, error_type: ErrorType, context: dict[str, str | int | float | decimal.Decimal] | None = None
278-
) -> None: ...
280+
def __new__(
281+
cls, error_type: ErrorType, context: dict[str, str | int | float | decimal.Decimal] | None = None
282+
) -> Self: ...
279283
@property
280284
def context(self) -> dict[str, str | int | float] | None: ...
281285
@property
@@ -286,23 +290,23 @@ class PydanticKnownError(ValueError):
286290

287291
@final
288292
class PydanticOmit(Exception):
289-
def __new__(self) -> PydanticOmit: ...
293+
def __new__(cls) -> Self: ...
290294

291295
@final
292296
class PydanticUseDefault(Exception):
293-
def __new__(self) -> PydanticUseDefault: ...
297+
def __new__(cls) -> Self: ...
294298

295299
@final
296300
class PydanticSerializationError(ValueError):
297-
def __init__(self, message: str) -> None: ...
301+
def __new__(cls, message: str) -> Self: ...
298302

299303
@final
300304
class PydanticSerializationUnexpectedValue(ValueError):
301-
def __init__(self, message: str | None = None) -> None: ...
305+
def __new__(cls, message: str | None = None) -> Self: ...
302306

303307
@final
304308
class ArgsKwargs:
305-
def __init__(self, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> None: ...
309+
def __new__(cls, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> Self: ...
306310
@property
307311
def args(self) -> tuple[Any, ...]: ...
308312
@property

python/pydantic_core/core_schema.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3811,6 +3811,7 @@ def definition_reference_schema(
38113811
'string_too_short',
38123812
'string_too_long',
38133813
'string_pattern_mismatch',
3814+
'enum',
38143815
'dict_type',
38153816
'mapping_type',
38163817
'list_type',

src/errors/types.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ pub enum ErrorType {
159159
pattern: String,
160160
},
161161
// ---------------------
162+
// enum errors
163+
Enum {
164+
expected: String,
165+
},
166+
// ---------------------
162167
// dict errors
163168
DictType,
164169
MappingType {
@@ -199,10 +204,10 @@ pub enum ErrorType {
199204
// ---------------------
200205
// python errors from functions
201206
ValueError {
202-
error: String,
207+
error: Option<PyObject>, // Use Option because EnumIter requires Default to be implemented
203208
},
204209
AssertionError {
205-
error: String,
210+
error: Option<PyObject>, // Use Option because EnumIter requires Default to be implemented
206211
},
207212
// Note: strum message and serialize are not used here
208213
CustomError {
@@ -415,12 +420,13 @@ impl ErrorType {
415420
Self::IterationError { .. } => extract_context!(IterationError, ctx, error: String),
416421
Self::StringTooShort { .. } => extract_context!(StringTooShort, ctx, min_length: usize),
417422
Self::StringTooLong { .. } => extract_context!(StringTooLong, ctx, max_length: usize),
423+
Self::Enum { .. } => extract_context!(Enum, ctx, expected: String),
418424
Self::StringPatternMismatch { .. } => extract_context!(StringPatternMismatch, ctx, pattern: String),
419425
Self::MappingType { .. } => extract_context!(Cow::Owned, MappingType, ctx, error: String),
420426
Self::BytesTooShort { .. } => extract_context!(BytesTooShort, ctx, min_length: usize),
421427
Self::BytesTooLong { .. } => extract_context!(BytesTooLong, ctx, max_length: usize),
422-
Self::ValueError { .. } => extract_context!(ValueError, ctx, error: String),
423-
Self::AssertionError { .. } => extract_context!(AssertionError, ctx, error: String),
428+
Self::ValueError { .. } => extract_context!(ValueError, ctx, error: Option<PyObject>),
429+
Self::AssertionError { .. } => extract_context!(AssertionError, ctx, error: Option<PyObject>),
424430
Self::LiteralError { .. } => extract_context!(LiteralError, ctx, expected: String),
425431
Self::DateParsing { .. } => extract_context!(Cow::Owned, DateParsing, ctx, error: String),
426432
Self::DateFromDatetimeParsing { .. } => extract_context!(DateFromDatetimeParsing, ctx, error: String),
@@ -492,6 +498,7 @@ impl ErrorType {
492498
Self::StringTooShort {..} => "String should have at least {min_length} characters",
493499
Self::StringTooLong {..} => "String should have at most {max_length} characters",
494500
Self::StringPatternMismatch {..} => "String should match pattern '{pattern}'",
501+
Self::Enum {..} => "Input should be {expected}",
495502
Self::DictType => "Input should be a valid dictionary",
496503
Self::MappingType {..} => "Input should be a valid mapping, error: {error}",
497504
Self::ListType => "Input should be a valid list",
@@ -628,11 +635,22 @@ impl ErrorType {
628635
Self::StringTooShort { min_length } => to_string_render!(tmpl, min_length),
629636
Self::StringTooLong { max_length } => to_string_render!(tmpl, max_length),
630637
Self::StringPatternMismatch { pattern } => render!(tmpl, pattern),
638+
Self::Enum { expected } => to_string_render!(tmpl, expected),
631639
Self::MappingType { error } => render!(tmpl, error),
632640
Self::BytesTooShort { min_length } => to_string_render!(tmpl, min_length),
633641
Self::BytesTooLong { max_length } => to_string_render!(tmpl, max_length),
634-
Self::ValueError { error } => render!(tmpl, error),
635-
Self::AssertionError { error } => render!(tmpl, error),
642+
Self::ValueError { error, .. } => {
643+
let error = &error
644+
.as_ref()
645+
.map_or(Cow::Borrowed("None"), |v| Cow::Owned(v.as_ref(py).to_string()));
646+
render!(tmpl, error)
647+
}
648+
Self::AssertionError { error, .. } => {
649+
let error = &error
650+
.as_ref()
651+
.map_or(Cow::Borrowed("None"), |v| Cow::Owned(v.as_ref(py).to_string()));
652+
render!(tmpl, error)
653+
}
636654
Self::CustomError {
637655
custom_error: value_error,
638656
} => value_error.message(py),
@@ -687,6 +705,7 @@ impl ErrorType {
687705
Self::StringTooShort { min_length } => py_dict!(py, min_length),
688706
Self::StringTooLong { max_length } => py_dict!(py, max_length),
689707
Self::StringPatternMismatch { pattern } => py_dict!(py, pattern),
708+
Self::Enum { expected } => py_dict!(py, expected),
690709
Self::MappingType { error } => py_dict!(py, error),
691710
Self::BytesTooShort { min_length } => py_dict!(py, min_length),
692711
Self::BytesTooLong { max_length } => py_dict!(py, max_length),

0 commit comments

Comments
 (0)