Fix new ruff findings
diff --git a/python/tests/_test_utils.py b/python/tests/_test_utils.py
index d94ebd0..6e5a1ea 100644
--- a/python/tests/_test_utils.py
+++ b/python/tests/_test_utils.py
@@ -1,6 +1,5 @@
 """Common utilities for Brotli tests."""
 
-from __future__ import print_function
 import glob
 import os
 import pathlib
@@ -19,9 +18,9 @@
 
 # Get the platform/version-specific build folder.
 # By default, the distutils build base is in the same location as setup.py.
-platform_lib_name = 'lib.{platform}-{version[0]}.{version[1]}'.format(
-    platform=sysconfig.get_platform(), version=sys.version_info
-)
+platform = sysconfig.get_platform()
+version = sys.version_info
+platform_lib_name = f'lib.{platform}-{version[0]}.{version[1]}'
 build_dir = os.path.join(project_dir, 'bin', platform_lib_name)
 
 # Prepend the build folder to sys.path and the PYTHONPATH environment variable.
diff --git a/scripts/dictionary/step-04-generate-java-literals.py b/scripts/dictionary/step-04-generate-java-literals.py
index c0926ab..6e81fcb 100644
--- a/scripts/dictionary/step-04-generate-java-literals.py
+++ b/scripts/dictionary/step-04-generate-java-literals.py
@@ -15,11 +15,6 @@
 #
 # This script generates literals used in Java code.
 
-try:
-  unichr  # Python 2
-except NameError:
-  unichr = chr  # Python 3
-
 bin_path = "dictionary.bin"
 
 with open(bin_path, "rb") as raw:
@@ -38,15 +33,15 @@
       cntr += 1
     else:
       is_skip = False
-      hi.append(unichr(cntr))
+      hi.append(chr(cntr))
       cntr = skip_flip_offset + 1
   elif value >= 0x80:
     cntr += 1
   else:
     is_skip = True
-    hi.append(unichr(cntr))
+    hi.append(chr(cntr))
     cntr = skip_flip_offset + 1
