Gentoo Archives: gentoo-commits

From: Sam James <sam@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/, lib/_emerge/
Date: Sun, 27 Mar 2022 23:07:18
Message-Id: 1648422395.7c8875c38b187fbd2e0fa5fc39bbb38c1588d0fb.sam@gentoo
1 commit: 7c8875c38b187fbd2e0fa5fc39bbb38c1588d0fb
2 Author: Kenneth Raplee <kenrap <AT> kennethraplee <DOT> com>
3 AuthorDate: Tue Mar 22 07:01:05 2022 +0000
4 Commit: Sam James <sam <AT> gentoo <DOT> org>
5 CommitDate: Sun Mar 27 23:06:35 2022 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=7c8875c3
7
8 Begin modernizing the string usage
9
10 These files are updated to use the newer f-string feature (String
11 Interpolation) that has been added since Python 3.6.
12
13 The benefits to using String Interpolation are:
14 * Avoids incurring bugs from using Old Style formatting.
15 * Makes it easier to understand how the string is being formatted.
16 * Using embedded expressions into strings removes the need for string
17 concatenation and will execute much faster for the same effect.
18
19 Also, a few global string constants with Old Style formatting had their
20 literals inlined to their respective function arguments while
21 transitioning them be f-strings. When Python searches for variables, it
22 looks them up in the local namespace first, which is the namespace
23 specific a function or class method. If not found, then it searches in
24 the global namespace. Removing the need for global strings will also
25 improve performance a bit along the way.
26
27 The Portage codebase as a whole still needs this kind of work.
28
29 Signed-off-by: Kenneth Raplee <kenrap <AT> kennethraplee.com>
30 Signed-off-by: Sam James <sam <AT> gentoo.org>
31
32 lib/_emerge/main.py | 38 +++++------
33 lib/portage/__init__.py | 35 +++++-----
34 lib/portage/_global_updates.py | 4 +-
35 lib/portage/checksum.py | 15 ++--
36 lib/portage/const.py | 42 ++++++------
37 lib/portage/data.py | 2 +-
38 lib/portage/dispatch_conf.py | 81 ++++++++++------------
39 lib/portage/eclass_cache.py | 4 +-
40 lib/portage/exception.py | 6 +-
41 lib/portage/getbinpkg.py | 89 +++++++++++-------------
42 lib/portage/glsa.py | 151 +++++++++++++++++++----------------------
43 lib/portage/gpg.py | 4 +-
44 lib/portage/gpkg.py | 18 +++--
45 lib/portage/localization.py | 8 +--
46 lib/portage/mail.py | 12 ++--
47 15 files changed, 230 insertions(+), 279 deletions(-)
48
49 diff --git a/lib/_emerge/main.py b/lib/_emerge/main.py
50 index 8928f268d..f3ff02404 100644
51 --- a/lib/_emerge/main.py
52 +++ b/lib/_emerge/main.py
53 @@ -81,7 +81,7 @@ shortmapping = {
54
55 COWSAY_MOO = r"""
56
57 - Larry loves Gentoo (%s)
58 + Larry loves Gentoo ({})
59
60 _______________________
61 < Have you mooed today? >
62 @@ -97,7 +97,7 @@ COWSAY_MOO = r"""
63
64 def multiple_actions(action1, action2):
65 sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n")
66 - sys.stderr.write("!!! '%s' or '%s'\n\n" % (action1, action2))
67 + sys.stderr.write(f"!!! '{action1}' or '{action2}'\n\n")
68 sys.exit(1)
69
70
71 @@ -745,7 +745,7 @@ def parse_opts(tmpcmdline, silent=False):
72
73 for action_opt in actions:
74 parser.add_argument(
75 - "--" + action_opt,
76 + f"--{action_opt}",
77 action="store_true",
78 dest=action_opt.replace("-", "_"),
79 default=False,
80 @@ -759,7 +759,7 @@ def parse_opts(tmpcmdline, silent=False):
81 )
82 for shortopt, longopt in shortmapping.items():
83 parser.add_argument(
84 - "-" + shortopt,
85 + f"-{shortopt}",
86 action="store_true",
87 dest=longopt.lstrip("--").replace("-", "_"),
88 default=False,
89 @@ -832,9 +832,9 @@ def parse_opts(tmpcmdline, silent=False):
90 if myoptions.buildpkg_exclude:
91 bad_atoms = _find_bad_atoms(myoptions.buildpkg_exclude, less_strict=True)
92 if bad_atoms and not silent:
93 + invalid_atoms = ",".join(bad_atoms)
94 parser.error(
95 - "Invalid Atom(s) in --buildpkg-exclude parameter: '%s'\n"
96 - % (",".join(bad_atoms),)
97 + f"Invalid Atom(s) in --buildpkg-exclude parameter: '{invalid_atoms}'\n"
98 )
99
100 if myoptions.changed_deps is not None:
101 @@ -1014,7 +1014,7 @@ def parse_opts(tmpcmdline, silent=False):
102 backtrack = None
103 if not silent:
104 parser.error(
105 - "Invalid --backtrack parameter: '%s'\n" % (myoptions.backtrack,)
106 + f"Invalid --backtrack parameter: '{myoptions.backtrack}'\n"
107 )
108
109 myoptions.backtrack = backtrack
110 @@ -1032,7 +1032,7 @@ def parse_opts(tmpcmdline, silent=False):
111 if deep is not True and deep < 0:
112 deep = None
113 if not silent:
114 - parser.error("Invalid --deep parameter: '%s'\n" % (myoptions.deep,))
115 + parser.error(f"Invalid --deep parameter: '{myoptions.deep}'\n")
116
117 myoptions.deep = deep
118
119 @@ -1049,7 +1049,7 @@ def parse_opts(tmpcmdline, silent=False):
120 if jobs is not True and jobs < 1:
121 jobs = None
122 if not silent:
123 - parser.error("Invalid --jobs parameter: '%s'\n" % (myoptions.jobs,))
124 + parser.error(f"Invalid --jobs parameter: '{myoptions.jobs}'\n")
125
126 myoptions.jobs = jobs
127
128 @@ -1066,8 +1066,7 @@ def parse_opts(tmpcmdline, silent=False):
129 load_average = None
130 if not silent:
131 parser.error(
132 - "Invalid --load-average parameter: '%s'\n"
133 - % (myoptions.load_average,)
134 + f"Invalid --load-average parameter: '{myoptions.load_average}'\n"
135 )
136
137 myoptions.load_average = load_average
138 @@ -1082,8 +1081,7 @@ def parse_opts(tmpcmdline, silent=False):
139 rebuilt_binaries_timestamp = 0
140 if not silent:
141 parser.error(
142 - "Invalid --rebuilt-binaries-timestamp parameter: '%s'\n"
143 - % (myoptions.rebuilt_binaries_timestamp,)
144 + f"Invalid --rebuilt-binaries-timestamp parameter: '{myoptions.rebuilt_binaries_timestamp}'\n"
145 )
146
147 myoptions.rebuilt_binaries_timestamp = rebuilt_binaries_timestamp
148 @@ -1093,14 +1091,12 @@ def parse_opts(tmpcmdline, silent=False):
149 search_similarity = float(myoptions.search_similarity)
150 except ValueError:
151 parser.error(
152 - "Invalid --search-similarity parameter "
153 - "(not a number): '{}'\n".format(myoptions.search_similarity)
154 + f"Invalid --search-similarity parameter (not a number): '{myoptions.search_similarity}'\n"
155 )
156
157 if search_similarity < 0 or search_similarity > 100:
158 parser.error(
159 - "Invalid --search-similarity parameter "
160 - "(not between 0 and 100): '{}'\n".format(myoptions.search_similarity)
161 + f"Invalid --search-similarity parameter (not between 0 and 100): '{myoptions.search_similarity}'\n"
162 )
163
164 myoptions.search_similarity = search_similarity
165 @@ -1181,7 +1177,7 @@ def profile_check(trees, myaction):
166 "--help, --info, --search, --sync, and --version."
167 )
168 writemsg_level(
169 - "".join("!!! %s\n" % l for l in textwrap.wrap(msg, 70)),
170 + "".join(f"!!! {l}\n" for l in textwrap.wrap(msg, 70)),
171 level=logging.ERROR,
172 noiselevel=-1,
173 )
174 @@ -1203,7 +1199,7 @@ def emerge_main(args=None):
175 try:
176 locale.setlocale(locale.LC_ALL, "")
177 except locale.Error as e:
178 - writemsg_level("setlocale: %s\n" % e, level=logging.WARN)
179 + writemsg_level(f"setlocale: {e}\n", level=logging.WARN)
180
181 # Disable color until we're sure that it should be enabled (after
182 # EMERGE_DEFAULT_OPTS has been parsed).
183 @@ -1234,7 +1230,7 @@ def emerge_main(args=None):
184 emerge_help()
185 return os.EX_OK
186 if myaction == "moo":
187 - print(COWSAY_MOO % platform.system())
188 + print(COWSAY_MOO.format(platform.system()))
189 return os.EX_OK
190 if myaction == "sync":
191 # need to set this to True now in order for the repository config
192 @@ -1311,7 +1307,7 @@ def emerge_main(args=None):
193 try:
194 locale.setlocale(locale.LC_ALL, "")
195 except locale.Error as e:
196 - writemsg_level("setlocale: %s\n" % e, level=logging.WARN)
197 + writemsg_level(f"setlocale: {e}\n", level=logging.WARN)
198
199 tmpcmdline = []
200 if "--ignore-default-opts" not in myopts:
201
202 diff --git a/lib/portage/__init__.py b/lib/portage/__init__.py
203 index 3042de1aa..82e47d0ff 100644
204 --- a/lib/portage/__init__.py
205 +++ b/lib/portage/__init__.py
206 @@ -48,7 +48,7 @@ except ImportError as e:
207 sys.stderr.write(
208 "!!! gone wrong. Here is the information we got for this exception:\n"
209 )
210 - sys.stderr.write(" " + str(e) + "\n\n")
211 + sys.stderr.write(f" {e}\n\n")
212 raise
213
214 try:
215 @@ -93,7 +93,7 @@ try:
216 + "doebuild_environment,spawn,spawnebuild",
217 "portage.package.ebuild.config:autouse,best_from_dict,"
218 + "check_config_instance,config",
219 - "portage.package.ebuild.deprecated_profile_check:" + "deprecated_profile_check",
220 + "portage.package.ebuild.deprecated_profile_check:deprecated_profile_check",
221 "portage.package.ebuild.digestcheck:digestcheck",
222 "portage.package.ebuild.digestgen:digestgen",
223 "portage.package.ebuild.fetch:fetch",
224 @@ -184,7 +184,7 @@ except ImportError as e:
225 "!!! There is a README.RESCUE file that details the steps required to perform\n"
226 )
227 sys.stderr.write("!!! a recovery of portage.\n")
228 - sys.stderr.write(" " + str(e) + "\n\n")
229 + sys.stderr.write(f" {e}\n\n")
230 raise
231
232
233 @@ -409,7 +409,7 @@ try:
234 _selinux_merge = _unicode_module_wrapper(_selinux, encoding=_encodings["merge"])
235 except (ImportError, OSError) as e:
236 if isinstance(e, OSError):
237 - sys.stderr.write("!!! SELinux not loaded: %s\n" % str(e))
238 + sys.stderr.write(f"!!! SELinux not loaded: {e}\n")
239 del e
240 _selinux = None
241 selinux = None
242 @@ -482,10 +482,10 @@ def _shell_quote(s):
243 """
244 if _shell_quote_re.search(s) is None:
245 return s
246 - for letter in '\\"$`':
247 + for letter in r"\"$`":
248 if letter in s:
249 - s = s.replace(letter, "\\" + letter)
250 - return '"%s"' % s
251 + s = s.replace(letter, rf"\{letter}")
252 + return f'"{s}"'
253
254
255 bsd_chflags = None
256 @@ -534,7 +534,7 @@ def abssymlink(symlink, target=None):
257 mylink = os.readlink(symlink)
258 if mylink[0] != "/":
259 mydir = os.path.dirname(symlink)
260 - mylink = mydir + "/" + mylink
261 + mylink = f"{mydir}/{mylink}"
262 return os.path.normpath(mylink)
263
264
265 @@ -597,7 +597,7 @@ def _parse_eapi_ebuild_head(f):
266 def _movefile(src, dest, **kwargs):
267 """Calls movefile and raises a PortageException if an error occurs."""
268 if movefile(src, dest, **kwargs) is None:
269 - raise portage.exception.PortageException("mv '%s' '%s'" % (src, dest))
270 + raise portage.exception.PortageException(f"mv '{src}' '{dest}'")
271
272
273 auxdbkeys = (
274 @@ -726,12 +726,11 @@ if VERSION == "HEAD":
275 BASH_BINARY,
276 "-c",
277 (
278 - "cd %s ; git describe --match 'portage-*' || exit $? ; "
279 - + 'if [ -n "`git diff-index --name-only --diff-filter=M HEAD`" ] ; '
280 - + "then echo modified ; git rev-list --format=%%ct -n 1 HEAD ; fi ; "
281 - + "exit 0"
282 - )
283 - % _shell_quote(PORTAGE_BASE_PATH),
284 + f"cd {_shell_quote(PORTAGE_BASE_PATH)} ; git describe --match 'portage-*' || exit $? ; "
285 + 'if [ -n "`git diff-index --name-only --diff-filter=M HEAD`" ] ; '
286 + "then echo modified ; git rev-list --format=%%ct -n 1 HEAD ; fi ; "
287 + "exit 0"
288 + ),
289 ]
290 cmd = [
291 _unicode_encode(x, encoding=encoding, errors="strict") for x in cmd
292 @@ -750,7 +749,7 @@ if VERSION == "HEAD":
293 patchlevel = False
294 if len(version_split) > 2:
295 patchlevel = True
296 - VERSION = "%s_p%s" % (VERSION, version_split[2])
297 + VERSION = f"{VERSION}_p{version_split[2]}"
298 if len(output_lines) > 1 and output_lines[1] == "modified":
299 head_timestamp = None
300 if len(output_lines) > 3:
301 @@ -765,8 +764,8 @@ if VERSION == "HEAD":
302 ):
303 timestamp = timestamp - head_timestamp
304 if not patchlevel:
305 - VERSION = "%s_p0" % (VERSION,)
306 - VERSION = "%s_p%d" % (VERSION, timestamp)
307 + VERSION = f"{VERSION}_p0"
308 + VERSION = f"{VERSION}_p{timestamp}"
309 return VERSION
310 VERSION = "HEAD"
311 return VERSION
312
313 diff --git a/lib/portage/_global_updates.py b/lib/portage/_global_updates.py
314 index 80728fb43..a17ee861d 100644
315 --- a/lib/portage/_global_updates.py
316 +++ b/lib/portage/_global_updates.py
317 @@ -118,14 +118,14 @@ def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True):
318 myupd.extend(valid_updates)
319 if not quiet:
320 writemsg_stdout(bold(mykey))
321 - writemsg_stdout(len(valid_updates) * "." + "\n")
322 + writemsg_stdout(f"{len(valid_updates) * '.'}\n")
323 if len(errors) == 0:
324 # Update our internal mtime since we
325 # processed all of our directives.
326 timestamps[mykey] = mystat[stat.ST_MTIME]
327 else:
328 for msg in errors:
329 - writemsg("%s\n" % msg, noiselevel=-1)
330 + writemsg(f"{msg}\n", noiselevel=-1)
331 if myupd:
332 retupd = True
333
334
335 diff --git a/lib/portage/checksum.py b/lib/portage/checksum.py
336 index c19a50df9..3b5a10d24 100644
337 --- a/lib/portage/checksum.py
338 +++ b/lib/portage/checksum.py
339 @@ -44,7 +44,7 @@ def _open_file(filename):
340 _unicode_encode(filename, encoding=_encodings["fs"], errors="strict"), "rb"
341 )
342 except IOError as e:
343 - func_call = "open('%s')" % _unicode_decode(filename)
344 + func_call = f"open('{_unicode_decode(filename)}')"
345 if e.errno == errno.EPERM:
346 raise portage.exception.OperationNotPermitted(func_call)
347 elif e.errno == errno.EACCES:
348 @@ -525,12 +525,11 @@ def verify_all(filename, mydict, calc_prelink=0, strict=0):
349 if mydict[x] != myhash:
350 if strict:
351 raise portage.exception.DigestException(
352 - ("Failed to verify '$(file)s' on " + "checksum type '%(type)s'")
353 - % {"file": filename, "type": x}
354 + f"Failed to verify '{filename}' on checksum type '{x}'"
355 )
356 else:
357 file_is_ok = False
358 - reason = (("Failed on %s verification" % x), myhash, mydict[x])
359 + reason = (f"Failed on {x} verification", myhash, mydict[x])
360 break
361
362 return file_is_ok, reason
363 @@ -578,8 +577,7 @@ def perform_checksum(filename, hashname="MD5", calc_prelink=0):
364 try:
365 if hashname not in hashfunc_keys:
366 raise portage.exception.DigestException(
367 - hashname
368 - + " hash function not available (needs dev-python/pycrypto)"
369 + f"{hashname} hash function not available (needs dev-python/pycrypto)"
370 )
371 myhash, mysize = hashfunc_map[hashname].checksum_file(myfilename)
372 except (OSError, IOError) as e:
373 @@ -618,8 +616,7 @@ def perform_multiple_checksums(filename, hashes=["MD5"], calc_prelink=0):
374 for x in hashes:
375 if x not in hashfunc_keys:
376 raise portage.exception.DigestException(
377 - x
378 - + " hash function not available (needs dev-python/pycrypto or >=dev-lang/python-2.5)"
379 + f"{x} hash function not available (needs dev-python/pycrypto)"
380 )
381 rVal[x] = perform_checksum(filename, x, calc_prelink)[0]
382 return rVal
383 @@ -638,6 +635,6 @@ def checksum_str(data, hashname="MD5"):
384 """
385 if hashname not in hashfunc_keys:
386 raise portage.exception.DigestException(
387 - hashname + " hash function not available (needs dev-python/pycrypto)"
388 + f"{hashname} hash function not available (needs dev-python/pycrypto)"
389 )
390 return hashfunc_map[hashname].checksum_str(data)
391
392 diff --git a/lib/portage/const.py b/lib/portage/const.py
393 index 9e6f9311d..98eb7e4f2 100644
394 --- a/lib/portage/const.py
395 +++ b/lib/portage/const.py
396 @@ -29,32 +29,32 @@ import sys
397
398 # variables used with config_root (these need to be relative)
399 USER_CONFIG_PATH = "etc/portage"
400 -BINREPOS_CONF_FILE = USER_CONFIG_PATH + "/binrepos.conf"
401 -MAKE_CONF_FILE = USER_CONFIG_PATH + "/make.conf"
402 -MODULES_FILE_PATH = USER_CONFIG_PATH + "/modules"
403 -CUSTOM_PROFILE_PATH = USER_CONFIG_PATH + "/profile"
404 -USER_VIRTUALS_FILE = USER_CONFIG_PATH + "/virtuals"
405 -EBUILD_SH_ENV_FILE = USER_CONFIG_PATH + "/bashrc"
406 -EBUILD_SH_ENV_DIR = USER_CONFIG_PATH + "/env"
407 -CUSTOM_MIRRORS_FILE = USER_CONFIG_PATH + "/mirrors"
408 -COLOR_MAP_FILE = USER_CONFIG_PATH + "/color.map"
409 -PROFILE_PATH = USER_CONFIG_PATH + "/make.profile"
410 -MAKE_DEFAULTS_FILE = PROFILE_PATH + "/make.defaults" # FIXME: not used
411 -DEPRECATED_PROFILE_FILE = PROFILE_PATH + "/deprecated"
412 +BINREPOS_CONF_FILE = f"{USER_CONFIG_PATH}/binrepos.conf"
413 +MAKE_CONF_FILE = f"{USER_CONFIG_PATH}/make.conf"
414 +MODULES_FILE_PATH = f"{USER_CONFIG_PATH}/modules"
415 +CUSTOM_PROFILE_PATH = f"{USER_CONFIG_PATH}/profile"
416 +USER_VIRTUALS_FILE = f"{USER_CONFIG_PATH}/virtuals"
417 +EBUILD_SH_ENV_FILE = f"{USER_CONFIG_PATH}/bashrc"
418 +EBUILD_SH_ENV_DIR = f"{USER_CONFIG_PATH}/env"
419 +CUSTOM_MIRRORS_FILE = f"{USER_CONFIG_PATH}/mirrors"
420 +COLOR_MAP_FILE = f"{USER_CONFIG_PATH}/color.map"
421 +PROFILE_PATH = f"{USER_CONFIG_PATH}/make.profile"
422 +MAKE_DEFAULTS_FILE = f"{PROFILE_PATH}/make.defaults" # FIXME: not used
423 +DEPRECATED_PROFILE_FILE = f"{PROFILE_PATH}/deprecated"
424
425 # variables used with targetroot (these need to be absolute, but not
426 # have a leading '/' since they are used directly with os.path.join on EROOT)
427 VDB_PATH = "var/db/pkg"
428 CACHE_PATH = "var/cache/edb"
429 PRIVATE_PATH = "var/lib/portage"
430 -WORLD_FILE = PRIVATE_PATH + "/world"
431 -WORLD_SETS_FILE = PRIVATE_PATH + "/world_sets"
432 -CONFIG_MEMORY_FILE = PRIVATE_PATH + "/config"
433 +WORLD_FILE = f"{PRIVATE_PATH}/world"
434 +WORLD_SETS_FILE = f"{PRIVATE_PATH}/world_sets"
435 +CONFIG_MEMORY_FILE = f"{PRIVATE_PATH}/config"
436 NEWS_LIB_PATH = "var/lib/gentoo"
437
438 # these variables get EPREFIX prepended automagically when they are
439 # translated into their lowercase variants
440 -DEPCACHE_PATH = "/var/cache/edb/dep"
441 +DEPCACHE_PATH = f"/{CACHE_PATH}/dep"
442 GLOBAL_CONFIG_PATH = "/usr/share/portage/config"
443
444 # these variables are not used with target_root or config_root
445 @@ -64,11 +64,11 @@ GLOBAL_CONFIG_PATH = "/usr/share/portage/config"
446 # fmt:off
447 PORTAGE_BASE_PATH = os.path.join(os.sep, os.sep.join(os.path.realpath(__file__.rstrip("co")).split(os.sep)[:-3]))
448 # fmt:on
449 -PORTAGE_BIN_PATH = PORTAGE_BASE_PATH + "/bin"
450 +PORTAGE_BIN_PATH = f"{PORTAGE_BASE_PATH}/bin"
451 PORTAGE_PYM_PATH = os.path.realpath(os.path.join(__file__, "../.."))
452 -LOCALE_DATA_PATH = PORTAGE_BASE_PATH + "/locale" # FIXME: not used
453 -EBUILD_SH_BINARY = PORTAGE_BIN_PATH + "/ebuild.sh"
454 -MISC_SH_BINARY = PORTAGE_BIN_PATH + "/misc-functions.sh"
455 +LOCALE_DATA_PATH = f"{PORTAGE_BASE_PATH}/locale" # FIXME: not used
456 +EBUILD_SH_BINARY = f"{PORTAGE_BIN_PATH}/ebuild.sh"
457 +MISC_SH_BINARY = f"{PORTAGE_BIN_PATH}/misc-functions.sh"
458 SANDBOX_BINARY = "/usr/bin/sandbox"
459 FAKEROOT_BINARY = "/usr/bin/fakeroot"
460 BASH_BINARY = "/bin/bash"
461 @@ -78,7 +78,7 @@ PRELINK_BINARY = "/usr/sbin/prelink"
462 INVALID_ENV_FILE = "/etc/spork/is/not/valid/profile.env"
463 MERGING_IDENTIFIER = "-MERGING-"
464 REPO_NAME_FILE = "repo_name"
465 -REPO_NAME_LOC = "profiles" + "/" + REPO_NAME_FILE
466 +REPO_NAME_LOC = f"profiles/{REPO_NAME_FILE}"
467
468 PORTAGE_PACKAGE_ATOM = "sys-apps/portage"
469 LIBC_PACKAGE_ATOM = "virtual/libc"
470
471 diff --git a/lib/portage/data.py b/lib/portage/data.py
472 index 09a4dd079..e1457c566 100644
473 --- a/lib/portage/data.py
474 +++ b/lib/portage/data.py
475 @@ -78,7 +78,7 @@ def _target_root():
476 # Handle either empty or unset ROOT.
477 root = os.sep
478 root = portage.util.normalize_path(root)
479 - return root.rstrip(os.sep) + os.sep
480 + return f"{root.rstrip(os.sep)}{os.sep}"
481
482
483 def portage_group_warning():
484
485 diff --git a/lib/portage/dispatch_conf.py b/lib/portage/dispatch_conf.py
486 index fa9780937..6c6036c4e 100644
487 --- a/lib/portage/dispatch_conf.py
488 +++ b/lib/portage/dispatch_conf.py
489 @@ -25,9 +25,6 @@ RCS_BRANCH = "1.1.1"
490 RCS_LOCK = "rcs -ko -M -l"
491 RCS_PUT = 'ci -t-"Archived config file." -m"dispatch-conf update."'
492 RCS_GET = "co"
493 -RCS_MERGE = "rcsmerge -p -r" + RCS_BRANCH + " '%s' > '%s'"
494 -
495 -DIFF3_MERGE = "diff3 -mE '%s' '%s' '%s' > '%s'"
496 _ARCHIVE_ROTATE_MAX = 9
497
498
499 @@ -75,18 +72,18 @@ def diff_mixed(func, file1, file2):
500
501 if tempdir is None:
502 tempdir = tempfile.mkdtemp()
503 - diff_files[i] = os.path.join(tempdir, "%d" % i)
504 + diff_files[i] = os.path.join(tempdir, f"{i}")
505 if st is None:
506 content = "/dev/null\n"
507 elif stat.S_ISLNK(st.st_mode):
508 link_dest = os.readlink(files[i])
509 - content = "SYM: %s -> %s\n" % (file1, link_dest)
510 + content = f"SYM: {file1} -> {link_dest}\n"
511 elif stat.S_ISDIR(st.st_mode):
512 - content = "DIR: %s\n" % (file1,)
513 + content = f"DIR: {file1}\n"
514 elif stat.S_ISFIFO(st.st_mode):
515 - content = "FIF: %s\n" % (file1,)
516 + content = f"FIF: {file1}\n"
517 else:
518 - content = "DEV: %s\n" % (file1,)
519 + content = f"DEV: {file1}\n"
520 with io.open(
521 diff_files[i], mode="w", encoding=_encodings["stdio"]
522 ) as f:
523 @@ -124,10 +121,7 @@ def read_config(mandatory_opts):
524 loader = KeyValuePairFileLoader(config_path, None)
525 opts, _errors = loader.load()
526 if not opts:
527 - print(
528 - _("dispatch-conf: Error reading {}; fatal").format(config_path),
529 - file=sys.stderr,
530 - )
531 + print(_(f"dispatch-conf: Error reading {config_path}; fatal"), file=sys.stderr)
532 sys.exit(1)
533
534 # Handle quote removal here, since KeyValuePairFileLoader doesn't do that.
535 @@ -143,9 +137,8 @@ def read_config(mandatory_opts):
536 else:
537 print(
538 _(
539 - 'dispatch-conf: Missing option "%s" in /etc/dispatch-conf.conf; fatal'
540 - )
541 - % (key,),
542 + f'dispatch-conf: Missing option "{key}" in /etc/dispatch-conf.conf; fatal'
543 + ),
544 file=sys.stderr,
545 )
546
547 @@ -160,8 +153,9 @@ def read_config(mandatory_opts):
548 os.chmod(opts["archive-dir"], 0o700)
549 elif not os.path.isdir(opts["archive-dir"]):
550 print(
551 - _("dispatch-conf: Config archive dir [%s] must exist; fatal")
552 - % (opts["archive-dir"],),
553 + _(
554 + rf"""dispatch-conf: Config archive dir [{opts["archive-dir"]}] must exist; fatal"""
555 + ),
556 file=sys.stderr,
557 )
558 sys.exit(1)
559 @@ -195,11 +189,7 @@ def _archive_copy(src_st, src_path, dest_path):
560 shutil.copy2(src_path, dest_path)
561 except EnvironmentError as e:
562 portage.util.writemsg(
563 - _(
564 - "dispatch-conf: Error copying %(src_path)s to "
565 - "%(dest_path)s: %(reason)s\n"
566 - )
567 - % {"src_path": src_path, "dest_path": dest_path, "reason": e},
568 + f"dispatch-conf: Error copying {src_path} to {dest_path}: {e}\n",
569 noiselevel=-1,
570 )
571
572 @@ -226,9 +216,9 @@ def rcs_archive(archive, curconf, newconf, mrgconf):
573 ):
574 _archive_copy(curconf_st, curconf, archive)
575
576 - if os.path.lexists(archive + ",v"):
577 - os.system(RCS_LOCK + " " + archive)
578 - os.system(RCS_PUT + " " + archive)
579 + if os.path.lexists(f"{archive},v"):
580 + os.system(f"{RCS_LOCK} {archive}")
581 + os.system(f"{RCS_PUT} {archive}")
582
583 ret = 0
584 mystat = None
585 @@ -241,17 +231,17 @@ def rcs_archive(archive, curconf, newconf, mrgconf):
586 if mystat is not None and (
587 stat.S_ISREG(mystat.st_mode) or stat.S_ISLNK(mystat.st_mode)
588 ):
589 - os.system(RCS_GET + " -r" + RCS_BRANCH + " " + archive)
590 + os.system(f"{RCS_GET} -r{RCS_BRANCH} {archive}")
591 has_branch = os.path.lexists(archive)
592 if has_branch:
593 - os.rename(archive, archive + ".dist")
594 + os.rename(archive, f"{archive}.dist")
595
596 _archive_copy(mystat, newconf, archive)
597
598 if has_branch:
599 if mrgconf and os.path.isfile(archive) and os.path.isfile(mrgconf):
600 # This puts the results of the merge into mrgconf.
601 - ret = os.system(RCS_MERGE % (archive, mrgconf))
602 + ret = os.system(f"rcsmerge -p -r{RCS_BRANCH} '{archive}' > '{mrgconf}'")
603 os.chmod(mrgconf, mystat.st_mode)
604 os.chown(mrgconf, mystat.st_uid, mystat.st_gid)
605 os.rename(archive, archive + ".dist.new")
606 @@ -273,8 +263,7 @@ def _file_archive_rotate(archive):
607 for max_suf, max_st, max_path in (
608 (suf, os.lstat(path), path)
609 for suf, path in (
610 - (suf, "%s.%s" % (archive, suf))
611 - for suf in range(1, _ARCHIVE_ROTATE_MAX + 1)
612 + (suf, f"{archive}.{suf}") for suf in range(1, _ARCHIVE_ROTATE_MAX + 1)
613 )
614 ):
615 pass
616 @@ -290,7 +279,7 @@ def _file_archive_rotate(archive):
617 # Removing a directory might destroy something important,
618 # so rename it instead.
619 head, tail = os.path.split(archive)
620 - placeholder = tempfile.NamedTemporaryFile(prefix="%s." % tail, dir=head)
621 + placeholder = tempfile.NamedTemporaryFile(prefix=f"{tail}.", dir=head)
622 placeholder.close()
623 os.rename(max_path, placeholder.name)
624 else:
625 @@ -300,9 +289,9 @@ def _file_archive_rotate(archive):
626 max_suf -= 1
627
628 for suf in range(max_suf + 1, 1, -1):
629 - os.rename("%s.%s" % (archive, suf - 1), "%s.%s" % (archive, suf))
630 + os.rename(f"{archive}.{suf - 1}", f"{archive}.{suf}")
631
632 - os.rename(archive, "%s.1" % (archive,))
633 + os.rename(archive, f"{archive}.1")
634
635
636 def _file_archive_ensure_dir(parent_dir):
637 @@ -347,7 +336,7 @@ def file_archive(archive, curconf, newconf, mrgconf):
638 # Archive the current config file if it isn't already saved
639 if (
640 os.path.lexists(archive)
641 - and len(diffstatusoutput_mixed("diff -aq '%s' '%s'", curconf, archive)[1]) != 0
642 + and len(diffstatusoutput_mixed(f"diff -aq '{curconf}' '{archive}'")[1]) != 0
643 ):
644 _file_archive_rotate(archive)
645
646 @@ -372,7 +361,7 @@ def file_archive(archive, curconf, newconf, mrgconf):
647 stat.S_ISREG(mystat.st_mode) or stat.S_ISLNK(mystat.st_mode)
648 ):
649 # Save off new config file in the archive dir with .dist.new suffix
650 - newconf_archive = archive + ".dist.new"
651 + newconf_archive = f"{archive}.dist.new"
652 if os.path.isdir(newconf_archive) and not os.path.islink(newconf_archive):
653 _file_archive_rotate(newconf_archive)
654 _archive_copy(mystat, newconf, newconf_archive)
655 @@ -382,11 +371,11 @@ def file_archive(archive, curconf, newconf, mrgconf):
656 mrgconf
657 and os.path.isfile(curconf)
658 and os.path.isfile(newconf)
659 - and os.path.isfile(archive + ".dist")
660 + and os.path.isfile(f"{archive}.dist")
661 ):
662 # This puts the results of the merge into mrgconf.
663 ret = os.system(
664 - DIFF3_MERGE % (curconf, archive + ".dist", newconf, mrgconf)
665 + f"diff3 -mE '{curconf}' '{archive}.dist' '{newconf}' > '{mrgconf}'"
666 )
667 os.chmod(mrgconf, mystat.st_mode)
668 os.chown(mrgconf, mystat.st_uid, mystat.st_gid)
669 @@ -397,24 +386,24 @@ def file_archive(archive, curconf, newconf, mrgconf):
670 def rcs_archive_post_process(archive):
671 """Check in the archive file with the .dist.new suffix on the branch
672 and remove the one with the .dist suffix."""
673 - os.rename(archive + ".dist.new", archive)
674 - if os.path.lexists(archive + ".dist"):
675 + os.rename(f"{archive}.dist.new", archive)
676 + if os.path.lexists(f"{archive}.dist"):
677 # Commit the last-distributed version onto the branch.
678 - os.system(RCS_LOCK + RCS_BRANCH + " " + archive)
679 - os.system(RCS_PUT + " -r" + RCS_BRANCH + " " + archive)
680 - os.unlink(archive + ".dist")
681 + os.system(f"{RCS_LOCK}{RCS_BRANCH} {archive}")
682 + os.system(f"{RCS_PUT} -r{RCS_BRANCH} {archive}")
683 + os.unlink(f"{archive}.dist")
684 else:
685 # Forcefully commit the last-distributed version onto the branch.
686 - os.system(RCS_PUT + " -f -r" + RCS_BRANCH + " " + archive)
687 + os.system(f"{RCS_PUT} -f -r{RCS_BRANCH} {archive}")
688
689
690 def file_archive_post_process(archive):
691 """Rename the archive file with the .dist.new suffix to a .dist suffix"""
692 - if os.path.lexists(archive + ".dist.new"):
693 - dest = "%s.dist" % archive
694 + if os.path.lexists(f"{archive}.dist.new"):
695 + dest = f"{archive}.dist"
696 if os.path.isdir(dest) and not os.path.islink(dest):
697 _file_archive_rotate(dest)
698 - os.rename(archive + ".dist.new", dest)
699 + os.rename(f"{archive}.dist.new", dest)
700
701
702 def perform_conf_update_hooks(kind, conf):
703
704 diff --git a/lib/portage/eclass_cache.py b/lib/portage/eclass_cache.py
705 index c89b70922..f1729326d 100644
706 --- a/lib/portage/eclass_cache.py
707 +++ b/lib/portage/eclass_cache.py
708 @@ -47,7 +47,7 @@ class hashed_path:
709 return val
710
711 def __repr__(self):
712 - return "<portage.eclass_cache.hashed_path('%s')>" % (self.location,)
713 + return f"<portage.eclass_cache.hashed_path('{self.location}')>"
714
715
716 class cache:
717 @@ -98,7 +98,7 @@ class cache:
718 that have the same name.
719 """
720 if not isinstance(other, self.__class__):
721 - raise TypeError("expected type %s, got %s" % (self.__class__, type(other)))
722 + raise TypeError(f"expected type {self.__class__}, got {type(other)}")
723 self.porttrees = self.porttrees + other.porttrees
724 self.eclasses.update(other.eclasses)
725 self._eclass_locations.update(other._eclass_locations)
726
727 diff --git a/lib/portage/exception.py b/lib/portage/exception.py
728 index ff40e463b..360febcc8 100644
729 --- a/lib/portage/exception.py
730 +++ b/lib/portage/exception.py
731 @@ -217,10 +217,10 @@ class UnsupportedAPIException(PortagePackageException):
732 eapi = str(eapi)
733 eapi = eapi.lstrip("-")
734 msg = _(
735 - "Unable to do any operations on '%(cpv)s', since "
736 + f"Unable to do any operations on '{self.cpv}', since "
737 "its EAPI is higher than this portage version's. Please upgrade"
738 - " to a portage version that supports EAPI '%(eapi)s'."
739 - ) % {"cpv": self.cpv, "eapi": eapi}
740 + f" to a portage version that supports EAPI '{eapi}'."
741 + )
742 return _unicode_decode(msg, encoding=_encodings["content"], errors="replace")
743
744
745
746 diff --git a/lib/portage/getbinpkg.py b/lib/portage/getbinpkg.py
747 index 6aa8f1de1..1e89119fb 100644
748 --- a/lib/portage/getbinpkg.py
749 +++ b/lib/portage/getbinpkg.py
750 @@ -172,12 +172,10 @@ def create_conn(baseurl, conn=None):
751 except AttributeError:
752 # Python 2
753 encodebytes = base64.encodestring
754 - http_headers = {
755 - b"Authorization": "Basic %s"
756 - % encodebytes(_unicode_encode("%s:%s" % (username, password))).replace(
757 - b"\012", b""
758 - ),
759 - }
760 + unicode_bytes = encodebytes(_unicode_encode(f"{username}:{password}")).replace(
761 + b"\012", b""
762 + )
763 + http_headers = {b"Authorization": f"Basic {unicode_bytes}"}
764
765 if not conn:
766 if protocol == "https":
767 @@ -204,9 +202,10 @@ def create_conn(baseurl, conn=None):
768 conn.login(username, password)
769 else:
770 sys.stderr.write(
771 - colorize("WARN", _(" * No password provided for username"))
772 - + " '%s'" % (username,)
773 - + "\n\n"
774 + colorize(
775 + "WARN",
776 + _(f" * No password provided for username '{username}'\n\n"),
777 + )
778 )
779 conn.login(username)
780 conn.set_pasv(passive)
781 @@ -245,15 +244,12 @@ def make_ftp_request(conn, address, rest=None, dest=None):
782 conn.voidcmd("TYPE I")
783 fsize = conn.size(address)
784
785 - if (rest != None) and (rest < 0):
786 + retr_address = f"RETR {address}"
787 + if rest and rest < 0:
788 rest = fsize + int(rest)
789 - if rest < 0:
790 - rest = 0
791 -
792 - if rest != None:
793 - mysocket = conn.transfercmd("RETR %s" % str(address), rest)
794 + mysocket = conn.transfercmd(retr_address, rest)
795 else:
796 - mysocket = conn.transfercmd("RETR %s" % str(address))
797 + mysocket = conn.transfercmd(retr_address)
798
799 mydata = ""
800 while 1:
801 @@ -262,7 +258,7 @@ def make_ftp_request(conn, address, rest=None, dest=None):
802 if dest:
803 dest.write(somedata)
804 else:
805 - mydata = mydata + somedata
806 + mydata = f"{mydata}{somedata}"
807 else:
808 break
809
810 @@ -302,7 +298,7 @@ def make_http_request(conn, address, _params={}, headers={}, dest=None):
811 except SystemExit as e:
812 raise
813 except Exception as e:
814 - return None, None, "Server request failed: %s" % str(e)
815 + return None, None, f"Server request failed: {e}"
816 response = conn.getresponse()
817 rc = response.status
818
819 @@ -315,15 +311,11 @@ def make_http_request(conn, address, _params={}, headers={}, dest=None):
820 if parts[0] == "Location":
821 if rc == 301:
822 sys.stderr.write(
823 - colorize("BAD", _("Location has moved: "))
824 - + str(parts[1])
825 - + "\n"
826 + f"{colorize('BAD', _('Location has moved: '))}{parts[1]}\n"
827 )
828 if rc == 302:
829 sys.stderr.write(
830 - colorize("BAD", _("Location has temporarily moved: "))
831 - + str(parts[1])
832 - + "\n"
833 + f"{colorize('BAD', _('Location has temporarily moved: '))}{parts[1]}\n"
834 )
835 address = parts[1]
836 break
837 @@ -332,8 +324,7 @@ def make_http_request(conn, address, _params={}, headers={}, dest=None):
838 return (
839 None,
840 rc,
841 - "Server did not respond successfully (%s: %s)"
842 - % (str(response.status), str(response.reason)),
843 + f"Server did not respond successfully ({response.status}: {response.reason})",
844 )
845
846 if dest:
847 @@ -408,7 +399,7 @@ def dir_get_list(baseurl, conn=None):
848 if not address.endswith("/"):
849 # http servers can return a 400 error here
850 # if the address doesn't end with a slash.
851 - address += "/"
852 + address = f"{address}/"
853 page, rc, msg = make_http_request(conn, address, params, headers)
854
855 if page:
856 @@ -461,7 +452,7 @@ def file_get_metadata(baseurl, conn=None, chunk_size=3000):
857 conn, protocol, address, params, headers = create_conn(baseurl, conn)
858
859 if protocol in ["http", "https"]:
860 - headers["Range"] = "bytes=-%s" % str(chunk_size)
861 + headers["Range"] = f"bytes=-{chunk_size}"
862 data, _x, _x = make_http_request(conn, address, params, headers)
863 elif protocol in ["ftp"]:
864 data, _x, _x = make_ftp_request(conn, address, -chunk_size)
865 @@ -473,7 +464,7 @@ def file_get_metadata(baseurl, conn=None, chunk_size=3000):
866 finally:
867 f.close()
868 else:
869 - raise TypeError(_("Unknown protocol. '%s'") % protocol)
870 + raise TypeError(_(f"Unknown protocol. '{protocol}'"))
871
872 if data:
873 xpaksize = portage.xpak.decodeint(data[-8:-4])
874 @@ -523,14 +514,14 @@ def file_get(
875 if "DISTDIR" not in variables:
876 if dest is None:
877 raise portage.exception.MissingParameter(
878 - _("%s is missing required '%s' key") % ("fcmd_vars", "DISTDIR")
879 + _("fcmd_vars is missing required 'DISTDIR' key")
880 )
881 variables["DISTDIR"] = dest
882
883 if "URI" not in variables:
884 if baseurl is None:
885 raise portage.exception.MissingParameter(
886 - _("%s is missing required '%s' key") % ("fcmd_vars", "URI")
887 + _("fcmd_vars is missing required 'URI' key")
888 )
889 variables["URI"] = baseurl
890
891 @@ -576,7 +567,7 @@ def file_get_lib(baseurl, dest, conn=None):
892
893 conn, protocol, address, params, headers = create_conn(baseurl, conn)
894
895 - sys.stderr.write("Fetching '" + str(os.path.basename(address)) + "'\n")
896 + sys.stderr.write(f"Fetching '{os.path.basename(address)}'\n")
897 if protocol in ["http", "https"]:
898 data, rc, _msg = make_http_request(conn, address, params, headers, dest=dest)
899 elif protocol in ["ftp"]:
900 @@ -673,7 +664,7 @@ def dir_get_metadata(
901
902 if not os.access(cache_path, os.W_OK):
903 sys.stderr.write(_("!!! Unable to write binary metadata to disk!\n"))
904 - sys.stderr.write(_("!!! Permission denied: '%s'\n") % cache_path)
905 + sys.stderr.write(_(f"!!! Permission denied: '{cache_path}'\n"))
906 return metadata[baseurl]["data"]
907
908 import portage.exception
909 @@ -681,10 +672,8 @@ def dir_get_metadata(
910 try:
911 filelist = dir_get_list(baseurl, conn)
912 except portage.exception.PortageException as e:
913 - sys.stderr.write(
914 - _("!!! Error connecting to '%s'.\n") % _hide_url_passwd(baseurl)
915 - )
916 - sys.stderr.write("!!! %s\n" % str(e))
917 + sys.stderr.write(_(f"!!! Error connecting to '{_hide_url_passwd(baseurl)}'.\n"))
918 + sys.stderr.write(f"!!! {e}\n")
919 del e
920 return metadata[baseurl]["data"]
921 tbz2list = match_in_array(filelist, suffix=".tbz2")
922 @@ -743,10 +732,8 @@ def dir_get_metadata(
923 except SystemExit as e:
924 raise
925 except Exception as e:
926 - sys.stderr.write(
927 - _("!!! Failed to read data from index: ") + str(mfile) + "\n"
928 - )
929 - sys.stderr.write("!!! %s" % str(e))
930 + sys.stderr.write(f"!!! Failed to read data from index: {mfile}\n")
931 + sys.stderr.write(f"!!! {e}")
932 sys.stderr.flush()
933 try:
934 metadatafile = open(
935 @@ -761,7 +748,7 @@ def dir_get_metadata(
936 raise
937 except Exception as e:
938 sys.stderr.write(_("!!! Failed to write binary metadata to disk!\n"))
939 - sys.stderr.write("!!! %s\n" % str(e))
940 + sys.stderr.write(f"!!! {e}\n")
941 sys.stderr.flush()
942 break
943 # We may have metadata... now we run through the tbz2 list and check.
944 @@ -784,10 +771,14 @@ def dir_get_metadata(
945
946 def display(self):
947 self.out.write(
948 - "\r"
949 - + colorize("WARN", _("cache miss: '") + str(self.misses) + "'")
950 - + " --- "
951 - + colorize("GOOD", _("cache hit: '") + str(self.hits) + "'")
952 + "".join(
953 + (
954 + "\r",
955 + colorize("WARN", _(f"cache miss: '{self.misses}'")),
956 + " --- ",
957 + colorize("GOOD", _(f"cache hit: '{self.hits}'")),
958 + )
959 + )
960 )
961 self.out.flush()
962
963 @@ -829,9 +820,7 @@ def dir_get_metadata(
964 metadata[baseurl]["data"][x] = make_metadata_dict(myid)
965 elif verbose:
966 sys.stderr.write(
967 - colorize("BAD", _("!!! Failed to retrieve metadata on: "))
968 - + str(x)
969 - + "\n"
970 + f"{colorize('BAD', _('!!! Failed to retrieve metadata on: '))}{x}\n"
971 )
972 sys.stderr.flush()
973 else:
974 @@ -946,7 +935,7 @@ class PackageIndex:
975
976 def _writepkgindex(self, pkgfile, items):
977 for k, v in items:
978 - pkgfile.write("%s: %s\n" % (self._write_translation_map.get(k, k), v))
979 + pkgfile.write(f"{self._write_translation_map.get(k, k)}: {v}\n")
980 pkgfile.write("\n")
981
982 def read(self, pkgfile):
983
984 diff --git a/lib/portage/glsa.py b/lib/portage/glsa.py
985 index 19f226db1..05de3ade6 100644
986 --- a/lib/portage/glsa.py
987 +++ b/lib/portage/glsa.py
988 @@ -76,26 +76,29 @@ def wrap(text, width, caption=""):
989 words = text.split()
990 indentLevel = len(caption) + 1
991
992 - for w in words:
993 - if line != "" and line[-1] == "\n":
994 - rValue += line
995 + for word in words:
996 + if line and line[-1] == "\n":
997 + rValue = f"{rValue}{line}"
998 line = " " * indentLevel
999 - if len(line) + len(w.replace(NEWLINE_ESCAPE, "")) + 1 > width:
1000 - rValue += line + "\n"
1001 - line = " " * indentLevel + w.replace(NEWLINE_ESCAPE, "\n")
1002 - elif w.find(NEWLINE_ESCAPE) >= 0:
1003 + if len(line) + len(word.replace(NEWLINE_ESCAPE, "")) + 1 > width:
1004 + rValue = f"{rValue}{line}\n"
1005 + escaped_word = word.replace(NEWLINE_ESCAPE, "\n")
1006 + line = f"{' ' * indentLevel}{escaped_word}"
1007 + elif word.find(NEWLINE_ESCAPE) >= 0:
1008 + escaped_word = word.replace(NEWLINE_ESCAPE, "\n")
1009 + whitespace = ""
1010 if len(line.strip()) > 0:
1011 - rValue += line + " " + w.replace(NEWLINE_ESCAPE, "\n")
1012 - else:
1013 - rValue += line + w.replace(NEWLINE_ESCAPE, "\n")
1014 + whitespace = " "
1015 + rValue = f"{rValue}{line}{whitespace}{escaped_word}"
1016 line = " " * indentLevel
1017 else:
1018 + whitespace = ""
1019 if len(line.strip()) > 0:
1020 - line += " " + w
1021 - else:
1022 - line += w
1023 + whitespace = " "
1024 + line = f"{line}{whitespace}{word}"
1025 if len(line) > 0:
1026 - rValue += line.replace(NEWLINE_ESCAPE, "\n")
1027 + escaped_line = line.replace(NEWLINE_ESCAPE, "\n")
1028 + rValue = f"{rValue}{escaped_line}"
1029 rValue = rValue.replace(SPACE_ESCAPE, " ")
1030 return rValue
1031
1032 @@ -184,7 +187,7 @@ def getText(node, format, textfd=None): # pylint: disable=redefined-builtin
1033 returnNone = True
1034 if format in ["strip", "keep"]:
1035 if node.nodeName in ["uri", "mail"]:
1036 - textfd.write(node.childNodes[0].data + ": " + node.getAttribute("link"))
1037 + textfd.write(f"{node.childNodes[0].data}:{node.getAttribute('link')}")
1038 else:
1039 for subnode in node.childNodes:
1040 if subnode.nodeName == "#text":
1041 @@ -203,14 +206,12 @@ def getText(node, format, textfd=None): # pylint: disable=redefined-builtin
1042 textfd.write(NEWLINE_ESCAPE)
1043 elif subnode.nodeName == "ul":
1044 for li in getListElements(subnode):
1045 - textfd.write("-" + SPACE_ESCAPE + li + NEWLINE_ESCAPE + " ")
1046 + textfd.write(f"-{SPACE_ESCAPE}{li}{NEWLINE_ESCAPE} ")
1047 elif subnode.nodeName == "ol":
1048 i = 0
1049 for li in getListElements(subnode):
1050 i = i + 1
1051 - textfd.write(
1052 - str(i) + "." + SPACE_ESCAPE + li + NEWLINE_ESCAPE + " "
1053 - )
1054 + textfd.write(f"{i}.{SPACE_ESCAPE}{li}{NEWLINE_ESCAPE} ")
1055 elif subnode.nodeName == "code":
1056 textfd.write(
1057 getText(subnode, format="keep")
1058 @@ -262,19 +263,16 @@ def makeAtom(pkgname, versionNode):
1059 @rtype: String
1060 @return: the portage atom
1061 """
1062 - rValue = (
1063 - opMapping[versionNode.getAttribute("range")]
1064 - + pkgname
1065 - + "-"
1066 - + getText(versionNode, format="strip")
1067 - )
1068 + op = opMapping[versionNode.getAttribute("range")]
1069 + version = getText(versionNode, format="strip")
1070 + rValue = f"{op}{pkgname}-{version}"
1071 try:
1072 slot = versionNode.getAttribute("slot").strip()
1073 except KeyError:
1074 pass
1075 else:
1076 if slot and slot != "*":
1077 - rValue += _slot_separator + slot
1078 + rValue = f"{rValue}{_slot_separator}{slot}"
1079 return str(rValue)
1080
1081
1082 @@ -289,16 +287,16 @@ def makeVersion(versionNode):
1083 @rtype: String
1084 @return: the version string
1085 """
1086 - rValue = opMapping[versionNode.getAttribute("range")] + getText(
1087 - versionNode, format="strip"
1088 - )
1089 + op = opMapping[versionNode.getAttribute("range")]
1090 + version = getText(versionNode, format="strip")
1091 + rValue = f"{op}{version}"
1092 try:
1093 slot = versionNode.getAttribute("slot").strip()
1094 except KeyError:
1095 pass
1096 else:
1097 if slot and slot != "*":
1098 - rValue += _slot_separator + slot
1099 + rValue = f"{rValue}{_slot_separator}{slot}"
1100 return rValue
1101
1102
1103 @@ -421,9 +419,9 @@ def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=
1104 and portdbapi._pkg_str(c, None).slot
1105 == vardbapi._pkg_str(vuln, None).slot
1106 ):
1107 - update = c_pv[0] + "/" + c_pv[1] + "-" + c_pv[2]
1108 + update = f"{c_pv[0]}/{c_pv[1]}-{c_pv[2]}"
1109 if c_pv[3] != "r0": # we don't like -r0 for display
1110 - update += "-" + c_pv[3]
1111 + update = f"{update}-{c_pv[3]}"
1112 update = portdbapi._pkg_str(update, None)
1113 vuln_update.append([vuln, update])
1114
1115 @@ -466,7 +464,7 @@ def format_date(datestr):
1116
1117 class GlsaTypeException(Exception):
1118 def __init__(self, doctype):
1119 - Exception.__init__(self, "wrong DOCTYPE: %s" % doctype)
1120 + Exception.__init__(self, f"wrong DOCTYPE: {doctype}")
1121
1122
1123 class GlsaFormatException(Exception):
1124 @@ -509,7 +507,7 @@ class Glsa:
1125 self.type = "file"
1126 else:
1127 raise GlsaArgumentException(
1128 - _("Given ID %s isn't a valid GLSA ID or filename.") % myid
1129 + _(f"Given ID {myid} isn't a valid GLSA ID or filename.")
1130 )
1131 self.nr = myid
1132 self.config = myconfig
1133 @@ -526,13 +524,13 @@ class Glsa:
1134 @return: None
1135 """
1136 if "GLSA_DIR" in self.config:
1137 - repository = "file://" + self.config["GLSA_DIR"] + "/"
1138 + repository = f"file://{self.config['GLSA_DIR']}/"
1139 else:
1140 - repository = "file://" + self.config["PORTDIR"] + "/metadata/glsa/"
1141 + repository = f"file://{self.config['PORTDIR']}/metadata/glsa/"
1142 if self.type == "file":
1143 - myurl = "file://" + self.nr
1144 + myurl = f"file://{self.nr}"
1145 else:
1146 - myurl = repository + "glsa-%s.xml" % str(self.nr)
1147 + myurl = f"{repository}glsa-{self.nr}.xml"
1148
1149 f = urllib_request_urlopen(myurl)
1150 try:
1151 @@ -563,10 +561,9 @@ class Glsa:
1152 myroot = self.DOM.getElementsByTagName("glsa")[0]
1153 if self.type == "id" and myroot.getAttribute("id") != self.nr:
1154 raise GlsaFormatException(
1155 - _("filename and internal id don't match:")
1156 - + myroot.getAttribute("id")
1157 - + " != "
1158 - + self.nr
1159 + _(
1160 + f"filename and internal id don't match: {myroot.getAttribute('id')} != {self.nr}"
1161 + )
1162 )
1163
1164 # the simple (single, required, top-level, #PCDATA) tags first
1165 @@ -585,17 +582,14 @@ class Glsa:
1166 count = revisedEl.getAttribute("count")
1167 if not count:
1168 raise GlsaFormatException(
1169 - "Count attribute is missing or blank in GLSA: "
1170 - + myroot.getAttribute("id")
1171 + f"Count attribute is missing or blank in GLSA: {myroot.getAttribute('id')}"
1172 )
1173
1174 try:
1175 self.count = int(count)
1176 except ValueError:
1177 raise GlsaFormatException(
1178 - "Revision attribute in GLSA: "
1179 - + myroot.getAttribute("id")
1180 - + " is not an integer"
1181 + f"Revision attribute in GLSA: {myroot.getAttribute('id')} is not an integer"
1182 )
1183
1184 self.revised = format_date(self.revised)
1185 @@ -645,9 +639,9 @@ class Glsa:
1186 try:
1187 name = portage.dep.Atom(name)
1188 except portage.exception.InvalidAtom:
1189 - raise GlsaFormatException(_("invalid package name: %s") % name)
1190 + raise GlsaFormatException(_(f"invalid package name: {name}"))
1191 if name != name.cp:
1192 - raise GlsaFormatException(_("invalid package name: %s") % name)
1193 + raise GlsaFormatException(_(f"invalid package name: {name}"))
1194 name = name.cp
1195 if name not in self.packages:
1196 self.packages[name] = []
1197 @@ -683,58 +677,51 @@ class Glsa:
1198 outstream = getattr(outstream, "buffer", outstream)
1199 outstream = codecs.getwriter(encoding)(outstream)
1200 width = 76
1201 - outstream.write(("GLSA %s: \n%s" % (self.nr, self.title)).center(width) + "\n")
1202 - outstream.write((width * "=") + "\n")
1203 - outstream.write(
1204 - wrap(self.synopsis, width, caption=_("Synopsis: ")) + "\n"
1205 - )
1206 - outstream.write(_("Announced on: %s\n") % self.announced)
1207 - outstream.write(
1208 - _("Last revised on: %s : %02d\n\n") % (self.revised, self.count)
1209 + buffer = "\n".join(
1210 + (
1211 + f"GLSA {self.nr}: ",
1212 + f"{self.title}".center(width),
1213 + "=" * width,
1214 + wrap(self.synopsis, width, caption=_("Synopsis: ")),
1215 + _(f"Announced on: {self.announced}"),
1216 + _(f"Last revised on: {self.revised} : %{self.count}\n"),
1217 + )
1218 )
1219 + outstream.write(buffer)
1220 if self.glsatype == "ebuild":
1221 for k in self.packages:
1222 pkg = self.packages[k]
1223 for path in pkg:
1224 vul_vers = ", ".join(path["vul_vers"])
1225 unaff_vers = ", ".join(path["unaff_vers"])
1226 - outstream.write(_("Affected package: %s\n") % k)
1227 + outstream.write(_(f"Affected package: {k}\n"))
1228 outstream.write(_("Affected archs: "))
1229 if path["arch"] == "*":
1230 outstream.write(_("All\n"))
1231 else:
1232 - outstream.write("%s\n" % path["arch"])
1233 - outstream.write(_("Vulnerable: %s\n") % vul_vers)
1234 - outstream.write(_("Unaffected: %s\n\n") % unaff_vers)
1235 + outstream.write(f"{path['arch']}\n")
1236 + outstream.write(_(f"Vulnerable: {vul_vers}\n"))
1237 + outstream.write(_(f"Unaffected: {unaff_vers}\n\n"))
1238 elif self.glsatype == "infrastructure":
1239 pass
1240 if len(self.bugs) > 0:
1241 - outstream.write(_("\nRelated bugs: "))
1242 - outstream.write(", ".join(self.bugs))
1243 - outstream.write("\n")
1244 + outstream.write(_(f"\nRelated bugs: {', '.join(self.bugs)}\n"))
1245 if self.background:
1246 - outstream.write(
1247 - "\n" + wrap(self.background, width, caption=_("Background: "))
1248 - )
1249 - outstream.write(
1250 - "\n" + wrap(self.description, width, caption=_("Description: "))
1251 - )
1252 - outstream.write(
1253 - "\n" + wrap(self.impact_text, width, caption=_("Impact: "))
1254 - )
1255 - outstream.write(
1256 - "\n" + wrap(self.workaround, width, caption=_("Workaround: "))
1257 - )
1258 - outstream.write(
1259 - "\n" + wrap(self.resolution, width, caption=_("Resolution: "))
1260 - )
1261 + bg = wrap(self.background, width, caption=_("Background: "))
1262 + outstream.write(f"\n{bg}")
1263 myreferences = " ".join(
1264 r.replace(" ", SPACE_ESCAPE) + NEWLINE_ESCAPE for r in self.references
1265 )
1266 - outstream.write(
1267 - "\n" + wrap(myreferences, width, caption=_("References: "))
1268 + buffer = "\n".join(
1269 + (
1270 + wrap(self.description, width, caption=_("Description: ")),
1271 + wrap(self.impact_text, width, caption=_("Impact: ")),
1272 + wrap(self.workaround, width, caption=_("Workaround: ")),
1273 + wrap(self.resolution, width, caption=_("Resolution: ")),
1274 + wrap(myreferences, width, caption=_("References: ")),
1275 + )
1276 )
1277 - outstream.write("\n")
1278 + outstream.write(f"\n{buffer}\n")
1279
1280 def isVulnerable(self):
1281 """
1282
1283 diff --git a/lib/portage/gpg.py b/lib/portage/gpg.py
1284 index 57be2ebc0..6238ea6f7 100644
1285 --- a/lib/portage/gpg.py
1286 +++ b/lib/portage/gpg.py
1287 @@ -58,13 +58,13 @@ class GPG:
1288 # When run with no input/output tty, this will fail.
1289 # However, if the password is given by command,
1290 # GPG does not need to ask password, so can be ignored.
1291 - writemsg(colorize("WARN", str(e)) + "\n")
1292 + writemsg(f"{colorize('WARN', str(e))}\n")
1293
1294 cmd = shlex_split(varexpand(self.GPG_unlock_command, mydict=self.settings))
1295 return_code = subprocess.Popen(cmd).wait()
1296
1297 if return_code == os.EX_OK:
1298 - writemsg_stdout(colorize("GOOD", "unlocked") + "\n")
1299 + writemsg_stdout(f"{colorize('GOOD', 'unlocked')}\n")
1300 sys.stdout.flush()
1301 else:
1302 raise GPGException("GPG unlock failed")
1303
1304 diff --git a/lib/portage/gpkg.py b/lib/portage/gpkg.py
1305 index b642e74ec..fc111e44a 100644
1306 --- a/lib/portage/gpkg.py
1307 +++ b/lib/portage/gpkg.py
1308 @@ -427,7 +427,7 @@ class tar_stream_reader:
1309 if not self.proc.stderr.closed:
1310 stderr = self.proc.stderr.read().decode()
1311 if not self.killed:
1312 - writemsg(colorize("BAD", "!!!" + "\n" + stderr))
1313 + writemsg(colorize("BAD", f"!!!\n{stderr}"))
1314 raise CompressorOperationFailed("decompression failed")
1315 finally:
1316 self.proc.stdout.close()
1317 @@ -610,7 +610,7 @@ class checksum_helper:
1318 trust_signature = True
1319
1320 if (not good_signature) or (not trust_signature):
1321 - writemsg(colorize("BAD", "!!!" + "\n" + self.gpg_result.decode()))
1322 + writemsg(colorize("BAD", f"!!!\n{self.gpg_result.decode()}"))
1323 raise InvalidSignature("GPG verify failed")
1324
1325 def _drop_privileges(self):
1326 @@ -673,7 +673,7 @@ class checksum_helper:
1327 if self.gpg_operation == checksum_helper.VERIFY:
1328 self._check_gpg_status(self.gpg_result.decode())
1329 else:
1330 - writemsg(colorize("BAD", "!!!" + "\n" + self.gpg_result.decode()))
1331 + writemsg(colorize("BAD", f"!!!\n{self.gpg_result.decode()}"))
1332 if self.gpg_operation == checksum_helper.SIGNING:
1333 writemsg(colorize("BAD", self.gpg_output.decode()))
1334 raise GPGException("GPG signing failed")
1335 @@ -913,9 +913,7 @@ class gpkg:
1336 container_file.write(
1337 urlopen(
1338 url,
1339 - headers={
1340 - "Range": "bytes=" + str(init_size + 1) + "-" + str(end_size)
1341 - },
1342 + headers={"Range": f"bytes={init_size + 1}-{end_size}"},
1343 ).read()
1344 )
1345
1346 @@ -1057,7 +1055,7 @@ class gpkg:
1347 image_safe = tar_safe_extract(image, "image")
1348 image_safe.extractall(decompress_dir)
1349 except Exception as ex:
1350 - writemsg(colorize("BAD", "!!!" + "Extract failed."))
1351 + writemsg(colorize("BAD", "!!!Extract failed."))
1352 raise
1353 finally:
1354 image_tar.kill()
1355 @@ -1079,7 +1077,7 @@ class gpkg:
1356 raise InvalidBinaryPackageFormat("Cannot identify tar format")
1357
1358 # container
1359 - tmp_gpkg_file_name = self.gpkg_file + "." + str(os.getpid())
1360 + tmp_gpkg_file_name = f"{self.gpkg_file}.{os.getpid()}"
1361 with tarfile.TarFile(
1362 name=tmp_gpkg_file_name, mode="w", format=container_tar_format
1363 ) as container:
1364 @@ -1178,7 +1176,7 @@ class gpkg:
1365 """
1366
1367 protect_file = io.BytesIO(
1368 - b"# empty file because --include-config=n " + b"when `quickpkg` was used\n"
1369 + b"# empty file because --include-config=n when `quickpkg` was used\n"
1370 )
1371 protect_file.seek(0, io.SEEK_END)
1372 protect_file_size = protect_file.tell()
1373 @@ -1412,7 +1410,7 @@ class gpkg:
1374 raise GPGException("GPG signature is not exists")
1375
1376 signature = io.BytesIO(checksum_info.gpg_output)
1377 - signature_tarinfo = tarfile.TarInfo(tarinfo.name + ".sig")
1378 + signature_tarinfo = tarfile.TarInfo(f"{tarinfo.name}.sig")
1379 signature_tarinfo.size = len(signature.getvalue())
1380 signature_tarinfo.mtime = datetime.utcnow().timestamp()
1381 container.addfile(signature_tarinfo, signature)
1382
1383 diff --git a/lib/portage/localization.py b/lib/portage/localization.py
1384 index 9df71d62d..b9c9e90ec 100644
1385 --- a/lib/portage/localization.py
1386 +++ b/lib/portage/localization.py
1387 @@ -28,10 +28,7 @@ def localization_example():
1388 a_value = "value.of.a"
1389 b_value = 123
1390 c_value = [1, 2, 3, 4]
1391 - print(
1392 - _("A: %(a)s -- B: %(b)s -- C: %(c)s")
1393 - % {"a": a_value, "b": b_value, "c": c_value}
1394 - )
1395 + print(_(f"A: {a_value} -- B: {b_value} -- C: {c_value}"))
1396
1397
1398 def localized_size(num_bytes):
1399 @@ -47,4 +44,5 @@ def localized_size(num_bytes):
1400 except UnicodeDecodeError:
1401 # failure to decode locale data
1402 formatted_num = str(num_kib)
1403 - return _unicode_decode(formatted_num, encoding=_encodings["stdio"]) + " KiB"
1404 + unicode_num = _unicode_decode(formatted_num, encoding=_encodings["stdio"])
1405 + return f"{unicode_num} KiB"
1406
1407 diff --git a/lib/portage/mail.py b/lib/portage/mail.py
1408 index aa2617b42..7ffb0d269 100644
1409 --- a/lib/portage/mail.py
1410 +++ b/lib/portage/mail.py
1411 @@ -54,7 +54,7 @@ def create_message(sender, recipient, subject, body, attachments=None):
1412 mymessage.attach(TextMessage(x))
1413 else:
1414 raise portage.exception.PortageException(
1415 - _("Can't handle type of attachment: %s") % type(x)
1416 + _(f"Can't handle type of attachment: {type(x)}")
1417 )
1418
1419 mymessage.set_unixfrom(sender)
1420 @@ -117,14 +117,13 @@ def send_mail(mysettings, message):
1421
1422 # user wants to use a sendmail binary instead of smtp
1423 if mymailhost[0] == os.sep and os.path.exists(mymailhost):
1424 - fd = os.popen(mymailhost + " -f " + myfrom + " " + myrecipient, "w")
1425 + fd = os.popen(f"{mymailhost } -f {myfrom} {myrecipient}", "w")
1426 fd.write(_force_ascii_if_necessary(message.as_string()))
1427 if fd.close() != None:
1428 sys.stderr.write(
1429 _(
1430 - "!!! %s returned with a non-zero exit code. This generally indicates an error.\n"
1431 + f"!!! {mymailhost} returned with a non-zero exit code. This generally indicates an error.\n"
1432 )
1433 - % mymailhost
1434 )
1435 else:
1436 try:
1437 @@ -149,12 +148,11 @@ def send_mail(mysettings, message):
1438 myconn.quit()
1439 except smtplib.SMTPException as e:
1440 raise portage.exception.PortageException(
1441 - _("!!! An error occurred while trying to send logmail:\n") + str(e)
1442 + _(f"!!! An error occurred while trying to send logmail:\n{e}")
1443 )
1444 except socket.error as e:
1445 raise portage.exception.PortageException(
1446 _(
1447 - "!!! A network error occurred while trying to send logmail:\n%s\nSure you configured PORTAGE_ELOG_MAILURI correctly?"
1448 + f"!!! A network error occurred while trying to send logmail:\n{e}\nSure you configured PORTAGE_ELOG_MAILURI correctly?"
1449 )
1450 - % str(e)
1451 )