8000 Merge pull request #14 from jakkdl/meta-tests · python-trio/flake8-async@18a0d29 · GitHub
[go: up one dir, main page]

Skip to content

Commit 18a0d29

Browse files
authored
Merge pull request #14 from jakkdl/meta-tests
Meta-tests
2 parents 9763448 + 3e356ac commit 18a0d29

File tree

10 files changed

+120
-41
lines changed

10 files changed

+120
-41
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ pip install flake8-trio
2121
- **TRIO100**: a `with trio.fail_after(...):` or `with trio.move_on_after(...):`
2222
context does not contain any `await` statements. This makes it pointless, as
2323
the timeout can only be triggered by a checkpoint.
24-
- **TRIO101** `yield` inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
25-
- **TRIO102** it's unsafe to await inside `finally:` unless you use a shielded
24+
-< 8000 /span> **TRIO101**: `yield` inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling.
25+
- **TRIO102**: it's unsafe to await inside `finally:` unless you use a shielded
2626
cancel scope with a timeout"
27-
- **TRIO105** Calling a trio async function without immediately `await`ing it.
27+
- **TRIO105**: Calling a trio async function without immediately `await`ing it.
28+
- **TRIO106**: trio must be imported with `import trio` for the linter to work

flake8_trio.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ def check_for_trio100(self, node: Union[ast.With, ast.AsyncWith]) -> None:
150150
for item in (i.context_expr for i in node.items):
151151
call = get_trio_scope(item, *cancel_scope_names)
152152
if call and not any(
153-
isinstance(x, checkpoint_node_types) for x in ast.walk(node)
153+
isinstance(x, checkpoint_node_types) and x != node
154+
for x in ast.walk(node)
154155
):
155156
self.problems.append(
156157
make_error(TRIO100, item.lineno, item.col_offset, call)

tests/test_changelog_and_version.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Tests for flake8-trio package metadata."""
22
import re
3+
import unittest
34
from pathlib import Path
4-
from typing import Iterable, NamedTuple
5+
from typing import Dict, Iterable, NamedTuple, Set
56

67
import flake8_trio
78

@@ -43,3 +44,25 @@ def test_version_increments_are_correct():
4344
assert prev[:2] < current[:2], msg
4445
else:
4546
assert current == prev._replace(patch=prev.patch + 1), msg
47+
48+
49+
class test_messages_documented(unittest.TestCase):
50+
def runTest(self):
51+
documented_errors: Dict[str, Set[str]] = {}
52+
for 6D40 filename in (
53+
"CHANGELOG.md",
54+
"README.md",
55+
"flake8_trio.py",
56+
"tests/test_flake8_trio.py",
57+
):
58+
with open(Path(__file__).parent.parent / filename) as f:
59+
lines = f.readlines()
60+
documented_errors[filename] = set()
61+
for line in lines:
62+
for error_msg in re.findall(r"TRIO\d\d\d", line):
63+
documented_errors[filename].add(error_msg)
64+
65+
file_items = list(documented_errors.items())
66+
first = file_items.pop()
67+
for file, documented in file_items:
68+
self.assertSetEqual(first[1], documented, msg=(first[0], file))

tests/test_flake8_trio.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,19 @@ def assert_expected_errors(self, test_file: str, *expected: Error) -> None:
2929
filename = Path(__file__).absolute().parent / test_file
3030
plugin = Plugin.from_filename(str(filename))
3131
errors = tuple(plugin.run())
32-
self.assertEqual(errors, expected)
32+
self.assertTupleEqual(errors, expected)
3333

3434
def test_tree(self):
3535
plugin = Plugin(ast.parse(""))
3636
errors = list(plugin.run())
37-
self.assertEqual(errors, [])
37+
self.assertSequenceEqual(errors, [])
3838

3939
def test_trio100(self):
4040
self.assert_expected_errors(
4141
"trio100.py",
4242
make_error(TRIO100, 3, 5, "trio.move_on_after"),
43-
make_error(TRIO100, 23, 9, "trio.fail_after"),
43+
make_error(TRIO100, 8, 15, "trio.fail_after"),
44+
make_error(TRIO100, 26, 9, "trio.fail_after"),
4445
)
4546

4647
@unittest.skipIf(sys.version_info < (3, 9), "requires 3.9+")
@@ -100,11 +101,11 @@ def test_trio105(self):
100101
make_error(TRIO105, 36, 4, "sleep"),
101102
make_error(TRIO105, 37, 4, "sleep_forever"),
102103
make_error(TRIO105, 38, 4, "sleep_until"),
103-
make_error(TRIO105, 45, 15, "open_file"),
104-
make_error(TRIO105, 50, 8, "open_file"),
104+
make_error(TRIO105, 44, 15, "open_file"),
105+
make_error(TRIO105, 49, 8, "open_file"),
105106
)
106107

107-
self.assertEqual(
108+
self.assertSetEqual(
108109
set(trio_async_functions),
109110
{
110111
o[0]

tests/test_trio_tests.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import inspect
2+
import os
3+
import os.path
4+
import re
5+
import unittest
6+
from types import FunctionType
7+
from typing import Dict, Optional, Set, Tuple
8+
9+
from test_flake8_trio import Flake8TrioTestCase
10+
11+
12+
class TestTrioTests(unittest.TestCase):
13+
def runTest(self):
14+
# get files
15+
trio_tests: Dict[str, Tuple[str, Optional[FunctionType]]] = {
16+
os.path.splitext(f)[0]: (f, None)
17+
for f in os.listdir("tests")
18+
if re.match(r"^trio.*.py", f)
19+
}
20+
21+
# get functions
22+
for o in inspect.getmembers(Flake8TrioTestCase):
23+
if inspect.isfunction(o[1]) and re.match(r"^test_trio\d\d\d", o[0]):
24+
key = o[0][5:]
25+
26+
self.assertIn(key, trio_tests)
27+
self.assertIsNone(trio_tests[key][1], msg=key)
28+
29+
trio_tests[key] = (trio_tests[key][0], o[1])
30+
31+
for test, (filename, func) in sorted(trio_tests.items()):
32+
self.assertIsNotNone(func, msg=test)
33+
assert func is not None # for type checkers
34+
35+
with open(os.path.join("tests", filename), encoding="utf-8") as file:
36+
file_error_lines = {
37+
lineno + 1
38+
for lineno, line in enumerate(file)
39+
if re.search(r"# *error", line, flags=re.I)
40+
}
41+
42+
func_error_lines: Set[int] = set()
43+
for line in inspect.getsourcelines(func)[0]:
44+
m = re.search(r"(?<=make_error\(TRIO\d\d\d, )\d*", line)
45+
if not m:
46+
continue
47+
lineno = int(m.group())
48+
self.assertNotIn(lineno, func_error_lines, msg=test)
49+
func_error_lines.add(lineno)
50+
51+
self.assertSetEqual(file_error_lines, func_error_lines, msg=test)

tests/trio100.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import trio
22

3-
with trio.move_on_after(10):
3+
with trio.move_on_after(10): # error
44
pass
55

66

77
async def function_name():
8+
async with trio.fail_after(10): # error
9+
pass
10+
811
with trio.move_on_after(10):
912
await trio.sleep(1)
1013

@@ -20,7 +23,7 @@ async def function_name():
2023
with open("filename") as _:
2124
pass
2225

23-
with trio.fail_after(10):
26+
with trio.fail_after(10): # error
2427
pass
2528

2629
send_channel, receive_channel = trio.open_memory_channel(0)

tests/trio100_py39.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
async def function_name():
55
with (
66
open("veryverylongfilenamesoshedsplitsthisintotwolines") as _,
7-
trio.fail_after(10),
7+
trio.fail_after(10), # error
88
):
99
pass
1010

1111
with (
12-
trio.fail_after(5),
12+
trio.fail_after(5), # error
1313
open("veryverylongfilenamesoshedsplitsthisintotwolines") as _,
14-
trio.move_on_after(5),
14+
trio.move_on_after(5), # error
1515
):
1616
pass

tests/trio102.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ async def foo():
6767
pass
6868
finally:
6969
with open("bar"):
70-
await foo() # safe
70+
await foo() # error
7171
with open("bar"):
7272
pass
7373
with trio.move_on_after():
@@ -81,11 +81,11 @@ async def foo():
8181
with trio.CancelScope(deadline=30):
8282
await foo() # error
8383
with trio.CancelScope(deadline=30, shield=(1 == 1)):
84-
await foo() # safe in theory, but deemed error
84+
await foo() # error: though safe in theory
8585
myvar = True
8686
with trio.open_nursery(10) as s:
8787
s.shield = myvar
88-
await foo() # safe in theory, but deemed error
88+
await foo() # error: though safe in theory
8989
with trio.CancelScope(deadline=30, shield=True):
9090
with trio.move_on_after(30):
9191
await foo() # safe
@@ -120,4 +120,4 @@ async def foo3():
120120
await foo() # safe
121121
with trio.fail_after(5), trio.move_on_after(30) as s:
122122
s.shield = True
123-
await foo() # safe in theory, but we don't bother parsing
123+
await foo() # error: safe in theory, but we don't bother parsing

tests/trio105.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,30 @@ async def foo():
2121
await trio.sleep_forever()
2222
await trio.sleep_until()
2323

24-
# errors
25-
trio.aclose_forcefully()
26-
trio.open_file()
27-
trio.open_ssl_over_tcp_listeners()
28-
trio.open_ssl_over_tcp_stream()
29-
trio.open_tcp_listeners()
30-
trio.open_tcp_stream()
31-
trio.open_unix_socket()
32-
trio.run_process()
33-
trio.serve_listeners()
34-
trio.serve_ssl_over_tcp()
35-
trio.serve_tcp()
36-
trio.sleep()
37-
trio.sleep_forever()
38-
trio.sleep_until()
24+
# all async functions
25+
trio.aclose_forcefully() # error
26+
trio.open_file() # error
27+
trio.open_ssl_over_tcp_listeners() # error
28+
trio.open_ssl_over_tcp_stream() # error
29+
trio.open_tcp_listeners() # error
30+
trio.open_tcp_stream() # error
31+
trio.open_unix_socket() # error
32+
trio.run_process() # error
33+
trio.serve_listeners() # error
34+
trio.serve_ssl_over_tcp() # error
35+
trio.serve_tcp() # error
36+
trio.sleep() # error
37+
trio.sleep_forever() # error
38+
trio.sleep_until() # error
3939

4040
# safe
4141
async with await trio.open_file() as f:
4242
pass
4343

44-
# error
45-
async with trio.open_file() as f:
44+
async with trio.open_file() as f: # error
4645
pass
4746

4847
# safe in theory, but deemed sufficiently poor style that parsing
4948
# it isn't supported
50-
k = trio.open_file()
49+
k = trio.open_file() # error
5150
await k

tests/trio106.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import importlib
22

33
import trio
4-
import trio as foo
5-
from trio import * # noqa
6-
from trio import blah, open_file as foo # noqa
4+
import trio as foo # error
5+
from trio import * # noqa # error
6+
from trio import blah, open_file as foo # noqa # error
77

88
# Note that our tests exercise the Visitor classes, without going through the noqa filter later in flake8 - so these suppressions are picked up by our project-wide linter stack but not the tests.
99

0 commit comments

Comments
 (0)
0