Coverage for src/debputy/maintscript_snippet.py: 63%

87 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-07 12:14 +0200

1import dataclasses 

2from typing import Sequence, Optional, List, Literal, Iterable, Dict, Self 

3 

4from debputy.manifest_parser.base_types import DebputyDispatchableType 

5from debputy.manifest_parser.util import AttributePath 

6 

7STD_CONTROL_SCRIPTS = frozenset( 

8 { 

9 "preinst", 

10 "prerm", 

11 "postinst", 

12 "postrm", 

13 } 

14) 

15UDEB_CONTROL_SCRIPTS = frozenset( 

16 { 

17 "postinst", 

18 "menutest", 

19 "isinstallable", 

20 } 

21) 

22ALL_CONTROL_SCRIPTS = STD_CONTROL_SCRIPTS | UDEB_CONTROL_SCRIPTS | {"config"} 

23 

24 

25@dataclasses.dataclass(slots=True, frozen=True) 

26class MaintscriptSnippet: 

27 definition_source: str 

28 snippet: str 

29 snippet_order: Optional[Literal["service"]] = None 

30 

31 def script_content(self) -> str: 

32 lines = [ 

33 f"# Snippet source: {self.definition_source}\n", 

34 self.snippet, 

35 ] 

36 if not self.snippet.endswith("\n"): 36 ↛ 37line 36 didn't jump to line 37, because the condition on line 36 was never true

37 lines.append("\n") 

38 return "".join(lines) 

39 

40 

41class MaintscriptSnippetContainer: 

42 def __init__(self) -> None: 

43 self._generic_snippets: List[MaintscriptSnippet] = [] 

44 self._snippets_by_order: Dict[Literal["service"], List[MaintscriptSnippet]] = {} 

45 

46 def copy(self) -> "MaintscriptSnippetContainer": 

47 instance = self.__class__() 

48 instance._generic_snippets = self._generic_snippets.copy() 

49 instance._snippets_by_order = self._snippets_by_order.copy() 

50 return instance 

51 

52 def append(self, maintscript_snippet: MaintscriptSnippet) -> None: 

53 if maintscript_snippet.snippet_order is None: 53 ↛ 56line 53 didn't jump to line 56, because the condition on line 53 was never false

54 self._generic_snippets.append(maintscript_snippet) 

55 else: 

56 if maintscript_snippet.snippet_order not in self._snippets_by_order: 

57 self._snippets_by_order[maintscript_snippet.snippet_order] = [] 

58 self._snippets_by_order[maintscript_snippet.snippet_order].append( 

59 maintscript_snippet 

60 ) 

61 

62 def has_content(self, snippet_order: Optional[Literal["service"]] = None) -> bool: 

63 if snippet_order is None: 

64 return bool(self._generic_snippets) 

65 if snippet_order not in self._snippets_by_order: 

66 return False 

67 return bool(self._snippets_by_order[snippet_order]) 

68 

69 def all_snippets(self) -> Iterable[MaintscriptSnippet]: 

70 yield from self._generic_snippets 

71 for snippets in self._snippets_by_order.values(): 

72 yield from snippets 

73 

74 def generate_snippet( 

75 self, 

76 tool_with_version: Optional[str] = None, 

77 snippet_order: Optional[Literal["service"]] = None, 

78 reverse: bool = False, 

79 ) -> Optional[str]: 

80 inner_content = "" 

81 if snippet_order is None: 81 ↛ 86line 81 didn't jump to line 86, because the condition on line 81 was never false

82 snippets = ( 

83 reversed(self._generic_snippets) if reverse else self._generic_snippets 

84 ) 

85 inner_content = "".join(s.script_content() for s in snippets) 

86 elif snippet_order in self._snippets_by_order: 

87 snippets = self._snippets_by_order[snippet_order] 

88 if reverse: 

89 snippets = reversed(snippets) 

90 inner_content = "".join(s.script_content() for s in snippets) 

91 

92 if not inner_content: 92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true

93 return None 

94 

95 if tool_with_version: 95 ↛ 96line 95 didn't jump to line 96, because the condition on line 95 was never true

96 return ( 

97 f"# Automatically added by {tool_with_version}\n" 

98 + inner_content 

99 + "# End automatically added section" 

100 ) 

101 return inner_content 

102 

103 

104class DpkgMaintscriptHelperCommand(DebputyDispatchableType): 

105 __slots__ = ("cmdline", "definition_source") 

106 

107 def __init__(self, cmdline: Sequence[str], definition_source: str): 

108 self.cmdline = cmdline 

109 self.definition_source = definition_source 

110 

111 @classmethod 

112 def _finish_cmd( 

113 cls, 

114 definition_source: str, 

115 cmdline: List[str], 

116 prior_version: Optional[str], 

117 owning_package: Optional[str], 

118 ) -> Self: 

119 if prior_version is not None: 

120 cmdline.append(prior_version) 

121 if owning_package is not None: 

122 if prior_version is None: 122 ↛ 124line 122 didn't jump to line 124, because the condition on line 122 was never true

123 # Empty is allowed according to `man dpkg-maintscript-helper` 

124 cmdline.append("") 

125 cmdline.append(owning_package) 

126 return cls( 

127 tuple(cmdline), 

128 definition_source, 

129 ) 

130 

131 @classmethod 

132 def rm_conffile( 

133 cls, 

134 definition_source: AttributePath, 

135 conffile: str, 

136 prior_version: Optional[str] = None, 

137 owning_package: Optional[str] = None, 

138 ) -> Self: 

139 cmdline = ["rm_conffile", conffile] 

140 return cls._finish_cmd( 

141 definition_source.path, cmdline, prior_version, owning_package 

142 ) 

143 

144 @classmethod 

145 def mv_conffile( 

146 cls, 

147 definition_source: AttributePath, 

148 old_conffile: str, 

149 new_confile: str, 

150 prior_version: Optional[str] = None, 

151 owning_package: Optional[str] = None, 

152 ) -> Self: 

153 cmdline = ["mv_conffile", old_conffile, new_confile] 

154 return cls._finish_cmd( 

155 definition_source.path, cmdline, prior_version, owning_package 

156 ) 

157 

158 @classmethod 

159 def symlink_to_dir( 

160 cls, 

161 definition_source: AttributePath, 

162 pathname: str, 

163 old_target: str, 

164 prior_version: Optional[str] = None, 

165 owning_package: Optional[str] = None, 

166 ) -> Self: 

167 cmdline = ["symlink_to_dir", pathname, old_target] 

168 return cls._finish_cmd( 

169 definition_source.path, cmdline, prior_version, owning_package 

170 ) 

171 

172 @classmethod 

173 def dir_to_symlink( 

174 cls, 

175 definition_source: AttributePath, 

176 pathname: str, 

177 new_target: str, 

178 prior_version: Optional[str] = None, 

179 owning_package: Optional[str] = None, 

180 ) -> Self: 

181 cmdline = ["dir_to_symlink", pathname, new_target] 

182 return cls._finish_cmd( 

183 definition_source.path, cmdline, prior_version, owning_package 

184 )