Source code for dataclass_wizard.type_def

__all__ = [
    'PyForwardRef',
    'PyLiteral',
    'PyProtocol',
    'PyDeque',
    'PyTypedDict',
    'PyTypedDicts',
    'FrozenKeys',
    'DefFactory',
    'NoneType',
    'ExplicitNullType',
    'ExplicitNull',
    'JSONList',
    'JSONObject',
    'ListOfJSONObject',
    'JSONValue',
    'Encoder',
    'FileEncoder',
    'Decoder',
    'FileDecoder',
    'NUMBERS',
    'T',
    'E',
    'U',
    'M',
    'NT',
    'DT',
    'DD',
    'N',
    'S',
    'LT',
    'LSQ',
    'FREF',
]

from collections import deque
from datetime import date, time, datetime
from enum import Enum
from typing import (
    Any, Type, TypeVar, Sequence, Mapping, List, Dict, DefaultDict, FrozenSet,
    Union, NamedTuple, Callable, AnyStr, TextIO, BinaryIO
)
from uuid import UUID

from .constants import PY36, PY38_OR_ABOVE
from .decorators import discard_kwargs


# Type check for numeric types - needed because `bool` is technically
# a Number.
NUMBERS = int, float

# Generic type
T = TypeVar('T')

# Enum subclass type
E = TypeVar('E', bound=Enum)

# UUID subclass type
U = TypeVar('U', bound=UUID)

# Mapping type
M = TypeVar('M', bound=Mapping)

# NamedTuple type
NT = TypeVar('NT', bound=NamedTuple)

# Date, time, or datetime type
DT = TypeVar('DT', date, time, datetime)

# DefaultDict type
DD = TypeVar('DD', bound=DefaultDict)

# Numeric type
N = TypeVar('N', int, float, complex)

# Sequence type
S = TypeVar('S', bound=Sequence)

# List or Tuple type
LT = TypeVar('LT', list, tuple)

# List, Set, or Deque (Double ended queue) type
LSQ = TypeVar('LSQ', list, set, frozenset, deque)

# A fixed set of key names
FrozenKeys = FrozenSet[str]

# Default factory type, assuming a no-args constructor
DefFactory = Callable[[], T]

# The class of the `None` singleton, cached for re-usability
NoneType = type(None)

# For Python 3.8+, we need to use both `TypedDict` implementations (from both
# the `typing` and `typing_extensions` modules). Because it's not clear which
# version users might choose to use. And they might choose to use either, due
# to the issues mentioned below (comment taken from `typing_extensions`):
#
#   The standard library TypedDict in Python 3.8 does not store runtime information
#   about which (if any) keys are optional.  See https://bugs.python.org/issue38834
#   The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
#   keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
PyTypedDicts: List[Type['TypedDict']] = []

# Valid collection types in JSON.
JSONList = List[Any]
JSONObject = Dict[str, Any]
ListOfJSONObject = List[JSONObject]

# Valid value types in JSON.
JSONValue = Union[None, str, bool, int, float, JSONList, JSONObject]


if PY38_OR_ABOVE:  # pragma: no cover
    from typing import ForwardRef as PyForwardRef
    from typing import Literal as PyLiteral
    from typing import Protocol as PyProtocol
    from typing import TypedDict as PyTypedDict
    from typing import Deque as PyDeque

    PyTypedDicts.append(PyTypedDict)
    # Python 3.8+ users might import from either `typing` or
    # `typing_extensions`, so check for both types.
    try:
        # noinspection PyUnresolvedReferences
        from typing_extensions import TypedDict as PyTypedDict
        PyTypedDicts.append(PyTypedDict)
    except ImportError:
        pass
else:  # pragma: no cover
    from typing_extensions import Literal as PyLiteral
    from typing_extensions import Protocol as PyProtocol
    from typing_extensions import TypedDict as PyTypedDict
    # Seems like `Deque` was only introduced to `typing` in 3.6.1, so Python
    # 3.6.0 won't have it; to be safe, we'll instead import from the
    # `typing_extensions` module here.
    from typing_extensions import Deque as PyDeque

    PyTypedDicts.append(PyTypedDict)

    if PY36:
        import typing
        from typing import _ForwardRef as PyForwardRef
        from functools import wraps

        # Need to wrap the constructor to discard arguments like `is_argument`
        PyForwardRef.__init__ = discard_kwargs(PyForwardRef.__init__)

        # This is needed to avoid an`AttributeError` when using PyForwardRef
        # as an argument to `TypeVar`, as we do below.
        #
        # See https://stackoverflow.com/a/69436981/10237506.
        _old_type_check = typing._type_check

        @wraps(_old_type_check)
        def _new_type_check(arg, message):
            if arg is PyForwardRef:
                return arg
            return _old_type_check(arg, message)

        typing._type_check = _new_type_check
        # ensure the global namespace is the same for users
        # regardless of the version of Python they're using
        del _new_type_check, typing, wraps

    else:
        from typing import ForwardRef as PyForwardRef


# Forward references can be either strings or explicit `ForwardRef` objects.
# noinspection SpellCheckingInspection
FREF = TypeVar('FREF', str, PyForwardRef)


# Create our own "nullish" type for explicit type assertions
[docs] class ExplicitNullType: __slots__ = () def __bool__(self): return False def __repr__(self): return self.__class__.__qualname__
ExplicitNull = ExplicitNullType()
[docs] class Encoder(PyProtocol): """ Represents an encoder for Python object -> JSON, e.g. analogous to `json.dumps` """ def __call__(self, obj: Union[JSONObject, JSONList], **kwargs) -> AnyStr: ...
[docs] class FileEncoder(PyProtocol): """ Represents an encoder for Python object -> JSON file, e.g. analogous to `json.dump` """ def __call__(self, obj: Union[JSONObject, JSONList], file: Union[TextIO, BinaryIO], **kwargs) -> AnyStr: ...
[docs] class Decoder(PyProtocol): """ Represents a decoder for JSON -> Python object, e.g. analogous to `json.loads` """ def __call__(self, s: AnyStr, **kwargs) -> Union[JSONObject, ListOfJSONObject]: ...
[docs] class FileDecoder(PyProtocol): """ Represents a decoder for JSON file -> Python object, e.g. analogous to `json.load` """ def __call__(self, file: Union[TextIO, BinaryIO], **kwargs) -> Union[JSONObject, ListOfJSONObject]: ...