From 1adacac1b6fd509d564cad4d720b070bdfff239e Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Mon, 24 Jan 2022 10:13:42 +0000 Subject: [PATCH 1/4] Add check for alphabetical order. --- doc-source/usage.rst | 15 +++ flake8_dunder_all/__init__.py | 115 +++++++++++++++++-- formate.toml | 1 + requirements.txt | 1 + tests/test_flake8_dunder_all.py | 192 ++++++++++++++++++++++++++++++-- 5 files changed, 300 insertions(+), 24 deletions(-) diff --git a/doc-source/usage.rst b/doc-source/usage.rst index 617c19d..203febb 100644 --- a/doc-source/usage.rst +++ b/doc-source/usage.rst @@ -12,6 +12,21 @@ Flake8 codes .. flake8-codes:: flake8_dunder_all DALL000 + DALL001 + DALL002 + + +For the ``DALL001`` option there exists a configuration option (``dunder-all-alphabetical``) +which controls the alphabetical grouping expected of ``__all__``. +The options are: + +* ``ignore`` -- ``__all__`` should be sorted alphabetically ignoring case, e.g. ``['bar', 'Baz', 'foo']`` +* ``lower`` -- group lowercase names first, then uppercase names, e.g. ``['bar', 'foo', 'Baz']`` +* ``upper`` -- group uppercase names first, then uppercase names, e.g. ``['Baz', 'Foo', 'bar']`` + +If the ``dunder-all-alphabetical`` option is omitted the ``DALL001`` check is disabled. + +.. versionchanged:: 0.5.0 Added the ``DALL001`` and ``DALL002`` checks. .. note:: diff --git a/flake8_dunder_all/__init__.py b/flake8_dunder_all/__init__.py index f9a0d95..34027b6 100644 --- a/flake8_dunder_all/__init__.py +++ b/flake8_dunder_all/__init__.py @@ -32,13 +32,16 @@ # stdlib import ast import sys -from typing import Any, Generator, Iterator, List, Set, Tuple, Type, Union +from enum import Enum +from typing import Any, Generator, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, cast # 3rd party +import natsort from consolekit.terminal_colours import Fore from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike from domdf_python_tools.utils import stderr_writer +from flake8.options.manager import OptionManager # type: ignore # this package from flake8_dunder_all.utils import find_noqa, get_docstring_lineno, mark_text_ranges @@ -49,9 +52,32 @@ __version__: str = "0.4.1" __email__: str = "dominic@davis-foster.co.uk" -__all__ = ("Visitor", "Plugin", "check_and_add_all", "DALL000") +__all__ = ( + "check_and_add_all", + "AlphabeticalOptions", + "DALL000", + "DALL001", + "DALL002", + "Plugin", + "Visitor", + ) DALL000 = "DALL000 Module lacks __all__." +DALL001 = "DALL001 __all__ not sorted alphabetically" +DALL002 = "DALL002 __all__ not a list of strings." + + +class AlphabeticalOptions(Enum): + """ + Enum of possible values for the ``--dunder-all-alphabetical`` option. + + .. versionadded:: 0.5.0 + """ + + UPPER = "upper" + LOWER = "lower" + IGNORE = "ignore" + NONE = "none" class Visitor(ast.NodeVisitor): @@ -61,30 +87,56 @@ class Visitor(ast.NodeVisitor): :param use_endlineno: Flag to indicate whether the end_lineno functionality is available. This functionality is available on Python 3.8 and above, or when the tree has been passed through :func:`flake8_dunder_all.utils.mark_text_ranges``. + + .. versionchanged:: 0.5.0 + + Added the ``sorted_upper_first``, ``sorted_lower_first`` and ``all_lineno`` attributes. """ found_all: bool #: Flag to indicate a ``__all__`` declaration has been found in the AST. last_import: int #: The lineno of the last top-level or conditional import members: Set[str] #: List of functions and classed defined in the AST use_endlineno: bool + all_members: Optional[Sequence[str]] #: The value of ``__all__``. + all_lineno: int #: The line number where ``__all__`` is defined. def __init__(self, use_endlineno: bool = False) -> None: self.found_all = False self.members = set() self.last_import = 0 self.use_endlineno = use_endlineno + self.all_members = None + self.all_lineno = -1 - def visit_Name(self, node: ast.Name) -> None: - """ - Visit a variable. + def visit_Assign(self, node: ast.Assign) -> None: # noqa: D102 + targets = [] + for t in node.targets: + if isinstance(t, ast.Name): + targets.append(t.id) - :param node: The node being visited. - """ - - if node.id == "__all__": + if "__all__" in targets: self.found_all = True - else: - self.generic_visit(node) + self.all_lineno = node.lineno + self.all_members = self._parse_all(cast(ast.List, node.value)) + + def visit_AnnAssign(self, node: ast.AnnAssign) -> None: # noqa: D102 + if isinstance(node.target, ast.Name): + if node.target.id == "__all__": + self.all_lineno = node.lineno + self.found_all = True + self.all_members = self._parse_all(cast(ast.List, node.value)) + + @staticmethod + def _parse_all(all_node: ast.List) -> Optional[Sequence[str]]: + try: + all_ = ast.literal_eval(all_node) + except ValueError: + return None + + if not isinstance(all_, Sequence): + return None + + return all_ def handle_def(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef]) -> None: """ @@ -252,6 +304,7 @@ class Plugin: name: str = __name__ version: str = __version__ #: The plugin version + dunder_all_alphabetical: AlphabeticalOptions = AlphabeticalOptions.NONE def __init__(self, tree: ast.AST): self._tree = tree @@ -272,12 +325,50 @@ def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]: visitor.visit(self._tree) if visitor.found_all: - return + if visitor.all_members is None: + yield visitor.all_lineno, 0, DALL002, type(self) + + elif self.dunder_all_alphabetical == AlphabeticalOptions.IGNORE: + # Alphabetical, upper or lower don't matter + sorted_alphabetical = natsort.natsorted(visitor.all_members, key=str.lower) + if visitor.all_members != sorted_alphabetical: + yield visitor.all_lineno, 0, f"{DALL001}.", type(self) + elif self.dunder_all_alphabetical == AlphabeticalOptions.UPPER: + # Alphabetical, uppercase grouped first + sorted_alphabetical = natsort.natsorted(visitor.all_members) + if visitor.all_members != sorted_alphabetical: + yield visitor.all_lineno, 0, f"{DALL001} (uppercase first).", type(self) + elif self.dunder_all_alphabetical == AlphabeticalOptions.LOWER: + # Alphabetical, lowercase grouped first + sorted_alphabetical = natsort.natsorted(visitor.all_members, alg=natsort.ns.LOWERCASEFIRST) + if visitor.all_members != sorted_alphabetical: + yield visitor.all_lineno, 0, f"{DALL001} (lowercase first).", type(self) + elif not visitor.members: return + else: yield 1, 0, DALL000, type(self) + @classmethod + def add_options(cls, option_manager: OptionManager) -> None: # noqa: D102 # pragma: no cover + + option_manager.add_option( + "--dunder-all-alphabetical", + choices=[member.value for member in AlphabeticalOptions], + parse_from_config=True, + default=AlphabeticalOptions.NONE.value, + help=( + "Require entries in '__all__' to be alphabetical ([upper] or [lower]case first)." + "(Default: %(default)s)" + ), + ) + + @classmethod + def parse_options(cls, options): # noqa: D102 # pragma: no cover + # note: this sets the option on the class and not the instance + cls.dunder_all_alphabetical = AlphabeticalOptions(options.dunder_all_alphabetical) + def check_and_add_all(filename: PathLike, quote_type: str = '"', use_tuple: bool = False) -> int: """ diff --git a/formate.toml b/formate.toml index 82a7136..f127306 100644 --- a/formate.toml +++ b/formate.toml @@ -38,6 +38,7 @@ known_third_party = [ "flake8", "github", "importlib_metadata", + "natsort", "pytest", "pytest_cov", "pytest_randomly", diff --git a/requirements.txt b/requirements.txt index ac873e6..6bd2469 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ click>=7.1.2 consolekit>=0.8.1 domdf-python-tools>=2.6.0 flake8>=3.7 +natsort>=8.0.2 diff --git a/tests/test_flake8_dunder_all.py b/tests/test_flake8_dunder_all.py index d1c35dd..a870e2d 100644 --- a/tests/test_flake8_dunder_all.py +++ b/tests/test_flake8_dunder_all.py @@ -11,7 +11,15 @@ if_type_checking_else_source, if_type_checking_source, if_type_checking_try_finally_source, - if_type_checking_try_source, + if_type_checking_try_source + ) +from consolekit.terminal_colours import Fore +from domdf_python_tools.paths import PathPlus + +# this package +from flake8_dunder_all import AlphabeticalOptions, Plugin, Visitor, check_and_add_all +from flake8_dunder_all.utils import mark_text_ranges +from tests.common import ( mangled_source, not_type_checking_if_source, results, @@ -32,12 +40,6 @@ testing_source_m, testing_source_n ) -from consolekit.terminal_colours import Fore -from domdf_python_tools.paths import PathPlus - -# this package -from flake8_dunder_all import Visitor, check_and_add_all -from flake8_dunder_all.utils import mark_text_ranges @pytest.mark.parametrize( @@ -49,13 +51,16 @@ pytest.param(testing_source_b, {"1:0: DALL000 Module lacks __all__."}, id="function no __all__"), pytest.param(testing_source_c, {"1:0: DALL000 Module lacks __all__."}, id="class no __all__"), pytest.param( - testing_source_d, {"1:0: DALL000 Module lacks __all__."}, - id="function and class no __all__" + testing_source_d, + {"1:0: DALL000 Module lacks __all__."}, + id="function and class no __all__.", ), - pytest.param(testing_source_e, set(), id="function and class with __all__"), - pytest.param(testing_source_f, set(), id="function and class with __all__ and extra variable"), + pytest.param(testing_source_e, set(), id="function and class with __all__."), + pytest.param(testing_source_f, set(), id="function and class with __all__. and extra variable"), pytest.param( - testing_source_g, {"1:0: DALL000 Module lacks __all__."}, id="async function no __all__" + testing_source_g, + {"1:0: DALL000 Module lacks __all__."}, + id="async function no __all__", ), pytest.param(testing_source_h, set(), id="from import"), pytest.param(testing_source_i, {"1:0: DALL000 Module lacks __all__."}, id="lots of lines"), @@ -66,6 +71,169 @@ def test_plugin(source: str, expects: Set[str]): assert results(source) == expects +@pytest.mark.parametrize( + "source, dunder_all_alphabetical, expects", + [ + pytest.param( + "__all__ = ['foo', 'bar', 'Baz']", + AlphabeticalOptions.NONE, + set(), + id="NONE_wrong_order", + ), + pytest.param( + "__all__ = ['bar', 'Baz', 'foo']", + AlphabeticalOptions.NONE, + set(), + id="NONE_right_order_1", + ), + pytest.param( + "__all__ = ['Bar', 'baz', 'foo']", + AlphabeticalOptions.NONE, + set(), + id="NONE_right_order_2", + ), + pytest.param( + "__all__ = ['foo', 'bar', 'Baz']", + AlphabeticalOptions.IGNORE, + {"1:0: DALL001 __all__ not sorted alphabetically."}, + id="IGNORE_wrong_order", + ), + pytest.param( + "__all__ = ['bar', 'Baz', 'foo']", + AlphabeticalOptions.IGNORE, + set(), + id="IGNORE_right_order", + ), + pytest.param( + "__all__ = ['Baz', 'bar', 'foo']", + AlphabeticalOptions.LOWER, + {"1:0: DALL001 __all__ not sorted alphabetically (lowercase first)."}, + id="LOWER_wrong_order", + ), + pytest.param( + "__all__ = ['bar', 'foo', 'Baz']", + AlphabeticalOptions.LOWER, + set(), + id="LOWER_right_order", + ), + pytest.param( + "__all__ = ['bar', 'Baz', 'foo']", + AlphabeticalOptions.UPPER, + {"1:0: DALL001 __all__ not sorted alphabetically (uppercase first)."}, + id="UPPER_wrong_order", + ), + pytest.param( + "__all__ = ['Baz', 'bar', 'foo']", + AlphabeticalOptions.UPPER, + set(), + id="UPPER_right_order_1", + ), + pytest.param( + "__all__ = ['Baz', 'Foo', 'bar']", + AlphabeticalOptions.UPPER, + set(), + id="UPPER_right_order_2", + ), + ] + ) +def test_plugin_alphabetical(source: str, expects: Set[str], dunder_all_alphabetical: AlphabeticalOptions): + plugin = Plugin(ast.parse(source)) + plugin.dunder_all_alphabetical = dunder_all_alphabetical + assert {"{}:{}: {}".format(*r) for r in plugin.run()} == expects + + +@pytest.mark.parametrize( + "source, dunder_all_alphabetical, expects", + [ + pytest.param( + "__all__: List[str] = ['foo', 'bar', 'Baz']", + AlphabeticalOptions.NONE, + set(), + id="NONE_wrong_order", + ), + pytest.param( + "__all__: List[str] = ['bar', 'Baz', 'foo']", + AlphabeticalOptions.NONE, + set(), + id="NONE_right_order_1", + ), + pytest.param( + "__all__: List[str] = ['Bar', 'baz', 'foo']", + AlphabeticalOptions.NONE, + set(), + id="NONE_right_order_1", + ), + pytest.param( + "__all__: List[str] = ['foo', 'bar', 'Baz']", + AlphabeticalOptions.IGNORE, + {"1:0: DALL001 __all__ not sorted alphabetically."}, + id="IGNORE_wrong_order", + ), + pytest.param( + "__all__: List[str] = ['bar', 'Baz', 'foo']", + AlphabeticalOptions.IGNORE, + set(), + id="IGNORE_right_order", + ), + pytest.param( + "__all__: List[str] = ['Baz', 'bar', 'foo']", + AlphabeticalOptions.LOWER, + {"1:0: DALL001 __all__ not sorted alphabetically (lowercase first)."}, + id="LOWER_wrong_order", + ), + pytest.param( + "__all__: List[str] = ['bar', 'foo', 'Baz']", + AlphabeticalOptions.LOWER, + set(), + id="LOWER_right_order", + ), + pytest.param( + "__all__: List[str] = ['bar', 'Baz', 'foo']", + AlphabeticalOptions.UPPER, + {"1:0: DALL001 __all__ not sorted alphabetically (uppercase first)."}, + id="UPPER_wrong_order", + ), + pytest.param( + "__all__: List[str] = ['Baz', 'bar', 'foo']", + AlphabeticalOptions.UPPER, + set(), + id="UPPER_right_order_1", + ), + pytest.param( + "__all__: List[str] = ['Baz', 'Foo', 'bar']", + AlphabeticalOptions.UPPER, + set(), + id="UPPER_right_order_2", + ), + ] + ) +def test_plugin_alphabetical_ann_assign( + source: str, expects: Set[str], dunder_all_alphabetical: AlphabeticalOptions + ): + plugin = Plugin(ast.parse(source)) + plugin.dunder_all_alphabetical = dunder_all_alphabetical + assert {"{}:{}: {}".format(*r) for r in plugin.run()} == expects + + +@pytest.mark.parametrize( + "source, dunder_all_alphabetical", + [ + pytest.param("__all__ = 12345", AlphabeticalOptions.IGNORE, id="IGNORE_123"), + pytest.param("__all__ = 12345", AlphabeticalOptions.LOWER, id="LOWER_123"), + pytest.param("__all__ = 12345", AlphabeticalOptions.NONE, id="NONE_123"), + pytest.param("__all__ = 12345", AlphabeticalOptions.UPPER, id="UPPER_123"), + pytest.param("__all__ = abc", AlphabeticalOptions.IGNORE, id="IGNORE_abc"), + pytest.param("__all__ = abc", AlphabeticalOptions.LOWER, id="LOWER_abc"), + pytest.param("__all__ = abc", AlphabeticalOptions.NONE, id="NONE_abc"), + pytest.param("__all__ = abc", AlphabeticalOptions.UPPER, id="UPPER_abc"), + ] + ) +def test_plugin_alphabetical_not_list(source: str, dunder_all_alphabetical: AlphabeticalOptions): + plugin = Plugin(ast.parse(source)) + plugin.dunder_all_alphabetical = dunder_all_alphabetical + assert {"{}:{}: {}".format(*r) for r in plugin.run()} == {"1:0: DALL002 __all__ not a list of strings."} + + @pytest.mark.parametrize( "source, members, found_all, last_import", [ From 42a89031d9f9d5c7942084a660dae828d503f242 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Wed, 21 May 2025 14:37:05 +0100 Subject: [PATCH 2/4] Allow __all__ to be tuple (which brings slight benefits) --- flake8_dunder_all/__init__.py | 8 ++++---- tests/test_flake8_dunder_all.py | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/flake8_dunder_all/__init__.py b/flake8_dunder_all/__init__.py index 34027b6..6cb83e9 100644 --- a/flake8_dunder_all/__init__.py +++ b/flake8_dunder_all/__init__.py @@ -64,7 +64,7 @@ DALL000 = "DALL000 Module lacks __all__." DALL001 = "DALL001 __all__ not sorted alphabetically" -DALL002 = "DALL002 __all__ not a list of strings." +DALL002 = "DALL002 __all__ not a list or tuple of strings." class AlphabeticalOptions(Enum): @@ -331,17 +331,17 @@ def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]: elif self.dunder_all_alphabetical == AlphabeticalOptions.IGNORE: # Alphabetical, upper or lower don't matter sorted_alphabetical = natsort.natsorted(visitor.all_members, key=str.lower) - if visitor.all_members != sorted_alphabetical: + if list(visitor.all_members) != sorted_alphabetical: yield visitor.all_lineno, 0, f"{DALL001}.", type(self) elif self.dunder_all_alphabetical == AlphabeticalOptions.UPPER: # Alphabetical, uppercase grouped first sorted_alphabetical = natsort.natsorted(visitor.all_members) - if visitor.all_members != sorted_alphabetical: + if list(visitor.all_members) != sorted_alphabetical: yield visitor.all_lineno, 0, f"{DALL001} (uppercase first).", type(self) elif self.dunder_all_alphabetical == AlphabeticalOptions.LOWER: # Alphabetical, lowercase grouped first sorted_alphabetical = natsort.natsorted(visitor.all_members, alg=natsort.ns.LOWERCASEFIRST) - if visitor.all_members != sorted_alphabetical: + if list(visitor.all_members) != sorted_alphabetical: yield visitor.all_lineno, 0, f"{DALL001} (lowercase first).", type(self) elif not visitor.members: diff --git a/tests/test_flake8_dunder_all.py b/tests/test_flake8_dunder_all.py index a870e2d..dd34182 100644 --- a/tests/test_flake8_dunder_all.py +++ b/tests/test_flake8_dunder_all.py @@ -231,7 +231,14 @@ def test_plugin_alphabetical_ann_assign( def test_plugin_alphabetical_not_list(source: str, dunder_all_alphabetical: AlphabeticalOptions): plugin = Plugin(ast.parse(source)) plugin.dunder_all_alphabetical = dunder_all_alphabetical - assert {"{}:{}: {}".format(*r) for r in plugin.run()} == {"1:0: DALL002 __all__ not a list of strings."} + msg = "1:0: DALL002 __all__ not a list or tuple of strings." + assert {"{}:{}: {}".format(*r) for r in plugin.run()} == {msg} + + +def test_plugin_alphabetical_tuple(): + plugin = Plugin(ast.parse("__all__ = ('bar',\n'foo')")) + plugin.dunder_all_alphabetical = AlphabeticalOptions.IGNORE + assert {"{}:{}: {}".format(*r) for r in plugin.run()} == set() @pytest.mark.parametrize( From 0c22f7cb155070b6ac202b9eb01baa5d4869d4bf Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Wed, 21 May 2025 15:03:16 +0100 Subject: [PATCH 3/4] Use CliRunner from consolekit --- tests/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index fdd55ff..2ab3e15 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -4,9 +4,9 @@ # 3rd party import pytest -from click.testing import CliRunner, Result from coincidence.regressions import AdvancedFileRegressionFixture from consolekit.terminal_colours import Fore +from consolekit.testing import CliRunner, Result from domdf_python_tools.paths import PathPlus from test_flake8_dunder_all import ( mangled_source, From d33af41837ae2de75735b1cb217f0327368929b1 Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Wed, 21 May 2025 15:22:26 +0100 Subject: [PATCH 4/4] Lint --- flake8_dunder_all/__init__.py | 10 +++++++--- tests/__init__.py | 0 tests/test_flake8_dunder_all.py | 10 ++++------ tests/test_main.py | 8 ++++---- 4 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 tests/__init__.py diff --git a/flake8_dunder_all/__init__.py b/flake8_dunder_all/__init__.py index 6cb83e9..ee73b72 100644 --- a/flake8_dunder_all/__init__.py +++ b/flake8_dunder_all/__init__.py @@ -33,7 +33,7 @@ import ast import sys from enum import Enum -from typing import Any, Generator, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, cast +from typing import TYPE_CHECKING, Any, Generator, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, cast # 3rd party import natsort @@ -41,11 +41,15 @@ from domdf_python_tools.paths import PathPlus from domdf_python_tools.typing import PathLike from domdf_python_tools.utils import stderr_writer -from flake8.options.manager import OptionManager # type: ignore +from flake8.options.manager import OptionManager # type: ignore[import] # this package from flake8_dunder_all.utils import find_noqa, get_docstring_lineno, mark_text_ranges +if TYPE_CHECKING: + # stdlib + from argparse import Namespace + __author__: str = "Dominic Davis-Foster" __copyright__: str = "2020 Dominic Davis-Foster" __license__: str = "MIT" @@ -365,7 +369,7 @@ def add_options(cls, option_manager: OptionManager) -> None: # noqa: D102 # pr ) @classmethod - def parse_options(cls, options): # noqa: D102 # pragma: no cover + def parse_options(cls, options: "Namespace") -> None: # noqa: D102 # pragma: no cover # note: this sets the option on the class and not the instance cls.dunder_all_alphabetical = AlphabeticalOptions(options.dunder_all_alphabetical) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_flake8_dunder_all.py b/tests/test_flake8_dunder_all.py index dd34182..a7ede9d 100644 --- a/tests/test_flake8_dunder_all.py +++ b/tests/test_flake8_dunder_all.py @@ -7,12 +7,6 @@ # 3rd party import pytest from coincidence.regressions import AdvancedFileRegressionFixture -from common import ( - if_type_checking_else_source, - if_type_checking_source, - if_type_checking_try_finally_source, - if_type_checking_try_source - ) from consolekit.terminal_colours import Fore from domdf_python_tools.paths import PathPlus @@ -20,6 +14,10 @@ from flake8_dunder_all import AlphabeticalOptions, Plugin, Visitor, check_and_add_all from flake8_dunder_all.utils import mark_text_ranges from tests.common import ( + if_type_checking_else_source, + if_type_checking_source, + if_type_checking_try_finally_source, + if_type_checking_try_source, mangled_source, not_type_checking_if_source, results, diff --git a/tests/test_main.py b/tests/test_main.py index 2ab3e15..e9383a9 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -8,7 +8,10 @@ from consolekit.terminal_colours import Fore from consolekit.testing import CliRunner, Result from domdf_python_tools.paths import PathPlus -from test_flake8_dunder_all import ( + +# this package +from flake8_dunder_all.__main__ import main +from tests.test_flake8_dunder_all import ( mangled_source, testing_source_a, testing_source_b, @@ -25,9 +28,6 @@ testing_source_l ) -# this package -from flake8_dunder_all.__main__ import main - @pytest.mark.parametrize( "source, members, ret",