From b337b33564da0b21d244b46c2b3e954ae6afc099 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:45:59 +0200 Subject: [PATCH 1/8] PERF101 --- pyproject.toml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6d9910ca140..4190f091ad0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,21 +139,22 @@ exclude = "wheels/multibuild" exclude = [ "wheels/multibuild" ] fix = true lint.select = [ - "C4", # flake8-comprehensions - "E", # pycodestyle errors - "EM", # flake8-errmsg - "F", # pyflakes errors - "I", # isort - "ISC", # flake8-implicit-str-concat - "LOG", # flake8-logging - "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PT", # flake8-pytest-style - "PYI", # flake8-pyi - "RUF100", # unused noqa (yesqa) - "UP", # pyupgrade - "W", # pycodestyle warnings - "YTT", # flake8-2020 + "C4", # flake8-comprehensions + "E", # pycodestyle errors + "EM", # flake8-errmsg + "F", # pyflakes errors + "I", # isort + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PERF101", # perflint: unnecessary-list-cast + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RUF100", # unused noqa (yesqa) + "UP", # pyupgrade + "W", # pycodestyle warnings + "YTT", # flake8-2020 ] lint.ignore = [ "E203", # Whitespace before ':' From 624fc87d2d91f9bd763f3cd721f999f58a6752bb Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:46:16 +0200 Subject: [PATCH 2/8] PERF102 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 4190f091ad0..7178cb2deca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -147,6 +147,7 @@ lint.select = [ "ISC", # flake8-implicit-str-concat "LOG", # flake8-logging "PERF101", # perflint: unnecessary-list-cast + "PERF102", # perflint: incorrect-dict-iterator "PGH", # pygrep-hooks "PIE", # flake8-pie "PT", # flake8-pytest-style From b85b8534d7ca1132cfe4067ae2963c26f07b7811 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:11:12 +0200 Subject: [PATCH 3/8] PERF401 and fixes --- pyproject.toml | 1 + setup.py | 8 ++++---- src/PIL/IcnsImagePlugin.py | 3 +-- src/PIL/ImageDraw.py | 4 +--- src/PIL/ImageFile.py | 6 ++++-- winbuild/build_prepare.py | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7178cb2deca..ea43e9cb1cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -148,6 +148,7 @@ lint.select = [ "LOG", # flake8-logging "PERF101", # perflint: unnecessary-list-cast "PERF102", # perflint: incorrect-dict-iterator + "PERF401", # perflint: manual-list-comprehension "PGH", # pygrep-hooks "PIE", # flake8-pie "PT", # flake8-pytest-style diff --git a/setup.py b/setup.py index 3d975950b6c..175aed25a02 100644 --- a/setup.py +++ b/setup.py @@ -1078,10 +1078,10 @@ def debug_build() -> bool: ] files: list[str | os.PathLike[str]] = ["src/_imaging.c"] -for src_file in _IMAGING: - files.append("src/" + src_file + ".c") -for src_file in _LIB_IMAGING: - files.append(os.path.join("src/libImaging", src_file + ".c")) +files.extend("src/" + src_file + ".c" for src_file in _IMAGING) +files.extend( + os.path.join("src/libImaging", src_file + ".c") for src_file in _LIB_IMAGING +) ext_modules = [ Extension("PIL._imaging", files), Extension("PIL._imagingft", ["src/_imagingft.c"]), diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 023835fb71e..cb7a74c2e3b 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -80,8 +80,7 @@ def read_32( if byte_int & 0x80: blocksize = byte_int - 125 byte = fobj.read(1) - for i in range(blocksize): - data.append(byte) + data.extend([byte] * blocksize) else: blocksize = byte_int + 1 data.append(fobj.read(blocksize)) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index eb108ac41ca..506bb3b43b0 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -597,9 +597,7 @@ def draw_text(ink: int, stroke_width: float = 0) -> None: mode = self.fontmode if stroke_width == 0 and embedded_color: mode = "RGBA" - coord = [] - for i in range(2): - coord.append(int(xy[i])) + coord = [int(xy[i]) for i in range(2)] start = (math.modf(xy[0])[0], math.modf(xy[1])[0]) try: mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc] diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 50e0075a29d..df2a82b7366 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -215,8 +215,10 @@ def get_child_images(self) -> list[ImageFile]: if subifd_offsets: if not isinstance(subifd_offsets, tuple): subifd_offsets = (subifd_offsets,) - for subifd_offset in subifd_offsets: - ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) + ifds = [ + (exif._get_ifd_dict(subifd_offset), subifd_offset) + for subifd_offset in subifd_offsets + ] ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset): assert exif._info is not None diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index d958a459277..1438827cabb 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -544,11 +544,11 @@ def write_script( def get_footer(dep: dict[str, Any]) -> list[str]: lines = [] for out in dep.get("headers", []): - lines.append(cmd_copy(out, "{inc_dir}")) + lines.append(cmd_copy(out, "{inc_dir}")) # noqa: PERF401 for out in dep.get("libs", []): - lines.append(cmd_copy(out, "{lib_dir}")) + lines.append(cmd_copy(out, "{lib_dir}")) # noqa: PERF401 for out in dep.get("bins", []): - lines.append(cmd_copy(out, "{bin_dir}")) + lines.append(cmd_copy(out, "{bin_dir}")) # noqa: PERF401 return lines From 9a358fa289e87b849d08ead61a6dccabf5961121 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:12:32 +0200 Subject: [PATCH 4/8] PERF402 and fixes --- Tests/test_file_container.py | 4 +--- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_container.py b/Tests/test_file_container.py index 597ab508342..c73f2a40cba 100644 --- a/Tests/test_file_container.py +++ b/Tests/test_file_container.py @@ -179,9 +179,7 @@ def test_iter(bytesmode: bool) -> None: container = ContainerIO.ContainerIO(fh, 0, 120) # Act - data = [] - for line in container: - data.append(line) + data = list(container) # Assert if bytesmode: diff --git a/pyproject.toml b/pyproject.toml index ea43e9cb1cc..149dbbac5dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,6 +149,7 @@ lint.select = [ "PERF101", # perflint: unnecessary-list-cast "PERF102", # perflint: incorrect-dict-iterator "PERF401", # perflint: manual-list-comprehension + "PERF402", # perflint: manual-list-copy "PGH", # pygrep-hooks "PIE", # flake8-pie "PT", # flake8-pytest-style From 090ca9461b1ce0e0f91644f6cc7a1d1416e7b915 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:20:06 +0200 Subject: [PATCH 5/8] PERF403 and fixes --- pyproject.toml | 1 + src/PIL/IptcImagePlugin.py | 11 ++--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 149dbbac5dd..bda99c3bf43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -150,6 +150,7 @@ lint.select = [ "PERF102", # perflint: incorrect-dict-iterator "PERF401", # perflint: manual-list-comprehension "PERF402", # perflint: manual-list-copy + "PERF403", # perflint: manual-dict-comprehension "PGH", # pygrep-hooks "PIE", # flake8-pie "PT", # flake8-pytest-style diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index 6fc824e4caa..9c8be8b4e36 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -185,13 +185,9 @@ def getiptcinfo( data = None - info: dict[tuple[int, int], bytes | list[bytes]] = {} if isinstance(im, IptcImageFile): # return info dictionary right away - for k, v in im.info.items(): - if isinstance(k, tuple): - info[k] = v - return info + return {k: v for k, v in im.info.items() if isinstance(k, tuple)} elif isinstance(im, JpegImagePlugin.JpegImageFile): # extract the IPTC/NAA resource @@ -227,7 +223,4 @@ class FakeImage: except (IndexError, KeyError): pass # expected failure - for k, v in iptc_im.info.items(): - if isinstance(k, tuple): - info[k] = v - return info + return {k: v for k, v in iptc_im.info.items() if isinstance(k, tuple)} From 754c7ea3a0aa47429809a1675f249263de3eac7b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:28:11 +0200 Subject: [PATCH 6/8] PERF203 and fixes --- Tests/test_bmp_reference.py | 4 ++-- Tests/test_file_libtiff.py | 5 +---- pyproject.toml | 36 ++++++++++++++++-------------------- setup.py | 2 +- src/PIL/GifImagePlugin.py | 2 +- src/PIL/Image.py | 2 +- src/PIL/ImageFont.py | 2 +- src/PIL/ImagePalette.py | 6 ++---- src/PIL/JpegImagePlugin.py | 4 ++-- src/PIL/PcfFontFile.py | 2 +- src/PIL/PngImagePlugin.py | 2 +- 11 files changed, 29 insertions(+), 38 deletions(-) diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 8fbd737484d..ea0853100cc 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -56,7 +56,7 @@ def test_questionable() -> None: im.load() if os.path.basename(f) not in supported: print(f"Please add {f} to the partially supported bmp specs.") - except Exception: # as msg: + except Exception: # noqa: PERF203 if os.path.basename(f) in supported: raise @@ -106,7 +106,7 @@ def get_compare(f: str) -> str: assert_image_similar(im_converted, compare_converted, 5) - except Exception as msg: + except Exception as msg: # noqa: PERF203 # there are three here that are unsupported: unsupported = ( os.path.join(base, "g", "rgb32bf.bmp"), diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index b453e3aa5c5..6f20900e490 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -224,10 +224,7 @@ def test_additional_metadata( with Image.open("Tests/images/hopper_g4.tif") as im: assert isinstance(im, TiffImagePlugin.TiffImageFile) for tag in im.tag_v2: - try: - del core_items[tag] - except KeyError: - pass + core_items.pop(tag, None) del core_items[320] # colormap is special, tested below # Type codes: diff --git a/pyproject.toml b/pyproject.toml index bda99c3bf43..7eb9a3fbdde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,26 +139,22 @@ exclude = "wheels/multibuild" exclude = [ "wheels/multibuild" ] fix = true lint.select = [ - "C4", # flake8-comprehensions - "E", # pycodestyle errors - "EM", # flake8-errmsg - "F", # pyflakes errors - "I", # isort - "ISC", # flake8-implicit-str-concat - "LOG", # flake8-logging - "PERF101", # perflint: unnecessary-list-cast - "PERF102", # perflint: incorrect-dict-iterator - "PERF401", # perflint: manual-list-comprehension - "PERF402", # perflint: manual-list-copy - "PERF403", # perflint: manual-dict-comprehension - "PGH", # pygrep-hooks - "PIE", # flake8-pie - "PT", # flake8-pytest-style - "PYI", # flake8-pyi - "RUF100", # unused noqa (yesqa) - "UP", # pyupgrade - "W", # pycodestyle warnings - "YTT", # flake8-2020 + "C4", # flake8-comprehensions + "E", # pycodestyle errors + "EM", # flake8-errmsg + "F", # pyflakes errors + "I", # isort + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PERF", # perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RUF100", # unused noqa (yesqa) + "UP", # pyupgrade + "W", # pycodestyle warnings + "YTT", # flake8-2020 ] lint.ignore = [ "E203", # Whitespace before ':' diff --git a/setup.py b/setup.py index 175aed25a02..496c8cb1f0e 100644 --- a/setup.py +++ b/setup.py @@ -302,7 +302,7 @@ def _pkg_config(name: str) -> tuple[list[str], list[str]] | None: subprocess.check_output(command_cflags).decode("utf8").strip(), )[::2][1:] return libs, cflags - except Exception: + except Exception: # noqa: PERF203 pass return None diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 390b3b374ab..1ffb18b9c8f 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -167,7 +167,7 @@ def seek(self, frame: int) -> None: for f in range(self.__frame + 1, frame + 1): try: self._seek(f) - except EOFError as e: + except EOFError as e: # noqa: PERF203 self.seek(last_frame) msg = "no more images in GIF file" raise EOFError(msg) from e diff --git a/src/PIL/Image.py b/src/PIL/Image.py index bde335504e8..6062857da06 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -488,7 +488,7 @@ def init() -> bool: try: logger.debug("Importing %s", plugin) __import__(f"{__spec__.parent}.{plugin}", globals(), locals(), []) - except ImportError as e: + except ImportError as e: # noqa: PERF203 logger.debug("Image: failed to import %s: %s", plugin, e) if OPEN or SAVE: diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index ea7f4dc5477..ec7c7cb0813 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -930,7 +930,7 @@ def load_path(filename: str | bytes) -> ImageFont: for directory in sys.path: try: return load(os.path.join(directory, filename)) - except OSError: + except OSError: # noqa: PERF203 pass msg = f'cannot find font file "{filename}" in sys.path' if os.path.exists(filename): diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 99ad2771b4b..2abbd46eaf1 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -198,13 +198,11 @@ def save(self, fp: str | IO[str]) -> None: try: fp.write("# Palette\n") fp.write(f"# Mode: {self.mode}\n") + palette_len = len(self.palette) for i in range(256): fp.write(f"{i}") for j in range(i * len(self.mode), (i + 1) * len(self.mode)): - try: - fp.write(f" {self.palette[j]}") - except IndexError: - fp.write(" 0") + fp.write(f" {self.palette[j] if j < palette_len else 0}") fp.write("\n") finally: if open_fp: diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 2f11cbfe313..d5b67bba56c 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -153,7 +153,7 @@ def APP(self: JpegImageFile, marker: int) -> None: photoshop[code] = data offset += size offset += offset & 1 # align - except struct.error: + except struct.error: # noqa: PERF203 break # insufficient data elif marker == 0xFFEE and s.startswith(b"Adobe"): @@ -744,7 +744,7 @@ def validate_qtables( msg = "Invalid quantization table" raise TypeError(msg) table_array = array.array("H", table) - except TypeError as e: + except TypeError as e: # noqa: PERF203 msg = "Invalid quantization table" raise ValueError(msg) from e else: diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index a00e9b91984..b923293b06a 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -251,7 +251,7 @@ def _load_encoding(self) -> list[int | None]: ] if encoding_offset != 0xFFFF: encoding[i] = encoding_offset - except UnicodeDecodeError: + except UnicodeDecodeError: # noqa: PERF203 # character is not supported in selected encoding pass diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 4e082a293ff..d58426c5511 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -869,7 +869,7 @@ def seek(self, frame: int) -> None: for f in range(self.__frame + 1, frame + 1): try: self._seek(f) - except EOFError as e: + except EOFError as e: # noqa: PERF203 self.seek(last_frame) msg = "no more images in APNG file" raise EOFError(msg) from e From 9a7b91e5dbb6630ea4e3d5d6eccbf48f4463eda4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 Mar 2026 15:11:04 +1100 Subject: [PATCH 7/8] PERF203 fixes --- src/PIL/GifImagePlugin.py | 12 ++++++------ src/PIL/JpegImagePlugin.py | 22 ++++++++++------------ src/PIL/PngImagePlugin.py | 12 ++++++------ 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 1ffb18b9c8f..b8db5d83284 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -164,13 +164,13 @@ def seek(self, frame: int) -> None: self._seek(0) last_frame = self.__frame - for f in range(self.__frame + 1, frame + 1): - try: + try: + for f in range(self.__frame + 1, frame + 1): self._seek(f) - except EOFError as e: # noqa: PERF203 - self.seek(last_frame) - msg = "no more images in GIF file" - raise EOFError(msg) from e + except EOFError as e: + self.seek(last_frame) + msg = "no more images in GIF file" + raise EOFError(msg) from e def _seek(self, frame: int, update_image: bool = True) -> None: if isinstance(self._fp, DeferredError): diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index d5b67bba56c..46320eb3b5b 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -127,8 +127,8 @@ def APP(self: JpegImageFile, marker: int) -> None: # parse the image resource block offset = 14 photoshop = self.info.setdefault("photoshop", {}) - while s[offset : offset + 4] == b"8BIM": - try: + try: + while s[offset : offset + 4] == b"8BIM": offset += 4 # resource code code = i16(s, offset) @@ -153,8 +153,8 @@ def APP(self: JpegImageFile, marker: int) -> None: photoshop[code] = data offset += size offset += offset & 1 # align - except struct.error: # noqa: PERF203 - break # insufficient data + except struct.error: + pass # insufficient data elif marker == 0xFFEE and s.startswith(b"Adobe"): self.info["adobe"] = i16(s, 5) @@ -738,17 +738,15 @@ def validate_qtables( if not (0 < len(qtables) < 5): msg = "None or too many quantization tables" raise ValueError(msg) - for idx, table in enumerate(qtables): - try: + try: + for idx, table in enumerate(qtables): if len(table) != 64: msg = "Invalid quantization table" raise TypeError(msg) - table_array = array.array("H", table) - except TypeError as e: # noqa: PERF203 - msg = "Invalid quantization table" - raise ValueError(msg) from e - else: - qtables[idx] = list(table_array) + qtables[idx] = list(array.array("H", table)) + except TypeError as e: + msg = "Invalid quantization table" + raise ValueError(msg) from e return qtables if qtables == "keep": diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index d58426c5511..76a15bd0dc6 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -866,13 +866,13 @@ def seek(self, frame: int) -> None: self._seek(0, True) last_frame = self.__frame - for f in range(self.__frame + 1, frame + 1): - try: + try: + for f in range(self.__frame + 1, frame + 1): self._seek(f) - except EOFError as e: # noqa: PERF203 - self.seek(last_frame) - msg = "no more images in APNG file" - raise EOFError(msg) from e + except EOFError as e: + self.seek(last_frame) + msg = "no more images in APNG file" + raise EOFError(msg) from e def _seek(self, frame: int, rewind: bool = False) -> None: assert self.png is not None From 701b49adc5d3f64dfb9eb2c42f0411b3d8490fef Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 Mar 2026 15:13:26 +1100 Subject: [PATCH 8/8] PERF401 fix --- winbuild/build_prepare.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 1438827cabb..466cca17696 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -542,14 +542,11 @@ def write_script( def get_footer(dep: dict[str, Any]) -> list[str]: - lines = [] - for out in dep.get("headers", []): - lines.append(cmd_copy(out, "{inc_dir}")) # noqa: PERF401 - for out in dep.get("libs", []): - lines.append(cmd_copy(out, "{lib_dir}")) # noqa: PERF401 - for out in dep.get("bins", []): - lines.append(cmd_copy(out, "{bin_dir}")) # noqa: PERF401 - return lines + return ( + [cmd_copy(out, "{inc_dir}") for out in dep.get("headers", [])] + + [cmd_copy(out, "{lib_dir}") for out in dep.get("libs", [])] + + [cmd_copy(out, "{bin_dir}") for out in dep.get("bins", [])] + ) def build_env(prefs: dict[str, str], verbose: bool) -> None: