8000 Switching to `pydantic_core` by samuelcolvin · Pull Request #4516 · pydantic/pydantic · GitHub
[go: up one dir, main page]

Skip to content

Switching to pydantic_core #4516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 70 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
e2e4602
working on core schema generation
samuelcolvin Sep 12, 2022
964bd48
adapting main.py
samuelcolvin Sep 14, 2022
71fd9a6
getting tests to run
samuelcolvin Sep 14, 2022
9c0b13d
fix tests
samuelcolvin Sep 14, 2022
3c50562
disable pyright, fix mypy
samuelcolvin Sep 14, 2022
4fd2027
moving to class-based model generation
samuelcolvin Sep 14, 2022
169db6e
working on validators
samuelcolvin Sep 15, 2022
9b4a41d
change how models are created
samuelcolvin Sep 16, 2022
59bdd49
start fixing test_main.py
samuelcolvin Sep 16, 2022
246fff2
fixing mypy
samuelcolvin Sep 20, 2022
5b19c5e
SelfType
samuelcolvin Sep 20, 2022
99a8a15
recursive models working, more tests fixed
samuelcolvin Sep 24, 2022 8000
8426d5c
fix tests on <3.10
samuelcolvin Sep 24, 2022
034132e
get docs build to pass
samuelcolvin Sep 24, 2022
a4cf9d7
starting to cleanup types.py
samuelcolvin Sep 24, 2022
b948a30
starting works on custom types
samuelcolvin Sep 24, 2022
98c3391
working on using annotated-types
samuelcolvin Sep 25, 2022
8638a00
using annoated types for constraints
samuelcolvin Sep 26, 2022
e6f424d
lots of cleanup, fixing network tests
samuelcolvin Sep 26, 2022
e40986d
network tests passing :tada:
samuelcolvin Sep 27, 2022
cd96301
working on types
samuelcolvin Sep 27, 2022
0ec15d8
working on types and cleanup
samuelcolvin Sep 29, 2022
b73d44a
fixing UUID type, restructing again
samuelcolvin Sep 30, 2022
8ddc5ba
more types and newer pydantic-core
samuelcolvin Sep 30, 2022
e1060fa
working on Iterable
samuelcolvin Sep 30, 2022
20eefe2
more test_types tests
samuelcolvin Oct 11, 2022
9e81a50
support newer pydantic-core, fixing more test_types.py
samuelcolvin Oct 13, 2022
eaf4aa6
working through more test_types.py
samuelcolvin Oct 14, 2022
3bf5ef7
test_types.py at last passing locally :tada:
samuelcolvin Oct 16, 2022
38ee60e
fixing more tests in test_types.py
samuelcolvin Oct 16, 2022
50b745c
fix datetime_parse tests and linting
samuelcolvin Oct 16, 2022
0c6ebd6
get tests running again, rename to test_datetime.py
samuelcolvin Oct 16, 2022
e0f0a69
renaming internal modules
samuelcolvin Oct 17, 2022
7b1f337
working through mypy errors
samuelcolvin Oct 17, 2022
167313a
fixing mypy
samuelcolvin Oct 18, 2022
018e2c9
refactoring _generate_schema.py
samuelcolvin Oct 18, 2022
43b160d
test_main.py passing
samuelcolvin Oct 18, 2022
508db66
uprev deps
samuelcolvin Oct 18, 2022
7dd6a1b
fix conftest and linting?
samuelcolvin Oct 18, 2022
d1ac9c9
importing Annotated
samuelcolvin Oct 18, 2022
6b7d15b
ltining
samuelcolvin Oct 18, 2022
6c5bd7a
import Annotated from typing_extensions
samuelcolvin Oct 18, 2022
f6b8281
fixing 3.7 compatibility
samuelcolvin Oct 18, 2022
996a922
fixing tests on 3.9
samuelcolvin Oct 18, 2022
41c0530
fix linting
samuelcolvin Oct 19, 2022
66ac24c
fixing SecretField and 3.9 tests
samuelcolvin Oct 19, 2022
e7d8be8
customising get_type_hints
samuelcolvin Oct 19, 2022
7768b6d
ignore warnings on 3.11
samuelcolvin Oct 19, 2022
f3a3101
spliting repr out of utils
samuelcolvin Oct 19, 2022
aa65758
removing unused bits of _repr, fix tests for 3.7
samuelcolvin Oct 19, 2022
8c9639c
more cleanup, removing many type aliases
samuelcolvin Oct 19, 2022
730ebbd
clean up repr
samuelcolvin Oct 19, 2022
c6bf124
support namedtuples and typeddicts
samuelcolvin Oct 20, 2022
fa0462c
test is_union
samuelcolvin Oct 20, 2022
50c193b
removing errors, uprev pydantic-core
samuelcolvin Oct 20, 2022
60f1e1b
fix tests on 3.8
samuelcolvin Oct 25, 2022
3d2603b
fixing private attributes and model_post_init
samuelcolvin Oct 25, 2022
10cf0b4
renaming and cleanup
samuelcolvin Oct 26, 2022
fe1c3b2
remove unnecessary PydanticMetadata inheritance
samuelcolvin Oct 26, 2022
eb610b4
fixing forward refs and mypy tests
samuelcolvin Oct 26, 2022
6c1ba91
fix signatures, change how xfail works
samuelcolvin Oct 26, 2022
10ac55f
revert mypy tests to 3.7 syntax
samuelcolvin Oct 26, 2022
bb3db68
correct model title
samuelcolvin Oct 26, 2022
5656056
try to fix tests
samuelcolvin Oct 26, 2022
40358de
fixing ClassVar forward refs
samuelcolvin Oct 27, 2022
fa83ac9
uprev pydantic-core, new error format
samuelcolvin Oct 27, 2022
eaa2ad1
add "force" argument to model_rebuild
samuelcolvin Oct 27, 2022
cc7aaab
Apply suggestions from code review
samuelcolvin Nov 2, 2022
a356144
more suggestions from @tiangolo
samuelcolvin Nov 2, 2022
708c77b
extra -> json_schema_extra on Field
samuelcolvin Nov 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fixing mypy
  • Loading branch information
