Repository: pydantic/pydantic

Issue:
typing aliases should go in to pydantic.typing
We have quite a few aliases for standard lib `typing` to support both 3.6 and 3.7.

Upon version 1 we should move all these into a new module `pydantic.typing`.


Comments:

Patch:
diff --git a/pydantic/class_validators.py b/pydantic/class_validators.py
--- a/pydantic/class_validators.py
+++ b/pydantic/class_validators.py
@@ -6,7 +6,8 @@
 from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Type
 
 from .errors import ConfigError
-from .utils import AnyCallable, in_ipython
+from .typing import AnyCallable
+from .utils import in_ipython
 
 if TYPE_CHECKING:  # pragma: no cover
     from .main import BaseConfig
diff --git a/pydantic/color.py b/pydantic/color.py
--- a/pydantic/color.py
+++ b/pydantic/color.py
@@ -18,7 +18,7 @@
 from .utils import almost_equal_floats
 
 if TYPE_CHECKING:  # pragma: no cover
-    from .types import CallableGenerator
+    from .typing import CallableGenerator
 
 ColorTuple = Union[Tuple[int, int, int], Tuple[int, int, int, float]]
 ColorType = Union[ColorTuple, str]
diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py
--- a/pydantic/dataclasses.py
+++ b/pydantic/dataclasses.py
@@ -6,7 +6,7 @@
 from .errors import DataclassTypeError
 from .fields import Required
 from .main import create_model, validate_model
-from .utils import AnyType
+from .typing import AnyType
 
 if TYPE_CHECKING:  # pragma: no cover
     from .main import BaseConfig, BaseModel  # noqa: F401
diff --git a/pydantic/errors.py b/pydantic/errors.py
--- a/pydantic/errors.py
+++ b/pydantic/errors.py
@@ -2,7 +2,7 @@
 from pathlib import Path
 from typing import Any, Union
 
-from .utils import AnyType, display_as_type
+from .typing import AnyType, display_as_type
 
 
 class PydanticErrorMixin:
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -22,7 +22,7 @@
 from .class_validators import Validator, make_generic_validator
 from .error_wrappers import ErrorWrapper
 from .types import Json, JsonWrapper
-from .utils import (
+from .typing import (
     AnyCallable,
     AnyType,
     Callable,
@@ -31,8 +31,8 @@
     is_literal_type,
     lenient_issubclass,
     literal_values,
-    sequence_like,
 )
+from .utils import sequence_like
 from .validators import NoneType, constant_validator, dict_validator, find_validators
 
 try:
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -7,22 +7,7 @@
 from functools import partial
 from pathlib import Path
 from types import FunctionType
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Callable,
-    Dict,
-    Generator,
-    List,
-    Optional,
-    Set,
-    Tuple,
-    Type,
-    TypeVar,
-    Union,
-    cast,
-    no_type_check,
-)
+from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type, TypeVar, Union, cast, no_type_check
 
 from .class_validators import ValidatorGroup, extract_validators, inherit_validators
 from .error_wrappers import ErrorWrapper, ValidationError
@@ -32,36 +17,18 @@
 from .parse import Protocol, load_file, load_str_bytes
 from .schema import model_schema
 from .types import PyObject, StrBytes
-from .utils import (
-    AnyCallable,
-    AnyType,
-    ForwardRef,
-    GetterDict,
-    ValueItems,
-    change_exception,
-    is_classvar,
-    resolve_annotations,
-    truncate,
-    update_field_forward_refs,
-    validate_field_name,
-)
+from .typing import AnyCallable, AnyType, ForwardRef, is_classvar, resolve_annotations, update_field_forward_refs
+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 .class_validators import ValidatorListDict
 
-    AnyGenerator = Generator[Any, None, None]
-    TupleGenerator = Generator[Tuple[str, Any], None, None]
-    DictStrAny = Dict[str, Any]
+    from .typing import TupleGenerator, DictStrAny, DictAny, SetStr, SetIntStr, DictIntStrAny  # noqa: F401
+
     ConfigType = Type['BaseConfig']
-    DictAny = Dict[Any, Any]
-    SetStr = Set[str]
-    ListStr = List[str]
     Model = TypeVar('Model', bound='BaseModel')
-    IntStr = Union[int, str]
-    SetIntStr = Set[IntStr]
-    DictIntStrAny = Dict[IntStr, Any]
 
 try:
     import cython  # type: ignore
