Pydantic

Pydantic is used to enforce type hints at runtime, and provides user friendly errors when data is invalid.

Models

The primary means of defining objects in pydantic is via models (models are classes which inherit from BaseModel). Models work (conceptually) the same as types in strictly typed languages. The idea is that untrusted data can be passed to a model, and after parsing and validation pydantic guarantees that the fields of the resultant model instance will conform to the field types defined on the model.

In the example below are we fetching some account details and want to make sure that the system and username are of type `StrictStr` and the password is a `SecretStr`. The difference between StrictStr and str is that pydantic will complain if the value is not a string when using StrictStr. With only str will pydantic cast the value into a string if it can and only complain if it can't.

Model Signature

All pydantic models will have their [signature generated based on their fields](https://docs.pydantic.dev/usage/models/#model-signature). An accurate signature is useful for introspection purposes.

To be included in the signature, a field's alias or name must be a valid Python identifier. pydantic prefers aliases over names, but may use field names if the alias is not a valid Python identifier.

Field Order

Field order is important in models for the following reasons:

  1. validation is performed in the order fields are defined; fields validators can access the values of earlier fields, but not later ones
  2. field order is preserved in the model schema
  3. field order is preserved in validation errors
  4. field order is preserved by .dict() and .json() functions.

Data Conversion

pydantic will try to cast input data into the model field types. In case the model type permits less information then the amount of data provided, will this lead to data loss.

class Model(BaseModel) a: int

a = Model(a=123.45) # Returns 123

[Strict type checking](https://docs.pydantic.dev/usage/types/#strict-types) is partially supported.

Required Fields

You can declare a field as required in three different ways. Either by just providing the type, or a type and an ellipses or the Field function followed by ellipses.

from pydantic import BaseModel, Field

class Model(BaseModel): a: int b: int = … c: int = Field(…)

To specify that a field is required but that the value can be Null can you use the `None` value. The same three options to write the same is possible.

class Model(BaseModel): a: int | None b: int | None = … c: int | None = Field(…)

The Field function can take many different arguments to provide extra [customization](#field-customization) to the information.

Default Values

A model can have default properties which are defined either with the assignment operator or the [Field customization](#field-customization) `default`.

If you want the default to be generated dynamically can you specify that through the Field `defaultfactory` argument and add a custom function.

Model Methods and Properties

Models possess the following methods and attributes:

  • dict(): returns a dictionary of the model's fields and values; cf. exporting models
  • json(): returns a JSON string representation dict()
  • copy(): returns a copy (by default, [shallow copy](list.md)) of the model
  • parse_obj(): a utility for loading any object into a model with error handling if the object is not a dictionary
  • parse_raw(): a utility for loading strings of numerous formats
  • parse_file(): like parse<sub>raw</sub>() but for file paths
  • from_orm(): loads data into a model from an arbitrary class
  • schema(): returns a dictionary representing the model as JSON Schema
  • schema_json(): returns a JSON string representation of schema()
  • construct(): a class method for creating models without running validation
  • __fields_set__: Set of names of fields which were set when the model instance was initialised
  • __fields__: a dictionary of the model's fields
  • __config__: the configuration class for the model

Limit attribute scope

Include attributes

Recursive Models

https://docs.pydantic.dev/usage/models/#recursive-models

Below is some Json data and then a data class enhanced with Pydantic's BaseModel. The model is using [recursive models](https://docs.pydantic.dev/usage/models/#recursive-models).

from pydantic import BaseModel from typing import Optional

data = { "name": "Max", "age": "55", "location": { "city": "Stockholm", "state": "Stockholm" }, "bike": "best bike", "rides": [7, 3, 12, 14, "12"], "bikebooks": [ { "isin": "23232123459", "title": "Riding my bike", "subtitle": "And other musings" }, { "isin": "2323212345", "title": "Riding my bike again", "subtitle": "No more musings" }, ] }

class Location(BaseModel): city: str state: str country: Optional[str] = "Mariehamn" # Sets the default to "Mariehamn"

@validator('city') @classmethod def cityismariehamn(cls, v): if v != 'Mariehamn': raise ValueError('City must be "Mariehamn".') return v.title()

class Book(BaseModel): isin: int title: str subtitle: str

@validator('isin') @classmethod def isinmustbecorrectlength(cls, v): if len(str(v)) != 11: raise ValueError('isin must be 11 characters long') return v

class User(BaseModel): name: str location: Optional[Location] = None bike: str rides: list[int] = [] bikebooks: list[Book] = [Book(**book) for book in data.get('bikebooks')]

user = User(**data)

Reserved Names

If you encounter reserved names for your data classes, then can you use an [alias](https://docs.pydantic.dev/usage/models/#reserved-names). This is for example useful if you use Pydantic with an ORM or if you want to expose names as camelCase but internally use snake​\case.

class MyModel(BaseModel): metadata: dict[str, str] = Field(alias='metadata_')

class Config: ormmode = True

Alias precedendence

https://docs.pydantic.dev/usage/model_config/#alias-precedence

<a id="org3f29da8"></a>

## Construct

The [construct() method](https://docs.pydantic.dev/usage/models/#creating-models-without-validation) allows models to be created without validation. This can be useful when data has already been validated or comes from a trusted source and you want to create a model as efficiently as possible (construct() is generally around 30x faster than creating a model with full validation).

<a id="orgfab40f8"></a>

## Create Model from NamedTuple or TypedDict

Sometimes you already use in your application classes that inherit from NamedTuple or TypedDict and you don't want to duplicate all your information to have a BaseModel. For this pydantic provides [create<sub>model</sub><sub>from</sub><sub>namedtuple</sub> and create<sub>model</sub><sub>from</sub><sub>typeddict</sub> methods](https://docs.pydantic.dev/usage/models/#model-creation-from-namedtuple-or-typeddict). Those methods have the exact same keyword arguments as create<sub>model</sub>.

<a id="org90bf254"></a>

## Custom Root Types

Pydantic models can be defined with a [custom root type](https://docs.pydantic.dev/usage/models/#custom-root-types) by declaring the <span class="underline"><span class="underline">root</span></span> field.

The root type can be any type supported by pydantic, and is specified by the type hint on the <span class="underline"><span class="underline">root</span></span> field. The root value can be passed to the model <span class="underline"><span class="underline">init</span></span> via the <span class="underline"><span class="underline">root</span></span> keyword argument, or as the first and only argument to parse<sub>obj</sub>.

If you call the parse<sub>obj</sub> method for a model with a custom root type with a dict as the first argument, the following logic is used:

If the custom root type is a mapping type (eg., Dict or Mapping), the argument itself is always validated against the custom root type. For other custom root types, if the dict has precisely one key with the value <span class="underline"><span class="underline">root</span></span>, the corresponding value will be validated against the custom root type. Otherwise, the dict itself is validated against the custom root type.

<a id="org18adef4"></a>

## Faux Immutability

[Models can be configured to be immutable](https://docs.pydantic.dev/usage/models/#faux-immutability) via allow​\mutation = False. When this is set, attempting to change the values of instance attributes will raise errors. See model config for more details on Config.

<a id="org0adeaa3"></a>

## Generic Models

The [generic models](https://docs.pydantic.dev/usage/models/#generic-models) makes it easier to reuse a common model structure.

In order to declare a generic model, you perform the following steps:

  • Declare one or more typing.TypeVar instances to use to parameterize your model.
  • Declare a pydantic model that inherits from pydantic.generics.GenericModel and typing.Generic, where you pass the TypeVar instances as parameters to typing.Generic.
  • Use the TypeVar instances as annotations where you will want to replace them with other types or pydantic models.

If you set Config or make use of validator in your generic model definition, it is applied to concrete subclasses in the same way as when inheriting from BaseModel. Any methods defined on your generic class will also be inherited.

<a id="org929a3c1"></a>

## Dynamic Model Creation

There are some occasions where the shape of a model is not known until runtime. For this pydantic provides the [create<sub>model</sub> method to allow models to be created on the fly](https://docs.pydantic.dev/usage/models/#dynamic-model-creation).

<a id="org92cb9d4"></a>

## Abstract Base Classes

Pydantic models can be used alongside Python's [Abstract Base Classes](https://docs.pydantic.dev/usage/models/#abstract-base-classes) (ABCs).

<a id="org922ba25"></a>

## Structural Pattern Matching

https://docs.pydantic.dev/usage/models/#structural-pattern-matching

<a id="org5c1f69c"></a>

## Postponed annotations

https://docs.pydantic.dev/usage/postponed_annotations/

<a id="org54f73be"></a>

https://docs.pydantic.dev/usage/model_config/

<a id="org04c305f"></a>

## Change behaviour globally

https://docs.pydantic.dev/usage/model_config/#change-behaviour-globally

<a id="org8f802ef"></a>

## Alias Generator

https://docs.pydantic.dev/usage/model_config/#alias-generator

<a id="orgee271f8"></a>

## Alias precedence

https://docs.pydantic.dev/usage/model_config/#alias-precedence

See also Models.

<a id="org3e25452"></a>

## Smart union

https://docs.pydantic.dev/usage/model_config/#smart-union

<a id="org5659c83"></a>

Pydantic provides three [classmethod helper functions](https://docs.pydantic.dev/usage/models/#helper-functions) on models for parsing data:

  1. `parseobj` this is very similar to the `_init__` method of the model, except it takes a dict rather than keyword arguments. If the object passed is not a dict a ValidationError will be raised.
  2. `parseraw` this takes a str or bytes and parses it as json, then passes the result to `parseobj`. Parsing pickle data is also supported by setting the content​\type argument appropriately.
  3. `parsefile` this takes in a file path, reads the file and passes the contents to `parseraw`. If content​\type is omitted, it is inferred from the file's extension.

<a id="orgf20576f"></a>

## Parsing data into a specified type

https://docs.pydantic.dev/usage/models/#parsing-data-into-a-specified-type

<a id="org05241c6"></a>

<a id="field-customization"></a>

## Field Customizations

The field function can be used to provide extra information about the field and validations. The field function has the following arguments:

  • `default`: (a positional argument) the default value of the field. Since the Field replaces the field's default, this first argument can be used to set the default. Use ellipsis (&#x2026;) to indicate the field is required.
  • `defaultfactory`: a zero-argument callable that will be called when a default value is needed for this field. Among other purposes, this can be used to set dynamic default values. It is forbidden to set both default and default<sub>factory</sub>.
  • `alias`: the public name of the field
  • `title`: if omitted, field<sub>name.title</sub>() is used
  • `description`: if omitted and the annotation is a sub-model, the docstring of the sub-model will be used
  • `exclude`: exclude this field when dumping (.dict and .json) the instance. The exact syntax and configuration options are described in details in the exporting models section.
  • `include`: include (only) this field when dumping (.dict and .json) the instance. The exact syntax and configuration options are described in details in the exporting models section.
  • `const`: this argument must be the same as the field's default value if present.
  • `gt`: for numeric values (int, float, Decimal), adds a validation of "greater than" and an annotation of exclusiveMinimum to the JSON Schema
  • `ge`: for numeric values, this adds a validation of "greater than or equal" and an annotation of minimum to the JSON Schema
  • `lt`: for numeric values, this adds a validation of "less than" and an annotation of exclusiveMaximum to the JSON Schema
  • `le`: for numeric values, this adds a validation of "less than or equal" and an annotation of maximum to the JSON Schema
  • `multipleof`: for numeric values, this adds a validation of "a multiple of" and an annotation of multipleOf to the JSON Schema
  • `maxdigits`: for Decimal values, this adds a validation to have a maximum number of digits within the decimal. It does not include a zero before the decimal point or trailing decimal zeroes.
  • `decimalplaces`: for Decimal values, this adds a validation to have at most a number of decimal places allowed. It does not include trailing decimal zeroes.
  • `minitems`: for list values, this adds a corresponding validation and an annotation of minItems to the JSON Schema
  • `maxitems`: for list values, this adds a corresponding validation and an annotation of maxItems to the JSON Schema
  • `uniqueitems`: for list values, this adds a corresponding validation and an annotation of uniqueItems to the JSON Schema
  • `minlength`: for string values, this adds a corresponding validation and an annotation of minLength to the JSON Schema
  • `maxlength`: for string values, this adds a corresponding validation and an annotation of maxLength to the JSON Schema
  • `allowmutation`: a boolean which defaults to True. When False, the field raises a TypeError if the field is assigned on an instance. The model config must set validate<sub>assignment</sub> to True for this check to be performed.
  • `regex`: for string values, this adds a Regular Expression validation generated from the passed string and an annotation of pattern to the JSON Schema

pydantic validates strings using re.match, which treats regular expressions as implicitly anchored at the beginning. On the contrary, JSON Schema validators treat the pattern keyword as implicitly unanchored, more like what re.search does.

For interoperability, depending on your desired behavior, either explicitly anchor your regular expressions with ^ (e.g. ^foo to match any string starting with foo), or explicitly allow an arbitrary prefix with .\*? (e.g. .\*?foo to match any string containing the substring foo).

It is possible to use the Config class to set the field properties as well. The only property which is it not possible to set through the configuration is the `default` property.

<a id="orgcfb6219"></a>

## Standard Library Types

https://docs.pydantic.dev/usage/types/#standard-library-types

<a id="orgf3b8472"></a>

## Arbitrary Types Allowed Config

https://docs.pydantic.dev/usage/types/#arbitrary-types-allowed

<a id="orgc2b7ae7"></a>

## Literal Type (only certain values)

https://docs.pydantic.dev/usage/types/#literal-type

One benefit of this field type is that it can be used to check for equality with one or more specific values without needing to declare custom validators.

<a id="org2be4f29"></a>

## Typing Iterables / Complex objects

https://docs.pydantic.dev/usage/types/#typing-iterables

<a id="org9ccfaeb"></a>

## Infinite Generators

https://docs.pydantic.dev/usage/types/#infinite-generators

<a id="org1584709"></a>

## Unions

https://docs.pydantic.dev/usage/types/#unions

<a id="org57c2512"></a>

## Enums

https://docs.pydantic.dev/usage/types/#enums-and-choices

<a id="org11ee131"></a>

## Date Time

https://docs.pydantic.dev/usage/types/#datetime-types

<a id="org22ab145"></a>

## Booleans

https://docs.pydantic.dev/usage/types/#booleans

<a id="org82daf91"></a>

## Callable

https://docs.pydantic.dev/usage/types/#callable

<a id="org66d1b11"></a>

## Type (only Classes, not instances)

https://docs.pydantic.dev/usage/types/#type

<a id="orgd347480"></a>

## TypeVar

https://docs.pydantic.dev/usage/types/#typevar

<a id="orgbda1c93"></a>

## Annotated Types

https://docs.pydantic.dev/usage/types/#annotated-types

<a id="orgb33d7f6"></a>

## Typed Dict

https://docs.pydantic.dev/usage/types/#typeddict

<a id="orgc749b7b"></a>

## Pydantic Specific Types

https://docs.pydantic.dev/usage/types/#pydantic-types

<a id="org9f3662f"></a>

## Secret Types

https://docs.pydantic.dev/usage/types/#secret-types

<a id="orge460e7e"></a>

## Json type

https://docs.pydantic.dev/usage/types/#json-type

<a id="org4b401b3"></a>

## Payment card numbers

https://docs.pydantic.dev/usage/types/#payment-card-numbers

<a id="org8c490b4"></a>

## Constrained Types

https://docs.pydantic.dev/usage/types/#constrained-types

<a id="org56ffec9"></a>

## Strict Types

Use strict types to prevent casting from compatible types. These types will only pass validation when the validated value is of the respective type or is a subtype of that type.

  • Strict Types:
    • StrictStr
    • StrictBytes
    • StrictInt
    • StrictFloat
    • StrictBool

The same behavior is also exposed via the strict field of the constrained types which can be combined with a multitude of complex validation rules.

  • Constrained Types:
    • ConstrainedStr
    • ConstrainedBytes
    • ConstrainedFloat
    • ConstrainedInt

The following caveats apply:

  • `StrictBytes` (and the strict option of `ConstrainedBytes`) will accept both bytes, and bytearray types.
  • `StrictInt` (and the strict option of `ConstrainedInt`) will not accept bool types, even though bool is a subclass of int in Python. Other subclasses will work.
  • `StrictFloat` (and the strict option of `ConstrainedFloat`) will not accept int.

<a id="orge77c9ba"></a>

## ByteSize

https://docs.pydantic.dev/usage/types/#bytesize

<a id="org2541f34"></a>

## Custom Data Types

https://docs.pydantic.dev/usage/types/#custom-data-types

<a id="org1d427ec"></a>

## Generic Classes as Types

https://docs.pydantic.dev/usage/types/#generic-classes-as-types

<a id="orgccaa3bc"></a>

<a id="org077478e"></a>

## Validators

Custom validation and complex relationships between objects can be achieved using the validator decorator.

https://docs.pydantic.dev/usage/validators/

<a id="orge73c216"></a>

## Pre and per-item validators

https://docs.pydantic.dev/usage/validators/#pre-and-per-item-validators

<a id="org7738875"></a>

## Subclass validators and each<sub>item</sub>

https://docs.pydantic.dev/usage/validators/#subclass-validators-and-each_item

<a id="org965a5ef"></a>

## Validate always

https://docs.pydantic.dev/usage/validators/#validate-always

<a id="orgc0c23b2"></a>

## Reuse validators

Reuse a validator by setting `allowresuse​=True`.

def setfloat(number: str) -> str: return number.replace(',', '.')

class Accountingrow(BaseModel): sum: float

_setfloat = validator('sum', allowreuse=True, checkfields=False)(setfloat)

summan = Accountingrow(sum='99,99')

<a id="orgc8570d0"></a>

## Root validators

It is possible to check the data class itself by using a root validator. https://docs.pydantic.dev/usage/validators/#root-validators

class Book(Basemodel): title: str subtitle: str

@rootvalidator(pre=True) @classmethod def checkthetile(cls, values): """Check if either title or subtitle exist.""" if "title" not in values and "subtitle" not in values: raise ValueError('The book need a title or subtitle.') return values

<a id="org2ca87fa"></a>

## Dataclass validators

https://docs.pydantic.dev/usage/validators/#dataclass-validators

<a id="org03416da"></a>

## Validation decorator

https://docs.pydantic.dev/usage/validation_decorator/

<a id="orgbcff8fc"></a>

## Validate Arguments

The validate<sub>arguments</sub> decorator allows the arguments passed to a function to be parsed and validated using the function's annotations before the function is called. While under the hood this uses the same approach of model creation and initialisation; it provides an extremely easy way to apply validation to your code with minimal boilerplate.

from pydantic import validatearguments, ValidationError

@validatearguments def repeat(s: str, count: int, *, separator: bytes = b'') -> bytes: b = s.encode() return separator.join(b for _ in range(count))

a = repeat('hello', 3) print(a) #> b'hellohellohello'

b = repeat('x', '4', separator=' ') print(b) #> b'x x x x'

try: c = repeat('hello', 'wrong') except ValidationError as exc: print(exc) """ 1 validation error for Repeat count value is not a valid integer (type=typeerror.integer) """

<a id="org416a11d"></a>

### Argument Types

Argument types are inferred from type annotations on the function, arguments without a type decorator are considered as Any. Since validate<sub>arguments</sub> internally uses a standard BaseModel, all types listed in types can be validated, including pydantic models and custom types. As with the rest of pydantic, types can be coerced by the decorator before they're passed to the actual function:

import os from pathlib import Path from re import Pattern

from pydantic import validatearguments, DirectoryPath

@validatearguments def findfile(path: DirectoryPath, regex: Pattern, max=None) -> Path | None: for i, f in enumerate(path.glob('*/')): if max and i > max: return if f.isfile() and regex.fullmatch(str(f.relativeto(path))): return f

thisdir = os.path.dirname(file)

print(findfile(thisdir, 'validation.*')) #> home/runner/work/pydantic/pydantic/docs.tmpexamples/upgraded/validationde #> coratortypes310.py print(findfile(thisdir, 'foobar.*', max=3)) #> None

A few notes:

  • Though they're passed as strings, path and regex are converted to a Path object and regex respectively by the decorator.
  • Max has no type annotation, so will be considered as Any by the decorator.
  • Type coercion like this can be extremely helpful but also confusing or not desired, see below for a discussion of validate<sub>arguments</sub>'s limitations in this regard.

<a id="org92caff3"></a>

## Validate Arguments

The validate<sub>arguments</sub> decorator allows the arguments passed to a function to be parsed and validated using the function's annotations before the function is called. While under the hood this uses the same approach of model creation and initialisation; it provides an extremely easy way to apply validation to your code with minimal boilerplate.

from pydantic import validatearguments, ValidationError

@validatearguments def repeat(s: str, count: int, *, separator: bytes = b'') -> bytes: b = s.encode() return separator.join(b for _ in range(count))

a = repeat('hello', 3) print(a) #> b'hellohellohello'

b = repeat('x', '4', separator=' ') print(b) #> b'x x x x'

try: c = repeat('hello', 'wrong') except ValidationError as exc: print(exc) """ 1 validation error for Repeat count value is not a valid integer (type=typeerror.integer) """

<a id="org392cae7"></a>

### Argument Types

Argument types are inferred from type annotations on the function, arguments without a type decorator are considered as Any. Since validate<sub>arguments</sub> internally uses a standard BaseModel, all types listed in types can be validated, including pydantic models and custom types. As with the rest of pydantic, types can be coerced by the decorator before they're passed to the actual function:

import os from pathlib import Path from re import Pattern

from pydantic import validatearguments, DirectoryPath

@validatearguments def findfile(path: DirectoryPath, regex: Pattern, max=None) -> Path | None: for i, f in enumerate(path.glob('*/')): if max and i > max: return if f.isfile() and regex.fullmatch(str(f.relativeto(path))): return f

thisdir = os.path.dirname(file)

print(findfile(thisdir, 'validation.*')) #> home/runner/work/pydantic/pydantic/docs.tmpexamples/upgraded/validationde #> coratortypes310.py print(findfile(thisdir, 'foobar.*', max=3)) #> None

A few notes:

  • Though they're passed as strings, path and regex are converted to a Path object and regex respectively by the decorator.
  • Max has no type annotation, so will be considered as Any by the decorator.
  • Type coercion like this can be extremely helpful but also confusing or not desired, see below for a discussion of validate<sub>arguments</sub>'s limitations in this regard.

<a id="orgf1e3670"></a>

Pydantic allows auto creation of JSON Schemas from models.

https://docs.pydantic.dev/usage/schema/

<a id="orgc142656"></a>

## Getting schema of a specified type.

https://docs.pydantic.dev/usage/schema/#getting-schema-of-a-specified-type

<a id="org49708a9"></a>

## Field customizations

https://docs.pydantic.dev/usage/schema/#field-customization

<a id="org670a6cd"></a>

## Unenforced Field constraints

https://docs.pydantic.dev/usage/schema/#unenforced-field-constraints

<a id="org88afded"></a>

## typing.Annotated Fields

https://docs.pydantic.dev/usage/schema/#typingannotated-fields

<a id="org81551d1"></a>

## Modifying schema in custom fields

https://docs.pydantic.dev/usage/schema/#modifying-schema-in-custom-fields

<a id="org3d58e0c"></a>

## JSON Schema Types

https://docs.pydantic.dev/usage/schema/#json-schema-types

<a id="orge0a097e"></a>

## Top-level schema generation

https://docs.pydantic.dev/usage/schema/#top-level-schema-generation

<a id="org83ba17e"></a>

## Schema customization

https://docs.pydantic.dev/usage/schema/#schema-customization

<a id="orge0d82ad"></a>

https://docs.pydantic.dev/usage/exporting_models/

<a id="orgd92bda6"></a>

## Include and exclude

<a id="orgc55ad74"></a>

https://docs.pydantic.dev/usage/models/#error-handling pydantic will raise ValidationError whenever it finds an error in the data it's validating.

Validation code should not raise ValidationError itself, but rather raise `ValueError`, `TypeError` or `AssertionError` (or subclasses of ValueError or TypeError) which will be caught and used to populate ValidationError.

One exception will be raised regardless of the number of errors found, that ValidationError will contain information about all the errors and how they happened.

You can access these errors in several ways:

  • `e.errors()` : method will return list of errors found in the input data.
  • `e.json()` : method will return a JSON representation of errors.
  • `str(e)` : method will return a human readable representation of the errors.

Each error object contains:

  • `loc` : the error's location as a list. The first item in the list will be the field where the error occurred, and if the field is a sub-model, subsequent items will be present to indicate the nested location of the error.
  • `type` : a computer-readable identifier of the error type.
  • `msg` : a human readable explanation of the error.
  • `ctx` : an optional object which contains values required to render the error message.

<a id="org4530022"></a>

## Field checks

https://docs.pydantic.dev/usage/validators/#field-checks

<a id="org64e9e2c"></a>

## Custom Errors

https://docs.pydantic.dev/usage/models/#custom-errors

In your custom data types or validators you should use ValueError, TypeError or AssertionError to [raise (custom) errors](https://docs.pydantic.dev/usage/models/#custom-errors).

It is also possible to define your own custom error classes.

<a id="org00a819b"></a>

## Custom Errors

In your custom data types or validators you should use `ValueError`, `TypeError` or `AssertionError` to raise errors.

class NotABarError(PydanticValueError): code = 'notabar' msgtemplate = 'value is not "bar", got "{wrongvalue}"'

class Model(BaseModel): foo: str

@validator('foo') def valuemustequalbar(cls, v): if v != 'bar': raise NotABarError(wrongvalue=v) return v

try: Model(foo='ber') except ValidationError as e: print(e.json())

<a id="settings"></a>

https://docs.pydantic.dev/usage/settings/

<a id="org720c055"></a>

[pydantic-xml](https://pydantic-xml.readthedocs.io/en/latest/index.html) is a pydantic extension implementing model xml serialization (`toxml`) and deserialization (`fromxml`). It uses BaseXMLModel which inherits from the pydantic BaseModel class.

pydantic-xml tries to use the fastest xml parser in your system. It uses lxml if it is installed in your environment. To install pydantic-xml with lxml do install with `pip install pydantic-xml[lxml]`.

  • Fields from primitive types (int, float, str, datetime, etc) are extracted from element text by default.
  • To alter the default behaviour should the field be marked as `attr` or `element`.

    class Company(BaseXmlModel): tradename: str = attr(name='trade-name') # extracted from the 'trade-name' attribute website: HttpUrl = element() # extracted from the 'website' element text description: str # extracted from the root element text

    <Company trade-name="SpaceX"> company description text <website>https://www.spacex.com</website> </Company>

The elements are by default using the strict [search mode](https://pydantic-xml.readthedocs.io/en/latest/pages/data-binding/elements.html#elements-search-mode). You can however apply the `searchmode​='Ordered'` or `searchmode​='Unordered` as well.

<a id="orgca2811c"></a>

## Generic XML Models

import datetime as dt import pathlib from typing import Generic, TypeVar from pydantic import HttpUrl from pydanticxml import BaseGenericXmlModel, BaseXmlModel, element

AuthType = TypeVar('AuthType')

class SoapHeader( BaseGenericXmlModel, Generic[AuthType], tag='Header', ns='soap',): auth: AuthType

class SoapMethod(BaseXmlModel): pass

MethodType = TypeVar('MethodType', bound=SoapMethod)

class SoapBody( BaseGenericXmlModel, Generic[MethodType], tag='Body', ns='soap',): call: MethodType

HeaderType = TypeVar('HeaderType', bound=SoapHeader) BodyType = TypeVar('BodyType', bound=SoapBody)

class SoapEnvelope( BaseGenericXmlModel, Generic[HeaderType, BodyType], tag='Envelope', ns='soap', nsmap={ 'soap': 'http://www.w3.org/2003/05/soap-envelope/', },): header: HeaderType body: BodyType

class BasicAuth( BaseXmlModel, tag='BasicAuth', ns='auth', nsmap={ 'auth': 'http://www.company.com/auth', },): user: str = element(tag='Username') password: str = element(tag='Password')

class CreateCompanyMethod( SoapMethod, tag='CreateCompany', ns='co', nsmap={ 'co': 'https://www.company.com/co', },): tradename: str = element(tag='TradeName') founded: dt.date = element(tag='Founded') website: HttpUrl = element(tag='WebSite')

CreateCompanyRequest = SoapEnvelope[ SoapHeader[ BasicAuth ], SoapBody[ CreateCompanyMethod ], ]

xmldoc = pathlib.Path('./doc.xml').readtext()

request = CreateCompanyRequest.fromxml(xmldoc)

assert request == CreateCompanyRequest.parsefile('./doc.json')

<a id="org840a4c0"></a>

https://docs.pydantic.dev/usage/models/#orm-mode-aka-arbitrary-class-instances

<a id="orgcf430b6"></a>

## Recursive ORM Models

https://docs.pydantic.dev/usage/models/#recursive-orm-models

<a id="org4fbbae3"></a>

## Data binding

https://docs.pydantic.dev/usage/models/#data-binding Arbitrary classes are processed by pydantic using the GetterDict class (see utils.py), which attempts to provide a dictionary-like interface to any class. You can customise how this works by setting your own sub-class of `GetterDict` as the value of Config.getter<sub>dict</sub>.

You can also customise class validation using root<sub>validators</sub> with `pre=True`. In this case your validator function will be passed a GetterDict instance which you may copy and modify.

<a id="org9e8b38a"></a>

[SQLModel](https://sqlmodel.tiangolo.com/) is a library for interacting with SQL databases from Python code, with Python objects.

When working with pure SQL, it's common to name the tables in plural. So, the table would be named heroes instead of hero, because it could contain multiple rows, each with one hero. And it's common practice to use singular names for classes (e.g. class Hero, instead of class Heroes). Using singular names for classes like class Hero also makes your code more intuitive. It is possible to override the table name.

<a id="orgd97e7bb"></a>

## Create DB Connection

from sqlmodel import createengine engine = createengine("sqlite:///database.db")

SQLModel.metadata.createall(engine)

<a id="orga528d30"></a>

## Create SQLModel

from typing import Optional from sqlmodel import Field, SQLModel

class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primarykey=True) name: str secretname: str age: Optional[int] = None

<a id="orgdec2792"></a>

## Create Data

from sqlmodel import Session

hero1 = Hero(name="Rusty", secretname="Mr Bean")

with Session(engine) as session: session.add(hero1) session.commit()

<a id="orgbc8500f"></a>

## Select

from sqlmodel import select

with Session(engine) as session: statement = select(Hero).where(Hero.name == "Spider-Boy") hero = session.exec(statement).first() print(hero)

To ask for user input and then do an SQL query based on that you can do:

userid = input("Type the user ID: ")

session.exec( select(Hero).where(Hero.id == userid) ).all()

<a id="org284e60a"></a>

<a id="orgceff815"></a>

## mypy

https://docs.pydantic.dev/usage/mypy/

<a id="org23d4756"></a>

## devtools

https://docs.pydantic.dev/usage/devtools/

<a id="orge05d12d"></a>

## rich

https://docs.pydantic.dev/usage/rich/

Dataclasses

src/conf.py

An example of structuring configurations with pydantic dataclasses.

"""Configuration module for hax."""
import keyring
import platform
from datetime import date, datetime, timedelta
from hax.dev import BrewPackage
from tomli import load
from pathlib import Path
from pydantic import BaseModel, SecretStr, computed_field
from pydantic.dataclasses import dataclass


def get_config(file: Path) -> dict:
    """Read a file in TOML format and return a dict."""
    with open(file, mode="rb") as f:
        config = load(f)
    return config


@dataclass
class Project:
    """Project class which contains project related settings.

    Attributes:
        name: The name of the project.
        version: The version of the project.
        path: The path to the project. Is a computed value based on which operating system is used.

    """

    os = platform.system()
    path: Path = Path.home() / "Repos" / "bojort" if os == "Darwin" else Path.home() / "hax"
    database_dir = Path.home() / "Databases"
    temp_dir: Path = Path.home() / "temp"
    config_file: Path = path / "pyproject.toml"
    playwright_auth: Path = temp_dir / "playwright_auth.json"
    name: str = get_config(file=config_file)["project"]["name"]
    version: str = get_config(file=config_file)["project"]["version"]


@dataclass
class Deps:
    """Dependencies class which contains dependencies related settings.

    Attributes:
        az_cli_version: The version of Azure CLI.
    """

    az_cli_version: str = BrewPackage(package_name="az").version()


@dataclass
class Bojort:
    """Bojort class which contains Bojort related settings."""

    path: Path = Path.home() / "Repos" / "bojort"
    config_file: Path = path / "pyproject.toml"
    name: str = get_config(file=config_file)["project"]["name"]
    version: str = get_config(file=config_file)["project"]["version"]


@dataclass
class Crosskey:
    """Crosskey class which contains Crosskey related settings.

    Attributes:
        ad_username: The username for Crosskey AD.
        jira_url: The url for Crosskey Jira.
        wiki_url: The url for Crosskey Confluence.
    """

    ad_username: str = "kgronber"
    jira_url: str = "https://jira.crosskey.fi"
    wiki_url: str = "https://wiki.crosskey.fi"


class Secret(BaseModel):
    """Class which contains secrets."""

    @computed_field()
    def crosskey_ad_password(self) -> SecretStr:
        password = keyring.get_password(
            service_name=f"Crosskey AD ({Crosskey.ad_username})", username=Crosskey.ad_username)
        return SecretStr(password)

    @computed_field()
    def openai_token(self) -> SecretStr:
        token = keyring.get_password(service_name="OpenAI", username="openai-token")
        return SecretStr(token)


@dataclass(frozen=True)
class Time:
    """Class for time related settings."""

    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    today = date.today()
    yesterday = today - timedelta(days=1)
    ty = today.strftime("%Y")
    tm = today.strftime("%m")
    td = today.strftime("%d")
    yy = yesterday.strftime("%Y")
    ym = yesterday.strftime("%m")
    yd = yesterday.strftime("%d")