From 32efbf069bcb189eaae165c56a73c9341d51fec2 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Wed, 28 Aug 2019 11:54:59 +0100 Subject: [PATCH] dataclass import alias for export (#783) * dataclass import alias for export, fix #781 * support --no-implicit-reexport in pydantic, add changes --- changes/783-samuelcolvin.rst | 1 + pydantic/__init__.py | 2 +- pydantic/dataclasses.py | 2 +- pydantic/error_wrappers.py | 2 +- pydantic/main.py | 6 ++++-- pydantic/schema.py | 25 ++++++++++--------------- pydantic/typing.py | 4 ++-- setup.cfg | 1 + tests/mypy/mypy-default.ini | 3 ++- 9 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 changes/783-samuelcolvin.rst diff --git a/changes/783-samuelcolvin.rst b/changes/783-samuelcolvin.rst new file mode 100644 index 0000000..4d3f570 --- /dev/null +++ b/changes/783-samuelcolvin.rst @@ -0,0 +1 @@ +support ``mypy --no-implicit-reexport`` for dataclasses, also respect ``--no-implicit-reexport`` in pydantic itself. diff --git a/pydantic/__init__.py b/pydantic/__init__.py index 717f1f7..600131b 100644 --- a/pydantic/__init__.py +++ b/pydantic/__init__.py @@ -5,7 +5,7 @@ from .env_settings import BaseSettings from .error_wrappers import ValidationError from .errors import * from .fields import Required -from .main import BaseConfig, BaseModel, Extra, compiled, create_model, validate_model +from .main import * from .parse import Protocol from .schema import Schema from .types import * diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py index 787f215..f76992e 100644 --- a/pydantic/dataclasses.py +++ b/pydantic/dataclasses.py @@ -98,7 +98,7 @@ def _process_class( if TYPE_CHECKING: # pragma: no cover # see https://github.com/python/mypy/issues/6239 for explanation of why we do this - from dataclasses import dataclass + from dataclasses import dataclass as dataclass else: def dataclass( diff --git a/pydantic/error_wrappers.py b/pydantic/error_wrappers.py index 58c3a47..15ed6c7 100644 --- a/pydantic/error_wrappers.py +++ b/pydantic/error_wrappers.py @@ -2,7 +2,7 @@ import json from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Sequence, Tuple, Type, Union if TYPE_CHECKING: # pragma: no cover - from pydantic import BaseConfig # noqa: F401 + from .main import BaseConfig # noqa: F401 from .types import ModelOrDc # noqa: F401 __all__ = 'ErrorWrapper', 'ValidationError' diff --git a/pydantic/main.py b/pydantic/main.py index 9c2e8cc..1c1c72d 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -21,8 +21,8 @@ from .typing import AnyCallable, AnyType, ForwardRef, is_classvar, resolve_annot from .utils import GetterDict, ValueItems, change_exception, truncate, validate_field_name if TYPE_CHECKING: # pragma: no cover - from .dataclasses import DataclassType # noqa: F401 - from .types import CallableGenerator, ModelOrDc + from .typing import CallableGenerator + from .types import ModelOrDc from .class_validators import ValidatorListDict from .typing import TupleGenerator, DictStrAny, DictAny, SetStr, SetIntStr, DictIntStrAny # noqa: F401 @@ -40,6 +40,8 @@ else: # pragma: no cover except AttributeError: compiled = False +__all__ = 'BaseConfig', 'BaseModel', 'Extra', 'compiled', 'create_model', 'validate_model' + class Extra(str, Enum): allow = 'allow' diff --git a/pydantic/schema.py b/pydantic/schema.py index aceb126..13756ff 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -4,11 +4,11 @@ import warnings from datetime import date, datetime, time, timedelta from decimal import Decimal from enum import Enum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, cast from uuid import UUID -import pydantic from pydantic.color import Color from .fields import SHAPE_LIST, SHAPE_MAPPING, SHAPE_SET, SHAPE_SINGLETON, SHAPE_TUPLE, Field @@ -27,12 +27,6 @@ from .types import ( DirectoryPath, EmailStr, FilePath, - IPv4Address, - IPv4Interface, - IPv4Network, - IPv6Address, - IPv6Interface, - IPv6Network, IPvAnyAddress, IPvAnyInterface, IPvAnyNetwork, @@ -58,9 +52,7 @@ from .typing import ( ) if TYPE_CHECKING: # pragma: no cover - from . import dataclasses # noqa: F401 - - BaseModel = pydantic.main.BaseModel + from .main import BaseModel # noqa: F401 __all__ = [ @@ -412,14 +404,16 @@ def get_flat_models_from_field(field: Field, known_models: Set[Type['BaseModel'] :param known_models: used to solve circular references :return: a set with the model used in the declaration for this field, if any, and all its sub-models """ - flat_models: Set[Type['BaseModel']] = set() + from .main import BaseModel # noqa: F811 + + flat_models: Set[Type[BaseModel]] = set() # Handle dataclass-based models field_type = field.type_ - if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), pydantic.BaseModel): + if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): field_type = field_type.__pydantic_model__ # type: ignore if field.sub_fields: flat_models |= get_flat_models_from_fields(field.sub_fields, known_models=known_models) - elif lenient_issubclass(field_type, pydantic.BaseModel) and field_type not in known_models: + elif lenient_issubclass(field_type, BaseModel) and field_type not in known_models: flat_models |= get_flat_models_from_model(field_type, known_models=known_models) return flat_models @@ -734,6 +728,7 @@ def field_singleton_schema( # noqa: C901 (ignore complexity) Take a single Pydantic ``Field``, and return its schema and any additional definitions from sub-models. """ + from .main import BaseModel # noqa: F811 ref_prefix = ref_prefix or default_prefix definitions: Dict[str, Any] = {} @@ -782,9 +777,9 @@ def field_singleton_schema( # noqa: C901 (ignore complexity) if issubclass(field_type, type_): return t_schema, definitions, nested_models # Handle dataclass-based models - if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), pydantic.BaseModel): + if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): field_type = field_type.__pydantic_model__ # type: ignore - if issubclass(field_type, pydantic.BaseModel): + if issubclass(field_type, BaseModel): model_name = model_name_map[field_type] if field_type not in known_models: sub_schema, sub_definitions, sub_nested_models = model_process_schema( diff --git a/pydantic/typing.py b/pydantic/typing.py index 1cf8fd2..2731e03 100644 --- a/pydantic/typing.py +++ b/pydantic/typing.py @@ -37,11 +37,11 @@ except ImportError: if sys.version_info < (3, 7): - from typing import Callable + from typing import Callable as Callable AnyCallable = Callable[..., Any] else: - from collections.abc import Callable + from collections.abc import Callable as Callable from typing import Callable as TypingCallable AnyCallable = TypingCallable[..., Any] diff --git a/setup.cfg b/setup.cfg index 6268faa..32f159a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,6 +41,7 @@ warn_redundant_casts = True warn_unused_ignores = True disallow_any_generics = True check_untyped_defs = True +no_implicit_reexport = True # for strict mypy: (this is the tricky one :-)) disallow_untyped_defs = True diff --git a/tests/mypy/mypy-default.ini b/tests/mypy/mypy-default.ini index 62d2396..c55bfa4 100644 --- a/tests/mypy/mypy-default.ini +++ b/tests/mypy/mypy-default.ini @@ -5,6 +5,7 @@ warn_redundant_casts = True warn_unused_ignores = True disallow_any_generics = True check_untyped_defs = True +no_implicit_reexport = True # for strict mypy: (this is the tricky one :-)) -disallow_untyped_defs = True \ No newline at end of file +disallow_untyped_defs = True