Package maintenance

pydantic-typer

Pydantic Typer Typer extension to enable pydantic support [!WARNING] This package is still in early development and some things might not work as expected, or change between versions. Table of Contents Installation Usage License Installation console pip install pydantic-typer [!NOTE] pydantic-typer comes with pydantic and typer as dependencies, so you don't need to install anything else. Usage For general typer usage, please refer to the typer documentation. All the code blocks below can be copied and used directly (they are tested Python files). To run any of the examples, copy the code to a file main.py, and run it: console python main.py Basic Usage :technologist: Simply use pydantic_typer.run instead of typer.run to enable pydantic support ```python from typing import Annotated import pydantic import typer import pydantic_typer class User(pydantic.BaseModel): id: Annotated[int, pydantic.Field(description="The id of the user.")] name: Annotated[str, pydantic.Field(description="The name of the user.")] = "Jane Doe" def main(num: int, user: User): typer.echo(f"{num} {type(num)}") typer.echo(f"{user} {type(user)}") if name == "main": pydantic_typer.run(main) ``` :t-rex: Non-Annotated Version ```python import pydantic import typer import pydantic_typer class User(pydantic.BaseModel): id: int = pydantic.Field(description="The id of the user.") name: str = pydantic.Field("Jane Doe", description="The name of the user.") def main(num: int, user: User): typer.echo(f"{num} {type(num)}") typer.echo(f"{user} {type(user)}") if name == "main": pydantic_typer.run(main) ``` :computer: Usage ```console $ # Run the basic example: $ python main.py Usage: main.py [OPTIONS] NUM Try 'main.py --help' for help. ╭─ Error ────────────────────────────────────────────────────────────╮ │ Missing argument 'NUM'. │ ╰────────────────────────────────────────────────────────────────────╯ $ # We're missing a required argument, try using --help as suggested: $ python main.py --help Usage: main.py [OPTIONS] NUM ╭─ Arguments ────────────────────────────────────────────────────────╮ │ * num INTEGER [default: None] [required] │ ╰────────────────────────────────────────────────────────────────────╯ ╭─ Options ──────────────────────────────────────────────────────────╮ │ * --user.id INTEGER The id of the user. [default: None] │ │ [required] │ │ --user.name TEXT The name of the user. │ │ [default: Jane Doe] │ │ --help Show this message and exit. │ ╰──────────────────────────────────────────────────────────────────── $ # Notice the help text for user.id and user.name are inferred from the pydantic.Field. $ # user.id is reqired, because we don't provide a default value for the field. $ # Now run the example with the required arguments: $ python main.py 1 --user.id 1 1 id=1 name='Jane Doe' $ # It worked! You can also experiment with an invalid user.id: $ python main.py 1 --user.id some-string Usage: example_001_basic.py [OPTIONS] NUM Try 'example_001_basic.py --help' for help. ╭─ Error ─────────────────────────────────────────────────────────────╮ │ Invalid value for '--user.id': 'some-string' is not a valid integer.│ ╰─────────────────────────────────────────────────────────────────────╯ ``` Usage with nested models :technologist: pydantic_typer.run also works with nested pydantic models ```python from future import annotations from typing import Optional import pydantic import typer import pydantic_typer class Pet(pydantic.BaseModel): name: str species: str class Person(pydantic.BaseModel): name: str age: Optional[float] = None # noqa: UP007 For Python versions >=3.10, prefer float | None pet: Pet def main(person: Person): typer.echo(f"{person} {type(person)}") if name == "main": pydantic_typer.run(main) ``` :computer: Usage console $ # Run the nested models example with the required options: $ python main.py --person.name "Patrick" --person.pet.name "Snoopy" --person.pet.species "Dog" name='Patrick' age=None pet=Pet(name='Snoopy', species='Dog') Use pydantic models with typer.Argument :technologist: You can annotate the parameters with typer.Argument to make all model fields CLI arguments ```python from future import annotations import pydantic import typer from typing_extensions import Annotated import pydantic_typer class User(pydantic.BaseModel): id: int name: str def main(num: Annotated[int, typer.Option()], user: Annotated[User, typer.Argument()]): typer.echo(f"{num} {type(num)}") typer.echo(f"{user} {type(user)}") if name == "main": pydantic_typer.run(main) ``` :computer: Usage ```console $ # Run the example $ python main.py Usage: main.py [OPTIONS] _PYDANTIC_USER_ID _PYDANTIC_USER_NAME Try 'main.py --help' for help. ╭─ Error ─────────────────────────────────────────────────────────────╮ │ Missing argument '_PYDANTIC_USER_ID'. │ ╰─────────────────────────────────────────────────────────────────────╯ $ #Notice how _PYDANTIC_USER_ID and _PYDANTIC_USER_NAME are now cli arguments instead of options. $ # Supply the arguments in the right order: python main.py 1 Patrick --num 1 1 id=1 name='Patrick' ``` :bulb: You can also override annotations directly on the pydantic model fields: ```python from future import annotations import pydantic import typer from typing_extensions import Annotated import pydantic_typer class User(pydantic.BaseModel): id: Annotated[int, typer.Argument(metavar="THE_ID")] name: Annotated[str, typer.Option()] def main(num: Annotated[int, typer.Option()], user: Annotated[User, typer.Argument()]): typer.echo(f"{num} {type(num)}") typer.echo(f"{user} {type(user)}") if name == "main": pydantic_typer.run(main) ``` Here, User is a typer.Argument, but we manually override the fields again: We override the metavar of to User.id be THE_ID And User.name to be a typer.Option Use pydantic models in multiple commands :technologist: For larger typer apps, you can use pydantic_typer.Typer instead of annotating each command function individually to enable pydantic models on all commands ```python from future import annotations import pydantic import typer from typing_extensions import Annotated from pydantic_typer import Typer app = Typer() class User(pydantic.BaseModel): id: int name: Annotated[str, typer.Option()] = "John" @app.command() def hi(user: User): typer.echo(f"Hi {user}") @app.command() def bye(user: User): typer.echo(f"Bye {user}") if name == "main": app() ``` Use pydantic types :technologist: You can also annotate arguments with pydantic types and they will be validated ```python import click import typer from pydantic import HttpUrl, conint import pydantic_typer EvenInt = conint(multiple_of=2) def main(num: EvenInt, url: HttpUrl, ctx: click.Context): # type: ignore typer.echo(f"{num} {type(num)}") typer.echo(f"{url} {type(url)}") if name == "main": pydantic_typer.run(main) ``` :technologist: Pydantic types also work in lists and tuples ```python from typing import List import typer from pydantic import AnyHttpUrl import pydantic_typer def main(urls: List[AnyHttpUrl] = typer.Option([], "--url")): typer.echo(f"urls: {urls}") if name == "main": pydantic_typer.run(main) ``` Use Union types :technologist: Thanks to pydantic.TypeAdapter, which we use internally, we also support Union types ```python import typer import pydantic_typer def main(value: bool | int | float | str = 1): typer.echo(f"{value} {type(value)}") if name == "main": pydantic_typer.run(main) ``` :computer: Usage ```console $ # Run the example using a boolean $ python main.py --value True True $ # Run the example using an integer $ python main.py --value 2 2 $ # Run the example using a float $ python main.py --value 2.1 2.1 $ # Run the example using a string $ python main.py --value "Hello World" Hello World $ # Before, we intentionally used 2, when testing the integer $ # Check what happens if you pass 1 $ python main.py --value 1 True $ # We get back a boolean! $ # This is because Unions are generally evaluated left to right. $ # So in this case bool > int > float > str, if parsing is successful. $ # There are some exceptions, where pydantic tries to be smart, see here for details: $ # https://docs.pydantic.dev/latest/concepts/unions/#smart-mode ``` Limitations [!WARNING] pydantic-typer does not yet support sequences of pydantic models: See this issue for details [!WARNING] pydantic-typer does not yet support self-referential pydantic models. [!WARNING] pydantic-typer does not yet support lists with complex sub-types, in particular unions such as list[str|int]. License pydantic-typer is distributed under the terms of the MIT license.

