diff -pruN 7.20251223/Makefile 7.20251225/Makefile
--- 7.20251223/Makefile	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/Makefile	2025-12-25 15:48:54.000000000 +0000
@@ -48,11 +48,6 @@ manpages: $(MANPAGES)
 dist_fallback:
 	make -C pydist $@
 
-# TESTS
-nose:
-	#nosetests3 --verbose --with-doctest --with-coverage
-	nose2-3 --verbose --plugin nose2.plugins.doctests --with-doctest
-
 mypy:
 	mypy --strict dh_python3 dhpython/ tests/*.py
 	mypy --strict pybuild
@@ -60,6 +55,14 @@ mypy:
 black:
 	black $(BLACK_ARGS) pybuild dh_python3 dhpython/ pydist/*.py tests/*.py
 
+pyupgrade:
+	pyupgrade --py311-plus dh_python3 pybuild pydist/*.py tests/*.py $(shell find dhpython -name *.py)
+
+# TESTS
+nose:
+	#nosetests3 --verbose --with-doctest --with-coverage
+	nose2-3 --verbose --plugin nose2.plugins.doctests --with-doctest
+
 tests: nose
 	make -C tests
 
diff -pruN 7.20251223/debian/changelog 7.20251225/debian/changelog
--- 7.20251223/debian/changelog	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/debian/changelog	2025-12-25 15:48:54.000000000 +0000
@@ -1,3 +1,19 @@
+dh-python (7.20251225) unstable; urgency=medium
+
+  [ Stefano Rivera ]
+  * Configure a reproducible build-dir for meson pyproject builds.
+    (Closes: #1113976)
+  * pybuild: Allow --name's effect on --dest-dir to be overridden
+  * Compare absolute paths in pybuild when deciding to skip passing destdir
+  * Use Python 3.11 compatible alias syntax (pypy3 is still 3.11)
+  * pyupgrade --py311-plus
+
+  [ Alexandre Detiste ]
+  * drop conditional dependency on python3-tomli backport
+  * trim dead code for Python < 3.11
+
+ -- Stefano Rivera <stefanor@debian.org>  Thu, 25 Dec 2025 11:48:54 -0400
+
 dh-python (7.20251223) unstable; urgency=medium
 
   * Bump major-version, post-trixie (a little late).
diff -pruN 7.20251223/debian/control 7.20251225/debian/control
--- 7.20251223/debian/control	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/debian/control	2025-12-25 15:48:54.000000000 +0000
@@ -51,7 +51,6 @@ Multi-Arch: foreign
 Depends:
  dh-python (= ${source:Version}),
  python3-build (>> 0.7~),
- python3 (>> 3.11~) | python3-tomli,
  python3-installer,
  ${misc:Depends},
 Description: Debian helper tools for packaging Python libraries using PEP517
diff -pruN 7.20251223/dh/pybuild.pm 7.20251225/dh/pybuild.pm
--- 7.20251223/dh/pybuild.pm	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dh/pybuild.pm	2025-12-25 15:48:54.000000000 +0000
@@ -8,6 +8,8 @@
 package Debian::Debhelper::Buildsystem::pybuild;
 
 use strict;
+use Cwd;
+use File::Spec;
 use Dpkg::Control;
 use Dpkg::Changelog::Debian;
 use Debian::Debhelper::Dh_Lib qw(%dh error doit);
@@ -115,7 +117,7 @@ sub pybuild_commands {
 	# Pass dh's destdir, if it's not the default value
 	if ($step eq 'install') {
 		my $destdir = shift(@options);
-		if ($destdir ne "debian/tmp") {
+		if (File::Spec->rel2abs($destdir, getcwd()) ne File::Spec->rel2abs("debian/tmp", getcwd())) {
 			push @options, "--dest-dir=$destdir";
 		}
 	}
diff -pruN 7.20251223/dh_python3 7.20251225/dh_python3
--- 7.20251223/dh_python3	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dh_python3	2025-12-25 15:48:54.000000000 +0000
@@ -62,7 +62,7 @@ class Scanner(Scan):
         tagver = m.groupdict()["ver"]
         if tagver is None:
             return None
-        tagver = Version("%s.%s" % (tagver[0], tagver[1:]))
+        tagver = Version(f"{tagver[0]}.{tagver[1:]}")
         return tagver
 
 
diff -pruN 7.20251223/dhpython/_defaults.py 7.20251225/dhpython/_defaults.py
--- 7.20251223/dhpython/_defaults.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/_defaults.py	2025-12-25 15:48:54.000000000 +0000
@@ -53,11 +53,11 @@ def cpython_versions(major: int) -> CPyt
     default_ver: tuple[int, int] | None = None
     supported_vers: tuple[tuple[int, int], ...] = ()
     ver = str(major)
-    supported = environ.get("DEBPYTHON{}_SUPPORTED".format(ver))
-    default = environ.get("DEBPYTHON{}_DEFAULT".format(ver))
+    supported = environ.get(f"DEBPYTHON{ver}_SUPPORTED")
+    default = environ.get(f"DEBPYTHON{ver}_DEFAULT")
     if not supported or not default:
         config = ConfigParser()
-        config.read("/usr/share/python{}/debian_defaults".format(ver))
+        config.read(f"/usr/share/python{ver}/debian_defaults")
         if not default:
             default = config.get("DEFAULT", "default-version", fallback="")[6:]
         if not supported:
@@ -83,7 +83,7 @@ def cpython_versions(major: int) -> CPyt
 def from_file(fpath: str) -> None:
     if not exists(fpath):
         raise ValueError("missing interpreter: %s" % fpath)
-    command = "{} --version".format(fpath)
+    command = f"{fpath} --version"
     with Popen(command, shell=True, stdout=PIPE, encoding="utf-8") as process:
         stdout, _ = process.communicate()
 
@@ -106,4 +106,4 @@ if __name__ == "__main__":
     if sys.argv[1] == "default":
         print(".".join(str(i) for i in DEFAULT[sys.argv[2]]))
     elif sys.argv[1] == "supported":
-        print(",".join((".".join(str(i) for i in v) for v in SUPPORTED[sys.argv[2]])))
+        print(",".join(".".join(str(i) for i in v) for v in SUPPORTED[sys.argv[2]]))
diff -pruN 7.20251223/dhpython/build/base.py 7.20251225/dhpython/build/base.py
--- 7.20251223/dhpython/build/base.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/build/base.py	2025-12-25 15:48:54.000000000 +0000
@@ -20,6 +20,7 @@
 
 import logging
 from argparse import Namespace
+from collections.abc import Callable
 from functools import wraps
 from glob import glob1
 from os import environ, remove, walk, makedirs
@@ -30,7 +31,6 @@ from shutil import rmtree, copyfile, cop
 from typing import (
     TYPE_CHECKING,
     Any,
-    Callable,
     ClassVar,
     Concatenate,
     Literal,
@@ -81,7 +81,7 @@ def copy_test_files(
                 tpl = tpl.format(i=args['interpreter'].name,
                                  v=args['version'],
                                  m=args['version'].major)
-                fpath = join(args['dir'], 'debian/pybuild{}.testfiles'.format(tpl))
+                fpath = join(args['dir'], f'debian/pybuild{tpl}.testfiles')
                 if exists(fpath):
                     with open(fpath, encoding='utf-8') as fp:
                         # overwrite files_to_copy if .testfiles file found
@@ -227,9 +227,9 @@ class Base:
 
         dh = DebHelper(build_options())
         # Plugins that rely on repository contents to build MANIFEST
-        clean_sources_txt = not set(
-            ('python3-setuptools-scm', 'python3-setuptools-git')
-        ).intersection(set(dh.build_depends))
+        clean_sources_txt = not {
+            'python3-setuptools-scm', 'python3-setuptools-git'
+        }.intersection(set(dh.build_depends))
 
         for root, dirs, file_names in walk(context['dir']):
             for name in dirs[:]:
@@ -430,7 +430,7 @@ def shell_command(
 
         log_file: str | Literal[False]
         if self.cfg.quiet:
-            log_file = join(args['home_dir'], '{}_cmd.log'.format(func.__name__))
+            log_file = join(args['home_dir'], f'{func.__name__}_cmd.log')
         else:
             log_file = False
 
diff -pruN 7.20251223/dhpython/build/plugin_distutils.py 7.20251225/dhpython/build/plugin_distutils.py
--- 7.20251223/dhpython/build/plugin_distutils.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/build/plugin_distutils.py	2025-12-25 15:48:54.000000000 +0000
@@ -19,6 +19,7 @@
 # THE SOFTWARE.
 
 import logging
+from collections.abc import Callable
 from functools import wraps
 from glob import glob1
 from os import remove
@@ -26,7 +27,6 @@ from os.path import exists, isdir, join
 from shutil import rmtree, move
 from typing import (
     TYPE_CHECKING,
-    Callable,
     Concatenate,
     Literal,
     ParamSpec,
diff -pruN 7.20251223/dhpython/build/plugin_pyproject.py 7.20251225/dhpython/build/plugin_pyproject.py
--- 7.20251223/dhpython/build/plugin_pyproject.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/build/plugin_pyproject.py	2025-12-25 15:48:54.000000000 +0000
@@ -22,19 +22,14 @@
 
 from contextlib import contextmanager
 from pathlib import Path
-from typing import Iterator, override
+from typing import override
+from collections.abc import Iterator
 import logging
 import os.path as osp
 import shutil
 import sysconfig
-try:
-    import tomllib
-except ModuleNotFoundError:
-    try:
-        import tomli as tomllib  # type: ignore
-    except ModuleNotFoundError:
-        # Plugin still works, only needed for autodetection
-        pass
+import tomllib
+
 try:
     from installer import install
     from installer.destinations import SchemeDictionaryDestination
@@ -119,11 +114,14 @@ class BuildSystem(Base):
         self.build_wheel(context, args)
         self.unpack_wheel(context, args)
 
-    def _backend_config_settings(self) -> list[str]:
+    def _backend_config_settings(self, args: Args) -> list[str]:
         backend = _build_backend()
         if backend == "mesonpy":
             arch_data = dpkg_architecture()
             return [
+                # build_dir is where we unpack the wheel, so we need to build
+                # elsewhere.
+                f"build-dir={args['home_dir']}/meson-build",
                 "compile-args=--verbose",
                 # From Debhelper's meson.pm
                 "setup-args=--wrap-mode=nodownload",
@@ -141,7 +139,7 @@ class BuildSystem(Base):
         """ build a wheel using the PEP517 builder defined by upstream """
         log.info('Building wheel for %s with "build" module',
                  args['interpreter'])
-        config_settings = self._backend_config_settings()
+        config_settings = self._backend_config_settings(args)
         context['ENV']['FLIT_NO_NETWORK'] = '1'
         context['ENV']['HOME'] = args['home_dir']
         return ('{interpreter} -m build '
diff -pruN 7.20251223/dhpython/debhelper.py 7.20251225/dhpython/debhelper.py
--- 7.20251223/dhpython/debhelper.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/debhelper.py	2025-12-25 15:48:54.000000000 +0000
@@ -24,7 +24,7 @@ import re
 from os import makedirs, chmod, environ
 from os.path import basename, exists, join, dirname
 from sys import argv
-from typing import NamedTuple, TypedDict
+from typing import NamedTuple, TypeAlias, TypedDict
 
 from dhpython import DEPENDS_SUBSTVARS, PKG_NAME_TPLS, RT_LOCATIONS, RT_TPLS
 
@@ -77,7 +77,7 @@ def build_options(
     )
 
 
-type BD = dict[str, dict[str | None, str]]
+BD: TypeAlias = dict[str, dict[str | None, str]]
 
 
 class DebHelper:
@@ -107,7 +107,7 @@ class DebHelper:
         skip_pkgs = options.no_package
 
         try:
-            with open("debian/control", "r", encoding="utf-8") as fp:
+            with open("debian/control", encoding="utf-8") as fp:
                 paragraphs: list[dict[str, str]] = [{}]
                 field = None
                 for lineno, line in enumerate(fp, 1):
@@ -130,8 +130,10 @@ class DebHelper:
                     field, value = line.split(":", 1)
                     field = field.lower()
                     paragraphs[-1][field] = value.strip()
-        except IOError:
-            raise Exception("cannot find debian/control file")
+        except OSError as e:
+            if e.errno == errno.ENOENT:
+                raise Exception("cannot find debian/control file")
+            raise
 
         # Trailing new lines?
         if not paragraphs[-1]:
@@ -225,11 +227,11 @@ class DebHelper:
 
     def has_acted_on_package(self, package: str) -> bool:
         try:
-            with open("debian/{}.debhelper.log".format(package), encoding="utf-8") as f:
+            with open(f"debian/{package}.debhelper.log", encoding="utf-8") as f:
                 for line in f:
                     if line.strip() == self.command:
                         return True
-        except IOError as e:
+        except OSError as e:
             if e.errno != errno.ENOENT:
                 raise
         return False
@@ -254,9 +256,9 @@ class DebHelper:
                 continue
 
             for when, templates in autoscripts.items():
-                fn = "debian/%s.%s.debhelper" % (package, when)
+                fn = f"debian/{package}.{when}.debhelper"
                 if exists(fn):
-                    with open(fn, "r", encoding="utf-8") as datafile:
+                    with open(fn, encoding="utf-8") as datafile:
                         data = datafile.read()
                 else:
                     data = ""
@@ -270,7 +272,7 @@ class DebHelper:
                         )
                         if not exists(fpath):
                             fpath = "/usr/share/debhelper/autoscripts/%s" % tpl_name
-                        with open(fpath, "r", encoding="utf-8") as tplfile:
+                        with open(fpath, encoding="utf-8") as tplfile:
                             tpl = tplfile.read()
                         if self.options.compile_all and args:
                             # TODO: should args be checked to contain dir name?
@@ -279,14 +281,15 @@ class DebHelper:
                             tpl = tpl.replace("#PACKAGE#", package)
                         else:
                             arch = environ["DEB_HOST_ARCH"]
-                            tpl = tpl.replace("#PACKAGE#", "%s:%s" % (package, arch))
+                            tpl = tpl.replace("#PACKAGE#", f"{package}:{arch}")
                         tpl = tpl.replace("#ARGS#", i)
                         if tpl not in data and tpl not in new_data:
                             new_data += "\n%s" % tpl
                 if new_data:
-                    data += "\n# Automatically added by {}".format(
-                        basename(argv[0])
-                    ) + "{}\n# End automatically added section\n".format(new_data)
+                    data += (
+                        f"\n# Automatically added by {basename(argv[0])}"
+                        + f"{new_data}\n# End automatically added section\n"
+                    )
                     with open(fn, "w", encoding="utf-8") as fp:
                         fp.write(data)
 
@@ -297,7 +300,7 @@ class DebHelper:
                 continue
             fn = "debian/%s.substvars" % package
             if exists(fn):
-                with open(fn, "r", encoding="utf-8") as datafile:
+                with open(fn, encoding="utf-8") as datafile:
                     data = datafile.read()
             else:
                 data = ""
@@ -308,7 +311,7 @@ class DebHelper:
                     line = data[p + len("%s=" % name) : p + e if e > -1 else None]
                     items = [i.strip() for i in line.split(",") if i]
                     if e > -1 and data[p + e :].strip():
-                        data = "%s\n%s" % (data[:p], data[p + e :])
+                        data = f"{data[:p]}\n{data[p + e :]}"
                     else:
                         data = data[:p]
                 else:
@@ -319,7 +322,7 @@ class DebHelper:
                 if items:
                     if data:
                         data += "\n"
-                    data += "%s=%s\n" % (name, ", ".join(items))
+                    data += "{}={}\n".format(name, ", ".join(items))
             data = data.replace("\n\n", "\n")
             if data:
                 with open(fn, "w", encoding="utf-8") as fp:
@@ -331,12 +334,12 @@ class DebHelper:
             values = settings.get("rtupdates")
             if not values:
                 continue
-            d = "debian/{}/{}".format(package, RT_LOCATIONS[self.impl])
+            d = f"debian/{package}/{RT_LOCATIONS[self.impl]}"
             if not exists(d):
                 makedirs(d)
-            fn = "%s/%s.rtupdate" % (d, package)
+            fn = f"{d}/{package}.rtupdate"
             if exists(fn):
-                with open(fn, "r", encoding="utf-8") as fp:
+                with open(fn, encoding="utf-8") as fp:
                     data = fp.read()
             else:
                 data = "#! /bin/sh\nset -e"
@@ -353,9 +356,7 @@ class DebHelper:
         if not self.options.write_log:
             return
         for package, _ in self.packages.items():
-            with open(
-                "debian/{}.debhelper.log".format(package), "a", encoding="utf-8"
-            ) as f:
+            with open(f"debian/{package}.debhelper.log", "a", encoding="utf-8") as f:
                 f.write(self.command + "\n")
 
     def save(self) -> None:
diff -pruN 7.20251223/dhpython/depends.py 7.20251225/dhpython/depends.py
--- 7.20251223/dhpython/depends.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/depends.py	2025-12-25 15:48:54.000000000 +0000
@@ -90,20 +90,20 @@ class Dependencies:
         """Fill in debhelper's substvars."""
         prefix = PKG_PREFIX_MAP.get(self.impl, "misc")
         for i in sorted(self.depends):
-            dh.addsubstvar(self.package, "{}:Depends".format(prefix), i)
+            dh.addsubstvar(self.package, f"{prefix}:Depends", i)
         for i in sorted(self.recommends):
-            dh.addsubstvar(self.package, "{}:Recommends".format(prefix), i)
+            dh.addsubstvar(self.package, f"{prefix}:Recommends", i)
         for i in sorted(self.suggests):
-            dh.addsubstvar(self.package, "{}:Suggests".format(prefix), i)
+            dh.addsubstvar(self.package, f"{prefix}:Suggests", i)
         for i in sorted(self.enhances):
-            dh.addsubstvar(self.package, "{}:Enhances".format(prefix), i)
+            dh.addsubstvar(self.package, f"{prefix}:Enhances", i)
         for i in sorted(self.breaks):
-            dh.addsubstvar(self.package, "{}:Breaks".format(prefix), i)
+            dh.addsubstvar(self.package, f"{prefix}:Breaks", i)
         for script in sorted(self.rtscripts):
             dh.add_rtupdate(self.package, script)
 
     def __str__(self) -> str:
-        return "D=%s; R=%s; S=%s; E=%s, B=%s; RT=%s" % (
+        return "D={}; R={}; S={}; E={}, B={}; RT={}".format(
             self.depends,
             self.recommends,
             self.suggests,
@@ -173,17 +173,17 @@ class Dependencies:
                 self.depend(vtpl % minv)
                 minv = maxv = None
             if minv:
-                self.depend("%s (>= %s~)" % (tpl_tmp, minv))
+                self.depend(f"{tpl_tmp} (>= {minv}~)")
             if maxv:
-                self.depend("%s (<< %s)" % (tpl_tmp, maxv))
+                self.depend(f"{tpl_tmp} (<< {maxv})")
 
         if stats["ext_vers"]:
             sorted_vers = sorted(stats["ext_vers"])
             minv = sorted_vers[0]
             maxv = sorted_vers[-1]
             # self.depend('|'.join(vtpl % i for i in stats['ext_vers']))
-            self.depend("%s (>= %s~)" % (tpl, minv))
-            self.depend("%s (<< %s)" % (tpl, maxv + 1))
+            self.depend(f"{tpl} (>= {minv}~)")
+            self.depend(f"{tpl} (<< {maxv + 1})")
         elif stats["ext_stableabi"]:
             self.depend("%s" % tpl)
 
@@ -225,12 +225,12 @@ class Dependencies:
                 extensions = sorted(details.get("ext_vers", set()))
                 # self.depend('|'.join(vtpl % i for i in extensions))
                 if extensions:
-                    self.depend("%s (>= %s~)" % (tpl, extensions[0]))
-                    self.depend("%s (<< %s)" % (tpl, extensions[-1] + 1))
+                    self.depend(f"{tpl} (>= {extensions[0]}~)")
+                    self.depend(f"{tpl} (<< {extensions[-1] + 1})")
                 elif details.get("ext_no_version"):
                     # assume unrecognized extension was built for default interpreter version
-                    self.depend("%s (>= %s~)" % (tpl, default_version))
-                    self.depend("%s (<< %s)" % (tpl, default_version + 1))
+                    self.depend(f"{tpl} (>= {default_version}~)")
+                    self.depend(f"{tpl} (<< {default_version + 1})")
 
             if details.get("compile"):
                 if self.impl in MINPYCDEP:
@@ -253,9 +253,9 @@ class Dependencies:
                         self.depend(vtpl % vrange.minver)
                     else:
                         if vrange.minver:  # minimum version specified
-                            self.depend("%s (>= %s~)" % (tpl_ma, vrange.minver))
+                            self.depend(f"{tpl_ma} (>= {vrange.minver}~)")
                         if vrange.maxver:  # maximum version specified
-                            self.depend("%s (<< %s)" % (tpl_ma, vrange.maxver + 1))
+                            self.depend(f"{tpl_ma} (<< {vrange.maxver + 1})")
 
                 for regex in options.regexpr or []:
                     args += " -X '%s'" % regex.pattern.replace("'", r"'\''")
@@ -280,7 +280,7 @@ class Dependencies:
                     parse_pydep(self.impl, fn, bdep=self.bdep, **section_options)
                 )
             for fpath in stats["egg-info"]:
-                with open(fpath, "r", encoding="utf-8") as fp:
+                with open(fpath, encoding="utf-8") as fp:
                     for line in fp:
                         if line.startswith("Requires: "):
                             req = line[10:].strip()
diff -pruN 7.20251223/dhpython/fs.py 7.20251225/dhpython/fs.py
--- 7.20251223/dhpython/fs.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/fs.py	2025-12-25 15:48:54.000000000 +0000
@@ -24,12 +24,13 @@ import os
 import re
 import sys
 from argparse import Namespace
+from collections.abc import Iterable, Sequence
 from filecmp import cmp as cmpfile
 from os.path import lexists, exists, isdir, islink, join, realpath, split, splitext
 from pathlib import Path
 from shutil import rmtree
 from stat import ST_MODE, S_IXUSR, S_IXGRP, S_IXOTH
-from typing import Iterable, Literal, Sequence, TypedDict
+from typing import Literal, TypedDict
 
 from dhpython import MULTIARCH_DIR_TPL
 from dhpython.tools import fix_shebang, clean_egg_name
@@ -180,7 +181,7 @@ def merge_WHEEL(src: str | Path, dst: st
     """
     log.debug("Merging WHEEL file %s into %s", src, dst)
     missing = missing_lines(src, dst)
-    with open(dst, "at", encoding="UTF-8") as fh:
+    with open(dst, "a", encoding="UTF-8") as fh:
         for line in missing:
             if line.startswith("Tag: "):
                 fh.write(line)
diff -pruN 7.20251223/dhpython/interpreter.py 7.20251225/dhpython/interpreter.py
--- 7.20251223/dhpython/interpreter.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/interpreter.py	2025-12-25 15:48:54.000000000 +0000
@@ -189,18 +189,18 @@ class Interpreter:
         if consider_default_ver and (not version or version == self.default_version):
             version = "3"
         if self.debug:
-            return "python{}-dbg".format(version)
+            return f"python{version}-dbg"
         return self.name + str(version)
 
     def binary(self, version: "Version | None" = None) -> str:
-        return "{}{}".format(self.path, self._vstr(version))
+        return f"{self.path}{self._vstr(version)}"
 
     @property
     def binary_dv(self) -> str:
         """Like binary(), but returns path to default intepreter symlink
         if version matches default one for given implementation.
         """
-        return "{}{}".format(self.path, self._vstr(consider_default_ver=True))
+        return f"{self.path}{self._vstr(consider_default_ver=True)}"
 
     @property
     def default_version(self) -> "Version | None":
@@ -285,7 +285,7 @@ class Interpreter:
         if gdb:
             path = "/usr/lib/debug%s" % path
         if package:
-            path = "debian/%s%s" % (package, path)
+            path = f"debian/{package}{path}"
 
         return path
 
@@ -310,13 +310,11 @@ class Interpreter:
                     result.append(res)
 
         if gdb:
-            result = ["/usr/lib/debug{}".format(i) for i in result]
+            result = [f"/usr/lib/debug{i}" for i in result]
             if self.impl.startswith("cpython"):
-                result.append(
-                    "/usr/lib/debug/usr/lib/pyshared/python{}".format(version)
-                )
+                result.append(f"/usr/lib/debug/usr/lib/pyshared/python{version}")
         if package:
-            result = ["debian/{}{}".format(package, i) for i in result]
+            result = [f"debian/{package}{i}" for i in result]
 
         return result
 
@@ -338,7 +336,7 @@ class Interpreter:
         cache_key = f"should_ignore_{self.impl}"
         if cache_key not in self.__class__._re_cache:
             expr = [v for k, v in INTERPRETER_DIR_TPLS.items() if k != self.impl]
-            regexp = re.compile("|".join("({})".format(i) for i in expr))
+            regexp = re.compile("|".join(f"({i})" for i in expr))
             self.__class__._re_cache[cache_key] = regexp
         else:
             regexp = self.__class__._re_cache[cache_key]
@@ -360,7 +358,7 @@ class Interpreter:
         except Exception:
             result = ""
             log.debug("cannot get include path", exc_info=True)
-        result = "/usr/include/{}".format(self.name)
+        result = f"/usr/include/{self.name}"
         version = self.version
         assert version
         if self.debug:
@@ -388,7 +386,7 @@ class Interpreter:
             ldlibrary = ldlibrary.replace(".a", ".so")
         if libpl and ldlibrary:
             return join(libpl, ldlibrary)
-        raise Exception("cannot find library file for {}".format(self))
+        raise Exception(f"cannot find library file for {self}")
 
     def check_extname(self, fname: str, version: "Version | None" = None) -> str | None:
         """Return extension file name if file can be renamed."""
@@ -408,7 +406,7 @@ class Interpreter:
         if info["ver"] and (not version or version.minor is None):
             # get version from soabi if version is not set of only major
             # version number is set
-            version = Version("%s.%s" % (info["ver"][0], info["ver"][1]))
+            version = Version("{}.{}".format(info["ver"][0], info["ver"][1]))
 
         if info["stableabi"] and version < Version("3.13"):
             # We added support for stableabi multiarch filenames in 3.13
@@ -463,7 +461,7 @@ class Interpreter:
         'python3-bar-dbg'
         """
         name = name.replace("_", "-")
-        result = "python3-{}".format(name)
+        result = f"python3-{name}"
         if self.debug:
             result += "-dbg"
         return result
@@ -491,7 +489,7 @@ class Interpreter:
         self, command: str, version: "str | Version | None" = None, cache: bool = True
     ) -> list[str] | str:
         version = Version(version or self.version)
-        exe = "{}{}".format(self.path, self._vstr(version))
+        exe = f"{self.path}{self._vstr(version)}"
         command = "{} -c '{}'".format(exe, command.replace("'", "'"))
         if cache and command in self.__class__._cmd_cache:
             return self.__class__._cmd_cache[command]
diff -pruN 7.20251223/dhpython/option.py 7.20251225/dhpython/option.py
--- 7.20251223/dhpython/option.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/option.py	2025-12-25 15:48:54.000000000 +0000
@@ -1,4 +1,3 @@
-# -*- coding: UTF-8 -*-
 # Copyright © 2010-2013 Piotr Ożarowski <piotr@debian.org>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
diff -pruN 7.20251223/dhpython/pydist.py 7.20251225/dhpython/pydist.py
--- 7.20251223/dhpython/pydist.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/pydist.py	2025-12-25 15:48:54.000000000 +0000
@@ -26,10 +26,11 @@ import os
 import re
 import subprocess
 from argparse import Namespace
+from collections.abc import Callable
 from enum import Enum, StrEnum, auto
 from functools import cache, partial
 from os.path import exists, isdir, join
-from typing import Callable, Literal, NamedTuple, TypedDict, cast
+from typing import Literal, NamedTuple, TypedDict, cast
 
 if __name__ == "__main__":
     import sys
@@ -190,7 +191,7 @@ def load(impl: str) -> dict[str, list[Py
         to_check.extend(join(dname, i) for i in os.listdir(dname))
 
     fbdir = os.environ.get("DH_PYTHON_DIST", "/usr/share/dh-python/dist/")
-    fbname = join(fbdir, "{}_fallback".format(impl))
+    fbname = join(fbdir, f"{impl}_fallback")
     if exists(fbname):  # fall back generated at dh-python build time
         to_check.append(fbname)  # last one!
 
@@ -203,7 +204,7 @@ def load(impl: str) -> dict[str, list[Py
                 if line.startswith("#") or not line:
                     continue
                 if not (m := PYDIST_RE.search(line)):
-                    raise Exception("invalid pydist line: %s (in %s)" % (line, fpath))
+                    raise Exception(f"invalid pydist line: {line} (in {fpath})")
                 data = m.groupdict()
                 dist = PyDist(
                     name=normalize_name(data["name"]),
@@ -645,7 +646,7 @@ def parse_pydep(
     section: str | None = None
     modification = RequirementModification(action=ModificationAction.KEEP)
     processed = []
-    with open(fname, "r", encoding="utf-8") as fp:
+    with open(fname, encoding="utf-8") as fp:
         for line in fp:
             line = line.strip()
             if not line or line.startswith("#"):
@@ -737,7 +738,7 @@ def parse_requires_dist(
     )
     result = NewDependencies(depends=[], recommends=[], suggests=[])
     section = None
-    with open(fname, "r", encoding="utf-8") as fp:
+    with open(fname, encoding="utf-8") as fp:
         metadata = email.message_from_string(fp.read())
     requires = metadata.get_all("Requires-Dist", [])
     result_key: Literal["depends", "recommends", "suggests"]
@@ -775,9 +776,7 @@ def sensible_pname(impl: str, dist_name:
 
 def ci_regexp(name: str) -> str:
     """Return case insensitive dpkg -S regexp."""
-    return "".join(
-        "[%s%s]" % (i.upper(), i) if i.isalpha() else i for i in name.lower()
-    )
+    return "".join(f"[{i.upper()}{i}]" if i.isalpha() else i for i in name.lower())
 
 
 PEP386_PRE_VER_RE = re.compile(r"[-.]?(alpha|beta|rc|dev|a|b|c)")
diff -pruN 7.20251223/dhpython/tools.py 7.20251225/dhpython/tools.py
--- 7.20251223/dhpython/tools.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/tools.py	2025-12-25 15:48:54.000000000 +0000
@@ -1,4 +1,3 @@
-# -*- coding: UTF-8 -*-
 # Copyright © 2010-2013 Piotr Ożarowski <piotr@debian.org>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -22,6 +21,7 @@
 import logging
 import os
 import re
+from collections.abc import Callable, Iterable
 from contextlib import ExitStack
 from datetime import datetime
 from functools import cache
@@ -30,7 +30,7 @@ from shutil import rmtree
 from os.path import exists, getsize, isdir, islink, join, split
 from pathlib import Path
 from subprocess import Popen, PIPE
-from typing import Callable, Iterable, Literal, TextIO, NamedTuple, overload
+from typing import Literal, TextIO, NamedTuple, overload
 
 
 log = logging.getLogger("dhpython")
@@ -127,7 +127,7 @@ def fix_shebang(fpath: str | Path, repla
         try:
             with open(fpath, "rb") as fp:
                 fcontent = fp.readlines()
-        except IOError:
+        except OSError:
             log.error("cannot open %s", fpath)
             return False
         # do not catch IOError here, the file is zeroed at this stage so it's
@@ -175,7 +175,7 @@ def parse_ns(
     """Parse namespace_packages.txt files."""
     result = set(other or [])
     for fpath in fpaths:
-        with open(fpath, "r", encoding="utf-8") as fp:
+        with open(fpath, encoding="utf-8") as fp:
             for line in fp:
                 if line:
                     result.add(line.strip())
@@ -286,10 +286,8 @@ def execute(
                     open(log_output, "a", encoding="utf-8")
                 )
             assert isinstance(log_output, TextIO)
-            log_output.write(
-                "\n# command executed on {}".format(datetime.now().isoformat())
-            )
-            log_output.write("\n$ {}\n".format(command))
+            log_output.write(f"\n# command executed on {datetime.now().isoformat()}")
+            log_output.write(f"\n$ {command}\n")
             log_output.flush()
             output = log_output
 
@@ -382,7 +380,7 @@ def pyremove(interpreter: "Interpreter",
             if not line.strip() or line.startswith("#"):
                 continue
             if not (m := REMOVE_RE.match(line)):
-                raise ValueError("unrecognized line: %s: %s" % (package, line))
+                raise ValueError(f"unrecognized line: {package}: {line}")
             details = m.groupdict()
             myvers = versions & get_requested_versions(impl, details["vrange"])
             if not myvers:
diff -pruN 7.20251223/dhpython/version.py 7.20251225/dhpython/version.py
--- 7.20251223/dhpython/version.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/dhpython/version.py	2025-12-25 15:48:54.000000000 +0000
@@ -20,7 +20,8 @@
 
 import logging
 import re
-from typing import Iterable
+from collections.abc import Iterable
+from typing import TypeAlias
 from os.path import exists
 
 from dhpython import _defaults
@@ -40,7 +41,7 @@ VERSION_RE = re.compile(
 log = logging.getLogger("dhpython")
 Interpreter = None
 
-type V = "str | Version | Iterable[int]"
+V: TypeAlias = "str | Version | Iterable[int]"
 
 
 class Version:
@@ -53,7 +54,7 @@ class Version:
     # TODO: Upgrade to PEP-440
     def __init__(
         self,
-        value: V | None = None,
+        value: "V | None" = None,
         *,
         major: int | None = None,
         minor: int | None = None,
@@ -103,7 +104,7 @@ class Version:
         """
         result = str(self.major)
         if self.minor is not None:
-            result += ".{}".format(self.minor)
+            result += f".{self.minor}"
         return result
 
     def __hash__(self) -> int:
@@ -117,12 +118,12 @@ class Version:
         >>> repr(Version(major=2))
         "Version('2')"
         """
-        result = "Version('{}".format(self)
+        result = f"Version('{self}"
         for name in ("micro", "releaselevel", "serial"):
             value = getattr(self, name)
             if not value:
                 break
-            result += ".{}".format(value)
+            result += f".{value}"
         return result + "')"
 
     def __add__(self, other: int | str) -> "Version":
@@ -254,8 +255,8 @@ class VersionRange:
     def __init__(
         self,
         value: str | None = None,
-        minver: V | None = None,
-        maxver: V | None = None,
+        minver: "V | None" = None,
+        maxver: "V | None" = None,
     ) -> None:
         if minver:
             self.minver = Version(minver)
@@ -299,11 +300,11 @@ class VersionRange:
         if self.minver == self.maxver:
             return str(self.minver)
         elif self.minver is None:
-            return "-{}".format(self.maxver)
+            return f"-{self.maxver}"
         elif self.maxver is None:
-            return "{}-".format(self.minver)
+            return f"{self.minver}-"
         else:
-            return "{}-{}".format(self.minver, self.maxver)
+            return f"{self.minver}-{self.maxver}"
 
     def __repr__(self) -> str:
         """Return version range string.
@@ -474,14 +475,14 @@ def get_requested_versions(
         if minv == maxv:
             versions = set([minv] if minv in supported(impl) else tuple())
         else:
-            versions = set(v for v in supported(impl) if minv <= v < maxv)
+            versions = {v for v in supported(impl) if minv <= v < maxv}
 
     if available:
         interpreter = Interpreter(impl=impl)
-        versions = set(v for v in versions if exists(interpreter.binary(v)))
+        versions = {v for v in versions if exists(interpreter.binary(v))}
     elif available is False:
         interpreter = Interpreter(impl=impl)
-        versions = set(v for v in versions if not exists(interpreter.binary(v)))
+        versions = {v for v in versions if not exists(interpreter.binary(v))}
 
     return versions
 
diff -pruN 7.20251223/pybuild 7.20251225/pybuild
--- 7.20251223/pybuild	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/pybuild	2025-12-25 15:48:54.000000000 +0000
@@ -24,12 +24,13 @@ import logging
 import argparse
 import re
 import sys
+from collections.abc import Callable
 from enum import StrEnum, auto
 from os import environ, getcwd, makedirs, remove
 from os.path import abspath, exists, isdir, join
 from shutil import rmtree
 from tempfile import mkdtemp
-from typing import Callable, Literal, overload, cast
+from typing import Literal, overload, cast
 
 INTERP_VERSION_RE = re.compile(r"^python(?P<version>3\.\d+)(?P<dbg>-dbg)?$")
 logging.basicConfig(
@@ -155,7 +156,7 @@ def main(cfg: argparse.Namespace) -> Non
                 m = INTERP_VERSION_RE.match(ipreter)
                 if m:
                     ver = m.group("version")
-                    updated = set(tpl.format(version=ver) for tpl in tpls)
+                    updated = {tpl.format(version=ver) for tpl in tpls}
                     if updated:
                         plugin.SUPPORTED_INTERPRETERS.update(updated)
 
@@ -221,17 +222,17 @@ def main(cfg: argparse.Namespace) -> Non
         if interpreter:
             # try PYBUILD_NAME_python3.3-dbg (or hardcoded interpreter)
             i = interpreter.format(version=version or "")
-            opt = "PYBUILD_{}_{}".format(name.upper(), i)
+            opt = f"PYBUILD_{name.upper()}_{i}"
             if opt in environ:
                 return environ[opt]
             # try PYBUILD_NAME_python3-dbg (if not checked above)
             if "{version}" in interpreter and version:
                 i = interpreter.format(version=version.major)
-                opt = "PYBUILD_{}_{}".format(name.upper(), i)
+                opt = f"PYBUILD_{name.upper()}_{i}"
                 if opt in environ:
                     return environ[opt]
         # try PYBUILD_NAME
-        opt = "PYBUILD_{}".format(name.upper())
+        opt = f"PYBUILD_{name.upper()}"
         if opt in environ:
             return environ[opt]
         # try command line args
@@ -277,8 +278,8 @@ def main(cfg: argparse.Namespace) -> Non
             package = ipreter.suggest_pkg_name(cfg.name)
         else:
             package = "PYBUILD_NAME_not_set"
-        if cfg.name and destdir.rstrip("/").endswith("debian/tmp"):
-            destdir = "debian/{}".format(package)
+        if cfg.name and get_option("destdir", interpreter, version) is None:
+            destdir = f"debian/{package}"
         destdir = abspath(destdir)
 
         args = cast(Args, dict(context["args"]))
@@ -321,7 +322,7 @@ def main(cfg: argparse.Namespace) -> Non
         ):
             pp.insert(
                 0,
-                ("/usr/lib/python{0}/plat-{1}").format(
+                ("/usr/lib/python{}/plat-{}").format(
                     version, arch_data["DEB_HOST_MULTIARCH"]
                 ),
             )
@@ -348,7 +349,7 @@ def main(cfg: argparse.Namespace) -> Non
 
     def is_disabled(step: Step, interpreter: str, version: Version) -> bool:
         i = interpreter
-        prefix = "{}/".format(step)
+        prefix = f"{step}/"
         disabled = (get_option("disable", i, version) or "").split()
         for item in disabled:
             if item in (step, "1"):
@@ -378,11 +379,11 @@ def main(cfg: argparse.Namespace) -> Non
         if "ENV" in args:
             env.update(args["ENV"])
 
-        before_cmd = get_option("before_{}".format(step), interpreter, version)
+        before_cmd = get_option(f"before_{step}", interpreter, version)
         if before_cmd:
             log_file: str | Literal[False]
             if cfg.quiet:
-                log_file = join(args["home_dir"], "before_{}_cmd.log".format(step))
+                log_file = join(args["home_dir"], f"before_{step}_cmd.log")
             else:
                 log_file = False
             command = before_cmd.format(**args)
@@ -405,10 +406,10 @@ def main(cfg: argparse.Namespace) -> Non
             remove(fpath)
         func(context, args)
 
-        after_cmd = get_option("after_{}".format(step), interpreter, version)
+        after_cmd = get_option(f"after_{step}", interpreter, version)
         if after_cmd:
             if cfg.quiet:
-                log_file = join(args["home_dir"], "after_{}_cmd.log".format(step))
+                log_file = join(args["home_dir"], f"after_{step}_cmd.log")
             else:
                 log_file = False
             command = after_cmd.format(**args)
@@ -453,6 +454,9 @@ def main(cfg: argparse.Namespace) -> Non
     elif cfg.print_args:
         func = plugin.print_args
 
+    # Default value for --dest-dir
+    default_destdir = environ.get("DESTDIR", "debian/tmp")
+
     ### one function for each interpreter at a time mode ###
     if func:
         step = func.__func__.__name__
@@ -474,7 +478,7 @@ def main(cfg: argparse.Namespace) -> Non
                     continue
                 c = cast(Context, dict(context))
                 c["dir"] = get_option("dir", i, version, cfg.dir)
-                c["destdir"] = get_option("destdir", i, version, cfg.destdir)
+                c["destdir"] = get_option("destdir", i, version, default_destdir)
                 try:
                     run(func, i, version, c)
                 except Exception as err:
@@ -517,7 +521,7 @@ def main(cfg: argparse.Namespace) -> Non
                 else:
                     c = cast(Context, dict(context))
                     c["dir"] = get_option("dir", i, version, cfg.dir)
-                    c["destdir"] = get_option("destdir", i, version, cfg.destdir)
+                    c["destdir"] = get_option("destdir", i, version, default_destdir)
                     context_map[key] = c
 
                 if not is_disabled(Step.CLEAN, i, version):
@@ -732,7 +736,7 @@ def parse_args(argv: list[str]) -> argpa
         action="store",
         metavar="DIR",
         dest="destdir",
-        default=environ.get("DESTDIR", "debian/tmp"),
+        default=None,  # We handle this default manually, because --name affects it
         help="destination directory [default: debian/tmp]",
     )
     dirs.add_argument(
diff -pruN 7.20251223/pydist/generate_fallback_list.py 7.20251225/pydist/generate_fallback_list.py
--- 7.20251223/pydist/generate_fallback_list.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/pydist/generate_fallback_list.py	2025-12-25 15:48:54.000000000 +0000
@@ -130,15 +130,13 @@ for line in data.splitlines():
             processed[dist_name] = pkg_name
 
 for impl, details in result.items():
-    with open("{}_fallback".format(impl), "w") as fp:
+    with open(f"{impl}_fallback", "w") as fp:
         overrides = OVERRIDES[impl]
         lines = []
         for egg, value in overrides.items():
             if value:
-                lines.append("{} {}\n".format(egg, value))
+                lines.append(f"{egg} {value}\n")
         lines.extend(
-            "{} {}\n".format(egg, pkg)
-            for egg, pkg in details.items()
-            if egg not in overrides
+            f"{egg} {pkg}\n" for egg, pkg in details.items() if egg not in overrides
         )
         fp.writelines(sorted(lines))
diff -pruN 7.20251223/tests/test_depends.py 7.20251225/tests/test_depends.py
--- 7.20251223/tests/test_depends.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/tests/test_depends.py	2025-12-25 15:48:54.000000000 +0000
@@ -3,9 +3,10 @@ import logging
 import platform
 import unittest
 from argparse import Namespace
-from unittest.mock import patch
+from collections.abc import Callable, Sequence
 from tempfile import TemporaryDirectory
-from typing import Any, Callable, Sequence, cast
+from typing import Any, cast
+from unittest.mock import patch
 
 from dhpython.fs import ScanResult
 from dhpython.pydist import PyDist, Standard
diff -pruN 7.20251223/tests/test_distutils_extra.py 7.20251225/tests/test_distutils_extra.py
--- 7.20251223/tests/test_distutils_extra.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/tests/test_distutils_extra.py	2025-12-25 15:48:54.000000000 +0000
@@ -28,7 +28,7 @@ class TestDistutilsExtra(unittest.TestCa
         {
             "compile": False,
             "dist-info": set(),
-            "egg-info": set(("PKG-INFO",)),
+            "egg-info": {"PKG-INFO"},
             "ext_no_version": set(),
             "ext_stableabi": set(),
             "ext_vers": set(),
diff -pruN 7.20251223/tests/test_fs.py 7.20251225/tests/test_fs.py
--- 7.20251223/tests/test_fs.py	2025-12-23 12:04:11.000000000 +0000
+++ 7.20251225/tests/test_fs.py	2025-12-25 15:48:54.000000000 +0000
@@ -1,8 +1,9 @@
 import os
 from argparse import Namespace
+from collections.abc import Sequence
 from pathlib import Path
 from tempfile import TemporaryDirectory
-from typing import Any, Sequence, cast
+from typing import Any, cast
 from unittest import TestCase
 
 from dhpython.interpreter import Interpreter