diff --git a/pydantic/schema.py b/pydantic/schema.py
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -48,7 +48,7 @@
     conlist,
     constr,
 )
-from .utils import (
+from .typing import (
     is_callable_type,
     is_literal_type,
     is_new_type,
diff --git a/pydantic/types.py b/pydantic/types.py
--- a/pydantic/types.py
+++ b/pydantic/types.py
@@ -13,26 +13,12 @@
 )
 from pathlib import Path
 from types import new_class
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Callable,
-    Dict,
-    Generator,
-    List,
-    Optional,
-    Pattern,
-    Set,
-    Tuple,
-    Type,
-    TypeVar,
-    Union,
-    cast,
-)
+from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Pattern, Set, Tuple, Type, TypeVar, Union, cast
 from uuid import UUID
 
 from . import errors
-from .utils import AnyType, change_exception, import_string, make_dsn, url_regex_generator, validate_email
+from .typing import AnyType
+from .utils import change_exception, import_string, make_dsn, url_regex_generator, validate_email
 from .validators import (
     bytes_validator,
     decimal_validator,
@@ -108,9 +94,8 @@
     from .fields import Field
     from .dataclasses import DataclassType  # noqa: F401
     from .main import BaseModel, BaseConfig  # noqa: F401
-    from .utils import AnyCallable
+    from .typing import CallableGenerator
 
-    CallableGenerator = Generator[AnyCallable, None, None]
     ModelOrDc = Type[Union['BaseModel', 'DataclassType']]
 
 
diff --git a/pydantic/typing.py b/pydantic/typing.py
new file mode 100644
--- /dev/null
+++ b/pydantic/typing.py
@@ -0,0 +1,200 @@
+import sys
+from enum import Enum
+from typing import (  # type: ignore
+    TYPE_CHECKING,
+    Any,
+    ClassVar,
+    Dict,
+    Generator,
+    List,
+    NewType,
+    Optional,
+    Set,
+    Tuple,
+    Type,
+    Union,
+    _eval_type,
+)
+
+try:
+    from typing import _TypingBase as typing_base  # type: ignore
+except ImportError:
+    from typing import _Final as typing_base  # type: ignore
+
+try:
+    from typing import ForwardRef  # type: ignore
+
+    def evaluate_forwardref(type_, globalns, localns):  # type: ignore
+        return type_._evaluate(globalns, localns)
+
+
+except ImportError:
+    # python 3.6
+    from typing import _ForwardRef as ForwardRef  # type: ignore
+
+    def evaluate_forwardref(type_, globalns, localns):  # type: ignore
+        return type_._eval_type(globalns, localns)
+
+
+if sys.version_info < (3, 7):
+    from typing import Callable
+
+    AnyCallable = Callable[..., Any]
+else:
+    from collections.abc import Callable
+    from typing import Callable as TypingCallable
+
+    AnyCallable = TypingCallable[..., Any]
+
+try:
+    from typing_extensions import Literal
+except ImportError:
+    Literal = None  # type: ignore
+
+
+if TYPE_CHECKING:  # pragma: no cover
+    from .fields import Field
+
+    TupleGenerator = Generator[Tuple[str, Any], None, None]
+    DictStrAny = Dict[str, Any]
+    DictAny = Dict[Any, Any]
+    SetStr = Set[str]
+    ListStr = List[str]
+    IntStr = Union[int, str]
+    SetIntStr = Set[IntStr]
+    DictIntStrAny = Dict[IntStr, Any]
+    CallableGenerator = Generator[AnyCallable, None, None]
+
+__all__ = (
+    'ForwardRef',
+    'Callable',
+    'AnyCallable',
+    'AnyType',
+    'display_as_type',
+    'lenient_issubclass',
+    'resolve_annotations',
+    'is_callable_type',
+    'is_literal_type',
+    'literal_values',
+    'Literal',
+    'is_new_type',
+    'new_type_supertype',
+    'is_classvar',
+    'update_field_forward_refs',
+    'TupleGenerator',
+    'DictStrAny',
+    'DictAny',
+    'SetStr',
+    'ListStr',
+    'IntStr',
+    'SetIntStr',
+    'DictIntStrAny',
+    'CallableGenerator',
+)
+
+
+AnyType = Type[Any]
+
+
+def display_as_type(v: AnyType) -> str:
+    if not isinstance(v, typing_base) and not isinstance(v, type):
+        v = type(v)
+
+    if lenient_issubclass(v, Enum):
+        if issubclass(v, int):
+            return 'int'
+        elif issubclass(v, str):
+            return 'str'
+        else:
+            return 'enum'
+
+    try:
+        return v.__name__
+    except AttributeError:
+        # happens with unions
+        return str(v)
+
+
+def lenient_issubclass(cls: Any, class_or_tuple: Union[AnyType, Tuple[AnyType, ...]]) -> bool:
+    return isinstance(cls, type) and issubclass(cls, class_or_tuple)
+
+
+def resolve_annotations(raw_annotations: Dict[str, AnyType], module_name: Optional[str]) -> Dict[str, AnyType]:
+    """
+    Partially taken from typing.get_type_hints.
+
+    Resolve string or ForwardRef annotations into type objects if possible.
+    """
+    if module_name:
+        base_globals: Optional[Dict[str, Any]] = sys.modules[module_name].__dict__
+    else:
+        base_globals = None
+    annotations = {}
+    for name, value in raw_annotations.items():
+        if isinstance(value, str):
+            if sys.version_info >= (3, 7):
+                value = ForwardRef(value, is_argument=False)
+            else:
+                value = ForwardRef(value)
+        try:
+            value = _eval_type(value, base_globals, None)
+        except NameError:
+            # this is ok, it can be fixed with update_forward_refs
+            pass
+        annotations[name] = value
+    return annotations
+
+
+def is_callable_type(type_: AnyType) -> bool:
+    return type_ is Callable or getattr(type_, '__origin__', None) is Callable
+
+
+if sys.version_info >= (3, 7):
+
+    def is_literal_type(type_: AnyType) -> bool:
+        return Literal is not None and getattr(type_, '__origin__', None) is Literal
+
+    def literal_values(type_: AnyType) -> Tuple[Any, ...]:
+        return type_.__args__
+
+
+else:
+
+    def is_literal_type(type_: AnyType) -> bool:
+        return Literal is not None and hasattr(type_, '__values__') and type_ == Literal[type_.__values__]
+
+    def literal_values(type_: AnyType) -> Tuple[Any, ...]:
+        return type_.__values__
+
+
+test_type = NewType('test_type', str)
+
+
+def is_new_type(type_: AnyType) -> bool:
+    return isinstance(type_, type(test_type)) and hasattr(type_, '__supertype__')
+
+
+def new_type_supertype(type_: AnyType) -> AnyType:
+    while hasattr(type_, '__supertype__'):
+        type_ = type_.__supertype__
+    return type_
+
+
+def _check_classvar(v: AnyType) -> bool:
+    return type(v) == type(ClassVar) and (sys.version_info < (3, 7) or getattr(v, '_name', None) == 'ClassVar')
+
+
+def is_classvar(ann_type: AnyType) -> bool:
+    return _check_classvar(ann_type) or _check_classvar(getattr(ann_type, '__origin__', None))
+
+
+def update_field_forward_refs(field: 'Field', globalns: Any, localns: Any) -> None:
+    """
+    Try to update ForwardRefs on fields based on this Field, globalns and localns.
+    """
+    if type(field.type_) == ForwardRef:
+        field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
+        field.prepare()
+    if field.sub_fields:
+        for sub_f in field.sub_fields:
+            update_field_forward_refs(sub_f, globalns=globalns, localns=localns)
diff --git a/pydantic/utils.py b/pydantic/utils.py
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -1,79 +1,24 @@
 import inspect
 import re
-import sys
 from contextlib import contextmanager
-from enum import Enum
 from functools import lru_cache
 from importlib import import_module
-from typing import (  # type: ignore
-    TYPE_CHECKING,
-    Any,
-    ClassVar,
-    Dict,
-    Generator,
-    List,
-    NewType,
-    Optional,
-    Pattern,
-    Set,
-    Tuple,
-    Type,
-    Union,
-    _eval_type,
-    no_type_check,
-)
-
-import pydantic
+from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Pattern, Set, Tuple, Type, Union, no_type_check
 
-try:
-    from typing_extensions import Literal
-except ImportError:
-    Literal = None  # type: ignore
+from . import errors
+from .typing import AnyType
 
 try:
     import email_validator
 except ImportError:
     email_validator = None
 
-try:
-    from typing import _TypingBase as typing_base  # type: ignore
-except ImportError:
-    from typing import _Final as typing_base  # type: ignore
-
-try:
-    from typing import ForwardRef  # type: ignore
-
-    def evaluate_forwardref(type_, globalns, localns):  # type: ignore
-        return type_._evaluate(globalns, localns)
-
-
-except ImportError:
-    # python 3.6
-    from typing import _ForwardRef as ForwardRef  # type: ignore
-
-    def evaluate_forwardref(type_, globalns, localns):  # type: ignore
-        return type_._eval_type(globalns, localns)
-
-
 if TYPE_CHECKING:  # pragma: no cover
     from .main import BaseModel  # noqa: F401
-    from .main import Field  # noqa: F401
-    from .main import SetIntStr, DictIntStrAny, IntStr  # noqa: F401
-    from . import errors  # noqa: F401
-
-if sys.version_info < (3, 7):
-    from typing import Callable
-
-    AnyCallable = Callable[..., Any]
-else:
-    from collections.abc import Callable
-    from typing import Callable as TypingCallable
-
-    AnyCallable = TypingCallable[..., Any]
+    from .typing import SetIntStr, DictIntStrAny, IntStr  # noqa: F401
 
 
 PRETTY_REGEX = re.compile(r'([\w ]*?) *<(.*)> *')
-AnyType = Type[Any]
 
 
 def validate_email(value: str) -> Tuple[str, str]:
@@ -100,7 +45,7 @@ def validate_email(value: str) -> Tuple[str, str]:
     try:
         email_validator.validate_email(email, check_deliverability=False)
     except email_validator.EmailNotValidError as e:
-        raise pydantic.errors.EmailError() from e
+        raise errors.EmailError() from e
 
     return name or email[: email.index('@')], email.lower()
 
@@ -180,25 +125,6 @@ def truncate(v: Union[str], *, max_len: int = 80) -> str:
     return v
 
 
-def display_as_type(v: AnyType) -> str:
-    if not isinstance(v, typing_base) and not isinstance(v, type):
-        v = type(v)
-
-    if lenient_issubclass(v, Enum):
-        if issubclass(v, int):
-            return 'int'
-        elif issubclass(v, str):
-            return 'str'
-        else:
-            return 'enum'
-
-    try:
-        return v.__name__
-    except AttributeError:
-        # happens with unions
-        return str(v)
-
-
 ExcType = Type[Exception]
 
 
@@ -257,10 +183,6 @@ def url_regex_generator(*, relative: bool, require_tld: bool) -> Pattern[str]:
     )
 
 
