From ddb6065ee2ec71dbd02644399d684707d576062d Mon Sep 17 00:00:00 2001 From: Dan Ryan Date: Sun, 26 Aug 2018 19:59:36 -0400 Subject: [PATCH] Final fixes to vendoring - Add news - upate test runner - Unvendor typing - Fix environment error - Fix click completion patch - update vendoring script - Install flit with vendoring script for wheels - disable build isolation also Signed-off-by: Dan Ryan --- news/2639.bugfix | 2 + news/2639.vendor | 2 +- pipenv/utils.py | 3 +- pipenv/vendor/backports/__init__.py | 3 +- pipenv/vendor/backports/typing/__init__.py | 1 - pipenv/vendor/requirementslib/models/utils.py | 2 +- pipenv/vendor/tomlkit/api.py | 5 - pipenv/vendor/tomlkit/container.py | 17 - pipenv/vendor/tomlkit/exceptions.py | 6 - pipenv/vendor/tomlkit/items.py | 15 - pipenv/vendor/tomlkit/parser.py | 10 - pipenv/vendor/tomlkit/toml_file.py | 7 - .../vendor/{backports/typing => }/typing.py | 992 +++++++++++------- pytest.ini | 5 +- run-tests.sh | 20 +- setup.py | 2 +- tasks/vendoring/__init__.py | 30 +- .../vendor/tomlkit-typing-imports.patch | 96 +- 18 files changed, 660 insertions(+), 558 deletions(-) create mode 100644 news/2639.bugfix delete mode 100644 pipenv/vendor/backports/typing/__init__.py rename pipenv/vendor/{backports/typing => }/typing.py (74%) diff --git a/news/2639.bugfix b/news/2639.bugfix new file mode 100644 index 00000000..c1036f7b --- /dev/null +++ b/news/2639.bugfix @@ -0,0 +1,2 @@ +Fixed a bug which caused attempted hashing of ``ssh://`` style URIs which could cause failures during installation of private ssh repositories. +- Corrected path conversion issues which caused certain editable VCS paths to be converted to ``ssh://`` URIs improperly. diff --git a/news/2639.vendor b/news/2639.vendor index 9b454d9b..b159c33f 100644 --- a/news/2639.vendor +++ b/news/2639.vendor @@ -8,7 +8,7 @@ - ``pytoml`` to ``0.1.18`` - ``certifi`` to ``2018.8.24`` - ``ptyprocess`` to ``0.6.0`` - - ``requirementslib`` to ``1.1.2`` + - ``requirementslib`` to ``1.1.5`` - ``pythonfinder`` to ``1.0.2`` - ``pipdeptree`` to ``0.13.0`` - ``python-dotenv`` to ``0.9.1`` diff --git a/pipenv/utils.py b/pipenv/utils.py index cdb375dd..12750fda 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -362,7 +362,8 @@ def venv_resolve_deps( "--system" if allow_global else "", ) with temp_environ(): - os.environ["PIPENV_PACKAGES"] = "\n".join(deps) + os.environ = {fs_str(k): fs_str(val) for k, val in os.environ.items()} + os.environ["PIPENV_PACKAGES"] = str("\n".join(deps)) if pypi_mirror: os.environ["PIPENV_PYPI_MIRROR"] = str(pypi_mirror) os.environ["PIPENV_VERBOSITY"] = str(environments.PIPENV_VERBOSITY) diff --git a/pipenv/vendor/backports/__init__.py b/pipenv/vendor/backports/__init__.py index 791e7ec6..3cd30963 100644 --- a/pipenv/vendor/backports/__init__.py +++ b/pipenv/vendor/backports/__init__.py @@ -3,6 +3,5 @@ from pkgutil import extend_path __path__ = extend_path(__path__, __name__) from . import weakref -from . import shutil_get_terminal_size from . import enum - +from . import shutil_get_terminal_size diff --git a/pipenv/vendor/backports/typing/__init__.py b/pipenv/vendor/backports/typing/__init__.py deleted file mode 100644 index ddef31b4..00000000 --- a/pipenv/vendor/backports/typing/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import typing diff --git a/pipenv/vendor/requirementslib/models/utils.py b/pipenv/vendor/requirementslib/models/utils.py index 6999204c..6fd55b6f 100644 --- a/pipenv/vendor/requirementslib/models/utils.py +++ b/pipenv/vendor/requirementslib/models/utils.py @@ -117,7 +117,7 @@ def strip_ssh_from_git_uri(uri): def add_ssh_scheme_to_git_uri(uri): - """Cleans VCS uris from pip format""" + """Cleans VCS uris from pipenv.patched.notpip format""" if isinstance(uri, six.string_types): # Add scheme for parsing purposes, this is also what pip does if uri.startswith("git+") and "://" not in uri: diff --git a/pipenv/vendor/tomlkit/api.py b/pipenv/vendor/tomlkit/api.py index d36375e8..0ac26752 100644 --- a/pipenv/vendor/tomlkit/api.py +++ b/pipenv/vendor/tomlkit/api.py @@ -1,10 +1,5 @@ import datetime as _datetime -try: - from typing import Tuple -except ImportError: - from pipenv.vendor.backports.typing import Tuple - from ._utils import parse_rfc3339 from .container import Container from .items import AoT diff --git a/pipenv/vendor/tomlkit/container.py b/pipenv/vendor/tomlkit/container.py index c7595a52..a7876ff1 100644 --- a/pipenv/vendor/tomlkit/container.py +++ b/pipenv/vendor/tomlkit/container.py @@ -1,22 +1,5 @@ from __future__ import unicode_literals -try: - from typing import Any - from typing import Dict - from typing import Generator - from typing import List - from typing import Optional - from typing import Tuple - from typing import Union -except ImportError: - from pipenv.vendor.backports.typing import Any - from pipenv.vendor.backports.typing import Dict - from pipenv.vendor.backports.typing import Generator - from pipenv.vendor.backports.typing import List - from pipenv.vendor.backports.typing import Optional - from pipenv.vendor.backports.typing import Tuple - from pipenv.vendor.backports.typing import Union - from ._compat import decode from .exceptions import KeyAlreadyPresent from .exceptions import NonExistentKey diff --git a/pipenv/vendor/tomlkit/exceptions.py b/pipenv/vendor/tomlkit/exceptions.py index dae29f6f..d889a924 100644 --- a/pipenv/vendor/tomlkit/exceptions.py +++ b/pipenv/vendor/tomlkit/exceptions.py @@ -1,9 +1,3 @@ -try: - from typing import Optional -except ImportError: - from pipenv.vendor.backports.typing import Optional - - class TOMLKitError(Exception): pass diff --git a/pipenv/vendor/tomlkit/items.py b/pipenv/vendor/tomlkit/items.py index 83b17612..8807f4b3 100644 --- a/pipenv/vendor/tomlkit/items.py +++ b/pipenv/vendor/tomlkit/items.py @@ -11,21 +11,6 @@ if sys.version_info >= (3, 4): from enum import Enum else: from pipenv.vendor.backports.enum import Enum -try: - from typing import Any - from typing import Dict - from typing import Generator - from typing import List - from typing import Optional - from typing import Union -except ImportError: - from pipenv.vendor.backports.typing import Any - from pipenv.vendor.backports.typing import Dict - from pipenv.vendor.backports.typing import Generator - from pipenv.vendor.backports.typing import List - from pipenv.vendor.backports.typing import Optional - from pipenv.vendor.backports.typing import Union - from ._compat import PY2 from ._compat import decode diff --git a/pipenv/vendor/tomlkit/parser.py b/pipenv/vendor/tomlkit/parser.py index 28c0a6e0..3d4984d1 100644 --- a/pipenv/vendor/tomlkit/parser.py +++ b/pipenv/vendor/tomlkit/parser.py @@ -7,16 +7,6 @@ import re import string from copy import copy -try: - from typing import Iterator - from typing import Optional - from typing import Tuple - from typing import Union -except ImportError: - from pipenv.vendor.backports.typing import Iterator - from pipenv.vendor.backports.typing import Optional - from pipenv.vendor.backports.typing import Tuple - from pipenv.vendor.backports.typing import Union from ._compat import PY2 from ._compat import chr diff --git a/pipenv/vendor/tomlkit/toml_file.py b/pipenv/vendor/tomlkit/toml_file.py index 3e4cc721..631e9959 100644 --- a/pipenv/vendor/tomlkit/toml_file.py +++ b/pipenv/vendor/tomlkit/toml_file.py @@ -1,12 +1,5 @@ import io -try: - from typing import Any - from typing import Dict -except ImportError: - from pipenv.vendor.backports.typing import Any - from pipenv.vendor.backports.typing import Dict - from .api import loads from .toml_document import TOMLDocument diff --git a/pipenv/vendor/backports/typing/typing.py b/pipenv/vendor/typing.py similarity index 74% rename from pipenv/vendor/backports/typing/typing.py rename to pipenv/vendor/typing.py index 7d8c7a34..b5564cc2 100644 --- a/pipenv/vendor/backports/typing/typing.py +++ b/pipenv/vendor/typing.py @@ -1,17 +1,23 @@ -from __future__ import absolute_import, unicode_literals - import abc from abc import abstractmethod, abstractproperty import collections +import contextlib import functools import re as stdlib_re # Avoid confusion with the re we export. import sys import types -import copy try: import collections.abc as collections_abc except ImportError: import collections as collections_abc # Fallback for PY3.2. +if sys.version_info[:2] >= (3, 6): + import _collections_abc # Needed for private function _check_methods # noqa +try: + from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType +except ImportError: + WrapperDescriptorType = type(object.__init__) + MethodWrapperType = type(object().__str__) + MethodDescriptorType = type(str.join) # Please keep __all__ alphabetized within each category. @@ -47,13 +53,24 @@ __all__ = [ 'Sequence', 'Sized', 'ValuesView', + # The following are added depending on presence + # of their non-generic counterparts in stdlib: + # Awaitable, + # AsyncIterator, + # AsyncIterable, + # Coroutine, + # Collection, + # AsyncGenerator, + # AsyncContextManager # Structural checks, a.k.a. protocols. 'Reversible', 'SupportsAbs', + 'SupportsBytes', 'SupportsComplex', 'SupportsFloat', 'SupportsInt', + 'SupportsRound', # Concrete collection types. 'Counter', @@ -73,7 +90,6 @@ __all__ = [ 'NewType', 'no_type_check', 'no_type_check_decorator', - 'NoReturn', 'overload', 'Text', 'TYPE_CHECKING', @@ -103,21 +119,22 @@ class TypingMeta(type): """Metaclass for most types defined in typing module (not a part of public API). + This overrides __new__() to require an extra keyword parameter + '_root', which serves as a guard against naive subclassing of the + typing classes. Any legitimate class defined using a metaclass + derived from TypingMeta must pass _root=True. + This also defines a dummy constructor (all the work for most typing constructs is done in __new__) and a nicer repr(). """ _is_protocol = False - def __new__(cls, name, bases, namespace): - return super(TypingMeta, cls).__new__(cls, str(name), bases, namespace) - - @classmethod - def assert_no_subclassing(cls, bases): - for base in bases: - if isinstance(base, cls): - raise TypeError("Cannot subclass %s" % - (', '.join(map(_type_repr, bases)) or '()')) + def __new__(cls, name, bases, namespace, *, _root=False): + if not _root: + raise TypeError("Cannot subclass %s" % + (', '.join(map(_type_repr, bases)) or '()')) + return super().__new__(cls, name, bases, namespace) def __init__(self, *args, **kwds): pass @@ -140,9 +157,9 @@ class TypingMeta(type): return '%s.%s' % (self.__module__, qname) -class _TypingBase(object): +class _TypingBase(metaclass=TypingMeta, _root=True): """Internal indicator of special typing constructs.""" - __metaclass__ = TypingMeta + __slots__ = ('__weakref__',) def __init__(self, *args, **kwds): @@ -159,7 +176,7 @@ class _TypingBase(object): isinstance(args[1], tuple)): # Close enough. raise TypeError("Cannot subclass %r" % cls) - return super(_TypingBase, cls).__new__(cls) + return super().__new__(cls) # Things that are not classes also need these. def _eval_type(self, globalns, localns): @@ -177,7 +194,7 @@ class _TypingBase(object): raise TypeError("Cannot instantiate %r" % type(self)) -class _FinalTypingBase(_TypingBase): +class _FinalTypingBase(_TypingBase, _root=True): """Internal mix-in class to prevent instantiation. Prevents instantiation unless _root=True is given in class call. @@ -186,9 +203,9 @@ class _FinalTypingBase(_TypingBase): __slots__ = () - def __new__(cls, *args, **kwds): - self = super(_FinalTypingBase, cls).__new__(cls, *args, **kwds) - if '_root' in kwds and kwds['_root'] is True: + def __new__(cls, *args, _root=False, **kwds): + self = super().__new__(cls, *args, **kwds) + if _root is True: return self raise TypeError("Cannot instantiate %r" % cls) @@ -196,15 +213,15 @@ class _FinalTypingBase(_TypingBase): return _trim_name(type(self).__name__) -class _ForwardRef(_TypingBase): +class _ForwardRef(_TypingBase, _root=True): """Internal wrapper to hold a forward reference.""" __slots__ = ('__forward_arg__', '__forward_code__', '__forward_evaluated__', '__forward_value__') def __init__(self, arg): - super(_ForwardRef, self).__init__(arg) - if not isinstance(arg, basestring): + super().__init__(arg) + if not isinstance(arg, str): raise TypeError('Forward reference must be a string -- got %r' % (arg,)) try: code = compile(arg, '', 'eval') @@ -249,7 +266,7 @@ class _ForwardRef(_TypingBase): return '_ForwardRef(%r)' % (self.__forward_arg__,) -class _TypeAlias(_TypingBase): +class _TypeAlias(_TypingBase, _root=True): """Internal helper class for defining generic variants of concrete types. Note that this is not a type; let's call it a pseudo-type. It cannot @@ -271,7 +288,7 @@ class _TypeAlias(_TypingBase): type_checker: Function that takes an impl_type instance. and returns a value that should be a type_var instance. """ - assert isinstance(name, basestring), repr(name) + assert isinstance(name, str), repr(name) assert isinstance(impl_type, type), repr(impl_type) assert not isinstance(impl_type, TypingMeta), repr(impl_type) assert isinstance(type_var, (type, _TypingBase)), repr(type_var) @@ -348,7 +365,7 @@ def _type_check(arg, msg): """ if arg is None: return type(None) - if isinstance(arg, basestring): + if isinstance(arg, str): arg = _ForwardRef(arg) if ( isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or @@ -374,91 +391,17 @@ def _type_repr(obj): else, we fall back on repr(obj). """ if isinstance(obj, type) and not isinstance(obj, TypingMeta): - if obj.__module__ == '__builtin__': + if obj.__module__ == 'builtins': return _qualname(obj) return '%s.%s' % (obj.__module__, _qualname(obj)) - if obj is Ellipsis: + if obj is ...: return('...') if isinstance(obj, types.FunctionType): return obj.__name__ return repr(obj) -class ClassVarMeta(TypingMeta): - """Metaclass for _ClassVar""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(ClassVarMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _ClassVar(_FinalTypingBase): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats = {} # type: ClassVar[Dict[str, int]] # class variable - damage = 10 # type: int # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __metaclass__ = ClassVarMeta - __slots__ = ('__type__',) - - def __init__(self, tp=None, _root=False): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(_type_check(item, - '{} accepts only types.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - return type(self)(_eval_type(self.__type__, globalns, localns), - _root=True) - - def __repr__(self): - r = super(_ClassVar, self).__repr__() - if self.__type__ is not None: - r += '[{}]'.format(_type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - -ClassVar = _ClassVar(_root=True) - - -class AnyMeta(TypingMeta): - """Metaclass for Any.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(AnyMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _Any(_FinalTypingBase): +class _Any(_FinalTypingBase, _root=True): """Special type indicating an unconstrained type. - Any is compatible with every type. @@ -469,7 +412,7 @@ class _Any(_FinalTypingBase): static type checkers. At runtime, Any should not be used with instance or class checks. """ - __metaclass__ = AnyMeta + __slots__ = () def __instancecheck__(self, obj): @@ -482,16 +425,7 @@ class _Any(_FinalTypingBase): Any = _Any(_root=True) -class NoReturnMeta(TypingMeta): - """Metaclass for NoReturn.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(NoReturnMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _NoReturn(_FinalTypingBase): +class _NoReturn(_FinalTypingBase, _root=True): """Special type indicating functions that never return. Example:: @@ -503,7 +437,7 @@ class _NoReturn(_FinalTypingBase): This type is invalid in other positions, e.g., ``List[NoReturn]`` will fail in static type checkers. """ - __metaclass__ = NoReturnMeta + __slots__ = () def __instancecheck__(self, obj): @@ -516,13 +450,7 @@ class _NoReturn(_FinalTypingBase): NoReturn = _NoReturn(_root=True) -class TypeVarMeta(TypingMeta): - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - return super(TypeVarMeta, cls).__new__(cls, name, bases, namespace) - - -class TypeVar(_TypingBase): +class TypeVar(_TypingBase, _root=True): """Type variable. Usage:: @@ -564,15 +492,13 @@ class TypeVar(_TypingBase): A.__constraints__ == (str, bytes) """ - __metaclass__ = TypeVarMeta __slots__ = ('__name__', '__bound__', '__constraints__', '__covariant__', '__contravariant__') - def __init__(self, name, *constraints, **kwargs): - super(TypeVar, self).__init__(name, *constraints, **kwargs) - bound = kwargs.get('bound', None) - covariant = kwargs.get('covariant', False) - contravariant = kwargs.get('contravariant', False) + def __init__(self, name, *constraints, bound=None, + covariant=False, contravariant=False): + super().__init__(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant) self.__name__ = name if covariant and contravariant: raise ValueError("Bivariant types are not supported.") @@ -621,7 +547,7 @@ T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. # A useful type variable with constraints. This represents string types. # (This one *is* for export!) -AnyStr = TypeVar('AnyStr', bytes, unicode) +AnyStr = TypeVar('AnyStr', bytes, str) def _replace_arg(arg, tvars, args): @@ -740,38 +666,24 @@ _cleanups = [] def _tp_cache(func): - maxsize = 128 - cache = {} - _cleanups.append(cache.clear) + """Internal wrapper caching __getitem__ of generic types with a fallback to + original function for non-hashable arguments. + """ + + cached = functools.lru_cache()(func) + _cleanups.append(cached.cache_clear) @functools.wraps(func) - def inner(*args): - key = args + def inner(*args, **kwds): try: - return cache[key] + return cached(*args, **kwds) except TypeError: - # Assume it's an unhashable argument. - return func(*args) - except KeyError: - value = func(*args) - if len(cache) >= maxsize: - # If the cache grows too much, just start over. - cache.clear() - cache[key] = value - return value - + pass # All real errors (not unhashable args) are raised below. + return func(*args, **kwds) return inner -class UnionMeta(TypingMeta): - """Metaclass for Union.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - return super(UnionMeta, cls).__new__(cls, name, bases, namespace) - - -class _Union(_FinalTypingBase): +class _Union(_FinalTypingBase, _root=True): """Union type; Union[X, Y] means either X or Y. To define a union, use e.g. Union[int, str]. Details: @@ -815,11 +727,10 @@ class _Union(_FinalTypingBase): - You can use Optional[X] as a shorthand for Union[X, None]. """ - __metaclass__ = UnionMeta __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__') - def __new__(cls, parameters=None, origin=None, *args, **kwds): - self = super(_Union, cls).__new__(cls, parameters, origin, *args, **kwds) + def __new__(cls, parameters=None, origin=None, *args, _root=False): + self = super().__new__(cls, parameters, origin, *args, _root=_root) if origin is None: self.__parameters__ = None self.__args__ = None @@ -861,7 +772,7 @@ class _Union(_FinalTypingBase): def __repr__(self): if self.__origin__ is None: - return super(_Union, self).__repr__() + return super().__repr__() tree = self._subs_tree() if not isinstance(tree, tuple): return repr(tree) @@ -874,7 +785,7 @@ class _Union(_FinalTypingBase): arg_list.append(_type_repr(arg)) else: arg_list.append(arg[0]._tree_repr(arg)) - return super(_Union, self).__repr__() + '[%s]' % ', '.join(arg_list) + return super().__repr__() + '[%s]' % ', '.join(arg_list) @_tp_cache def __getitem__(self, parameters): @@ -921,21 +832,12 @@ class _Union(_FinalTypingBase): Union = _Union(_root=True) -class OptionalMeta(TypingMeta): - """Metaclass for Optional.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - return super(OptionalMeta, cls).__new__(cls, name, bases, namespace) - - -class _Optional(_FinalTypingBase): +class _Optional(_FinalTypingBase, _root=True): """Optional type. Optional[X] is equivalent to Union[X, None]. """ - __metaclass__ = OptionalMeta __slots__ = () @_tp_cache @@ -970,11 +872,11 @@ def _make_subclasshook(cls): # The logic mirrors that of ABCMeta.__subclasscheck__. # Registered classes need not be checked here because # cls and its extra share the same _abc_registry. - def __extrahook__(cls, subclass): + def __extrahook__(subclass): res = cls.__extra__.__subclasshook__(subclass) if res is not NotImplemented: return res - if cls.__extra__ in getattr(subclass, '__mro__', ()): + if cls.__extra__ in subclass.__mro__: return True for scls in cls.__extra__.__subclasses__(): if isinstance(scls, GenericMeta): @@ -984,11 +886,22 @@ def _make_subclasshook(cls): return NotImplemented else: # For non-ABC extras we'll just call issubclass(). - def __extrahook__(cls, subclass): + def __extrahook__(subclass): if cls.__extra__ and issubclass(subclass, cls.__extra__): return True return NotImplemented - return classmethod(__extrahook__) + return __extrahook__ + + +def _no_slots_copy(dct): + """Internal helper: copy class __dict__ and clean slots class variables. + (They will be re-created if necessary by normal class machinery.) + """ + dict_copy = dict(dct) + if '__slots__' in dict_copy: + for slot in dict_copy['__slots__']: + dict_copy.pop(slot, None) + return dict_copy class GenericMeta(TypingMeta, abc.ABCMeta): @@ -1053,8 +966,6 @@ class GenericMeta(TypingMeta, abc.ABCMeta): tvars = gvars initial_bases = bases - if extra is None: - extra = namespace.get('__extra__') if extra is not None and type(extra) is abc.ABCMeta and extra not in bases: bases = (extra,) + bases bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases) @@ -1062,15 +973,15 @@ class GenericMeta(TypingMeta, abc.ABCMeta): # remove bare Generic from bases if there are other generic bases if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): bases = tuple(b for b in bases if b is not Generic) - namespace.update({'__origin__': origin, '__extra__': extra}) - self = super(GenericMeta, cls).__new__(cls, name, bases, namespace) + namespace.update({'__origin__': origin, '__extra__': extra, + '_gorg': None if not origin else origin._gorg}) + self = super().__new__(cls, name, bases, namespace, _root=True) super(GenericMeta, self).__setattr__('_gorg', self if not origin else origin._gorg) - self.__parameters__ = tvars # Be prepared that GenericMeta will be subclassed by TupleMeta # and CallableMeta, those two allow ..., (), or [] in __args___. - self.__args__ = tuple(Ellipsis if a is _TypingEllipsis else + self.__args__ = tuple(... if a is _TypingEllipsis else () if a is _TypingEmpty else a for a in args) if args else None # Speed hack (https://github.com/python/typing/issues/196). @@ -1088,6 +999,12 @@ class GenericMeta(TypingMeta, abc.ABCMeta): getattr(self.__subclasshook__, '__name__', '') == '__extrahook__' ): self.__subclasshook__ = _make_subclasshook(self) + if isinstance(extra, abc.ABCMeta): + self._abc_registry = extra._abc_registry + self._abc_cache = extra._abc_cache + elif origin is not None: + self._abc_registry = origin._abc_registry + self._abc_cache = origin._abc_cache if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2. self.__qualname__ = origin.__qualname__ @@ -1095,15 +1012,6 @@ class GenericMeta(TypingMeta, abc.ABCMeta): super(GenericMeta, self).__hash__()) return self - def __init__(self, *args, **kwargs): - super(GenericMeta, self).__init__(*args, **kwargs) - if isinstance(self.__extra__, abc.ABCMeta): - self._abc_registry = self.__extra__._abc_registry - self._abc_cache = self.__extra__._abc_cache - elif self.__origin__ is not None: - self._abc_registry = self.__origin__._abc_registry - self._abc_cache = self.__origin__._abc_cache - # _abc_negative_cache and _abc_negative_cache_version # realised as descriptors, since GenClass[t1, t2, ...] always # share subclass info with GenClass. @@ -1149,7 +1057,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): return self return self.__class__(self.__name__, self.__bases__, - dict(self.__dict__), + _no_slots_copy(self.__dict__), tvars=_type_vars(ev_args) if ev_args else None, args=ev_args, origin=ev_origin, @@ -1158,7 +1066,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def __repr__(self): if self.__origin__ is None: - return super(GenericMeta, self).__repr__() + return super().__repr__() return self._tree_repr(self._subs_tree()) def _tree_repr(self, tree): @@ -1170,7 +1078,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): arg_list.append(_type_repr(arg)) else: arg_list.append(arg[0]._tree_repr(arg)) - return super(GenericMeta, self).__repr__() + '[%s]' % ', '.join(arg_list) + return super().__repr__() + '[%s]' % ', '.join(arg_list) def _subs_tree(self, tvars=None, args=None): if self.__origin__ is None: @@ -1227,7 +1135,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): prepend = (self,) if self.__origin__ is None else () return self.__class__(self.__name__, prepend + self.__bases__, - dict(self.__dict__), + _no_slots_copy(self.__dict__), tvars=tvars, args=args, origin=self, @@ -1236,18 +1144,14 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def __subclasscheck__(self, cls): if self.__origin__ is not None: - # This should only be modules within the standard - # library. singledispatch is the only exception, because - # it's a Python 2 backport of functools.singledispatch. - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', - 'singledispatch']: + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: raise TypeError("Parameterized generics cannot be used with class " "or instance checks") return False if self is Generic: raise TypeError("Class %r cannot be used with class " "or instance checks" % self) - return super(GenericMeta, self).__subclasscheck__(cls) + return super().__subclasscheck__(cls) def __instancecheck__(self, instance): # Since we extend ABC.__subclasscheck__ and @@ -1255,31 +1159,20 @@ class GenericMeta(TypingMeta, abc.ABCMeta): # latter, we must extend __instancecheck__ too. For simplicity # we just skip the cache check -- instance checks for generic # classes are supposed to be rare anyways. - if not isinstance(instance, type): - return issubclass(instance.__class__, self) - return False + return issubclass(instance.__class__, self) def __setattr__(self, attr, value): - # We consider all the subscripted genrics as proxies for original class + # We consider all the subscripted generics as proxies for original class if ( attr.startswith('__') and attr.endswith('__') or - attr.startswith('_abc_') + attr.startswith('_abc_') or + self._gorg is None # The class is not fully created, see #typing/506 ): super(GenericMeta, self).__setattr__(attr, value) else: super(GenericMeta, self._gorg).__setattr__(attr, value) -def _copy_generic(self): - """Hack to work around https://bugs.python.org/issue11480 on Python 2""" - return self.__class__(self.__name__, self.__bases__, dict(self.__dict__), - self.__parameters__, self.__args__, self.__origin__, - self.__extra__, self.__orig_bases__) - - -copy._copy_dispatch[GenericMeta] = _copy_generic - - # Prevent checks for Generic to crash when defining Generic. Generic = None @@ -1288,18 +1181,10 @@ def _generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ is None: - if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): - return base_cls.__new__(cls) - else: - return base_cls.__new__(cls, *args, **kwds) + return base_cls.__new__(cls) else: origin = cls._gorg - if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): - obj = base_cls.__new__(origin) - else: - obj = base_cls.__new__(origin, *args, **kwds) + obj = base_cls.__new__(origin) try: obj.__orig_class__ = cls except AttributeError: @@ -1308,7 +1193,7 @@ def _generic_new(base_cls, cls, *args, **kwds): return obj -class Generic(object): +class Generic(metaclass=GenericMeta): """Abstract base class for generic types. A generic type is typically declared by inheriting from @@ -1329,7 +1214,6 @@ class Generic(object): return default """ - __metaclass__ = GenericMeta __slots__ = () def __new__(cls, *args, **kwds): @@ -1339,14 +1223,14 @@ class Generic(object): return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) -class _TypingEmpty(object): +class _TypingEmpty: """Internal placeholder for () or []. Used by TupleMeta and CallableMeta to allow empty list/tuple in specific places, without allowing them to sneak in where prohibited. """ -class _TypingEllipsis(object): +class _TypingEllipsis: """Internal placeholder for ... (ellipsis).""" @@ -1358,18 +1242,18 @@ class TupleMeta(GenericMeta): if self.__origin__ is not None or self._gorg is not Tuple: # Normal generic rules apply if this is not the first subscription # or a subscription of a subclass. - return super(TupleMeta, self).__getitem__(parameters) + return super().__getitem__(parameters) if parameters == (): - return super(TupleMeta, self).__getitem__((_TypingEmpty,)) + return super().__getitem__((_TypingEmpty,)) if not isinstance(parameters, tuple): parameters = (parameters,) - if len(parameters) == 2 and parameters[1] is Ellipsis: + if len(parameters) == 2 and parameters[1] is ...: msg = "Tuple[t, ...]: t must be a type." p = _type_check(parameters[0], msg) - return super(TupleMeta, self).__getitem__((p, _TypingEllipsis)) + return super().__getitem__((p, _TypingEllipsis)) msg = "Tuple[t0, t1, ...]: each t must be a type." parameters = tuple(_type_check(p, msg) for p in parameters) - return super(TupleMeta, self).__getitem__(parameters) + return super().__getitem__(parameters) def __instancecheck__(self, obj): if self.__args__ is None: @@ -1384,10 +1268,7 @@ class TupleMeta(GenericMeta): "with issubclass().") -copy._copy_dispatch[TupleMeta] = _copy_generic - - -class Tuple(tuple): +class Tuple(tuple, extra=tuple, metaclass=TupleMeta): """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. Example: Tuple[T1, T2] is a tuple of two elements corresponding @@ -1397,8 +1278,6 @@ class Tuple(tuple): To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. """ - __metaclass__ = TupleMeta - __extra__ = tuple __slots__ = () def __new__(cls, *args, **kwds): @@ -1409,18 +1288,18 @@ class Tuple(tuple): class CallableMeta(GenericMeta): - """ Metaclass for Callable.""" + """Metaclass for Callable (internal).""" def __repr__(self): if self.__origin__ is None: - return super(CallableMeta, self).__repr__() + return super().__repr__() return self._tree_repr(self._subs_tree()) def _tree_repr(self, tree): if self._gorg is not Callable: - return super(CallableMeta, self)._tree_repr(tree) + return super()._tree_repr(tree) # For actual Callable (not its subclass) we override - # super(CallableMeta, self)._tree_repr() for nice formatting. + # super()._tree_repr() for nice formatting. arg_list = [] for arg in tree[1:]: if not isinstance(arg, tuple): @@ -1438,7 +1317,7 @@ class CallableMeta(GenericMeta): """ if self.__origin__ is not None or self._gorg is not Callable: - return super(CallableMeta, self).__getitem__(parameters) + return super().__getitem__(parameters) if not isinstance(parameters, tuple) or len(parameters) != 2: raise TypeError("Callable must be used as " "Callable[[arg, ...], result].") @@ -1458,17 +1337,14 @@ class CallableMeta(GenericMeta): msg = "Callable[args, result]: result must be a type." result = _type_check(result, msg) if args is Ellipsis: - return super(CallableMeta, self).__getitem__((_TypingEllipsis, result)) + return super().__getitem__((_TypingEllipsis, result)) msg = "Callable[[arg, ...], result]: each arg must be a type." args = tuple(_type_check(arg, msg) for arg in args) parameters = args + (result,) - return super(CallableMeta, self).__getitem__(parameters) + return super().__getitem__(parameters) -copy._copy_dispatch[CallableMeta] = _copy_generic - - -class Callable(object): +class Callable(extra=collections_abc.Callable, metaclass=CallableMeta): """Callable type; Callable[[int], str] is a function of (int) -> str. The subscription syntax must always be used with exactly two @@ -1479,8 +1355,6 @@ class Callable(object): such function types are rarely used as callback types. """ - __metaclass__ = CallableMeta - __extra__ = collections_abc.Callable __slots__ = () def __new__(cls, *args, **kwds): @@ -1490,6 +1364,63 @@ class Callable(object): return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) +class _ClassVar(_FinalTypingBase, _root=True): + """Special type construct to mark class variables. + + An annotation wrapped in ClassVar indicates that a given + attribute is intended to be used as a class variable and + should not be set on instances of that class. Usage:: + + class Starship: + stats: ClassVar[Dict[str, int]] = {} # class variable + damage: int = 10 # instance variable + + ClassVar accepts only types and cannot be further subscribed. + + Note that ClassVar is not a class itself, and should not + be used with isinstance() or issubclass(). + """ + + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(_type_check(item, + '{} accepts only single type.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + new_tp = _eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super().__repr__() + if self.__type__ is not None: + r += '[{}]'.format(_type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _ClassVar): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + +ClassVar = _ClassVar(_root=True) + + def cast(typ, val): """Cast a value to a type. @@ -1503,7 +1434,11 @@ def cast(typ, val): def _get_defaults(func): """Internal helper to extract the default arguments, by name.""" - code = func.__code__ + try: + code = func.__code__ + except AttributeError: + # Some built-in functions don't have __code__, __defaults__, etc. + return {} pos_count = code.co_argcount arg_names = code.co_varnames arg_names = arg_names[:pos_count] @@ -1517,9 +1452,91 @@ def _get_defaults(func): return res +_allowed_types = (types.FunctionType, types.BuiltinFunctionType, + types.MethodType, types.ModuleType, + WrapperDescriptorType, MethodWrapperType, MethodDescriptorType) + + def get_type_hints(obj, globalns=None, localns=None): - """In Python 2 this is not supported and always returns None.""" - return 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. + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + 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. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - 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 {} + # Classes require a special treatment. + if isinstance(obj, type): + hints = {} + for base in reversed(obj.__mro__): + if globalns is None: + base_globals = sys.modules[base.__module__].__dict__ + else: + base_globals = globalns + ann = base.__dict__.get('__annotations__', {}) + for name, value in ann.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, base_globals, localns) + hints[name] = value + return hints + + if globalns is None: + if isinstance(obj, types.ModuleType): + globalns = obj.__dict__ + else: + globalns = getattr(obj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: + localns = globalns + hints = getattr(obj, '__annotations__', None) + if hints is None: + # Return empty annotations for something that _could_ have them. + if isinstance(obj, _allowed_types): + return {} + else: + raise TypeError('{!r} is not a module, class, method, ' + 'or function.'.format(obj)) + defaults = _get_defaults(obj) + hints = dict(hints) + 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 def no_type_check(arg): @@ -1611,7 +1628,7 @@ class _ProtocolMeta(GenericMeta): def __instancecheck__(self, obj): if _Protocol not in self.__bases__: - return super(_ProtocolMeta, self).__instancecheck__(obj) + return super().__instancecheck__(obj) raise TypeError("Protocols cannot be used with isinstance().") def __subclasscheck__(self, cls): @@ -1650,6 +1667,8 @@ class _ProtocolMeta(GenericMeta): else: if (not attr.startswith('_abc_') and attr != '__abstractmethods__' and + attr != '__annotations__' and + attr != '__weakref__' and attr != '_is_protocol' and attr != '_gorg' and attr != '__dict__' and @@ -1668,7 +1687,7 @@ class _ProtocolMeta(GenericMeta): return attrs -class _Protocol(object): +class _Protocol(metaclass=_ProtocolMeta): """Internal base class for protocol classes. This implements a simple-minded structural issubclass check @@ -1676,7 +1695,6 @@ class _Protocol(object): such as Hashable). """ - __metaclass__ = _ProtocolMeta __slots__ = () _is_protocol = True @@ -1688,21 +1706,47 @@ class _Protocol(object): Hashable = collections_abc.Hashable # Not generic. -class Iterable(Generic[T_co]): - __slots__ = () - __extra__ = collections_abc.Iterable +if hasattr(collections_abc, 'Awaitable'): + class Awaitable(Generic[T_co], extra=collections_abc.Awaitable): + __slots__ = () + + __all__.append('Awaitable') -class Iterator(Iterable[T_co]): +if hasattr(collections_abc, 'Coroutine'): + class Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co], + extra=collections_abc.Coroutine): + __slots__ = () + + __all__.append('Coroutine') + + +if hasattr(collections_abc, 'AsyncIterable'): + + class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable): + __slots__ = () + + class AsyncIterator(AsyncIterable[T_co], + extra=collections_abc.AsyncIterator): + __slots__ = () + + __all__.append('AsyncIterable') + __all__.append('AsyncIterator') + + +class Iterable(Generic[T_co], extra=collections_abc.Iterable): + __slots__ = () + + +class Iterator(Iterable[T_co], extra=collections_abc.Iterator): __slots__ = () - __extra__ = collections_abc.Iterator class SupportsInt(_Protocol): __slots__ = () @abstractmethod - def __int__(self): + def __int__(self) -> int: pass @@ -1710,7 +1754,7 @@ class SupportsFloat(_Protocol): __slots__ = () @abstractmethod - def __float__(self): + def __float__(self) -> float: pass @@ -1718,7 +1762,15 @@ class SupportsComplex(_Protocol): __slots__ = () @abstractmethod - def __complex__(self): + def __complex__(self) -> complex: + pass + + +class SupportsBytes(_Protocol): + __slots__ = () + + @abstractmethod + def __bytes__(self) -> bytes: pass @@ -1726,81 +1778,102 @@ class SupportsAbs(_Protocol[T_co]): __slots__ = () @abstractmethod - def __abs__(self): + def __abs__(self) -> T_co: + pass + + +class SupportsRound(_Protocol[T_co]): + __slots__ = () + + @abstractmethod + def __round__(self, ndigits: int = 0) -> T_co: pass if hasattr(collections_abc, 'Reversible'): - class Reversible(Iterable[T_co]): + class Reversible(Iterable[T_co], extra=collections_abc.Reversible): __slots__ = () - __extra__ = collections_abc.Reversible else: class Reversible(_Protocol[T_co]): __slots__ = () @abstractmethod - def __reversed__(self): + def __reversed__(self) -> 'Iterator[T_co]': pass Sized = collections_abc.Sized # Not generic. -class Container(Generic[T_co]): +class Container(Generic[T_co], extra=collections_abc.Container): __slots__ = () - __extra__ = collections_abc.Container + + +if hasattr(collections_abc, 'Collection'): + class Collection(Sized, Iterable[T_co], Container[T_co], + extra=collections_abc.Collection): + __slots__ = () + + __all__.append('Collection') # Callable was defined earlier. +if hasattr(collections_abc, 'Collection'): + class AbstractSet(Collection[T_co], + extra=collections_abc.Set): + __slots__ = () +else: + class AbstractSet(Sized, Iterable[T_co], Container[T_co], + extra=collections_abc.Set): + __slots__ = () -class AbstractSet(Sized, Iterable[T_co], Container[T_co]): + +class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet): __slots__ = () - __extra__ = collections_abc.Set - - -class MutableSet(AbstractSet[T]): - __slots__ = () - __extra__ = collections_abc.MutableSet # NOTE: It is only covariant in the value type. -class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co]): - __slots__ = () - __extra__ = collections_abc.Mapping +if hasattr(collections_abc, 'Collection'): + class Mapping(Collection[KT], Generic[KT, VT_co], + extra=collections_abc.Mapping): + __slots__ = () +else: + class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co], + extra=collections_abc.Mapping): + __slots__ = () -class MutableMapping(Mapping[KT, VT]): +class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping): __slots__ = () - __extra__ = collections_abc.MutableMapping if hasattr(collections_abc, 'Reversible'): - class Sequence(Sized, Reversible[T_co], Container[T_co]): - __slots__ = () - __extra__ = collections_abc.Sequence + if hasattr(collections_abc, 'Collection'): + class Sequence(Reversible[T_co], Collection[T_co], + extra=collections_abc.Sequence): + __slots__ = () + else: + class Sequence(Sized, Reversible[T_co], Container[T_co], + extra=collections_abc.Sequence): + __slots__ = () else: - class Sequence(Sized, Iterable[T_co], Container[T_co]): + class Sequence(Sized, Iterable[T_co], Container[T_co], + extra=collections_abc.Sequence): __slots__ = () - __extra__ = collections_abc.Sequence -class MutableSequence(Sequence[T]): +class MutableSequence(Sequence[T], extra=collections_abc.MutableSequence): __slots__ = () - __extra__ = collections_abc.MutableSequence -class ByteString(Sequence[int]): - pass - - -ByteString.register(str) -ByteString.register(bytearray) - - -class List(list, MutableSequence[T]): +class ByteString(Sequence[int], extra=collections_abc.ByteString): + __slots__ = () + + +class List(list, MutableSequence[T], extra=list): + __slots__ = () - __extra__ = list def __new__(cls, *args, **kwds): if cls._gorg is List: @@ -1809,9 +1882,9 @@ class List(list, MutableSequence[T]): return _generic_new(list, cls, *args, **kwds) -class Deque(collections.deque, MutableSequence[T]): +class Deque(collections.deque, MutableSequence[T], extra=collections.deque): + __slots__ = () - __extra__ = collections.deque def __new__(cls, *args, **kwds): if cls._gorg is Deque: @@ -1819,9 +1892,9 @@ class Deque(collections.deque, MutableSequence[T]): return _generic_new(collections.deque, cls, *args, **kwds) -class Set(set, MutableSet[T]): +class Set(set, MutableSet[T], extra=set): + __slots__ = () - __extra__ = set def __new__(cls, *args, **kwds): if cls._gorg is Set: @@ -1830,9 +1903,8 @@ class Set(set, MutableSet[T]): return _generic_new(set, cls, *args, **kwds) -class FrozenSet(frozenset, AbstractSet[T_co]): +class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset): __slots__ = () - __extra__ = frozenset def __new__(cls, *args, **kwds): if cls._gorg is FrozenSet: @@ -1841,55 +1913,89 @@ class FrozenSet(frozenset, AbstractSet[T_co]): return _generic_new(frozenset, cls, *args, **kwds) -class MappingView(Sized, Iterable[T_co]): +class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView): __slots__ = () - __extra__ = collections_abc.MappingView -class KeysView(MappingView[KT], AbstractSet[KT]): +class KeysView(MappingView[KT], AbstractSet[KT], + extra=collections_abc.KeysView): __slots__ = () - __extra__ = collections_abc.KeysView class ItemsView(MappingView[Tuple[KT, VT_co]], AbstractSet[Tuple[KT, VT_co]], - Generic[KT, VT_co]): - __slots__ = () - __extra__ = collections_abc.ItemsView - - -class ValuesView(MappingView[VT_co]): - __slots__ = () - __extra__ = collections_abc.ValuesView - - -class ContextManager(Generic[T_co]): + Generic[KT, VT_co], + extra=collections_abc.ItemsView): __slots__ = () - def __enter__(self): + +class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView): + __slots__ = () + + +if hasattr(contextlib, 'AbstractContextManager'): + class ContextManager(Generic[T_co], extra=contextlib.AbstractContextManager): + __slots__ = () +else: + class ContextManager(Generic[T_co]): + __slots__ = () + + def __enter__(self): + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + return None + + @classmethod + def __subclasshook__(cls, C): + if cls is ContextManager: + # In Python 3.6+, it is possible to set a method to None to + # explicitly indicate that the class does not implement an ABC + # (https://bugs.python.org/issue25958), but we do not support + # that pattern here because this fallback class is only used + # in Python 3.5 and earlier. + if (any("__enter__" in B.__dict__ for B in C.__mro__) and + any("__exit__" in B.__dict__ for B in C.__mro__)): + return True + return NotImplemented + + +if hasattr(contextlib, 'AbstractAsyncContextManager'): + class AsyncContextManager(Generic[T_co], + extra=contextlib.AbstractAsyncContextManager): + __slots__ = () + + __all__.append('AsyncContextManager') +elif sys.version_info[:2] >= (3, 5): + exec(""" +class AsyncContextManager(Generic[T_co]): + __slots__ = () + + async def __aenter__(self): return self @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): + async def __aexit__(self, exc_type, exc_value, traceback): return None @classmethod def __subclasshook__(cls, C): - if cls is ContextManager: - # In Python 3.6+, it is possible to set a method to None to - # explicitly indicate that the class does not implement an ABC - # (https://bugs.python.org/issue25958), but we do not support - # that pattern here because this fallback class is only used - # in Python 3.5 and earlier. - if (any("__enter__" in B.__dict__ for B in C.__mro__) and - any("__exit__" in B.__dict__ for B in C.__mro__)): + if cls is AsyncContextManager: + if sys.version_info[:2] >= (3, 6): + return _collections_abc._check_methods(C, "__aenter__", "__aexit__") + if (any("__aenter__" in B.__dict__ for B in C.__mro__) and + any("__aexit__" in B.__dict__ for B in C.__mro__)): return True return NotImplemented +__all__.append('AsyncContextManager') +""") + + +class Dict(dict, MutableMapping[KT, VT], extra=dict): -class Dict(dict, MutableMapping[KT, VT]): __slots__ = () - __extra__ = dict def __new__(cls, *args, **kwds): if cls._gorg is Dict: @@ -1898,9 +2004,10 @@ class Dict(dict, MutableMapping[KT, VT]): return _generic_new(dict, cls, *args, **kwds) -class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): +class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], + extra=collections.defaultdict): + __slots__ = () - __extra__ = collections.defaultdict def __new__(cls, *args, **kwds): if cls._gorg is DefaultDict: @@ -1908,9 +2015,9 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): return _generic_new(collections.defaultdict, cls, *args, **kwds) -class Counter(collections.Counter, Dict[T, int]): +class Counter(collections.Counter, Dict[T, int], extra=collections.Counter): + __slots__ = () - __extra__ = collections.Counter def __new__(cls, *args, **kwds): if cls._gorg is Counter: @@ -1918,6 +2025,21 @@ class Counter(collections.Counter, Dict[T, int]): return _generic_new(collections.Counter, cls, *args, **kwds) +if hasattr(collections, 'ChainMap'): + # ChainMap only exists in 3.3+ + __all__.append('ChainMap') + + class ChainMap(collections.ChainMap, MutableMapping[KT, VT], + extra=collections.ChainMap): + + __slots__ = () + + def __new__(cls, *args, **kwds): + if cls._gorg is ChainMap: + return collections.ChainMap(*args, **kwds) + return _generic_new(collections.ChainMap, cls, *args, **kwds) + + # Determine what base class to use for Generator. if hasattr(collections_abc, 'Generator'): # Sufficiently recent versions of 3.5 have a Generator ABC. @@ -1927,9 +2049,9 @@ else: _G_base = types.GeneratorType -class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]): +class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co], + extra=_G_base): __slots__ = () - __extra__ = _G_base def __new__(cls, *args, **kwds): if cls._gorg is Generator: @@ -1938,12 +2060,20 @@ class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]): return _generic_new(_G_base, cls, *args, **kwds) +if hasattr(collections_abc, 'AsyncGenerator'): + class AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra], + extra=collections_abc.AsyncGenerator): + __slots__ = () + + __all__.append('AsyncGenerator') + + # Internal type variable used for Type[]. CT_co = TypeVar('CT_co', covariant=True, bound=type) # This is not a real generic class. Don't use outside annotations. -class Type(Generic[CT_co]): +class Type(Generic[CT_co], extra=type): """A special construct usable to annotate class objects. For example, suppose we have the following classes:: @@ -1966,35 +2096,105 @@ class Type(Generic[CT_co]): At this point the type checker knows that joe has type BasicUser. """ + __slots__ = () - __extra__ = type -def NamedTuple(typename, fields): +def _make_nmtuple(name, types): + msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type" + types = [(n, _type_check(t, msg)) for n, t in types] + nm_tpl = collections.namedtuple(name, [n for n, t in types]) + # Prior to PEP 526, only _field_types attribute was assigned. + # Now, both __annotations__ and _field_types are used to maintain compatibility. + nm_tpl.__annotations__ = nm_tpl._field_types = collections.OrderedDict(types) + try: + nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + return nm_tpl + + +_PY36 = sys.version_info[:2] >= (3, 6) + +# attributes prohibited to set in NamedTuple class syntax +_prohibited = ('__new__', '__init__', '__slots__', '__getnewargs__', + '_fields', '_field_defaults', '_field_types', + '_make', '_replace', '_asdict', '_source') + +_special = ('__module__', '__name__', '__qualname__', '__annotations__') + + +class NamedTupleMeta(type): + + def __new__(cls, typename, bases, ns): + if ns.get('_root', False): + return super().__new__(cls, typename, bases, ns) + if not _PY36: + raise TypeError("Class syntax for NamedTuple is only supported" + " in Python 3.6+") + types = ns.get('__annotations__', {}) + nm_tpl = _make_nmtuple(typename, types.items()) + defaults = [] + defaults_dict = {} + for field_name in types: + if field_name in ns: + default_value = ns[field_name] + defaults.append(default_value) + defaults_dict[field_name] = default_value + elif defaults: + raise TypeError("Non-default namedtuple field {field_name} cannot " + "follow default field(s) {default_names}" + .format(field_name=field_name, + default_names=', '.join(defaults_dict.keys()))) + nm_tpl.__new__.__defaults__ = tuple(defaults) + nm_tpl._field_defaults = defaults_dict + # update from user namespace without overriding special namedtuple attributes + for key in ns: + if key in _prohibited: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special and key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + return nm_tpl + + +class NamedTuple(metaclass=NamedTupleMeta): """Typed version of namedtuple. - Usage:: + Usage in Python versions >= 3.6:: - Employee = typing.NamedTuple('Employee', [('name', str), ('id', int)]) + 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 + The resulting class has extra __annotations__ and _field_types + attributes, giving an ordered dict mapping field names to types. + __annotations__ should be preferred, while _field_types + is kept to maintain pre PEP 526 compatibility. (The field names are in the _fields attribute, which is part of the namedtuple - API.) + API.) Alternative equivalent keyword syntax is also accepted:: + + Employee = NamedTuple('Employee', name=str, id=int) + + In Python versions <= 3.5 use:: + + Employee = NamedTuple('Employee', [('name', str), ('id', int)]) """ - 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'). - try: - cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - return cls + _root = True + + def __new__(self, typename, fields=None, **kwargs): + if kwargs and not _PY36: + raise TypeError("Keyword syntax for NamedTuple is only supported" + " in Python 3.6+") + if fields is None: + fields = kwargs.items() + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + return _make_nmtuple(typename, fields) def NewType(name, tp): @@ -2005,8 +2205,7 @@ def NewType(name, tp): UserId = NewType('UserId', int) - def name_by_id(user_id): - # type: (UserId) -> str + def name_by_id(user_id: UserId) -> str: ... UserId('user') # Fails type check @@ -2020,14 +2219,13 @@ def NewType(name, tp): def new_type(x): return x - # Some versions of Python 2 complain because of making all strings unicode - new_type.__name__ = str(name) + new_type.__name__ = name new_type.__supertype__ = tp return new_type # Python-version-specific alias (Python 2: unicode; Python 3: str) -Text = unicode +Text = str # Constant that's True when type checking, but False here. @@ -2050,83 +2248,83 @@ class IO(Generic[AnyStr]): __slots__ = () @abstractproperty - def mode(self): + def mode(self) -> str: pass @abstractproperty - def name(self): + def name(self) -> str: pass @abstractmethod - def close(self): - pass - - @abstractproperty - def closed(self): + def close(self) -> None: pass @abstractmethod - def fileno(self): + def closed(self) -> bool: pass @abstractmethod - def flush(self): + def fileno(self) -> int: pass @abstractmethod - def isatty(self): + def flush(self) -> None: pass @abstractmethod - def read(self, n=-1): + def isatty(self) -> bool: pass @abstractmethod - def readable(self): + def read(self, n: int = -1) -> AnyStr: pass @abstractmethod - def readline(self, limit=-1): + def readable(self) -> bool: pass @abstractmethod - def readlines(self, hint=-1): + def readline(self, limit: int = -1) -> AnyStr: pass @abstractmethod - def seek(self, offset, whence=0): + def readlines(self, hint: int = -1) -> List[AnyStr]: pass @abstractmethod - def seekable(self): + def seek(self, offset: int, whence: int = 0) -> int: pass @abstractmethod - def tell(self): + def seekable(self) -> bool: pass @abstractmethod - def truncate(self, size=None): + def tell(self) -> int: pass @abstractmethod - def writable(self): + def truncate(self, size: int = None) -> int: pass @abstractmethod - def write(self, s): + def writable(self) -> bool: pass @abstractmethod - def writelines(self, lines): + def write(self, s: AnyStr) -> int: pass @abstractmethod - def __enter__(self): + def writelines(self, lines: List[AnyStr]) -> None: pass @abstractmethod - def __exit__(self, type, value, traceback): + def __enter__(self) -> 'IO[AnyStr]': + pass + + @abstractmethod + def __exit__(self, type, value, traceback) -> None: pass @@ -2136,45 +2334,45 @@ class BinaryIO(IO[bytes]): __slots__ = () @abstractmethod - def write(self, s): + def write(self, s: Union[bytes, bytearray]) -> int: pass @abstractmethod - def __enter__(self): + def __enter__(self) -> 'BinaryIO': pass -class TextIO(IO[unicode]): +class TextIO(IO[str]): """Typed version of the return of open() in text mode.""" __slots__ = () @abstractproperty - def buffer(self): + def buffer(self) -> BinaryIO: pass @abstractproperty - def encoding(self): + def encoding(self) -> str: pass @abstractproperty - def errors(self): + def errors(self) -> Optional[str]: pass @abstractproperty - def line_buffering(self): + def line_buffering(self) -> bool: pass @abstractproperty - def newlines(self): + def newlines(self) -> Any: pass @abstractmethod - def __enter__(self): + def __enter__(self) -> 'TextIO': pass -class io(object): +class io: """Wrapper namespace for IO generic classes.""" __all__ = ['IO', 'TextIO', 'BinaryIO'] @@ -2183,7 +2381,7 @@ class io(object): BinaryIO = BinaryIO -io.__name__ = __name__ + b'.io' +io.__name__ = __name__ + '.io' sys.modules[io.__name__] = io @@ -2193,7 +2391,7 @@ Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')), lambda m: m.re.pattern) -class re(object): +class re: """Wrapper namespace for re type aliases.""" __all__ = ['Pattern', 'Match'] @@ -2201,5 +2399,5 @@ class re(object): Match = Match -re.__name__ = __name__ + b'.re' +re.__name__ = __name__ + '.re' sys.modules[re.__name__] = re diff --git a/pytest.ini b/pytest.ini index 80c3e3a3..eee579da 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,5 @@ [pytest] -addopts = -n auto +addopts = -ra -n auto +testpaths = tests/ ; Add vendor and patched in addition to the default list of ignored dirs -norecursedirs = .* build dist CVS _darcs {arch} *.egg vendor patched +norecursedirs = .* build dist CVS _darcs {arch} *.egg vendor patched news tasks docs diff --git a/run-tests.sh b/run-tests.sh index 64d79648..cc5252d1 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -28,8 +28,10 @@ fi # pip uninstall -y pipenv echo "Path: $PATH" echo "Installing Pipenv…" -pip install -e "$(pwd)" --upgrade -pipenv install --deploy --dev +PIP_USER="1" python -m pip install --upgrade setuptools +PIP_USER="1" python3 -m pip install --upgrade setuptools +python -m pip install -e "$(pwd)" --upgrade && python3 -m pip install -e "$(pwd)" --upgrade +python3 -m pipenv install --deploy --dev --system # Otherwise, we're on a development machine. # First, try MacOS… @@ -47,20 +49,20 @@ else fi echo "Installing dependencies…" -PIPENV_PYTHON=2.7 pipenv run pip install -e . --upgrade -PIPENV_PYTHON=3.7 pipenv run pip install -e . --upgrade -PIPENV_PYTHON=2.7 pipenv install --dev -PIPENV_PYTHON=3.7 pipenv install --dev +PIPENV_PYTHON=2.7 python3 -m pipenv --venv && pipenv --rm && pipenv install --dev +PIPENV_PYTHON=3.7 python3 -m pipenv --venv && pipenv --rm && pipenv install --dev +PIPENV_PYTHON=2.7 python3 -m pipenv run pip install --upgrade -e . +PIPENV_PYTHON=3.7 python3 -m pipenv run pip install --upgrade -e . echo "$ pipenv run time pytest -v -n auto tests -m \"$TEST_SUITE\"" # PIPENV_PYTHON=2.7 pipenv run time pytest -v -n auto tests -m "$TEST_SUITE" | prefix 2.7 & # PIPENV_PYTHON=3.6 pipenv run time pytest -v -n auto tests -m "$TEST_SUITE" | prefix 3.6 # Better to run them sequentially. -PIPENV_PYTHON=2.7 pipenv run time pytest -PIPENV_PYTHON=3.7 pipenv run time pytest +PIPENV_PYTHON=2.7 python3 -m pipenv run time pytest +PIPENV_PYTHON=3.7 python3 -m pipenv run time pytest # test revendoring -pip3 install --upgrade invoke requests parver +pip3 install --upgrade invoke requests parver vistir python3 -m invoke vendoring.update # Cleanup junk. rm -fr .venv diff --git a/setup.py b/setup.py index 426483dd..ea5af031 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ required = [ 'requests[security];python_version<"2.7"', 'ordereddict;python_version<"2.7"', 'enum34; python_version<"3"', - 'typing; python_version<"3"' + 'typing; python_version<"3.5"' ] diff --git a/tasks/vendoring/__init__.py b/tasks/vendoring/__init__.py index 84e8b1ad..9e36723b 100644 --- a/tasks/vendoring/__init__.py +++ b/tasks/vendoring/__init__.py @@ -2,8 +2,9 @@ """"Vendoring script, python 3.5 needed""" # Taken from pip # see https://github.com/pypa/pip/blob/95bcf8c5f6394298035a7332c441868f3b0169f4/tasks/vendoring/__init__.py -from vistir.compat import NamedTemporaryFile, TemporaryDirectory, Path -from vistir.path import mkdir_p +from pipenv._compat import NamedTemporaryFile, TemporaryDirectory +from pathlib import Path +from pipenv.utils import mkdir_p # from tempfile import TemporaryDirectory import tarfile import zipfile @@ -19,16 +20,17 @@ LIBRARY_DIRNAMES = { 'requirements-parser': 'requirements', 'backports.shutil_get_terminal_size': 'backports/shutil_get_terminal_size', 'backports.weakref': 'backports/weakref', - 'typing.py': 'backports/typing.py', 'shutil_backports': 'backports/shutil_get_terminal_size', 'python-dotenv': 'dotenv', 'pip-tools': 'piptools', 'setuptools': 'pkg_resources', 'msgpack-python': 'msgpack', 'attrs': 'attr', - 'enum34': 'backports/enum' + 'enum': 'backports/enum' } +PY2_DOWNLOAD = ['enum34',] + # from time to time, remove the no longer needed ones HARDCODED_LICENSE_URLS = { 'pytoml': 'https://github.com/avakar/pytoml/raw/master/LICENSE', @@ -68,8 +70,7 @@ PATCHED_RENAMES = { LIBRARY_RENAMES = { 'pip': 'pipenv.patched.notpip', - 'enum': 'backports/enum', - 'typing.py': 'backports/typing.py' + 'enum34': 'enum', } @@ -420,6 +421,9 @@ def packages_missing_licenses(ctx, vendor_dir=None, requirements_file='vendor.tx pkg = req.strip().split("=")[0] possible_pkgs = [pkg, pkg.replace('-', '_')] match_found = False + if pkg in PY2_DOWNLOAD: + match_found = True + # print("pkg ===> %s" % pkg) if pkg in LIBRARY_DIRNAMES: possible_pkgs.append(LIBRARY_DIRNAMES[pkg]) for pkgpath in possible_pkgs: @@ -429,21 +433,22 @@ def packages_missing_licenses(ctx, vendor_dir=None, requirements_file='vendor.tx licensepath = pkgpath.joinpath(licensepath) if licensepath.exists(): match_found = True - log("%s: Trying path %s... FOUND" % (pkg, licensepath)) + # log("%s: Trying path %s... FOUND" % (pkg, licensepath)) break elif (pkgpath.exists() or pkgpath.parent.joinpath("{0}.py".format(pkgpath.stem)).exists()): for licensepath in LICENSES: licensepath = pkgpath.parent.joinpath("{0}.{1}".format(pkgpath.stem, licensepath)) if licensepath.exists(): match_found = True - log("%s: Trying path %s... FOUND" % (pkg, licensepath)) + # log("%s: Trying path %s... FOUND" % (pkg, licensepath)) break if match_found: break if match_found: continue - log("%s: No license found in %s" % (pkg, pkgpath)) - new_requirements.append(req) + else: + # log("%s: No license found in %s" % (pkg, pkgpath)) + new_requirements.append(req) return new_requirements @@ -458,10 +463,12 @@ def download_licenses(ctx, vendor_dir=None, requirements_file='vendor.txt', pack vendor_dir = _get_vendor_dir(ctx) requirements_file = vendor_dir / requirements_file requirements = packages_missing_licenses(ctx, vendor_dir, requirements_file, package=package) + with NamedTemporaryFile(prefix="pipenv", suffix="vendor-reqs", delete=False, mode="w") as fh: fh.write("\n".join(requirements)) new_requirements_file = fh.name new_requirements_file = Path(new_requirements_file) + log(requirements) requirement = "-r {0}".format(new_requirements_file.as_posix()) if package: if not only: @@ -472,8 +479,9 @@ def download_licenses(ctx, vendor_dir=None, requirements_file='vendor.txt', pack requirement = package tmp_dir = vendor_dir / '__tmp__' # TODO: Fix this whenever it gets sorted out (see https://github.com/pypa/pip/issues/5739) + ctx.run('pip install flit') # needed for the next step ctx.run( - 'pip download --no-binary :all: --only-binary requests_download --no-deps -d {0} {1}'.format( + 'pip download --no-binary :all: --only-binary requests_download --no-build-isolation --no-deps -d {0} {1}'.format( tmp_dir.as_posix(), requirement, ) diff --git a/tasks/vendoring/patches/vendor/tomlkit-typing-imports.patch b/tasks/vendoring/patches/vendor/tomlkit-typing-imports.patch index b7e29fe0..2288b513 100644 --- a/tasks/vendoring/patches/vendor/tomlkit-typing-imports.patch +++ b/tasks/vendoring/patches/vendor/tomlkit-typing-imports.patch @@ -1,23 +1,20 @@ diff --git a/pipenv/vendor/tomlkit/api.py b/pipenv/vendor/tomlkit/api.py -index e541c20c..d36375e8 100644 +index e541c20c..0ac26752 100644 --- a/pipenv/vendor/tomlkit/api.py +++ b/pipenv/vendor/tomlkit/api.py -@@ -1,6 +1,9 @@ +@@ -1,7 +1,5 @@ import datetime as _datetime -from typing import Tuple -+try: -+ from typing import Tuple -+except ImportError: -+ from pipenv.vendor.backports.typing import Tuple - +- from ._utils import parse_rfc3339 from .container import Container + from .items import AoT diff --git a/pipenv/vendor/tomlkit/container.py b/pipenv/vendor/tomlkit/container.py -index c1d2d7c6..c7595a52 100644 +index c1d2d7c6..a7876ff1 100644 --- a/pipenv/vendor/tomlkit/container.py +++ b/pipenv/vendor/tomlkit/container.py -@@ -1,12 +1,21 @@ +@@ -1,13 +1,5 @@ from __future__ import unicode_literals -from typing import Any @@ -27,43 +24,26 @@ index c1d2d7c6..c7595a52 100644 -from typing import Optional -from typing import Tuple -from typing import Union -+try: -+ from typing import Any -+ from typing import Dict -+ from typing import Generator -+ from typing import List -+ from typing import Optional -+ from typing import Tuple -+ from typing import Union -+except ImportError: -+ from pipenv.vendor.backports.typing import Any -+ from pipenv.vendor.backports.typing import Dict -+ from pipenv.vendor.backports.typing import Generator -+ from pipenv.vendor.backports.typing import List -+ from pipenv.vendor.backports.typing import Optional -+ from pipenv.vendor.backports.typing import Tuple -+ from pipenv.vendor.backports.typing import Union - +- from ._compat import decode from .exceptions import KeyAlreadyPresent + from .exceptions import NonExistentKey diff --git a/pipenv/vendor/tomlkit/exceptions.py b/pipenv/vendor/tomlkit/exceptions.py -index 8d48bf19..dae29f6f 100644 +index 8d48bf19..d889a924 100644 --- a/pipenv/vendor/tomlkit/exceptions.py +++ b/pipenv/vendor/tomlkit/exceptions.py -@@ -1,4 +1,7 @@ +@@ -1,6 +1,3 @@ -from typing import Optional -+try: -+ from typing import Optional -+except ImportError: -+ from pipenv.vendor.backports.typing import Optional - - +- +- class TOMLKitError(Exception): + + pass diff --git a/pipenv/vendor/tomlkit/items.py b/pipenv/vendor/tomlkit/items.py -index 747dbd50..83b17612 100644 +index 747dbd50..8807f4b3 100644 --- a/pipenv/vendor/tomlkit/items.py +++ b/pipenv/vendor/tomlkit/items.py -@@ -6,13 +6,25 @@ import string +@@ -6,14 +6,11 @@ import string from datetime import date from datetime import datetime from datetime import time @@ -74,33 +54,20 @@ index 747dbd50..83b17612 100644 -from typing import List -from typing import Optional -from typing import Union +- +import sys +if sys.version_info >= (3, 4): + from enum import Enum +else: + from pipenv.vendor.backports.enum import Enum -+try: -+ from typing import Any -+ from typing import Dict -+ from typing import Generator -+ from typing import List -+ from typing import Optional -+ from typing import Union -+except ImportError: -+ from pipenv.vendor.backports.typing import Any -+ from pipenv.vendor.backports.typing import Dict -+ from pipenv.vendor.backports.typing import Generator -+ from pipenv.vendor.backports.typing import List -+ from pipenv.vendor.backports.typing import Optional -+ from pipenv.vendor.backports.typing import Union - from ._compat import PY2 + from ._compat import decode diff --git a/pipenv/vendor/tomlkit/parser.py b/pipenv/vendor/tomlkit/parser.py -index b55a3fe4..28c0a6e0 100644 +index b55a3fe4..3d4984d1 100644 --- a/pipenv/vendor/tomlkit/parser.py +++ b/pipenv/vendor/tomlkit/parser.py -@@ -7,10 +7,16 @@ import re +@@ -7,10 +7,6 @@ import re import string from copy import copy @@ -108,34 +75,19 @@ index b55a3fe4..28c0a6e0 100644 -from typing import Optional -from typing import Tuple -from typing import Union -+try: -+ from typing import Iterator -+ from typing import Optional -+ from typing import Tuple -+ from typing import Union -+except ImportError: -+ from pipenv.vendor.backports.typing import Iterator -+ from pipenv.vendor.backports.typing import Optional -+ from pipenv.vendor.backports.typing import Tuple -+ from pipenv.vendor.backports.typing import Union from ._compat import PY2 from ._compat import chr diff --git a/pipenv/vendor/tomlkit/toml_file.py b/pipenv/vendor/tomlkit/toml_file.py -index 3b416664..3e4cc721 100644 +index 3b416664..631e9959 100644 --- a/pipenv/vendor/tomlkit/toml_file.py +++ b/pipenv/vendor/tomlkit/toml_file.py -@@ -1,7 +1,11 @@ +@@ -1,8 +1,5 @@ import io -from typing import Any -from typing import Dict -+try: -+ from typing import Any -+ from typing import Dict -+except ImportError: -+ from pipenv.vendor.backports.typing import Any -+ from pipenv.vendor.backports.typing import Dict - +- from .api import loads from .toml_document import TOMLDocument +