samuelcolvin committed Sep 20, 2022
commit 246fff2a2b49c2ce17781aba75095d7d382fc4b3
21 changes: 15 additions & 6 deletions pydantic/_internal/babelfish.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def generate_config(config: type[BaseConfig]) -> PydanticCoreConfig:
return {
'typed_dict_extra_behavior': config.extra.value,
# 'allow_inf_nan': config.allow_inf_nan,
'populate_by_name': config.allow_population_by_field_name,
}


Expand Down Expand Up @@ -134,12 +135,20 @@ def apply_validators(schema: PydanticCoreSchema, validators: list[Validator]) ->
"""
for validator in validators:
assert validator.sub_path is None, 'validator.sub_path is not yet supported'
schema = {
'type': 'function',
'mode': validator.mode,
'function': validator.function,
'schema': schema,
}
function = typing.cast(typing.Callable[..., Any], validator.function)
if validator.mode == 'plain':
schema = { # type: ignore[misc,assignment]
'type': 'function',
'mode': 'plain',
'function': function,
}
else:
schema = {
'type': 'function',
'mode': validator.mode,
'function': function,
'schema': schema,
}
return schema


Expand Down
35 changes: 13 additions & 22 deletions pydantic/_internal/model_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from types import FunctionType
from typing import TYPE_CHECKING, Any, Callable, Type, get_type_hints

from pydantic_core import Schema as PydanticCoreSchema, SchemaValidator
from pydantic_core import SchemaValidator

from ..fields import FieldInfo, ModelPrivateAttr, PrivateAttr
from ..utils import ClassAttribute, is_valid_identifier
Expand All @@ -23,22 +23,9 @@


IGNORED_TYPES: tuple[Any, ...] = (FunctionType, property, type, classmethod, staticmethod)
DUNDER_ATTRIBUTES = {
'__annotations__',
'__classcell__',
'__doc__',
'__module__',
'__orig_bases__',
'__orig_class__',
'__qualname__',
'__slots__',
'__config__',
}
# attribute names to ignore off the bat, mostly to save time
IGNORED_ATTRIBUTES = DUNDER_ATTRIBUTES | {'Config'}


def inspect_namespace(namespace) -> None:


def inspect_namespace(namespace: dict[str, Any]) -> None:
"""
iterate over the namespace and:
* gather private attributes
Expand All @@ -48,13 +35,13 @@ def inspect_namespace(namespace) -> None:
raw_annotations = namespace.get('__annotations__', {})
for var_name, value in namespace.items():
if isinstance(value, ModelPrivateAttr):
if not var_name.startswith('_') or var_name in DUNDER_ATTRIBUTES:
if not single_underscore(var_name):
raise NameError(
f'Private attributes "{var_name}" must not be a valid field name; '
f'Use sunder or dunder names, e. g. "_{var_name}" or "__{var_name}__"'
f'Use sunder or dunder names, e. g. "_{var_name}"'
)
private_attributes[var_name] = value
elif var_name in IGNORED_ATTRIBUTES:
elif not single_underscore(var_name):
continue
elif var_name.startswith('_'):
private_attributes[var_name] = PrivateAttr(default=value)
Expand All @@ -73,6 +60,10 @@ def inspect_namespace(namespace) -> None:
namespace['__slots__'] = slots | private_attributes.keys()


