Skip to content

Commit 703b7b2

Browse files
authored
run stubtest on _pydantic_core.pyi (#702)
1 parent 00e9f1b commit 703b7b2

File tree

10 files changed

+96
-51
lines changed

10 files changed

+96
-51
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ jobs:
172172

173173
- uses: actions/setup-python@v4
174174
with:
175-
python-version: '3.10'
175+
python-version: '3.11'
176176

177177
# used to lint js code
178178
- uses: actions/setup-node@v3

.mypy-stubtest-allowlist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# TODO: __init__ signature inherited from BaseException, probably needs investigating
2+
pydantic_core._pydantic_core.PydanticOmit.__init__
3+
# TODO: don't want to expose this staticmethod, requires https://github.com/PyO3/pyo3/issues/2384
4+
pydantic_core._pydantic_core.PydanticUndefinedType.new

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.DEFAULT_GOAL := all
22
black = black python/pydantic_core tests generate_self_schema.py wasm-preview/run_tests.py
33
ruff = ruff python/pydantic_core tests generate_self_schema.py wasm-preview/run_tests.py
4+
mypy-stubtest = python -m mypy.stubtest pydantic_core._pydantic_core --allowlist .mypy-stubtest-allowlist
45

56
.PHONY: install
67
install:
@@ -46,6 +47,7 @@ format:
4647
lint-python:
4748
$(ruff)
4849
$(black) --check --diff
50+
$(mypy-stubtest)
4951
griffe dump -f -d google -LWARNING -o/dev/null python/pydantic_core
5052

5153
.PHONY: lint-rust

python/pydantic_core/__init__.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import sys as _sys
24
from typing import Any as _Any
35

@@ -21,7 +23,7 @@
2123
to_json,
2224
to_jsonable_python,
2325
)
24-
from .core_schema import CoreConfig, CoreSchema, CoreSchemaType
26+
from .core_schema import CoreConfig, CoreSchema, CoreSchemaType, ErrorType
2527

2628
if _sys.version_info < (3, 11):
2729
from typing_extensions import NotRequired as _NotRequired
@@ -33,7 +35,7 @@
3335
else:
3436
from typing import TypedDict as _TypedDict
3537

36-
__all__ = (
38+
__all__ = [
3739
'__version__',
3840
'CoreConfig',
3941
'CoreSchema',
@@ -57,19 +59,35 @@
5759
'PydanticSerializationUnexpectedValue',
5860
'to_json',
5961
'to_jsonable_python',
60-
)
62+
]
6163

6264

6365
class ErrorDetails(_TypedDict):
6466
type: str
65-
loc: 'tuple[int | str, ...]'
67+
loc: tuple[int | str, ...]
6668
msg: str
6769
input: _Any
68-
ctx: _NotRequired['dict[str, str | int | float]']
70+
ctx: _NotRequired[dict[str, str | int | float]]
6971

7072

7173
class InitErrorDetails(_TypedDict):
72-
type: 'str | PydanticCustomError'
73-
loc: _NotRequired['tuple[int | str, ...]']
74+
type: str | PydanticCustomError
75+
loc: _NotRequired[tuple[int | str, ...]]
7476
input: _Any
75-
ctx: _NotRequired['dict[str, str | int | float]']
77+
ctx: _NotRequired[dict[str, str | int | float]]
78+
79+
80+
class ErrorTypeInfo(_TypedDict):
81+
type: ErrorType
82+
message_template_python: str
83+
example_message_python: str
84+
message_template_json: _NotRequired[str]
85+
example_message_json: _NotRequired[str]
86+
example_context: dict[str, str | int | float] | None
87+
88+
89+
class MultiHostHost(_TypedDict):
90+
username: str | None
91+
password: str | None
92+
host: str | None
93+
port: int | None

python/pydantic_core/_pydantic_core.pyi

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import decimal
22
import sys
3-
from typing import Any, Callable, Generic, TypeVar
3+
from typing import Any, Callable, Generic, Type, TypeVar
44

5-
from pydantic_core import ErrorDetails, InitErrorDetails
5+
from pydantic_core import ErrorDetails, ErrorTypeInfo, InitErrorDetails, MultiHostHost
66
from pydantic_core.core_schema import CoreConfig, CoreSchema, ErrorType
77

8-
if sys.version_info < (3, 9):
9-
from typing_extensions import TypedDict
8+
if sys.version_info < (3, 8):
9+
from typing_extensions import final
1010
else:
11-
from typing import TypedDict
11+
from typing import final
1212

1313
if sys.version_info < (3, 11):
14-
from typing_extensions import Literal, LiteralString, NotRequired, TypeAlias
14+
from typing_extensions import Literal, LiteralString, Self, TypeAlias
1515
else:
16-
from typing import Literal, LiteralString, NotRequired, TypeAlias
16+
from typing import Literal, LiteralString, Self, TypeAlias
1717

