Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions .flake8-tests
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ exclude =
setup.py
python2/typing.py,
src/typing.py
typing_inspect/typing_inspect.py
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ install:

script:
- export PYTHONPATH=`python -c "import sys; print('python2' if sys.version.startswith('2') else 'src')"`; py.test $PYTHONPATH
- py.test typing_inspect
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then flake8; fi
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then flake8 --config=.flake8-tests src/test_typing.py python2/test_typing.py; fi
- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then flake8 --config=.flake8-tests src/test_typing.py python2/test_typing.py typing_inspect/test_typing_inspect.py; fi
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ exclude =
# tests have more relaxed formatting rules
# and its own specific config in .flake8-tests
python2/test_typing.py,
src/test_typing.py
src/test_typing.py,
typing_inspect/test_typing_inspect.py
8 changes: 8 additions & 0 deletions typing_inspect/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Typing Inspect
==============

The "typing_inspect" module defines experimental API for runtime
inspection of types defined in the standard "typing" module.

Example usage::
from typing_inspect import is_generic
46 changes: 46 additions & 0 deletions typing_inspect/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python

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

from distutils.core import setup

version = '0.1.0-dev'
description = 'Runtime inspection utilities for typing module.'
long_description = '''
Typing Inspect
==============

The "typing_inspect" module defines experimental API for runtime
inspection of types defined in the standard "typing" module.
'''.lstrip()

classifiers = [
'Development Status :: 3 - Alpha',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Python Software Foundation License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Software Development',
]

setup(
name='typing_inspect',
version=version,
description=description,
long_description=long_description,
author='Ivan Levkivskyi',
author_email='levkivskyi@gmail.com',
url='https://github.com/python/typing',
license='PSF',
keywords='typing function annotations type hints hinting checking '
'checker typehints typehinting typechecking inspect reflection',
py_modules=['typing_inspect'],
classifiers=classifiers,
)
124 changes: 124 additions & 0 deletions typing_inspect/test_typing_inspect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from typing_inspect import (
is_generic_type, is_callable_type, is_tuple_type, is_union_type,
is_typevar, is_classvar, get_origin, get_parameters, get_last_args, get_args,
get_generic_type, get_generic_bases,
)
from unittest import TestCase, main
from typing import (
Union, ClassVar, Callable, Optional, TypeVar, Sequence, Mapping,
MutableMapping, Iterable, Generic, List, Any, Dict, Tuple,
)


class IsUtilityTestCase(TestCase):
def sample_test(self, fun, samples, nonsamples):
for s in samples:
self.assertTrue(fun(s))
for s in nonsamples:
self.assertFalse(fun(s))

def test_generic(self):
T = TypeVar('T')
samples = [Generic, Generic[T], Iterable[int], Mapping,
MutableMapping[T, List[int]], Sequence[Union[str, bytes]]]
nonsamples = [int, Union[int, str], Union[int, T], ClassVar[List[int]],
Callable[..., T], ClassVar, Optional, bytes, list]
self.sample_test(is_generic_type, samples, nonsamples)

def test_callable(self):
samples = [Callable, Callable[..., int],
Callable[[int, int], Iterable[str]]]
nonsamples = [int, type, 42, [], List[int],
Union[callable, Callable[..., int]]]
self.sample_test(is_callable_type, samples, nonsamples)
class MyClass(Callable[[int], int]):
pass
self.assertTrue(is_callable_type(MyClass))

def test_tuple(self):
samples = [Tuple, Tuple[str, int], Tuple[Iterable, ...]]
nonsamples = [int, tuple, 42, List[int]]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add some NamedTuples as test cases; I assume they'll be nonsamples because typing.Tuple and typing.NamedTuple are completely distinct, but it would be good to clarify anyway.

self.sample_test(is_tuple_type, samples, nonsamples)
class MyClass(Tuple[str, int]):
pass
self.assertTrue(is_tuple_type(MyClass))

def test_union(self):
T = TypeVar('T')
S = TypeVar('S')
samples = [Union, Union[T, int], Union[int, Union[T, S]]]
nonsamples = [int, Union[int, int], [], Iterable[Any]]
self.sample_test(is_union_type, samples, nonsamples)

def test_typevar(self):
T = TypeVar('T')
S_co = TypeVar('S_co', covariant=True)
samples = [T, S_co]
nonsamples = [int, Union[T, int], Union[T, S_co], type, ClassVar[int]]
self.sample_test(is_typevar, samples, nonsamples)

def test_classvar(self):
T = TypeVar('T')
samples = [ClassVar, ClassVar[int], ClassVar[List[T]]]
nonsamples = [int, 42, Iterable, List[int], type, T]
self.sample_test(is_classvar, samples, nonsamples)


class GetUtilityTestCase(TestCase):
def test_origin(self):
T = TypeVar('T')
self.assertEqual(get_origin(int), None)
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)

def test_parameters(self):
T = TypeVar('T')
S_co = TypeVar('S_co', covariant=True)
U = TypeVar('U')
self.assertEqual(get_parameters(int), ())
self.assertEqual(get_parameters(Generic), ())
self.assertEqual(get_parameters(Union), ())
self.assertEqual(get_parameters(List[int]), ())
self.assertEqual(get_parameters(Generic[T]), (T,))
self.assertEqual(get_parameters(Tuple[List[T], List[S_co]]), (T, S_co))
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))

def test_last_args(self):
T = TypeVar('T')
S = TypeVar('S')
self.assertEqual(get_last_args(int), ())
self.assertEqual(get_last_args(Union), ())
self.assertEqual(get_last_args(ClassVar[int]), (int,))
self.assertEqual(get_last_args(Union[T, int]), (T, int))
self.assertEqual(get_last_args(Iterable[Tuple[T, S]][int, T]), (int, T))

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), ())
self.assertEqual(get_args(Dict[int, Tuple[T, T]][Optional[int]], evaluate=True),
(int, Tuple[Optional[int], Optional[int]]))

def test_generic_type(self):
T = TypeVar('T')
class Node(Generic[T]): pass
self.assertIs(get_generic_type(Node()), Node)
self.assertIs(get_generic_type(Node[int]()), Node[int])
self.assertIs(get_generic_type(Node[T]()), Node[T],)
self.assertIs(get_generic_type(1), int)

def test_generic_bases(self):
class MyClass(List[int], Mapping[str, List[int]]): pass
self.assertEqual(get_generic_bases(MyClass),
(List[int], Mapping[str, List[int]]))
self.assertEqual(get_generic_bases(int), ())


if __name__ == '__main__':
main()
Loading