pypi package. Binary | Source

Latest version: 0.0.13 Released: 2024-11-14

pydantic-graph

Pydantic Graph Graph and finite state machine library. This library is developed as part of PydanticAI, however it has no dependency on pydantic-ai or related packages and can be considered as a pure graph-based state machine library. You may find it useful whether or not you're using PydanticAI or even building with GenAI. As with PydanticAI, this library prioritizes type safety and use of common Python syntax over esoteric, domain-specific use of Python syntax. pydantic-graph allows you to define graphs using standard Python syntax. In particular, edges are defined using the return type hint of nodes. Full documentation is available at ai.pydantic.dev/graph. Here's a basic example: ```python {noqa="I001" py="3.10"} from future import annotations from dataclasses import dataclass from pydantic_graph import BaseNode, End, Graph, GraphRunContext @dataclass class DivisibleBy5(BaseNode[None, None, int]): foo: int async def run( self, ctx: GraphRunContext, ) -> Increment | End[int]: if self.foo % 5 == 0: return End(self.foo) else: return Increment(self.foo) @dataclass class Increment(BaseNode): foo: int async def run(self, ctx: GraphRunContext) -> DivisibleBy5: return DivisibleBy5(self.foo + 1) fives_graph = Graph(nodes=[DivisibleBy5, Increment]) result = fives_graph.run_sync(DivisibleBy5(4)) print(result.output) > 5 ```