-def lenient_issubclass(cls: Any, class_or_tuple: Union[AnyType, Tuple[AnyType, ...]]) -> bool:
-    return isinstance(cls, type) and issubclass(cls, class_or_tuple)
-
-
 def in_ipython() -> bool:
     """
     Check whether we're in an ipython environment, including jupyter notebooks.
@@ -273,87 +195,6 @@ def in_ipython() -> bool:
         return True
 
 
-def resolve_annotations(raw_annotations: Dict[str, AnyType], module_name: Optional[str]) -> Dict[str, AnyType]:
-    """
-    Partially taken from typing.get_type_hints.
-
-    Resolve string or ForwardRef annotations into type objects if possible.
-    """
-    if module_name:
-        base_globals: Optional[Dict[str, Any]] = sys.modules[module_name].__dict__
-    else:
-        base_globals = None
-    annotations = {}
-    for name, value in raw_annotations.items():
-        if isinstance(value, str):
-            if sys.version_info >= (3, 7):
-                value = ForwardRef(value, is_argument=False)
-            else:
-                value = ForwardRef(value)
-        try:
-            value = _eval_type(value, base_globals, None)
-        except NameError:
-            # this is ok, it can be fixed with update_forward_refs
-            pass
-        annotations[name] = value
-    return annotations
-
-
-def is_callable_type(type_: AnyType) -> bool:
-    return type_ is Callable or getattr(type_, '__origin__', None) is Callable
-
-
-if sys.version_info >= (3, 7):
-
-    def is_literal_type(type_: AnyType) -> bool:
-        return Literal is not None and getattr(type_, '__origin__', None) is Literal
-
-    def literal_values(type_: AnyType) -> Tuple[Any, ...]:
-        return type_.__args__
-
-
-else:
-
-    def is_literal_type(type_: AnyType) -> bool:
-        return Literal is not None and hasattr(type_, '__values__') and type_ == Literal[type_.__values__]
-
-    def literal_values(type_: AnyType) -> Tuple[Any, ...]:
-        return type_.__values__
-
-
-test_type = NewType('test_type', str)
-
-
-def is_new_type(type_: AnyType) -> bool:
-    return isinstance(type_, type(test_type)) and hasattr(type_, '__supertype__')
-
-
-def new_type_supertype(type_: AnyType) -> AnyType:
-    while hasattr(type_, '__supertype__'):
-        type_ = type_.__supertype__
-    return type_
-
-
-def _check_classvar(v: AnyType) -> bool:
-    return type(v) == type(ClassVar) and (sys.version_info < (3, 7) or getattr(v, '_name', None) == 'ClassVar')
-
-
-def is_classvar(ann_type: AnyType) -> bool:
-    return _check_classvar(ann_type) or _check_classvar(getattr(ann_type, '__origin__', None))
-
-
-def update_field_forward_refs(field: 'Field', globalns: Any, localns: Any) -> None:
-    """
-    Try to update ForwardRefs on fields based on this Field, globalns and localns.
-    """
-    if type(field.type_) == ForwardRef:
-        field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
-        field.prepare()
-    if field.sub_fields:
-        for sub_f in field.sub_fields:
-            update_field_forward_refs(sub_f, globalns=globalns, localns=localns)
-
-
 def almost_equal_floats(value_1: float, value_2: float, *, delta: float = 1e-8) -> bool:
     """
     Return True if two floats are almost equal
diff --git a/pydantic/validators.py b/pydantic/validators.py
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -25,17 +25,8 @@
 
 from . import errors
 from .datetime_parse import parse_date, parse_datetime, parse_duration, parse_time
-from .utils import (
-    AnyCallable,
-    AnyType,
-    ForwardRef,
-    almost_equal_floats,
-    change_exception,
-    display_as_type,
-    is_callable_type,
-    is_literal_type,
-    sequence_like,
-)
+from .typing import AnyCallable, AnyType, ForwardRef, display_as_type, is_callable_type, is_literal_type
+from .utils import almost_equal_floats, change_exception, sequence_like
 
 if TYPE_CHECKING:  # pragma: no cover
     from .fields import Field


Test patch:
diff --git a/tests/test_forward_ref.py b/tests/test_forward_ref.py
--- a/tests/test_forward_ref.py
+++ b/tests/test_forward_ref.py
@@ -43,7 +43,7 @@ def test_basic_forward_ref(create_module):
         """
 from typing import Optional
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 class Foo(BaseModel):
     a: int
@@ -63,7 +63,7 @@ def test_self_forward_ref_module(create_module):
     module = create_module(
         """
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 Foo = ForwardRef('Foo')
 
@@ -84,7 +84,7 @@ def test_self_forward_ref_collection(create_module):
         """
 from typing import List, Dict
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 Foo = ForwardRef('Foo')
 
@@ -117,7 +117,7 @@ def test_self_forward_ref_local(create_module):
     module = create_module(
         """
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 def main():
     Foo = ForwardRef('Foo')
