Merge ~silverdrake11/landscape-charm:update_apt_lib into landscape-charm:main
- Git
- lp:~silverdrake11/landscape-charm
- update_apt_lib
- Merge into main
Proposed by
Kevin Nasto
Status: | Merged |
---|---|
Merged at revision: | 14c34233b846d584ba47b2ce3ba75d6d6722c4e7 |
Proposed branch: | ~silverdrake11/landscape-charm:update_apt_lib |
Merge into: | landscape-charm:main |
Diff against target: |
340 lines (+90/-59) 1 file modified
lib/charms/operator_libs_linux/v0/apt.py (+90/-59) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mitch Burton | Approve | ||
Review via email: mp+442181@code.launchpad.net |
Commit message
Applied latest update to apt library, which fixes the noninteractive mode issue some were experiencing
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/charms/operator_libs_linux/v0/apt.py b/lib/charms/operator_libs_linux/v0/apt.py | |||
2 | index 2f921f0..7afb183 100644 | |||
3 | --- a/lib/charms/operator_libs_linux/v0/apt.py | |||
4 | +++ b/lib/charms/operator_libs_linux/v0/apt.py | |||
5 | @@ -78,7 +78,6 @@ Keys are constructed as `{repo_type}-{}-{release}` in order to uniquely identify | |||
6 | 78 | Repositories can be added with explicit values through a Python constructor. | 78 | Repositories can be added with explicit values through a Python constructor. |
7 | 79 | 79 | ||
8 | 80 | Example: | 80 | Example: |
9 | 81 | |||
10 | 82 | ```python | 81 | ```python |
11 | 83 | repositories = apt.RepositoryMapping() | 82 | repositories = apt.RepositoryMapping() |
12 | 84 | 83 | ||
13 | @@ -91,7 +90,6 @@ Alternatively, any valid `sources.list` line may be used to construct a new | |||
14 | 91 | `DebianRepository`. | 90 | `DebianRepository`. |
15 | 92 | 91 | ||
16 | 93 | Example: | 92 | Example: |
17 | 94 | |||
18 | 95 | ```python | 93 | ```python |
19 | 96 | repositories = apt.RepositoryMapping() | 94 | repositories = apt.RepositoryMapping() |
20 | 97 | 95 | ||
21 | @@ -124,7 +122,7 @@ LIBAPI = 0 | |||
22 | 124 | 122 | ||
23 | 125 | # Increment this PATCH version before using `charmcraft publish-lib` or reset | 123 | # Increment this PATCH version before using `charmcraft publish-lib` or reset |
24 | 126 | # to 0 if you are raising the major API version | 124 | # to 0 if you are raising the major API version |
26 | 127 | LIBPATCH = 8 | 125 | LIBPATCH = 11 |
27 | 128 | 126 | ||
28 | 129 | 127 | ||
29 | 130 | VALID_SOURCE_TYPES = ("deb", "deb-src") | 128 | VALID_SOURCE_TYPES = ("deb", "deb-src") |
30 | @@ -135,7 +133,7 @@ class Error(Exception): | |||
31 | 135 | """Base class of most errors raised by this library.""" | 133 | """Base class of most errors raised by this library.""" |
32 | 136 | 134 | ||
33 | 137 | def __repr__(self): | 135 | def __repr__(self): |
35 | 138 | """String representation of Error.""" | 136 | """Represent the Error.""" |
36 | 139 | return "<{}.{} {}>".format(type(self).__module__, type(self).__name__, self.args) | 137 | return "<{}.{} {}>".format(type(self).__module__, type(self).__name__, self.args) |
37 | 140 | 138 | ||
38 | 141 | @property | 139 | @property |
39 | @@ -212,15 +210,15 @@ class DebianPackage: | |||
40 | 212 | ) == (other._name, other._version.number) | 210 | ) == (other._name, other._version.number) |
41 | 213 | 211 | ||
42 | 214 | def __hash__(self): | 212 | def __hash__(self): |
44 | 215 | """A basic hash so this class can be used in Mappings and dicts.""" | 213 | """Return a hash of this package.""" |
45 | 216 | return hash((self._name, self._version.number)) | 214 | return hash((self._name, self._version.number)) |
46 | 217 | 215 | ||
47 | 218 | def __repr__(self): | 216 | def __repr__(self): |
49 | 219 | """A representation of the package.""" | 217 | """Represent the package.""" |
50 | 220 | return "<{}.{}: {}>".format(self.__module__, self.__class__.__name__, self.__dict__) | 218 | return "<{}.{}: {}>".format(self.__module__, self.__class__.__name__, self.__dict__) |
51 | 221 | 219 | ||
52 | 222 | def __str__(self): | 220 | def __str__(self): |
54 | 223 | """A human-readable representation of the package.""" | 221 | """Return a human-readable representation of the package.""" |
55 | 224 | return "<{}: {}-{}.{} -- {}>".format( | 222 | return "<{}: {}-{}.{} -- {}>".format( |
56 | 225 | self.__class__.__name__, | 223 | self.__class__.__name__, |
57 | 226 | self._name, | 224 | self._name, |
58 | @@ -250,7 +248,8 @@ class DebianPackage: | |||
59 | 250 | package_names = [package_names] | 248 | package_names = [package_names] |
60 | 251 | _cmd = ["apt-get", "-y", *optargs, command, *package_names] | 249 | _cmd = ["apt-get", "-y", *optargs, command, *package_names] |
61 | 252 | try: | 250 | try: |
63 | 253 | env = {"DEBIAN_FRONTEND": "noninteractive"} | 251 | env = os.environ.copy() |
64 | 252 | env["DEBIAN_FRONTEND"] = "noninteractive" | ||
65 | 254 | check_call(_cmd, env=env, stderr=PIPE, stdout=PIPE) | 253 | check_call(_cmd, env=env, stderr=PIPE, stdout=PIPE) |
66 | 255 | except CalledProcessError as e: | 254 | except CalledProcessError as e: |
67 | 256 | raise PackageError( | 255 | raise PackageError( |
68 | @@ -266,7 +265,7 @@ class DebianPackage: | |||
69 | 266 | ) | 265 | ) |
70 | 267 | 266 | ||
71 | 268 | def _remove(self) -> None: | 267 | def _remove(self) -> None: |
73 | 269 | """Removes a package from the system. Implementation-specific.""" | 268 | """Remove a package from the system. Implementation-specific.""" |
74 | 270 | return self._apt("remove", "{}={}".format(self.name, self.version)) | 269 | return self._apt("remove", "{}={}".format(self.name, self.version)) |
75 | 271 | 270 | ||
76 | 272 | @property | 271 | @property |
77 | @@ -275,7 +274,7 @@ class DebianPackage: | |||
78 | 275 | return self._name | 274 | return self._name |
79 | 276 | 275 | ||
80 | 277 | def ensure(self, state: PackageState): | 276 | def ensure(self, state: PackageState): |
82 | 278 | """Ensures that a package is in a given state. | 277 | """Ensure that a package is in a given state. |
83 | 279 | 278 | ||
84 | 280 | Args: | 279 | Args: |
85 | 281 | state: a `PackageState` to reconcile the package to | 280 | state: a `PackageState` to reconcile the package to |
86 | @@ -307,7 +306,7 @@ class DebianPackage: | |||
87 | 307 | 306 | ||
88 | 308 | @state.setter | 307 | @state.setter |
89 | 309 | def state(self, state: PackageState) -> None: | 308 | def state(self, state: PackageState) -> None: |
91 | 310 | """Sets the package state to a given value. | 309 | """Set the package state to a given value. |
92 | 311 | 310 | ||
93 | 312 | Args: | 311 | Args: |
94 | 313 | state: a `PackageState` to reconcile the package to | 312 | state: a `PackageState` to reconcile the package to |
95 | @@ -356,7 +355,7 @@ class DebianPackage: | |||
96 | 356 | 355 | ||
97 | 357 | Args: | 356 | Args: |
98 | 358 | package: a string representing the package | 357 | package: a string representing the package |
100 | 359 | version: an optional string if a specific version isr equested | 358 | version: an optional string if a specific version is requested |
101 | 360 | arch: an optional architecture, defaulting to `dpkg --print-architecture`. If an | 359 | arch: an optional architecture, defaulting to `dpkg --print-architecture`. If an |
102 | 361 | architecture is not specified, this will be used for selection. | 360 | architecture is not specified, this will be used for selection. |
103 | 362 | 361 | ||
104 | @@ -389,7 +388,7 @@ class DebianPackage: | |||
105 | 389 | 388 | ||
106 | 390 | Args: | 389 | Args: |
107 | 391 | package: a string representing the package | 390 | package: a string representing the package |
109 | 392 | version: an optional string if a specific version isr equested | 391 | version: an optional string if a specific version is requested |
110 | 393 | arch: an optional architecture, defaulting to `dpkg --print-architecture`. | 392 | arch: an optional architecture, defaulting to `dpkg --print-architecture`. |
111 | 394 | If an architecture is not specified, this will be used for selection. | 393 | If an architecture is not specified, this will be used for selection. |
112 | 395 | """ | 394 | """ |
113 | @@ -459,7 +458,7 @@ class DebianPackage: | |||
114 | 459 | 458 | ||
115 | 460 | Args: | 459 | Args: |
116 | 461 | package: a string representing the package | 460 | package: a string representing the package |
118 | 462 | version: an optional string if a specific version isr equested | 461 | version: an optional string if a specific version is requested |
119 | 463 | arch: an optional architecture, defaulting to `dpkg --print-architecture`. | 462 | arch: an optional architecture, defaulting to `dpkg --print-architecture`. |
120 | 464 | If an architecture is not specified, this will be used for selection. | 463 | If an architecture is not specified, this will be used for selection. |
121 | 465 | """ | 464 | """ |
122 | @@ -515,7 +514,7 @@ class Version: | |||
123 | 515 | """An abstraction around package versions. | 514 | """An abstraction around package versions. |
124 | 516 | 515 | ||
125 | 517 | This seems like it should be strictly unnecessary, except that `apt_pkg` is not usable inside a | 516 | This seems like it should be strictly unnecessary, except that `apt_pkg` is not usable inside a |
127 | 518 | venv, and wedging version comparisions into `DebianPackage` would overcomplicate it. | 517 | venv, and wedging version comparisons into `DebianPackage` would overcomplicate it. |
128 | 519 | 518 | ||
129 | 520 | This class implements the algorithm found here: | 519 | This class implements the algorithm found here: |
130 | 521 | https://www.debian.org/doc/debian-policy/ch-controlfields.html#version | 520 | https://www.debian.org/doc/debian-policy/ch-controlfields.html#version |
131 | @@ -526,11 +525,11 @@ class Version: | |||
132 | 526 | self._epoch = epoch or "" | 525 | self._epoch = epoch or "" |
133 | 527 | 526 | ||
134 | 528 | def __repr__(self): | 527 | def __repr__(self): |
136 | 529 | """A representation of the package.""" | 528 | """Represent the package.""" |
137 | 530 | return "<{}.{}: {}>".format(self.__module__, self.__class__.__name__, self.__dict__) | 529 | return "<{}.{}: {}>".format(self.__module__, self.__class__.__name__, self.__dict__) |
138 | 531 | 530 | ||
139 | 532 | def __str__(self): | 531 | def __str__(self): |
141 | 533 | """A human-readable representation of the package.""" | 532 | """Return human-readable representation of the package.""" |
142 | 534 | return "{}{}".format("{}:".format(self._epoch) if self._epoch else "", self._version) | 533 | return "{}{}".format("{}:".format(self._epoch) if self._epoch else "", self._version) |
143 | 535 | 534 | ||
144 | 536 | @property | 535 | @property |
145 | @@ -731,13 +730,16 @@ def add_package( | |||
146 | 731 | """Add a package or list of packages to the system. | 730 | """Add a package or list of packages to the system. |
147 | 732 | 731 | ||
148 | 733 | Args: | 732 | Args: |
149 | 733 | package_names: single package name, or list of package names | ||
150 | 734 | name: the name(s) of the package(s) | 734 | name: the name(s) of the package(s) |
151 | 735 | version: an (Optional) version as a string. Defaults to the latest known | 735 | version: an (Optional) version as a string. Defaults to the latest known |
152 | 736 | arch: an optional architecture for the package | 736 | arch: an optional architecture for the package |
153 | 737 | update_cache: whether or not to run `apt-get update` prior to operating | 737 | update_cache: whether or not to run `apt-get update` prior to operating |
154 | 738 | 738 | ||
155 | 739 | Raises: | 739 | Raises: |
156 | 740 | TypeError if no package name is given, or explicit version is set for multiple packages | ||
157 | 740 | PackageNotFoundError if the package is not in the cache. | 741 | PackageNotFoundError if the package is not in the cache. |
158 | 742 | PackageError if packages fail to install | ||
159 | 741 | """ | 743 | """ |
160 | 742 | cache_refreshed = False | 744 | cache_refreshed = False |
161 | 743 | if update_cache: | 745 | if update_cache: |
162 | @@ -785,7 +787,7 @@ def _add( | |||
163 | 785 | version: Optional[str] = "", | 787 | version: Optional[str] = "", |
164 | 786 | arch: Optional[str] = "", | 788 | arch: Optional[str] = "", |
165 | 787 | ) -> Tuple[Union[DebianPackage, str], bool]: | 789 | ) -> Tuple[Union[DebianPackage, str], bool]: |
167 | 788 | """Adds a package. | 790 | """Add a package to the system. |
168 | 789 | 791 | ||
169 | 790 | Args: | 792 | Args: |
170 | 791 | name: the name(s) of the package(s) | 793 | name: the name(s) of the package(s) |
171 | @@ -806,7 +808,7 @@ def _add( | |||
172 | 806 | def remove_package( | 808 | def remove_package( |
173 | 807 | package_names: Union[str, List[str]] | 809 | package_names: Union[str, List[str]] |
174 | 808 | ) -> Union[DebianPackage, List[DebianPackage]]: | 810 | ) -> Union[DebianPackage, List[DebianPackage]]: |
176 | 809 | """Removes a package from the system. | 811 | """Remove package(s) from the system. |
177 | 810 | 812 | ||
178 | 811 | Args: | 813 | Args: |
179 | 812 | package_names: the name of a package | 814 | package_names: the name of a package |
180 | @@ -834,10 +836,72 @@ def remove_package( | |||
181 | 834 | 836 | ||
182 | 835 | 837 | ||
183 | 836 | def update() -> None: | 838 | def update() -> None: |
185 | 837 | """Updates the apt cache via `apt-get update`.""" | 839 | """Update the apt cache via `apt-get update`.""" |
186 | 838 | check_call(["apt-get", "update"], stderr=PIPE, stdout=PIPE) | 840 | check_call(["apt-get", "update"], stderr=PIPE, stdout=PIPE) |
187 | 839 | 841 | ||
188 | 840 | 842 | ||
189 | 843 | def import_key(key: str) -> str: | ||
190 | 844 | """Import an ASCII Armor key. | ||
191 | 845 | |||
192 | 846 | A Radix64 format keyid is also supported for backwards | ||
193 | 847 | compatibility. In this case Ubuntu keyserver will be | ||
194 | 848 | queried for a key via HTTPS by its keyid. This method | ||
195 | 849 | is less preferable because https proxy servers may | ||
196 | 850 | require traffic decryption which is equivalent to a | ||
197 | 851 | man-in-the-middle attack (a proxy server impersonates | ||
198 | 852 | keyserver TLS certificates and has to be explicitly | ||
199 | 853 | trusted by the system). | ||
200 | 854 | |||
201 | 855 | Args: | ||
202 | 856 | key: A GPG key in ASCII armor format, including BEGIN | ||
203 | 857 | and END markers or a keyid. | ||
204 | 858 | |||
205 | 859 | Returns: | ||
206 | 860 | The GPG key filename written. | ||
207 | 861 | |||
208 | 862 | Raises: | ||
209 | 863 | GPGKeyError if the key could not be imported | ||
210 | 864 | """ | ||
211 | 865 | key = key.strip() | ||
212 | 866 | if "-" in key or "\n" in key: | ||
213 | 867 | # Send everything not obviously a keyid to GPG to import, as | ||
214 | 868 | # we trust its validation better than our own. eg. handling | ||
215 | 869 | # comments before the key. | ||
216 | 870 | logger.debug("PGP key found (looks like ASCII Armor format)") | ||
217 | 871 | if ( | ||
218 | 872 | "-----BEGIN PGP PUBLIC KEY BLOCK-----" in key | ||
219 | 873 | and "-----END PGP PUBLIC KEY BLOCK-----" in key | ||
220 | 874 | ): | ||
221 | 875 | logger.debug("Writing provided PGP key in the binary format") | ||
222 | 876 | key_bytes = key.encode("utf-8") | ||
223 | 877 | key_name = DebianRepository._get_keyid_by_gpg_key(key_bytes) | ||
224 | 878 | key_gpg = DebianRepository._dearmor_gpg_key(key_bytes) | ||
225 | 879 | gpg_key_filename = "/etc/apt/trusted.gpg.d/{}.gpg".format(key_name) | ||
226 | 880 | DebianRepository._write_apt_gpg_keyfile( | ||
227 | 881 | key_name=gpg_key_filename, key_material=key_gpg | ||
228 | 882 | ) | ||
229 | 883 | return gpg_key_filename | ||
230 | 884 | else: | ||
231 | 885 | raise GPGKeyError("ASCII armor markers missing from GPG key") | ||
232 | 886 | else: | ||
233 | 887 | logger.warning( | ||
234 | 888 | "PGP key found (looks like Radix64 format). " | ||
235 | 889 | "SECURELY importing PGP key from keyserver; " | ||
236 | 890 | "full key not provided." | ||
237 | 891 | ) | ||
238 | 892 | # as of bionic add-apt-repository uses curl with an HTTPS keyserver URL | ||
239 | 893 | # to retrieve GPG keys. `apt-key adv` command is deprecated as is | ||
240 | 894 | # apt-key in general as noted in its manpage. See lp:1433761 for more | ||
241 | 895 | # history. Instead, /etc/apt/trusted.gpg.d is used directly to drop | ||
242 | 896 | # gpg | ||
243 | 897 | key_asc = DebianRepository._get_key_by_keyid(key) | ||
244 | 898 | # write the key in GPG format so that apt-key list shows it | ||
245 | 899 | key_gpg = DebianRepository._dearmor_gpg_key(key_asc.encode("utf-8")) | ||
246 | 900 | gpg_key_filename = "/etc/apt/trusted.gpg.d/{}.gpg".format(key) | ||
247 | 901 | DebianRepository._write_apt_gpg_keyfile(key_name=gpg_key_filename, key_material=key_gpg) | ||
248 | 902 | return gpg_key_filename | ||
249 | 903 | |||
250 | 904 | |||
251 | 841 | class InvalidSourceError(Error): | 905 | class InvalidSourceError(Error): |
252 | 842 | """Exceptions for invalid source entries.""" | 906 | """Exceptions for invalid source entries.""" |
253 | 843 | 907 | ||
254 | @@ -901,7 +965,7 @@ class DebianRepository: | |||
255 | 901 | 965 | ||
256 | 902 | @filename.setter | 966 | @filename.setter |
257 | 903 | def filename(self, fname: str) -> None: | 967 | def filename(self, fname: str) -> None: |
259 | 904 | """Sets the filename used when a repo is written back to diskself. | 968 | """Set the filename used when a repo is written back to disk. |
260 | 905 | 969 | ||
261 | 906 | Args: | 970 | Args: |
262 | 907 | fname: a filename to write the repository information to. | 971 | fname: a filename to write the repository information to. |
263 | @@ -1004,7 +1068,7 @@ class DebianRepository: | |||
264 | 1004 | A Radix64 format keyid is also supported for backwards | 1068 | A Radix64 format keyid is also supported for backwards |
265 | 1005 | compatibility. In this case Ubuntu keyserver will be | 1069 | compatibility. In this case Ubuntu keyserver will be |
266 | 1006 | queried for a key via HTTPS by its keyid. This method | 1070 | queried for a key via HTTPS by its keyid. This method |
268 | 1007 | is less preferrable because https proxy servers may | 1071 | is less preferable because https proxy servers may |
269 | 1008 | require traffic decryption which is equivalent to a | 1072 | require traffic decryption which is equivalent to a |
270 | 1009 | man-in-the-middle attack (a proxy server impersonates | 1073 | man-in-the-middle attack (a proxy server impersonates |
271 | 1010 | keyserver TLS certificates and has to be explicitly | 1074 | keyserver TLS certificates and has to be explicitly |
272 | @@ -1017,40 +1081,7 @@ class DebianRepository: | |||
273 | 1017 | Raises: | 1081 | Raises: |
274 | 1018 | GPGKeyError if the key could not be imported | 1082 | GPGKeyError if the key could not be imported |
275 | 1019 | """ | 1083 | """ |
310 | 1020 | key = key.strip() | 1084 | self._gpg_key_filename = import_key(key) |
277 | 1021 | if "-" in key or "\n" in key: | ||
278 | 1022 | # Send everything not obviously a keyid to GPG to import, as | ||
279 | 1023 | # we trust its validation better than our own. eg. handling | ||
280 | 1024 | # comments before the key. | ||
281 | 1025 | logger.debug("PGP key found (looks like ASCII Armor format)") | ||
282 | 1026 | if ( | ||
283 | 1027 | "-----BEGIN PGP PUBLIC KEY BLOCK-----" in key | ||
284 | 1028 | and "-----END PGP PUBLIC KEY BLOCK-----" in key | ||
285 | 1029 | ): | ||
286 | 1030 | logger.debug("Writing provided PGP key in the binary format") | ||
287 | 1031 | key_bytes = key.encode("utf-8") | ||
288 | 1032 | key_name = self._get_keyid_by_gpg_key(key_bytes) | ||
289 | 1033 | key_gpg = self._dearmor_gpg_key(key_bytes) | ||
290 | 1034 | self._gpg_key_filename = "/etc/apt/trusted.gpg.d/{}.gpg".format(key_name) | ||
291 | 1035 | self._write_apt_gpg_keyfile(key_name=self._gpg_key_filename, key_material=key_gpg) | ||
292 | 1036 | else: | ||
293 | 1037 | raise GPGKeyError("ASCII armor markers missing from GPG key") | ||
294 | 1038 | else: | ||
295 | 1039 | logger.warning( | ||
296 | 1040 | "PGP key found (looks like Radix64 format). " | ||
297 | 1041 | "SECURELY importing PGP key from keyserver; " | ||
298 | 1042 | "full key not provided." | ||
299 | 1043 | ) | ||
300 | 1044 | # as of bionic add-apt-repository uses curl with an HTTPS keyserver URL | ||
301 | 1045 | # to retrieve GPG keys. `apt-key adv` command is deprecated as is | ||
302 | 1046 | # apt-key in general as noted in its manpage. See lp:1433761 for more | ||
303 | 1047 | # history. Instead, /etc/apt/trusted.gpg.d is used directly to drop | ||
304 | 1048 | # gpg | ||
305 | 1049 | key_asc = self._get_key_by_keyid(key) | ||
306 | 1050 | # write the key in GPG format so that apt-key list shows it | ||
307 | 1051 | key_gpg = self._dearmor_gpg_key(key_asc.encode("utf-8")) | ||
308 | 1052 | self._gpg_key_filename = "/etc/apt/trusted.gpg.d/{}.gpg".format(key) | ||
309 | 1053 | self._write_apt_gpg_keyfile(key_name=key, key_material=key_gpg) | ||
311 | 1054 | 1085 | ||
312 | 1055 | @staticmethod | 1086 | @staticmethod |
313 | 1056 | def _get_keyid_by_gpg_key(key_material: bytes) -> str: | 1087 | def _get_keyid_by_gpg_key(key_material: bytes) -> str: |
314 | @@ -1116,7 +1147,7 @@ class DebianRepository: | |||
315 | 1116 | 1147 | ||
316 | 1117 | @staticmethod | 1148 | @staticmethod |
317 | 1118 | def _dearmor_gpg_key(key_asc: bytes) -> bytes: | 1149 | def _dearmor_gpg_key(key_asc: bytes) -> bytes: |
319 | 1119 | """Converts a GPG key in the ASCII armor format to the binary format. | 1150 | """Convert a GPG key in the ASCII armor format to the binary format. |
320 | 1120 | 1151 | ||
321 | 1121 | Args: | 1152 | Args: |
322 | 1122 | key_asc: A GPG key in ASCII armor format. | 1153 | key_asc: A GPG key in ASCII armor format. |
323 | @@ -1140,7 +1171,7 @@ class DebianRepository: | |||
324 | 1140 | 1171 | ||
325 | 1141 | @staticmethod | 1172 | @staticmethod |
326 | 1142 | def _write_apt_gpg_keyfile(key_name: str, key_material: bytes) -> None: | 1173 | def _write_apt_gpg_keyfile(key_name: str, key_material: bytes) -> None: |
328 | 1143 | """Writes GPG key material into a file at a provided path. | 1174 | """Write GPG key material into a file at a provided path. |
329 | 1144 | 1175 | ||
330 | 1145 | Args: | 1176 | Args: |
331 | 1146 | key_name: A key name to use for a key file (could be a fingerprint) | 1177 | key_name: A key name to use for a key file (could be a fingerprint) |
332 | @@ -1188,7 +1219,7 @@ class RepositoryMapping(Mapping): | |||
333 | 1188 | return len(self._repository_map) | 1219 | return len(self._repository_map) |
334 | 1189 | 1220 | ||
335 | 1190 | def __iter__(self) -> Iterable[DebianRepository]: | 1221 | def __iter__(self) -> Iterable[DebianRepository]: |
337 | 1191 | """Iterator magic method for RepositoryMapping.""" | 1222 | """Return iterator for RepositoryMapping.""" |
338 | 1192 | return iter(self._repository_map.values()) | 1223 | return iter(self._repository_map.values()) |
339 | 1193 | 1224 | ||
340 | 1194 | def __getitem__(self, repository_uri: str) -> DebianRepository: | 1225 | def __getitem__(self, repository_uri: str) -> DebianRepository: |
+1 LGTM