pypi package. Binary | Source

Latest version: 0.2.1 Released: 2025-05-13

pydantic-views

.. |docs| image:: https://readthedocs.org/projects/pydantic-views/badge/?version=latest :alt: Documentation Status :target: https://pydantic-views.readthedocs.io/latest/?badge=latest .. |python-versions| image:: https://img.shields.io/pypi/pyversions/pydantic-views :alt: PyPI - Python Version .. |typed| image:: https://img.shields.io/pypi/types/pydantic-views :alt: PyPI - Types .. |license| image:: https://img.shields.io/pypi/l/pydantic-views :alt: PyPI - License .. |version| image:: https://img.shields.io/pypi/v/pydantic-views :alt: PyPI - Version |docs| |python-versions| |typed| |license| |version| .. start-doc ====================================== View for Pydantic models documentation ====================================== This package provides a simple way to create views from pydantic models. A view is another pydantic models with some of field of original model. So, for example, read only fields does not appears on Create or Update views. As rest service definition you could do: .. code-block:: python ExampleModelCreate = BuilderCreate().build_view(ExampleModel) ExampleModelCreateResult = BuilderCreateResult().build_view(ExampleModel) ExampleModelLoad = BuilderLoad().build_view(ExampleModel) ExampleModelUpdate = BuilderUpdate().build_view(ExampleModel) def create(input: ExampleModelCreate) -> ExampleModelCreateResult: ... def load(model_id: str) -> ExampleModelLoad: ... def update(model_id: str, input: ExampleModelUpdate) -> ExampleModelLoad: ... Features Unlimited views per model. Create view for referenced inner models. It is possible to set a view manually. Tested code. Full typed. Opensource. Installation Using pip: .. code-block:: bash pip install pydantic-views Using poetry_: .. code-block:: bash poetry add pydantic-views How to use When you define a pydantic model you must mark the access model for each field. It means you should use our annotations_ to define field typing. .. code-block:: python from typing import Annotated from pydantic import BaseModel, gt from pydantic_views import ReadOnly, ReadOnlyOnCreation, Hidden, AccessMode class ExampleModel(BaseModel): # No marked fields are treated like ReadAndWrite fields. field_str: str # Read only fields are removed on view for create and update views. field_read_only_str: ReadOnly[str] # Read only on creation fields are removed on view for create, update and load views. # But it is shown on create result view. field_api_secret: ReadOnlyOnCreation[str] # It is possible to set more than one access mode and to use annotation standard pattern. field_int: Annotated[int, AccessMode.READ_ONLY, AccessMode.WRITE_ONLY_ON_CREATION, gt(5)] # Hidden field do not appears in any view. field_hidden_int: Hidden[int] # Computed fields only appears on reading views. @computed_field def field_computed_field(self) -> int: return self.field_hidden_int * 5 So, in order to build a Load view it is so simple: .. code-block:: python from pydantic_views import BuilderLoad ExampleModelLoad = BuilderLoad().build_view(ExampleModel) It is equivalent to: .. code-block:: python from pydantic import gt from pydantic_views import View class ExampleModelLoad(View[ExampleModel]): field_str: str field_int: Annotated[int, gt(5)] field_computed_field: int In same way to build a Update view you must do: .. code-block:: python from pydantic_views import BuilderUpdate ExampleModelUpdate = BuilderUpdate().build_view(ExampleModel) It is equivalent to: .. code-block:: python from pydantic import PydanticUndefined from pydantic_views import View class ExampleModelUpdate(View[ExampleModel]): field_str: str = Field(default_factory=lambda: PydanticUndefined) As you can see, on Update view all fields has a default factory returning PydanticUndefined in order to make them optionals. And when an update view is applied to a given model, the fields that are not set (use default value) will not be applied to the model. .. code-block:: python original_model = ExampleModel( field_str="anything" field_read_only_str="anything" field_api_secret="anything" field_int=10 field_hidden_int=33 ) update = ExampleModelUpdate(field_str="new_data") updated_model = update.view_apply_to(original_model) assert isinstance(updated_model, ExampleModel) assert updated_model.field_str == "new_data" But if a field is not set on update view, the original value is kept. .. code-block:: python original_model = ExampleModel( field_str="anything" field_read_only_str="anything" field_api_secret="anything" field_int=10 field_hidden_int=33 ) update = ExampleModelUpdate() updated_model = update.view_apply_to(original_model) assert isinstance(updated_model, ExampleModel) assert updated_model.field_str == "anything"

pypi package. Binary | Source

Latest version: 0.1.0 Released: 2025-03-14

