diff --git a/flake8_async/__init__.py b/flake8_async/__init__.py index d5e42ff8..92af9654 100644 --- a/flake8_async/__init__.py +++ b/flake8_async/__init__.py @@ -138,7 +138,7 @@ def __init__( self.module: cst.Module = cst_parse_module_native(source) @classmethod - def from_filename(cls, filename: str | PathLike[str]) -> Plugin: # pragma: no cover + def from_filename(cls, filename: str | PathLike[str]) -> Plugin: # only used with --runslow with tokenize.open(filename) as f: source = f.read() @@ -204,7 +204,7 @@ def add_options(option_manager: OptionManager | ArgumentParser): 'lines with "# noqa" at the end.' ), ) - else: # if run as a flake8 plugin + else: # pragma: no-cov-no-flake8 Plugin.standalone = False # Disable ASYNC9xx calls by default option_manager.extend_default_ignore(default_disabled_error_codes) @@ -380,7 +380,7 @@ def get_matching_codes( } assert all_codes - if options.autofix and not Plugin.standalone: + if options.autofix and not Plugin.standalone: # pragma: no-cov-no-flake8 print("Cannot autofix when run as a flake8 plugin.", file=sys.stderr) sys.exit(1) autofix_codes = set(get_matching_codes(options.autofix, all_codes)) @@ -457,6 +457,7 @@ def parse_async200_dict(raw_value: str) -> dict[str, str]: # not run if flake8 is installed +# TODO: this is not tested at all atm, I'm not even sure if it works def parse_per_file_disable( # pragma: no cover raw_value: str, ) -> dict[str, tuple[str, ...]]: diff --git a/flake8_async/visitors/helpers.py b/flake8_async/visitors/helpers.py index 642c62bd..4646fc4f 100644 --- a/flake8_async/visitors/helpers.py +++ b/flake8_async/visitors/helpers.py @@ -371,7 +371,7 @@ def with_has_call( """ if isinstance(base, str): - base = (base,) # pragma: no cover + base = (base,) # build matcher, using SaveMatchedNode to save the base and the function name. matcher = m.Call( diff --git a/flake8_async/visitors/visitor123.py b/flake8_async/visitors/visitor123.py index 58237c18..32fd5a06 100644 --- a/flake8_async/visitors/visitor123.py +++ b/flake8_async/visitors/visitor123.py @@ -78,8 +78,7 @@ def visit_ExceptHandler(self, node: ast.ExceptHandler): self.exception_group_names = {node.name} # ast.TryStar added in py311 - # we run strict codecov on all python versions, this one doesn't run on bool: - if code is None: # pragma: no branch + if code is None: code = "ASYNC911" if self.has_yield else "ASYNC910" return ( diff --git a/pyproject.toml b/pyproject.toml index 30bbbd85..ae0b6180 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,34 @@ force-exclude = "tests/autofix_files/.*.py" [tool.codespell] ignore-words-list = 'spawnve' +[tool.coverage.coverage_conditional_plugin.rules] +no-cov-has-flake8 = "is_installed('flake8')" +no-cov-no-flake8 = "not is_installed('flake8')" +no-cov-py-lt-311 = "sys.version_info < (3, 11)" + +[tool.coverage.paths] +source = [ + "flake8_async", + "*/site-packages/flake8_async" +] + +[tool.coverage.report] +exclude_lines = [ + # Have to re-enable the standard pragma + "pragma: no cover", + # Don't complain about abstract methods, they aren't run: + "@(abc\\.)?abstractmethod", + # Don't check guarded type imports + "if (typing.)?TYPE_CHECKING:" +] +fail_under = 100 +skip_covered = true +skip_empty = true + +[tool.coverage.run] +branch = true +plugins = ["coverage_conditional_plugin"] + [tool.isort] only_modified = true profile = "black" @@ -45,6 +73,10 @@ reportUnnecessaryTypeIgnoreComment = false reportUnusedCallResult = false strict = ["*.py", "tests/*.py", "flake8_async/**/*.py"] +[tool.pytest.ini_options] +filterwarnings = ["error"] +testpaths = ["tests"] + [tool.ruff] extend-exclude = [ ".*", diff --git a/tests/test_config_and_args.py b/tests/test_config_and_args.py index a999a095..3d3c03ec 100644 --- a/tests/test_config_and_args.py +++ b/tests/test_config_and_args.py @@ -282,6 +282,24 @@ def test_async200_from_config_subprocess(tmp_path: Path): assert res.stdout == err_msg.encode("ascii") +def test_trio200_warning(tmp_path: Path): + fpath = tmp_path / "foo.py" + fpath.touch() + res = subprocess.run( + ["flake8-async", "--trio200-blocking-calls=foo->bar", "foo.py"], + cwd=tmp_path, + capture_output=True, + check=False, + encoding="utf8", + ) + assert res.returncode == 0 + assert res.stderr.endswith( + "UserWarning: trio200-blocking-calls has been deprecated in favor of " + "async200-blocking-calls\n warnings.warn(\n" + ) + assert not res.stdout + + @pytest.mark.skipif(flake8 is None, reason="flake8 is not installed") def test_async200_from_config_subprocess_cli_ignore(tmp_path: Path): _ = _test_async200_from_config_common(tmp_path) @@ -297,7 +315,7 @@ def test_async200_from_config_subprocess_cli_ignore(tmp_path: Path): assert res.returncode == 0 -def test_900_default_off(capsys: pytest.CaptureFixture[str]): +def test_900_default_off(): res = subprocess.run( ["flake8-async", "tests/eval_files/async900.py"], capture_output=True, diff --git a/tox.ini b/tox.ini index ab012d9c..f9578884 100644 --- a/tox.ini +++ b/tox.ini @@ -11,14 +11,16 @@ deps = flake8_7: flake8>=7.0 flake8_6: flake8>=6.0, <7.0 pytest - pytest-cov + pytest-cov # to make it easy to pass --no-cov + coverage + coverage-conditional-plugin pytest-xdist hypothesis # 0.3.3 adds py313 support hypothesmith >= 0.3.3 trio commands = - pytest {posargs:-n auto} + coverage run -m pytest {posargs:-n auto} [testenv:docs] description = Generate docs locally @@ -32,29 +34,3 @@ skip_install = True commands = make clean make html - -# Settings for other tools -[pytest] -addopts = - --cov=flake8_async - --cov-branch - --cov-report=term-missing:skip-covered - --cov-fail-under=100 -filterwarnings = - error - -[coverage:paths] -source = - flake8_async - */site-packages/flake8_async - -[coverage:report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain about abstract methods, they aren't run: - @(abc\.)?abstractmethod - - # Don't check guarded type imports - if (typing.)?TYPE_CHECKING: