Skip to content

Commit 4a17549

Browse files
msullivanelprans
authored andcommitted
Add in mypy tests
1 parent 39f9f0d commit 4a17549

File tree

6 files changed

+114
-4
lines changed

6 files changed

+114
-4
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
max-parallel: 4
1717
matrix:
18-
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
18+
python-version: [3.6, 3.7, 3.8, 3.9]
1919
os: [windows-latest, ubuntu-18.04, macos-latest]
2020
exclude:
2121
# Python 3.5 is unable to properly
@@ -52,4 +52,4 @@ jobs:
5252
pip install -e .[test]
5353
flake8 immutables/ tests/
5454
mypy immutables/
55-
python -m unittest -v tests.suite
55+
python -m pytest -v

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ debug:
1414
DEBUG_IMMUTABLES=1 $(PYTHON) setup.py build_ext --inplace
1515

1616
test:
17-
$(PYTHON) -m unittest -v
17+
$(PYTHON) -m pytest -v
1818

1919
rtest:
2020
~/dev/venvs/36-debug/bin/python setup.py build_ext --inplace

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
# (example breakage: https://gitlab.com/pycqa/flake8/issues/427)
1111
'flake8~=3.8.4',
1212
'pycodestyle~=2.6.0',
13-
'mypy>=0.800',
13+
'mypy>=0.910',
14+
'pytest~=6.2.4',
1415
]
1516

