Skip to content

Add tests and enable travis #3

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

Merged
merged 11 commits into from
Jan 23, 2019
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
46 changes: 46 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
language: python
# cache package wheels (1 cache per python version)
cache: pip
# newer python versions are available only on xenial (while some older only on trusty) Ubuntu distribution
dist: trusty
sudo: required

env:
TOXENV=py

jobs:
include:
- python: 2.7
- python: 3.4
- python: 3.5.0
- python: 3.5.1
- python: 3.5.2
- python: 3.5.3
- python: 3.5
- python: 3.6.0
- python: 3.6.1
- python: 3.6.2
- python: 3.6
dist: xenial
- python: 3.7.0
dist: xenial
- python: 3.7.1
dist: xenial
- python: 3.7
dist: xenial
- python: 3.8-dev
dist: xenial

- name: "check code style with flake8"
python: 3.7
dist: xenial
env:
- TOXENV=lint

install:
- pip install -U pip setuptools
- pip install -U tox
- tox --notest

script:
- tox
Copy link
Member

Choose a reason for hiding this comment

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

Since this is a "runtime" package, I would rather use the Travis settings from python/typing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Which things from typing do you prefer? Is it just running on a bunch more versions, or do you want to not use tox?

Copy link
Member

Choose a reason for hiding this comment

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

Actually both (but first is probably more important).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What's the reason to avoid tox? (I don't have any real opinion one way or another except that the tox version works already :P)

Copy link
Member

Choose a reason for hiding this comment

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

I just don't want to learn something the need for what I don't understand. But since it already works, then OK.

142 changes: 142 additions & 0 deletions tests/testextensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import sys
import pickle
import typing
from unittest import TestCase, main, skipUnless
from mypy_extensions import TypedDict


class BaseTestCase(TestCase):

def assertIsSubclass(self, cls, class_or_tuple, msg=None):
if not issubclass(cls, class_or_tuple):
message = '%r is not a subclass of %r' % (cls, class_or_tuple)
if msg is not None:
message += ' : %s' % msg
raise self.failureException(message)

def assertNotIsSubclass(self, cls, class_or_tuple, msg=None):
if issubclass(cls, class_or_tuple):
message = '%r is a subclass of %r' % (cls, class_or_tuple)
if msg is not None:
message += ' : %s' % msg
raise self.failureException(message)


PY36 = sys.version_info[:2] >= (3, 6)

PY36_TESTS = """
Label = TypedDict('Label', [('label', str)])

class Point2D(TypedDict):
x: int
y: int

class LabelPoint2D(Point2D, Label): ...

class Options(TypedDict, total=False):
log_level: int
log_path: str
"""

if PY36:
exec(PY36_TESTS)


class TypedDictTests(BaseTestCase):

def test_basics_iterable_syntax(self):
Emp = TypedDict('Emp', {'name': str, 'id': int})
self.assertIsSubclass(Emp, dict)
self.assertIsSubclass(Emp, typing.MutableMapping)
if sys.version_info[0] >= 3:
import collections.abc
self.assertNotIsSubclass(Emp, collections.abc.Sequence)
jim = Emp(name='Jim', id=1)
self.assertIs(type(jim), dict)
self.assertEqual(jim['name'], 'Jim')
self.assertEqual(jim['id'], 1)
self.assertEqual(Emp.__name__, 'Emp')
self.assertEqual(Emp.__module__, __name__)
self.assertEqual(Emp.__bases__, (dict,))
self.assertEqual(Emp.__annotations__, {'name': str, 'id': int})
self.assertEqual(Emp.__total__, True)

def test_basics_keywords_syntax(self):
Emp = TypedDict('Emp', name=str, id=int)
self.assertIsSubclass(Emp, dict)
self.assertIsSubclass(Emp, typing.MutableMapping)
if sys.version_info[0] >= 3:
import collections.abc
self.assertNotIsSubclass(Emp, collections.abc.Sequence)
jim = Emp(name='Jim', id=1) # type: ignore # mypy doesn't support keyword syntax yet
self.assertIs(type(jim), dict)
self.assertEqual(jim['name'], 'Jim')
self.assertEqual(jim['id'], 1)
self.assertEqual(Emp.__name__, 'Emp')
self.assertEqual(Emp.__module__, __name__)
self.assertEqual(Emp.__bases__, (dict,))
self.assertEqual(Emp.__annotations__, {'name': str, 'id': int})
self.assertEqual(Emp.__total__, True)

def test_typeddict_errors(self):
Emp = TypedDict('Emp', {'name': str, 'id': int})
self.assertEqual(TypedDict.__module__, 'mypy_extensions')
jim = Emp(name='Jim', id=1)
with self.assertRaises(TypeError):
isinstance({}, Emp) # type: ignore
with self.assertRaises(TypeError):
isinstance(jim, Emp) # type: ignore
with self.assertRaises(TypeError):
issubclass(dict, Emp) # type: ignore
with self.assertRaises(TypeError):
TypedDict('Hi', x=1)
with self.assertRaises(TypeError):
TypedDict('Hi', [('x', int), ('y', 1)])
with self.assertRaises(TypeError):
TypedDict('Hi', [('x', int)], y=int)

@skipUnless(PY36, 'Python 3.6 required')
def test_py36_class_syntax_usage(self):
self.assertEqual(LabelPoint2D.__annotations__, {'x': int, 'y': int, 'label': str}) # noqa
self.assertEqual(LabelPoint2D.__bases__, (dict,)) # noqa
self.assertEqual(LabelPoint2D.__total__, True) # noqa
self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) # noqa
not_origin = Point2D(x=0, y=1) # noqa
self.assertEqual(not_origin['x'], 0)
self.assertEqual(not_origin['y'], 1)
other = LabelPoint2D(x=0, y=1, label='hi') # noqa
self.assertEqual(other['label'], 'hi')

def test_pickle(self):
global EmpD # pickle wants to reference the class by name
EmpD = TypedDict('EmpD', name=str, id=int)
jane = EmpD({'name': 'jane', 'id': 37})
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
z = pickle.dumps(jane, proto)
jane2 = pickle.loads(z)
self.assertEqual(jane2, jane)
self.assertEqual(jane2, {'name': 'jane', 'id': 37})
ZZ = pickle.dumps(EmpD, proto)
EmpDnew = pickle.loads(ZZ)
self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane)

def test_optional(self):
EmpD = TypedDict('EmpD', name=str, id=int)

self.assertEqual(typing.Optional[EmpD], typing.Union[None, EmpD])
self.assertNotEqual(typing.List[EmpD], typing.Tuple[EmpD])

def test_total(self):
D = TypedDict('D', {'x': int}, total=False)
self.assertEqual(D(), {})
self.assertEqual(D(x=1), {'x': 1})
self.assertEqual(D.__total__, False)

if PY36:
self.assertEqual(Options(), {}) # noqa
self.assertEqual(Options(log_level=2), {'log_level': 2}) # noqa
self.assertEqual(Options.__total__, False) # noqa


if __name__ == '__main__':
main()
20 changes: 20 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[tox]
minversion = 2.9.1
skip_missing_interpreters = true
envlist = py27, py34, py35, py36, py37

[testenv]
description = run the test driver with {basepython}
commands = python -m unittest discover tests

[testenv:lint]
description = check the code style
basepython = python3.7
deps = flake8
commands = flake8 -j0 {posargs}

[flake8]
max-line-length = 99
ignore =
# multiple statements on one line (colon) (we use this for classes with empty body)
E701