From 904764e49bf3b3d32e18f189c09c4a78f8491e3e Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 22 Jan 2019 20:06:15 -0500 Subject: [PATCH 01/11] Copy testextensions in and fix it to be standalone --- tests/testextensions.py | 139 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/testextensions.py diff --git a/tests/testextensions.py b/tests/testextensions.py new file mode 100644 index 0000000..047ed85 --- /dev/null +++ b/tests/testextensions.py @@ -0,0 +1,139 @@ +import sys +import pickle +import typing +import collections.abc +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) + 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) + 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() From 353fdc69efcaef74cfb3ffd9ef8d8769ab66af2c Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 22 Jan 2019 20:07:10 -0500 Subject: [PATCH 02/11] Make the tests run under python 2 --- tests/testextensions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/testextensions.py b/tests/testextensions.py index 047ed85..a76f565 100644 --- a/tests/testextensions.py +++ b/tests/testextensions.py @@ -1,7 +1,6 @@ import sys import pickle import typing -import collections.abc from unittest import TestCase, main, skipUnless from mypy_extensions import TypedDict @@ -49,7 +48,9 @@ def test_basics_iterable_syntax(self): Emp = TypedDict('Emp', {'name': str, 'id': int}) self.assertIsSubclass(Emp, dict) self.assertIsSubclass(Emp, typing.MutableMapping) - self.assertNotIsSubclass(Emp, collections.abc.Sequence) + 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') @@ -64,7 +65,9 @@ def test_basics_keywords_syntax(self): Emp = TypedDict('Emp', name=str, id=int) self.assertIsSubclass(Emp, dict) self.assertIsSubclass(Emp, typing.MutableMapping) - self.assertNotIsSubclass(Emp, collections.abc.Sequence) + 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') From eeb1ca54c9cecfe2b058d1e31f4756083dea5455 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 22 Jan 2019 21:00:05 -0500 Subject: [PATCH 03/11] Add a tox.ini to drive tests --- tox.ini | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tox.ini diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..fe02312 --- /dev/null +++ b/tox.ini @@ -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 From 41c0deb507eb6f594e226cc80f2ae06b11c2f9c2 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 22 Jan 2019 21:00:31 -0500 Subject: [PATCH 04/11] Enable travis --- .travis.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a73c8dd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,39 @@ +# we only CI the master, release branches, tags and PRs +if: tag IS present OR type = pull_request OR ((branch = master OR branch =~ release-*) AND type = push) + +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: xenial +sudo: required + +env: + TOXENV=py + +jobs: + include: + - name: "run test suite with python 3.4" + python: 3.4 + dist: trusty + - name: "run test suite with python 3.5" + python: 3.5.1 + dist: trusty + - name: "run test suite with python 3.6" + python: 3.6 + - name: "run test suite with python 3.7" + python: 3.7 + - name: "run test suite with python 3.8-dev" + python: 3.8-dev + - name: "check code style with flake8" + python: 3.7 + env: + - TOXENV=lint + +install: +- pip install -U pip setuptools +- pip install -U tox +- tox --notest + +script: +- tox From a2d5773ec4a13507523490842398b116efab3f54 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 22 Jan 2019 21:13:13 -0500 Subject: [PATCH 05/11] No real reason to not CI branches before PRs are created --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a73c8dd..d807342 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,3 @@ -# we only CI the master, release branches, tags and PRs -if: tag IS present OR type = pull_request OR ((branch = master OR branch =~ release-*) AND type = push) - language: python # cache package wheels (1 cache per python version) cache: pip From 5ea79a70d4315602ad7ce332fa1ce326f7fdad86 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Tue, 22 Jan 2019 22:13:55 -0800 Subject: [PATCH 06/11] trigger a build? From a598c33cd837074bba62fad8f7c33ad706036727 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 23 Jan 2019 15:13:03 -0800 Subject: [PATCH 07/11] Run on a bunch more versions --- .travis.yml | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index d807342..4e4704c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,18 +10,28 @@ env: jobs: include: - - name: "run test suite with python 3.4" - python: 3.4 + - python: 2.7 + - python: 3.4 dist: trusty - - name: "run test suite with python 3.5" - python: 3.5.1 + - python: 3.5.0 dist: trusty - - name: "run test suite with python 3.6" - python: 3.6 - - name: "run test suite with python 3.7" - python: 3.7 - - name: "run test suite with python 3.8-dev" - python: 3.8-dev + - python: 3.5.1 + dist: trusty + - python: 3.5.2 + dist: trusty + - python: 3.5.3 + dist: trusty + - python: 3.5 + dist: trusty + - python: 3.6.0 + - python: 3.6.1 + - python: 3.6.2 + - python: 3.6 + - python: 3.7.0 + - python: 3.7.1 + - python: 3.7 + - python: 3.8-dev + - name: "check code style with flake8" python: 3.7 env: From d06b240b125bca06d6696b014e887aadcad880b3 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 23 Jan 2019 15:15:40 -0800 Subject: [PATCH 08/11] trigger a build? From 18ef7ae973edf4799d1a35fad2b51fb58d90691f Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 23 Jan 2019 15:22:23 -0800 Subject: [PATCH 09/11] tweak dist --- .travis.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4e4704c..00f5619 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ 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: xenial +dist: trusty sudo: required env: @@ -12,25 +12,24 @@ jobs: include: - python: 2.7 - python: 3.4 - dist: trusty - python: 3.5.0 - dist: trusty - python: 3.5.1 - dist: trusty - python: 3.5.2 - dist: trusty - python: 3.5.3 - dist: trusty - python: 3.5 - dist: trusty - 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 From 88e388e7a64edb748b137f2166319343a643ca9d Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 23 Jan 2019 15:26:17 -0800 Subject: [PATCH 10/11] asdf --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 00f5619..7d150e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ jobs: - name: "check code style with flake8" python: 3.7 + dist: xenial env: - TOXENV=lint From d7cbae41078c944853ed4ba544dec9e6606092a1 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 23 Jan 2019 15:30:35 -0800 Subject: [PATCH 11/11] trigger a build?