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

1import dataclasses 

2from enum import Enum 

3from typing import Set, Tuple, cast, Dict 

4from collections.abc import Sequence 

5 

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 

13 

14 

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)") 

27 

28 @property 

29 def message(self) -> str: 

30 return cast("str", self.value[1]) 

31 

32 @property 

33 def is_consistent(self) -> bool: 

34 return self.value[0] is not None 

35 

36 @property 

37 def is_discarded(self) -> bool: 

38 return self.value[0] is True 

39 

40 @property 

41 def is_kept(self) -> bool: 

42 return self.value[0] is False 

43 

44 

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 

51 

52 

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]) 

58 

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 = [] 

66 

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) 

76 

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 

85 

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)) 

99 

100 return ProcessedDiscardRuleExample(rendered_paths, inconsistent_paths, fs_root)