Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python 3.7 #18

Merged
merged 13 commits into from
Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions test_typing_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
is_typevar, is_classvar, get_origin, get_parameters, get_last_args, get_args,
get_generic_type, get_generic_bases, get_last_origin,
)
from unittest import TestCase, main
from unittest import TestCase, main, skipIf
from typing import (
Union, ClassVar, Callable, Optional, TypeVar, Sequence, Mapping,
MutableMapping, Iterable, Generic, List, Any, Dict, Tuple, NamedTuple,
)

import sys
NEW_TYPING = sys.version_info[:3] >= (3, 7, 0) # PEP 560


class IsUtilityTestCase(TestCase):
def sample_test(self, fun, samples, nonsamples):
Expand Down Expand Up @@ -66,6 +69,7 @@ def test_classvar(self):

class GetUtilityTestCase(TestCase):

@skipIf(NEW_TYPING, "Not supported in Python 3.7")
def test_last_origin(self):
T = TypeVar('T')
self.assertEqual(get_last_origin(int), None)
Expand All @@ -81,7 +85,7 @@ def test_origin(self):
self.assertEqual(get_origin(ClassVar[int]), None)
self.assertEqual(get_origin(Generic), Generic)
self.assertEqual(get_origin(Generic[T]), Generic)
self.assertEqual(get_origin(List[Tuple[T, T]][int]), List)
self.assertEqual(get_origin(List[Tuple[T, T]][int]), list if NEW_TYPING else List)

def test_parameters(self):
T = TypeVar('T')
Expand All @@ -96,6 +100,7 @@ def test_parameters(self):
self.assertEqual(get_parameters(Union[S_co, Tuple[T, T]][int, U]), (U,))
self.assertEqual(get_parameters(Mapping[T, Tuple[S_co, T]]), (T, S_co))

@skipIf(NEW_TYPING, "Not supported in Python 3.7")
def test_last_args(self):
T = TypeVar('T')
S = TypeVar('S')
Expand All @@ -107,13 +112,17 @@ def test_last_args(self):
self.assertEqual(get_last_args(Callable[[T, S], int]), (T, S, int))
self.assertEqual(get_last_args(Callable[[], int]), (int,))

@skipIf(NEW_TYPING, "Not supported in Python 3.7")
def test_args(self):
T = TypeVar('T')
self.assertEqual(get_args(Union[int, Tuple[T, int]][str]),
(int, (Tuple, str, int)))
self.assertEqual(get_args(Union[int, Union[T, int], str][int]),
(int, str))
self.assertEqual(get_args(int), ())