@@ -139,7 +139,7 @@ def test_missing_update_forward_refs(create_module):
     module = create_module(
         """
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 Foo = ForwardRef('Foo')
 
@@ -190,7 +190,7 @@ def test_forward_ref_sub_types(create_module):
         """
 from typing import Union
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 class Leaf(BaseModel):
     a: str
@@ -222,7 +222,7 @@ def test_forward_ref_nested_sub_types(create_module):
         """
 from typing import Tuple, Union
 from pydantic import BaseModel
-from pydantic.utils import ForwardRef
+from pydantic.typing import ForwardRef
 
 class Leaf(BaseModel):
     a: str
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -65,7 +65,7 @@
     constr,
     urlstr,
 )
-from pydantic.utils import Literal
+from pydantic.typing import Literal
 
 try:
     import email_validator
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -4,17 +4,8 @@
 
 import pytest
 
-from pydantic.utils import (
-    ValueItems,
-    display_as_type,
-    import_string,
-    is_new_type,
-    lenient_issubclass,
-    make_dsn,
-    new_type_supertype,
-    truncate,
-    validate_email,
-)
+from pydantic.typing import display_as_type, is_new_type, lenient_issubclass, new_type_supertype
+from pydantic.utils import ValueItems, import_string, make_dsn, truncate, validate_email
 
 try:
     import email_validator