def single_underscore(name: str) -> bool:
return name.startswith('_') and not name.startswith('__')


class SelfType:
"""
This is used to identify a reference to the current model class, e.g. in recursive classes
Expand All @@ -83,7 +74,7 @@ class SelfType:

def complete_model_class(
cls: Type[BaseModel], name: str, validator_functions: ValidationFunctions, bases: tuple[type[Any], ...]
):
) -> None:
"""
Collect bound validator functions, build the model validation schema and set the model signature.

Expand Down Expand Up @@ -133,7 +124,7 @@ def complete_model_class(

cls.__fields__ = fields
cls.__pydantic_validator__ = SchemaValidator(inner_schema, core_config)
cls.__pydantic_validation_schema__: PydanticCoreSchema = {
cls.__pydantic_validation_schema__ = {
'type': 'new-class',
'class_type': cls,
'schema': inner_schema,
Expand Down
68 changes: 1 addition & 67 deletions pydantic/_internal/typing_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
Mapping,
NewType,
Optional,
Sequence,
Set,
Tuple,
Type,
Expand Down Expand Up @@ -46,8 +45,6 @@
'new_type_supertype',
'is_classvar',
'is_finalvar',
'update_field_forward_refs',
'update_model_forward_refs',
'TupleGenerator',
'DictStrAny',
'DictAny',
Expand Down Expand Up @@ -305,8 +302,6 @@ def origin_is_union(tp: Optional[Type[Any]]) -> bool:


if TYPE_CHECKING:
from ..fields import ModelField

TupleGenerator = Generator[Tuple[str, Any], None, None]
DictStrAny = Dict[str, Any]
DictAny = Dict[Any, Any]
Expand All @@ -317,7 +312,7 @@ def origin_is_union(tp: Optional[Type[Any]]) -> bool:
DictIntStrAny = Dict[IntStr, Any]
MappingIntStrAny = Mapping[IntStr, Any]
CallableGenerator = Generator[AnyCallable, None, None]
ReprArgs = Sequence[Tuple[Optional[str], Any]]
ReprArgs = Iterable[Tuple[Optional[str], Any]]
AnyClassMethod = classmethod[Any]


Expand Down Expand Up @@ -522,67 +517,6 @@ def is_finalvar(ann_type: Type[Any]) -> bool:
return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type))


def update_field_forward_refs(field: 'ModelField', globalns: Any, localns: Any) -> None:
"""
Try to update ForwardRefs on fields based on this ModelField, globalns and localns.
"""
prepare = False
if field.type_.__class__ == ForwardRef:
prepare = True
field.type_ = evaluate_forwardref(field.type_, globalns, localns)
if field.outer_type_.__class__ == ForwardRef:
prepare = True
field.outer_type_ = evaluate_forwardref(field.outer_type_, globalns, localns)
if prepare:
field.prepare()

if field.sub_fields:
for sub_f in field.sub_fields:
update_field_forward_refs(sub_f, globalns=globalns, localns=localns)

if field.discriminator_key is not None:
field.prepare_discriminated_union_sub_fields()


def update_model_forward_refs(
model: Type[Any],
fields: Iterable['ModelField'],
json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable],
localns: 'DictStrAny',
exc_to_suppress: Tuple[Type[BaseException], ...] = (),
) -> None:
"""
Try to update model fields ForwardRefs based on model and localns.
"""
if model.__module__ in sys.modules:
globalns = sys.modules[model.__module__].__dict__.copy()
else:
globalns = {}

globalns.setdefault(model.__name__, model)

for f in fields:
try:
update_field_forward_refs(f, globalns=globalns, localns=localns)
except exc_to_suppress:
pass

for key in set(json_encoders.keys()):
if isinstance(key, str):
fr: ForwardRef = ForwardRef(key)
elif isinstance(key, ForwardRef):
fr = key
else:
continue

try:
new_key = evaluate_forwardref(fr, globalns, localns)
except exc_to_suppress: # pragma: no cover
continue

json_encoders[new_key] = json_encoders.pop(key)


def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]:
"""
Tries to get the class of a Type[T] annotation. Returns True if Type is used
Expand Down
2 changes: 1 addition & 1 deletion pydantic/_internal/valdation_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def extract_validator(self, name: str, value: Any) -> None:
self._validators[name] = r_validator
self._root_validators.append(name)

def set_bound_functions(self, cls: type[BaseModel]):
def set_bound_functions(self, cls: type[BaseModel]) -> None:
"""
Set functions in self._validators, now that the class is created and functions are bound.
"""
Expand Down
44 changes: 22 additions & 22 deletions pydantic/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,40 +70,40 @@ class ConfigDict(TypedDict, total=False):

class BaseConfig:
title: Optional[str] = None
anystr_lower: bool = False
anystr_upper: bool = False
anystr_strip_whitespace: bool = False
min_anystr_length: int = 0
max_anystr_length: Optional[int] = None
validate_all: bool = False
anystr_lower: bool = False # TODO rename to str_to_lower
anystr_upper: bool = False # TODO rename to str_to_upper
anystr_strip_whitespace: bool = False # TODO rename to str_strip_whitespace
min_anystr_length: int = 0 # TODO rename to str_min_length
max_anystr_length: Optional[int] = None # TODO rename to str_max_length
validate_all: bool = False # TODO remove
extra: Extra = Extra.ignore
allow_mutation: bool = True
allow_mutation: bool = True # TODO remove - replaced by frozen
frozen: bool = False
allow_population_by_field_name: bool = False
allow_population_by_field_name: bool = False # TODO rename to populate_by_name
use_enum_values: bool = False
fields: Dict[str, Union[str, Dict[str, str]]] = {}
fields: Dict[str, Union[str, Dict[str, str]]] = {} # TODO remove
validate_assignment: bool = False
error_msg_templates: Dict[str, str] = {}
arbitrary_types_allowed: bool = False
orm_mode: bool = False
getter_dict: Type[GetterDict] = GetterDict
error_msg_templates: Dict[str, str] = {} # TODO remove
arbitrary_types_allowed: bool = False # TODO default True, or remove
orm_mode: bool = False # TODO rename to from_attributes
getter_dict: Type[GetterDict] = GetterDict # TODO remove
alias_generator: Optional[Callable[[str], str]] = None
keep_untouched: Tuple[type, ...] = ()
schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {}
json_loads: Callable[[str], Any] = json.loads
json_dumps: Callable[..., str] = json.dumps
json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {}
underscore_attrs_are_private: bool = False
keep_untouched: Tuple[type, ...] = () # TODO remove??
schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {} # TODO remove, new model method
json_loads: Callable[[str], Any] = json.loads # TODO decide
json_dumps: Callable[..., str] = json.dumps # TODO decide
json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {} # TODO decide
underscore_attrs_are_private: bool = False # TODO remove
allow_inf_nan: bool = True

# whether inherited models as fields should be reconstructed as base model,
# and whether such a copy should be shallow or deep
copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow'
copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow' # TODO remove???

# whether `Union` should check all allowed types before even trying to coerce
smart_union: bool = False
smart_union: bool = False # TODO remove
# whether dataclass `__post_init__` should be run before or after validation
post_init_call: Literal['before_validation', 'after_validation'] = 'before_validation'
post_init_call: Literal['before_validation', 'after_validation'] = 'before_validation' # TODO remove

@classmethod
def get_field_info(cls, name: str) -> Dict[str, Any]:
Expand Down
12 changes: 5 additions & 7 deletions pydantic/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class M:

from .class_validators import gather_all_validators
from .config import BaseConfig, ConfigDict, Extra, get_config
from .error_wrappers import ValidationError
from .errors import DataclassTypeError
from .fields import Field, FieldInfo, Required, Undefined
from .main import create_model
Expand Down Expand Up @@ -222,7 +221,6 @@ def wrap(cls: Type[Any]) -> 'DataclassClassOrWrapper':

should_validate_on_init = default_validate_on_init if validate_on_init is None else validate_on_init
_add_pydantic_validation_attributes(cls, the_config, should_validate_on_init, dc_cls_doc)
dc_cls.__pydantic_model__.__try_update_forward_refs__(**{cls.__name__: cls})
return dc_cls

if _cls is None:
Expand Down Expand Up @@ -423,11 +421,11 @@ def _dataclass_validate_assignment_setattr(self: 'Dataclass', name: str, value:
if self.__pydantic_initialised__:
d = dict(self.__dict__)
d.pop(name, None)
known_field = self.__pydantic_model__.__fields__.get(name, None)
if known_field:
value, error_ = known_field.validate(value, d, loc=name, cls=self.__class__)
if error_:
raise ValidationError([error_], self.__class__)
# known_field = self.__pydantic_model__.__fields__.get(name, None)
# if known_field:
# value, error_ = known_field.validate(value, d, loc=name, cls=self.__class__)
# if error_:
# raise ValidationError([error_], self.__class__)

object.__setattr__(self, name, value)

Expand Down
14 changes: 7 additions & 7 deletions pydantic/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ class FieldInfo(Representation):
'description',
'exclude',
'include',
'const',
'gt',
'ge',
'lt',
Expand Down Expand Up @@ -107,7 +106,6 @@ def __init__(self, **kwargs: Any) -> None:
self.description = kwargs.pop('description', None)
self.exclude = kwargs.pop('exclude', None)
self.include = kwargs.pop('include', None)
self.const = kwargs.pop('const', None)
self.gt = kwargs.pop('gt', None)
self.ge = kwargs.pop('ge', None)
self.lt = kwargs.pop('lt', None)
Expand Down Expand Up @@ -206,7 +204,6 @@ def Field(
description: str = None,
exclude: Union['AbstractSetIntStr', 'MappingIntStrAny', Any] = None,
include: Union['AbstractSetIntStr', 'MappingIntStrAny', Any] = None,
const: bool = None,
gt: float = None,
ge: float = None,
lt: float = None,
Expand Down Expand Up @@ -241,7 +238,6 @@ def Field(
Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method.
:param include: include this field while dumping.
Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method.
:param const: this field is required and *must* take it's default value
:param gt: only applies to numbers, requires the field to be "greater than". The schema
will have an ``exclusiveMinimum`` validation keyword
:param ge: only applies to numbers, requires the field to be "greater than or equal to". The
Expand Down Expand Up @@ -285,7 +281,6 @@ def Field(
description=description,
exclude=exclude,
include=include,
const=const,
gt=gt,
ge=ge,
lt=lt,
Expand Down Expand Up @@ -338,8 +333,13 @@ def Field(
MAPPING_LIKE_SHAPES: Set[int] = {SHAPE_DEFAULTDICT, SHAPE_DICT, SHAPE_MAPPING, SHAPE_COUNTER}


class ModelField(Representation):
pass
# TODO remove, this is just here to satisfy imports of ModelField
if TYPE_CHECKING:
ModelField = Any
else:

class ModelField(Representation):
pass


class ModelPrivateAttr(Representation):
Expand Down
Loading
0