diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79bc475..cee9e1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ --- -name: flake8-trio CI +name: flake8-async CI on: push: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb86fc7..889155e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,13 +5,11 @@ Contributions welcome! We'll expand this guide as we go. ## Development -When you wish to add a check to `flake8-trio` please ensure the following: +When you wish to add a check to `flake8-async` please ensure the following: - `README.md` gets a one line about your new warning - Add a CHANGELOG entry (see 'Releasing a new version' below) -- Unittests are added showing the check highlight where it should and shouldn't - (see flake8-bugbear for examples of good linter tests) - +- A test in `tests/eval_files` is added for your check. See the "Test generator" heading below. To run our test suite please use tox. ```console diff --git a/README.md b/README.md index 5839e59..1e58a5b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/python-trio/flake8-trio/main.svg)](https://results.pre-commit.ci/latest/github/python-trio/flake8-trio/main) [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/) -# flake8-trio +# flake8-async -A highly opinionated flake8 plugin for [Trio](https://github.com/python-trio/trio)-related problems. +A highly opinionated flake8 plugin for problems related to [Trio](https://github.com/python-trio/trio), [AnyIO](https://github.com/agronholm/anyio), or [asyncio](https://docs.python.org/3/library/asyncio.html). This can include anything from outright bugs, to pointless/dead code, to likely performance issues, to minor points of idiom that might signal @@ -10,55 +10,57 @@ a misunderstanding. It may well be too noisy for anyone with different opinions, that's OK. -It also supports the [anyio](https://github.com/agronholm/anyio) library. - Pairs well with flake8-bugbear. +Some checks are incorporated into [ruff](https://github.com/astral-sh/ruff). + +This plugin was previously known as flake8-trio, and there was a separate small plugin known as flake8-async for asyncio. But this plugin was a superset of the checks in flake8-async, and support for anyio was added, so it's now named flake8-async to more properly convey its usage. At the same time all error codes were renamed from TRIOxxx to ASYNCxxx, as was previously used by the old flake8-async. + ## Installation ```console -pip install flake8-trio +pip install flake8-async ``` ## List of warnings -- **TRIO100**: A `with trio.fail_after(...):` or `with trio.move_on_after(...):` +- **ASYNC100**: A `with trio.fail_after(...):` or `with trio.move_on_after(...):` context does not contain any `await` statements. This makes it pointless, as the timeout can only be triggered by a checkpoint. -- **TRIO101**: `yield` inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling. -- **TRIO102**: It's unsafe to await inside `finally:` or `except BaseException/trio.Cancelled` unless you use a shielded +- **ASYNC101**: `yield` inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling. +- **ASYNC102**: It's unsafe to await inside `finally:` or `except BaseException/trio.Cancelled` unless you use a shielded cancel scope with a timeout. -- **TRIO103**: `except BaseException`, `except trio.Cancelled` or a bare `except:` with a code path that doesn't re-raise. If you don't want to re-raise `BaseException`, add a separate handler for `trio.Cancelled` before. -- **TRIO104**: `Cancelled` and `BaseException` must be re-raised - when a user tries to `return` or `raise` a different exception. -- **TRIO105**: Calling a trio async function without immediately `await`ing it. -- **TRIO106**: `trio` must be imported with `import trio` for the linter to work. -- **TRIO109**: Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead -- **TRIO110**: `while : await trio.sleep()` should be replaced by a `trio.Event`. -- **TRIO111**: Variable, from context manager opened inside nursery, passed to `start[_soon]` might be invalidly accessed while in use, due to context manager closing before the nursery. This is usually a bug, and nurseries should generally be the inner-most context manager. -- **TRIO112**: Nursery body with only a call to `nursery.start[_soon]` and not passing itself as a parameter can be replaced with a regular function call. -- **TRIO113**: Using `nursery.start_soon` in `__aenter__` doesn't wait for the task to begin. Consider replacing with `nursery.start`. -- **TRIO114**: Startable function (i.e. has a `task_status` keyword parameter) not in `--startable-in-context-manager` parameter list, please add it so TRIO113 can catch errors when using it. -- **TRIO115**: Replace `trio.sleep(0)` with the more suggestive `trio.lowlevel.checkpoint()`. -- **TRIO116**: `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()`. -- **TRIO118**: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs. +- **ASYNC103**: `except BaseException`, `except trio.Cancelled` or a bare `except:` with a code path that doesn't re-raise. If you don't want to re-raise `BaseException`, add a separate handler for `trio.Cancelled` before. +- **ASYNC104**: `Cancelled` and `BaseException` must be re-raised - when a user tries to `return` or `raise` a different exception. +- **ASYNC105**: Calling a trio async function without immediately `await`ing it. +- **ASYNC106**: `trio`/`anyio` must be imported with `import trio`/`import anyio` for the linter to work. +- **ASYNC109**: Async function definition with a `timeout` parameter - use `trio.[fail/move_on]_[after/at]` instead +- **ASYNC110**: `while : await trio.sleep()` should be replaced by a `trio.Event`. +- **ASYNC111**: Variable, from context manager opened inside nursery, passed to `start[_soon]` might be invalidly accessed while in use, due to context manager closing before the nursery. This is usually a bug, and nurseries should generally be the inner-most context manager. +- **ASYNC112**: Nursery body with only a call to `nursery.start[_soon]` and not passing itself as a parameter can be replaced with a regular function call. +- **ASYNC113**: Using `nursery.start_soon` in `__aenter__` doesn't wait for the task to begin. Consider replacing with `nursery.start`. +- **ASYNC114**: Startable function (i.e. has a `task_status` keyword parameter) not in `--startable-in-context-manager` parameter list, please add it so ASYNC113 can catch errors when using it. +- **ASYNC115**: Replace `trio.sleep(0)` with the more suggestive `trio.lowlevel.checkpoint()`. +- **ASYNC116**: `trio.sleep()` with >24 hour interval should usually be `trio.sleep_forever()`. +- **ASYNC118**: Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs. ### Warnings for blocking sync calls in async functions -- **TRIO200**: User-configured error for blocking sync calls in async functions. Does nothing by default, see [`trio200-blocking-calls`](#trio200-blocking-calls) for how to configure it. -- **TRIO210**: Sync HTTP call in async function, use `httpx.AsyncClient`. -- **TRIO211**: Likely sync HTTP call in async function, use `httpx.AsyncClient`. Looks for `urllib3` method calls on pool objects, but only matching on the method signature and not the object. -- **TRIO212**: Blocking sync HTTP call on httpx object, use httpx.AsyncClient. -- **TRIO220**: Sync process call in async function, use `await nursery.start(trio.run_process, ...)`. -- **TRIO221**: Sync process call in async function, use `await trio.run_process(...)`. -- **TRIO222**: Sync `os.*` call in async function, wrap in `await trio.to_thread.run_sync()`. -- **TRIO230**: Sync IO call in async function, use `trio.open_file(...)`. -- **TRIO231**: Sync IO call in async function, use `trio.wrap_file(...)`. -- **TRIO232**: Blocking sync call on file object, wrap the file object in `trio.wrap_file()` to get an async file object. -- **TRIO240**: Avoid using `os.path` in async functions, prefer using `trio.Path` objects. +- **ASYNC200**: User-configured error for blocking sync calls in async functions. Does nothing by default, see [`trio200-blocking-calls`](#trio200-blocking-calls) for how to configure it. +- **ASYNC210**: Sync HTTP call in async function, use `httpx.AsyncClient`. +- **ASYNC211**: Likely sync HTTP call in async function, use `httpx.AsyncClient`. Looks for `urllib3` method calls on pool objects, but only matching on the method signature and not the object. +- **ASYNC212**: Blocking sync HTTP call on httpx object, use httpx.AsyncClient. +- **ASYNC220**: Sync process call in async function, use `await nursery.start(trio.run_process, ...)`. +- **ASYNC221**: Sync process call in async function, use `await trio.run_process(...)`. +- **ASYNC222**: Sync `os.*` call in async function, wrap in `await trio.to_thread.run_sync()`. +- **ASYNC230**: Sync IO call in async function, use `trio.open_file(...)`. +- **ASYNC231**: Sync IO call in async function, use `trio.wrap_file(...)`. +- **ASYNC232**: Blocking sync call on file object, wrap the file object in `trio.wrap_file()` to get an async file object. +- **ASYNC240**: Avoid using `os.path` in async functions, prefer using `trio.Path` objects. ### Warnings disabled by default -- **TRIO900**: Async generator without `@asynccontextmanager` not allowed. -- **TRIO910**: Exit or `return` from async function with no guaranteed checkpoint or exception since function definition. -- **TRIO911**: Exit, `yield` or `return` from async iterable with no guaranteed checkpoint since possible function entry (yield or function definition) +- **ASYNC900**: Async generator without `@asynccontextmanager` not allowed. +- **ASYNC910**: Exit or `return` from async function with no guaranteed checkpoint or exception since function definition. +- **ASYNC911**: Exit, `yield` or `return` from async iterable with no guaranteed checkpoint since possible function entry (yield or function definition) Checkpoints are `await`, `async for`, and `async with` (on one of enter/exit). ### Removed Warnings @@ -69,55 +71,55 @@ pip install flake8-trio ## Examples ### install and run through flake8 ```sh -pip install flake8 flake8-trio +pip install flake8 flake8-async flake8 . ``` ### install and run with pre-commit -If you use [pre-commit](https://pre-commit.com/), you can use it with flake8-trio by +If you use [pre-commit](https://pre-commit.com/), you can use it with flake8-async by adding the following to your `.pre-commit-config.yaml`: ```yaml minimum_pre_commit_version: '2.9.0' repos: -- repo: https://github.com/Zac-HD/flake8-trio +- repo: https://github.com/python-trio/flake8-async rev: 23.2.5 hooks: - - id: flake8-trio - # args: [--enable=TRIO, --disable=TRIO9, --autofix=TRIO] + - id: flake8-async + # args: [--enable=ASYNC, --disable=ASYNC9, --autofix=ASYNC] ``` This is often considerably faster for large projects, because `pre-commit` -can avoid running `flake8-trio` on unchanged files. +can avoid running `flake8-async` on unchanged files. Afterwards, run ```sh -pip install pre-commit flake8-trio +pip install pre-commit flake8-async pre-commit run . ``` ### install and run as standalone If inside a git repository, running without arguments will run it against all `*.py` files in the repository. ```sh -pip install flake8-trio -flake8-trio +pip install flake8-async +flake8-async ``` #### with autofixes ```sh -flake8-trio --autofix=TRIO +flake8-async --autofix=ASYNC ``` #### specifying source files ```sh -flake8-trio my_python_file.py +flake8-async my_python_file.py ``` ##### zsh-only ```zsh -flake8-trio **/*.py +flake8-async **/*.py ``` ## Configuration [You can configure `flake8` with command-line options](https://flake8.pycqa.org/en/latest/user/configuration.html), but we prefer using a config file. The file needs to start with a section marker `[flake8]` and the following options are then parsed using flake8's config parser, and can be used just like any other flake8 options. -Note that it's not currently possible to use a configuration file when running `flake8-trio` standalone. +Note that it's not currently possible to use a configuration file when running `flake8-async` standalone. ### `--enable` Comma-separated list of error codes to enable, similar to flake8 --select but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors. @@ -126,7 +128,7 @@ Comma-separated list of error codes to enable, similar to flake8 --select but is Comma-separated list of error codes to disable, similar to flake8 --ignore but is additionally more performant as it will disable non-enabled visitors from running instead of just silencing their errors. ### `--autofix` -Comma-separated list of error-codes to enable autofixing for if implemented. Requires running as a standalone program. Pass `--autofix=TRIO` to enable all autofixes. +Comma-separated list of error-codes to enable autofixing for if implemented. Requires running as a standalone program. Pass `--autofix=ASYNC` to enable all autofixes. ### `--error-on-autofix` Whether to also print an error message for autofixed errors. @@ -135,7 +137,7 @@ Whether to also print an error message for autofixed errors. Change the default library to be anyio instead of trio. If trio is imported it will assume both are available and print suggestions with [anyio|trio]. ### `no-checkpoint-warning-decorators` -Comma-separated list of decorators to disable checkpointing checks for, turning off TRIO910 and TRIO911 warnings for functions decorated with any decorator matching any in the list. Matching is done with [fnmatch](https://docs.python.org/3/library/fnmatch.html). Defaults to disabling for `asynccontextmanager`. +Comma-separated list of decorators to disable checkpointing checks for, turning off ASYNC910 and ASYNC911 warnings for functions decorated with any decorator matching any in the list. Matching is done with [fnmatch](https://docs.python.org/3/library/fnmatch.html). Defaults to disabling for `asynccontextmanager`. Decorators-to-match must be identifiers or dotted names only (not PEP-614 expressions), and will match against the name only - e.g. `foo.bar` matches `foo.bar`, `foo.bar()`, and `foo.bar(args, here)`, etc. @@ -159,14 +161,14 @@ startable-in-context-manager = myfun2, ``` -### `trio200-blocking-calls` +### `async200-blocking-calls` Comma-separated list of pairs of values separated by `->` (optional whitespace stripped), where the first is a pattern for a call that should raise an error if found inside an async function, and the second is what should be suggested to use instead. It uses fnmatch as per [`no-checkpoint-warning-decorators`](#no-checkpoint-warning-decorators) for matching. The part after `->` is not used by the checker other than when printing the error, so you could add extra info there if you want. The format of the error message is `User-configured blocking sync call {0} in async function, consider replacing with {1}.`, where `{0}` is the pattern the call matches and `{1}` is the suggested replacement. Example: ```ini -trio200-blocking-calls = +async200-blocking-calls = my_blocking_call -> async.alternative, module.block_call -> other_function_to_use, common_error_call -> alternative(). But sometimes you should use other_function(). Ask joe if you're unsure which one, diff --git a/flake8_trio/__init__.py b/flake8_trio/__init__.py index 42135a0..d3c301c 100644 --- a/flake8_trio/__init__.py +++ b/flake8_trio/__init__.py @@ -23,7 +23,7 @@ import libcst as cst -from .base import Options +from .base import Options, error_has_subidentifier from .runner import Flake8TrioRunner, Flake8TrioRunner_cst from .visitors import ERROR_CLASSES, ERROR_CLASSES_CST, default_disabled_error_codes @@ -199,7 +199,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): ) else: # if run as a flake8 plugin Plugin.standalone = False - # Disable TRIO9xx calls by default + # Disable ASYNC9xx calls by default option_manager.extend_default_ignore(default_disabled_error_codes) # add parameter to parse from flake8 config add_argument = functools.partial( # type: ignore @@ -209,7 +209,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): add_argument( "--enable", type=comma_separated_list, - default="TRIO", + default="ASYNC", required=False, help=( "Comma-separated list of error codes to enable, similar to flake8" @@ -221,7 +221,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): add_argument( "--disable", type=comma_separated_list, - default="TRIO9" if Plugin.standalone else "", + default="ASYNC9" if Plugin.standalone else "", required=False, help=( "Comma-separated list of error codes to disable, similar to flake8" @@ -253,7 +253,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): required=False, type=comma_separated_list, help=( - "Comma-separated list of decorators to disable TRIO910 & TRIO911 " + "Comma-separated list of decorators to disable ASYNC910 & ASYNC911 " "checkpoint warnings for. " "Decorators can be dotted or not, as well as support * as a wildcard. " "For example, ``--no-checkpoint-warning-decorators=app.route," @@ -266,7 +266,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): default="", required=False, help=( - "Comma-separated list of method calls to additionally enable TRIO113 " + "Comma-separated list of method calls to additionally enable ASYNC113 " "warnings for. Will also check for the pattern inside function calls. " "Methods must be valid identifiers as per `str.isidientifier()` and " "not reserved keywords. " @@ -281,7 +281,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): required=False, help=( "Comma-separated list of key->value pairs, where key is a [dotted] " - "function that if found inside an async function will raise TRIO200, " + "function that if found inside an async function will raise ASYNC200, " "suggesting it be replaced with {value}" ), ) @@ -313,8 +313,9 @@ def get_matching_codes( err_code for err_class in (*ERROR_CLASSES, *ERROR_CLASSES_CST) for err_code in err_class.error_codes # type: ignore[attr-defined] - if len(err_code) == 7 # exclude e.g. TRIO103_anyio_trio + if not error_has_subidentifier(err_code) # exclude e.g. ASYNC103_anyio_trio } + assert all_codes if options.autofix and not Plugin.standalone: print("Cannot autofix when run as a flake8 plugin.", file=sys.stderr) @@ -328,8 +329,10 @@ def get_matching_codes( enabled_codes -= set(get_matching_codes(options.disable, enabled_codes)) # if disable has default value, re-enable explicitly enabled codes - if options.disable == ["TRIO9"]: - enabled_codes.update(code for code in options.enable if len(code) == 7) + if options.disable == ["ASYNC9"]: + enabled_codes.update( + code for code in options.enable if not error_has_subidentifier(code) + ) Plugin._options = Options( enabled_codes=enabled_codes, diff --git a/flake8_trio/base.py b/flake8_trio/base.py index 3677e1e..4ebead4 100644 --- a/flake8_trio/base.py +++ b/flake8_trio/base.py @@ -9,6 +9,17 @@ from collections.abc import Collection +# strip the sub-identifier on error used to specify which message to print, when +# several different ones are used for the same code. Used in e.g. Visitor103 +def strip_error_subidentifier(s: str) -> str: + """Turn e.g. ASYNC103_anyio_trio => ASYNC103.""" + return s.split("_")[0] + + +def error_has_subidentifier(s: str) -> bool: + return "_" in s + + @dataclass class Options: # error codes to give errors for diff --git a/flake8_trio/runner.py b/flake8_trio/runner.py index 229979f..a1b843d 100644 --- a/flake8_trio/runner.py +++ b/flake8_trio/runner.py @@ -26,7 +26,7 @@ from libcst import Module from .base import Error, Options - from .visitors.flake8triovisitor import Flake8TrioVisitor, Flake8TrioVisitor_cst + from .visitors.flake8triovisitor import Flake8AsyncVisitor, Flake8AsyncVisitor_cst @dataclass @@ -73,7 +73,7 @@ def visit(self, node: ast.AST): """Visit a node.""" # tracks the subclasses that, from this node on, iterated through it's subfields # we need to remember it so we can restore it at the end of the function. - novisit: set[Flake8TrioVisitor] = set() + novisit: set[Flake8AsyncVisitor] = set() method = "visit_" + node.__class__.__name__ @@ -122,14 +122,14 @@ def __init__(self, options: Options, module: Module): # Could possibly enable/disable utility visitors here, if visitors declared # dependencies - self.utility_visitors: tuple[Flake8TrioVisitor_cst, ...] = tuple( + self.utility_visitors: tuple[Flake8AsyncVisitor_cst, ...] = tuple( v(self.state) for v in utility_visitors ) # sort the error classes to get predictable behaviour when multiple autofixers # are enabled sorted_error_classes_cst = sorted(ERROR_CLASSES_CST, key=lambda x: x.__name__) - self.visitors: tuple[Flake8TrioVisitor_cst, ...] = tuple( + self.visitors: tuple[Flake8AsyncVisitor_cst, ...] = tuple( v(self.state) for v in sorted_error_classes_cst if self.selected(v.error_codes) diff --git a/flake8_trio/visitors/__init__.py b/flake8_trio/visitors/__init__.py index 6aa2fe7..ab392ba 100644 --- a/flake8_trio/visitors/__init__.py +++ b/flake8_trio/visitors/__init__.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from .flake8triovisitor import Flake8TrioVisitor, Flake8TrioVisitor_cst + from .flake8triovisitor import Flake8AsyncVisitor, Flake8AsyncVisitor_cst __all__ = [ "ERROR_CLASSES", @@ -19,11 +19,11 @@ "utility_visitors", "utility_visitors_cst", ] -ERROR_CLASSES: set[type[Flake8TrioVisitor]] = set() -ERROR_CLASSES_CST: set[type[Flake8TrioVisitor_cst]] = set() +ERROR_CLASSES: set[type[Flake8AsyncVisitor]] = set() +ERROR_CLASSES_CST: set[type[Flake8AsyncVisitor_cst]] = set() default_disabled_error_codes: list[str] = [] -utility_visitors: set[type[Flake8TrioVisitor]] = set() -utility_visitors_cst: set[type[Flake8TrioVisitor_cst]] = set() +utility_visitors: set[type[Flake8AsyncVisitor]] = set() +utility_visitors_cst: set[type[Flake8AsyncVisitor_cst]] = set() # Import all visitors so their decorators run, filling the above containers # This has to be done at the end to avoid circular imports diff --git a/flake8_trio/visitors/flake8triovisitor.py b/flake8_trio/visitors/flake8triovisitor.py index b97e073..d53249f 100644 --- a/flake8_trio/visitors/flake8triovisitor.py +++ b/flake8_trio/visitors/flake8triovisitor.py @@ -9,7 +9,7 @@ import libcst as cst from libcst.metadata import PositionProvider -from ..base import Error, Statement +from ..base import Error, Statement, strip_error_subidentifier if TYPE_CHECKING: from collections.abc import Iterable, Mapping @@ -21,7 +21,7 @@ ] -class Flake8TrioVisitor(ast.NodeVisitor, ABC): +class Flake8AsyncVisitor(ast.NodeVisitor, ABC): # abstract attribute by not providing a value error_codes: Mapping[str, str] @@ -36,7 +36,7 @@ def __init__(self, shared_state: SharedState): # mark variables that shouldn't be saved/loaded in self.get_state self.nocopy = { - "_Flake8TrioVisitor__state", + "_Flake8AsyncVisitor__state", "error_codes", "nocopy", "novisit", @@ -98,13 +98,12 @@ def error( ), "No error code defined, but class has multiple codes" error_code = next(iter(self.error_codes)) # don't emit an error if this code is disabled in a multi-code visitor - elif error_code[:7] not in self.options.enabled_codes: + elif strip_error_subidentifier(error_code) not in self.options.enabled_codes: return self.__state.problems.append( Error( - # 7 == len('TRIO...'), so alt messages raise the original code - error_code[:7], + strip_error_subidentifier(error_code), node.lineno, node.col_offset, self.error_codes[error_code], @@ -156,7 +155,7 @@ def add_library(self, name: str) -> None: self.__state.library = (*self.__state.library, name) -class Flake8TrioVisitor_cst(cst.CSTTransformer, ABC): +class Flake8AsyncVisitor_cst(cst.CSTTransformer, ABC): # abstract attribute by not providing a value error_codes: Mapping[str, str] METADATA_DEPENDENCIES = (PositionProvider,) @@ -218,7 +217,7 @@ def error( error_code = next(iter(self.error_codes)) # don't emit an error if this code is disabled in a multi-code visitor # TODO: write test for only one of 910/911 enabled/autofixed - elif error_code[:7] not in self.options.enabled_codes: + elif strip_error_subidentifier(error_code) not in self.options.enabled_codes: return False # pragma: no cover if self.is_noqa(node, error_code): @@ -228,8 +227,7 @@ def error( pos = self.get_metadata(PositionProvider, node).start # type: ignore self.__state.problems.append( Error( - # 7 == len('TRIO...'), so alt messages raise the original code - error_code[:7], + strip_error_subidentifier(error_code), pos.line, # type: ignore pos.column, # type: ignore self.error_codes[error_code], diff --git a/flake8_trio/visitors/helpers.py b/flake8_trio/visitors/helpers.py index d953e2e..2eac570 100644 --- a/flake8_trio/visitors/helpers.py +++ b/flake8_trio/visitors/helpers.py @@ -25,12 +25,16 @@ if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Sequence - from .flake8triovisitor import Flake8TrioVisitor, Flake8TrioVisitor_cst, HasLineCol + from .flake8triovisitor import ( + Flake8AsyncVisitor, + Flake8AsyncVisitor_cst, + HasLineCol, + ) - T = TypeVar("T", bound=Flake8TrioVisitor) - T_CST = TypeVar("T_CST", bound=Flake8TrioVisitor_cst) + T = TypeVar("T", bound=Flake8AsyncVisitor) + T_CST = TypeVar("T_CST", bound=Flake8AsyncVisitor_cst) T_EITHER = TypeVar( - "T_EITHER", bound=Union[Flake8TrioVisitor, Flake8TrioVisitor_cst] + "T_EITHER", bound=Union[Flake8AsyncVisitor, Flake8AsyncVisitor_cst] ) @@ -364,7 +368,7 @@ def get_comments(node: cst.CSTNode | Iterable[cst.CSTNode]) -> Iterator[cst.Empt yield from get_comments(n) -# used in TRIO100 +# used in ASYNC100 def flatten_preserving_comments(node: cst.BaseCompoundStatement): # add leading lines (comments and empty lines) for the node to be removed new_leading_lines = list(node.leading_lines) diff --git a/flake8_trio/visitors/visitor100.py b/flake8_trio/visitors/visitor100.py index 27e0588..7284372 100644 --- a/flake8_trio/visitors/visitor100.py +++ b/flake8_trio/visitors/visitor100.py @@ -1,4 +1,4 @@ -"""Contains visitor for TRIO100. +"""Contains visitor for ASYNC100. A `with trio.fail_after(...):` or `with trio.move_on_after(...):` context does not contain any `await` statements. This makes it pointless, as @@ -13,7 +13,7 @@ import libcst as cst import libcst.matchers as m -from .flake8triovisitor import Flake8TrioVisitor_cst +from .flake8triovisitor import Flake8AsyncVisitor_cst from .helpers import ( AttributeCall, error_class_cst, @@ -26,9 +26,9 @@ @error_class_cst -class Visitor100_libcst(Flake8TrioVisitor_cst): +class Visitor100_libcst(Flake8AsyncVisitor_cst): error_codes: Mapping[str, str] = { - "TRIO100": ( + "ASYNC100": ( "{0}.{1} context contains no checkpoints, remove the context or add" " `await {0}.lowlevel.checkpoint()`." ), diff --git a/flake8_trio/visitors/visitor101.py b/flake8_trio/visitors/visitor101.py index 8a3ac58..29cbbf7 100644 --- a/flake8_trio/visitors/visitor101.py +++ b/flake8_trio/visitors/visitor101.py @@ -1,4 +1,4 @@ -"""Contains visitor for TRIO101. +"""Contains visitor for ASYNC101. `yield` inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling. @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any -from .flake8triovisitor import Flake8TrioVisitor_cst +from .flake8triovisitor import Flake8AsyncVisitor_cst from .helpers import ( cancel_scope_names, error_class_cst, @@ -23,9 +23,9 @@ @error_class_cst -class Visitor101(Flake8TrioVisitor_cst): +class Visitor101(Flake8AsyncVisitor_cst): error_codes: Mapping[str, str] = { - "TRIO101": ( + "ASYNC101": ( "`yield` inside a nursery or cancel scope is only safe when implementing " "a context manager - otherwise, it breaks exception handling." ), diff --git a/flake8_trio/visitors/visitor102.py b/flake8_trio/visitors/visitor102.py index 7af3de4..5afc63e 100644 --- a/flake8_trio/visitors/visitor102.py +++ b/flake8_trio/visitors/visitor102.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any from ..base import Statement -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import cancel_scope_names, critical_except, error_class, get_matching_call if TYPE_CHECKING: @@ -17,9 +17,9 @@ @error_class -class Visitor102(Flake8TrioVisitor): +class Visitor102(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO102": ( + "ASYNC102": ( "await inside {0.name} on line {0.lineno} must have shielded cancel " "scope with a timeout." ), diff --git a/flake8_trio/visitors/visitor103_104.py b/flake8_trio/visitors/visitor103_104.py index db77499..8ce2992 100644 --- a/flake8_trio/visitors/visitor103_104.py +++ b/flake8_trio/visitors/visitor103_104.py @@ -11,13 +11,13 @@ import ast from typing import TYPE_CHECKING, Any -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import critical_except, error_class, iter_guaranteed_once if TYPE_CHECKING: from collections.abc import Mapping -_trio103_common_msg = "{} block with a code path that doesn't re-raise the error." +_async103_common_msg = "{} block with a code path that doesn't re-raise the error." _suggestion = " Consider adding an `except {}: raise` before this exception handler." _suggestion_dict: dict[tuple[str, ...], str] = { ("anyio",): "anyio.get_cancelled_exc_class()", @@ -26,17 +26,17 @@ _suggestion_dict[("anyio", "trio")] = "[" + "|".join(_suggestion_dict.values()) + "]" _error_codes = { - "TRIO103": _trio103_common_msg, - "TRIO104": "Cancelled (and therefore BaseException) must be re-raised.", + "ASYNC103": _async103_common_msg, + "ASYNC104": "Cancelled (and therefore BaseException) must be re-raised.", } for poss_library in _suggestion_dict: - _error_codes[f"TRIO103_{'_'.join(poss_library)}"] = ( - _trio103_common_msg + _suggestion.format(_suggestion_dict[poss_library]) + _error_codes[f"ASYNC103_{'_'.join(poss_library)}"] = ( + _async103_common_msg + _suggestion.format(_suggestion_dict[poss_library]) ) @error_class -class Visitor103_104(Flake8TrioVisitor): +class Visitor103_104(Flake8AsyncVisitor): error_codes: Mapping[str, str] = _error_codes def __init__(self, *args: Any, **kwargs: Any): @@ -61,21 +61,21 @@ def visit_ExceptHandler(self, node: ast.ExceptHandler): # If previous excepts have handled trio.Cancelled, don't do anything - namely # don't set self.unraised (so 104 isn't triggered) nor check for 103. if marker.name == "trio.Cancelled": - error_code = "TRIO103" + error_code = "ASYNC103" self.cancelled_caught.add("trio") elif marker.name in ( "anyio.get_cancelled_exc_class()", "get_cancelled_exc_class()", ): - error_code = "TRIO103" + error_code = "ASYNC103" self.cancelled_caught.add("anyio") else: if self.cancelled_caught: return if len(self.library) < 2: - error_code = f"TRIO103_{self.library_str}" + error_code = f"ASYNC103_{self.library_str}" else: - error_code = f"TRIO103_{'_'.join(sorted(self.library))}" + error_code = f"ASYNC103_{'_'.join(sorted(self.library))}" self.cancelled_caught.update("trio", "anyio") # Don't save the state of cancelled_caught, that's handled in Try and would @@ -117,7 +117,7 @@ def visit_Raise(self, node: ast.Raise): and node.exc is not None and not (isinstance(node.exc, ast.Name) and node.exc.id == self.except_name) ): - self.error(node, error_code="TRIO104") + self.error(node, error_code="ASYNC104") # treat it as safe regardless, to avoid unnecessary error messages. self.unraised = False @@ -125,7 +125,7 @@ def visit_Raise(self, node: ast.Raise): def visit_Return(self, node: ast.Return | ast.Yield): if self.unraised: # Error: must re-raise - self.error(node, error_code="TRIO104") + self.error(node, error_code="ASYNC104") visit_Yield = visit_Return @@ -209,10 +209,10 @@ def visit_For(self, node: ast.For | ast.While): def visit_Break(self, node: ast.Break): if self.unraised and self.loop_depth == 0: - self.error(node, error_code="TRIO104") + self.error(node, error_code="ASYNC104") self.unraised_break |= self.unraised def visit_Continue(self, node: ast.Continue): if self.unraised and self.loop_depth == 0: - self.error(node, error_code="TRIO104") + self.error(node, error_code="ASYNC104") self.unraised_continue |= self.unraised diff --git a/flake8_trio/visitors/visitor105.py b/flake8_trio/visitors/visitor105.py index 012ba82..594ad89 100644 --- a/flake8_trio/visitors/visitor105.py +++ b/flake8_trio/visitors/visitor105.py @@ -1,11 +1,11 @@ -"""TRIO105: library async function must be immediately awaited.""" +"""ASYNC105: library async function must be immediately awaited.""" from __future__ import annotations import ast from typing import TYPE_CHECKING, Any -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import error_class if TYPE_CHECKING: @@ -42,9 +42,9 @@ @error_class -class Visitor105(Flake8TrioVisitor): +class Visitor105(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO105": "{0} async {1} must be immediately awaited.", + "ASYNC105": "{0} async {1} must be immediately awaited.", } def __init__(self, *args: Any, **kwargs: Any): diff --git a/flake8_trio/visitors/visitor111.py b/flake8_trio/visitors/visitor111.py index 8e554e4..397d0a2 100644 --- a/flake8_trio/visitors/visitor111.py +++ b/flake8_trio/visitors/visitor111.py @@ -5,7 +5,7 @@ import ast from typing import TYPE_CHECKING, Any, NamedTuple -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import error_class, get_matching_call if TYPE_CHECKING: @@ -13,9 +13,9 @@ @error_class -class Visitor111(Flake8TrioVisitor): +class Visitor111(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO111": ( + "ASYNC111": ( "variable {2} is usable within the context manager on line {0}, but that " "will close before nursery opened on line {1} - this is usually a bug. " "Nurseries should generally be the inner-most context manager." diff --git a/flake8_trio/visitors/visitor118.py b/flake8_trio/visitors/visitor118.py index 044f762..b8a1d04 100644 --- a/flake8_trio/visitors/visitor118.py +++ b/flake8_trio/visitors/visitor118.py @@ -1,4 +1,4 @@ -"""Visitor for TRIO118. +"""Visitor for ASYNC118. Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable, since that breaks linter checks and multi-backend programs. @@ -10,7 +10,7 @@ import re from typing import TYPE_CHECKING -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import error_class if TYPE_CHECKING: @@ -18,9 +18,9 @@ @error_class -class Visitor118(Flake8TrioVisitor): +class Visitor118(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO118": ( + "ASYNC118": ( "Don't assign the value of `anyio.get_cancelled_exc_class()` to a variable," " since that breaks linter checks and multi-backend programs." ) @@ -35,7 +35,7 @@ def visit_Assign(self, node: ast.Assign | ast.AnnAssign): visit_AnnAssign = visit_Assign - # redundant check with TRIO106 + # redundant check with ASYNC106 def visit_ImportFrom(self, node: ast.ImportFrom): if node.module == "anyio": for alias in node.names: diff --git a/flake8_trio/visitors/visitor2xx.py b/flake8_trio/visitors/visitor2xx.py index e9e6eb7..a7349f7 100644 --- a/flake8_trio/visitors/visitor2xx.py +++ b/flake8_trio/visitors/visitor2xx.py @@ -14,7 +14,7 @@ import re from typing import TYPE_CHECKING, Any -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import error_class, fnmatch_qualified_name, get_matching_call if TYPE_CHECKING: @@ -22,9 +22,9 @@ @error_class -class Visitor200(Flake8TrioVisitor): +class Visitor200(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO200": ( + "ASYNC200": ( "User-configured blocking sync call {0} in async function, consider " "replacing with {1}." ) @@ -59,8 +59,8 @@ def visit_blocking_call(self, node: ast.Call): @error_class class Visitor21X(Visitor200): error_codes: Mapping[str, str] = { - "TRIO210": "Sync HTTP call {} in async function, use `httpx.AsyncClient`.", - "TRIO211": ( + "ASYNC210": "Sync HTTP call {} in async function, use `httpx.AsyncClient`.", + "ASYNC211": ( "Likely sync HTTP call {} in async function, use `httpx.AsyncClient`." ), } @@ -92,7 +92,7 @@ def visit_blocking_call(self, node: ast.Call): func_name = ast.unparse(node.func) for http_package in "requests", "httpx": if get_matching_call(node, *http_methods | {"request"}, base=http_package): - self.error(node, func_name, error_code="TRIO210") + self.error(node, func_name, error_code="ASYNC210") return if func_name in ( @@ -101,7 +101,7 @@ def visit_blocking_call(self, node: ast.Call): "request.urlopen", "urlopen", ): - self.error(node, func_name, error_code="TRIO210") + self.error(node, func_name, error_code="ASYNC210") elif ( "urllib3" in self.imports @@ -112,13 +112,13 @@ def visit_blocking_call(self, node: ast.Call): and isinstance(node.args[0].value, str) and node.args[0].value.lower() in http_methods | {"trace", "connect"} ): - self.error(node, func_name, error_code="TRIO211") + self.error(node, func_name, error_code="ASYNC211") @error_class class Visitor212(Visitor200): error_codes: Mapping[str, str] = { - "TRIO212": ( + "ASYNC212": ( "Blocking sync HTTP call {1} on httpx object {0}, use httpx.AsyncClient." ) } @@ -170,12 +170,12 @@ def visit_blocking_call(self, node: ast.Call): @error_class class Visitor22X(Visitor200): error_codes: Mapping[str, str] = { - "TRIO220": ( + "ASYNC220": ( "Sync call {} in async function, use " "`await nursery.start({}.run_process, ...)`." ), - "TRIO221": "Sync call {} in async function, use `await {}.run_process(...)`.", - "TRIO222": "Sync call {} in async function, wrap in `{}.to_thread.run_sync()`.", + "ASYNC221": "Sync call {} in async function, use `await {}.run_process(...)`.", + "ASYNC222": "Sync call {} in async function, wrap in `{}.to_thread.run_sync()`.", } def visit_blocking_call(self, node: ast.Call): @@ -196,30 +196,30 @@ def is_p_wait(arg: ast.expr) -> bool: func_name = ast.unparse(node.func) error_code: str | None = None if func_name in ("subprocess.Popen", "os.popen"): - error_code = "TRIO220" + error_code = "ASYNC220" elif func_name in ( "os.system", "os.posix_spawn", "os.posix_spawnp", ) or get_matching_call(node, *subprocess_calls, base="subprocess"): - error_code = "TRIO221" + error_code = "ASYNC221" elif re.fullmatch("os.wait([34]|(id)|(pid))?", func_name): - error_code = "TRIO222" + error_code = "ASYNC222" elif re.fullmatch("os.spawn[vl]p?e?", func_name): - error_code = "TRIO221" + error_code = "ASYNC221" - # if mode= is given and not [os.]P_WAIT: TRIO220 + # if mode= is given and not [os.]P_WAIT: ASYNC220 # 1. as a positional parameter if node.args and not is_p_wait(node.args[0]): - error_code = "TRIO220" + error_code = "ASYNC220" # 2. as a keyword parameter for kw in node.keywords: if kw.arg == "mode" and not is_p_wait(kw.value): - error_code = "TRIO220" + error_code = "ASYNC220" break if error_code is not None: @@ -229,8 +229,8 @@ def is_p_wait(arg: ast.expr) -> bool: @error_class class Visitor23X(Visitor200): error_codes: Mapping[str, str] = { - "TRIO230": "Sync call {0} in async function, use `{1}.open_file(...)`.", - "TRIO231": "Sync call {0} in async function, use `{1}.wrap_file({0})`.", + "ASYNC230": "Sync call {0} in async function, use `{1}.open_file(...)`.", + "ASYNC231": "Sync call {0} in async function, use `{1}.wrap_file({0})`.", } def visit_Call(self, node: ast.Call): @@ -244,9 +244,9 @@ def visit_blocking_call(self, node: ast.Call): return func_name = ast.unparse(node.func) if func_name in ("open", "io.open", "io.open_code"): - error_code = "TRIO230" + error_code = "ASYNC230" elif func_name == "os.fdopen": - error_code = "TRIO231" + error_code = "ASYNC231" else: return self.error(node, func_name, self.library_str, error_code=error_code) @@ -255,7 +255,7 @@ def visit_blocking_call(self, node: ast.Call): @error_class class Visitor232(Visitor200): error_codes: Mapping[str, str] = { - "TRIO232": ( + "ASYNC232": ( "Blocking sync call {1} on file object {0}, wrap the file object" "in `{2}.wrap_file()` to get an async file object." ) @@ -285,7 +285,7 @@ def visit_blocking_call(self, node: ast.Call): @error_class class Visitor24X(Visitor200): error_codes: Mapping[str, str] = { - "TRIO240": "Avoid using os.path, prefer using {1}.Path objects.", + "ASYNC240": "Avoid using os.path, prefer using {1}.Path objects.", } def __init__(self, *args: Any, **kwargs: Any): diff --git a/flake8_trio/visitors/visitor91x.py b/flake8_trio/visitors/visitor91x.py index 42f0353..c1af6d9 100644 --- a/flake8_trio/visitors/visitor91x.py +++ b/flake8_trio/visitors/visitor91x.py @@ -16,7 +16,7 @@ from libcst.metadata import PositionProvider from ..base import Statement -from .flake8triovisitor import Flake8TrioVisitor_cst +from .flake8triovisitor import Flake8AsyncVisitor_cst from .helpers import ( disabled_by_default, error_class_cst, @@ -226,13 +226,13 @@ def leave_Yield( @error_class_cst @disabled_by_default -class Visitor91X(Flake8TrioVisitor_cst, CommonVisitors): +class Visitor91X(Flake8AsyncVisitor_cst, CommonVisitors): error_codes: Mapping[str, str] = { - "TRIO910": ( + "ASYNC910": ( "{0} from async function with no guaranteed checkpoint or exception " "since function definition on line {1.lineno}." ), - "TRIO911": ( + "ASYNC911": ( "{0} from async iterable with no guaranteed checkpoint since {1.name} " "on line {1.lineno}." ), @@ -251,7 +251,7 @@ def __init__(self, *args: Any, **kwargs: Any): def should_autofix(self, node: cst.CSTNode, code: str | None = None) -> bool: return not self.noautofix and super().should_autofix( - node, "TRIO911" if self.has_yield else "TRIO910" + node, "ASYNC911" if self.has_yield else "ASYNC910" ) def checkpoint_statement(self) -> cst.SimpleStatementLine: @@ -384,7 +384,7 @@ def error_91x( node, msg, statement, - error_code="TRIO911" if self.has_yield else "TRIO910", + error_code="ASYNC911" if self.has_yield else "ASYNC910", ) def leave_Await( diff --git a/flake8_trio/visitors/visitor_utility.py b/flake8_trio/visitors/visitor_utility.py index 35efe59..81bdcf0 100644 --- a/flake8_trio/visitors/visitor_utility.py +++ b/flake8_trio/visitors/visitor_utility.py @@ -10,7 +10,7 @@ import libcst.matchers as m from libcst.metadata import PositionProvider -from .flake8triovisitor import Flake8TrioVisitor, Flake8TrioVisitor_cst +from .flake8triovisitor import Flake8AsyncVisitor, Flake8AsyncVisitor_cst from .helpers import utility_visitor, utility_visitor_cst if TYPE_CHECKING: @@ -21,7 +21,7 @@ @utility_visitor -class VisitorTypeTracker(Flake8TrioVisitor): +class VisitorTypeTracker(Flake8AsyncVisitor): def visit_AsyncFunctionDef( self, node: ast.AsyncFunctionDef | ast.FunctionDef | ast.Lambda ): @@ -101,7 +101,7 @@ def visit_With(self, node: ast.With | ast.AsyncWith): @utility_visitor -class VisitorAwaitModifier(Flake8TrioVisitor): +class VisitorAwaitModifier(Flake8AsyncVisitor): def visit_Await(self, node: ast.Await): if isinstance(node.value, ast.Call): # add attribute to indicate it's awaited @@ -109,7 +109,7 @@ def visit_Await(self, node: ast.Await): @utility_visitor -class VisitorLibraryHandler(Flake8TrioVisitor): +class VisitorLibraryHandler(Flake8AsyncVisitor): def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) # check whether library we're working towards has been explicitly @@ -126,7 +126,7 @@ def visit_Import(self, node: ast.Import): @utility_visitor_cst -class VisitorLibraryHandler_cst(Flake8TrioVisitor_cst): +class VisitorLibraryHandler_cst(Flake8AsyncVisitor_cst): def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) # check whether library we're working towards has been explicitly @@ -172,7 +172,7 @@ def _find_noqa(physical_line: str) -> Match[str] | None: @utility_visitor_cst -class NoqaHandler(Flake8TrioVisitor_cst): +class NoqaHandler(Flake8AsyncVisitor_cst): def visit_Comment(self, node: cst.Comment): noqa_match = _find_noqa(node.value) if noqa_match is None: diff --git a/flake8_trio/visitors/visitors.py b/flake8_trio/visitors/visitors.py index eb3623e..40d52a3 100644 --- a/flake8_trio/visitors/visitors.py +++ b/flake8_trio/visitors/visitors.py @@ -5,7 +5,7 @@ import ast from typing import TYPE_CHECKING, Any, cast -from .flake8triovisitor import Flake8TrioVisitor +from .flake8triovisitor import Flake8AsyncVisitor from .helpers import disabled_by_default, error_class, get_matching_call, has_decorator if TYPE_CHECKING: @@ -13,9 +13,9 @@ @error_class -class Visitor106(Flake8TrioVisitor): +class Visitor106(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO106": "{0} must be imported with `import {0}` for the linter to work.", + "ASYNC106": "{0} must be imported with `import {0}` for the linter to work.", } def visit_ImportFrom(self, node: ast.ImportFrom): @@ -29,9 +29,9 @@ def visit_Import(self, node: ast.Import): @error_class -class Visitor109(Flake8TrioVisitor): +class Visitor109(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO109": ( + "ASYNC109": ( "Async function definition with a `timeout` parameter - use " "`{}.[fail/move_on]_[after/at]` instead." ), @@ -50,9 +50,9 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef): @error_class -class Visitor110(Flake8TrioVisitor): +class Visitor110(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO110": ( + "ASYNC110": ( "`while : await {0}.sleep()` should be replaced by " "a `{0}.Event`." ), @@ -69,9 +69,9 @@ def visit_While(self, node: ast.While): @error_class -class Visitor112(Flake8TrioVisitor): +class Visitor112(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO112": ( + "ASYNC112": ( "Redundant nursery {}, consider replacing with directly awaiting " "the function call." ), @@ -121,9 +121,9 @@ def visit_With(self, node: ast.With | ast.AsyncWith): @error_class -class Visitor113(Flake8TrioVisitor): +class Visitor113(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO113": ( + "ASYNC113": ( "Dangerous `.start_soon()`, function might not be executed before" " `__aenter__` exits. Consider replacing with `.start()`." ), @@ -188,11 +188,11 @@ def is_nursery_call(node: ast.expr): # name, so may miss cases where functions are named the same in different modules/classes # and option names are specified including the module name. @error_class -class Visitor114(Flake8TrioVisitor): +class Visitor114(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO114": ( + "ASYNC114": ( "Startable function {} not in --startable-in-context-manager parameter " - "list, please add it so TRIO113 can catch errors using it." + "list, please add it so ASYNC113 can catch errors using it." ), } @@ -210,9 +210,9 @@ def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef): # Suggests replacing all `trio.sleep(0)` with the more suggestive # `trio.lowlevel.checkpoint()` @error_class -class Visitor115(Flake8TrioVisitor): +class Visitor115(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO115": "Use `{0}.lowlevel.checkpoint()` instead of `{0}.sleep(0)`.", + "ASYNC115": "Use `{0}.lowlevel.checkpoint()` instead of `{0}.sleep(0)`.", } def visit_Call(self, node: ast.Call): @@ -227,9 +227,9 @@ def visit_Call(self, node: ast.Call): @error_class -class Visitor116(Flake8TrioVisitor): +class Visitor116(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO116": ( + "ASYNC116": ( "{0}.sleep() with >24 hour interval should usually be " "`{0}.sleep_forever()`." ), @@ -266,9 +266,9 @@ def visit_Call(self, node: ast.Call): @error_class @disabled_by_default -class Visitor900(Flake8TrioVisitor): +class Visitor900(Flake8AsyncVisitor): error_codes: Mapping[str, str] = { - "TRIO900": "Async generator without `@asynccontextmanager` not allowed." + "ASYNC900": "Async generator without `@asynccontextmanager` not allowed." } def __init__(self, *args: Any, **kwargs: Any): diff --git a/pyproject.toml b/pyproject.toml index 668a4d7..d85b63a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ ignore = [ "COM", # flake8-comma, handled by black "ANN", # annotations, handled by pyright/mypy "T20", # flake8-print - "TID252", # relative imports from parent modules https://github.com/Zac-HD/flake8-trio/pull/196#discussion_r1200413372 + "TID252", # relative imports from parent modules https://github.com/python-trio/flake8-trio/pull/196#discussion_r1200413372 "D101", "D102", "D103", diff --git a/setup.py b/setup.py index 86db921..6e1555f 100755 --- a/setup.py +++ b/setup.py @@ -20,12 +20,12 @@ def local_file(name: str) -> Path: setup( - name="flake8-trio", + name="flake8-async", version=__version__, author="Zac Hatfield-Dodds, John Litborn, and Contributors", author_email="zac@zhd.dev", packages=find_packages(include=["flake8_trio", "flake8_trio.*"]), - url="https://github.com/Zac-HD/flake8-trio", + url="https://github.com/python-trio/flake8-trio", license="MIT", description="A highly opinionated flake8 plugin for Trio-related problems.", zip_safe=False, @@ -51,7 +51,10 @@ def local_file(name: str) -> Path: ), long_description_content_type="text/markdown", entry_points={ - "flake8.extension": ["TRI = flake8_trio:Plugin"], - "console_scripts": ["flake8-trio=flake8_trio:main"], + # You're not allowed to register error codes longer than 3 characters. But flake8 + # doesn't enforce anything about the characters trailing the code, so we can say + # the code is ASY and then just always happen to print NCxxx directly after it. + "flake8.extension": ["ASY = flake8_trio:Plugin"], + "console_scripts": ["flake8-async=flake8_trio:main"], }, ) diff --git a/tests/autofix_files/noqa.py b/tests/autofix_files/noqa.py index c411f99..7d69ee0 100644 --- a/tests/autofix_files/noqa.py +++ b/tests/autofix_files/noqa.py @@ -1,6 +1,6 @@ # AUTOFIX # NOANYIO # TODO -# ARG --enable=TRIO100,TRIO911 +# ARG --enable=ASYNC100,ASYNC911 from typing import Any import trio @@ -9,8 +9,8 @@ # fmt: off async def foo_no_noqa(): await trio.lowlevel.checkpoint() - # TRIO100: 9, 'trio', 'fail_after' - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + # ASYNC100: 9, 'trio', 'fail_after' + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() @@ -21,48 +21,48 @@ async def foo_noqa_bare(): async def foo_noqa_100(): - with trio.fail_after(5): # noqa: TRIO100 + with trio.fail_after(5): # noqa: ASYNC100 await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() async def foo_noqa_911(): - # TRIO100: 9, 'trio', 'fail_after' - yield # noqa: TRIO911 + # ASYNC100: 9, 'trio', 'fail_after' + yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() async def foo_noqa_100_911(): - with trio.fail_after(5): # noqa: TRIO100, TRIO911 - yield # noqa: TRIO911 + with trio.fail_after(5): # noqa: ASYNC100, ASYNC911 + yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() async def foo_noqa_100_911_500(): - with trio.fail_after(5): # noqa: TRIO100, TRIO911 , TRIO500,,, - yield # noqa: TRIO100, TRIO911 , TRIO500,,, + with trio.fail_after(5): # noqa: ASYNC100, ASYNC911 , ASYNC500,,, + yield # noqa: ASYNC100, ASYNC911 , ASYNC500,,, await trio.lowlevel.checkpoint() # fmt: on # check that noqas work after line numbers have been modified in a different visitor # this will remove one line -# TRIO100: 5, 'trio', 'fail_after' +# ASYNC100: 5, 'trio', 'fail_after' ... async def foo_changed_lineno(): - yield # noqa: TRIO911 + yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() # this will add two lines -async def foo_changing_lineno(): # TRIO911: 0, "exit", Statement("yield", lineno+1) +async def foo_changing_lineno(): # ASYNC911: 0, "exit", Statement("yield", lineno+1) await trio.lowlevel.checkpoint() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-1) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-1) await trio.lowlevel.checkpoint() -with trio.fail_after(5): # noqa: TRIO100 +with trio.fail_after(5): # noqa: ASYNC100 ... diff --git a/tests/autofix_files/noqa.py.diff b/tests/autofix_files/noqa.py.diff index 2fd89e1..671616a 100644 --- a/tests/autofix_files/noqa.py.diff +++ b/tests/autofix_files/noqa.py.diff @@ -4,28 +4,28 @@ # fmt: off async def foo_no_noqa(): -- with trio.fail_after(5): # TRIO100: 9, 'trio', 'fail_after' -- yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) +- with trio.fail_after(5): # ASYNC100: 9, 'trio', 'fail_after' +- yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) + await trio.lowlevel.checkpoint() -+ # TRIO100: 9, 'trio', 'fail_after' -+ yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) ++ # ASYNC100: 9, 'trio', 'fail_after' ++ yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() @@ x,13 x,14 @@ async def foo_noqa_100(): - with trio.fail_after(5): # noqa: TRIO100 + with trio.fail_after(5): # noqa: ASYNC100 + await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() async def foo_noqa_911(): -- with trio.fail_after(5): # TRIO100: 9, 'trio', 'fail_after' -- yield # noqa: TRIO911 -+ # TRIO100: 9, 'trio', 'fail_after' -+ yield # noqa: TRIO911 +- with trio.fail_after(5): # ASYNC100: 9, 'trio', 'fail_after' +- yield # noqa: ASYNC911 ++ # ASYNC100: 9, 'trio', 'fail_after' ++ yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() @@ -33,9 +33,9 @@ # check that noqas work after line numbers have been modified in a different visitor # this will remove one line --with trio.fail_after(5): # TRIO100: 5, 'trio', 'fail_after' +-with trio.fail_after(5): # ASYNC100: 5, 'trio', 'fail_after' - ... -+# TRIO100: 5, 'trio', 'fail_after' ++# ASYNC100: 5, 'trio', 'fail_after' +... @@ -43,10 +43,10 @@ @@ x,7 x,9 @@ # this will add two lines - async def foo_changing_lineno(): # TRIO911: 0, "exit", Statement("yield", lineno+1) + async def foo_changing_lineno(): # ASYNC911: 0, "exit", Statement("yield", lineno+1) + await trio.lowlevel.checkpoint() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-1) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-1) + await trio.lowlevel.checkpoint() - with trio.fail_after(5): # noqa: TRIO100 + with trio.fail_after(5): # noqa: ASYNC100 diff --git a/tests/autofix_files/noqa_testing.py b/tests/autofix_files/noqa_testing.py index 4ecc03f..703a48c 100644 --- a/tests/autofix_files/noqa_testing.py +++ b/tests/autofix_files/noqa_testing.py @@ -1,10 +1,10 @@ # AUTOFIX # NOANYIO # TODO -# ARG --enable=TRIO911 +# ARG --enable=ASYNC911 import trio async def foo_0(): await trio.lowlevel.checkpoint() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-1) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-1) await trio.lowlevel.checkpoint() diff --git a/tests/autofix_files/noqa_testing.py.diff b/tests/autofix_files/noqa_testing.py.diff index a154297..13aa612 100644 --- a/tests/autofix_files/noqa_testing.py.diff +++ b/tests/autofix_files/noqa_testing.py.diff @@ -5,5 +5,5 @@ async def foo_0(): + await trio.lowlevel.checkpoint() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-1) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-1) await trio.lowlevel.checkpoint() diff --git a/tests/autofix_files/trio100.py b/tests/autofix_files/trio100.py index 458b06c..f899fe3 100644 --- a/tests/autofix_files/trio100.py +++ b/tests/autofix_files/trio100.py @@ -72,7 +72,7 @@ async def foo(): # Seems like the inner context manager 'hides' the checkpoint. async def does_contain_checkpoints(): - with trio.fail_after(1): # false-alarm TRIO100 + with trio.fail_after(1): # false-alarm ASYNC100 with trio.CancelScope(): # or any other context manager await trio.sleep_forever() diff --git a/tests/autofix_files/trio100_simple_autofix.py b/tests/autofix_files/trio100_simple_autofix.py index 39d01eb..450e9c1 100644 --- a/tests/autofix_files/trio100_simple_autofix.py +++ b/tests/autofix_files/trio100_simple_autofix.py @@ -55,5 +55,5 @@ # same-line with # fmt: off -print(1) # TRIO100: 5, 'trio', 'fail_after' +print(1) # ASYNC100: 5, 'trio', 'fail_after' # fmt: on diff --git a/tests/autofix_files/trio100_simple_autofix.py.diff b/tests/autofix_files/trio100_simple_autofix.py.diff index b3b6066..118e77a 100644 --- a/tests/autofix_files/trio100_simple_autofix.py.diff +++ b/tests/autofix_files/trio100_simple_autofix.py.diff @@ -87,6 +87,6 @@ # same-line with # fmt: off --with trio.fail_after(5): print(1) # TRIO100: 5, 'trio', 'fail_after' -+print(1) # TRIO100: 5, 'trio', 'fail_after' +-with trio.fail_after(5): print(1) # ASYNC100: 5, 'trio', 'fail_after' ++print(1) # ASYNC100: 5, 'trio', 'fail_after' # fmt: on diff --git a/tests/autofix_files/trio910.py b/tests/autofix_files/trio910.py index 3e23199..1a9e698 100644 --- a/tests/autofix_files/trio910.py +++ b/tests/autofix_files/trio910.py @@ -20,7 +20,7 @@ async def foo() -> Any: def bar() -> Any: ... -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 # ARG --no-checkpoint-warning-decorator=custom_disabled_decorator diff --git a/tests/autofix_files/trio911.py b/tests/autofix_files/trio911.py index 3cfcca1..720a981 100644 --- a/tests/autofix_files/trio911.py +++ b/tests/autofix_files/trio911.py @@ -6,7 +6,7 @@ _: Any = "" -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 async def foo() -> Any: diff --git a/tests/autofix_files/trio91x_autofix.py b/tests/autofix_files/trio91x_autofix.py index aa58e88..d7d99b3 100644 --- a/tests/autofix_files/trio91x_autofix.py +++ b/tests/autofix_files/trio91x_autofix.py @@ -6,7 +6,7 @@ So we make sure that import is added after it. """ # isort: skip_file -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 from typing import Any import trio @@ -19,7 +19,7 @@ async def foo() -> Any: await foo() -async def foo1(): # TRIO910: 0, "exit", Statement("function definition", lineno) +async def foo1(): # ASYNC910: 0, "exit", Statement("function definition", lineno) bar() await trio.lowlevel.checkpoint() @@ -27,33 +27,33 @@ async def foo1(): # TRIO910: 0, "exit", Statement("function definition", lineno async def foo_return(): bar() await trio.lowlevel.checkpoint() - return # TRIO910: 4, "return", Statement("function definition", lineno-2) + return # ASYNC910: 4, "return", Statement("function definition", lineno-2) -async def foo_yield(): # TRIO911: 0, "exit", Statement("yield", lineno+2) +async def foo_yield(): # ASYNC911: 0, "exit", Statement("yield", lineno+2) bar() await trio.lowlevel.checkpoint() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() async def foo_if(): if bar(): await trio.lowlevel.checkpoint() - return # TRIO910: 8, "return", Statement("function definition", lineno-2) + return # ASYNC910: 8, "return", Statement("function definition", lineno-2) elif bar(): await trio.lowlevel.checkpoint() - return # TRIO910: 8, "return", Statement("function definition", lineno-4) + return # ASYNC910: 8, "return", Statement("function definition", lineno-4) else: await trio.lowlevel.checkpoint() - return # TRIO910: 8, "return", Statement("function definition", lineno-6) + return # ASYNC910: 8, "return", Statement("function definition", lineno-6) async def foo_while(): await foo() while True: await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("yield", lineno) + yield # ASYNC911: 8, "yield", Statement("yield", lineno) async def foo_while2(): @@ -76,10 +76,10 @@ async def foo_while4(): while True: if bar(): await trio.lowlevel.checkpoint() - yield # TRIO911: 12, "yield", Statement("yield", lineno) # TRIO911: 12, "yield", Statement("yield", lineno+2) # TRIO911: 12, "yield", Statement("function definition", lineno-3) + yield # ASYNC911: 12, "yield", Statement("yield", lineno) # ASYNC911: 12, "yield", Statement("yield", lineno+2) # ASYNC911: 12, "yield", Statement("function definition", lineno-3) if bar(): await trio.lowlevel.checkpoint() - yield # TRIO911: 12, "yield", Statement("yield", lineno) # TRIO911: 12, "yield", Statement("yield", lineno-2) # TRIO911: 12, "yield", Statement("function definition", lineno-5) # TRIO911: 12, "yield", Statement("yield", lineno-2) + yield # ASYNC911: 12, "yield", Statement("yield", lineno) # ASYNC911: 12, "yield", Statement("yield", lineno-2) # ASYNC911: 12, "yield", Statement("function definition", lineno-5) # ASYNC911: 12, "yield", Statement("yield", lineno-2) # this warns about the yield on lineno-2 twice, since it can arrive here from it in two different ways @@ -87,19 +87,19 @@ async def foo_while4(): async def foo_nested_while(): while True: await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) while True: await trio.lowlevel.checkpoint() - yield # TRIO911: 12, "yield", Statement("yield", lineno-2) + yield # ASYNC911: 12, "yield", Statement("yield", lineno-2) while True: await trio.lowlevel.checkpoint() - yield # TRIO911: 16, "yield", Statement("yield", lineno-2) # TRIO911: 16, "yield", Statement("yield", lineno) + yield # ASYNC911: 16, "yield", Statement("yield", lineno-2) # ASYNC911: 16, "yield", Statement("yield", lineno) async def foo_while_nested_func(): while True: await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) # TRIO911: 8, "yield", Statement("yield", lineno) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) # ASYNC911: 8, "yield", Statement("yield", lineno) async def bar(): while bar(): diff --git a/tests/autofix_files/trio91x_autofix.py.diff b/tests/autofix_files/trio91x_autofix.py.diff index 3b22f13..2c84b10 100644 --- a/tests/autofix_files/trio91x_autofix.py.diff +++ b/tests/autofix_files/trio91x_autofix.py.diff @@ -1,7 +1,7 @@ --- +++ @@ x,6 x,7 @@ - # ARG --enable=TRIO910,TRIO911 + # ARG --enable=ASYNC910,ASYNC911 from typing import Any +import trio @@ -10,7 +10,7 @@ def bar() -> Any: ... @@ x,30 x,38 @@ - async def foo1(): # TRIO910: 0, "exit", Statement("function definition", lineno) + async def foo1(): # ASYNC910: 0, "exit", Statement("function definition", lineno) bar() + await trio.lowlevel.checkpoint() @@ -18,33 +18,33 @@ async def foo_return(): bar() + await trio.lowlevel.checkpoint() - return # TRIO910: 4, "return", Statement("function definition", lineno-2) + return # ASYNC910: 4, "return", Statement("function definition", lineno-2) - async def foo_yield(): # TRIO911: 0, "exit", Statement("yield", lineno+2) + async def foo_yield(): # ASYNC911: 0, "exit", Statement("yield", lineno+2) bar() + await trio.lowlevel.checkpoint() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-2) + await trio.lowlevel.checkpoint() async def foo_if(): if bar(): + await trio.lowlevel.checkpoint() - return # TRIO910: 8, "return", Statement("function definition", lineno-2) + return # ASYNC910: 8, "return", Statement("function definition", lineno-2) elif bar(): + await trio.lowlevel.checkpoint() - return # TRIO910: 8, "return", Statement("function definition", lineno-4) + return # ASYNC910: 8, "return", Statement("function definition", lineno-4) else: + await trio.lowlevel.checkpoint() - return # TRIO910: 8, "return", Statement("function definition", lineno-6) + return # ASYNC910: 8, "return", Statement("function definition", lineno-6) async def foo_while(): await foo() while True: + await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("yield", lineno) + yield # ASYNC911: 8, "yield", Statement("yield", lineno) @@ x,8 x,10 @@ @@ -52,10 +52,10 @@ while True: if bar(): + await trio.lowlevel.checkpoint() - yield # TRIO911: 12, "yield", Statement("yield", lineno) # TRIO911: 12, "yield", Statement("yield", lineno+2) # TRIO911: 12, "yield", Statement("function definition", lineno-3) + yield # ASYNC911: 12, "yield", Statement("yield", lineno) # ASYNC911: 12, "yield", Statement("yield", lineno+2) # ASYNC911: 12, "yield", Statement("function definition", lineno-3) if bar(): + await trio.lowlevel.checkpoint() - yield # TRIO911: 12, "yield", Statement("yield", lineno) # TRIO911: 12, "yield", Statement("yield", lineno-2) # TRIO911: 12, "yield", Statement("function definition", lineno-5) # TRIO911: 12, "yield", Statement("yield", lineno-2) + yield # ASYNC911: 12, "yield", Statement("yield", lineno) # ASYNC911: 12, "yield", Statement("yield", lineno-2) # ASYNC911: 12, "yield", Statement("function definition", lineno-5) # ASYNC911: 12, "yield", Statement("yield", lineno-2) # this warns about the yield on lineno-2 twice, since it can arrive here from it in two different ways @@ x,15 x,19 @@ @@ -63,18 +63,18 @@ async def foo_nested_while(): while True: + await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) while True: + await trio.lowlevel.checkpoint() - yield # TRIO911: 12, "yield", Statement("yield", lineno-2) + yield # ASYNC911: 12, "yield", Statement("yield", lineno-2) while True: + await trio.lowlevel.checkpoint() - yield # TRIO911: 16, "yield", Statement("yield", lineno-2) # TRIO911: 16, "yield", Statement("yield", lineno) + yield # ASYNC911: 16, "yield", Statement("yield", lineno-2) # ASYNC911: 16, "yield", Statement("yield", lineno) async def foo_while_nested_func(): while True: + await trio.lowlevel.checkpoint() - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) # TRIO911: 8, "yield", Statement("yield", lineno) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) # ASYNC911: 8, "yield", Statement("yield", lineno) async def bar(): diff --git a/tests/conftest.py b/tests/conftest.py index c838e9a..de4d780 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,7 +20,7 @@ def pytest_addoption(parser: pytest.Parser): ) parser.addoption( "--enable-codes", - default="TRIO", + default="ASYNC", help="select error codes whose visitors to run.", ) diff --git a/tests/eval_files/anyio_trio.py b/tests/eval_files/anyio_trio.py index 0394c59..93e8dba 100644 --- a/tests/eval_files/anyio_trio.py +++ b/tests/eval_files/anyio_trio.py @@ -1,5 +1,5 @@ # type: ignore -# ARG --enable=TRIO220 +# ARG --enable=ASYNC220 # NOTRIO # anyio eval will automatically prepend this test with `--anyio` @@ -7,4 +7,4 @@ async def foo(): - subprocess.Popen() # TRIO220: 4, 'subprocess.Popen', "[anyio|trio]" + subprocess.Popen() # ASYNC220: 4, 'subprocess.Popen', "[anyio|trio]" diff --git a/tests/eval_files/no_library.py b/tests/eval_files/no_library.py index ee435e1..de648ed 100644 --- a/tests/eval_files/no_library.py +++ b/tests/eval_files/no_library.py @@ -1,5 +1,5 @@ # type: ignore -# ARG --enable=TRIO220 +# ARG --enable=ASYNC220 # NOANYIO async def foo(): - subprocess.Popen() # TRIO220: 4, 'subprocess.Popen', "trio" + subprocess.Popen() # ASYNC220: 4, 'subprocess.Popen', "trio" diff --git a/tests/eval_files/noqa.py b/tests/eval_files/noqa.py index 59516dc..4cde1a7 100644 --- a/tests/eval_files/noqa.py +++ b/tests/eval_files/noqa.py @@ -1,6 +1,6 @@ # AUTOFIX # NOANYIO # TODO -# ARG --enable=TRIO100,TRIO911 +# ARG --enable=ASYNC100,ASYNC911 from typing import Any import trio @@ -8,8 +8,8 @@ # fmt: off async def foo_no_noqa(): - with trio.fail_after(5): # TRIO100: 9, 'trio', 'fail_after' - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + with trio.fail_after(5): # ASYNC100: 9, 'trio', 'fail_after' + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() @@ -20,45 +20,45 @@ async def foo_noqa_bare(): async def foo_noqa_100(): - with trio.fail_after(5): # noqa: TRIO100 - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + with trio.fail_after(5): # noqa: ASYNC100 + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) await trio.lowlevel.checkpoint() async def foo_noqa_911(): - with trio.fail_after(5): # TRIO100: 9, 'trio', 'fail_after' - yield # noqa: TRIO911 + with trio.fail_after(5): # ASYNC100: 9, 'trio', 'fail_after' + yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() async def foo_noqa_100_911(): - with trio.fail_after(5): # noqa: TRIO100, TRIO911 - yield # noqa: TRIO911 + with trio.fail_after(5): # noqa: ASYNC100, ASYNC911 + yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() async def foo_noqa_100_911_500(): - with trio.fail_after(5): # noqa: TRIO100, TRIO911 , TRIO500,,, - yield # noqa: TRIO100, TRIO911 , TRIO500,,, + with trio.fail_after(5): # noqa: ASYNC100, ASYNC911 , ASYNC500,,, + yield # noqa: ASYNC100, ASYNC911 , ASYNC500,,, await trio.lowlevel.checkpoint() # fmt: on # check that noqas work after line numbers have been modified in a different visitor # this will remove one line -with trio.fail_after(5): # TRIO100: 5, 'trio', 'fail_after' +with trio.fail_after(5): # ASYNC100: 5, 'trio', 'fail_after' ... async def foo_changed_lineno(): - yield # noqa: TRIO911 + yield # noqa: ASYNC911 await trio.lowlevel.checkpoint() # this will add two lines -async def foo_changing_lineno(): # TRIO911: 0, "exit", Statement("yield", lineno+1) - yield # TRIO911: 4, "yield", Statement("function definition", lineno-1) +async def foo_changing_lineno(): # ASYNC911: 0, "exit", Statement("yield", lineno+1) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-1) -with trio.fail_after(5): # noqa: TRIO100 +with trio.fail_after(5): # noqa: ASYNC100 ... diff --git a/tests/eval_files/noqa_no_autofix.py b/tests/eval_files/noqa_no_autofix.py index c179b75..36f98bf 100644 --- a/tests/eval_files/noqa_no_autofix.py +++ b/tests/eval_files/noqa_no_autofix.py @@ -1,4 +1,4 @@ -# ARG --enable=TRIO102 +# ARG --enable=ASYNC102 import trio from typing import Any @@ -12,14 +12,14 @@ async def foo_no_noqa_102(): try: pass finally: - await foo() # TRIO102: 8, Statement("try/finally", lineno-3) + await foo() # ASYNC102: 8, Statement("try/finally", lineno-3) async def foo_noqa_102(): try: pass finally: - await foo() # noqa: TRIO102 + await foo() # noqa: ASYNC102 async def foo_bare_noqa_102(): diff --git a/tests/eval_files/noqa_testing.py b/tests/eval_files/noqa_testing.py index 7c4b230..746f2c6 100644 --- a/tests/eval_files/noqa_testing.py +++ b/tests/eval_files/noqa_testing.py @@ -1,9 +1,9 @@ # AUTOFIX # NOANYIO # TODO -# ARG --enable=TRIO911 +# ARG --enable=ASYNC911 import trio async def foo_0(): - yield # TRIO911: 4, "yield", Statement("function definition", lineno-1) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-1) await trio.lowlevel.checkpoint() diff --git a/tests/eval_files/trio100.py b/tests/eval_files/trio100.py index 8cb83cf..acf6e70 100644 --- a/tests/eval_files/trio100.py +++ b/tests/eval_files/trio100.py @@ -72,7 +72,7 @@ async def foo(): # Seems like the inner context manager 'hides' the checkpoint. async def does_contain_checkpoints(): - with trio.fail_after(1): # false-alarm TRIO100 + with trio.fail_after(1): # false-alarm ASYNC100 with trio.CancelScope(): # or any other context manager await trio.sleep_forever() diff --git a/tests/eval_files/trio100_simple_autofix.py b/tests/eval_files/trio100_simple_autofix.py index 0c39bfe..95286b2 100644 --- a/tests/eval_files/trio100_simple_autofix.py +++ b/tests/eval_files/trio100_simple_autofix.py @@ -54,5 +54,5 @@ # same-line with # fmt: off -with trio.fail_after(5): print(1) # TRIO100: 5, 'trio', 'fail_after' +with trio.fail_after(5): print(1) # ASYNC100: 5, 'trio', 'fail_after' # fmt: on diff --git a/tests/eval_files/trio102.py b/tests/eval_files/trio102.py index 2d501f1..c891bdc 100644 --- a/tests/eval_files/trio102.py +++ b/tests/eval_files/trio102.py @@ -157,7 +157,7 @@ async def foo3(): s.shield = True await foo() # safe with trio.move_on_after(30) as s, trio.fail_after(5): - await foo() # TRIO102: 12, Statement("try/finally", lineno-7) + await foo() # ASYNC102: 12, Statement("try/finally", lineno-7) with open(""), trio.CancelScope(deadline=30, shield=True): await foo() # safe with trio.fail_after(5), trio.move_on_after(30) as s: diff --git a/tests/eval_files/trio102_anyio.py b/tests/eval_files/trio102_anyio.py index ec71f48..6110225 100644 --- a/tests/eval_files/trio102_anyio.py +++ b/tests/eval_files/trio102_anyio.py @@ -2,7 +2,7 @@ import anyio from anyio import get_cancelled_exc_class -# this one is fine to also run with TRIO +# this one is fine to also run with ASYNC async def foo(): ... diff --git a/tests/eval_files/trio103.py b/tests/eval_files/trio103.py index a4e39be..82bf8e1 100644 --- a/tests/eval_files/trio103.py +++ b/tests/eval_files/trio103.py @@ -1,4 +1,4 @@ -# ARG --enable=TRIO103,TRIO104 +# ARG --enable=ASYNC103,ASYNC104 from typing import Any @@ -19,7 +19,7 @@ def foo() -> Any: ... except ( SyntaxError, ValueError, - BaseException, # TRIO103_trio: 4, "BaseException" + BaseException, # ASYNC103_trio: 4, "BaseException" ) as p: ... @@ -40,13 +40,13 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" ... # if try: ... -except BaseException as e: # TRIO103_trio: 7, "BaseException" +except BaseException as e: # ASYNC103_trio: 7, "BaseException" if foo(): raise e elif foo(): @@ -56,7 +56,7 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" if True: raise @@ -74,7 +74,7 @@ def foo() -> Any: ... # raises inside the body are never guaranteed to run and are ignored try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" while foo(): raise @@ -97,7 +97,7 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" while ...: if ...: break @@ -107,7 +107,7 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for _ in "": if ...: break @@ -132,13 +132,13 @@ def foo() -> Any: ... # But is a very weird pattern that we don't handle. try: ... -except BaseException as e: # TRIO103_trio: 7, "BaseException" +except BaseException as e: # ASYNC103_trio: 7, "BaseException" try: raise e except ValueError: raise e except: - raise e # TRIO104: 8 + raise e # ASYNC104: 8 try: ... @@ -175,7 +175,7 @@ def foo() -> Any: ... try: ... except ValueError as g: - raise g # TRIO104: 8 + raise g # ASYNC104: 8 except BaseException as h: raise h # error? currently treated as safe raise e @@ -183,7 +183,7 @@ def foo() -> Any: ... # bare except, equivalent to `except baseException` try: ... -except: # TRIO103_trio: 0, "bare except" +except: # ASYNC103_trio: 0, "bare except" ... try: @@ -198,7 +198,7 @@ def foo() -> Any: ... except ( my_super_mega_long_exception_so_it_gets_split, SyntaxError, - BaseException, # TRIO103_trio: 4, "BaseException" + BaseException, # ASYNC103_trio: 4, "BaseException" ValueError, BaseException, # no complaint on this line ): @@ -223,13 +223,13 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for i in [1, 2, 3]: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for i in [1, 2, 3]: if ...: continue @@ -237,7 +237,7 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for i in foo(): raise @@ -267,19 +267,19 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for i in range(foo()): raise try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for i in []: raise try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" for i in {}: raise @@ -297,7 +297,7 @@ def foo() -> Any: ... try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" while True: if ...: break @@ -322,7 +322,7 @@ def foo() -> Any: ... # don't throw multiple 103's even if `Cancelled` wasn't properly handled. try: ... -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" ... except: # now silent ... @@ -333,10 +333,10 @@ def foo() -> Any: ... ... except BaseException: raise -except BaseException: # TRIO103_trio: 7, "BaseException" +except BaseException: # ASYNC103_trio: 7, "BaseException" ... except: try: ... - except BaseException: # TRIO103_trio: 11, "BaseException" + except BaseException: # ASYNC103_trio: 11, "BaseException" ... diff --git a/tests/eval_files/trio103_both_imported.py b/tests/eval_files/trio103_both_imported.py index bc57a9c..720fa81 100644 --- a/tests/eval_files/trio103_both_imported.py +++ b/tests/eval_files/trio103_both_imported.py @@ -4,32 +4,38 @@ try: ... -except trio.Cancelled: # TRIO103: 7, "trio.Cancelled" +except trio.Cancelled: # ASYNC103: 7, "trio.Cancelled" ... -except anyio.get_cancelled_exc_class(): # TRIO103: 7, "anyio.get_cancelled_exc_class()" +except ( + anyio.get_cancelled_exc_class() # ASYNC103: 4, "anyio.get_cancelled_exc_class()" +): ... except: # safe ... try: ... -except anyio.get_cancelled_exc_class(): # TRIO103: 7, "anyio.get_cancelled_exc_class()" +except ( + anyio.get_cancelled_exc_class() # ASYNC103: 4, "anyio.get_cancelled_exc_class()" +): ... -except trio.Cancelled: # TRIO103: 7, "trio.Cancelled" +except trio.Cancelled: # ASYNC103: 7, "trio.Cancelled" ... except: # safe ... try: ... -except anyio.get_cancelled_exc_class(): # TRIO103: 7, "anyio.get_cancelled_exc_class()" +except ( + anyio.get_cancelled_exc_class() # ASYNC103: 4, "anyio.get_cancelled_exc_class()" +): ... except: # safe ? ... try: ... -except trio.Cancelled: # TRIO103: 7, "trio.Cancelled" +except trio.Cancelled: # ASYNC103: 7, "trio.Cancelled" ... except: # safe ? ... @@ -37,10 +43,10 @@ # Check we get the proper suggestion when both are imported try: ... -except BaseException: # TRIO103_anyio_trio: 7, "BaseException" +except BaseException: # ASYNC103_anyio_trio: 7, "BaseException" ... try: ... -except: # TRIO103_anyio_trio: 0, "bare except" +except: # ASYNC103_anyio_trio: 0, "bare except" ... diff --git a/tests/eval_files/trio103_no_104.py b/tests/eval_files/trio103_no_104.py index 63c37b1..4eb7e7e 100644 --- a/tests/eval_files/trio103_no_104.py +++ b/tests/eval_files/trio103_no_104.py @@ -1,4 +1,4 @@ -# ARG --enable=TRIO103 +# ARG --enable=ASYNC103 # check that partly disabling a visitor works from typing import Any @@ -11,10 +11,10 @@ def foo() -> Any: ... # But is a very weird pattern that we don't handle. try: ... -except BaseException as e: # TRIO103_trio: 7, "BaseException" +except BaseException as e: # ASYNC103_trio: 7, "BaseException" try: raise e except ValueError: raise e except: - raise e # disabled TRIO104 error + raise e # disabled ASYNC104 error diff --git a/tests/eval_files/trio103_trio.py b/tests/eval_files/trio103_trio.py index 870acc9..82aad9b 100644 --- a/tests/eval_files/trio103_trio.py +++ b/tests/eval_files/trio103_trio.py @@ -1,4 +1,4 @@ -# ARG --enable=TRIO103,TRIO104 +# ARG --enable=ASYNC103,ASYNC104 # NOANYIO from typing import Any @@ -17,7 +17,7 @@ def foo() -> Any: ... except ( SyntaxError, ValueError, - trio.Cancelled, # TRIO103: 4, "trio.Cancelled" + trio.Cancelled, # ASYNC103: 4, "trio.Cancelled" ) as p: ... @@ -38,7 +38,7 @@ def foo() -> Any: ... try: ... -except trio.Cancelled: # TRIO103: 7, "trio.Cancelled" +except trio.Cancelled: # ASYNC103: 7, "trio.Cancelled" ... # Issue #106, false alarm on excepts after `Cancelled` has already been handled @@ -69,7 +69,7 @@ def foo() -> Any: ... # don't throw multiple 103's even if `Cancelled` wasn't properly handled. try: ... -except trio.Cancelled: # TRIO103: 7, "trio.Cancelled" +except trio.Cancelled: # ASYNC103: 7, "trio.Cancelled" ... except BaseException: # now silent ... @@ -82,10 +82,10 @@ def foo() -> Any: ... ... except trio.Cancelled: raise -except trio.Cancelled: # TRIO103: 7, "trio.Cancelled" +except trio.Cancelled: # ASYNC103: 7, "trio.Cancelled" ... except: try: ... - except trio.Cancelled: # TRIO103: 11, "trio.Cancelled" + except trio.Cancelled: # ASYNC103: 11, "trio.Cancelled" ... diff --git a/tests/eval_files/trio104.py b/tests/eval_files/trio104.py index 64c4ea0..1701f26 100644 --- a/tests/eval_files/trio104.py +++ b/tests/eval_files/trio104.py @@ -1,4 +1,4 @@ -# ARG --enable=TRIO103,TRIO104 +# ARG --enable=ASYNC103,ASYNC104 try: ... # raise different exception @@ -25,7 +25,7 @@ # But is a very weird pattern that we don't handle. try: ... -except BaseException as e: # TRIO103_trio: 7, "BaseException" +except BaseException as e: # ASYNC103_trio: 7, "BaseException" try: raise e except ValueError: @@ -90,13 +90,13 @@ def foo(): def foo2(): try: ... - except BaseException: # TRIO103_trio: 11, "BaseException" + except BaseException: # ASYNC103_trio: 11, "BaseException" return # error: 8 # check that we properly iterate over all nodes in try try: ... - except BaseException: # TRIO103_trio: 11, "BaseException" + except BaseException: # ASYNC103_trio: 11, "BaseException" try: return # error: 12 except ValueError: @@ -179,7 +179,7 @@ def foo_cancelled_handled(): def foo_cancelled_not_handled(): try: ... - except BaseException: # TRIO103_trio: 11, "BaseException" - return # TRIO104: 8 + except BaseException: # ASYNC103_trio: 11, "BaseException" + return # ASYNC104: 8 except: return # would otherwise error diff --git a/tests/eval_files/trio112.py b/tests/eval_files/trio112.py index dbd7982..bb97bd2 100644 --- a/tests/eval_files/trio112.py +++ b/tests/eval_files/trio112.py @@ -22,7 +22,7 @@ async def foo(): # weird ones with multiple `withitem`s -# but if split among several `with` they'd all be treated as error (or TRIO111), so +# but if split among several `with` they'd all be treated as error (or ASYNC111), so # treating as error for now. with trio.open_nursery() as n, trio.open("") as n: # error: 5, "n" n.start(...) diff --git a/tests/eval_files/trio113.py b/tests/eval_files/trio113.py index c5d606d..998e9e3 100644 --- a/tests/eval_files/trio113.py +++ b/tests/eval_files/trio113.py @@ -10,17 +10,17 @@ @asynccontextmanager async def foo(): with trio.open_nursery() as bar: - bar.start_soon(trio.run_process) # TRIO113: 8 + bar.start_soon(trio.run_process) # ASYNC113: 8 async with trio.open_nursery() as bar: - bar.start_soon(trio.run_process) # TRIO113: 8 + bar.start_soon(trio.run_process) # ASYNC113: 8 async with anyio.create_task_group() as bar_tg: - bar_tg.start_soon(anyio.run_process) # TRIO113: 8 + bar_tg.start_soon(anyio.run_process) # ASYNC113: 8 boo: trio.Nursery = ... # type: ignore - boo.start_soon(trio.run_process) # TRIO113: 4 + boo.start_soon(trio.run_process) # ASYNC113: 4 boo_anyio: anyio.TaskGroup = ... # type: ignore - boo_anyio.start_soon(anyio.run_process) # TRIO113: 4 + boo_anyio.start_soon(anyio.run_process) # ASYNC113: 4 yield diff --git a/tests/eval_files/trio118.py b/tests/eval_files/trio118.py index 1545fb2..eebc00a 100644 --- a/tests/eval_files/trio118.py +++ b/tests/eval_files/trio118.py @@ -1,17 +1,17 @@ from typing import Any import anyio -from anyio import get_cancelled_exc_class, get_cancelled_exc_class as foo # TRIO118: 0 +from anyio import get_cancelled_exc_class, get_cancelled_exc_class as foo # ASYNC118: 0 -bar1 = anyio.get_cancelled_exc_class # TRIO118: 7 -bar2 = anyio.get_cancelled_exc_class() # TRIO118: 7 -bar3 = get_cancelled_exc_class # TRIO118: 7 -bar4 = get_cancelled_exc_class() # TRIO118: 7 +bar1 = anyio.get_cancelled_exc_class # ASYNC118: 7 +bar2 = anyio.get_cancelled_exc_class() # ASYNC118: 7 +bar3 = get_cancelled_exc_class # ASYNC118: 7 +bar4 = get_cancelled_exc_class() # ASYNC118: 7 -bar5: Any = anyio.get_cancelled_exc_class # TRIO118: 12 -bar6: Any = anyio.get_cancelled_exc_class() # TRIO118: 12 -bar7: Any = get_cancelled_exc_class # TRIO118: 12 -bar8: Any = get_cancelled_exc_class() # TRIO118: 12 +bar5: Any = anyio.get_cancelled_exc_class # ASYNC118: 12 +bar6: Any = anyio.get_cancelled_exc_class() # ASYNC118: 12 +bar7: Any = get_cancelled_exc_class # ASYNC118: 12 +bar8: Any = get_cancelled_exc_class() # ASYNC118: 12 # code coverage bar9: Any diff --git a/tests/eval_files/trio200.py b/tests/eval_files/trio200.py index 7526ccb..f9e1a94 100644 --- a/tests/eval_files/trio200.py +++ b/tests/eval_files/trio200.py @@ -12,16 +12,16 @@ def bar(): async def afoo(): - bar() # TRIO200: 4, "bar", "BAR" - print(bar()) # TRIO200: 10, "bar", "BAR" + bar() # ASYNC200: 4, "bar", "BAR" + print(bar()) # ASYNC200: 10, "bar", "BAR" # check that bee.bonnet triggers, and neither `bee` nor `bonnet` - bee.bonnet() # TRIO200: 4, "bee.bonnet", "BEEBONNET" + bee.bonnet() # ASYNC200: 4, "bee.bonnet", "BEEBONNET" # check wildcard support - bar.postwild() # TRIO200: 4, "*.postwild", "POSTWILD" - prewild.anything() # TRIO200: 4, "prewild.*", "PREWILD" - a.b.c() # TRIO200: 4, "*.*.*", "TRIPLEDOT" + bar.postwild() # ASYNC200: 4, "*.postwild", "POSTWILD" + prewild.anything() # ASYNC200: 4, "prewild.*", "PREWILD" + a.b.c() # ASYNC200: 4, "*.*.*", "TRIPLEDOT" # don't error when it's not a call bar @@ -41,9 +41,9 @@ def bar2(): bar() async def bar3(): - bar() # TRIO200: 12, "bar", "BAR" + bar() # ASYNC200: 12, "bar", "BAR" - bar() # TRIO200: 4, "bar", "BAR" + bar() # ASYNC200: 4, "bar", "BAR" # don't error on directly awaited expressions # https://github.com/Zac-HD/flake8-trio/issues/85 @@ -51,15 +51,15 @@ async def bar3(): print(await bar()) # error on not directly awaited expressions - await print(bar()) # TRIO200: 16, "bar", "BAR" - await (foo() if bar() else foo()) # TRIO200: 20, "bar", "BAR" - await bee.bonnet(bar()) # TRIO200: 21, "bar", "BAR" + await print(bar()) # ASYNC200: 16, "bar", "BAR" + await (foo() if bar() else foo()) # ASYNC200: 20, "bar", "BAR" + await bee.bonnet(bar()) # ASYNC200: 21, "bar", "BAR" # known false alarms - await (x := bar()) # TRIO200: 16, "bar", "BAR" - await (bar() if True else foo()) # TRIO200: 11, "bar", "BAR" - await (await bee.bonnet() or bar()) # TRIO200: 33, "bar", "BAR" - await ((lambda: bee.bonnet()) and bar()) # TRIO200: 38, "bar", "BAR" + await (x := bar()) # ASYNC200: 16, "bar", "BAR" + await (bar() if True else foo()) # ASYNC200: 11, "bar", "BAR" + await (await bee.bonnet() or bar()) # ASYNC200: 33, "bar", "BAR" + await ((lambda: bee.bonnet()) and bar()) # ASYNC200: 38, "bar", "BAR" # check that errors are enabled again - bar() # TRIO200: 4, "bar", "BAR" + bar() # ASYNC200: 4, "bar", "BAR" diff --git a/tests/eval_files/trio210.py b/tests/eval_files/trio210.py index e7a17c6..e898e05 100644 --- a/tests/eval_files/trio210.py +++ b/tests/eval_files/trio210.py @@ -7,37 +7,37 @@ async def foo(): - requests.get() # TRIO210: 4, 'requests.get' - requests.get(...) # TRIO210: 4, 'requests.get' + requests.get() # ASYNC210: 4, 'requests.get' + requests.get(...) # ASYNC210: 4, 'requests.get' requests.get - print(requests.get()) # TRIO210: 10, 'requests.get' + print(requests.get()) # ASYNC210: 10, 'requests.get' # fmt: off - print(requests.get(requests.get()))# TRIO210: 10, 'requests.get' # TRIO210: 23, 'requests.get' + print(requests.get(requests.get()))# ASYNC210: 10, 'requests.get' # ASYNC210: 23, 'requests.get' # fmt: on - requests.options() # TRIO210: 4, 'requests.options' - requests.head() # TRIO210: 4, 'requests.head' - requests.post() # TRIO210: 4, 'requests.post' - requests.put() # TRIO210: 4, 'requests.put' - requests.patch() # TRIO210: 4, 'requests.patch' - requests.delete() # TRIO210: 4, 'requests.delete' + requests.options() # ASYNC210: 4, 'requests.options' + requests.head() # ASYNC210: 4, 'requests.head' + requests.post() # ASYNC210: 4, 'requests.post' + requests.put() # ASYNC210: 4, 'requests.put' + requests.patch() # ASYNC210: 4, 'requests.patch' + requests.delete() # ASYNC210: 4, 'requests.delete' requests.foo() - httpx.options("") # TRIO210: 4, 'httpx.options' - httpx.head("") # TRIO210: 4, 'httpx.head' - httpx.post("") # TRIO210: 4, 'httpx.post' - httpx.put("") # TRIO210: 4, 'httpx.put' - httpx.patch("") # TRIO210: 4, 'httpx.patch' - httpx.delete("") # TRIO210: 4, 'httpx.delete' + httpx.options("") # ASYNC210: 4, 'httpx.options' + httpx.head("") # ASYNC210: 4, 'httpx.head' + httpx.post("") # ASYNC210: 4, 'httpx.post' + httpx.put("") # ASYNC210: 4, 'httpx.put' + httpx.patch("") # ASYNC210: 4, 'httpx.patch' + httpx.delete("") # ASYNC210: 4, 'httpx.delete' httpx.foo() - urllib3.request() # TRIO210: 4, 'urllib3.request' - urllib3.request(...) # TRIO210: 4, 'urllib3.request' + urllib3.request() # ASYNC210: 4, 'urllib3.request' + urllib3.request(...) # ASYNC210: 4, 'urllib3.request' request() - urllib.request.urlopen("") # TRIO210: 4, 'urllib.request.urlopen' - request.urlopen() # TRIO210: 4, 'request.urlopen' - urlopen() # TRIO210: 4, 'urlopen' + urllib.request.urlopen("") # ASYNC210: 4, 'urllib.request.urlopen' + request.urlopen() # ASYNC210: 4, 'request.urlopen' + urlopen() # ASYNC210: 4, 'urlopen' r = {} r.get("not a sync http client") diff --git a/tests/eval_files/trio211.py b/tests/eval_files/trio211.py index b9fcedd..1fa24e0 100644 --- a/tests/eval_files/trio211.py +++ b/tests/eval_files/trio211.py @@ -4,21 +4,21 @@ async def foo(): foo = PoolManager() - foo.request("OPTIONS") # TRIO211: 4, 'foo.request' - foo.request("GET") # TRIO211: 4, 'foo.request' - foo.request("HEAD") # TRIO211: 4, 'foo.request' - foo.request("POST") # TRIO211: 4, 'foo.request' - foo.request("PUT") # TRIO211: 4, 'foo.request' - foo.request("DELETE") # TRIO211: 4, 'foo.request' - foo.request("TRACE") # TRIO211: 4, 'foo.request' - foo.request("CONNECT") # TRIO211: 4, 'foo.request' - foo.request("PATCH") # TRIO211: 4, 'foo.request' + foo.request("OPTIONS") # ASYNC211: 4, 'foo.request' + foo.request("GET") # ASYNC211: 4, 'foo.request' + foo.request("HEAD") # ASYNC211: 4, 'foo.request' + foo.request("POST") # ASYNC211: 4, 'foo.request' + foo.request("PUT") # ASYNC211: 4, 'foo.request' + foo.request("DELETE") # ASYNC211: 4, 'foo.request' + foo.request("TRACE") # ASYNC211: 4, 'foo.request' + foo.request("CONNECT") # ASYNC211: 4, 'foo.request' + foo.request("PATCH") # ASYNC211: 4, 'foo.request' - foo.request("PUT", ...) # TRIO211: 4, 'foo.request' + foo.request("PUT", ...) # ASYNC211: 4, 'foo.request' request("PUT") foo.request() foo.request(..., "PUT") - foo.request("put") # TRIO211: 4, 'foo.request' + foo.request("put") # ASYNC211: 4, 'foo.request' foo.request("sPUTnik") foo.request(None) foo.request(put) diff --git a/tests/eval_files/trio212.py b/tests/eval_files/trio212.py index fd3a479..1671c67 100644 --- a/tests/eval_files/trio212.py +++ b/tests/eval_files/trio212.py @@ -4,17 +4,17 @@ async def foo_call(): client = httpx.Client() - client.close() # TRIO212: 4, "close", "client" - client.delete() # TRIO212: 4, "delete", "client" - client.get() # TRIO212: 4, "get", "client" - client.head() # TRIO212: 4, "head", "client" - client.options() # TRIO212: 4, "options", "client" - client.patch() # TRIO212: 4, "patch", "client" - client.post() # TRIO212: 4, "post", "client" - client.put() # TRIO212: 4, "put", "client" - client.request() # TRIO212: 4, "request", "client" - client.send() # TRIO212: 4, "send", "client" - client.stream() # TRIO212: 4, "stream", "client" + client.close() # ASYNC212: 4, "close", "client" + client.delete() # ASYNC212: 4, "delete", "client" + client.get() # ASYNC212: 4, "get", "client" + client.head() # ASYNC212: 4, "head", "client" + client.options() # ASYNC212: 4, "options", "client" + client.patch() # ASYNC212: 4, "patch", "client" + client.post() # ASYNC212: 4, "post", "client" + client.put() # ASYNC212: 4, "put", "client" + client.request() # ASYNC212: 4, "request", "client" + client.send() # ASYNC212: 4, "send", "client" + client.stream() # ASYNC212: 4, "stream", "client" client.anything() client.build_request() @@ -22,31 +22,31 @@ async def foo_call(): async def foo_ann_par(client: httpx.Client): - client.send(...) # TRIO212: 4, "send", "client" + client.send(...) # ASYNC212: 4, "send", "client" async def foo_ann_par_leftNone(client: None | httpx.Client): - client.send(...) # TRIO212: 4, "send", "client" + client.send(...) # ASYNC212: 4, "send", "client" async def foo_ann_par_rightNone(client: httpx.Client | None): - client.send(...) # TRIO212: 4, "send", "client" + client.send(...) # ASYNC212: 4, "send", "client" async def foo_ann_par_optional(client: Optional[httpx.Client]): - client.send(...) # TRIO212: 4, "send", "client" + client.send(...) # ASYNC212: 4, "send", "client" async def foo_ann_var(): client: httpx.Client = ... - client.send() # TRIO212: 4, "send", "client" + client.send() # ASYNC212: 4, "send", "client" glob_client = httpx.Client() async def foo_global_client(): - glob_client.send() # TRIO212: 4, "send", "glob_client" + glob_client.send() # ASYNC212: 4, "send", "glob_client" def sync_global_client(): @@ -72,25 +72,25 @@ async def foo_alias_import(): # urllib3 pools async def foo_urllib3(): c: urllib3.HTTPConnectionPool = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.HTTPSConnectionPool = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.PoolManager = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.ProxyManager = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.connectionpool.ConnectionPool = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.connectionpool.HTTPConnectionPool = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.connectionpool.HTTPSConnectionPool = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.poolmanager.PoolManager = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.poolmanager.ProxyManager = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.request.RequestMethods = ... - c.anything() # TRIO212: 4, "anything", "c" + c.anything() # ASYNC212: 4, "anything", "c" c: urllib3.anything = ... c.anything() diff --git a/tests/eval_files/trio22x.py b/tests/eval_files/trio22x.py index 46f7392..40f39b0 100644 --- a/tests/eval_files/trio22x.py +++ b/tests/eval_files/trio22x.py @@ -1,27 +1,27 @@ # type: ignore -# ARG --enable=TRIO220,TRIO221,TRIO222 +# ARG --enable=ASYNC220,ASYNC221,ASYNC222 async def foo(): await async_fun( - subprocess.getoutput() # TRIO221: 8, 'subprocess.getoutput', "trio" + subprocess.getoutput() # ASYNC221: 8, 'subprocess.getoutput', "trio" ) - subprocess.Popen() # TRIO220: 4, 'subprocess.Popen', "trio" - os.system() # TRIO221: 4, 'os.system', "trio" + subprocess.Popen() # ASYNC220: 4, 'subprocess.Popen', "trio" + os.system() # ASYNC221: 4, 'os.system', "trio" system() os.system.anything() os.anything() - subprocess.run() # TRIO221: 4, 'subprocess.run', "trio" - subprocess.call() # TRIO221: 4, 'subprocess.call', "trio" - subprocess.check_call() # TRIO221: 4, 'subprocess.check_call', "trio" - subprocess.check_output() # TRIO221: 4, 'subprocess.check_output', "trio" - subprocess.getoutput() # TRIO221: 4, 'subprocess.getoutput', "trio" - subprocess.getstatusoutput() # TRIO221: 4, 'subprocess.getstatusoutput', "trio" + subprocess.run() # ASYNC221: 4, 'subprocess.run', "trio" + subprocess.call() # ASYNC221: 4, 'subprocess.call', "trio" + subprocess.check_call() # ASYNC221: 4, 'subprocess.check_call', "trio" + subprocess.check_output() # ASYNC221: 4, 'subprocess.check_output', "trio" + subprocess.getoutput() # ASYNC221: 4, 'subprocess.getoutput', "trio" + subprocess.getstatusoutput() # ASYNC221: 4, 'subprocess.getstatusoutput', "trio" await async_fun( - subprocess.getoutput() # TRIO221: 8, 'subprocess.getoutput', "trio" + subprocess.getoutput() # ASYNC221: 8, 'subprocess.getoutput', "trio" ) subprocess.anything() @@ -29,47 +29,47 @@ async def foo(): subprocess.bar.foo() subprocess() - os.posix_spawn() # TRIO221: 4, 'os.posix_spawn', "trio" - os.posix_spawnp() # TRIO221: 4, 'os.posix_spawnp', "trio" + os.posix_spawn() # ASYNC221: 4, 'os.posix_spawn', "trio" + os.posix_spawnp() # ASYNC221: 4, 'os.posix_spawnp', "trio" os.spawn() os.spawn os.spawnllll() - os.spawnl() # TRIO221: 4, 'os.spawnl', "trio" - os.spawnle() # TRIO221: 4, 'os.spawnle', "trio" - os.spawnlp() # TRIO221: 4, 'os.spawnlp', "trio" - os.spawnlpe() # TRIO221: 4, 'os.spawnlpe', "trio" - os.spawnv() # TRIO221: 4, 'os.spawnv', "trio" - os.spawnve() # TRIO221: 4, 'os.spawnve', "trio" - os.spawnvp() # TRIO221: 4, 'os.spawnvp', "trio" - os.spawnvpe() # TRIO221: 4, 'os.spawnvpe', "trio" + os.spawnl() # ASYNC221: 4, 'os.spawnl', "trio" + os.spawnle() # ASYNC221: 4, 'os.spawnle', "trio" + os.spawnlp() # ASYNC221: 4, 'os.spawnlp', "trio" + os.spawnlpe() # ASYNC221: 4, 'os.spawnlpe', "trio" + os.spawnv() # ASYNC221: 4, 'os.spawnv', "trio" + os.spawnve() # ASYNC221: 4, 'os.spawnve', "trio" + os.spawnvp() # ASYNC221: 4, 'os.spawnvp', "trio" + os.spawnvpe() # ASYNC221: 4, 'os.spawnvpe', "trio" - # if mode is given, and is not os.P_WAIT: TRIO220 - os.spawnl(os.P_NOWAIT) # TRIO220: 4, 'os.spawnl', "trio" - os.spawnl(P_NOWAIT) # TRIO220: 4, 'os.spawnl', "trio" - os.spawnl(mode=os.P_NOWAIT) # TRIO220: 4, 'os.spawnl', "trio" - os.spawnl(mode=P_NOWAIT) # TRIO220: 4, 'os.spawnl', "trio" + # if mode is given, and is not os.P_WAIT: ASYNC220 + os.spawnl(os.P_NOWAIT) # ASYNC220: 4, 'os.spawnl', "trio" + os.spawnl(P_NOWAIT) # ASYNC220: 4, 'os.spawnl', "trio" + os.spawnl(mode=os.P_NOWAIT) # ASYNC220: 4, 'os.spawnl', "trio" + os.spawnl(mode=P_NOWAIT) # ASYNC220: 4, 'os.spawnl', "trio" - # if it is P_WAIT, TRIO221 - os.spawnl(os.P_WAIT) # TRIO221: 4, 'os.spawnl', "trio" - os.spawnl(P_WAIT) # TRIO221: 4, 'os.spawnl', "trio" - os.spawnl(mode=os.P_WAIT) # TRIO221: 4, 'os.spawnl', "trio" - os.spawnl(mode=P_WAIT) # TRIO221: 4, 'os.spawnl', "trio" + # if it is P_WAIT, ASYNC221 + os.spawnl(os.P_WAIT) # ASYNC221: 4, 'os.spawnl', "trio" + os.spawnl(P_WAIT) # ASYNC221: 4, 'os.spawnl', "trio" + os.spawnl(mode=os.P_WAIT) # ASYNC221: 4, 'os.spawnl', "trio" + os.spawnl(mode=P_WAIT) # ASYNC221: 4, 'os.spawnl', "trio" # treating this as 221 to simplify code, and see no real reason not to - os.spawnl(foo.P_WAIT) # TRIO221: 4, 'os.spawnl', "trio" + os.spawnl(foo.P_WAIT) # ASYNC221: 4, 'os.spawnl', "trio" - # other weird cases: TRIO220 - os.spawnl(0) # TRIO220: 4, 'os.spawnl', "trio" - os.spawnl(1) # TRIO220: 4, 'os.spawnl', "trio" - os.spawnl(foo()) # TRIO220: 4, 'os.spawnl', "trio" + # other weird cases: ASYNC220 + os.spawnl(0) # ASYNC220: 4, 'os.spawnl', "trio" + os.spawnl(1) # ASYNC220: 4, 'os.spawnl', "trio" + os.spawnl(foo()) # ASYNC220: 4, 'os.spawnl', "trio" - # TRIO222 - os.wait() # TRIO222: 4, 'os.wait', "trio" - os.wait3() # TRIO222: 4, 'os.wait3', "trio" - os.wait4() # TRIO222: 4, 'os.wait4', "trio" - os.waitid() # TRIO222: 4, 'os.waitid', "trio" - os.waitpid() # TRIO222: 4, 'os.waitpid', "trio" + # ASYNC222 + os.wait() # ASYNC222: 4, 'os.wait', "trio" + os.wait3() # ASYNC222: 4, 'os.wait3', "trio" + os.wait4() # ASYNC222: 4, 'os.wait4', "trio" + os.waitid() # ASYNC222: 4, 'os.waitid', "trio" + os.waitpid() # ASYNC222: 4, 'os.waitpid', "trio" os.waitpi() os.waiti() diff --git a/tests/eval_files/trio232.py b/tests/eval_files/trio232.py index e9ccf7c..0fcc0d3 100644 --- a/tests/eval_files/trio232.py +++ b/tests/eval_files/trio232.py @@ -7,56 +7,56 @@ async def file_text(f: io.TextIOWrapper): - f.read() # TRIO232: 4, 'read', 'f', "trio" - f.readlines() # TRIO232: 4, 'readlines', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" + f.readlines() # ASYNC232: 4, 'readlines', 'f', "trio" # there might be non-sync calls on TextIOWrappers? - but it will currently trigger # on all calls - f.anything() # TRIO232: 4, 'anything', 'f', "trio" + f.anything() # ASYNC232: 4, 'anything', 'f', "trio" async def file_binary_read(f: io.BufferedReader): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_binary_write(f: io.BufferedWriter): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_binary_readwrite(f: io.BufferedRandom): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_text_(f: TextIOWrapper): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_binary_read_(f: BufferedReader): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_binary_write_(f: BufferedWriter): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_binary_readwrite_(f: BufferedRandom): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_text_3(f: TextIOWrapper = blah): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_text_4(f: TextIOWrapper | None): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" if f: - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" async def file_text_4_left(f: None | TextIOWrapper): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" if f: - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" # not handled @@ -70,25 +70,25 @@ async def file_text_4_non_none(f: TextIOWrapper | int): async def file_text_5(f: TextIOWrapper | None = None): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" if f: - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" async def file_text_6(f: Optional[TextIOWrapper] = None): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" if f: - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" # posonly async def file_text_7(f: TextIOWrapper = blah, /): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" # keyword-only async def file_text_8(*, f: TextIOWrapper = blah): - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def file_text_9(lf: list[TextIOWrapper]): @@ -108,7 +108,7 @@ async def open_file_2(): async def open_file_3(): with open("") as f: - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" async def open_file_4(): @@ -117,14 +117,14 @@ async def open_file_4(): async def open_file_5(): f = open("") - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" async def open_file_6(): ff = open("") f = ff - f.read() # TRIO232: 4, 'read', 'f', "trio" - ff.read() # TRIO232: 4, 'read', 'ff', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" + ff.read() # ASYNC232: 4, 'read', 'ff', "trio" async def noerror(): @@ -134,19 +134,19 @@ async def noerror(): def sync_fun(f: TextIOWrapper): async def async_fun(): - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" def sync_fun_2(): f = open("") async def async_fun(): - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" async def type_assign(): f: TextIOWrapper = ... - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" # define global variables @@ -164,16 +164,16 @@ async def inception(): f.read() - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" lambda f: f.read() - f.read() # TRIO232: 4, 'read', 'f', "trio" - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" # and show that they're still marked as TextIOWrappers async def global_vars(): - f.read() # TRIO232: 4, 'read', 'f', "trio" - g.read() # TRIO232: 4, 'read', 'g', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" + g.read() # ASYNC232: 4, 'read', 'g', "trio" # If the type is explicitly overridden, it will not error @@ -181,11 +181,11 @@ async def overridden_type(f: TextIOWrapper): f: int = 7 f.read() f: TextIOWrapper = ... - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" f: int = 7 f.read() f: TextIOWrapper = ... - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" # ***** Known unhandled cases ***** @@ -195,9 +195,9 @@ async def overridden_type(f: TextIOWrapper): async def implicit_overridden_type(): f: TextIOWrapper = ... f = arbitrary_function() - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" f = 7 - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" # Tuple assignments are completely ignored @@ -223,13 +223,13 @@ async def attribute_access_on_object(): # to the type async def type_restricting_1(f: Optional[TextIOWrapper] = None): if f is None: - f.read() # TRIO232: 8, 'read', 'f', "trio" + f.read() # ASYNC232: 8, 'read', 'f', "trio" async def type_restricting_2(f: Optional[TextIOWrapper] = None): if isinstance(f, TextIOWrapper): return - f.read() # TRIO232: 4, 'read', 'f', "trio" + f.read() # ASYNC232: 4, 'read', 'f', "trio" # Classes are not supported, partly due to not handling attributes at all, diff --git a/tests/eval_files/trio23x.py b/tests/eval_files/trio23x.py index c339a07..e1ea725 100644 --- a/tests/eval_files/trio23x.py +++ b/tests/eval_files/trio23x.py @@ -1,5 +1,5 @@ # type: ignore -# ARG --enable=TRIO230,TRIO231 +# ARG --enable=ASYNC230,ASYNC231 import io import os @@ -7,9 +7,9 @@ async def foo(): - open("") # TRIO230: 4, 'open', "trio" - io.open_code("") # TRIO230: 4, 'io.open_code', "trio" - os.fdopen(0) # TRIO231: 4, 'os.fdopen', "trio" + open("") # ASYNC230: 4, 'open', "trio" + io.open_code("") # ASYNC230: 4, 'io.open_code', "trio" + os.fdopen(0) # ASYNC231: 4, 'os.fdopen', "trio" # sync call is awaited, so it can't be sync await open("") @@ -19,15 +19,15 @@ async def foo(): await trio.wrap_file(os.fdopen(0)) # with uses the same code & logic - with os.fdopen(0): # TRIO231: 9, 'os.fdopen', "trio" + with os.fdopen(0): # ASYNC231: 9, 'os.fdopen', "trio" ... - with open(""): # TRIO230: 9, 'open', "trio" + with open(""): # ASYNC230: 9, 'open', "trio" ... - with open("") as f: # TRIO230: 9, 'open', "trio" + with open("") as f: # ASYNC230: 9, 'open', "trio" ... - with foo(), open(""): # TRIO230: 16, 'open', "trio" + with foo(), open(""): # ASYNC230: 16, 'open', "trio" ... - async with open(""): # TRIO230: 15, 'open', "trio" + async with open(""): # ASYNC230: 15, 'open', "trio" ... async with trio.wrap_file(open("")): ... @@ -36,7 +36,7 @@ async def foo(): # pyupgrade removes the unnecessary `io.` # https://github.com/asottile/pyupgrade#open-alias # and afaict neither respects fmt:off nor #noqa - so I don't know how to test it - open("") # TRIO230: 4, 'open', "trio" + open("") # ASYNC230: 4, 'open', "trio" def foo_sync(): diff --git a/tests/eval_files/trio240.py b/tests/eval_files/trio240.py index 73a9c2a..fbb781c 100644 --- a/tests/eval_files/trio240.py +++ b/tests/eval_files/trio240.py @@ -4,30 +4,30 @@ async def foo(): - normpath("") # TRIO240: 4, 'normpath', "trio" - relpath("") # TRIO240: 4, 'relpath', "trio" - isfile("") # TRIO240: 4, 'isfile', "trio" + normpath("") # ASYNC240: 4, 'normpath', "trio" + relpath("") # ASYNC240: 4, 'relpath', "trio" + isfile("") # ASYNC240: 4, 'isfile', "trio" - os.path._path_normpath(...) # TRIO240: 4, '_path_normpath', "trio" - os.path.normpath("") # TRIO240: 4, 'normpath', "trio" - os.path._joinrealpath(...) # TRIO240: 4, '_joinrealpath', "trio" - os.path.islink("") # TRIO240: 4, 'islink', "trio" - os.path.lexists("") # TRIO240: 4, 'lexists', "trio" - os.path.ismount("") # TRIO240: 4, 'ismount', "trio" - os.path.realpath("") # TRIO240: 4, 'realpath', "trio" - os.path.exists("") # TRIO240: 4, 'exists', "trio" - os.path.isdir("") # TRIO240: 4, 'isdir', "trio" - os.path.isfile("") # TRIO240: 4, 'isfile', "trio" - os.path.getatime("") # TRIO240: 4, 'getatime', "trio" - os.path.getctime("") # TRIO240: 4, 'getctime', "trio" - os.path.getmtime("") # TRIO240: 4, 'getmtime', "trio" - os.path.getsize("") # TRIO240: 4, 'getsize', "trio" - os.path.samefile("", "") # TRIO240: 4, 'samefile', "trio" - os.path.sameopenfile(0, 1) # TRIO240: 4, 'sameopenfile', "trio" - os.path.relpath("") # TRIO240: 4, 'relpath', "trio" + os.path._path_normpath(...) # ASYNC240: 4, '_path_normpath', "trio" + os.path.normpath("") # ASYNC240: 4, 'normpath', "trio" + os.path._joinrealpath(...) # ASYNC240: 4, '_joinrealpath', "trio" + os.path.islink("") # ASYNC240: 4, 'islink', "trio" + os.path.lexists("") # ASYNC240: 4, 'lexists', "trio" + os.path.ismount("") # ASYNC240: 4, 'ismount', "trio" + os.path.realpath("") # ASYNC240: 4, 'realpath', "trio" + os.path.exists("") # ASYNC240: 4, 'exists', "trio" + os.path.isdir("") # ASYNC240: 4, 'isdir', "trio" + os.path.isfile("") # ASYNC240: 4, 'isfile', "trio" + os.path.getatime("") # ASYNC240: 4, 'getatime', "trio" + os.path.getctime("") # ASYNC240: 4, 'getctime', "trio" + os.path.getmtime("") # ASYNC240: 4, 'getmtime', "trio" + os.path.getsize("") # ASYNC240: 4, 'getsize', "trio" + os.path.samefile("", "") # ASYNC240: 4, 'samefile', "trio" + os.path.sameopenfile(0, 1) # ASYNC240: 4, 'sameopenfile', "trio" + os.path.relpath("") # ASYNC240: 4, 'relpath', "trio" - await os.path.isfile("") # TRIO240: 10, 'isfile', "trio" - print(os.path.isfile("")) # TRIO240: 10, 'isfile', "trio" + await os.path.isfile("") # ASYNC240: 10, 'isfile', "trio" + print(os.path.isfile("")) # ASYNC240: 10, 'isfile', "trio" os.path.abspath("") os.path.anything("") diff --git a/tests/eval_files/trio900.py b/tests/eval_files/trio900.py index ff3d05c..235d211 100644 --- a/tests/eval_files/trio900.py +++ b/tests/eval_files/trio900.py @@ -3,7 +3,7 @@ from contextlib import asynccontextmanager -async def foo1(): # TRIO900: 0 +async def foo1(): # ASYNC900: 0 yield yield @@ -15,7 +15,7 @@ async def foo2(): @asynccontextmanager async def foo3(): - async def bar(): # TRIO900: 4 + async def bar(): # ASYNC900: 4 yield yield @@ -37,7 +37,7 @@ async def async_fixtures_can_take_arguments(): # no-checkpoint-warning-decorator now ignored @other_context_manager -async def foo5(): # TRIO900: 0 +async def foo5(): # ASYNC900: 0 yield diff --git a/tests/eval_files/trio910.py b/tests/eval_files/trio910.py index 5a4c372..38c003d 100644 --- a/tests/eval_files/trio910.py +++ b/tests/eval_files/trio910.py @@ -20,7 +20,7 @@ async def foo() -> Any: def bar() -> Any: ... -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 # ARG --no-checkpoint-warning-decorator=custom_disabled_decorator diff --git a/tests/eval_files/trio911.py b/tests/eval_files/trio911.py index 465b473..8a19c52 100644 --- a/tests/eval_files/trio911.py +++ b/tests/eval_files/trio911.py @@ -6,7 +6,7 @@ _: Any = "" -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 async def foo() -> Any: diff --git a/tests/eval_files/trio91x_autofix.py b/tests/eval_files/trio91x_autofix.py index 78b3fc4..de65031 100644 --- a/tests/eval_files/trio91x_autofix.py +++ b/tests/eval_files/trio91x_autofix.py @@ -6,7 +6,7 @@ So we make sure that import is added after it. """ # isort: skip_file -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 from typing import Any @@ -18,33 +18,33 @@ async def foo() -> Any: await foo() -async def foo1(): # TRIO910: 0, "exit", Statement("function definition", lineno) +async def foo1(): # ASYNC910: 0, "exit", Statement("function definition", lineno) bar() async def foo_return(): bar() - return # TRIO910: 4, "return", Statement("function definition", lineno-2) + return # ASYNC910: 4, "return", Statement("function definition", lineno-2) -async def foo_yield(): # TRIO911: 0, "exit", Statement("yield", lineno+2) +async def foo_yield(): # ASYNC911: 0, "exit", Statement("yield", lineno+2) bar() - yield # TRIO911: 4, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 4, "yield", Statement("function definition", lineno-2) async def foo_if(): if bar(): - return # TRIO910: 8, "return", Statement("function definition", lineno-2) + return # ASYNC910: 8, "return", Statement("function definition", lineno-2) elif bar(): - return # TRIO910: 8, "return", Statement("function definition", lineno-4) + return # ASYNC910: 8, "return", Statement("function definition", lineno-4) else: - return # TRIO910: 8, "return", Statement("function definition", lineno-6) + return # ASYNC910: 8, "return", Statement("function definition", lineno-6) async def foo_while(): await foo() while True: - yield # TRIO911: 8, "yield", Statement("yield", lineno) + yield # ASYNC911: 8, "yield", Statement("yield", lineno) async def foo_while2(): @@ -66,25 +66,25 @@ async def foo_while3(): async def foo_while4(): while True: if bar(): - yield # TRIO911: 12, "yield", Statement("yield", lineno) # TRIO911: 12, "yield", Statement("yield", lineno+2) # TRIO911: 12, "yield", Statement("function definition", lineno-3) + yield # ASYNC911: 12, "yield", Statement("yield", lineno) # ASYNC911: 12, "yield", Statement("yield", lineno+2) # ASYNC911: 12, "yield", Statement("function definition", lineno-3) if bar(): - yield # TRIO911: 12, "yield", Statement("yield", lineno) # TRIO911: 12, "yield", Statement("yield", lineno-2) # TRIO911: 12, "yield", Statement("function definition", lineno-5) # TRIO911: 12, "yield", Statement("yield", lineno-2) + yield # ASYNC911: 12, "yield", Statement("yield", lineno) # ASYNC911: 12, "yield", Statement("yield", lineno-2) # ASYNC911: 12, "yield", Statement("function definition", lineno-5) # ASYNC911: 12, "yield", Statement("yield", lineno-2) # this warns about the yield on lineno-2 twice, since it can arrive here from it in two different ways # check state management of nested loops async def foo_nested_while(): while True: - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) while True: - yield # TRIO911: 12, "yield", Statement("yield", lineno-2) + yield # ASYNC911: 12, "yield", Statement("yield", lineno-2) while True: - yield # TRIO911: 16, "yield", Statement("yield", lineno-2) # TRIO911: 16, "yield", Statement("yield", lineno) + yield # ASYNC911: 16, "yield", Statement("yield", lineno-2) # ASYNC911: 16, "yield", Statement("yield", lineno) async def foo_while_nested_func(): while True: - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) # TRIO911: 8, "yield", Statement("yield", lineno) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) # ASYNC911: 8, "yield", Statement("yield", lineno) async def bar(): while bar(): diff --git a/tests/eval_files/trio91x_noautofix.py b/tests/eval_files/trio91x_noautofix.py index 652f3dd..07ada15 100644 --- a/tests/eval_files/trio91x_noautofix.py +++ b/tests/eval_files/trio91x_noautofix.py @@ -1,4 +1,4 @@ -# ARG --enable=TRIO910,TRIO911 +# ARG --enable=ASYNC910,ASYNC911 from typing import Any @@ -13,7 +13,7 @@ async def foo() -> Any: async def foo_singleline(): await foo() # fmt: off - yield; yield # TRIO911: 11, "yield", Statement("yield", lineno, 4) + yield; yield # ASYNC911: 11, "yield", Statement("yield", lineno, 4) # fmt: on await foo() @@ -21,14 +21,14 @@ async def foo_singleline(): # not autofixed async def foo_singleline2(): # fmt: off - yield; await foo() # TRIO911: 4, "yield", Statement("function definition", lineno-2) + yield; await foo() # ASYNC911: 4, "yield", Statement("function definition", lineno-2) # fmt: on # not autofixed async def foo_singleline3(): # fmt: off - if ...: yield # TRIO911: 12, "yield", Statement("function definition", lineno-2) + if ...: yield # ASYNC911: 12, "yield", Statement("function definition", lineno-2) # fmt: on await foo() @@ -36,7 +36,7 @@ async def foo_singleline3(): # fmt: off async def foo_async_with_2(): # with'd expression evaluated before checkpoint - async with (yield): # TRIO911: 16, "yield", Statement("function definition", lineno-2) + async with (yield): # ASYNC911: 16, "yield", Statement("function definition", lineno-2) yield # fmt: on @@ -45,8 +45,8 @@ async def foo_boolops_3(): _ = (await foo() or (yield) or await foo()) or ( condition() or ( - (yield) # TRIO911: 13, "yield", Stmt("yield", line-3) - and (yield)) # TRIO911: 17, "yield", Stmt("yield", line-1) + (yield) # ASYNC911: 13, "yield", Stmt("yield", line-3) + and (yield)) # ASYNC911: 17, "yield", Stmt("yield", line-1) ) await foo() # fmt: on @@ -54,7 +54,7 @@ async def foo_boolops_3(): async def foo_async_for(): async for i in ( - yield # TRIO911: 8, "yield", Statement("function definition", lineno-2) + yield # ASYNC911: 8, "yield", Statement("function definition", lineno-2) ): yield # safe else: @@ -69,6 +69,6 @@ async def foo_boolops_2(): await foo() and (yield) and await foo() - and (yield) # TRIO911: 13, "yield", Stmt("yield", line-2, 13) + and (yield) # ASYNC911: 13, "yield", Stmt("yield", line-2, 13) ) await foo() diff --git a/tests/eval_files/trio_anyio.py b/tests/eval_files/trio_anyio.py index d1f12b8..2714e29 100644 --- a/tests/eval_files/trio_anyio.py +++ b/tests/eval_files/trio_anyio.py @@ -1,9 +1,9 @@ # type: ignore -# ARG --enable=TRIO220 +# ARG --enable=ASYNC220 # NOANYIO import trio # isort: skip import anyio # isort: skip async def foo(): - subprocess.Popen() # TRIO220: 4, 'subprocess.Popen', "[trio|anyio]" + subprocess.Popen() # ASYNC220: 4, 'subprocess.Popen', "[trio|anyio]" diff --git a/tests/test_changelog_and_version.py b/tests/test_changelog_and_version.py index 2a53ad2..628f8a5 100755 --- a/tests/test_changelog_and_version.py +++ b/tests/test_changelog_and_version.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -"""Tests for flake8-trio package metadata.""" +"""Tests for flake8-async package metadata.""" from __future__ import annotations diff --git a/tests/test_config_and_args.py b/tests/test_config_and_args.py index e3449ef..066bd43 100644 --- a/tests/test_config_and_args.py +++ b/tests/test_config_and_args.py @@ -19,7 +19,7 @@ """ EXAMPLE_PY_AUTOFIXED_TEXT = "import trio\n...\n" EXAMPLE_PY_ERROR = ( - "./example.py:2:6: TRIO100 trio.move_on_after context contains no checkpoints," + "./example.py:2:6: ASYNC100 trio.move_on_after context contains no checkpoints," " remove the context or add `await trio.lowlevel.checkpoint()`.\n" ) @@ -42,7 +42,7 @@ def monkeypatch_argv( argv: list[Path | str] | None = None, ) -> None: if argv is None: - argv = [tmp_path / "flake8-trio", "./example.py"] + argv = [tmp_path / "flake8-async", "./example.py"] monkeypatch.chdir(tmp_path) monkeypatch.setattr(sys, "argv", argv) @@ -51,7 +51,7 @@ def test_run_flake8_trio(tmp_path: Path): write_examplepy(tmp_path) res = subprocess.run( [ - "flake8-trio", + "flake8-async", "./example.py", ], cwd=tmp_path, @@ -100,7 +100,7 @@ def test_run_in_git_repo(tmp_path: Path): subprocess.run(["git", "add", "example.py"], cwd=tmp_path, check=True) res = subprocess.run( [ - "flake8-trio", + "flake8-async", ], cwd=tmp_path, capture_output=True, @@ -114,7 +114,7 @@ def test_run_in_git_repo(tmp_path: Path): def test_run_no_git_repo( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ): - monkeypatch_argv(monkeypatch, tmp_path, [tmp_path / "flake8-trio"]) + monkeypatch_argv(monkeypatch, tmp_path, [tmp_path / "flake8-async"]) assert main() == 1 out, err = capsys.readouterr() assert err == "Doesn't seem to be a git repo; pass filenames to format.\n" @@ -128,7 +128,7 @@ def test_run_100_autofix( monkeypatch_argv( monkeypatch, tmp_path, - [tmp_path / "flake8-trio", "--autofix=TRIO", "./example.py"], + [tmp_path / "flake8-async", "--autofix=ASYNC", "./example.py"], ) assert main() == 1 @@ -164,18 +164,18 @@ def test_anyio_from_config(tmp_path: Path, capsys: pytest.CaptureFixture[str]): """ [flake8] anyio = True -select = TRIO220 +select = ASYNC220 """ ) from flake8_trio.visitors.visitor2xx import Visitor22X - err_msg = Visitor22X.error_codes["TRIO220"].format( + err_msg = Visitor22X.error_codes["ASYNC220"].format( "subprocess.Popen", "[anyio|trio]", ) err_file = str(Path(__file__).parent / "eval_files" / "anyio_trio.py") - expected = f"{err_file}:10:5: TRIO220 {err_msg}\n" + expected = f"{err_file}:10:5: ASYNC220 {err_msg}\n" from flake8.main.cli import main returnvalue = main( @@ -198,7 +198,7 @@ def _test_trio200_from_config_common(tmp_path: Path) -> str: trio200-blocking-calls = other -> async, sync_fns.* -> the_async_equivalent, -select = TRIO200 +select = ASYNC200 """ ) assert tmp_path.joinpath("example.py").write_text( @@ -210,7 +210,7 @@ async def foo(): """ ) return ( - "./example.py:5:5: TRIO200 User-configured blocking sync call sync_fns.* " + "./example.py:5:5: ASYNC200 User-configured blocking sync call sync_fns.* " "in async function, consider replacing with the_async_equivalent.\n" ) @@ -260,7 +260,7 @@ def test_900_default_off(capsys: pytest.CaptureFixture[str]): out, err = capsys.readouterr() assert returnvalue == 1 assert not err - assert "TRIO900" not in out + assert "ASYNC900" not in out def test_enable( @@ -268,7 +268,7 @@ def test_enable( ): write_examplepy(tmp_path) - argv: list[Path | str] = [tmp_path / "flake8-trio", "./example.py"] + argv: list[Path | str] = [tmp_path / "flake8-async", "./example.py"] monkeypatch_argv(monkeypatch, tmp_path, argv) def _helper(*args: str, error: bool = False, autofix: bool = False) -> None: @@ -292,33 +292,33 @@ def _helper(*args: str, error: bool = False, autofix: bool = False) -> None: _helper(error=True) # explicit enable - _helper("--enable=TRIO100", error=True) + _helper("--enable=ASYNC100", error=True) # explicit enable other - _helper("--enable=TRIO101") + _helper("--enable=ASYNC101") # make sure commas don't enable others - _helper("--enable=TRIO101,") + _helper("--enable=ASYNC101,") _helper("--enable=,") - _helper("--enable=TRIO101,,TRIO102") + _helper("--enable=ASYNC101,,ASYNC102") # disable - _helper("--disable=TRIO100") + _helper("--disable=ASYNC100") # disable enabled code - _helper("--disable=TRIO100", "--enable=TRIO100") + _helper("--disable=ASYNC100", "--enable=ASYNC100") # don't enable, but autofix _helper( "--enable=''", - "--autofix=TRIO100", + "--autofix=ASYNC100", error=True, # TODO: should be False autofix=True, ) _helper( "--enable=''", - "--autofix=TRIO100", + "--autofix=ASYNC100", "--error-on-autofix", error=True, autofix=True, @@ -331,7 +331,7 @@ def test_flake8_plugin_with_autofix_fails(tmp_path: Path): [ "flake8", "./example.py", - "--autofix=TRIO", + "--autofix=ASYNC", ], cwd=tmp_path, capture_output=True, @@ -363,13 +363,13 @@ def test_disable_noqa_cst( monkeypatch_argv( monkeypatch, tmp_path, - [tmp_path / "flake8-trio", "./example.py", "--disable-noqa"], + [tmp_path / "flake8-async", "./example.py", "--disable-noqa"], ) assert main() == 1 out, err = capsys.readouterr() assert not err assert ( - out == "./example.py:2:6: TRIO100 trio.move_on_after context contains no" + out == "./example.py:2:6: ASYNC100 trio.move_on_after context contains no" " checkpoints, remove the context or add `await" " trio.lowlevel.checkpoint()`.\n" ) @@ -382,13 +382,13 @@ def test_disable_noqa_ast( monkeypatch_argv( monkeypatch, tmp_path, - [tmp_path / "flake8-trio", "./example.py", "--disable-noqa"], + [tmp_path / "flake8-async", "./example.py", "--disable-noqa"], ) assert main() == 1 out, err = capsys.readouterr() assert not err assert ( out - == "./example.py:1:1: TRIO106 trio must be imported with `import trio` for the" + == "./example.py:1:1: ASYNC106 trio must be imported with `import trio` for the" " linter to work.\n" ) diff --git a/tests/test_decorator.py b/tests/test_decorator.py index 3fd66e8..f74a1ba 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -90,7 +90,7 @@ def test_pep614(): file_path = str(Path(__file__).parent / "trio_options.py") -common_flags = ["--select=TRIO", file_path] +common_flags = ["--select=ASYNC", file_path] def test_command_line_1(capfd: pytest.CaptureFixture[str]): @@ -106,8 +106,8 @@ def test_command_line_1(capfd: pytest.CaptureFixture[str]): break expected_out = ( - f"{file_path}:{expected_lineno}:1: TRIO910 " - + Visitor91X.error_codes["TRIO910"].format( + f"{file_path}:{expected_lineno}:1: ASYNC910 " + + Visitor91X.error_codes["ASYNC910"].format( "exit", Statement("function definition", expected_lineno) ) + "\n" diff --git a/tests/test_flake8_trio.py b/tests/test_flake8_trio.py index 7d95e88..39b800c 100644 --- a/tests/test_flake8_trio.py +++ b/tests/test_flake8_trio.py @@ -30,15 +30,28 @@ if TYPE_CHECKING: from collections.abc import Iterable, Sequence - from flake8_trio.visitors.flake8triovisitor import Flake8TrioVisitor + from flake8_trio.visitors.flake8triovisitor import Flake8AsyncVisitor AUTOFIX_DIR = Path(__file__).parent / "autofix_files" + +# to be able to usefully view diffs on github+be able to run tests in each step +# we temporarily introduce these functions +# so we can do the *actual* renaming of files in a separate commit. +def rename_file(s: str) -> str: + return re.sub("TRIO", "ASYNC", s) + + +def unrename_file(s: str) -> str: + return re.sub("ASYNC", "TRIO", s) + + test_files: list[tuple[str, Path]] = sorted( - (f.stem.upper(), f) for f in (Path(__file__).parent / "eval_files").iterdir() + (rename_file(f.stem.upper()), f) + for f in (Path(__file__).parent / "eval_files").iterdir() ) autofix_files: dict[str, Path] = { - f.stem.upper(): f for f in AUTOFIX_DIR.iterdir() if f.suffix == ".py" + rename_file(f.stem.upper()): f for f in AUTOFIX_DIR.iterdir() if f.suffix == ".py" } # check that there's an eval file for each autofix file extra_autofix_files = set(autofix_files.keys()) - {f[0] for f in test_files} @@ -62,7 +75,7 @@ def check_version(test: str): # mypy does not see that both types have error_codes -ERROR_CODES: dict[str, Flake8TrioVisitor] = { +ERROR_CODES: dict[str, Flake8AsyncVisitor] = { err_code: err_class # type: ignore[misc] for err_class in (*ERROR_CLASSES, *ERROR_CLASSES_CST) for err_code in err_class.error_codes # type: ignore[attr-defined] @@ -131,7 +144,7 @@ def check_autofix( # file contains a previous diff showing what's added/removed by the autofixer # i.e. a diff between "eval_files/{test}.py" and "autofix_files/{test}.py" - autofix_diff_file = AUTOFIX_DIR / f"{test.lower()}.py.diff" + autofix_diff_file = AUTOFIX_DIR / f"{unrename_file(test).lower()}.py.diff" if not autofix_diff_file.exists(): assert generate_autofix, "autofix diff file doesn't exist" # if generate_autofix is set, the diff content isn't used and the file @@ -185,7 +198,7 @@ class MagicMarkers: NOANYIO: bool = False NOTRIO: bool = False ANYIO_NO_ERROR: bool = False - TRIO_NO_ERROR: bool = False + ASYNC_NO_ERROR: bool = False def find_magic_markers( @@ -234,7 +247,7 @@ def test_eval( if noqa: # replace all instances of some error with noqa - content = re.sub(r"#[\s]*(error|TRIO\d\d\d):.*", "# noqa", content) + content = re.sub(r"#[\s]*(error|ASYNC\d\d\d):.*", "# noqa", content) expected, parsed_args, enable = _parse_eval_file(test, content) if anyio: @@ -243,7 +256,7 @@ def test_eval( parsed_args.append(f"--autofix={enable}") if (anyio and magic_markers.ANYIO_NO_ERROR) or ( - not anyio and magic_markers.TRIO_NO_ERROR + not anyio and magic_markers.ASYNC_NO_ERROR ): expected = [] @@ -322,7 +335,7 @@ def _parse_eval_file(test: str, content: str) -> tuple[list[Error], list[str], s continue # get text between `error:` and (end of line or another comment) - k = re.findall(r"(error|TRIO...)(_.*)?:([^#]*)(?=#|$)", line) + k = re.findall(r"(error|ASYNC...)(_.*)?:([^#]*)(?=#|$)", line) for err_code, alt_code, err_args in k: try: @@ -365,7 +378,7 @@ def _parse_eval_file(test: str, content: str) -> tuple[list[Error], list[str], s enabled_codes_list = enabled_codes.split(",") for code in enabled_codes_list: assert re.fullmatch( - r"TRIO\d\d\d", code + r"ASYNC\d\d\d", code ), f"invalid code {code} in list {enabled_codes_list}" for error in expected: @@ -385,18 +398,18 @@ def _parse_eval_file(test: str, content: str) -> tuple[list[Error], list[str], s # Expand this list when adding a new check if it does not care about whether the code # is asynchronous or not. error_codes_ignored_when_checking_transformed_sync_code = { - "TRIO100", - "TRIO101", - "TRIO103", - "TRIO104", - "TRIO105", - "TRIO106", - "TRIO111", - "TRIO112", - "TRIO115", - "TRIO116", - "TRIO117", - "TRIO118", + "ASYNC100", + "ASYNC101", + "ASYNC103", + "ASYNC104", + "ASYNC105", + "ASYNC106", + "ASYNC111", + "ASYNC112", + "ASYNC115", + "ASYNC116", + "ASYNC117", + "ASYNC118", } @@ -438,7 +451,7 @@ def test_noerror_on_sync_code(test: str, path: Path): def initialize_options(plugin: Plugin, args: list[str] | None = None): - parser = ArgumentParser(prog="flake8-trio") + parser = ArgumentParser(prog="flake8-async") Plugin.add_options(parser) Plugin.parse_options(parser.parse_args(args)) @@ -523,7 +536,7 @@ def assert_correct_lines_and_codes(errors: Iterable[Error], expected: Iterable[E print( "Lines with different # of errors:", "-" * 38, - f"| line | {'code':7} | actual | expected |", + f"| line | {'code':8} | actual | expected |", sep="\n", file=sys.stderr, ) @@ -601,7 +614,7 @@ def test_noqa_respected_depending_on_standalone(): with trio.move_on_after(10): ... # noqa """ plugin = Plugin.from_source(text) - initialize_options(plugin, args=["--enable=TRIO100"]) + initialize_options(plugin, args=["--enable=ASYNC100"]) assert plugin.standalone assert not tuple(plugin.run()) @@ -620,14 +633,16 @@ def test_line_numbers_match_end_result(): trio.sleep(0) """ plugin = Plugin.from_source(text) - initialize_options(plugin, args=["--enable=TRIO100,TRIO115", "--autofix=TRIO100"]) + initialize_options( + plugin, args=["--enable=ASYNC100,ASYNC115", "--autofix=ASYNC100"] + ) errors = tuple(plugin.run()) assert errors[1].line != plugin.module.code.split("\n").index("trio.sleep(0)") + 1 @pytest.mark.fuzz() def test_910_permutations(): - """Tests all possible permutations for TRIO910. + """Tests all possible permutations for ASYNC910. Since each test is so fast, and there's so many permutations, manually doing the permutations in a single test is much faster than the permutations from using @@ -651,7 +666,7 @@ async def foo(): await foo() | ... | return | None """ plugin = Plugin(ast.AST(), []) - initialize_options(plugin, args=["--enable=TRIO910"]) + initialize_options(plugin, args=["--enable=ASYNC910"]) check = "await foo()" @@ -722,7 +737,7 @@ def test_does_not_crash_on_any_valid_code(self, syntax_tree: ast.AST): # so `--enable-codes` can be passed through. # Though I barely notice a difference manually changing this value, or even # not running the plugin at all, so overhead looks to be vast majority of runtime - enabled_codes = "TRIO" + enabled_codes = "ASYNC" # Given any syntatically-valid source code, the checker should # not crash. This tests doesn't check that we do the *right* thing, diff --git a/tests/test_messages_documented.py b/tests/test_messages_documented.py index 1d0a7b9..33536c6 100755 --- a/tests/test_messages_documented.py +++ b/tests/test_messages_documented.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -"""Tests for flake8-trio package metadata.""" +"""Tests for flake8-async package metadata.""" from __future__ import annotations @@ -14,8 +14,14 @@ README = CHANGELOG.parent / "README.md" # 107, 108 & 117 are removed (but still mentioned in changelog & readme) -# TRIOxxx_* are fake codes to get different error messages for the same code -IGNORED_CODES_REGEX = r"TRIO107|TRIO108|TRIO117|TRIO\d\d\d_.*" +# ASYNCxxx_* are fake codes to get different error messages for the same code +IGNORED_CODES_REGEX = r"(TRIO|ASYNC)(107|108|117)|ASYNC\d\d\d_.*" + + +# temp function for eval files +# less sure what to do with the changelog +def rename_trio_to_async(s: str) -> str: + return re.sub("TRIO", "ASYNC", s) def test_messages_documented(): @@ -26,12 +32,12 @@ def test_messages_documented(): filename = path.name documented_errors[filename] = set() for line in lines: - for error_msg in re.findall(r"TRIO\d\d\d", line): - documented_errors[filename].add(error_msg) + for error_msg in re.findall(r"TRIO\d\d\d|ASYNC\d\d\d", line): + documented_errors[filename].add(rename_trio_to_async(error_msg)) documented_errors["flake8_trio.py"] = set(ERROR_CODES) - # get tested error codes from file names and from `INCLUDE` lines + # get tested error codes from file names and from `# ARG --enable` lines documented_errors["eval_files"] = set() p = Path(__file__).parent / "eval_files" for file_path in p.iterdir(): @@ -39,26 +45,30 @@ def test_messages_documented(): continue if m := re.search(r"trio\d\d\d", str(file_path)): - documented_errors["eval_files"].add(m.group().upper()) + documented_errors["eval_files"].add(rename_trio_to_async(m.group().upper())) with open(file_path) as file: for line in file: if line.startswith("# ARG --enable"): - for m in re.findall(r"trio\d\d\d", line, re.IGNORECASE): + for m in re.findall(r"async\d\d\d", line, re.IGNORECASE): # pyright types m as `Any` (as it is in typeshed) # mypy types it as Optional[Match[str]] # but afaict it should be something like str|Tuple[str,...] # depending on whether there's a group in the pattern or not. # (or bytes, if both inputs are bytes) + # see https://github.com/python/typeshed/issues/263 documented_errors["eval_files"].add(cast("str", m)) break + # ignore codes that aren't actually codes, and removed codes for errset in documented_errors.values(): errset.difference_update( [c for c in errset if re.fullmatch(IGNORED_CODES_REGEX, c)] ) + # error is only listed in one file unique_errors: dict[str, set[str]] = {} + # error is mentioned in other files but not in this one missing_errors: dict[str, set[str]] = {} for key, codes in documented_errors.items(): unique_errors[key] = codes.copy() @@ -70,4 +80,5 @@ def test_messages_documented(): unique_errors[key].difference_update(other_codes) missing_errors[key].update(other_codes - codes) + # both of these should be dicts with empty sets assert unique_errors == missing_errors