Instagram / Fixit

Advanced Python linting framework with auto-fixes and hierarchical configuration that makes it easy to write custom in-repo lint rules.
https://fixit.rtfd.io/en/latest/
Other
659 stars 62 forks source link

Is there a way to detect from within a LintRule if it's being run under test? #466

Open jarshwah opened 3 weeks ago

jarshwah commented 3 weeks ago

I'd like to be able to detect when a LintRule is being run under test. I know it's possible to use the FilePathProvider to detect if the filename is valid.py or invalid.py but that can lead to false positives. Here's a full example of a rule I'm [re]implementing below.

From within the rule, I want to check that we're linting an __init__.py file, so use the FilePathProvider to retrieve that information. Unfortunately unit tests will fail unless we also allow-through tests.

Is there a better way to detect if we're running under test?

import libcst.matchers as m
from fixit import LintRule
from libcst import Module, metadata
from tools.fixit.utils.general_utils import fixit_invalid, fixit_valid

CONVENIENCE_IMPORTS_OR_DOCSTRING = (
    m.SimpleStatementLine(body=[m.Import()])
    | m.SimpleStatementLine(body=[m.ImportFrom()])
    | m.SimpleStatementLine(
        body=[m.Assign(targets=[m.AssignTarget(target=m.Name(value="__all__"))])]
    )
    | m.SimpleStatementLine(body=[m.Expr(value=m.SimpleString() | m.ConcatenatedString())])
)

class EmptyInitModule(LintRule):
    """
    Ensure that __init__ files only contain convenience imports.

    Reimplements EIM002 from https://github.com/samueljsb/flake8-empty-init-modules
    """

    VALID = [VALID_IMPORTS_WITH_ALL, VALID_IMPORTS]  # noqa: RUF012
    INVALID = [BAD_ASSIGNMENT, BAD_FUNCTION_CALL]  # noqa: RUF012
    METADATA_DEPENDENCIES = (metadata.FilePathProvider,)

    def visit_Module(self, node: Module) -> bool | None:
        filepath = str(self.get_metadata(metadata.FilePathProvider, node))
        if not filepath.endswith("__init__.py") and not filepath.endswith("valid.py"):
            return

        for child in node.body:
            if not m.matches(child, CONVENIENCE_IMPORTS_OR_DOCSTRING):
                self.report(
                    child, message="Only convenience imports are allowed in __init__ modules"
                )