1818
from _typeshed import SupportsAllComparisons
1919

20-
__all__ = (
20+
__all__ = [
2121
'__version__',
2222
'build_profile',
23+
'ArgsKwargs',
2324
'SchemaValidator',
2425
'SchemaSerializer',
2526
'Url',
@@ -30,19 +31,29 @@ __all__ = (
3031
'PydanticKnownError',
3132
'PydanticOmit',
3233
'PydanticSerializationError',
34+
'PydanticSerializationUnexpectedValue',
35+
'PydanticUndefined',
36+
'PydanticUndefinedType',
37+
'Some',
38+
'to_json',
39+
'to_jsonable_python',
3340
'list_all_errors',
34-
)
41+
]
3542
__version__: str
3643
build_profile: str
3744

38-
T = TypeVar('T', default=Any, covariant=True)
45+
_T = TypeVar('_T', default=Any, covariant=True)
3946

40-
class Some(Generic[T]):
47+
@final
48+
class Some(Generic[_T]):
4149
__match_args__ = ('value',)
4250

4351
@property
44-
def value(self) -> T: ...
52+
def value(self) -> _T: ...
53+
@classmethod
54+
def __class_getitem__(cls, item: Any) -> Type[Self]: ...
4555

56+
@final
4657
class SchemaValidator:
4758
def __init__(self, schema: CoreSchema, config: 'CoreConfig | None' = None) -> None: ...
4859
@property
@@ -85,17 +96,18 @@ class SchemaValidator:
8596
) -> 'dict[str, Any]': ...
8697
def get_default_value(self, *, strict: 'bool | None' = None, context: Any = None) -> Some | None: ...
8798

88-
IncEx: TypeAlias = 'set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None'
99+
_IncEx: TypeAlias = 'set[int] | set[str] | dict[int, _IncEx] | dict[str, _IncEx] | None'
89100