def test_args_evaluated(self):
T = TypeVar('T')
self.assertEqual(get_args(Union[int, Tuple[T, int]][str], evaluate=True),
(int, Tuple[str, int]))
self.assertEqual(get_args(Dict[int, Tuple[T, T]][Optional[int]], evaluate=True),
Expand Down
96 changes: 73 additions & 23 deletions typing_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,21 @@

# NOTE: This module must support Python 2.7 in addition to Python 3.x

from typing import (
Callable, CallableMeta, Union, _Union, TupleMeta, TypeVar,
_ClassVar, GenericMeta,
)
import sys
NEW_TYPING = sys.version_info[:3] >= (3, 7, 0) # PEP 560
if NEW_TYPING:
import collections.abc


if NEW_TYPING:
from typing import (
Generic, Callable, Union, TypeVar, ClassVar, Tuple, _GenericAlias
)
else:
from typing import (
Callable, CallableMeta, Union, _Union, TupleMeta, TypeVar,
_ClassVar, GenericMeta,
)


def _gorg(cls):
Expand Down Expand Up @@ -41,7 +52,10 @@ def is_generic_type(tp):
is_generic_type(MutableMapping[T, List[int]]) == True
is_generic_type(Sequence[Union[str, bytes]]) == True
"""

if NEW_TYPING:
return (isinstance(tp, type) and issubclass(tp, Generic) or
isinstance(tp, _GenericAlias) and
tp.__origin__ not in (Union, tuple, ClassVar, collections.abc.Callable))
return (isinstance(tp, GenericMeta) and not
isinstance(tp, (CallableMeta, TupleMeta)))

Expand All @@ -63,9 +77,13 @@ class MyClass(Callable[[int], int]):
For more general tests use callable(), for more precise test
(excluding subclasses) use::

get_origin(tp) is Callable
get_origin(tp) is collections.abc.Callable # Callable prior to Python 3.7
"""

if NEW_TYPING:
return (tp is Callable or isinstance(tp, _GenericAlias) and
tp.__origin__ is collections.abc.Callable or
isinstance(tp, type) and issubclass(tp, Generic) and
issubclass(tp, collections.abc.Callable))
return type(tp) is CallableMeta


Expand All @@ -85,9 +103,13 @@ class MyClass(Tuple[str, int]):
For more general tests use issubclass(..., tuple), for more precise test
(excluding subclasses) use::

get_origin(tp) is Tuple
get_origin(tp) is tuple # Tuple prior to Python 3.7
"""

if NEW_TYPING:
return (tp is Tuple or isinstance(tp, _GenericAlias) and
tp.__origin__ is tuple or
isinstance(tp, type) and issubclass(tp, Generic) and
issubclass(tp, tuple))
return type(tp) is TupleMeta


Expand All @@ -99,7 +121,9 @@ def is_union_type(tp):
is_union_type(Union[int, int]) == False
is_union_type(Union[T, int]) == True
"""

if NEW_TYPING:
return (tp is Union or
isinstance(tp, _GenericAlias) and tp.__origin__ is Union)
return type(tp) is _Union


Expand All @@ -122,23 +146,27 @@ def is_classvar(tp):
is_classvar(ClassVar[int]) == True
is_classvar(ClassVar[List[T]]) == True
"""

if NEW_TYPING:
return (tp is ClassVar or
isinstance(tp, _GenericAlias) and tp.__origin__ is ClassVar)
return type(tp) is _ClassVar


def get_last_origin(tp):
"""Get the last base of (multiply) subscripted type. Supports generic types,
Union, Callable, and Tuple. Returns None for unsupported types.
Examples::
Union, Callable, and Tuple. Returns None for unsupported types.
Examples::

get_last_origin(int) == None
get_last_origin(ClassVar[int]) == None
get_last_origin(Generic[T]) == Generic
get_last_origin(Union[T, int][str]) == Union[T, int]
get_last_origin(List[Tuple[T, T]][int]) == List[Tuple[T, T]]
get_last_origin(List) == List
"""

"""
if NEW_TYPING:
raise ValueError('This function is only supported in Python 3.6,'
' use get_origin instead')
sentinel = object()
origin = getattr(tp, '__origin__', sentinel)
if origin is sentinel:
Expand All @@ -157,9 +185,14 @@ def get_origin(tp):
get_origin(Generic) == Generic
get_origin(Generic[T]) == Generic
get_origin(Union[T, int]) == Union
get_origin(List[Tuple[T, T]][int]) == List
get_origin(List[Tuple[T, T]][int]) == list # List prior to Python 3.7
"""

if NEW_TYPING:
if isinstance(tp, _GenericAlias):
return tp.__origin__ if tp.__origin__ is not ClassVar else None
if tp is Generic:
return Generic
return None
if isinstance(tp, GenericMeta):
return _gorg(tp)
if is_union_type(tp):
Expand All @@ -183,7 +216,12 @@ def get_parameters(tp):
get_parameters(Union[S_co, Tuple[T, T]][int, U]) == (U,)
get_parameters(Mapping[T, Tuple[S_co, T]]) == (T, S_co)
"""

if NEW_TYPING:
if (isinstance(tp, _GenericAlias) or
isinstance(tp, type) and issubclass(tp, Generic) and
tp is not Generic):
return tp.__parameters__
return ()
if (
is_generic_type(tp) or is_union_type(tp) or
is_callable_type(tp) or is_tuple_type(tp)
Expand All @@ -204,7 +242,9 @@ def get_last_args(tp):
get_last_args(Callable[[T], int]) == (T, int)
get_last_args(Callable[[], int]) == (int,)
"""

if NEW_TYPING:
raise ValueError('This function is only supported in Python 3.6,'
' use get_args instead')
if is_classvar(tp):
return (tp.__type__,) if tp.__type__ is not None else ()
if (
Expand Down Expand Up @@ -233,11 +273,13 @@ def _eval_args(args):
return tuple(res)


def get_args(tp, evaluate=False):
def get_args(tp, evaluate=None):
"""Get type arguments with all substitutions performed. For unions,
basic simplifications used by Union constructor are performed.
If `evaluate` is False (default), report result as nested tuple, this matches
the internal representation of types. If `evaluate` is True, then all
On versions prior to 3.7 if `evaluate` is False (default),
report result as nested tuple, this matches
the internal representation of types. If `evaluate` is True
(or if Python version is 3.7 or greater), then all
type parameters are applied (this could be time and memory expensive).
Examples::

Expand All @@ -251,7 +293,15 @@ def get_args(tp, evaluate=False):
(int, Tuple[Optional[int], Optional[int]])
get_args(Callable[[], T][int], evaluate=True) == ([], int,)
"""

if NEW_TYPING:
if evaluate is not None and not evaluate:
raise ValueError('evaluate can only be True in Python 3.7')
if isinstance(tp, _GenericAlias):
res = tp.__args__
if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
res = (list(res[:-1]), res[-1])
return res
return ()
if is_classvar(tp):
return (tp.__type__,)
if (
Expand Down