Skip to content

Commit

Permalink
Add some tests based on coverage report: Part 2 (#375)
Browse files Browse the repository at this point in the history
* Add more tests based on coverage report

* Fix lint on PY2

* Simplify PR to only tests and tiny code clean-up

* Remove _right_ code on PY2

* Re-run Travis
  • Loading branch information
ilevkivskyi authored Feb 10, 2017
1 parent 58c86ad commit 370a137
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 9 deletions.
37 changes: 37 additions & 0 deletions python2/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ def test_repr(self):
self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__)
u = Union[int, Employee]
self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__)
T = TypeVar('T')
u = Union[T, int][int]
self.assertEqual(repr(u), repr(int))
u = Union[List[int], int]
self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]')

def test_cannot_subclass(self):
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -303,6 +308,14 @@ def test_union_instance_type_error(self):
with self.assertRaises(TypeError):
isinstance(42, Union[int, str])

def test_no_eval_union(self):
u = Union[int, str]
self.assertIs(u._eval_type({}, {}), u)

def test_function_repr_union(self):
def fun(): pass
self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]')

def test_union_str_pattern(self):
# Shouldn't crash; see http://bugs.python.org/issue25390
A = Union[str, Pattern]
Expand Down Expand Up @@ -853,6 +866,8 @@ def test_fail_with_bare_generic(self):
Tuple[Generic[T]]
with self.assertRaises(TypeError):
List[typing._Protocol]
with self.assertRaises(TypeError):
isinstance(1, Generic)

def test_type_erasure_special(self):
T = TypeVar('T')
Expand Down Expand Up @@ -1191,6 +1206,19 @@ def test_syntax_error(self):
with self.assertRaises(SyntaxError):
Generic['/T']

def test_forwardref_subclass_type_error(self):
fr = typing._ForwardRef('int')
with self.assertRaises(TypeError):
issubclass(int, fr)

def test_forward_equality(self):
fr = typing._ForwardRef('int')
self.assertEqual(fr, typing._ForwardRef('int'))
self.assertNotEqual(List['int'], List[int])

def test_forward_repr(self):
self.assertEqual(repr(List['int']), "typing.List[_ForwardRef(%r)]" % 'int')


class OverloadTests(BaseTestCase):

Expand Down Expand Up @@ -1674,6 +1702,12 @@ def test_basics(self):
Pattern[Union[str, bytes]]
Match[Union[bytes, str]]

def test_alias_equality(self):
self.assertEqual(Pattern[str], Pattern[str])
self.assertNotEqual(Pattern[str], Pattern[bytes])
self.assertNotEqual(Pattern[str], Match[str])
self.assertNotEqual(Pattern[str], str)

def test_errors(self):
with self.assertRaises(TypeError):
# Doesn't fit AnyStr.
Expand All @@ -1688,6 +1722,9 @@ def test_errors(self):
with self.assertRaises(TypeError):
# We don't support isinstance().
isinstance(42, Pattern[str])
with self.assertRaises(TypeError):
# We don't support issubclass().
issubclass(Pattern[bytes], Pattern[str])

def test_repr(self):
self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]')
Expand Down
22 changes: 19 additions & 3 deletions python2/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,15 @@ def _eval_type(self, globalns, localns):
self.__forward_evaluated__ = True
return self.__forward_value__

def __eq__(self, other):
if not isinstance(other, _ForwardRef):
return NotImplemented
return (self.__forward_arg__ == other.__forward_arg__ and
self.__forward_value__ == other.__forward_value__)

def __hash__(self):
return hash((self.__forward_arg__, self.__forward_value__))

def __instancecheck__(self, obj):
raise TypeError("Forward references cannot be used with isinstance().")

Expand Down Expand Up @@ -282,6 +291,14 @@ def __getitem__(self, parameter):
return self.__class__(self.name, parameter,
self.impl_type, self.type_checker)

def __eq__(self, other):
if not isinstance(other, _TypeAlias):
return NotImplemented
return self.name == other.name and self.type_var == other.type_var

def __hash__(self):
return hash((self.name, self.type_var))

def __instancecheck__(self, obj):
if not isinstance(self.type_var, TypeVar):
raise TypeError("Parameterized type aliases cannot be used "
Expand Down Expand Up @@ -1049,10 +1066,9 @@ def __new__(cls, name, bases, namespace,
# with issubclass() and isinstance() in the same way as their
# collections.abc counterparts (e.g., isinstance([], Iterable)).
if (
# allow overriding
'__subclasshook__' not in namespace and extra or
hasattr(self.__subclasshook__, '__name__') and
self.__subclasshook__.__name__ == '__extrahook__'
# allow overriding
getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
):
self.__subclasshook__ = _make_subclasshook(self)

Expand Down
52 changes: 49 additions & 3 deletions src/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ def test_repr(self):
self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__)
u = Union[int, Employee]
self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__)
T = TypeVar('T')
u = Union[T, int][int]
self.assertEqual(repr(u), repr(int))
u = Union[List[int], int]
self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]')

