diff --git a/Cargo.lock b/Cargo.lock index 1b8e38993..e443dbd30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -431,7 +431,7 @@ dependencies = [ [[package]] name = "pydantic-core" -version = "2.38.0" +version = "2.39.0" dependencies = [ "ahash", "base64", diff --git a/Cargo.toml b/Cargo.toml index 8b0ef81e1..fa7bff329 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pydantic-core" -version = "2.38.0" +version = "2.39.0" edition = "2021" license = "MIT" homepage = "https://github.com/pydantic/pydantic-core" diff --git a/Makefile b/Makefile index 6ca8fc075..385797581 100644 --- a/Makefile +++ b/Makefile @@ -14,50 +14,45 @@ USE_MATURIN = $(shell [ "$$VIRTUAL_ENV" != "" ] && (which maturin)) .uv: @uv -V || echo 'Please install uv: https://docs.astral.sh/uv/getting-started/installation/' -.PHONY: .pre-commit ## Check that pre-commit is installed -.pre-commit: - @pre-commit -V || echo 'Please install pre-commit: https://pre-commit.com/' - -.PHONY: install -install: .uv .pre-commit - uv pip install -U wheel +.PHONY: install ## Install the package, dependencies, and pre-commit for local development +install: .uv uv sync --frozen --group all - uv pip install -v -e . - pre-commit install + uv run pre-commit install --install-hooks .PHONY: rebuild-lockfiles ## Rebuild lockfiles from scratch, updating all dependencies rebuild-lockfiles: .uv uv lock --upgrade -.PHONY: install-rust-coverage +.PHONY: install-rust-coverage ## Install Rust coverage tools install-rust-coverage: cargo install rustfilt coverage-prepare rustup component add llvm-tools-preview -.PHONY: install-pgo +.PHONY: install-pgo ## Install Rust PGO tools +install-pgo: rustup component add llvm-tools-preview -.PHONY: build-dev +.PHONY: build-dev ## Build the development version of the package build-dev: @rm -f python/pydantic_core/*.so uv run maturin develop --uv -.PHONY: build-prod +.PHONY: build-prod ## Build the production version of the package build-prod: @rm -f python/pydantic_core/*.so uv run maturin develop --uv --release -.PHONY: build-profiling +.PHONY: build-profiling ## Build the profiling version of the package build-profiling: @rm -f python/pydantic_core/*.so uv run maturin develop --uv --profile profiling -.PHONY: build-coverage +.PHONY: build-coverage ## Build the coverage version of the package build-coverage: @rm -f python/pydantic_core/*.so RUSTFLAGS='-C instrument-coverage' uv run maturin develop --uv --release -.PHONY: build-pgo +.PHONY: build-pgo ## Build the PGO version of the package build-pgo: @rm -f python/pydantic_core/*.so $(eval PROFDATA := $(shell mktemp -d)) @@ -69,44 +64,44 @@ build-pgo: @rm -rf $(PROFDATA) -.PHONY: build-wasm +.PHONY: build-wasm ## Build the WebAssembly version of the package build-wasm: @echo 'This requires python 3.12, maturin and emsdk to be installed' uv run maturin build --release --target wasm32-unknown-emscripten --out dist -i 3.12 ls -lh dist -.PHONY: format +.PHONY: format ## Auto-format rust and python source files format: uv run ruff check --fix $(sources) uv run ruff format $(sources) cargo fmt -.PHONY: lint-python +.PHONY: lint-python ## Lint python source files lint-python: uv run ruff check $(sources) uv run ruff format --check $(sources) uv run griffe dump -f -d google -LWARNING -o/dev/null python/pydantic_core $(mypy-stubtest) -.PHONY: lint-rust +.PHONY: lint-rust ## Lint rust source files lint-rust: cargo fmt --version cargo fmt --all -- --check cargo clippy --version cargo clippy --tests -- -D warnings -.PHONY: lint +.PHONY: lint ## Lint rust and python source files lint: lint-python lint-rust -.PHONY: pyright +.PHONY: pyright ## Perform type-checking with pyright pyright: uv run pyright -.PHONY: test +.PHONY: test ## Run all tests test: uv run pytest -.PHONY: testcov +.PHONY: testcov ## Run tests and generate a coverage report testcov: build-coverage @rm -rf htmlcov @mkdir -p htmlcov @@ -115,10 +110,10 @@ testcov: build-coverage coverage html -d htmlcov/python coverage-prepare html python/pydantic_core/*.so -.PHONY: all +.PHONY: all ## Run the standard set of checks performed in CI all: format build-dev lint test -.PHONY: clean +.PHONY: clean ## Clear local caches and build artifacts clean: rm -rf `find . -name __pycache__` rm -f `find . -type f -name '*.py[co]' ` @@ -133,3 +128,10 @@ clean: rm -rf build rm -rf perf.data* rm -rf python/pydantic_core/*.so + +.PHONY: help ## Display this message +help: + @grep -E \ + '^.PHONY: .*?## .*$$' $(MAKEFILE_LIST) | \ + sort | \ + awk 'BEGIN {FS = ".PHONY: |## "}; {printf "\033[36m%-19s\033[0m %s\n", $$2, $$3}' diff --git a/README.md b/README.md index bac3a4758..6260d7b58 100644 --- a/README.md +++ b/README.md @@ -69,35 +69,52 @@ except ValidationError as e: ## Getting Started -You'll need rust stable [installed](https://rustup.rs/), or rust nightly if you want to generate accurate coverage. +### Prerequisites -With rust and python 3.9+ installed, compiling pydantic-core should be possible with roughly the following: +You'll need: +1. **[Rust](https://rustup.rs/)** - Rust stable (or nightly for coverage) +2. **[uv](https://docs.astral.sh/uv/getting-started/installation/)** - Fast Python package manager (will install Python 3.9+ automatically) +3. **[git](https://git-scm.com/)** - For version control +4. **[make](https://www.gnu.org/software/make/)** - For running development commands (or use `nmake` on Windows) + +### Quick Start ```bash -# clone this repo or your fork +# Clone the repository (or from your fork) git clone git@github.com:pydantic/pydantic-core.git cd pydantic-core -# create a new virtual env -python3 -m venv env -source env/bin/activate -# install dependencies and install pydantic-core + +# Install all dependencies using uv, setup pre-commit hooks, and build the development version make install ``` -That should be it, the example shown above should now run. +Verify your installation by running: + +```bash +make +``` + +This runs a full development cycle: formatting, building, linting, and testing + +### Development Commands + +Run `make help` to see all available commands, or use these common ones: + +```bash +make build-dev # to build the package during development +make build-prod # to perform an optimised build for benchmarking +make test # to run the tests +make testcov # to run the tests and generate a coverage report +make lint # to run the linter +make format # to format python and rust code +make all # to run to run build-dev + format + lint + test +``` -You might find it useful to look at [`python/pydantic_core/_pydantic_core.pyi`](./python/pydantic_core/_pydantic_core.pyi) and -[`python/pydantic_core/core_schema.py`](./python/pydantic_core/core_schema.py) for more information on the python API, -beyond that, [`tests/`](./tests) provide a large number of examples of usage. +### Useful Resources -If you want to contribute to pydantic-core, you'll want to use some other make commands: -* `make build-dev` to build the package during development -* `make build-prod` to perform an optimised build for benchmarking -* `make test` to run the tests -* `make testcov` to run the tests and generate a coverage report -* `make lint` to run the linter -* `make format` to format python and rust code -* `make` to run `format build-dev lint test` +* [`python/pydantic_core/_pydantic_core.pyi`](./python/pydantic_core/_pydantic_core.pyi) - Python API types +* [`python/pydantic_core/core_schema.py`](./python/pydantic_core/core_schema.py) - Core schema definitions +* [`tests/`](./tests) - Comprehensive usage examples ## Profiling diff --git a/pyproject.toml b/pyproject.toml index 8cab50714..d8051a47c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ authors = [ { name = 'David Montague', email = 'david@pydantic.dev' }, { name = 'David Hewitt', email = 'mail@davidhewitt.dev' }, { name = 'Sydney Runkle', email = 'sydneymarierunkle@gmail.com' }, - { name = 'Victorien Plot', email='contact@vctrn.dev' }, + { name = 'Victorien Plot', email = 'contact@vctrn.dev' }, ] classifiers = [ 'Development Status :: 3 - Alpha', @@ -39,9 +39,7 @@ classifiers = [ 'Operating System :: MacOS', 'Typing :: Typed', ] -dependencies = [ - 'typing-extensions>=4.14.1', -] +dependencies = ['typing-extensions>=4.14.1'] dynamic = ['readme', 'version'] [project.urls] @@ -55,26 +53,33 @@ testing = [ { include-group = "dev" }, 'coverage', 'dirty-equals', - 'inline-snapshot', + 'exceptiongroup; python_version < "3.11"', 'hypothesis', + 'inline-snapshot', + # numpy doesn't offer prebuilt wheels for all versions and platforms we test in CI e.g. aarch64 musllinux + 'numpy; python_version < "3.13" and implementation_name == "cpython" and platform_machine == "x86_64"', # pandas doesn't offer prebuilt wheels for all versions and platforms we test in CI e.g. aarch64 musllinux 'pandas; python_version < "3.13" and implementation_name == "cpython" and platform_machine == "x86_64"', 'pytest', # pytest-examples currently depends on aiohttp via black; we don't want to build it on platforms like aarch64 musllinux in CI 'pytest-examples; implementation_name == "cpython" and platform_machine == "x86_64"', - 'pytest-speed', 'pytest-mock', 'pytest-pretty', 'pytest-run-parallel', + 'pytest-speed', 'pytest-timeout', 'python-dateutil', - # numpy doesn't offer prebuilt wheels for all versions and platforms we test in CI e.g. aarch64 musllinux - 'numpy; python_version < "3.13" and implementation_name == "cpython" and platform_machine == "x86_64"', - 'exceptiongroup; python_version < "3.11"', - 'tzdata', 'typing-inspection>=0.4.1', + 'tzdata', +] +linting = [ + { include-group = "dev" }, + 'griffe', + 'mypy', + 'pre-commit', + 'pyright', + 'ruff', ] -linting = [{ include-group = "dev" }, 'griffe', 'pyright', 'ruff', 'mypy'] wasm = [{ include-group = "dev" }, 'ruff'] codspeed = [ # codspeed is only run on CI, with latest version of CPython diff --git a/python/pydantic_core/_pydantic_core.pyi b/python/pydantic_core/_pydantic_core.pyi index 875c99da8..74fde428f 100644 --- a/python/pydantic_core/_pydantic_core.pyi +++ b/python/pydantic_core/_pydantic_core.pyi @@ -304,6 +304,7 @@ class SchemaSerializer: exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal['none', 'warn', 'error'] = True, fallback: Callable[[Any], Any] | None = None, @@ -324,6 +325,7 @@ class SchemaSerializer: e.g. are not included in `__pydantic_fields_set__`. exclude_defaults: Whether to exclude fields that are equal to their default value. exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. round_trip: Whether to enable serialization and validation round-trip support. warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors, "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. @@ -351,6 +353,7 @@ class SchemaSerializer: exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal['none', 'warn', 'error'] = True, fallback: Callable[[Any], Any] | None = None, @@ -372,6 +375,7 @@ class SchemaSerializer: e.g. are not included in `__pydantic_fields_set__`. exclude_defaults: Whether to exclude fields that are equal to their default value. exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. round_trip: Whether to enable serialization and validation round-trip support. warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors, "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. diff --git a/python/pydantic_core/core_schema.py b/python/pydantic_core/core_schema.py index d03dded87..dc8f55b7c 100644 --- a/python/pydantic_core/core_schema.py +++ b/python/pydantic_core/core_schema.py @@ -169,6 +169,11 @@ def exclude_none(self) -> bool: """The `exclude_none` argument set during serialization.""" ... + @property + def exclude_computed_fields(self) -> bool: + """The `exclude_computed_fields` argument set during serialization.""" + ... + @property def serialize_as_any(self) -> bool: """The `serialize_as_any` argument set during serialization.""" diff --git a/src/serializers/computed_fields.rs b/src/serializers/computed_fields.rs index 9870a6682..ff83800bc 100644 --- a/src/serializers/computed_fields.rs +++ b/src/serializers/computed_fields.rs @@ -127,8 +127,8 @@ impl ComputedFields { convert_error: impl FnOnce(PyErr) -> E, mut serialize: impl FnMut(ComputedFieldToSerialize<'a, 'py>) -> Result<(), E>, ) -> Result<(), E> { - if extra.round_trip { - // Do not serialize computed fields + // In round trip mode, exclude computed fields: + if extra.round_trip || extra.exclude_computed_fields { return Ok(()); } diff --git a/src/serializers/extra.rs b/src/serializers/extra.rs index 0b1036bd5..f940e4dea 100644 --- a/src/serializers/extra.rs +++ b/src/serializers/extra.rs @@ -60,6 +60,7 @@ impl SerializationState { false, false, exclude_none, + false, round_trip, &self.config, &self.rec_guard, @@ -86,6 +87,7 @@ pub(crate) struct Extra<'a> { pub exclude_unset: bool, pub exclude_defaults: bool, pub exclude_none: bool, + pub exclude_computed_fields: bool, pub round_trip: bool, pub config: &'a SerializationConfig, pub rec_guard: &'a SerRecursionState, @@ -112,6 +114,7 @@ impl<'a> Extra<'a> { exclude_unset: bool, exclude_defaults: bool, exclude_none: bool, + exclude_computed_fields: bool, round_trip: bool, config: &'a SerializationConfig, rec_guard: &'a SerRecursionState, @@ -128,6 +131,7 @@ impl<'a> Extra<'a> { exclude_unset, exclude_defaults, exclude_none, + exclude_computed_fields, round_trip, config, rec_guard, @@ -196,6 +200,7 @@ pub(crate) struct ExtraOwned { exclude_unset: bool, exclude_defaults: bool, exclude_none: bool, + exclude_computed_fields: bool, round_trip: bool, config: SerializationConfig, rec_guard: SerRecursionState, @@ -217,6 +222,7 @@ impl ExtraOwned { exclude_unset: extra.exclude_unset, exclude_defaults: extra.exclude_defaults, exclude_none: extra.exclude_none, + exclude_computed_fields: extra.exclude_computed_fields, round_trip: extra.round_trip, config: extra.config.clone(), rec_guard: extra.rec_guard.clone(), @@ -239,6 +245,7 @@ impl ExtraOwned { exclude_unset: self.exclude_unset, exclude_defaults: self.exclude_defaults, exclude_none: self.exclude_none, + exclude_computed_fields: self.exclude_computed_fields, round_trip: self.round_trip, config: &self.config, rec_guard: &self.rec_guard, diff --git a/src/serializers/fields.rs b/src/serializers/fields.rs index 907078e00..f6c4ffc1a 100644 --- a/src/serializers/fields.rs +++ b/src/serializers/fields.rs @@ -226,7 +226,7 @@ impl GeneralFieldsSerializer { if extra.check.enabled() // If any of these are true we can't count fields - && !(extra.exclude_defaults || extra.exclude_unset || extra.exclude_none || exclude.is_some()) + && !(extra.exclude_defaults || extra.exclude_unset || extra.exclude_none || extra.exclude_computed_fields || exclude.is_some()) // Check for missing fields, we can't have extra fields here && self.required_fields > used_req_fields { diff --git a/src/serializers/infer.rs b/src/serializers/infer.rs index 285c2f99a..acd94c3d1 100644 --- a/src/serializers/infer.rs +++ b/src/serializers/infer.rs @@ -102,6 +102,7 @@ pub(crate) fn infer_to_python_known( extra.exclude_unset, extra.exclude_defaults, extra.exclude_none, + extra.exclude_computed_fields, extra.round_trip, extra.rec_guard, extra.serialize_unknown, @@ -496,6 +497,7 @@ pub(crate) fn infer_serialize_known( extra.exclude_unset, extra.exclude_defaults, extra.exclude_none, + extra.exclude_computed_fields, extra.round_trip, extra.rec_guard, extra.serialize_unknown, diff --git a/src/serializers/mod.rs b/src/serializers/mod.rs index acecaf749..3056f8fba 100644 --- a/src/serializers/mod.rs +++ b/src/serializers/mod.rs @@ -60,6 +60,7 @@ impl SchemaSerializer { exclude_unset: bool, exclude_defaults: bool, exclude_none: bool, + exclude_computed_fields: bool, round_trip: bool, rec_guard: &'a SerRecursionState, serialize_unknown: bool, @@ -75,6 +76,7 @@ impl SchemaSerializer { exclude_unset, exclude_defaults, exclude_none, + exclude_computed_fields, round_trip, &self.config, rec_guard, @@ -108,8 +110,8 @@ impl SchemaSerializer { #[allow(clippy::too_many_arguments)] #[pyo3(signature = (value, *, mode = None, include = None, exclude = None, by_alias = None, - exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = WarningsArg::Bool(true), - fallback = None, serialize_as_any = false, context = None))] + exclude_unset = false, exclude_defaults = false, exclude_none = false, exclude_computed_fields = false, + round_trip = false, warnings = WarningsArg::Bool(true), fallback = None, serialize_as_any = false, context = None))] pub fn to_python( &self, py: Python, @@ -121,6 +123,7 @@ impl SchemaSerializer { exclude_unset: bool, exclude_defaults: bool, exclude_none: bool, + exclude_computed_fields: bool, round_trip: bool, warnings: WarningsArg, fallback: Option<&Bound<'_, PyAny>>, @@ -142,6 +145,7 @@ impl SchemaSerializer { exclude_unset, exclude_defaults, exclude_none, + exclude_computed_fields, round_trip, &rec_guard, false, @@ -156,8 +160,8 @@ impl SchemaSerializer { #[allow(clippy::too_many_arguments)] #[pyo3(signature = (value, *, indent = None, ensure_ascii = false, include = None, exclude = None, by_alias = None, - exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = WarningsArg::Bool(true), - fallback = None, serialize_as_any = false, context = None))] + exclude_unset = false, exclude_defaults = false, exclude_none = false, exclude_computed_fields = false, + round_trip = false, warnings = WarningsArg::Bool(true), fallback = None, serialize_as_any = false, context = None))] pub fn to_json( &self, py: Python, @@ -170,6 +174,7 @@ impl SchemaSerializer { exclude_unset: bool, exclude_defaults: bool, exclude_none: bool, + exclude_computed_fields: bool, round_trip: bool, warnings: WarningsArg, fallback: Option<&Bound<'_, PyAny>>, @@ -190,6 +195,7 @@ impl SchemaSerializer { exclude_unset, exclude_defaults, exclude_none, + exclude_computed_fields, round_trip, &rec_guard, false, diff --git a/src/serializers/type_serializers/function.rs b/src/serializers/type_serializers/function.rs index 9a351694c..6504aee77 100644 --- a/src/serializers/type_serializers/function.rs +++ b/src/serializers/type_serializers/function.rs @@ -559,6 +559,8 @@ struct SerializationInfo { #[pyo3(get)] exclude_none: bool, #[pyo3(get)] + exclude_computed_fields: bool, + #[pyo3(get)] round_trip: bool, field_name: Option, #[pyo3(get)] @@ -583,6 +585,7 @@ impl SerializationInfo { exclude_unset: extra.exclude_unset, exclude_defaults: extra.exclude_defaults, exclude_none: extra.exclude_none, + exclude_computed_fields: extra.exclude_none, round_trip: extra.round_trip, field_name: Some(field_name.to_string()), serialize_as_any: extra.serialize_as_any, @@ -601,6 +604,7 @@ impl SerializationInfo { exclude_unset: extra.exclude_unset, exclude_defaults: extra.exclude_defaults, exclude_none: extra.exclude_none, + exclude_computed_fields: extra.exclude_computed_fields, round_trip: extra.round_trip, field_name: None, serialize_as_any: extra.serialize_as_any, @@ -651,6 +655,7 @@ impl SerializationInfo { d.set_item("exclude_unset", self.exclude_unset)?; d.set_item("exclude_defaults", self.exclude_defaults)?; d.set_item("exclude_none", self.exclude_none)?; + d.set_item("exclude_computed_fields", self.exclude_computed_fields)?; d.set_item("round_trip", self.round_trip)?; d.set_item("serialize_as_any", self.serialize_as_any)?; Ok(d) @@ -658,7 +663,7 @@ impl SerializationInfo { fn __repr__(&self, py: Python) -> PyResult { Ok(format!( - "SerializationInfo(include={}, exclude={}, context={}, mode='{}', by_alias={}, exclude_unset={}, exclude_defaults={}, exclude_none={}, round_trip={}, serialize_as_any={})", + "SerializationInfo(include={}, exclude={}, context={}, mode='{}', by_alias={}, exclude_unset={}, exclude_defaults={}, exclude_none={}, exclude_computed_fields={}, round_trip={}, serialize_as_any={})", match self.include { Some(ref include) => include.bind(py).repr()?.to_str()?.to_owned(), None => "None".to_owned(), @@ -676,6 +681,7 @@ impl SerializationInfo { py_bool(self.exclude_unset), py_bool(self.exclude_defaults), py_bool(self.exclude_none), + py_bool(self.exclude_computed_fields), py_bool(self.round_trip), py_bool(self.serialize_as_any), )) diff --git a/tests/serializers/test_functions.py b/tests/serializers/test_functions.py index ec1ced855..c33cae7b9 100644 --- a/tests/serializers/test_functions.py +++ b/tests/serializers/test_functions.py @@ -72,6 +72,7 @@ def double(value, info): 'exclude_unset': False, 'exclude_defaults': False, 'exclude_none': False, + 'exclude_computed_fields': False, 'round_trip': False, 'serialize_as_any': False, } @@ -85,6 +86,7 @@ def double(value, info): 'exclude_unset': False, 'exclude_defaults': False, 'exclude_none': False, + 'exclude_computed_fields': False, 'round_trip': False, 'serialize_as_any': False, } @@ -97,6 +99,7 @@ def double(value, info): 'exclude_unset': False, 'exclude_defaults': False, 'exclude_none': False, + 'exclude_computed_fields': False, 'round_trip': False, 'serialize_as_any': False, } @@ -109,6 +112,7 @@ def double(value, info): 'exclude_unset': True, 'exclude_defaults': False, 'exclude_none': False, + 'exclude_computed_fields': False, 'round_trip': False, 'serialize_as_any': False, } @@ -123,6 +127,7 @@ def double(value, info): 'exclude_unset': False, 'exclude_defaults': False, 'exclude_none': False, + 'exclude_computed_fields': False, 'round_trip': False, 'serialize_as_any': False, } @@ -136,6 +141,7 @@ def double(value, info): 'exclude_unset': False, 'exclude_defaults': False, 'exclude_none': False, + 'exclude_computed_fields': False, 'round_trip': False, 'serialize_as_any': False, } @@ -231,27 +237,27 @@ def append_args(value, info): ) assert s.to_python(123) == ( "123 info=SerializationInfo(include=None, exclude=None, context=None, mode='python', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) assert s.to_python(123, mode='other') == ( "123 info=SerializationInfo(include=None, exclude=None, context=None, mode='other', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) assert s.to_python(123, include={'x'}) == ( "123 info=SerializationInfo(include={'x'}, exclude=None, context=None, mode='python', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) assert s.to_python(123, context='context') == ( "123 info=SerializationInfo(include=None, exclude=None, context='context', mode='python', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) assert s.to_python(123, mode='json', exclude={1: {2}}) == ( "123 info=SerializationInfo(include=None, exclude={1: {2}}, context=None, mode='json', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) assert s.to_json(123) == ( b"\"123 info=SerializationInfo(include=None, exclude=None, context=None, mode='json', by_alias=False, exclude_unset=False, " - b'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)"' + b'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)"' ) diff --git a/tests/serializers/test_model.py b/tests/serializers/test_model.py index 74712685f..963584c63 100644 --- a/tests/serializers/test_model.py +++ b/tests/serializers/test_model.py @@ -2,14 +2,10 @@ import json import platform import warnings +from functools import cached_property from random import randint from typing import Any, ClassVar -try: - from functools import cached_property -except ImportError: - cached_property = None - import pytest from dirty_equals import IsJson @@ -504,7 +500,6 @@ def ser_x(self, v: Any, _) -> str: assert s.to_python(Model(x=1000)) == {'x': '1_000'} -@pytest.mark.skipif(cached_property is None, reason='cached_property is not available') def test_field_serializer_cached_property(): @dataclasses.dataclass class Model: @@ -709,6 +704,9 @@ def area(self) -> bytes: assert s.to_python(Model(width=3, height=4), round_trip=True) == {'width': 3, 'height': 4} assert s.to_json(Model(width=3, height=4), round_trip=True) == b'{"width":3,"height":4}' + assert s.to_python(Model(width=3, height=4), exclude_computed_fields=True) == {'width': 3, 'height': 4} + assert s.to_json(Model(width=3, height=4), exclude_computed_fields=True) == b'{"width":3,"height":4}' + def test_property_alias(): @dataclasses.dataclass @@ -881,7 +879,6 @@ def area(self) -> int: assert s.to_json(Model(3, 4), exclude_none=True, by_alias=True) == b'{"width":3,"height":4,"Area":12}' -@pytest.mark.skipif(cached_property is None, reason='cached_property is not available') def test_cached_property_alias(): @dataclasses.dataclass class Model: @@ -996,7 +993,6 @@ def b(self): assert s.to_json(Model(1), exclude={'b': [0]}) == b'{"a":1,"b":[2,"3"]}' -@pytest.mark.skipif(cached_property is None, reason='cached_property is not available') def test_property_setter(): class Square: side: float diff --git a/tests/test.rs b/tests/test.rs index 90f8062d3..88042d337 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -151,6 +151,7 @@ dump_json_input_2 = {'a': 'something'} false, false, false, + false, WarningsArg::Bool(false), None, false, @@ -173,6 +174,7 @@ dump_json_input_2 = {'a': 'something'} false, false, false, + false, WarningsArg::Bool(false), None, false, diff --git a/tests/test_typing.py b/tests/test_typing.py index 20a50a368..593b35fd8 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -233,7 +233,7 @@ def f(input: Any, info: core_schema.SerializationInfo, /) -> str: ) assert s.to_python(123) == ( "SerializationInfo(include=None, exclude=None, context=None, mode='python', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) @@ -254,7 +254,7 @@ def f( assert s.to_python(123, mode='json') == ( 'SerializationCallable(serializer=str) ' "SerializationInfo(include=None, exclude=None, context=None, mode='json', by_alias=False, exclude_unset=False, " - 'exclude_defaults=False, exclude_none=False, round_trip=False, serialize_as_any=False)' + 'exclude_defaults=False, exclude_none=False, exclude_computed_fields=False, round_trip=False, serialize_as_any=False)' ) diff --git a/tests/validators/test_arguments.py b/tests/validators/test_arguments.py index 5a147cc40..d5671c262 100644 --- a/tests/validators/test_arguments.py +++ b/tests/validators/test_arguments.py @@ -47,7 +47,7 @@ def test_args_kwargs(): [ ArgsKwargs((1, 'a', True), {'x': 1}), Err( - '', + '1 validation error for arguments', [ { 'type': 'unexpected_keyword_argument', @@ -61,7 +61,7 @@ def test_args_kwargs(): [ [1], Err( - '', + '2 validation errors for arguments', [ { 'type': 'missing_positional_only_argument', @@ -81,7 +81,7 @@ def test_args_kwargs(): [ [1, 'a', True, 4], Err( - '', + '1 validation error for arguments', [ { 'type': 'unexpected_positional_argument', @@ -95,7 +95,7 @@ def test_args_kwargs(): [ [1, 'a', True, 4, 5], Err( - '', + '2 validation errors for arguments', [ { 'type': 'unexpected_positional_argument', @@ -115,7 +115,7 @@ def test_args_kwargs(): [ ('x', 'a', 'wrong'), Err( - '', + '2 validation errors for arguments', [ { 'type': 'int_parsing', @@ -221,7 +221,7 @@ def test_positional_args(py_and_json: PyAndJson, input_value, expected): [ ArgsKwargs((), {'a': 'x', 'b': 'a', 'c': 'wrong'}), Err( - '', + '2 validation errors for arguments', [ { 'type': 'int_parsing', @@ -241,7 +241,7 @@ def test_positional_args(py_and_json: PyAndJson, input_value, expected): [ ArgsKwargs(()), Err( - '', + '3 validation errors for arguments', [ { 'type': 'missing_keyword_only_argument', diff --git a/uv.lock b/uv.lock index 962ea6e1f..5f665ed23 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version == '3.13.*' and implementation_name == 'cpython'", @@ -130,6 +130,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290, upload-time = "2024-09-04T20:45:20.226Z" }, ] +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, +] + [[package]] name = "click" version = "8.1.8" @@ -229,6 +238,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/0c/03cc99bf3b6328604b10829de3460f2b2ad3373200c45665c38508e550c6/dirty_equals-0.9.0-py3-none-any.whl", hash = "sha256:ff4d027f5cfa1b69573af00f7ba9043ea652dbdce3fe5cbe828e478c7346db9c", size = 28226, upload-time = "2025-01-11T23:23:37.489Z" }, ] +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -247,6 +265,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, ] +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, +] + [[package]] name = "griffe" version = "1.5.5" @@ -273,6 +300,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a3/e1/80f1819e65a9a0b1b744b453cd26803764fc192cff8eaea202e76f6f5a65/hypothesis-6.124.2-py3-none-any.whl", hash = "sha256:fef7709a404929a9cd3e785f60a6e026089aab986e288b1fdced13091574d474", size = 482180, upload-time = "2025-01-21T18:44:55.288Z" }, ] +[[package]] +name = "identify" +version = "2.6.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/88/d193a27416618628a5eea64e3223acd800b40749a96ffb322a9b55a49ed1/identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6", size = 99254, upload-time = "2025-05-23T20:37:53.3Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -554,6 +590,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] +[[package]] +name = "pre-commit" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, +] + [[package]] name = "pycparser" version = "2.22" @@ -583,6 +635,7 @@ all = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and implementation_name == 'cpython' and platform_machine == 'x86_64'" }, { name = "numpy", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and python_full_version < '3.13' and implementation_name == 'cpython' and platform_machine == 'x86_64'" }, { name = "pandas", marker = "python_full_version < '3.13' and implementation_name == 'cpython' and platform_machine == 'x86_64'" }, + { name = "pre-commit" }, { name = "pyright" }, { name = "pytest" }, { name = "pytest-examples", marker = "implementation_name == 'cpython' and platform_machine == 'x86_64'" }, @@ -606,6 +659,7 @@ linting = [ { name = "griffe" }, { name = "maturin" }, { name = "mypy" }, + { name = "pre-commit" }, { name = "pyright" }, { name = "ruff" }, ] @@ -650,6 +704,7 @@ all = [ { name = "mypy" }, { name = "numpy", marker = "python_full_version < '3.13' and implementation_name == 'cpython' and platform_machine == 'x86_64'" }, { name = "pandas", marker = "python_full_version < '3.13' and implementation_name == 'cpython' and platform_machine == 'x86_64'" }, + { name = "pre-commit" }, { name = "pyright" }, { name = "pytest" }, { name = "pytest-examples", marker = "implementation_name == 'cpython' and platform_machine == 'x86_64'" }, @@ -669,6 +724,7 @@ linting = [ { name = "griffe" }, { name = "maturin" }, { name = "mypy" }, + { name = "pre-commit" }, { name = "pyright" }, { name = "ruff" }, ] @@ -721,7 +777,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.3.4" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -729,11 +785,12 @@ dependencies = [ { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, + { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919, upload-time = "2024-12-01T12:54:25.98Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083, upload-time = "2024-12-01T12:54:19.735Z" }, + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] [[package]] @@ -858,6 +915,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002, upload-time = "2024-09-11T02:24:45.8Z" }, ] +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, +] + [[package]] name = "rich" version = "13.9.4" @@ -983,3 +1093,17 @@ sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be wheels = [ { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762, upload-time = "2025-01-21T19:49:37.187Z" }, ] + +[[package]] +name = "virtualenv" +version = "20.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/2e/8a70dcbe8bf15213a08f9b0325ede04faca5d362922ae0d62ef0fa4b069d/virtualenv-20.33.0.tar.gz", hash = "sha256:47e0c0d2ef1801fce721708ccdf2a28b9403fa2307c3268aebd03225976f61d2", size = 6082069, upload-time = "2025-08-03T08:09:19.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/87/b22cf40cdf7e2b2bf83f38a94d2c90c5ad6c304896e5a12d0c08a602eb59/virtualenv-20.33.0-py3-none-any.whl", hash = "sha256:106b6baa8ab1b526d5a9b71165c85c456fbd49b16976c88e2bc9352ee3bc5d3f", size = 6060205, upload-time = "2025-08-03T08:09:16.674Z" }, +]