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
« 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
4from debputy.manifest_parser.base_types import DebputyDispatchableType
5from debputy.manifest_parser.util import AttributePath
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"}
25@dataclasses.dataclass(slots=True, frozen=True)
26class MaintscriptSnippet:
27 definition_source: str
28 snippet: str
29 snippet_order: Optional[Literal["service"]] = None
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)
41class MaintscriptSnippetContainer:
42 def __init__(self) -> None:
43 self._generic_snippets: List[MaintscriptSnippet] = []
44 self._snippets_by_order: Dict[Literal["service"], List[MaintscriptSnippet]] = {}
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
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 )
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])
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
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)
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
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
104class DpkgMaintscriptHelperCommand(DebputyDispatchableType):
105 __slots__ = ("cmdline", "definition_source")
107 def __init__(self, cmdline: Sequence[str], definition_source: str):
108 self.cmdline = cmdline
109 self.definition_source = definition_source
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 )
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 )
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 )
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 )
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 )