flake8-async 25.3.1__tar.gz → 25.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flake8_async-25.3.1 → flake8_async-25.4.2}/PKG-INFO +2 -2
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/__init__.py +1 -1
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/helpers.py +13 -3
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor102_120.py +2 -2
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor103_104.py +18 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor91x.py +67 -1
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor_utility.py +1 -1
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitors.py +45 -4
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/PKG-INFO +2 -2
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/SOURCES.txt +4 -0
- flake8_async-25.4.2/tests/autofix_files/async91x_py310.py +90 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/check_changelog_and_version.py +3 -1
- flake8_async-25.4.2/tests/eval_files/async103_104_py310.py +58 -0
- flake8_async-25.4.2/tests/eval_files/async125.py +33 -0
- flake8_async-25.4.2/tests/eval_files/async91x_py310.py +86 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_flake8_async.py +1 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/CHANGELOG.md +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/CONTRIBUTING.md +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/LICENSE +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/MANIFEST.in +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/README.md +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/__main__.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/base.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/runner.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/__init__.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/flake8asyncvisitor.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor101.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor105.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor111.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor118.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor123.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async/visitors/visitor2xx.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/dependency_links.txt +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/entry_points.txt +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/not-zip-safe +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/requires.txt +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/flake8_async.egg-info/top_level.txt +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/pyproject.toml +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/setup.cfg +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/setup.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/__init__.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async100.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async100_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async100_simple_autofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async124.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async910.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async910_insert_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async911.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async911_insert_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async913.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async913_trio_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async91x_autofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/async91x_py311.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/exception_suppress_context_manager.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/exception_suppress_context_manager_import_star.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/noqa.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/autofix_files/noqa_testing.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/conftest.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/anyio_trio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async100.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async100_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async100_noautofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async100_simple_autofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async101.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async101_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102_120_py311.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102_aclose.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102_aclose_args.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async102_trio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async103.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async103_104_py311.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async103_all_imported.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async103_both_imported.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async103_no_104.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async103_trio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async104.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async104_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async104_trio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async105.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async105_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async106.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async109.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async110.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async111.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async111_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async112.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async112_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async113.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async113_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async113_trio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async114.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async115.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async116.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async118.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async119.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async120.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async121.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async121_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async121_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async122.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async123.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async123_py311.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async124.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async124_no_autofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async200.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async210.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async211.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async212.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async22x.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async22x_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async232.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async232_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async23x.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async23x_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async240.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async250.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async250_multi_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async251.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async251_multi_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async300.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async900.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async910.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async910_insert_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async911.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async911_insert_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async912.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async912_asyncio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async912_py311.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async913.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async913_trio_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async91x_autofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async91x_noautofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/async91x_py311.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/exception_suppress_context_manager.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/exception_suppress_context_manager_import_star.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/no_library.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/noqa.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/noqa_no_autofix.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/noqa_testing.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/trio_anyio.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_all_visitors_imported.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_config_and_args.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_decorator.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_exception_on_invalid_code.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_formatting.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/test_messages_documented.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tests/trio_options.py +0 -0
- {flake8_async-25.3.1 → flake8_async-25.4.2}/tox.ini +0 -0
|
@@ -6,6 +6,7 @@ Also contains the decorator definitions used to register error classes.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import ast
|
|
9
|
+
from dataclasses import dataclass
|
|
9
10
|
from fnmatch import fnmatch
|
|
10
11
|
from typing import TYPE_CHECKING, NamedTuple, TypeVar, Union
|
|
11
12
|
|
|
@@ -287,11 +288,20 @@ cancel_scope_names = (
|
|
|
287
288
|
)
|
|
288
289
|
|
|
289
290
|
|
|
291
|
+
@dataclass
|
|
292
|
+
class MatchingCall:
|
|
293
|
+
node: ast.Call
|
|
294
|
+
name: str
|
|
295
|
+
base: str
|
|
296
|
+
|
|
297
|
+
def __str__(self) -> str:
|
|
298
|
+
return self.base + "." + self.name
|
|
299
|
+
|
|
300
|
+
|
|
290
301
|
# convenience function used in a lot of visitors
|
|
291
|
-
# should probably return a named tuple
|
|
292
302
|
def get_matching_call(
|
|
293
303
|
node: ast.AST, *names: str, base: Iterable[str] = ("trio", "anyio")
|
|
294
|
-
) ->
|
|
304
|
+
) -> MatchingCall | None:
|
|
295
305
|
if isinstance(base, str):
|
|
296
306
|
base = (base,)
|
|
297
307
|
if (
|
|
@@ -301,7 +311,7 @@ def get_matching_call(
|
|
|
301
311
|
and node.func.value.id in base
|
|
302
312
|
and node.func.attr in names
|
|
303
313
|
):
|
|
304
|
-
return node, node.func.attr, node.func.value.id
|
|
314
|
+
return MatchingCall(node, node.func.attr, node.func.value.id)
|
|
305
315
|
return None
|
|
306
316
|
|
|
307
317
|
|
|
@@ -34,7 +34,7 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
class TrioScope:
|
|
37
|
-
def __init__(self, node: ast.Call, funcname: str
|
|
37
|
+
def __init__(self, node: ast.Call, funcname: str):
|
|
38
38
|
super().__init__()
|
|
39
39
|
self.node = node
|
|
40
40
|
self.funcname = funcname
|
|
@@ -126,7 +126,7 @@ class Visitor102(Flake8AsyncVisitor):
|
|
|
126
126
|
if call is None:
|
|
127
127
|
continue
|
|
128
128
|
|
|
129
|
-
trio_scope = self.TrioScope(
|
|
129
|
+
trio_scope = self.TrioScope(call.node, call.name)
|
|
130
130
|
# check if it's saved in a variable
|
|
131
131
|
if isinstance(item.optional_vars, ast.Name):
|
|
132
132
|
trio_scope.variable_name = item.optional_vars.id
|
|
@@ -199,6 +199,24 @@ class Visitor103_104(Flake8AsyncVisitor):
|
|
|
199
199
|
# if body didn't raise, or it's unraised after else, set unraise
|
|
200
200
|
self.unraised = not body_raised or self.unraised
|
|
201
201
|
|
|
202
|
+
def visit_Match(self, node: ast.Match): # type: ignore[name-defined]
|
|
203
|
+
if not self.unraised:
|
|
204
|
+
return
|
|
205
|
+
all_cases_raise = True
|
|
206
|
+
has_fallback = False
|
|
207
|
+
for case in node.cases:
|
|
208
|
+
# check for "bare pattern", i.e `case varname:`
|
|
209
|
+
has_fallback |= (
|
|
210
|
+
case.guard is None
|
|
211
|
+
and isinstance(case.pattern, ast.MatchAs) # type: ignore[attr-defined]
|
|
212
|
+
and case.pattern.pattern is None
|
|
213
|
+
)
|
|
214
|
+
self.visit_nodes(case.body)
|
|
215
|
+
all_cases_raise &= not self.unraised
|
|
216
|
+
self.unraised = True
|
|
217
|
+
|
|
218
|
+
self.unraised = not (all_cases_raise and has_fallback)
|
|
219
|
+
|
|
202
220
|
# A loop is guaranteed to raise if:
|
|
203
221
|
# condition always raises, or
|
|
204
222
|
# else always raises, and
|
|
@@ -198,6 +198,22 @@ class TryState:
|
|
|
198
198
|
)
|
|
199
199
|
|
|
200
200
|
|
|
201
|
+
@dataclass
|
|
202
|
+
class MatchState:
|
|
203
|
+
# TryState, LoopState, and MatchState all do fairly similar things. It would be nice
|
|
204
|
+
# to harmonize them and share logic.
|
|
205
|
+
base_uncheckpointed_statements: set[Statement] = field(default_factory=set)
|
|
206
|
+
case_uncheckpointed_statements: set[Statement] = field(default_factory=set)
|
|
207
|
+
has_fallback: bool = False
|
|
208
|
+
|
|
209
|
+
def copy(self):
|
|
210
|
+
return MatchState(
|
|
211
|
+
base_uncheckpointed_statements=self.base_uncheckpointed_statements.copy(),
|
|
212
|
+
case_uncheckpointed_statements=self.case_uncheckpointed_statements.copy(),
|
|
213
|
+
has_fallback=self.has_fallback,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
201
217
|
def checkpoint_statement(library: str) -> cst.SimpleStatementLine:
|
|
202
218
|
# logic before this should stop code from wanting to insert the non-existing
|
|
203
219
|
# asyncio.lowlevel.checkpoint
|
|
@@ -373,6 +389,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
373
389
|
|
|
374
390
|
self.loop_state = LoopState()
|
|
375
391
|
self.try_state = TryState()
|
|
392
|
+
self.match_state = MatchState()
|
|
376
393
|
|
|
377
394
|
# ASYNC100
|
|
378
395
|
self.has_checkpoint_stack: list[bool] = []
|
|
@@ -500,7 +517,7 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
500
517
|
|
|
501
518
|
# visit body
|
|
502
519
|
# we're not gonna get FlattenSentinel or RemovalSentinel
|
|
503
|
-
self.new_body = cast(cst.BaseSuite, node.body.visit(self))
|
|
520
|
+
self.new_body = cast("cst.BaseSuite", node.body.visit(self))
|
|
504
521
|
|
|
505
522
|
# we know that leave_FunctionDef for this FunctionDef will run immediately after
|
|
506
523
|
# this function exits so we don't need to worry about save_state for new_body
|
|
@@ -894,6 +911,55 @@ class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors):
|
|
|
894
911
|
self.leave_If(node, node) # type: ignore
|
|
895
912
|
return False # libcst shouldn't visit subnodes again
|
|
896
913
|
|
|
914
|
+
def leave_Match_subject(self, node: cst.Match) -> None:
|
|
915
|
+
# We start the match logic after parsing the subject, instead of visit_Match,
|
|
916
|
+
# since the subject is always executed and might checkpoint.
|
|
917
|
+
if not self.async_function:
|
|
918
|
+
return
|
|
919
|
+
self.save_state(node, "match_state", copy=True)
|
|
920
|
+
self.match_state = MatchState(
|
|
921
|
+
base_uncheckpointed_statements=self.uncheckpointed_statements.copy()
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
def visit_MatchCase(self, node: cst.MatchCase) -> None:
|
|
925
|
+
# enter each case from the state after parsing the subject
|
|
926
|
+
self.uncheckpointed_statements = self.match_state.base_uncheckpointed_statements
|
|
927
|
+
|
|
928
|
+
def leave_MatchCase_guard(self, node: cst.MatchCase) -> None:
|
|
929
|
+
# `case _:` is no pattern and no guard, which means we know body is executed.
|
|
930
|
+
# But we also know that `case _ if <guard>:` is guaranteed to execute the guard,
|
|
931
|
+
# so for later logic we can treat them the same *if* there's no pattern and that
|
|
932
|
+
# guard checkpoints.
|
|
933
|
+
if (
|
|
934
|
+
isinstance(node.pattern, cst.MatchAs)
|
|
935
|
+
and node.pattern.pattern is None
|
|
936
|
+
and (node.guard is None or not self.uncheckpointed_statements)
|
|
937
|
+
):
|
|
938
|
+
self.match_state.has_fallback = True
|
|
939
|
+
|
|
940
|
+
def leave_MatchCase(
|
|
941
|
+
self, original_node: cst.MatchCase, updated_node: cst.MatchCase
|
|
942
|
+
) -> cst.MatchCase:
|
|
943
|
+
# collect the state at the end of each case
|
|
944
|
+
self.match_state.case_uncheckpointed_statements.update(
|
|
945
|
+
self.uncheckpointed_statements
|
|
946
|
+
)
|
|
947
|
+
return updated_node
|
|
948
|
+
|
|
949
|
+
def leave_Match(
|
|
950
|
+
self, original_node: cst.Match, updated_node: cst.Match
|
|
951
|
+
) -> cst.Match:
|
|
952
|
+
# leave the Match with the worst-case of all branches
|
|
953
|
+
self.uncheckpointed_statements = self.match_state.case_uncheckpointed_statements
|
|
954
|
+
# if no fallback, also add the state at entering the match (after parsing subject)
|
|
955
|
+
if not self.match_state.has_fallback:
|
|
956
|
+
self.uncheckpointed_statements.update(
|
|
957
|
+
self.match_state.base_uncheckpointed_statements
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
self.restore_state(original_node)
|
|
961
|
+
return updated_node
|
|
962
|
+
|
|
897
963
|
def visit_While(self, node: cst.While | cst.For):
|
|
898
964
|
self.save_state(
|
|
899
965
|
node,
|
|
@@ -287,8 +287,7 @@ class Visitor115(Flake8AsyncVisitor):
|
|
|
287
287
|
and isinstance(node.args[0], ast.Constant)
|
|
288
288
|
and node.args[0].value == 0
|
|
289
289
|
):
|
|
290
|
-
|
|
291
|
-
self.error(node, m[2])
|
|
290
|
+
self.error(node, m.base)
|
|
292
291
|
|
|
293
292
|
|
|
294
293
|
@error_class
|
|
@@ -326,7 +325,7 @@ class Visitor116(Flake8AsyncVisitor):
|
|
|
326
325
|
and arg.value > 86400
|
|
327
326
|
)
|
|
328
327
|
):
|
|
329
|
-
self.error(node, m
|
|
328
|
+
self.error(node, m.base)
|
|
330
329
|
|
|
331
330
|
|
|
332
331
|
@error_class
|
|
@@ -452,7 +451,49 @@ class Visitor122(Flake8AsyncVisitor):
|
|
|
452
451
|
node, "fail_after", "move_on_after", base=("trio", "anyio")
|
|
453
452
|
)
|
|
454
453
|
):
|
|
455
|
-
self.error(node,
|
|
454
|
+
self.error(node, str(match))
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@error_class
|
|
458
|
+
class Visitor125(Flake8AsyncVisitor):
|
|
459
|
+
error_codes: Mapping[str, str] = {
|
|
460
|
+
"ASYNC125": (
|
|
461
|
+
"Using {} with a constant value is nonsensical, as the value is relative "
|
|
462
|
+
"to the runner clock. Use ``fail_after(...)``, ``move_on_after(...)``, "
|
|
463
|
+
"``CancelScope(relative_deadline=...)`` or calculate it relative to "
|
|
464
|
+
"``{}.current_time()``."
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
def visit_Call(self, node: ast.Call):
|
|
469
|
+
def is_constant(value: ast.expr) -> bool:
|
|
470
|
+
if isinstance(value, ast.Constant):
|
|
471
|
+
return True
|
|
472
|
+
if isinstance(value, ast.BinOp):
|
|
473
|
+
return is_constant(value.left) and is_constant(value.right)
|
|
474
|
+
return False
|
|
475
|
+
|
|
476
|
+
match = get_matching_call(
|
|
477
|
+
node, "fail_at", "move_on_at", "CancelScope", base=("trio", "anyio")
|
|
478
|
+
)
|
|
479
|
+
if match is None:
|
|
480
|
+
return
|
|
481
|
+
|
|
482
|
+
if match.name in ("fail_at", "move_on_at") and len(node.args) == 1:
|
|
483
|
+
value = node.args[0]
|
|
484
|
+
else:
|
|
485
|
+
for kw in node.keywords:
|
|
486
|
+
if kw.arg == "deadline":
|
|
487
|
+
value = kw.value
|
|
488
|
+
break
|
|
489
|
+
else:
|
|
490
|
+
return
|
|
491
|
+
if is_constant(value):
|
|
492
|
+
self.error(
|
|
493
|
+
value,
|
|
494
|
+
str(match),
|
|
495
|
+
match.base,
|
|
496
|
+
)
|
|
456
497
|
|
|
457
498
|
|
|
458
499
|
@error_class_cst
|
|
@@ -53,6 +53,7 @@ tests/autofix_files/async911_insert_library.py
|
|
|
53
53
|
tests/autofix_files/async913.py
|
|
54
54
|
tests/autofix_files/async913_trio_anyio.py
|
|
55
55
|
tests/autofix_files/async91x_autofix.py
|
|
56
|
+
tests/autofix_files/async91x_py310.py
|
|
56
57
|
tests/autofix_files/async91x_py311.py
|
|
57
58
|
tests/autofix_files/exception_suppress_context_manager.py
|
|
58
59
|
tests/autofix_files/exception_suppress_context_manager_import_star.py
|
|
@@ -73,6 +74,7 @@ tests/eval_files/async102_anyio.py
|
|
|
73
74
|
tests/eval_files/async102_asyncio.py
|
|
74
75
|
tests/eval_files/async102_trio.py
|
|
75
76
|
tests/eval_files/async103.py
|
|
77
|
+
tests/eval_files/async103_104_py310.py
|
|
76
78
|
tests/eval_files/async103_104_py311.py
|
|
77
79
|
tests/eval_files/async103_all_imported.py
|
|
78
80
|
tests/eval_files/async103_both_imported.py
|
|
@@ -107,6 +109,7 @@ tests/eval_files/async123.py
|
|
|
107
109
|
tests/eval_files/async123_py311.py
|
|
108
110
|
tests/eval_files/async124.py
|
|
109
111
|
tests/eval_files/async124_no_autofix.py
|
|
112
|
+
tests/eval_files/async125.py
|
|
110
113
|
tests/eval_files/async200.py
|
|
111
114
|
tests/eval_files/async210.py
|
|
112
115
|
tests/eval_files/async211.py
|
|
@@ -135,6 +138,7 @@ tests/eval_files/async913.py
|
|
|
135
138
|
tests/eval_files/async913_trio_anyio.py
|
|
136
139
|
tests/eval_files/async91x_autofix.py
|
|
137
140
|
tests/eval_files/async91x_noautofix.py
|
|
141
|
+
tests/eval_files/async91x_py310.py
|
|
138
142
|
tests/eval_files/async91x_py311.py
|
|
139
143
|
tests/eval_files/exception_suppress_context_manager.py
|
|
140
144
|
tests/eval_files/exception_suppress_context_manager_import_star.py
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ARG --enable=ASYNC910,ASYNC911,ASYNC913
|
|
2
|
+
# AUTOFIX
|
|
3
|
+
# ASYNCIO_NO_AUTOFIX
|
|
4
|
+
import trio
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def foo(): ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def match_subject() -> None:
|
|
11
|
+
match await foo():
|
|
12
|
+
case False:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def match_not_all_cases() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
17
|
+
None
|
|
18
|
+
):
|
|
19
|
+
match foo():
|
|
20
|
+
case 1:
|
|
21
|
+
...
|
|
22
|
+
case _:
|
|
23
|
+
await foo()
|
|
24
|
+
await trio.lowlevel.checkpoint()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def match_no_fallback() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
28
|
+
None
|
|
29
|
+
):
|
|
30
|
+
match foo():
|
|
31
|
+
case 1:
|
|
32
|
+
await foo()
|
|
33
|
+
case 2:
|
|
34
|
+
await foo()
|
|
35
|
+
case _ if True:
|
|
36
|
+
await foo()
|
|
37
|
+
await trio.lowlevel.checkpoint()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def match_fallback_is_guarded() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
41
|
+
None
|
|
42
|
+
):
|
|
43
|
+
match foo():
|
|
44
|
+
case 1:
|
|
45
|
+
await foo()
|
|
46
|
+
case 2:
|
|
47
|
+
await foo()
|
|
48
|
+
case _ if foo():
|
|
49
|
+
await foo()
|
|
50
|
+
await trio.lowlevel.checkpoint()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def match_all_cases() -> None:
|
|
54
|
+
match foo():
|
|
55
|
+
case 1:
|
|
56
|
+
await foo()
|
|
57
|
+
case 2:
|
|
58
|
+
await foo()
|
|
59
|
+
case _:
|
|
60
|
+
await foo()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def match_fallback_await_in_guard() -> None:
|
|
64
|
+
# The case guard is only executed if the pattern matches, so we can mostly treat
|
|
65
|
+
# it as part of the body, except for a special case for fallback+checkpointing guard.
|
|
66
|
+
match foo():
|
|
67
|
+
case 1 if await foo():
|
|
68
|
+
...
|
|
69
|
+
case _ if await foo():
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def match_checkpoint_guard() -> None:
|
|
74
|
+
# The above pattern is quite cursed, but this seems fairly reasonable to do.
|
|
75
|
+
match foo():
|
|
76
|
+
case 1 if await foo():
|
|
77
|
+
...
|
|
78
|
+
case _:
|
|
79
|
+
await foo()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def match_not_checkpoint_in_all_guards() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
83
|
+
None
|
|
84
|
+
):
|
|
85
|
+
match foo():
|
|
86
|
+
case 1:
|
|
87
|
+
...
|
|
88
|
+
case _ if await foo():
|
|
89
|
+
...
|
|
90
|
+
await trio.lowlevel.checkpoint()
|
|
@@ -8,6 +8,8 @@ import sys
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import TYPE_CHECKING, NamedTuple, TypeVar
|
|
10
10
|
|
|
11
|
+
from typing_extensions import Self
|
|
12
|
+
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
from collections.abc import Iterable
|
|
13
15
|
|
|
@@ -27,7 +29,7 @@ class Version(NamedTuple):
|
|
|
27
29
|
patch: int
|
|
28
30
|
|
|
29
31
|
@classmethod
|
|
30
|
-
def from_string(cls
|
|
32
|
+
def from_string(cls, string: str) -> Self:
|
|
31
33
|
return cls(*map(int, string.split(".")))
|
|
32
34
|
|
|
33
35
|
def __str__(self) -> str:
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Test for ASYNC103/ASYNC104 with structural pattern matching
|
|
2
|
+
|
|
3
|
+
ASYNC103: no-reraise-cancelled
|
|
4
|
+
ASYNC104: cancelled-not-raised
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# ARG --enable=ASYNC103,ASYNC104
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def foo() -> Any: ...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
...
|
|
15
|
+
except BaseException as e: # ASYNC103_trio: 7, "BaseException"
|
|
16
|
+
match foo():
|
|
17
|
+
case True:
|
|
18
|
+
raise e
|
|
19
|
+
case False:
|
|
20
|
+
...
|
|
21
|
+
case _:
|
|
22
|
+
raise e
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
...
|
|
26
|
+
except BaseException: # ASYNC103_trio: 7, "BaseException"
|
|
27
|
+
match foo():
|
|
28
|
+
case True:
|
|
29
|
+
raise
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
...
|
|
33
|
+
except BaseException: # safe
|
|
34
|
+
match foo():
|
|
35
|
+
case True:
|
|
36
|
+
raise
|
|
37
|
+
case False:
|
|
38
|
+
raise
|
|
39
|
+
case _:
|
|
40
|
+
raise
|
|
41
|
+
try:
|
|
42
|
+
...
|
|
43
|
+
except BaseException: # ASYNC103_trio: 7, "BaseException"
|
|
44
|
+
match foo():
|
|
45
|
+
case _ if foo():
|
|
46
|
+
raise
|
|
47
|
+
try:
|
|
48
|
+
...
|
|
49
|
+
except BaseException: # ASYNC103_trio: 7, "BaseException"
|
|
50
|
+
match foo():
|
|
51
|
+
case 1:
|
|
52
|
+
return # ASYNC104: 12
|
|
53
|
+
case 2:
|
|
54
|
+
raise
|
|
55
|
+
case 3:
|
|
56
|
+
return # ASYNC104: 12
|
|
57
|
+
case blah:
|
|
58
|
+
raise
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import trio
|
|
2
|
+
from typing import Final
|
|
3
|
+
|
|
4
|
+
# ASYNCIO_NO_ERROR
|
|
5
|
+
# anyio.[fail/move_on]_at doesn't exist, but no harm in erroring if we encounter them
|
|
6
|
+
|
|
7
|
+
trio.fail_at(5) # ASYNC125: 13, "trio.fail_at", "trio"
|
|
8
|
+
trio.fail_at(deadline=5) # ASYNC125: 22, "trio.fail_at", "trio"
|
|
9
|
+
trio.move_on_at(10**3) # ASYNC125: 16, "trio.move_on_at", "trio"
|
|
10
|
+
trio.fail_at(7 * 3 + 2 / 5 - (8**7)) # ASYNC125: 13, "trio.fail_at", "trio"
|
|
11
|
+
|
|
12
|
+
trio.CancelScope(deadline=7) # ASYNC125: 26, "trio.CancelScope", "trio"
|
|
13
|
+
trio.CancelScope(shield=True, deadline=7) # ASYNC125: 39, "trio.CancelScope", "trio"
|
|
14
|
+
|
|
15
|
+
# we *could* tell them to use math.inf here ...
|
|
16
|
+
trio.fail_at(10**1000) # ASYNC125: 13, "trio.fail_at", "trio"
|
|
17
|
+
|
|
18
|
+
# _after is fine
|
|
19
|
+
trio.fail_after(5)
|
|
20
|
+
trio.move_on_after(2.3)
|
|
21
|
+
|
|
22
|
+
trio.fail_at(trio.current_time())
|
|
23
|
+
trio.fail_at(trio.current_time() + 7)
|
|
24
|
+
|
|
25
|
+
# relative_deadline is fine, though anyio doesn't have it
|
|
26
|
+
trio.CancelScope(relative_deadline=7)
|
|
27
|
+
|
|
28
|
+
# does not trigger on other "constants".. but we could opt to trigger on
|
|
29
|
+
# any all-caps variable, or on :Final
|
|
30
|
+
MY_CONST_VALUE = 7
|
|
31
|
+
trio.fail_at(MY_CONST_VALUE)
|
|
32
|
+
my_final_value: Final = 3
|
|
33
|
+
trio.fail_at(my_final_value)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# ARG --enable=ASYNC910,ASYNC911,ASYNC913
|
|
2
|
+
# AUTOFIX
|
|
3
|
+
# ASYNCIO_NO_AUTOFIX
|
|
4
|
+
import trio
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def foo(): ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def match_subject() -> None:
|
|
11
|
+
match await foo():
|
|
12
|
+
case False:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def match_not_all_cases() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
17
|
+
None
|
|
18
|
+
):
|
|
19
|
+
match foo():
|
|
20
|
+
case 1:
|
|
21
|
+
...
|
|
22
|
+
case _:
|
|
23
|
+
await foo()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def match_no_fallback() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
27
|
+
None
|
|
28
|
+
):
|
|
29
|
+
match foo():
|
|
30
|
+
case 1:
|
|
31
|
+
await foo()
|
|
32
|
+
case 2:
|
|
33
|
+
await foo()
|
|
34
|
+
case _ if True:
|
|
35
|
+
await foo()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def match_fallback_is_guarded() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
39
|
+
None
|
|
40
|
+
):
|
|
41
|
+
match foo():
|
|
42
|
+
case 1:
|
|
43
|
+
await foo()
|
|
44
|
+
case 2:
|
|
45
|
+
await foo()
|
|
46
|
+
case _ if foo():
|
|
47
|
+
await foo()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def match_all_cases() -> None:
|
|
51
|
+
match foo():
|
|
52
|
+
case 1:
|
|
53
|
+
await foo()
|
|
54
|
+
case 2:
|
|
55
|
+
await foo()
|
|
56
|
+
case _:
|
|
57
|
+
await foo()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def match_fallback_await_in_guard() -> None:
|
|
61
|
+
# The case guard is only executed if the pattern matches, so we can mostly treat
|
|
62
|
+
# it as part of the body, except for a special case for fallback+checkpointing guard.
|
|
63
|
+
match foo():
|
|
64
|
+
case 1 if await foo():
|
|
65
|
+
...
|
|
66
|
+
case _ if await foo():
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def match_checkpoint_guard() -> None:
|
|
71
|
+
# The above pattern is quite cursed, but this seems fairly reasonable to do.
|
|
72
|
+
match foo():
|
|
73
|
+
case 1 if await foo():
|
|
74
|
+
...
|
|
75
|
+
case _:
|
|
76
|
+
await foo()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def match_not_checkpoint_in_all_guards() -> ( # ASYNC910: 0, "exit", Statement("function definition", lineno)
|
|
80
|
+
None
|
|
81
|
+
):
|
|
82
|
+
match foo():
|
|
83
|
+
case 1:
|
|
84
|
+
...
|
|
85
|
+
case _ if await foo():
|
|
86
|
+
...
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flake8_async-25.3.1 → flake8_async-25.4.2}/tests/eval_files/exception_suppress_context_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|