def test_cannot_subclass(self):
with self.assertRaises(TypeError):
Expand Down Expand Up @@ -308,6 +313,15 @@ def test_union_instance_type_error(self):
with self.assertRaises(TypeError):
isinstance(42, Union[int, str])

def test_no_eval_union(self):
u = Union[int, str]
def f(x: u): ...
self.assertIs(get_type_hints(f)['x'], u)

def test_function_repr_union(self):
def fun() -> int: ...
self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]')

def test_union_str_pattern(self):
# Shouldn't crash; see http://bugs.python.org/issue25390
A = Union[str, Pattern]
Expand Down Expand Up @@ -892,6 +906,8 @@ def test_fail_with_bare_generic(self):
Tuple[Generic[T]]
with self.assertRaises(TypeError):
List[typing._Protocol]
with self.assertRaises(TypeError):
isinstance(1, Generic)

def test_type_erasure_special(self):
T = TypeVar('T')
Expand Down Expand Up @@ -1265,6 +1281,19 @@ def test_forwardref_instance_type_error(self):
with self.assertRaises(TypeError):
isinstance(42, fr)

def test_forwardref_subclass_type_error(self):
fr = typing._ForwardRef('int')
with self.assertRaises(TypeError):
issubclass(int, fr)

def test_forward_equality(self):
fr = typing._ForwardRef('int')
self.assertEqual(fr, typing._ForwardRef('int'))
self.assertNotEqual(List['int'], List[int])

def test_forward_repr(self):
self.assertEqual(repr(List['int']), "typing.List[_ForwardRef('int')]")

def test_union_forward(self):

def foo(a: Union['T']):
Expand Down Expand Up @@ -1472,11 +1501,16 @@ class A:
class B(A):
x: ClassVar[Optional['B']] = None
y: int
b: int
class CSub(B):
z: ClassVar['CSub'] = B()
class G(Generic[T]):
lst: ClassVar[List[T]] = []
class NoneAndForward:
parent: 'NoneAndForward'
meaning: None
class CoolEmployee(NamedTuple):
name: str
cool: int
Expand Down Expand Up @@ -1505,7 +1539,7 @@ def __add__(self, other):
# fake names for the sake of static analysis
ann_module = ann_module2 = ann_module3 = None
A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object
XMeth = XRepr = object
XMeth = XRepr = NoneAndForward = object

gth = get_type_hints

Expand Down Expand Up @@ -1540,6 +1574,8 @@ def test_get_type_hints_classes(self):
{'y': Optional[ann_module.C]})
self.assertEqual(gth(ann_module.S), {'x': str, 'y': str})
self.assertEqual(gth(ann_module.foo), {'x': int})
self.assertEqual(gth(NoneAndForward, globals()),
{'parent': NoneAndForward, 'meaning': type(None)})

@skipUnless(PY36, 'Python 3.6 required')
def test_respect_no_type_check(self):
Expand Down Expand Up @@ -1585,9 +1621,10 @@ def test_get_type_hints_ClassVar(self):
self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__),
{'var': typing.ClassVar[ann_module2.CV]})
self.assertEqual(gth(B, globals()),
{'y': int, 'x': ClassVar[Optional[B]]})
{'y': int, 'x': ClassVar[Optional[B]], 'b': int})
self.assertEqual(gth(CSub, globals()),
{'z': ClassVar[CSub], 'y': int, 'x': ClassVar[Optional[B]]})
{'z': ClassVar[CSub], 'y': int, 'b': int,
'x': ClassVar[Optional[B]]})
self.assertEqual(gth(G), {'lst': ClassVar[List[T]]})


Expand Down Expand Up @@ -2271,6 +2308,12 @@ def test_basics(self):
Pattern[Union[str, bytes]]
Match[Union[bytes, str]]

def test_alias_equality(self):
self.assertEqual(Pattern[str], Pattern[str])
self.assertNotEqual(Pattern[str], Pattern[bytes])
self.assertNotEqual(Pattern[str], Match[str])
self.assertNotEqual(Pattern[str], str)

def test_errors(self):
with self.assertRaises(TypeError):
# Doesn't fit AnyStr.
Expand All @@ -2285,6 +2328,9 @@ def test_errors(self):
with self.assertRaises(TypeError):
# We don't support isinstance().
isinstance(42, Pattern[str])
with self.assertRaises(TypeError):
# We don't support issubclass().
issubclass(Pattern[bytes], Pattern[str])

def test_repr(self):
self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]')
Expand Down
5 changes: 2 additions & 3 deletions src/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -985,10 +985,9 @@ def __new__(cls, name, bases, namespace,
# with issubclass() and isinstance() in the same way as their
# collections.abc counterparts (e.g., isinstance([], Iterable)).
if (
# allow overriding
'__subclasshook__' not in namespace and extra or
hasattr(self.__subclasshook__, '__name__') and
self.__subclasshook__.__name__ == '__extrahook__'
# allow overriding
getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
):
self.__subclasshook__ = _make_subclasshook(self)
if isinstance(extra, abc.ABCMeta):
Expand Down

0 comments on commit 370a137

Please sign in to comment.