mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
Issue #28079: Update typing and test typing from python/typing repo.
Ivan Levkivskyi (3.6 version)
This commit is contained in:
parent
5fe668c672
commit
7ac1f7d269
2 changed files with 291 additions and 146 deletions
294
Lib/typing.py
294
Lib/typing.py
|
@ -6,11 +6,12 @@ import functools
|
|||
import re as stdlib_re # Avoid confusion with the re we export.
|
||||
import sys
|
||||
import types
|
||||
|
||||
try:
|
||||
import collections.abc as collections_abc
|
||||
except ImportError:
|
||||
import collections as collections_abc # Fallback for PY3.2.
|
||||
if sys.version_info[:2] >= (3, 3):
|
||||
from collections import ChainMap
|
||||
|
||||
|
||||
# Please keep __all__ alphabetized within each category.
|
||||
|
@ -1204,53 +1205,135 @@ def _get_defaults(func):
|
|||
return res
|
||||
|
||||
|
||||
def get_type_hints(obj, globalns=None, localns=None):
|
||||
"""Return type hints for an object.
|
||||
if sys.version_info[:2] >= (3, 3):
|
||||
def get_type_hints(obj, globalns=None, localns=None):
|
||||
"""Return type hints for an object.
|
||||
|
||||
This is often the same as obj.__annotations__, but it handles
|
||||
forward references encoded as string literals, and if necessary
|
||||
adds Optional[t] if a default value equal to None is set.
|
||||
This is often the same as obj.__annotations__, but it handles
|
||||
forward references encoded as string literals, and if necessary
|
||||
adds Optional[t] if a default value equal to None is set.
|
||||
|
||||
The argument may be a module, class, method, or function. The annotations
|
||||
are returned as a dictionary, or in the case of a class, a ChainMap of
|
||||
dictionaries.
|
||||
The argument may be a module, class, method, or function. The annotations
|
||||
are returned as a dictionary, or in the case of a class, a ChainMap of
|
||||
dictionaries.
|
||||
|
||||
TypeError is raised if the argument is not of a type that can contain
|
||||
annotations, and an empty dictionary is returned if no annotations are
|
||||
present.
|
||||
TypeError is raised if the argument is not of a type that can contain
|
||||
annotations, and an empty dictionary is returned if no annotations are
|
||||
present.
|
||||
|
||||
BEWARE -- the behavior of globalns and localns is counterintuitive
|
||||
(unless you are familiar with how eval() and exec() work). The
|
||||
search order is locals first, then globals.
|
||||
BEWARE -- the behavior of globalns and localns is counterintuitive
|
||||
(unless you are familiar with how eval() and exec() work). The
|
||||
search order is locals first, then globals.
|
||||
|
||||
- If no dict arguments are passed, an attempt is made to use the
|
||||
globals from obj, and these are also used as the locals. If the
|
||||
object does not appear to have globals, an exception is raised.
|
||||
- If no dict arguments are passed, an attempt is made to use the
|
||||
globals from obj, and these are also used as the locals. If the
|
||||
object does not appear to have globals, an exception is raised.
|
||||
|
||||
- If one dict argument is passed, it is used for both globals and
|
||||
locals.
|
||||
- If one dict argument is passed, it is used for both globals and
|
||||
locals.
|
||||
|
||||
- If two dict arguments are passed, they specify globals and
|
||||
locals, respectively.
|
||||
"""
|
||||
- If two dict arguments are passed, they specify globals and
|
||||
locals, respectively.
|
||||
"""
|
||||
|
||||
if getattr(obj, '__no_type_check__', None):
|
||||
return {}
|
||||
if globalns is None:
|
||||
globalns = getattr(obj, '__globals__', {})
|
||||
if localns is None:
|
||||
if getattr(obj, '__no_type_check__', None):
|
||||
return {}
|
||||
if globalns is None:
|
||||
globalns = getattr(obj, '__globals__', {})
|
||||
if localns is None:
|
||||
localns = globalns
|
||||
elif localns is None:
|
||||
localns = globalns
|
||||
elif localns is None:
|
||||
localns = globalns
|
||||
|
||||
if (isinstance(obj, types.FunctionType) or
|
||||
isinstance(obj, types.BuiltinFunctionType) or
|
||||
isinstance(obj, types.MethodType)):
|
||||
if (isinstance(obj, types.FunctionType) or
|
||||
isinstance(obj, types.BuiltinFunctionType) or
|
||||
isinstance(obj, types.MethodType)):
|
||||
defaults = _get_defaults(obj)
|
||||
hints = obj.__annotations__
|
||||
for name, value in hints.items():
|
||||
if value is None:
|
||||
value = type(None)
|
||||
if isinstance(value, str):
|
||||
value = _ForwardRef(value)
|
||||
value = _eval_type(value, globalns, localns)
|
||||
if name in defaults and defaults[name] is None:
|
||||
value = Optional[value]
|
||||
hints[name] = value
|
||||
return hints
|
||||
|
||||
if isinstance(obj, types.ModuleType):
|
||||
try:
|
||||
hints = obj.__annotations__
|
||||
except AttributeError:
|
||||
return {}
|
||||
# we keep only those annotations that can be accessed on module
|
||||
members = obj.__dict__
|
||||
hints = {name: value for name, value in hints.items()
|
||||
if name in members}
|
||||
for name, value in hints.items():
|
||||
if value is None:
|
||||
value = type(None)
|
||||
if isinstance(value, str):
|
||||
value = _ForwardRef(value)
|
||||
value = _eval_type(value, globalns, localns)
|
||||
hints[name] = value
|
||||
return hints
|
||||
|
||||
if isinstance(object, type):
|
||||
cmap = None
|
||||
for base in reversed(obj.__mro__):
|
||||
new_map = collections.ChainMap if cmap is None else cmap.new_child
|
||||
try:
|
||||
hints = base.__dict__['__annotations__']
|
||||
except KeyError:
|
||||
cmap = new_map()
|
||||
else:
|
||||
for name, value in hints.items():
|
||||
if value is None:
|
||||
value = type(None)
|
||||
if isinstance(value, str):
|
||||
value = _ForwardRef(value)
|
||||
value = _eval_type(value, globalns, localns)
|
||||
hints[name] = value
|
||||
cmap = new_map(hints)
|
||||
return cmap
|
||||
|
||||
raise TypeError('{!r} is not a module, class, method, '
|
||||
'or function.'.format(obj))
|
||||
|
||||
else:
|
||||
def get_type_hints(obj, globalns=None, localns=None):
|
||||
"""Return type hints for a function or method object.
|
||||
|
||||
This is often the same as obj.__annotations__, but it handles
|
||||
forward references encoded as string literals, and if necessary
|
||||
adds Optional[t] if a default value equal to None is set.
|
||||
|
||||
BEWARE -- the behavior of globalns and localns is counterintuitive
|
||||
(unless you are familiar with how eval() and exec() work). The
|
||||
search order is locals first, then globals.
|
||||
|
||||
- If no dict arguments are passed, an attempt is made to use the
|
||||
globals from obj, and these are also used as the locals. If the
|
||||
object does not appear to have globals, an exception is raised.
|
||||
|
||||
- If one dict argument is passed, it is used for both globals and
|
||||
locals.
|
||||
|
||||
- If two dict arguments are passed, they specify globals and
|
||||
locals, respectively.
|
||||
"""
|
||||
if getattr(obj, '__no_type_check__', None):
|
||||
return {}
|
||||
if globalns is None:
|
||||
globalns = getattr(obj, '__globals__', {})
|
||||
if localns is None:
|
||||
localns = globalns
|
||||
elif localns is None:
|
||||
localns = globalns
|
||||
defaults = _get_defaults(obj)
|
||||
hints = obj.__annotations__
|
||||
hints = dict(obj.__annotations__)
|
||||
for name, value in hints.items():
|
||||
if value is None:
|
||||
value = type(None)
|
||||
if isinstance(value, str):
|
||||
value = _ForwardRef(value)
|
||||
value = _eval_type(value, globalns, localns)
|
||||
|
@ -1259,62 +1342,30 @@ def get_type_hints(obj, globalns=None, localns=None):
|
|||
hints[name] = value
|
||||
return hints
|
||||
|
||||
if isinstance(obj, types.ModuleType):
|
||||
try:
|
||||
hints = obj.__annotations__
|
||||
except AttributeError:
|
||||
return {}
|
||||
# we keep only those annotations that can be accessed on module
|
||||
members = obj.__dict__
|
||||
hints = {name: value for name, value in hints.items()
|
||||
if name in members}
|
||||
for name, value in hints.items():
|
||||
if value is None:
|
||||
value = type(None)
|
||||
if isinstance(value, str):
|
||||
value = _ForwardRef(value)
|
||||
value = _eval_type(value, globalns, localns)
|
||||
hints[name] = value
|
||||
return hints
|
||||
|
||||
if isinstance(object, type):
|
||||
cmap = None
|
||||
for base in reversed(obj.__mro__):
|
||||
new_map = collections.ChainMap if cmap is None else cmap.new_child
|
||||
try:
|
||||
hints = base.__dict__['__annotations__']
|
||||
except KeyError:
|
||||
cmap = new_map()
|
||||
else:
|
||||
for name, value in hints.items():
|
||||
if value is None:
|
||||
value = type(None)
|
||||
if isinstance(value, str):
|
||||
value = _ForwardRef(value)
|
||||
value = _eval_type(value, globalns, localns)
|
||||
hints[name] = value
|
||||
cmap = new_map(hints)
|
||||
return cmap
|
||||
|
||||
raise TypeError('{!r} is not a module, class, method, '
|
||||
'or function.'.format(obj))
|
||||
|
||||
|
||||
def no_type_check(arg):
|
||||
"""Decorator to indicate that annotations are not type hints.
|
||||
|
||||
The argument must be a class or function; if it is a class, it
|
||||
applies recursively to all methods defined in that class (but not
|
||||
to methods defined in its superclasses or subclasses).
|
||||
applies recursively to all methods and classes defined in that class
|
||||
(but not to methods defined in its superclasses or subclasses).
|
||||
|
||||
This mutates the function(s) in place.
|
||||
This mutates the function(s) or class(es) in place.
|
||||
"""
|
||||
if isinstance(arg, type):
|
||||
for obj in arg.__dict__.values():
|
||||
arg_attrs = arg.__dict__.copy()
|
||||
for attr, val in arg.__dict__.items():
|
||||
if val in arg.__bases__:
|
||||
arg_attrs.pop(attr)
|
||||
for obj in arg_attrs.values():
|
||||
if isinstance(obj, types.FunctionType):
|
||||
obj.__no_type_check__ = True
|
||||
else:
|
||||
if isinstance(obj, type):
|
||||
no_type_check(obj)
|
||||
try:
|
||||
arg.__no_type_check__ = True
|
||||
except TypeError: # built-in classes
|
||||
pass
|
||||
return arg
|
||||
|
||||
|
||||
|
@ -1725,7 +1776,7 @@ CT_co = TypeVar('CT_co', covariant=True, bound=type)
|
|||
|
||||
|
||||
# This is not a real generic class. Don't use outside annotations.
|
||||
class Type(type, Generic[CT_co], extra=type):
|
||||
class Type(Generic[CT_co], extra=type):
|
||||
"""A special construct usable to annotate class objects.
|
||||
|
||||
For example, suppose we have the following classes::
|
||||
|
@ -1750,31 +1801,66 @@ class Type(type, Generic[CT_co], extra=type):
|
|||
"""
|
||||
|
||||
|
||||
def NamedTuple(typename, fields):
|
||||
"""Typed version of namedtuple.
|
||||
|
||||
Usage::
|
||||
|
||||
Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)])
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||
|
||||
The resulting class has one extra attribute: _field_types,
|
||||
giving a dict mapping field names to types. (The field names
|
||||
are in the _fields attribute, which is part of the namedtuple
|
||||
API.)
|
||||
"""
|
||||
fields = [(n, t) for n, t in fields]
|
||||
cls = collections.namedtuple(typename, [n for n, t in fields])
|
||||
cls._field_types = dict(fields)
|
||||
# Set the module to the caller's module (otherwise it'd be 'typing').
|
||||
def _make_nmtuple(name, types):
|
||||
nm_tpl = collections.namedtuple(name, [n for n, t in types])
|
||||
nm_tpl._field_types = dict(types)
|
||||
try:
|
||||
cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__')
|
||||
nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
return cls
|
||||
return nm_tpl
|
||||
|
||||
|
||||
if sys.version_info[:2] >= (3, 6):
|
||||
class NamedTupleMeta(type):
|
||||
|
||||
def __new__(cls, typename, bases, ns, *, _root=False):
|
||||
if _root:
|
||||
return super().__new__(cls, typename, bases, ns)
|
||||
types = ns.get('__annotations__', {})
|
||||
return _make_nmtuple(typename, types.items())
|
||||
|
||||
class NamedTuple(metaclass=NamedTupleMeta, _root=True):
|
||||
"""Typed version of namedtuple.
|
||||
|
||||
Usage::
|
||||
|
||||
class Employee(NamedTuple):
|
||||
name: str
|
||||
id: int
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||
|
||||
The resulting class has one extra attribute: _field_types,
|
||||
giving a dict mapping field names to types. (The field names
|
||||
are in the _fields attribute, which is part of the namedtuple
|
||||
API.) Backward-compatible usage::
|
||||
|
||||
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
||||
"""
|
||||
|
||||
def __new__(self, typename, fields):
|
||||
return _make_nmtuple(typename, fields)
|
||||
else:
|
||||
def NamedTuple(typename, fields):
|
||||
"""Typed version of namedtuple.
|
||||
|
||||
Usage::
|
||||
|
||||
Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)])
|
||||
|
||||
This is equivalent to::
|
||||
|
||||
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||
|
||||
The resulting class has one extra attribute: _field_types,
|
||||
giving a dict mapping field names to types. (The field names
|
||||
are in the _fields attribute, which is part of the namedtuple
|
||||
API.)
|
||||
"""
|
||||
return _make_nmtuple(typename, fields)
|
||||
|
||||
|
||||
def NewType(name, tp):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue