Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/coretrace_python/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,14 @@
)


def rule_descriptions(registry: PluginRegistry) -> Mapping[str, RuleDescription]:
def rule_descriptions(plugins: Iterable[Plugin]) -> Mapping[str, RuleDescription]:
"""What every rule of a check detects, for its report: the rules the engine reports
itself, and those each loaded plugin describes, the first declaration of an id kept."""
itself, and those each of its plugins describes, loaded or given as an instance, the
first declaration of an id kept."""

described: dict[str, RuleDescription] = dict(ENGINE_RULES)
for loaded in registry:
for rule_id, description in loaded.plugin.rules.items():
for plugin in plugins:
for rule_id, description in plugin.rules.items():
described.setdefault(rule_id, description)
return MappingProxyType(described)

Expand Down Expand Up @@ -395,7 +396,7 @@ def analyze_file(source: SourceFile, plugin_roots: Sequence[Path], root: Path |
functions = len(analyzable_functions(manager.module))
coverage = Coverage((FileCoverage(str(source.source_id), "analysed", functions, len(supported)),))
kept, suppressed = partition(findings, lambda sid: source.text if sid == source.source_id else None)
return FileAnalysis(kept, coverage, suppressed, rule_descriptions(registry))
return FileAnalysis(kept, coverage, suppressed, rule_descriptions(loaded.plugin for loaded in registry))


def resolve_dependencies(root: Path, sources: SourceManager) -> DependencyGraph:
Expand Down Expand Up @@ -692,7 +693,7 @@ def analyze_project(
suppressed,
accepted,
components,
rule_descriptions(registry),
rule_descriptions(all_plugins),
)


Expand Down
40 changes: 40 additions & 0 deletions tests/test_rule_descriptions_of_given_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Acceptance tests for the review of #274: a plugin given to ``analyze_project`` as an
instance, rather than loaded from a plugin root, describes its rules in the report as a
loaded plugin does, the first declaration of an id kept.
"""

from __future__ import annotations

from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import ClassVar

from coretrace_python import engine
from coretrace_python.findings import Finding, RuleDescription
from coretrace_python.plugins import Plugin, PluginContext

QUIET = RuleDescription("Quiet rule", "A rule of a plugin given as an instance.")


class Quiet(Plugin):
name: ClassVar[str] = "quiet"
rules: ClassVar[Mapping[str, RuleDescription]] = {
"quiet-rule": QUIET,
"command-injection": RuleDescription("Another title", "Another description."),
}

def analyze(self, ctx: PluginContext) -> Sequence[Finding]:
return ()


def analysed(tmp_path: Path) -> engine.ProjectAnalysis:
(tmp_path / "app.py").write_text("x = 1\n", encoding="utf-8")
return engine.analyze_project(tmp_path, [engine.BUNDLED_PLUGINS], plugins=[Quiet()])


def test_a_plugin_given_as_an_instance_describes_its_rules(tmp_path: Path) -> None:
assert analysed(tmp_path).rules["quiet-rule"] == QUIET


def test_a_loaded_plugin_keeps_the_description_of_a_rule_an_instance_describes_too(tmp_path: Path) -> None:
assert analysed(tmp_path).rules["command-injection"].title == "Command injection"
Loading