pydantic-sweep

pydantic-sweep pydantic_sweep is a library to programmatically, safely and flexibly define complex parameter sweeps over pydantic models in Python. Highlights: - Specify parameter sweeps in Python - Flexibility: specify complex parameter combinations by chaining simple functional operations - Safety checks for parameter combinations (get meaningful errors early) - pydantic field validation For example, the following code will instantiate models with (x=5, sub=Sub1(s=1) and (x=6, sub=Sub1(s=2) and try each of those with seed values of seed=43 and seed=44, leading to four different configurations: ```python import pydantic_sweep as ps class Sub1(ps.BaseModel): s: int = 5 class Sub2(ps.BaseModel): y: str = "hi" class Model(ps.BaseModel): seed: int = 42 x: int = 5 sub: Sub1 | Sub2 We want to test across two seeds configs = ps.config_product( ps.field("seed", [43, 44]), # And two specific Sub1 and x combinations ps.config_zip( ps.field("x", [ps.DefaultValue, 6]), ps.field("sub.s", [1, 2]), ) ) This includes safety checks that Sub1 / Sub2 are uniquely specified models = ps.initialize(Model, configs) The code above is equivalent to models_manual = [ Model(seed=43, sub=Sub1(s=1)), Model(seed=43, x=6, sub=Sub1(s=2)), Model(seed=44, sub=Sub1(s=1)), Model(seed=44, x=6, sub=Sub1(s=2)), ] assert models == models_manual We can also check that we didn't accidentally duplicate a setting ps.check_unique(models) ``` While in this toy example, manually specifying the combinations may still be viable, the library allows infinitely combining different configs and sub-models, making it a powerful tool for large-scale experiment definition. To learn mode about the capabilities of the library please visit the documentation page. Installation You can install the library by checking out the repo and running bash pip install 'pydantic-sweep' License The main code-base is licensed under MPL-2.0 a weak copy-left license that allows commercial use. See the license file for the exact clauses and this FAQ for a high-level description. An exception from this are the documentation in the docs and example folders as well as this README file, which are licensed under the 0BSD: a highly permissive license that does not require attribution. That way, you are free to copy & paste example code into your use-case. See here for a high-level description.

pypi package. Binary | Source

Latest version: 0.3.3 Released: 2025-02-23

pydantic-redis

pydantic-redis A simple declarative ORM for Redis Documentation: https://sopherapps.github.io/pydantic-redis Source Code: https://github.com/sopherapps/pydantic-redis Most Notable Features are: Define business domain objects as pydantic and automatically get ability to save them as is in redis with an intuitive API of insert, update, delete, select Maintain simple relationships between domain objects by simply nesting them either as single objects or lists, or tuples. Any direct or indirect update to a nested object will automatically reflect in all parent objects that have it nested in them when queried again from redis. Both synchronous and asynchronous APIs available. Benchmarks v0.5 (with pydantic v2) ``` ------------------------------------------------ benchmark: 22 tests ------------------------------------------------ Name (time in us) Mean Min Max benchmark_delete[redis_store-Wuthering Heights] 116.4282 (1.0) 103.9220 (1.0) 366.6500 (1.0) benchmark_bulk_delete[redis_store] 125.1484 (1.07) 110.2590 (1.06) 393.1860 (1.07) benchmark_select_columns_for_one_id[redis_store-book0] 176.7461 (1.52) 151.4150 (1.46) 484.4690 (1.32) benchmark_select_columns_for_one_id[redis_store-book3] 175.1838 (1.50) 152.3430 (1.47) 443.8120 (1.21) benchmark_select_columns_for_one_id[redis_store-book1] 176.9439 (1.52) 152.9350 (1.47) 464.4280 (1.27) benchmark_select_columns_for_one_id[redis_store-book2] 176.7885 (1.52) 153.0280 (1.47) 520.9390 (1.42) benchmark_select_all_for_one_id[redis_store-book0] 198.9879 (1.71) 173.8040 (1.67) 527.0550 (1.44) benchmark_select_all_for_one_id[redis_store-book1] 199.1717 (1.71) 175.8920 (1.69) 461.5000 (1.26) benchmark_select_all_for_one_id[redis_store-book2] 197.1996 (1.69) 177.9590 (1.71) 473.8830 (1.29) benchmark_select_all_for_one_id[redis_store-book3] 198.1436 (1.70) 178.1040 (1.71) 493.0560 (1.34) benchmark_select_columns_for_some_items[redis_store] 230.9837 (1.98) 209.6070 (2.02) 441.7680 (1.20) benchmark_select_columns_paginated[redis_store] 242.5208 (2.08) 212.4460 (2.04) 512.9250 (1.40) benchmark_update[redis_store-Wuthering Heights-data0] 253.0142 (2.17) 223.0690 (2.15) 548.3980 (1.50) benchmark_single_insert[redis_store-book2] 287.5952 (2.47) 246.2610 (2.37) 593.2120 (1.62) benchmark_select_some_items[redis_store] 274.5612 (2.36) 248.9740 (2.40) 539.6020 (1.47) benchmark_select_default_paginated[redis_store] 280.0070 (2.40) 254.9000 (2.45) 587.5320 (1.60) benchmark_single_insert[redis_store-book3] 293.2912 (2.52) 256.2000 (2.47) 523.5340 (1.43) benchmark_single_insert[redis_store-book1] 299.4127 (2.57) 258.5760 (2.49) 564.0550 (1.54) benchmark_single_insert[redis_store-book0] 293.0470 (2.52) 261.1910 (2.51) 590.2880 (1.61) benchmark_select_columns[redis_store] 347.7573 (2.99) 313.4880 (3.02) 624.8470 (1.70) benchmark_select_default[redis_store] 454.2192 (3.90) 398.2550 (3.83) 775.3050 (2.11) benchmark_bulk_insert[redis_store] 721.2247 (6.19) 673.9940 (6.49) 958.1200 (2.61) ``` >=v0.7 (with deeply nested models) ``` ------------------------------------------------- benchmark: 22 tests ------------------------------------------------- Name (time in us) Mean Min Max benchmark_delete[redis_store-Wuthering Heights] 123.2946 (1.02) 107.9690 (1.0) 502.6140 (1.33) benchmark_bulk_delete[redis_store] 120.5815 (1.0) 111.9320 (1.04) 378.8660 (1.0) benchmark_select_columns_for_one_id[redis_store-book2] 208.2612 (1.73) 180.4660 (1.67) 470.9860 (1.24) benchmark_select_columns_for_one_id[redis_store-book1] 207.9143 (1.72) 180.6440 (1.67) 489.6890 (1.29) benchmark_select_columns_for_one_id[redis_store-book0] 204.2471 (1.69) 183.4360 (1.70) 485.2500 (1.28) benchmark_select_columns_for_one_id[redis_store-book3] 209.5764 (1.74) 189.5780 (1.76) 462.5650 (1.22) benchmark_select_all_for_one_id[redis_store-book0] 226.4569 (1.88) 207.4920 (1.92) 499.9470 (1.32) benchmark_select_all_for_one_id[redis_store-book3] 241.5488 (2.00) 210.5230 (1.95) 504.5150 (1.33) benchmark_select_all_for_one_id[redis_store-book1] 234.4014 (1.94) 210.6420 (1.95) 501.2470 (1.32) benchmark_select_all_for_one_id[redis_store-book2] 228.9277 (1.90) 212.0090 (1.96) 509.5740 (1.34) benchmark_update[redis_store-Wuthering Heights-data0] 276.3908 (2.29) 238.3390 (2.21) 704.9450 (1.86) benchmark_single_insert[redis_store-book3] 311.0476 (2.58) 262.2940 (2.43) 589.3940 (1.56) benchmark_select_columns_for_some_items[redis_store] 291.2779 (2.42) 266.0960 (2.46) 564.3510 (1.49) benchmark_select_columns_paginated[redis_store] 300.4108 (2.49) 269.4740 (2.50) 552.8510 (1.46) benchmark_single_insert[redis_store-book1] 304.5771 (2.53) 274.1740 (2.54) 547.5210 (1.45) benchmark_single_insert[redis_store-book2] 317.2681 (2.63) 275.6170 (2.55) 641.5440 (1.69) benchmark_single_insert[redis_store-book0] 313.0004 (2.60) 277.3190 (2.57) 558.2160 (1.47) benchmark_select_some_items[redis_store] 343.2569 (2.85) 311.9140 (2.89) 624.6600 (1.65) benchmark_select_default_paginated[redis_store] 359.8463 (2.98) 325.8310 (3.02) 623.2360 (1.65) benchmark_select_columns[redis_store] 486.6047 (4.04) 429.3250 (3.98) 867.8780 (2.29) benchmark_select_default[redis_store] 631.3835 (5.24) 584.7630 (5.42) 1,033.5990 (2.73) benchmark_bulk_insert[redis_store] 761.0832 (6.31) 724.1240 (6.71) 1,034.2950 (2.73) ``` Contributions Contributions are welcome. The docs have to maintained, the code has to be made cleaner, more idiomatic and faster, and there might be need for someone else to take over this repo in case I move on to other things. It happens! When you are ready, look at the CONTRIBUTIONS GUIDELINES License Copyright (c) 2020 Martin Ahindura Licensed under the MIT License Gratitude "There is no condemnation now for those who live in union with Christ Jesus. For the law of the Spirit, which brings us life in union with Christ Jesus, has set me free from the law of sin and death" -- Romans 8: 1-2 All glory be to God

pypi package. Binary | Source

Latest version: 0.7.0 Released: 2024-07-20

pydantic-tools

pydantic-tools A set of tools for pydantic(i18n, alias(comming soon), etc.) Installation pip install pydantic-tools i18n A simple example ```python from pydantic_tools import i18n class TestModel(i18n.BaseModel_i18n): name: str age: int class Config: locale = "zh_cn" test = TestModel() ``` python pydantic.error_wrappers.ValidationError: 2 validation errors for TestModel name 确保该值不缺失 (type=value_error.missing) age 确保该值不缺失 (type=value_error.missing) Config available locale: The locale of the model, default is None, which means no translation enabled, and the default error message will be used. Use python -m pydantic_tools.i18n -l to show all available locales. locale_strict: Whether to raise an error when the locale is not found, default is False. translations: If specified, the error messages will be translated according to the given dict instead of the locale config. The dict should be like {"value_error.missing": "Missing"}. One of locale and translations should be specified, do not set both of them. extra_translations: If specified, an extra dict will be merged into the default translations. The dict should be like {"value_error.missing": "some msg"}. Show all available locales shell python -m pydantic_tools.i18n -l Show all available translations shell python -m pydantic_tools.i18n -t alias Comming soon

pypi package. Binary

Latest version: 0.3.3 Released: 2023-06-03

pydantic-cache

Pydantic Cache Cache results of Python functions, with support for serialization of rich data types powered by Pydantic. Supports caching to disk or Redis by default, but additional caching backends can easily be added. Examples Basic usage You can use any data types which can be serialized by Pydantic, both in the function signature (cache key) and the returned values: ```python from datetime import datetime, timedelta from pydantic import BaseModel from pydantic_cache import disk_cache class MyModel(BaseModel): a: int b: datetime d: set[datetime] @disk_cache(path="~/.cache/my-function", ttl=timedelta(days=1)) def my_function(date: datetime) -> list[MyModel]: return [] # Some expensive computation ``` In the above example, subsequent calls to the function with the same argument will fetch the results from the cache on disk. Serialization and deserialization are handled based on the function's type annotations. Redis support The library includes support for caching results to/from redis. This depends on redis, which can be installed via pip install pydantic-cache[redis]. ```python from datetime import timedelta from pydantic_cache import cache from pydantic_cache.backend import RedisBackend from redis import Redis redis = Redis(...) @cache(RedisBackend(redis, ttl=timedelta(days=1))) def my_function() -> dict: return {} ``` Custom cache backends You can implement custom cache backends by sub-classing Backend: ```python from pydantic_cache import Backend, cache class MemoryBackend(Backend): def init(self) -> None: # Optional initial set-up of the backend. self._cache: dict[str, str] = {} def get(self, key: str) -> str: # Implement cache retrieval here. # Cache misses should raise a KeyError. return self._cache[key] def write(self, key: str, value: str) -> None: # Write to the cache here. self._cache[key] = value @cache(backend=MemoryBackend) def my_function() -> dict: return {} ``` [!NOTE] Cache backends only interact with serialized data, so the str types above will apply for all backends. Deferred backend resolution Some backends may rely on reading configurations or creating connections to external services, which is best avoided at import time. To support this, the cache decorator optionally accepts a callable which returns the backend, instead of the backend itself. ```python from datetime import timedelta from pathlib import Path from pydantic_cache import DiskBackend, cache from pydantic_settings import BaseSettings class Settings(BaseSettings): cache_ttl: timedelta cache_path: Path def get_cache_backend() -> DiskBackend: settings = Settings() return DiskBackend(settings.cache_path, ttl=settings.cache_ttl) @cache(backend=get_cache_backend) def my_function() -> dict: return {} ``` asyncio support Asynchronous functions are supported by default, however using a synchronous backend will naturally result in blocking calls: ```python import asyncio from pydantic_cache import DiskBackend, cache @cache(backend=DiskBackend(...)) async def my_function() -> dict: return await asyncio.sleep(0, {}) ``` To avoid blocking IO calls in the cache backend, you can implement an asynchronous backend as a subclass of AsyncBackend. See the following example using aioredis: ```python import asyncio import aioredis from pydantic_cache import AsyncBackend, cache class AioRedisBackend(AsyncBackend): def init(self, redis: aioredis.Client) -> None: self.redis = redis async def get(self, key: str) -> str: result = await self.redis.get(key) if result is None: raise KeyError(key) return result async def write(self, key: str, value: str) -> None: await self.redis.set(key, value) @cache(backend=AioRedisBackend(...)) async def my_function() -> dict: return await asyncio.sleep(0, {}) ``` Installation This project is not currently packaged and so must be installed manually. Clone the project with the following command: git clone https://github.com/jacksmith15/pydantic-cache.git Development Install dependencies: shell pyenv shell 3.10.x pre-commit install # Configure commit hooks poetry install # Install Python dependencies Run tests: shell poetry run inv verify License This project is distributed under the MIT license.

pypi package. Binary | Source

Latest version: 0.1.0 Released: 2024-02-11

pydantic-kedro

pydantic-kedro Advanced serialization for Pydantic models via Kedro and fsspec. This package implements custom Kedro "datasets" for both "pure" and "arbitrary" Pydantic models. You can also use it stand-alone, using Kedro just for serializing other object types. Please see the documentation for a tutorial and more examples. Usage with Kedro You can use the [PydanticAutoDataset][pydantic_kedro.PydanticAutoDataset] or any other dataset from pydantic-kedro within your Kedro catalog to save your Pydantic models: ```yaml conf/base/catalog.yml my_pydantic_model: type: pydantic_kedro.PydanticAutoDataset filepath: folder/my_model ``` Direct Dataset Usage This example works for "pure", JSON-safe Pydantic models via PydanticJsonDataset: ```python from pydantic import BaseModel from pydantic.v1 import BaseModel # Pydantic V2 from pydantic_kedro import PydanticJsonDataset class MyPureModel(BaseModel): """Your custom Pydantic model with JSON-safe fields.""" x: int y: str obj = MyPureModel(x=1, y="why?") Create an in-memory (temporary) file via fsspec and save it ds = PydanticJsonDataset("memory://temporary-file.json") ds.save(obj) We can re-load it from the same file read_obj = ds.load() assert read_obj.x == 1 ``` Standalone Usage You can also use pydantic-kedro as a generic saving and loading engine for Pydantic models: ```python from tempfile import TemporaryDirectory from pydantic.v1 import BaseModel from pydantic_kedro import load_model, save_model class MyModel(BaseModel): """My custom model.""" name: str We can use any fsspec URL, so we'll make a temporary folder with TemporaryDirectory() as tmpdir: save_model(MyModel(name="foo"), f"{tmpdir}/my_model") obj = load_model(f"{tmpdir}/my_model") assert obj.name == "foo" ```

pypi package. Binary | Source

Latest version: 0.8.0 Released: 2024-04-01

pjdev-pydantic

pjdev-pydantic Table of Contents Installation License Installation console pip install pjdev-pydantic License pjdev-pydantic is distributed under the terms of the MIT license.

pypi package. Binary

Latest version: 4.4.2 Released: 2025-04-08

Flask-Pydantic

Flask-Pydantic Flask extension for integration of the awesome pydantic package with Flask. Pallets Community Ecosystem [!IMPORTANT]\ This project is part of the Pallets Community Ecosystem. Pallets is the open source organization that maintains Flask; Pallets-Eco enables community maintenance of Flask extensions. If you are interested in helping maintain this project, please reach out on the Pallets Discord server. Installation python3 -m pip install Flask-Pydantic Basics URL query and body parameters validate decorator validates query, body and form-data request parameters and makes them accessible two ways: Using validate arguments, via flask's request variable | parameter type | request attribute name | |:------------------:|:----------------------------:| | query | query_params | | body | body_params | | form | form_params | Using the decorated function argument parameters type hints URL path parameter If you use annotated path URL path parameters as follows ```python @app.route("/users/", methods=["GET"]) @validate() def get_user(user_id: str): pass `` flask_pydantic will parse and validateuser_id` variable in the same manner as for body and query parameters. Additional validate arguments Success response status code can be modified via on_success_status parameter of validate decorator. response_many parameter set to True enables serialization of multiple models (route function should therefore return iterable of models). request_body_many parameter set to False analogically enables serialization of multiple models inside of the root level of request body. If the request body doesn't contain an array of objects 400 response is returned, get_json_params - parameters to be passed to flask.Request.get_json function If validation fails, 400 response is returned with failure explanation. For more details see in-code docstring or example app. Usage Example 1: Query parameters only Simply use validate decorator on route function. :exclamation: Be aware that @app.route decorator must precede @validate (i. e. @validate must be closer to the function declaration). ```python from typing import Optional from flask import Flask, request from pydantic import BaseModel from flask_pydantic import validate app = Flask("flask_pydantic_app") class QueryModel(BaseModel): age: int class ResponseModel(BaseModel): id: int age: int name: str nickname: Optional[str] = None Example 1: query parameters only @app.route("/", methods=["GET"]) @validate() def get(query: QueryModel): age = query.age return ResponseModel( age=age, id=0, name="abc", nickname="123" ) ``` See the full example app here age query parameter is a required int curl --location --request GET 'http://127.0.0.1:5000/' if none is provided the response contains: json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "field required", "type": "value_error.missing" } ] } } for incompatible type (e. g. string /?age=not_a_number) curl --location --request GET 'http://127.0.0.1:5000/?age=abc' json { "validation_error": { "query_params": [ { "loc": ["age"], "msg": "value is not a valid integer", "type": "type_error.integer" } ] } } likewise for body parameters example call with valid parameters: curl --location --request GET 'http://127.0.0.1:5000/?age=20' -> {"id": 0, "age": 20, "name": "abc", "nickname": "123"} Example 2: URL path parameter python @app.route("/character//", methods=["GET"]) @validate() def get_character(character_id: int): characters = [ ResponseModel(id=1, age=95, name="Geralt", nickname="White Wolf"), ResponseModel(id=2, age=45, name="Triss Merigold", nickname="sorceress"), ResponseModel(id=3, age=42, name="Julian Alfred Pankratz", nickname="Jaskier"), ResponseModel(id=4, age=101, name="Yennefer", nickname="Yenn"), ] try: return characters[character_id] except IndexError: return {"error": "Not found"}, 400 Example 3: Request body only ```python class RequestBodyModel(BaseModel): name: str nickname: Optional[str] = None Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(body: RequestBodyModel): name = body.name nickname = body.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here Example 4: BOTH query paramaters and request body ```python Example 3: both query paramters and request body @app.route("/both", methods=["POST"]) @validate() def get_and_post(body: RequestBodyModel, query: QueryModel): name = body.name # From request body nickname = body.nickname # From request body age = query.age # from query parameters return ResponseModel( age=age, name=name, nickname=nickname, id=0 ) ``` See the full example app here Example 5: Request form-data only ```python class RequestFormDataModel(BaseModel): name: str nickname: Optional[str] = None Example2: request body only @app.route("/", methods=["POST"]) @validate() def post(form: RequestFormDataModel): name = form.name nickname = form.nickname return ResponseModel( name=name, nickname=nickname,id=0, age=1000 ) ``` See the full example app here Modify response status code The default success status code is 200. It can be modified in two ways in return statement ```python necessary imports, app and models definition ... @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel) def post(): return ResponseModel( id=id_, age=request.query_params.age, name=request.body_params.name, nickname=request.body_params.nickname, ), 201 ``` in validate decorator python @app.route("/", methods=["POST"]) @validate(body=BodyModel, query=QueryModel, on_success_status=201) def post(): ... Status code in case of validation error can be modified using FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE flask configuration variable. Using the decorated function kwargs Instead of passing body and query to validate, it is possible to directly defined them by using type hinting in the decorated function. ```python necessary imports, app and models definition ... @app.route("/", methods=["POST"]) @validate() def post(body: BodyModel, query: QueryModel): return ResponseModel( id=id_, age=query.age, name=body.name, nickname=body.nickname, ) ``` This way, the parsed data will be directly available in body and query. Furthermore, your IDE will be able to correctly type them. Model aliases Pydantic's alias feature is natively supported for query and body models. To use aliases in response modify response model ```python def modify_key(text: str) -> str: # do whatever you want with model keys return text class MyModel(BaseModel): ... model_config = ConfigDict( alias_generator=modify_key, populate_by_name=True ) ``` and set response_by_alias=True in validate decorator python @app.route(...) @validate(response_by_alias=True) def my_route(): ... return MyModel(...) Example app For more complete examples see example application. Configuration The behaviour can be configured using flask's application config FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE - response status code after validation error (defaults to 400) Additionally, you can set FLASK_PYDANTIC_VALIDATION_ERROR_RAISE to True to cause flask_pydantic.ValidationError to be raised with either body_params, form_params, path_params, or query_params set as a list of error dictionaries. You can use flask.Flask.register_error_handler to catch that exception and fully customize the output response for a validation error. Contributing Feature requests and pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. clone repository bash git clone https://github.com/pallets-eco/flask_pydantic.git cd flask_pydantic create virtual environment and activate it bash python3 -m venv venv source venv/bin/activate install development requirements bash python3 -m pip install -r requirements/test.txt checkout new branch and make your desired changes (don't forget to update tests) bash git checkout -b make sure your code style is compliant with Ruff. Your can check these errors and automatically correct some of them with ruff check --select I --fix . run tests and check code format bash python3 -m pytest --ruff --ruff-format push your changes and create a pull request to master branch TODOs: header request parameters cookie request parameters

pypi package. Binary | Source

Latest version: 0.13.1 Released: 2025-04-23