-hi.append(unichr(cntr))
+hi.append(chr(cntr))
 
 low0 = low[0:len(low) // 2]
 low1 = low[len(low) // 2:len(low)]
@@ -66,7 +61,7 @@
     elif c == "\\":
       result.append("\\\\")
     elif ord(c) < 32 or ord(c) >= 127:
-      result.append("\\u%04X" % ord(c))
+      result.append(f"\\u{ord(c):04X}")
     else:
       result.append(c)
   return result
diff --git a/setup.py b/setup.py
index 901412c..6ed1838 100644
--- a/setup.py
+++ b/setup.py
@@ -8,11 +8,10 @@
 import logging
 import os
 import re
-import setuptools
-import setuptools.command.build_ext as build_ext
-import setuptools.errors as errors
-import setuptools.modified as modified
 
+import setuptools
+from setuptools import errors, modified
+from setuptools.command import build_ext
 
 CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
 LOGGER = logging.getLogger(__name__)
@@ -27,8 +26,8 @@
   if value == "0":
     return False
   raise ValueError(
-      "Environment variable {} has invalid value {}. Please set it to 1, 0 or"
-      " an empty string".format(key, value)
+      f"Environment variable {key} has invalid value {value}."
+      " Please set it to 1, 0 or an empty string"
   )
 
 
@@ -45,7 +44,7 @@
   major, minor, patch = [defs.get("BROTLI_VERSION_" + key) for key in parts]
   if not major or not minor or not patch:
     return ""
-  return "{}.{}.{}".format(major, minor, patch)
+  return f"{major}.{minor}.{patch}"
 
 
 class BuildExt(build_ext.build_ext):
@@ -60,10 +59,9 @@
   def build_extension(self, ext):
     if ext.sources is None or not isinstance(ext.sources, (list, tuple)):
       raise errors.DistutilsSetupError(
-          "in 'ext_modules' option (extension '%s'), "
+          f"in 'ext_modules' option (extension '{ext.name}'), "
           "'sources' must be present and must be "
           "a list of source filenames"
-          % ext.name
       )
 
     ext_path = self.get_ext_fullpath(ext.name)
diff --git a/tests/regression/t01/copystat_regression_test.py b/tests/regression/t01/copystat_regression_test.py
index 01d9c6b..f40c1a2 100755
--- a/tests/regression/t01/copystat_regression_test.py
+++ b/tests/regression/t01/copystat_regression_test.py
@@ -14,13 +14,12 @@
 import contextlib
 import hashlib
 import os
-from pathlib import Path
 import shutil
 import stat
 import subprocess
 import tempfile
 import unittest
-
+from pathlib import Path
 
 PLAIN_BYTES = b"A" * 65537
 TARGET_BYTES = b"TARGET\n"
@@ -61,20 +60,25 @@
   def write_target(self, path):
     path.write_bytes(TARGET_BYTES)
 
+  def on_subprocess_failed(self, preamble: str, proc):
+      retcode = proc.returncode
+      stdout = proc.stdout.decode("utf-8", "replace")
+      stderr = proc.stderr.decode("utf-8", "replace")
+      self.fail(
+          f"{preamble}\n"
+          f"retcode:{retcode}\n"
+          f"stdout:\n{stdout}\n"
+          f"stderr:\n{stderr}\n")
+
   def run_brotli(self, *args, input_bytes=None, env=None, check=True):
     proc = subprocess.run(
         [str(self.brotli)] + [str(arg) for arg in args],
         input=input_bytes,
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
+        capture_output=True,
         env=env,
         check=False)
     if check and proc.returncode != 0:
-      self.fail(
-          "brotli exited with %d\nstdout:\n%s\nstderr:\n%s" % (
-              proc.returncode,
-              proc.stdout.decode("utf-8", "replace"),
-              proc.stderr.decode("utf-8", "replace")))
+      self.on_subprocess_failed("brotli failed", proc)
     return proc
 
   def compress(self, src, dst, no_copy_stat=False):
@@ -97,8 +101,8 @@
 
     with umask(0o022):
       for mode in modes:
-        with self.subTest(mode="%03o" % mode):
-          case_dir = self.workdir / ("%03o" % mode)
+        with self.subTest(mode=f"{mode:03o}"):
+          case_dir = self.workdir / (f"{mode:03o}")
           case_dir.mkdir()
           src = case_dir / "in.bin"
           compressed = case_dir / "in.bin.br"
@@ -257,11 +261,7 @@
 
     proc = self.run_brotli(*args, env=env, check=False)
     if proc.returncode != 0:
-      self.fail(
-          "brotli with fclose_swap exited with %d\nstdout:\n%s\nstderr:\n%s" % (
-              proc.returncode,
-              proc.stdout.decode("utf-8", "replace"),
-              proc.stderr.decode("utf-8", "replace")))
+      self.on_subprocess_failed("brotli with fclose_swap failed", proc)
 
   def ld_preload_value(self):
     preloads = []
@@ -286,21 +286,16 @@
     proc = subprocess.run(
         [cc, "-shared", "-fPIC", str(helper_src), "-o", str(helper_out),
          "-ldl"],
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
+        capture_output=True,
         check=False)
     if proc.returncode != 0:
-      self.fail(
-          "failed to build fclose_swap.so\nstdout:\n%s\nstderr:\n%s" % (
-              proc.stdout.decode("utf-8", "replace"),
-              proc.stderr.decode("utf-8", "replace")))
+      self.on_subprocess_failed("failed to build fclose_swap.so", proc)
     return helper_out
 
   def asan_runtime(self):
     try:
       proc = subprocess.run(["ldd", str(self.brotli)],
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE,
+                            capture_output=True,
                             check=False)
     except FileNotFoundError:
       return None
@@ -311,8 +306,7 @@
     if cc is None:
       return None
     proc = subprocess.run([cc, "-print-file-name=libasan.so"],
-                          stdout=subprocess.PIPE,
-                          stderr=subprocess.PIPE,
+                          capture_output=True,
                           text=True,
                           check=False)
     candidate = proc.stdout.strip()
@@ -329,7 +323,7 @@
 
   brotli = Path(args.brotli).resolve()
   if not brotli.is_file():
-    parser.error("%s is not a file" % brotli)
+    parser.error(f"{brotli} is not a file")
   CopyStatRegressionTest.brotli = brotli
 
   unittest.main(argv=[__file__], verbosity=2 if args.verbose else 1)