1617
EXTRA_DEPENDENCIES = {

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# We need the mypy pytest plugin to do the test collection for our
2+
# typing tests.
3+
4+
# mypy demands that its test-data be present for mypy.test.config to be
5+
# imported, so thwart that check. mypy PR #10919 fixes this.
6+
import unittest.mock
7+
with unittest.mock.patch('os.path.isdir') as isdir:
8+
isdir.return_value = True
9+
import mypy.test.config # noqa
10+
11+
pytest_plugins = [
12+
'mypy.test.data',
13+
]

tests/test-data/check-immu.test

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
[case testMypyImmu]
2+
# cmd: mypy test.py
3+
[file test.py]
4+
from immutables import Map
5+
from typing import Dict, Union, Any, cast
6+
7+
def init() -> None:
8+
def thing(m: Map[str, Union[str, int]]) -> None:
9+
...
10+
11+
thing(Map(foo=1))
12+
thing(Map(foo='bar', baz=1))
13+
thing(Map([('foo', 'bar'), ('bar', 1)]))
14+
thing(Map(Map(foo=1), bar='foo'))
15+
m = Map({1: 2})
16+
thing(m) # E: Argument 1 to "thing" has incompatible type "Map[int, int]"; expected "Map[str, Union[str, int]]"
17+
18+
def assignments() -> None:
19+
m_int__str = Map[int, str]()
20+
m_str__str = Map[str, str]()
21+
m_int_str__str = Map[Union[int, str], str]()
22+
m_str__int_str = Map[str, Union[int, str]]()
23+
24+
m_int__str = m_str__str # E: Incompatible types in assignment (expression has type "Map[str, str]", variable has type "Map[int, str]")
25+
m_int__str = m_int_str__str # E: Incompatible types in assignment (expression has type "Map[Union[int, str], str]", variable has type "Map[int, str]")
26+
m_int__str = m_str__int_str # E: Incompatible types in assignment (expression has type "Map[str, Union[int, str]]", variable has type "Map[int, str]")
27+
28+
m_str__str = m_int__str # E: Incompatible types in assignment (expression has type "Map[int, str]", variable has type "Map[str, str]")
29+
m_str__str = m_int_str__str # E: Incompatible types in assignment (expression has type "Map[Union[int, str], str]", variable has type "Map[str, str]")
30+
m_str__str = m_str__int_str # E: Incompatible types in assignment (expression has type "Map[str, Union[int, str]]", variable has type "Map[str, str]")
31+
32+
m_int_str__str = m_int__str # E: Incompatible types in assignment (expression has type "Map[int, str]", variable has type "Map[Union[int, str], str]")
33+
m_int_str__str = m_str__str # E: Incompatible types in assignment (expression has type "Map[str, str]", variable has type "Map[Union[int, str], str]")
34+
m_int_str__str = m_str__int_str # E: Incompatible types in assignment (expression has type "Map[str, Union[int, str]]", variable has type "Map[Union[int, str], str]")
35+
36+
m_str__int_str = m_int__str # E: Incompatible types in assignment (expression has type "Map[int, str]", variable has type "Map[str, Union[int, str]]")
37+
m_str__int_str = m_int_str__str # E: Incompatible types in assignment (expression has type "Map[Union[int, str], str]", variable has type "Map[str, Union[int, str]]")
38+
m_str__int_str = m_str__str
39+
40+
def update() -> None:
41+
m_int__str: Map[int, str] = Map()
42+
m_str__str: Map[str, str] = Map()
43+
m_int_str__str: Map[Union[int, str], str] = Map()
44+
m_str__int_str: Map[str, Union[int, str]] = Map()
45+
46+
m_int__str.update({1: '2'})
47+
m_int__str.update({1: '2'}, three='4') # E: Unexpected keyword argument "three" for "update" of "Map"
48+
m_int__str.update({1: 2}) # E: Argument 1 to "update" of "Map" has incompatible type "Dict[int, int]"; expected "Union[IterableItems[int, str], Iterable[Tuple[int, str]]]"
49+
50+
m_str__str.update({'1': '2'})
51+
m_str__str.update({'1': '2'}, three='4')
52+
m_str__str.update({'1': 2}) # E: Argument 1 to "update" of "Map" has incompatible type "Dict[str, int]"; expected "Union[IterableItems[str, str], Iterable[Tuple[str, str]]]"
53+
54+
m_int_str__str.update(cast(Dict[Union[int, str], str], {1: '2', '3': '4'}))
55+
m_int_str__str.update({1: '2'}, three='4')
56+
m_int_str__str.update({'1': 2}) # E: Argument 1 to "update" of "Map" has incompatible type "Dict[str, int]"; expected "Union[IterableItems[Union[int, str], str], Iterable[Tuple[Union[int, str], str]]]"
57+
58+
m_str__int_str.update({'1': 2, '2': 3})
59+
m_str__int_str.update({'1': 2, '2': 3}, four='5')
60+
m_str__int_str.update({1: 2}) # E: Argument 1 to "update" of "Map" has incompatible type "Dict[int, int]"; expected "Union[IterableItems[str, Union[int, str]], Iterable[Tuple[str, Union[int, str]]]]"
61+
62+
def mutate() -> None:
63+
m = Map[str, str]()
64+
65+
with m.mutate() as mm:
66+
mm[0] = '1' # E: Invalid index type "int" for "MapMutation[str, str]"; expected type "str"
67+
mm['1'] = 0 # E: Incompatible types in assignment (expression has type "int", target has type "str")
68+
mm['1'] = '2'
69+
del mm['1']
70+
mm.set('3', '4')
71+
m2 = mm.finish()
72+
73+
reveal_type(m2) # N: Revealed type is "immutables._map.Map[builtins.str*, builtins.str*]"

tests/test_mypy.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
import mypy.test.testcmdline
3+
from mypy.test.helpers import normalize_error_messages
4+
5+
6+
# I'm upset. There's no other way to deal with the little 'defined here'
7+
# notes that mypy emits when passing an unexpected keyword argument
8+
# and at no other time.
9+
def renormalize_error_messages(messages):
10+
messages = [x for x in messages if not x.endswith(' defined here')]
11+
return normalize_error_messages(messages)
12+
13+
14+
mypy.test.testcmdline.normalize_error_messages = renormalize_error_messages
15+
16+
17+
this_file_dir = os.path.dirname(os.path.realpath(__file__))
18+
test_data_prefix = os.path.join(this_file_dir, 'test-data')
19+
20+
21+
class ImmuMypyTest(mypy.test.testcmdline.PythonCmdlineSuite):
22+
data_prefix = test_data_prefix
23+
files = ['check-immu.test']

0 commit comments

Comments
 (0)