101+
@final
90102
class SchemaSerializer:
91103
def __init__(self, schema: CoreSchema, config: 'CoreConfig | None' = None) -> None: ...
92104
def to_python(
93105
self,
94106
value: Any,
95107
*,
96108
mode: str | None = None,
97-
include: IncEx = None,
98-
exclude: IncEx = None,
109+
include: _IncEx = None,
110+
exclude: _IncEx = None,
99111
by_alias: bool = True,
100112
exclude_unset: bool = False,
101113
exclude_defaults: bool = False,
@@ -109,8 +121,8 @@ class SchemaSerializer:
109121
value: Any,
110122
*,
111123
indent: int | None = None,
112-
include: IncEx = None,
113-
exclude: IncEx = None,
124+
include: _IncEx = None,
125+
exclude: _IncEx = None,
114126
by_alias: bool = True,
115127
exclude_unset: bool = False,
116128
exclude_defaults: bool = False,
@@ -124,8 +136,8 @@ def to_json(
124136
value: Any,
125137
*,
126138
indent: int | None = None,
127-
include: IncEx = None,
128-
exclude: IncEx = None,
139+
include: _IncEx = None,
140+
exclude: _IncEx = None,
129141
by_alias: bool = True,
130142
exclude_none: bool = False,
131143
round_trip: bool = False,
@@ -137,8 +149,8 @@ def to_json(
137149
def to_jsonable_python(
138150
value: Any,
139151
*,
140-
include: IncEx = None,
141-
exclude: IncEx = None,
152+
include: _IncEx = None,
153+
exclude: _IncEx = None,
142154
by_alias: bool = True,
143155
exclude_none: bool = False,
144156
round_trip: bool = False,
@@ -171,12 +183,7 @@ class Url(SupportsAllComparisons):
171183
def unicode_string(self) -> str: ...
172184
def __repr__(self) -> str: ...
173185
def __str__(self) -> str: ...
174-
175-
class MultiHostHost(TypedDict):
176-
username: 'str | None'
177-
password: 'str | None'
178-
host: 'str | None'
179-
port: 'int | None'
186+
def __deepcopy__(self, memo: dict) -> str: ...
180187

181188
class MultiHostUrl(SupportsAllComparisons):
182189
def __init__(self, url: str) -> None: ...
@@ -193,16 +200,19 @@ class MultiHostUrl(SupportsAllComparisons):
193200
def unicode_string(self) -> str: ...
194201
def __repr__(self) -> str: ...
195202
def __str__(self) -> str: ...
203+
def __deepcopy__(self, memo: dict) -> Self: ...
196204

205+
@final
197206
class SchemaError(Exception):
198207
def error_count(self) -> int: ...
199208
def errors(self) -> 'list[ErrorDetails]': ...
200209

210+
@final
201211
class ValidationError(ValueError):
202212
@staticmethod
203213
def from_exception_data(
204214
title: str,
205-
errors: 'list[InitErrorDetails]',
215+
line_errors: 'list[InitErrorDetails]',
206216
error_mode: Literal['python', 'json'] = 'python',
207217
hide_input: bool = False,
208218
) -> ValidationError:
@@ -218,6 +228,7 @@ class ValidationError(ValueError):
218228
def errors(self, *, include_url: bool = True, include_context: bool = True) -> 'list[ErrorDetails]': ...
219229
def json(self, *, indent: 'int | None' = None, include_url: bool = True, include_context: bool = True) -> str: ...
220230

231+
@final
221232
class PydanticCustomError(ValueError):
222233
def __init__(
223234
self, error_type: LiteralString, message_template: LiteralString, context: 'dict[str, Any] | None' = None
@@ -230,6 +241,7 @@ class PydanticCustomError(ValueError):
230241
def message_template(self) -> str: ...
231242
def message(self) -> str: ...
232243

244+
@final
233245
class PydanticKnownError(ValueError):
234246
def __init__(
235247
self, error_type: ErrorType, context: 'dict[str, str | int | float | decimal.Decimal] | None' = None
@@ -242,31 +254,30 @@ class PydanticKnownError(ValueError):
242254
def message_template(self) -> str: ...
243255
def message(self) -> str: ...
244256

257+
@final
245258
class PydanticOmit(Exception):
246259
def __init__(self) -> None: ...
247260

261+
@final
248262
class PydanticSerializationError(ValueError):
249263
def __init__(self, message: str) -> None: ...
250264

265+
@final
251266
class PydanticSerializationUnexpectedValue(ValueError):
252267
def __init__(self, message: 'str | None' = None) -> None: ...
253268

254-
class ErrorTypeInfo(TypedDict):
255-
type: ErrorType
256-
message_template_python: str
257-
example_message_python: str
258-
message_template_json: NotRequired[str]
259-
example_message_json: NotRequired[str]
260-
example_context: 'dict[str, str | int | float] | None'
261-
269+
@final
262270
class ArgsKwargs:
263271
def __init__(self, args: 'tuple[Any, ...]', kwargs: 'dict[str, Any] | None' = None) -> None: ...
264272
@property
265273
def args(self) -> 'tuple[Any, ...]': ...
266274
@property
267275
def kwargs(self) -> 'dict[str, Any] | None': ...
268276

269-
class PydanticUndefinedType: ...
277+
@final
278+
class PydanticUndefinedType:
279+
def __copy__(self) -> Self: ...
280+
def __deepcopy__(self, memo: Any) -> Self: ...
270281

271282
PydanticUndefined: PydanticUndefinedType
272283

python/pydantic_core/core_schema.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44
from collections.abc import Mapping
55
from datetime import date, datetime, time, timedelta
6-
from typing import Any, Callable, Dict, Hashable, List, Optional, Set, Type, Union
6+
from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Optional, Set, Type, Union
77

88
if sys.version_info < (3, 11):
99
from typing_extensions import Protocol, Required, TypeAlias
@@ -15,10 +15,16 @@
1515
else:
1616
from typing import Literal, TypedDict
1717

18-
try:
18+
if TYPE_CHECKING:
1919
from pydantic_core import PydanticUndefined
20-
except ImportError:
21-
PydanticUndefined = object()
20+
else:
21+
# The initial build of pydantic_core requires PydanticUndefined to generate
22+
# the core schema; so we need to conditionally skip it. mypy doesn't like
23+
# this at all, hence the TYPE_CHECKING branch above.
24+
try:
25+
from pydantic_core import PydanticUndefined
26+
except ImportError:
27+
PydanticUndefined = object()
2228

2329

2430
def dict_not_none(**kwargs: Any) -> Any:

src/argument_markers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ impl PydanticUndefinedType {
9393
UNDEFINED_CELL.get(py).unwrap().clone()
9494
}
9595

96+
#[pyo3(signature = (_memo, /))]
9697
fn __deepcopy__(&self, py: Python, _memo: &PyAny) -> Py<Self> {
9798
self.__copy__(py)
9899
}

src/url.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ impl PyUrl {
142142
true // an empty string is not a valid URL
143143
}
144144

145+
#[pyo3(signature = (_memo, /))]
145146
pub fn __deepcopy__(&self, py: Python, _memo: &PyDict) -> Py<PyAny> {
146147
self.clone().into_py(py)
147148
}

src/validators/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ impl PySome {
8282
}
8383

8484
#[classmethod]
85-
pub fn __class_getitem__(cls: &PyType, _args: &PyAny) -> Py<PyType> {
85+
#[pyo3(signature = (_item, /))]
86+
pub fn __class_getitem__(cls: &PyType, _item: &PyAny) -> Py<PyType> {
8687
cls.into_py(cls.py())
8788
}
8889

tests/requirements-linting.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ black==23.3.0
22
griffe==0.27.3
33
pyright==1.1.296
44
ruff==0.0.264
5+
mypy==1.4.1

0 commit comments

Comments
 (0)