Skip to content

Commit 08c473a

Browse files
authored
Index improvements (#6314)
1 parent 89032e1 commit 08c473a

19 files changed

+264
-115
lines changed

docs/errors/errors.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,9 @@ def convert_errors(
243243
custom_message = custom_messages.get(error['type'])
244244
if custom_message:
245245
ctx = error.get('ctx')
246-
error['msg'] = custom_message.format(**ctx) if ctx else custom_message
246+
error['msg'] = (
247+
custom_message.format(**ctx) if ctx else custom_message
248+
)
247249
new_errors.append(error)
248250
return new_errors
249251

docs/extra/tweaks.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ aside.blog img {
5959
[data-md-color-scheme="slate"] #company-grid {
6060
background-color: #ffffff;
6161
border-radius: .5rem;
62+
color: black;
6263
}
6364

6465
.tile {

docs/index.md

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,116 +7,140 @@
77

88
{{ version }}.
99

10-
Pydantic is the most widely used Python library for data validation and coercion using Python type annotations.
10+
Pydantic is the most widely used data validation library for Python.
1111

1212
!!! success
1313
Already using Pydantic V1? See the [Migration Guide](migration.md) for notes on upgrading to Pydantic V2 in your applications!
1414

15-
## Why use Pydantic?
16-
17-
Built-in Python type annotations are a useful way to hint about the expected type of data, improving code clarity and supporting development tools. However, Python's type annotations are optional and don't affect the runtime behavior of the program.
18-
19-
Static type checkers like [mypy](https://mypy-lang.org/) use type annotations to catch potential type-related errors before running the program. But static type checkers can't catch all errors, and they don't affect the runtime behavior of the program.
20-
21-
Pydantic, on the other hand, uses type annotations to perform [data validation](usage/validators.md) and [type coercion](usage/conversion_table.md) at runtime, which is particularly useful for ensuring the correctness of user or external data.
22-
23-
Pydantic enables you to convert input data to Python [standard library types and custom types](usage/types/types.md) in a controlled manner, ensuring they meet the specifications you've provided. This eliminates a significant amount of manual data validation and transformation code, making your program more robust and less prone to errors. It's particularly helpful when dealing with untrusted user input such as form data, [JSON documents](usage/json_schema.md), and other data types.
24-
25-
By providing a simple, declarative way of defining how data should be shaped, Pydantic helps you write cleaner, safer, and more reliable code.
26-
27-
## Features of Pydantic
15+
```py lint="skip" upgrade="skip" title="Pydantic Example" requires="3.10"
16+
from pydantic import BaseModel
2817

29-
Some of the main features of Pydantic include:
18+
class MyModel(BaseModel):
19+
a: int
20+
b: list[str]
3021

31-
- [**Data validation**](usage/validators.md): Pydantic validates data as it is assigned to ensure it meets the requirements. It automatically handles a broad range of data types, including custom types and custom validators.
32-
- [**Standard library and custom data types**](usage/types/types.md): Pydantic supports all of the Python standard library types, and you can define custom data types and specify how they should be validated and converted.
33-
- [**Conversion types**](usage/conversion_table.md): Pydantic will not only validate data, but also convert it to the appropriate type if possible. For instance, a string containing a number will be converted to the proper numerical type.
34-
- [**Custom and nested models**](usage/models.md): You can define models (similar to classes) that contain other models, allowing for complex data structures to be neatly and efficiently represented.
35-
- [**Generic models**](usage/models.md#generic-models): Pydantic supports generic models, which allow the declaration of models that are "parameterized" on one or more fields.
36-
- [**Dataclasses**](usage/dataclasses.md): Pydantic supports `dataclasses.dataclass`, offering same data validation as using `BaseModel`.
37-
- [**Model schema generation**](usage/json_schema.md): Pydantic models can be converted to and from a JSON Schema, which can be useful for documentation, code generation, or other purposes.
38-
- [**Error handling**](errors/errors.md): Pydantic models raise informative errors when invalid data is provided, with the option to create your own [custom errors](errors/errors.md#custom-errors).
39-
- [**Settings management**](api/pydantic_settings.md): The `BaseSettings` class from [pydantic-settings](https://github.com/pydantic/pydantic-settings) provides a way to validate, document, and provide default values for environment variables.
22+
m = MyModel(a=123, b=['a', 'b', 'c'])
23+
print(m.model_dump())
24+
#> {'a': 123, 'b': ['a', 'b', 'c']}
25+
```
4026

41-
Pydantic is simple to use, even when doing complex things, and enables you to define and validate data in pure, canonical Python.
27+
## Why use Pydantic?
4228

43-
[Installing Pydantic](install.md) is as simple as: [`pip install pydantic`](install.md).
29+
- **Powered by type hints** — with Pydantic, schema validation and serialization are controlled by type annotations; less to learn, less code to write and integration with your IDE and static analysis tools.
30+
- **Speed** — Pydantic's core validation logic is written in Rust, as a result Pydantic is among the fastest data validation libraries for Python.
31+
- **JSON Schema** — Pydantic models can emit JSON Schema allowing for easy integration with other tools.
32+
- **Strict** and **Lax** mode — Pydantic can run in either `strict=True` mode (where data is not converted) or `strict=False` mode where Pydantic tries to coerce data to the correct type where appropriate.
33+
- **Dataclasses**, **TypedDicts** and more — Pydantic supports validation of many standard library types including `dataclass` and `TypedDict`.
34+
- **Customisation** — Pydantic allows custom validators and serializers to alter how data is processed in many powerful ways.
35+
- **Ecosystem** — around 8,000 packages on PyPI use Pydantic, including massively popular libraries like
36+
[FastAPI](https://github.com/tiangolo/fastapi),
37+
[huggingface/transformers](https://github.com/huggingface/transformers),
38+
[Django Ninja](https://github.com/vitalik/django-ninja),
39+
[SQLModel](https://github.com/tiangolo/sqlmodel),
40+
and [LangChain](https://github.com/hwchase17/langchain).
41+
- **Battle tested** — Pydantic is downloaded >70m times/month and is used by all FAANG companies and 20 of the 25 largest companies on NASDAQ — if you're trying to do something with Pydantic, someone else has probably already done it.
42+
43+
[Installing Pydantic](install.md) is as simple as: [`pip install pydantic`](install.md)
4444

4545
## Pydantic examples
4646

4747
To see Pydantic at work, let's start with a simple example, creating a custom class that inherits from `BaseModel`:
4848

49-
```py
49+
```py upgrade="skip" title="Validation Successful" requires="3.10"
5050
from datetime import datetime
51-
from typing import Optional
5251

53-
from pydantic import BaseModel
52+
from pydantic import BaseModel, PositiveInt
5453

5554

5655
class User(BaseModel):
57-
id: int
58-
name: str = 'John Doe'
59-
signup_ts: Optional[datetime] = None
56+
id: int # (1)!
57+
name: str = 'John Doe' # (2)!
58+
signup_ts: datetime | None # (3)!
59+
tastes: dict[str, PositiveInt] # (4)!
6060

6161

6262
external_data = {
63-
'id': '123',
64-
'signup_ts': '2019-06-01 12:22',
63+
'id': 123,
64+
'signup_ts': '2019-06-01 12:22', # (5)!
65+
'tastes': {
66+
'wine': 9,
67+
b'cheese': 7, # (6)!
68+
'cabbage': '1', # (7)!
69+
},
6570
}
6671

67-
user = User(**external_data)
68-
69-
print(user.model_dump())
70-
#> {'id': 123, 'name': 'John Doe', 'signup_ts': datetime.datetime(2019, 6, 1, 12, 22)}
72+
user = User(**external_data) # (8)!
73+
74+
print(user.id) # (9)!
75+
#> 123
76+
print(user.model_dump()) # (10)!
77+
"""
78+
{
79+
'id': 123,
80+
'name': 'John Doe',
81+
'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
82+
'tastes': {'wine': 9, 'cheese': 7, 'cabbage': 1},
83+
}
84+
"""
7185
```
7286

73-
What's going on here:
74-
75-
* `id` is of type `int`; the annotation-only declaration tells Pydantic that this field is required. Strings,
87+
1. `id` is of type `int`; the annotation-only declaration tells Pydantic that this field is required. Strings,
7688
bytes, or floats will be coerced to ints if possible; otherwise an exception will be raised.
77-
* `name` is inferred as a string from the provided default; because it has a default, it is not required.
78-
* `signup_ts` is a `datetime` field that is not required (and takes the value `None` if a value is not supplied).
89+
2. `name` is a string; because it has a default, it is not required.
90+
3. `signup_ts` is a `datetime` field that is required, but the value `None` may be provided;
7991
Pydantic will process either a unix timestamp int (e.g. `1496498400`) or a string representing the date and time.
92+
4. `tastes` is a dictionary with string keys and positive integer values. The `PositiveInt` type is shorthand for `Annotated[int, annotated_types.Gt(0)]`.
93+
5. The input here is an ISO8601 formatted datetime, Pydantic will convert it to a `datetime` object.
94+
6. The key here is `bytes`, but Pydantic will take care of coercing it to a string.
95+
7. Similarly, Pydantic will coerce the string `'1'` to an integer `1`.
96+
8. Here we create instance of `User` by passing our external data to `User` as keyword arguments
97+
9. We can access fields as attributes of the model
98+
10. We can convert the model to a dictionary with `model_dump()`
8099

81100
If validation fails, Pydantic will raise an error with a breakdown of what was wrong:
82101

83-
```py
102+
```py upgrade="skip" title="Validation Error" requires="3.10"
84103
from datetime import datetime
85-
from typing import Optional
86104

87-
from pydantic import BaseModel, ValidationError
105+
from pydantic import BaseModel, PositiveInt, ValidationError
88106

89107

90108
class User(BaseModel):
91109
id: int
92110
name: str = 'John Doe'
93-
signup_ts: Optional[datetime] = None
111+
signup_ts: datetime | None
112+
tastes: dict[str, PositiveInt]
113+
94114

115+
external_data = {'id': 'not an int', 'tastes': {}} # (1)!
95116

96117
try:
97-
User(name=1234)
118+
User(**external_data) # (2)!
98119
except ValidationError as e:
99120
print(e.errors())
100121
"""
101122
[
102123
{
103-
'type': 'missing',
124+
'type': 'int_parsing',
104125
'loc': ('id',),
105-
'msg': 'Field required',
106-
'input': {'name': 1234},
107-
'url': 'https://errors.pydantic.dev/2/v/missing',
126+
'msg': 'Input should be a valid integer, unable to parse string as an integer',
127+
'input': 'not an int',
128+
'url': 'https://errors.pydantic.dev/2/v/int_parsing',
108129
},
109130
{
110-
'type': 'string_type',
111-
'loc': ('name',),
112-
'msg': 'Input should be a valid string',
113-
'input': 1234,
114-
'url': 'https://errors.pydantic.dev/2/v/string_type',
131+
'type': 'missing',
132+
'loc': ('signup_ts',),
133+
'msg': 'Field required',
134+
'input': {'id': 'not an int', 'tastes': {}},
135+
'url': 'https://errors.pydantic.dev/2/v/missing',
115136
},
116137
]
117138
"""
118139
```
119140

141+
1. the input data is wrong here — `id` is a valid int, and `signup_ts` is missing
142+
2. `User(...)` will raise a `ValidationError` with a list of errors
143+
120144
## Who is using Pydantic?
121145

122146
Hundreds of organisations and packages are using Pydantic. Some of the prominent companies and organizations around the world who are using Pydantic include:
@@ -294,7 +318,7 @@ for (const company of companies) {
294318
const tile = document.createElement('div');
295319
tile.classList.add('tile');
296320
tile.innerHTML = `
297-
<img src="${company.logoUrl}" />
321+
<img src="${company.logoUrl}" alt="${company.name}" />
298322
`;
299323
grid.appendChild(tile);
300324
}

docs/migration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ from pydantic import BaseModel, ValidationError
466466

467467
class Foo(BaseModel):
468468
f1: str # required, cannot be None
469-
f2: Optional[str] # required, can be None - same as Union[str, None] / str | None
469+
f2: Optional[str] # required, can be None - same as str | None
470470
f3: Optional[str] = None # not required, can be None
471471
f4: str = 'Foobar' # not required, but cannot be None
472472

docs/usage/dataclasses.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ class User:
1919

2020
user = User(id='42', signup_ts='2032-06-21T12:00')
2121
print(user)
22-
#> User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0))
22+
"""
23+
User(id=42, name='John Doe', signup_ts=datetime.datetime(2032, 6, 21, 12, 0))
24+
"""
2325
```
2426

2527
!!! note
@@ -63,7 +65,11 @@ print(TypeAdapter(User).json_schema())
6365
'properties': {
6466
'id': {'title': 'Id', 'type': 'integer'},
6567
'name': {'default': 'John Doe', 'title': 'Name', 'type': 'string'},
66-
'friends': {'items': {'type': 'integer'}, 'title': 'Friends', 'type': 'array'},
68+
'friends': {
69+
'items': {'type': 'integer'},
70+
'title': 'Friends',
71+
'type': 'array',
72+
},
6773
'age': {
6874
'anyOf': [{'type': 'integer'}, {'type': 'null'}],
6975
'default': None,
@@ -230,7 +236,9 @@ file = File(
230236
last_modification_time='2020-01-01T00:00',
231237
) # nothing is validated as expected
232238
print(file)
233-
#> File(filename=['not', 'a', 'string'], last_modification_time='2020-01-01T00:00')
239+
"""
240+
File(filename=['not', 'a', 'string'], last_modification_time='2020-01-01T00:00')
241+
"""
234242

235243
try:
236244
Foo(file=file)
@@ -286,8 +294,9 @@ try:
286294
dc: DC
287295
other: str
288296

297+
# invalid as it is now a pydantic dataclass
289298
Model(dc=my_dc, other='other')
290-
except PydanticSchemaGenerationError as e: # invalid as it is now a pydantic dataclass
299+
except PydanticSchemaGenerationError as e:
291300
print(e.message)
292301
"""
293302
Unable to generate pydantic-core schema for <class '__main__.ArbitraryType'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.

docs/usage/errors.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ class Model(BaseModel):
115115

116116

117117
print(Model.model_json_schema())
118-
#> {'examples': 'examples', 'properties': {}, 'title': 'Model', 'type': 'object'}
118+
"""
119+
{'examples': 'examples', 'properties': {}, 'title': 'Model', 'type': 'object'}
120+
"""
119121
```
120122

121123
## Decorator on missing field {#decorator-missing-field}
@@ -201,7 +203,9 @@ from pydantic import AliasChoices, BaseModel, Field, PydanticUserError
201203

202204

203205
class Cat(BaseModel):
204-
pet_type: Literal['cat'] = Field(validation_alias=AliasChoices('Pet', 'PET'))
206+
pet_type: Literal['cat'] = Field(
207+
validation_alias=AliasChoices('Pet', 'PET')
208+
)
205209
c: str
206210

207211

docs/usage/fields.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ You can read more about [Alias Precedence](model_config.md#alias-precedence) in
198198

199199
class MyModel(BaseModel):
200200
my_field: int = Field(
201-
..., alias='myValidationAlias', serialization_alias='my_serialization_alias'
201+
...,
202+
alias='myValidationAlias',
203+
serialization_alias='my_serialization_alias',
202204
)
203205

204206

@@ -321,7 +323,9 @@ foo = Foo(
321323
love_for_pydantic=float('inf'),
322324
)
323325
print(foo)
324-
#> positive=1 non_negative=0 negative=-1 non_positive=0 even=2 love_for_pydantic=inf
326+
"""
327+
positive=1 non_negative=0 negative=-1 non_positive=0 even=2 love_for_pydantic=inf
328+
"""
325329
```
326330

327331
??? info "JSON Schema"
@@ -702,7 +706,11 @@ print(User.model_json_schema())
702706
"""
703707
{
704708
'properties': {
705-
'age': {'description': 'Age of the user', 'title': 'Age', 'type': 'integer'},
709+
'age': {
710+
'description': 'Age of the user',
711+
'title': 'Age',
712+
'type': 'integer',
713+
},
706714
'email': {
707715
'examples': ['[email protected]'],
708716
'format': 'email',

docs/usage/json_schema.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,9 @@ class CompressedString:
410410
cls._validate,
411411
core_schema.str_schema(),
412412
serialization=core_schema.plain_serializer_function_ser_schema(
413-
cls._serialize, info_arg=False, return_schema=core_schema.str_schema()
413+
cls._serialize,
414+
info_arg=False,
415+
return_schema=core_schema.str_schema(),
414416
),
415417
)
416418

@@ -422,7 +424,9 @@ class CompressedString:
422424
if word not in inverse_dictionary:
423425
inverse_dictionary[word] = len(inverse_dictionary)
424426
text.append(inverse_dictionary[word])
425-
return CompressedString({v: k for k, v in inverse_dictionary.items()}, text)
427+
return CompressedString(
428+
{v: k for k, v in inverse_dictionary.items()}, text
429+
)
426430

427431
@staticmethod
428432
def _serialize(value: 'CompressedString') -> str:
@@ -443,7 +447,9 @@ print(MyModel.model_json_schema())
443447
}
444448
"""
445449
print(MyModel(value='fox fox fox dog fox'))
446-
#> value=CompressedString(dictionary={0: 'fox', 1: 'dog'}, text=[0, 0, 0, 1, 0])
450+
"""
451+
value = CompressedString(dictionary={0: 'fox', 1: 'dog'}, text=[0, 0, 0, 1, 0])
452+
"""
447453

448454
print(MyModel(value='fox fox fox dog fox').model_dump(mode='json'))
449455
#> {'value': 'fox fox fox dog fox'}
@@ -472,7 +478,9 @@ class RestrictCharacters:
472478
) -> core_schema.CoreSchema:
473479
if not self.alphabet:
474480
raise ValueError('Alphabet may not be empty')
475-
schema = handler(source) # get the CoreSchema from the type / inner constraints
481+
schema = handler(
482+
source
483+
) # get the CoreSchema from the type / inner constraints
476484
if schema['type'] != 'str':
477485
raise TypeError('RestrictCharacters can only be applied to strings')
478486
return core_schema.no_info_after_validator_function(
@@ -482,7 +490,9 @@ class RestrictCharacters:
482490

483491
def validate(self, value: str) -> str:
484492
if any(c not in self.alphabet for c in value):
485-
raise ValueError(f'{value!r} is not restricted to {self.alphabet!r}')
493+
raise ValueError(
494+
f'{value!r} is not restricted to {self.alphabet!r}'
495+
)
486496
return value
487497

488498

0 commit comments

Comments
 (0)