Coverage for src/debputy/plugin/api/example_processing.py: 95%
63 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-10-12 15:06 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-10-12 15:06 +0000
1import dataclasses
2from enum import Enum
3from typing import Set, Tuple, cast, Dict
4from collections.abc import Sequence
6from debputy.filesystem_scan import build_virtual_fs
7from debputy.plugin.api import VirtualPath
8from debputy.plugin.api.impl_types import (
9 AutomaticDiscardRuleExample,
10 PluginProvidedDiscardRule,
11)
12from debputy.util import _normalize_path
15class DiscardVerdict(Enum):
16 INCONSISTENT_CODE_KEPT = (
17 None,
18 "INCONSISTENT (code kept the path, but should have discarded)",
19 )
20 INCONSISTENT_CODE_DISCARDED = (
21 None,
22 "INCONSISTENT (code discarded the path, but should have kept it)",
23 )
24 KEPT = (False, "Kept")
25 DISCARDED_BY_CODE = (True, "Discarded (directly by the rule)")
26 DISCARDED_BY_DIRECTORY = (True, "Discarded (directory was discarded)")
28 @property
29 def message(self) -> str:
30 return cast("str", self.value[1])
32 @property
33 def is_consistent(self) -> bool:
34 return self.value[0] is not None
36 @property
37 def is_discarded(self) -> bool:
38 return self.value[0] is True
40 @property
41 def is_kept(self) -> bool:
42 return self.value[0] is False
45@dataclasses.dataclass(slots=True, frozen=True)
46class ProcessedDiscardRuleExample:
47 rendered_paths: Sequence[tuple[VirtualPath, DiscardVerdict]]
48 inconsistent_paths: set[VirtualPath]
49 # To avoid the parents being garbage collected
50 fs_root: VirtualPath
53def process_discard_rule_example(
54 discard_rule: PluginProvidedDiscardRule,
55 example: AutomaticDiscardRuleExample,
56) -> ProcessedDiscardRuleExample:
57 fs_root: VirtualPath = build_virtual_fs([p for p, _ in example.content])
59 actual_discarded: dict[str, bool] = {}
60 expected_output = {
61 "/" + _normalize_path(p.path_name, with_prefix=False): v
62 for p, v in example.content
63 }
64 inconsistent_paths = set()
65 rendered_paths = []
67 for p in fs_root.all_paths():
68 parent = p.parent_dir
69 discard_carry_over = False
70 path_name = p.absolute
71 if parent and actual_discarded[parent.absolute]:
72 verdict = True
73 discard_carry_over = True
74 else:
75 verdict = discard_rule.should_discard(p)
77 actual_discarded[path_name] = verdict
78 expected = expected_output.get(path_name)
79 if expected is not None:
80 inconsistent = expected != verdict
81 if inconsistent:
82 inconsistent_paths.add(p)
83 else:
84 continue
86 if inconsistent:
87 if verdict:
88 verdict_code = DiscardVerdict.INCONSISTENT_CODE_DISCARDED
89 else:
90 verdict_code = DiscardVerdict.INCONSISTENT_CODE_KEPT
91 elif verdict:
92 if discard_carry_over:
93 verdict_code = DiscardVerdict.DISCARDED_BY_DIRECTORY
94 else:
95 verdict_code = DiscardVerdict.DISCARDED_BY_CODE
96 else:
97 verdict_code = DiscardVerdict.KEPT
98 rendered_paths.append((p, verdict_code))
100 return ProcessedDiscardRuleExample(rendered_paths, inconsistent_paths, fs_root)