Subject: [bug#65010] [PATCH 0/8] Misc Python build system improvements

From: Lars-Dominik Braun <[email-masked]>
Date: Wed, 2 Aug 2023 12:37:57 +0200

Hi,

the attached patches contain various Python build system improvements
including an original TOML parser (which may be used by other build
systems and importers in the future too) and fixes for #62781 and #63044.
I’m also pushing the patches to the pyproject-toml branch. As always,
a world rebuild is required.

Cheers,
Lars

Lars-Dominik Braun (8):
  build-system/pyproject: Use python-sans-pip-wrapper as default-python.
  build-system/python: Ignore symlinks when changing mtime.
  gnu: criu: Change file mtimes to fix build.
  gnu: sssd: Change file mtimes to fix build.
  guix: toml: Add TOML parser.
  build-system/pyproject: Use TOML parser.
  gnu: python-pytest-xdist: Disable failing test.
  guix: pyproject-build-system: Default configure-flags to empty
    dictionary.

 Makefile.am                           |   2 +
 gnu/packages/admin.scm                |   2 +-
 gnu/packages/astronomy.scm            |  81 +++--
 gnu/packages/bioinformatics.scm       |  79 +++--
 gnu/packages/bootloaders.scm          |   3 +
 gnu/packages/build-tools.scm          |  10 +-
 gnu/packages/check.scm                |  14 +-
 gnu/packages/databases.scm            |  12 +-
 gnu/packages/digest.scm               |   2 +-
 gnu/packages/engineering.scm          |   2 +-
 gnu/packages/fontutils.scm            |   9 +-
 gnu/packages/geo.scm                  |  10 +-
 gnu/packages/graph.scm                |   7 +-
 gnu/packages/graphviz.scm             |   4 +-
 gnu/packages/license.scm              |   5 +-
 gnu/packages/machine-learning.scm     |  32 +-
 gnu/packages/maths.scm                |   4 +-
 gnu/packages/monitoring.scm           |   2 +-
 gnu/packages/package-management.scm   |   6 +-
 gnu/packages/potassco.scm             |  10 +-
 gnu/packages/protobuf.scm             |   4 +-
 gnu/packages/python-build.scm         |  33 +-
 gnu/packages/python-check.scm         |  30 +-
 gnu/packages/python-compression.scm   |  20 +-
 gnu/packages/python-crypto.scm        |  15 +-
 gnu/packages/python-science.scm       |  24 +-
 gnu/packages/python-web.scm           |  57 ++-
 gnu/packages/python-xyz.scm           | 260 +++++++++-----
 gnu/packages/qt.scm                   |   3 +-
 gnu/packages/rpc.scm                  |   2 +-
 gnu/packages/serialization.scm        |   4 +-
 gnu/packages/sphinx.scm               |   4 +-
 gnu/packages/sssd.scm                 |   9 +-
 gnu/packages/statistics.scm           |  14 +-
 gnu/packages/terminals.scm            |   5 +-
 gnu/packages/time.scm                 |   3 +-
 gnu/packages/tor.scm                  |   4 +-
 gnu/packages/tree-sitter.scm          |   4 +-
 gnu/packages/video.scm                |   2 +-
 gnu/packages/vim.scm                  |   4 +-
 gnu/packages/virtualization.scm       |  20 +-
 guix/build-system/pyproject.scm       |  12 +-
 guix/build/pyproject-build-system.scm |  56 +--
 guix/build/python-build-system.scm    |   4 +-
 guix/build/toml.scm                   | 478 ++++++++++++++++++++++++++
 tests/toml.scm                        | 442 ++++++++++++++++++++++++
 46 files changed, 1503 insertions(+), 306 deletions(-)
 create mode 100644 guix/build/toml.scm
 create mode 100644 tests/toml.scm

base-commit: 5aceacac65784bd2e1fd12304f965ae6026de49d
-- 
2.41.0

>From 70baccd8068f35ff8dce0955eaea60f7ea9c240e Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Thu, 11 May 2023 08:33:02 +0200
Subject: [PATCH 1/8] build-system/pyproject: Use python-sans-pip-wrapper as
 default-python.

Also adds python-setuptools and python-wheel to relevant packages,
either to native-inputs or to propagated inputs if the pkg_resources
Python module is loaded at runtime.

* guix/build-system/pyproject.scm (default-python): Default to
python-sans-pip-wrapper.
---
 gnu/packages/admin.scm              |   2 +-
 gnu/packages/astronomy.scm          |  81 ++++++---
 gnu/packages/bioinformatics.scm     |  79 ++++++---
 gnu/packages/bootloaders.scm        |   3 +
 gnu/packages/check.scm              |   9 +-
 gnu/packages/databases.scm          |  12 +-
 gnu/packages/digest.scm             |   2 +-
 gnu/packages/engineering.scm        |   2 +-
 gnu/packages/fontutils.scm          |   9 +-
 gnu/packages/geo.scm                |  10 +-
 gnu/packages/graph.scm              |   7 +-
 gnu/packages/graphviz.scm           |   4 +-
 gnu/packages/license.scm            |   5 +-
 gnu/packages/machine-learning.scm   |  32 ++--
 gnu/packages/maths.scm              |   4 +-
 gnu/packages/monitoring.scm         |   2 +-
 gnu/packages/package-management.scm |   6 +-
 gnu/packages/potassco.scm           |  10 +-
 gnu/packages/protobuf.scm           |   4 +-
 gnu/packages/python-build.scm       |   3 +-
 gnu/packages/python-check.scm       |  30 +++-
 gnu/packages/python-compression.scm |  20 ++-
 gnu/packages/python-crypto.scm      |  15 +-
 gnu/packages/python-science.scm     |  19 +-
 gnu/packages/python-web.scm         |  57 ++++--
 gnu/packages/python-xyz.scm         | 260 +++++++++++++++++++---------
 gnu/packages/qt.scm                 |   3 +-
 gnu/packages/rpc.scm                |   2 +-
 gnu/packages/serialization.scm      |   4 +-
 gnu/packages/sphinx.scm             |   4 +-
 gnu/packages/statistics.scm         |  14 +-
 gnu/packages/terminals.scm          |   5 +-
 gnu/packages/time.scm               |   3 +-
 gnu/packages/tor.scm                |   4 +-
 gnu/packages/tree-sitter.scm        |   4 +-
 gnu/packages/video.scm              |   2 +-
 gnu/packages/vim.scm                |   4 +-
 guix/build-system/pyproject.scm     |   7 +-
 38 files changed, 516 insertions(+), 227 deletions(-)

diff --git a/gnu/packages/admin.scm b/gnu/packages/admin.scm
index fa697b79fb..75b8c9f2f1 100644
--- a/gnu/packages/admin.scm
+++ b/gnu/packages/admin.scm
@@ -4848,7 +4848,7 @@ (define-public jc
                  (lambda _
                    (substitute* (find-files "tests" "^test.*\\.py$")
                      (("America/Los_Angeles") "PST8PDT")))))))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (propagated-inputs
      (list python-pygments python-ruamel.yaml python-xmltodict))
     (home-page "https://github.com/kellyjonbrazil/jc")
diff --git a/gnu/packages/astronomy.scm b/gnu/packages/astronomy.scm
index 5b9434fb33..8fc727e0d6 100644
--- a/gnu/packages/astronomy.scm
+++ b/gnu/packages/astronomy.scm
@@ -521,7 +521,8 @@ (define-public python-astroml
     (propagated-inputs (list python-astropy python-matplotlib python-numpy
                              python-scikit-learn python-scipy))
     (native-inputs (list python-pytest-astropy-header python-pytest-cov
-                         python-pytest-doctestplus python-pytest-remotedata))
+                         python-pytest-doctestplus python-pytest-remotedata
+                         python-setuptools python-wheel))
     (home-page "https://astroml.org")
     (synopsis "Tools for machine learning and data mining in astronomy")
     (description "This package provides tools for machine learning and data
@@ -613,7 +614,8 @@ (define-public python-gatspy
               ;; XXX: we don't have supersmoother
               " and not test_supersmoother.py"))))
     (propagated-inputs (list python-astroml python-numpy python-scipy))
-    (native-inputs (list python-pytest python-nose python-setuptools-scm))
+    (native-inputs (list python-pytest python-nose python-setuptools-scm
+                         python-setuptools python-wheel))
     (home-page "https://github.com/astroml/gatspy")
     (synopsis "General tools for astronomical time series in Python")
     (description "This package provides general tools for astronomical time
@@ -1470,7 +1472,9 @@ (define-public python-astropy-healpix
      (list python-extension-helpers
            python-hypothesis
            python-pytest-astropy
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-astropy python-numpy))
     (home-page "https://github.com/astropy/astropy-healpix")
@@ -1553,7 +1557,9 @@ (define-public python-astroscrappy
            python-pytest-astropy
            python-scikit-image
            python-scipy
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (propagated-inputs (list python-astropy python-numpy))
     (home-page "https://github.com/astropy/astroscrappy")
     (synopsis "Speedy Cosmic Ray Annihilation Package in Python")
@@ -1651,7 +1657,9 @@ (define-public python-cdflib
            python-pytest
            python-pytest-cov
            python-pytest-remotedata
-           python-xarray))
+           python-xarray
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/MAVENSDC/cdflib")
     (synopsis "Python library to deal with NASA's CDF astronmical data format")
     (description "This package provides a Python @acronym{CDF, Computable
@@ -1741,7 +1749,9 @@ (define-public python-czml3
      (list python-astropy
            python-pytest
            python-pytest-cov
-           python-pytest-mypy))
+           python-pytest-mypy
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/poliastro/czml3")
     (synopsis "Python library to write CZML")
     (description
@@ -1818,9 +1828,10 @@ (define-public python-drizzle
                        ;; Cython extensions have to be built before running
                        ;; the tests.
                        (invoke "python" "setup.py" "build_ext" "--inplace"))))))
-    (propagated-inputs (list python-astropy python-numpy))
+    (propagated-inputs (list python-astropy python-numpy python-setuptools))
     (native-inputs (list python-coverage python-flake8 python-pytest
-                         python-pytest-cov python-setuptools-scm))
+                         python-pytest-cov python-setuptools-scm
+                         python-wheel))
     (home-page "https://github.com/spacetelescope/drizzle")
     (synopsis
      "Astronomical tool for combining dithered images into a single image")
@@ -1871,7 +1882,8 @@ (define-public python-hvpy
     (arguments
      (list #:tests? #f)) ; Requires HTTP(S) access to api.beta.helioviewer.org
     (propagated-inputs (list python-pydantic python-requests))
-    (native-inputs (list python-pytest python-pytest-astropy))
+    (native-inputs (list python-pytest python-pytest-astropy python-setuptools
+                         python-wheel))
     (home-page "https://helioviewer.org/")
     (synopsis "Helioviewer Python API Wrapper")
     (description "@code{hvpy} is a Python API wrapper around the formal
@@ -1892,7 +1904,9 @@ (define-public python-mpl-animators
     (native-inputs
      (list python-pytest
            python-pytest-mpl
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-astropy
            python-matplotlib
@@ -2039,7 +2053,9 @@ (define-public python-poppy
            python-pytest
            python-pytest-astropy
            python-sphinx
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://poppy-optics.readthedocs.io/")
     (synopsis "Physical Optics Propagation in Python")
     (description
@@ -2069,7 +2085,8 @@ (define-public python-pyvo
         (base32 "17acv1yhz1jrsx9f35nr1vg276ibaivh4i243qkmp6abzvfyg907"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest-astropy python-requests-mock python-setuptools-scm))
+     (list python-pytest-astropy python-requests-mock python-setuptools-scm
+           python-setuptools python-wheel))
     (propagated-inputs
      (list python-astropy python-mimeparse python-pillow python-requests))
     (home-page "https://github.com/astropy/pyvo")
@@ -2405,8 +2422,9 @@ (define-public python-stsci-image
                             ;; the tests.
                             (invoke "python" "setup.py" "build_ext"
                                     "--inplace"))))))
-    (propagated-inputs (list python-numpy python-scipy))
-    (native-inputs (list python-pytest python-setuptools-scm))
+    (propagated-inputs (list python-numpy python-scipy python-setuptools))
+    (native-inputs (list python-pytest python-setuptools-scm
+                         python-wheel))
     (home-page "https://github.com/spacetelescope/stsci.image")
     (synopsis "Image array manipulation functions")
     (description
@@ -2456,7 +2474,8 @@ (define-public python-stsci-stimage
       #:tests? #f))
     (propagated-inputs (list python-numpy))
     (native-inputs (list python-codecov python-pytest python-pytest-cov
-                         python-setuptools-scm))
+                         python-setuptools python-setuptools-scm
+                         python-wheel))
     (home-page "https://stscistimage.readthedocs.io/en/latest/")
     (synopsis "STScI image processing")
     (description "This package provides an astronomical Python package with
@@ -2486,7 +2505,9 @@ (define-public python-stcal
                          python-pytest-cov
                          python-pytest-doctestplus
                          python-pytest-openfiles
-                         python-setuptools-scm))
+                         python-setuptools-scm
+                         python-setuptools
+                         python-wheel))
     (home-page "https://github.com/spacetelescope/stcal")
     (synopsis "STScI tools and algorithms used in calibration pipelines")
     (description "STScI tools and algorithms used in calibration pipelines.")
@@ -3296,7 +3317,9 @@ (define-public python-pynbody
     (native-inputs
      (list python-cython
            python-pandas
-           python-pytest))
+           python-pytest
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-h5py
            python-matplotlib
@@ -3328,9 +3351,10 @@ (define-public python-pysynphot
                             (setenv "PYSYN_CDBS"
                                     (string-append #$output "/crds")))))))
     (native-inputs (list python-pytest python-pytest-remotedata
-                         python-setuptools-scm))
+                         python-setuptools-scm python-wheel))
     (propagated-inputs (list python-astropy python-beautifulsoup4 python-numpy
-                             python-pytest-astropy-header python-six))
+                             python-pytest-astropy-header python-six
+                             python-setuptools))
     (home-page "https://github.com/spacetelescope/pysynphot")
     (synopsis "Python Synthetic Photometry Utilities")
     (description
@@ -3396,7 +3420,7 @@ (define-public python-synphot
       #:tests? #f))
     (propagated-inputs (list python-astropy python-numpy python-scipy))
     (native-inputs (list python-pytest python-pytest-astropy
-                         python-setuptools-scm))
+                         python-setuptools-scm python-setuptools python-wheel))
     (home-page "https://github.com/spacetelescope/synphot_refactor")
     (synopsis "Synthetic photometry using Astropy")
     (description
@@ -3467,7 +3491,9 @@ (define-public python-asdf
            python-pytest-openfiles
            python-pytest-remotedata
            python-semantic-version
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-asdf-standard
            python-asdf-transform-schemas
@@ -3514,7 +3540,9 @@ (define-public python-asdf-standard
                          python-pypa-build
                          python-pytest
                          python-packaging
-                         python-setuptools-scm))
+                         python-setuptools-scm
+                         python-setuptools
+                         python-wheel))
     (propagated-inputs (list python-importlib-resources))
     (home-page "https://asdf-standard.readthedocs.io/")
     (synopsis "ASDF standard schemas")
@@ -3542,7 +3570,7 @@ (define python-asdf-transform-schemas
     (arguments
      ;; Dependency cycle with python-asdf
      (list #:tests? #f))
-    (native-inputs (list python-setuptools-scm))
+    (native-inputs (list python-setuptools-scm python-setuptools python-wheel))
     (propagated-inputs (list python-asdf-standard python-importlib-resources))
     (home-page "https://github.com/asdf-format/asdf-transform-schemas")
     (synopsis "ASDF schemas for transforms")
@@ -3661,7 +3689,7 @@ (define python-asdf-unit-schemas
     (arguments
      ;; Dependency cycle with python-asdf
      (list #:tests? #f))
-    (native-inputs (list python-setuptools-scm))
+    (native-inputs (list python-setuptools-scm python-setuptools python-wheel))
     (propagated-inputs (list python-asdf-standard python-importlib-resources))
     (home-page "https://asdf-unit-schemas.readthedocs.io/")
     (synopsis "ASDF serialization schemas for the units defined by @code{astropy.units}")
@@ -3903,9 +3931,10 @@ (define-public python-wiimatch
                (base32
                 "0x6p5z6a2cqinckwlpinjxagvmswl149s1jn6ihmdxk4k0h8rrz0"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-numpy python-scipy))
+    (propagated-inputs (list python-numpy python-scipy python-setuptools))
     (native-inputs (list python-codecov python-pytest python-pytest-cov
-                         python-pytest-doctestplus python-setuptools-scm))
+                         python-pytest-doctestplus python-setuptools-scm
+                         python-wheel))
     (home-page "https://github.com/spacetelescope/wiimatch")
     (synopsis
      "Optimal matching of weighted N-dimensional image intensity data")
diff --git a/gnu/packages/bioinformatics.scm b/gnu/packages/bioinformatics.scm
index d1448e390f..e6ba3e561a 100644
--- a/gnu/packages/bioinformatics.scm
+++ b/gnu/packages/bioinformatics.scm
@@ -1401,7 +1401,8 @@ (define-public python-bcbio-gff
                (base32
                 "1pm1szyxabhn8jismrj9cjhf88ajgcmm39f0cgf36iagw5qakprl"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-biopython python-six))
+    (propagated-inputs (list python-biopython python-six python-setuptools
+                             python-wheel))
     (native-inputs (list python-pytest))
     (home-page "https://github.com/chapmanb/bcbb/tree/master/gff")
     (synopsis "Read and write GFF files with Biopython integration")
@@ -1526,7 +1527,7 @@ (define-public python-bulkvis
                                python-tornado-6
                                python-tqdm
                                python-umap-learn))
-      (native-inputs (list python-pytest))
+      (native-inputs (list python-pytest python-setuptools python-wheel))
       (home-page "https://github.com/LooseLab/bulkVis")
       (synopsis "Interactive visualization of bulk RNA-seq data")
       (description
@@ -1765,7 +1766,8 @@ (define-public python-demuxem
            python-scikit-learn
            python-scipy
            python-seaborn))
-    (native-inputs (list python-cython python-setuptools-scm))
+    (native-inputs (list python-cython python-setuptools-scm
+                         python-setuptools python-wheel))
     (home-page "https://github.com/lilab-bcb/demuxEM")
     (synopsis "Analyze cell-hashing/nucleus-hashing data")
     (description
@@ -1838,6 +1840,7 @@ (define-public python-hclust2
            python-numpy
            python-pandas
            python-scipy))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/SegataLab/hclust2/")
     (synopsis "Plotting heat-maps for publications")
     (description
@@ -1857,7 +1860,7 @@ (define-public python-htsget
               "111q4pzkav26aa3hkgh948wqlyrq7dq6sjml9z63n3blw8s6b0c4"))))
    (build-system pyproject-build-system)
    (native-inputs
-    (list python-setuptools-scm))
+    (list python-setuptools-scm python-setuptools python-wheel))
    (propagated-inputs
     (list python-humanize python-requests python-six))
    (home-page "https://pypi.org/project/htsget/")
@@ -1935,6 +1938,7 @@ (define-public python-logomaker
     (build-system pyproject-build-system)
     (propagated-inputs
      (list python-matplotlib python-numpy python-pandas))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://logomaker.readthedocs.io")
     (synopsis "Package for making Sequence Logos")
     (description "Logomaker is a Python package for generating
@@ -1968,7 +1972,8 @@ (define-public python-pegasusio
            python-pillow
            python-scipy
            python-zarr))
-    (native-inputs (list python-cython python-setuptools-scm))
+    (native-inputs (list python-cython python-setuptools-scm
+                         python-setuptools python-wheel))
     (home-page "https://github.com/lilab-bcb/pegasusio")
     (synopsis "Read or write single-cell genomics data")
     (description
@@ -2027,7 +2032,7 @@ (define-public python-phenograph
            python-scikit-learn
            python-scipy))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/dpeerlab/PhenoGraph.git")
     (synopsis "Graph-based clustering for high-dimensional single-cell data")
     (description
@@ -2058,6 +2063,7 @@ (define-public python-phylophlan
            python-numpy
            python-pandas
            python-seaborn))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/biobakery/phylophlan")
     (synopsis
      "Phylogenetic analysis of microbial isolates and genomes from metagenomes")
@@ -2137,6 +2143,8 @@ (define-public python-pybedtools
            python-cython
            kentutils ; for bedGraphToBigWig
            python-six
+           python-setuptools
+           python-wheel
            ;; For the test suite.
            python-pytest
            python-psutil))
@@ -2330,6 +2338,7 @@ (define-public python-readpaf
                 "15m6ffks4zwpp1ycwk6n02py6mw2yh7qr0vhpc178b91gldr97ia"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-pandas))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/alexomics/read-paf")
     (synopsis "Minimap2 PAF file reader")
     (description
@@ -2442,6 +2451,7 @@ (define-public python-biopython
            (lambda _ (setenv "HOME" "/tmp"))))))
     (propagated-inputs
      (list python-numpy))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://biopython.org/")
     (synopsis "Tools for biological computation in Python")
     (description
@@ -3494,7 +3504,8 @@ (define-public python-bx-python
     (inputs
      (list zlib))
     (native-inputs
-     (list python-cython python-lzo python-pytest))
+     (list python-cython python-lzo python-pytest python-setuptools
+           python-wheel))
     (home-page "https://github.com/bxlab/bx-python")
     (synopsis "Tools for manipulating biological data")
     (description
@@ -3656,6 +3667,8 @@ (define-public python-pysam
     (native-inputs
      (list python-cython
            python-pytest
+           python-setuptools
+           python-wheel
            ;; Dependencies below are are for tests only.
            samtools
            bcftools))
@@ -3734,6 +3747,8 @@ (define-public python-plastid
     (native-inputs
      `(("python-cython" ,python-cython)
        ("python-nose" ,python-nose)
+       ("python-setuptools" ,python-setuptools)
+       ("python-wheel" ,python-wheel)
        ("test-data"
         ,(origin
            (method url-fetch)
@@ -4061,7 +4076,8 @@ (define-public python-dnaio
     (arguments
      (list #:tests? #false)) ;there are none
     (native-inputs
-     (list python-cython python-pytest python-setuptools-scm))
+     (list python-cython python-pytest python-setuptools-scm
+           python-setuptools python-wheel))
     (propagated-inputs
      (list python-xopen))
     (home-page "https://github.com/marcelm/dnaio/")
@@ -4239,6 +4255,7 @@ (define-public python-pybigwig
      (list python-numpy))
     (inputs
      (list libbigwig zlib curl))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/dpryan79/pyBigWig")
     (synopsis "Access bigWig files in Python using libBigWig")
     (description
@@ -4277,9 +4294,11 @@ (define-public python-schema-salad
            python-rdflib-jsonld
            python-requests
            python-ruamel.yaml
+           python-setuptools ; For pkg_resources.
            python-typing-extensions))
     (native-inputs
-     (list python-black python-pytest python-pytest-runner))
+     (list python-black python-pytest python-pytest-runner
+           python-wheel))
     (home-page "https://github.com/common-workflow-language/schema_salad")
     (synopsis "Schema Annotations for Linked Avro Data (SALAD)")
     (description
@@ -4380,6 +4399,7 @@ (define-public python-scrublet
            python-scikit-learn
            python-scipy
            python-umap-learn))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/swolock/scrublet")
     (synopsis "Tool to indentify and remove doublets in single-cell data")
     (description "This package provides a tool for identifying and removing
@@ -4456,6 +4476,7 @@ (define-public cwltool
            python-requests
            python-ruamel.yaml
            python-schema-salad
+           python-setuptools ; For pkg_resources.
            python-shellescape
            python-typing-extensions
            ;; Not listed as needed but still necessary:
@@ -4467,7 +4488,8 @@ (define-public cwltool
            python-pytest
            python-pytest-cov
            python-pytest-mock
-           python-pytest-runner))
+           python-pytest-runner
+           python-wheel))
     (home-page
      "https://github.com/common-workflow-language/common-workflow-language")
     (synopsis "Common Workflow Language reference implementation")
@@ -4521,7 +4543,8 @@ (define-public python-dendropy
               (substitute* "tests/test_datamodel_taxon.py"
                 (("collections.Iterable")
                  "collections.abc.Iterable")))))))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-wheel))
+    (propagated-inputs (list python-setuptools))
     (home-page "https://dendropy.org/")
     (synopsis "Library for phylogenetics and phylogenetic computing")
     (description
@@ -5115,7 +5138,8 @@ (define-public python-bamnostic
                (base32
                 "0cjpzyqz6r4lmiwry2gcxdczwpkhl3lyyjg4s8addln17691ysxk"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-pytest))
+    (propagated-inputs (list python-pytest python-setuptools))
+    (native-inputs (list python-wheel))
     (home-page "https://github.com/betteridiot/bamnostic/")
     (synopsis "Tool for binary alignment map, random access and parsing")
     (description
@@ -5213,7 +5237,7 @@ (define-public python-ete3
            python-pyqt
            python-scipy))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "http://etetoolkit.org")
     (synopsis "Python environment for phylogenetic tree exploration")
     (description
@@ -5234,6 +5258,7 @@ (define-public python-illumina-utils
     (build-system pyproject-build-system)
     (arguments (list #:tests? #false))  ;there are none
     (propagated-inputs (list python-matplotlib python-numpy python-levenshtein))
+    (native-inputs (list python-setuptools python-wheel python-pip))
     (home-page "https://github.com/meren/illumina-utils")
     (synopsis "Library and scripts to work with Illumina paired-end data")
     (description
@@ -5255,6 +5280,7 @@ (define-public python-pyani
     (propagated-inputs
      (list python-biopython python-matplotlib python-pandas
            python-scipy python-seaborn))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://widdowquinn.github.io/pyani/")
     (synopsis "Calculate genome-scale average nucleotide identity")
     (description
@@ -8607,7 +8633,9 @@ (define-public python-screed
     (build-system pyproject-build-system)
     (native-inputs
      (list python-pytest-cov
-           python-pytest-runner))
+           python-pytest-runner
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/dib-lab/screed/")
     (synopsis "Short read sequence database utilities")
     (description "Screed parses FASTA and FASTQ files and generates databases.
@@ -8949,7 +8977,7 @@ (define-public seqmagick
     (inputs
      (list python-biopython python-pygtrie))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/fhcrc/seqmagick")
     (synopsis "Tools for converting and modifying sequence files")
     (description
@@ -11672,10 +11700,12 @@ (define-public multiqc
            python-requests
            python-rich
            python-rich-click
+           python-setuptools ; For pkg_resources.
            python-simplejson
            python-spectra))
     (native-inputs
      `(("python-pytest" ,python-pytest)
+       ("python-wheel" ,python-wheel)
        ("tests"
         ,(let ((commit "c3e7400affe3f3ca996973805797af61b93070ba"))
            (origin
@@ -13301,9 +13331,10 @@ (define-public python-goatools
                              python-pydot
                              python-requests
                              python-scipy
+                             python-setuptools
                              python-statsmodels
                              python-xlsxwriter))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-wheel))
     (home-page "https://github.com/tanghaibao/goatools")
     (synopsis "Python scripts to find enrichment of GO terms")
     (description "Python scripts to find enrichment of GO terms.  In addition,
@@ -13419,9 +13450,10 @@ (define-public python-mgatk
            python-pysam
            python-regex
            python-ruamel.yaml
+           python-setuptools
            snakemake))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-wheel))
     (home-page "https://github.com/caleblareau/mgatk")
     (synopsis "Mitochondrial genome analysis toolkit.")
     (description "This package is a Python-based command line interface for
@@ -15658,6 +15690,7 @@ (define-public python-scanpy
            python-patsy
            python-scikit-learn
            python-scipy
+           python-setuptools ; For pkg_resources.
            python-seaborn
            python-session-info
            python-sinfo
@@ -16008,7 +16041,9 @@ (define-public python-pyfaidx
            python-mock
            python-numpy
            python-pytest
-           python-pytest-cov))
+           python-pytest-cov
+           python-wheel))
+    (propagated-inputs (list python-setuptools))
     (home-page "http://mattshirley.com")
     (synopsis "Random access to fasta subsequences")
     (description
@@ -16174,7 +16209,7 @@ (define-public python-hicmatrix
            python-scipy
            python-tables))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/deeptools/HiCMatrix/")
     (synopsis "HiCMatrix class for HiCExplorer and pyGenomeTracks")
     (description
@@ -17038,6 +17073,7 @@ (define-public cnvkit
            python-scipy
            ;; R packages
            r-dnacopy))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://cnvkit.readthedocs.org/")
     (synopsis "Copy number variant detection from targeted DNA sequencing")
     (description
@@ -19146,7 +19182,9 @@ (define-public python-cgatcore
      (list python-pytest
            lsof
            inetutils
-           openssl))
+           openssl
+           python-setuptools
+           python-wheel))
     (inputs (list time))
     (propagated-inputs
      (list python-apsw
@@ -19647,6 +19685,7 @@ (define-public python-dna-features-viewer
      (list python-biopython
            python-matplotlib
            python-packaging))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page
      "https://github.com/Edinburgh-Genome-Foundry/DnaFeaturesViewer")
     (synopsis "Plot features from DNA sequences")
diff --git a/gnu/packages/bootloaders.scm b/gnu/packages/bootloaders.scm
index 069d02b52c..ec8cddbd16 100644
--- a/gnu/packages/bootloaders.scm
+++ b/gnu/packages/bootloaders.scm
@@ -69,6 +69,7 @@ (define-module (gnu packages bootloaders)
   #:use-module (gnu packages valgrind)
   #:use-module (gnu packages virtualization)
   #:use-module (gnu packages xorg)
+  #:use-module (gnu packages python-build)
   #:use-module (gnu packages python-web)
   #:use-module (gnu packages python-xyz)
   #:use-module (guix build-system gnu)
@@ -676,6 +677,8 @@ (define u-boot
            pkg-config                   ;for 'make menuconfig'
            python
            python-pyelftools
+           python-setuptools
+           python-wheel
            swig
            (list util-linux "lib")))
     (home-page "https://www.denx.de/wiki/U-Boot/")
diff --git a/gnu/packages/check.scm b/gnu/packages/check.scm
index 71dc9eb54b..691fe67605 100644
--- a/gnu/packages/check.scm
+++ b/gnu/packages/check.scm
@@ -833,7 +833,7 @@ (define-public python-gixy
                  (base32
                   "0gymjcnvjx9snyrzdbmjnk93ibb161q72xam29vnl3yyac4r1330"))))
       (build-system pyproject-build-system)
-      (native-inputs (list python-nose))
+      (native-inputs (list python-nose python-setuptools python-wheel))
       (propagated-inputs
        (list python-cached-property python-configargparse
              python-jinja2 python-six
@@ -1705,7 +1705,8 @@ (define-public python-pytest-forked
      ;; 'hypothesis' plugin is not in the environment (due to
      ;; <http://issues.guix.gnu.org/25235>), which would cause the test suite
      ;; to fail (see: https://github.com/pytest-dev/pytest-forked/issues/54).
-     (list python-pytest-bootstrap python-setuptools-scm))
+     (list python-pytest-bootstrap python-setuptools
+           python-setuptools-scm python-wheel))
     (home-page "https://github.com/pytest-dev/pytest-forked")
     (synopsis "Pytest plugin to run tests in isolated forked subprocesses")
     (description "This package provides a Pytest plugin which enables running
@@ -2030,7 +2031,7 @@ (define-public python-pytest-asyncio
                  (lambda _
                    (setenv "SETUPTOOLS_SCM_PRETEND_VERSION"
                            #$(package-version this-package)))))))
-    (native-inputs (list python-setuptools-scm))
+    (native-inputs (list python-setuptools-scm python-setuptools python-wheel))
     (propagated-inputs (list python-pytest))
     (home-page "https://github.com/pytest-dev/pytest-asyncio")
     (synopsis "Pytest support for asyncio")
@@ -2467,7 +2468,9 @@ (define-public python-pytest-mypy-plugins
                              python-mypy
                              python-pytest
                              python-pyyaml
+                             python-setuptools
                              python-regex))
+    (native-inputs (list python-wheel))
     (home-page "https://github.com/TypedDjango/pytest-mypy-plugins")
     (synopsis "Pytest plugin for writing tests for mypy plugins")
     (description "This package provides a pytest plugin for writing tests for
diff --git a/gnu/packages/databases.scm b/gnu/packages/databases.scm
index 762d8bd402..bdaa84f00a 100644
--- a/gnu/packages/databases.scm
+++ b/gnu/packages/databases.scm
@@ -3062,6 +3062,7 @@ (define-public python-aiomysql
     (build-system pyproject-build-system)
     (arguments '(#:tests? #f))           ;test suite requires docker
     (propagated-inputs (list python-pymysql))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/aio-libs/aiomysql")
     (synopsis "MySQL driver for Python")
     (description "@code{aiomysql} is a driver for accessing a MySQL database
@@ -3452,7 +3453,8 @@ (define-public python-sqlalchemy
     (build-system pyproject-build-system)
     (native-inputs
      (list python-cython ; for C extensions
-           python-pytest python-mock python-pytest-xdist)) ; for tests
+           python-pytest python-mock python-pytest-xdist ; for tests
+           python-setuptools python-wheel))
     (propagated-inputs
      (list python-greenlet))
     (arguments
@@ -3646,7 +3648,7 @@ (define-public python-sqlite-utils
     (propagated-inputs (list python-click python-click-default-group
                              python-dateutil python-sqlite-fts4
                              python-tabulate))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/simonw/sqlite-utils")
     (synopsis
      "CLI tool and Python utility functions for manipulating SQLite databases")
@@ -3719,7 +3721,7 @@ (define-public python-apsw
          "02sgja00azvd08wi2wm105apmhp2644s7aw9b1zdg3dkcwjnsiad"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-cython unzip))
+     (list python-cython unzip python-setuptools python-wheel))
     (inputs (list sqlite-next))         ;SQLite 3.40 required.
     (arguments
      (list
@@ -4039,7 +4041,7 @@ (define-public python-hiredis
                             ;; The fix was forwarded upstream, see:
                             ;; https://github.com/redis/hiredis-py/pull/160.
                             (delete-file "tests/__init__.py"))))))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (inputs (list hiredis))
     (home-page "https://github.com/redis/hiredis-py")
     (synopsis "Python extension that wraps protocol parsing code in hiredis")
@@ -4131,6 +4133,8 @@ (define-public python-redis
      (list python-pytest
            python-pytest-asyncio
            python-pytest-timeout
+           python-setuptools
+           python-wheel
            redis))
     (propagated-inputs
      (list python-async-timeout))
diff --git a/gnu/packages/digest.scm b/gnu/packages/digest.scm
index 481771804b..e478f43b77 100644
--- a/gnu/packages/digest.scm
+++ b/gnu/packages/digest.scm
@@ -123,7 +123,7 @@ (define-public python-xxhash
          "1hdxcscry59gh0znlm71ya23mm9rfmvz8lvvlplzxzf63pib28dc"))))
     (build-system pyproject-build-system)
     ;; Needed to embed the correct version string
-    (native-inputs (list python-setuptools-scm))
+    (native-inputs (list python-setuptools-scm python-setuptools python-wheel))
     (home-page "https://github.com/ifduyue/python-xxhash")
     (synopsis "Python binding for xxHash")
     (description "This package provides Python bindings for the xxHash hash
diff --git a/gnu/packages/engineering.scm b/gnu/packages/engineering.scm
index 88f541d2e9..11b85508ec 100644
--- a/gnu/packages/engineering.scm
+++ b/gnu/packages/engineering.scm
@@ -2530,7 +2530,7 @@ (define-public python-pyvisa
                             (substitute* "pyvisa/shell.py"
                               (("from .thirdparty import prettytable")
                                "import prettytable")))))))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (propagated-inputs (list python-dataclasses python-prettytable
                              python-typing-extensions))
     (home-page "https://pyvisa.readthedocs.io/en/latest/")
diff --git a/gnu/packages/fontutils.scm b/gnu/packages/fontutils.scm
index 153602b4b4..7b0123b6cb 100644
--- a/gnu/packages/fontutils.scm
+++ b/gnu/packages/fontutils.scm
@@ -491,7 +491,8 @@ (define-public python-ufo2ft
        (sha256
         (base32 "1rg2997af8blvswlwif0kpz2vxrlh555gzqslz6yv9y7i7v8lphl"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-pytest python-setuptools-scm))
+    (native-inputs (list python-pytest python-setuptools-scm
+                         python-setuptools python-wheel))
     (propagated-inputs
      (list python-booleanoperations
            python-cffsubr
@@ -638,7 +639,8 @@ (define-public python-glyphslib
                        "--ignore=tests/builder/interpolation_test.py")))
     (native-inputs
      (list python-setuptools-scm
-
+           python-setuptools
+           python-wheel
            ;; For tests.
            python-pytest
            python-xmldiff))
@@ -809,6 +811,7 @@ (define-public psautohint
            python-pytest-cov
            python-pytest-xdist
            python-setuptools-scm
+           python-setuptools
            python-wheel))
     (home-page "https://github.com/adobe-type-tools/psautohint")
     (synopsis "Adobe's PostScript autohinter")
@@ -1693,7 +1696,7 @@ (define-public python-ufolib2
         (base32 "0yx4i8q5rfyqhr2fj70a7z1bp1jv7bdlr64ww9z4nv9ycbda4x9j"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest python-setuptools-scm))
+     (list python-pytest python-setuptools-scm python-setuptools python-wheel))
     (propagated-inputs (list python-attrs python-fonttools))
     (home-page "https://github.com/fonttools/ufoLib2")
     (synopsis "Unified Font Object (UFO) font processing library")
diff --git a/gnu/packages/geo.scm b/gnu/packages/geo.scm
index cce4f2518d..78d683d9e4 100644
--- a/gnu/packages/geo.scm
+++ b/gnu/packages/geo.scm
@@ -351,7 +351,9 @@ (define-public python-h3
            python-numpy
            python-pytest
            python-scikit-build
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (inputs (list h3))
     (home-page "https://uber.github.io/h3-py")
     (synopsis "Python bindings for H3")
@@ -959,7 +961,7 @@ (define-public python-geopandas
       (list python-fiona python-packaging python-pandas python-pyproj
             python-shapely))
     (native-inputs
-      (list python-pytest))
+      (list python-pytest python-setuptools python-wheel))
     (home-page "https://geopandas.org")
     (synopsis "Geographic pandas extensions")
     (description "The goal of GeoPandas is to make working with
@@ -1014,7 +1016,7 @@ (define-public python-osmnx
             python-rtree
             python-shapely))
     (native-inputs
-      (list python-numpy python-pytest))
+      (list python-numpy python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/gboeing/osmnx")
     (synopsis
       "Retrieve, model, analyze, and visualize OpenStreetMap street networks")
@@ -1801,7 +1803,7 @@ (define-public python-rtree
                   (string-append  "\"" libspatialindex
                                   "/lib/libspatialindex_c.so\"")))))))))
     (native-inputs
-     (list python-numpy python-pytest python-wheel))
+     (list python-numpy python-pytest python-setuptools python-wheel))
     (inputs
      (list libspatialindex))
     (home-page "https://github.com/Toblerity/rtree")
diff --git a/gnu/packages/graph.scm b/gnu/packages/graph.scm
index 5510828edf..013ef1a193 100644
--- a/gnu/packages/graph.scm
+++ b/gnu/packages/graph.scm
@@ -185,7 +185,7 @@ (define-public python-igraph
                                   (string-append igraph "/lib")))))))))))
       (inputs (list igraph))
       (propagated-inputs (list python-texttable))
-      (native-inputs (list python-pytest))
+      (native-inputs (list python-pytest python-setuptools python-wheel))
       (home-page "https://igraph.org/python/")
       (synopsis "Python bindings for the igraph network analysis library"))))
 
@@ -353,11 +353,12 @@ (define-public python-vtraag-louvain
                (("self.use_pkgconfig = False")
                 "self.use_pkgconfig = True")))))))
     (inputs (list igraph))
-    (propagated-inputs (list python-igraph))
+    (propagated-inputs (list python-igraph python-setuptools))
     (native-inputs
      (list pkg-config
            python-ddt
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-wheel))
     (home-page "https://github.com/vtraag/louvain")
     (synopsis "Community detection in large networks")
     (description
diff --git a/gnu/packages/graphviz.scm b/gnu/packages/graphviz.scm
index b91c365e3c..9ba62f9e07 100644
--- a/gnu/packages/graphviz.scm
+++ b/gnu/packages/graphviz.scm
@@ -156,7 +156,9 @@ (define-public python-graphviz
            python-mock
            python-pytest
            python-pytest-cov
-           python-pytest-mock))
+           python-pytest-mock
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/xflr6/graphviz")
     (synopsis "Simple Python interface for Graphviz")
     (description
diff --git a/gnu/packages/license.scm b/gnu/packages/license.scm
index ca8375d19d..3a10bf3293 100644
--- a/gnu/packages/license.scm
+++ b/gnu/packages/license.scm
@@ -179,13 +179,14 @@ (define-public reuse
         (base32 "0ij2mpdnawjabnsy291157wzci9050dfclwib95phg7pnmd6xsw0"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-poetry-core python-pytest))
+     (list python-poetry-core python-pytest python-wheel))
     (inputs
      (list python-binaryornot
            python-boolean.py
            python-debian
            python-jinja2
-           python-license-expression))
+           python-license-expression
+           python-setuptools)) ; For pkg_resources.
     (home-page "https://reuse.software/")
     (synopsis "Provide and verify copyright and licensing information")
     (description
diff --git a/gnu/packages/machine-learning.scm b/gnu/packages/machine-learning.scm
index 3923573d57..0532083b02 100644
--- a/gnu/packages/machine-learning.scm
+++ b/gnu/packages/machine-learning.scm
@@ -745,7 +745,7 @@ (define-public python-sacrebleu
                              python-portalocker
                              python-regex
                              python-tabulate))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/mjpost/sacrebleu")
     (synopsis
      "Compute shareable, comparable, and reproducible BLEU, chrF, and TER scores")
@@ -786,7 +786,7 @@ (define-public python-spacy-legacy
     (build-system pyproject-build-system)
     ;; This package depends on spacy, which depends on this package.
     (arguments (list #:tests? #false))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://spacy.io")
     (synopsis "Legacy registered functions for spaCy backwards compatibility")
     (description
@@ -807,7 +807,7 @@ (define-public python-spacy-loggers
     (build-system pyproject-build-system)
     ;; This package depends on spacy, which depends on this package.
     (arguments (list #:tests? #false))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/explosion/spacy-loggers")
     (synopsis "Logging utilities for SpaCy")
     (description "This package provides logging utilities for the SpaCy
@@ -1548,7 +1548,7 @@ (define-public python-tslearn
                              python-scikit-learn
                              tensorflow
                              python-wheel))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/tslearn-team/tslearn")
     (synopsis "Machine learning toolkit for time series data")
     (description "This is a Python library for time series data mining.
@@ -1594,7 +1594,9 @@ (define-public python-imbalanced-learn
            python-mypy
            python-pandas
            python-pytest
-           python-pytest-cov))
+           python-pytest-cov
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/scikit-learn-contrib/imbalanced-learn")
     (synopsis "Toolbox for imbalanced dataset in machine learning")
     (description "This is a Python package offering a number of re-sampling
@@ -1737,7 +1739,9 @@ (define-public python-cleanlab
     (native-inputs
      (list python-pytest
            python-pytorch
-           python-torchvision))
+           python-torchvision
+           python-setuptools
+           python-wheel))
     (home-page "https://cleanlab.ai")
     (synopsis "Automatically find and fix dataset issues")
     (description
@@ -1769,7 +1773,7 @@ (define-public python-cleanlab-1
            python-scipy
            python-tqdm))
     (native-inputs
-     (list python-pytest))))
+     (list python-pytest python-setuptools python-wheel))))
 
 (define-public python-cma
   (package
@@ -1810,7 +1814,7 @@ (define-public python-cmaes
         (base32 "1f3143w8ii6i93bdh65iazrq1lryccd805ndnqww5l8h7qnnzpkm"))
        (file-name (git-file-name name version))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-hypothesis))
+    (native-inputs (list python-hypothesis python-setuptools python-wheel))
     (propagated-inputs (list python-numpy))
     (home-page "https://github.com/CyberAgentAILab/cmaes")
     (synopsis "CMA-ES implementation for Python")
@@ -1981,13 +1985,15 @@ (define-public python-hyperopt
            python-networkx
            python-numpy
            python-scipy
+           python-setuptools ; For pkg_resources.
            python-six
            python-tqdm))
     (native-inputs
      (list python-black
            python-nose
            python-pymongo
-           python-pytest))
+           python-pytest
+           python-wheel))
     (home-page "https://hyperopt.github.io/hyperopt/")
     (synopsis "Library for hyperparameter optimization")
     (description "Hyperopt is a Python library for serial and parallel
@@ -3537,7 +3543,8 @@ (define-public python-tensorly
          ;; nor is examples/plot_quantum.py
          " and not test_file_is_generated"))))
     (propagated-inputs (list python-jsmin python-numpy python-scipy))
-    (native-inputs (list python-pytest python-pytest-cov python-sphinx))
+    (native-inputs (list python-pytest python-pytest-cov python-sphinx
+                         python-setuptools python-wheel))
     (home-page "https://github.com/tensorly/tensorly")
     (synopsis "Tensor learning in Python")
     (description
@@ -3966,6 +3973,7 @@ (define-public python-readchar
            (lambda _
              (delete-file "readchar/_win_read.py"))))))
     (propagated-inputs (list python-setuptools))
+    (native-inputs (list python-wheel))
     (home-page "https://github.com/magmax/python-readchar")
     (synopsis "Library to easily read single chars and key strokes")
     (description "This package provides a Python library to easily read single
@@ -4199,7 +4207,7 @@ (define-public python-torchvision
            python-pillow-simd
            python-pytorch))
     (native-inputs
-     (list which python-pytest))
+     (list which python-pytest python-setuptools python-wheel))
     (home-page "https://pytorch.org/vision/stable/index.html")
     (synopsis "Datasets, transforms and models specific to computer vision")
     (description
@@ -4747,7 +4755,7 @@ (define-public python-brian2
                              ;; Required by codegen.
                              python-setuptools
                              python-sympy))
-    (native-inputs (list python-pytest python-pytest-xdist))
+    (native-inputs (list python-pytest python-pytest-xdist python-wheel))
     (home-page "https://briansimulator.org/")
     (synopsis "Clock-driven simulator for spiking neural networks")
     (description
diff --git a/gnu/packages/maths.scm b/gnu/packages/maths.scm
index 0d74a2a55c..112e3ee071 100644
--- a/gnu/packages/maths.scm
+++ b/gnu/packages/maths.scm
@@ -161,6 +161,7 @@ (define-module (gnu packages maths)
   #:use-module (gnu packages pkg-config)
   #:use-module (gnu packages pulseaudio)
   #:use-module (gnu packages python)
+  #:use-module (gnu packages python-build)
   #:use-module (gnu packages python-web)
   #:use-module (gnu packages python-xyz)
   #:use-module (gnu packages qt)
@@ -4823,7 +4824,8 @@ (define-public python-blis
              (invoke "python" "setup.py" "build_ext" "--inplace"
                      "-j" (number->string (parallel-job-count))))))))
     (propagated-inputs (list python-numpy))
-    (native-inputs (list python-cython python-pytest))
+    (native-inputs (list python-cython python-pytest
+                         python-setuptools python-wheel))
     (home-page "https://github.com/explosion/cython-blis")
     (synopsis "Blis as a self-contained C-extension for Python")
     (description
diff --git a/gnu/packages/monitoring.scm b/gnu/packages/monitoring.scm
index 3238f11fb4..b979feff06 100644
--- a/gnu/packages/monitoring.scm
+++ b/gnu/packages/monitoring.scm
@@ -326,7 +326,7 @@ (define-public zabbix-cli
                                      (string-append #$output "/share/doc/"
                                                     #$name "/html")))))))
     (native-inputs
-     (list python-pytest python-sphinx))
+     (list python-pytest python-sphinx python-setuptools python-wheel))
     (inputs
      (list ncurses python-requests))
     (home-page "https://github.com/unioslo/zabbix-cli")
diff --git a/gnu/packages/package-management.scm b/gnu/packages/package-management.scm
index dacd4433d2..4aedbd613c 100644
--- a/gnu/packages/package-management.scm
+++ b/gnu/packages/package-management.scm
@@ -963,7 +963,8 @@ (define-public python-anaconda-client
          (add-before 'check 'set-HOME
            (lambda _ (setenv "HOME" "/tmp"))))))
     (propagated-inputs
-     (list python-clyent python-nbformat python-pyyaml python-requests))
+     (list python-clyent python-nbformat python-pyyaml python-requests
+           python-setuptools))
     (native-inputs
      (list python-coverage
            python-dateutil
@@ -971,7 +972,8 @@ (define-public python-anaconda-client
            python-mock
            python-pillow
            python-pytest
-           python-pytz))
+           python-pytz
+           python-wheel))
     (home-page "https://github.com/Anaconda-Platform/anaconda-client")
     (synopsis "Anaconda Cloud command line client library")
     (description
diff --git a/gnu/packages/potassco.scm b/gnu/packages/potassco.scm
index e6d7caa1ef..217521f37b 100644
--- a/gnu/packages/potassco.scm
+++ b/gnu/packages/potassco.scm
@@ -341,7 +341,8 @@ (define-public python-clorm
                     (("self\\.assertTrue\\(os_called\\)" all)
                      (string-append "# " all))))))))
    (propagated-inputs (list python-clingo))
-   (native-inputs (list python-typing-extensions))
+   (native-inputs (list python-typing-extensions
+                        python-setuptools python-wheel))
    (home-page "https://potassco.org")
    (synopsis "Object relational mapping to clingo")
    (description "@acronym{Clorm, Clingo ORM} provides an @acronym{ORM,
@@ -366,6 +367,7 @@ (define-public python-telingo
                 "0g3khxfdzc2hc7dkiyyqhb399h6h21m5wkp6wy8w71n0m32fiy53"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-clingo))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://potassco.org/")
     (synopsis "Solve dynamic temporal logic programs")
     (description "This package provides a system to solve dynamic temporal
@@ -393,8 +395,10 @@ (define-public python-clingraph
                              python-imageio
                              python-jinja2
                              python-jsonschema
-                             python-networkx))
-    (native-inputs (list dot2tex graphviz python-pylint python-pytest))
+                             python-networkx
+                             python-setuptools))
+    (native-inputs (list dot2tex graphviz python-pylint python-pytest
+                         python-wheel))
     (home-page "https://github.com/potassco/clingraph")
     (synopsis "Visualizer for graphs defined as logic programs")
     (description
diff --git a/gnu/packages/protobuf.scm b/gnu/packages/protobuf.scm
index 8cd805799e..d026809093 100644
--- a/gnu/packages/protobuf.scm
+++ b/gnu/packages/protobuf.scm
@@ -381,7 +381,9 @@ (define-public python-mypy-protobuf
      (list python-grpc-stubs
            python-grpcio-tools
            python-pytest
-           python-typing-extensions))
+           python-typing-extensions
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list protobuf
            python-protobuf
diff --git a/gnu/packages/python-build.scm b/gnu/packages/python-build.scm
index 154c97e9e4..faaffbcea5 100644
--- a/gnu/packages/python-build.scm
+++ b/gnu/packages/python-build.scm
@@ -206,7 +206,7 @@ (define-public python-trove-classifiers
     (build-system pyproject-build-system)
     (arguments (list #:build-backend "setuptools.build_meta"
                      #:tests? #f))      ;keep dependencies to a minimum
-    (native-inputs (list python-wheel))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/pypa/trove-classifiers")
     (synopsis "Canonical source for classifiers on PyPI")
     (description "This package is the canonical source for classifiers use on
@@ -588,6 +588,7 @@ (define-public python-flit-scm
                      ;; builder instead.
                      #:build-backend "setuptools.build_meta"))
     (propagated-inputs (list python-flit-core python-setuptools-scm python-tomli))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://gitlab.com/WillDaSilva/flit_scm")
     (synopsis "PEP 518 build backend combining flit_core and setuptools_scm")
     (description "This package provides a PEP 518 build backend that uses
diff --git a/gnu/packages/python-check.scm b/gnu/packages/python-check.scm
index 87e15b4560..92289cb164 100644
--- a/gnu/packages/python-check.scm
+++ b/gnu/packages/python-check.scm
@@ -586,7 +586,9 @@ (define-public python-pytest-doctestplus
                          " and not test_remote_data_requires"
                          " and not test_remote_data_ignore_warnings"))))
     (native-inputs
-     (list python-numpy python-pytest python-setuptools-scm))
+     (list python-numpy python-pytest python-setuptools python-setuptools-scm
+           python-wheel))
+    (propagated-inputs (list python-setuptools)) ;; for pkg_resources
     (home-page "https://github.com/astropy/pytest-doctestplus")
     (synopsis "Pytest plugin with advanced doctest features")
     (description
@@ -636,7 +638,9 @@ (define-public python-pytest-filter-subpackage
      (list python-pytest
            python-pytest-cov
            python-pytest-doctestplus
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/astropy/pytest-filter-subpackage")
     (synopsis "Pytest plugin for filtering based on sub-packages")
     (description
@@ -749,7 +753,8 @@ (define-public python-pytest-remotedata
                                  "not test_default_behavior"
                                  " and not test_strict_with_decorator"))))
     (native-inputs
-     (list python-pytest python-setuptools-scm))
+     (list python-pytest python-setuptools python-setuptools-scm
+           python-wheel))
     (propagated-inputs
      (list python-packaging))
     (home-page "https://github.com/astropy/pytest-remotedata")
@@ -843,7 +848,9 @@ (define-public python-pytest-mpl
      (list
       #:test-flags #~(list "-m" "mpl_image_compare")))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-jinja2
            python-matplotlib
@@ -1770,7 +1777,8 @@ (define-public python-memory-profiler
                 (("def test_memory_profiler")
                  "def __off_test_memory_profiler")))))))
     (native-inputs
-     (list python-pytest python-pytest-fixture-config python-safety))
+     (list python-pytest python-pytest-fixture-config python-safety
+           python-setuptools python-wheel))
     (propagated-inputs (list python-psutil))
     (home-page "https://github.com/pythonprofilers/memory_profiler")
     (synopsis "Memory profiler for Python")
@@ -2178,7 +2186,7 @@ (define-public python-aioresponses
     (native-inputs
      (list python-pbr python-ddt python-pytest))
     (propagated-inputs
-     (list python-aiohttp))
+     (list python-aiohttp python-setuptools))
     (home-page "https://github.com/pnuckowski/aioresponses")
     (synopsis "Mock out requests made by ClientSession from aiohttp package")
     (description
@@ -2390,7 +2398,9 @@ (define-public python-nox
     (native-inputs
      (list python-jinja2
            python-pytest
-           python-tox))
+           python-tox
+           python-setuptools
+           python-wheel))
     (home-page "https://nox.thea.codes/")
     (synopsis "Flexible test automation")
     (description
@@ -2544,7 +2554,8 @@ (define-public python-pytest-httpx
         (base32 "1ncpd74hmsz4sadvjg99fnfscxpgh3mc2siini0dhxzwgwdkk5i7"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-httpx))
-    (native-inputs (list python-pytest python-pytest-asyncio))
+    (native-inputs (list python-pytest python-pytest-asyncio python-setuptools
+                         python-wheel))
     (home-page "https://colin-b.github.io/pytest_httpx/")
     (synopsis "Pytest plugin to mock httpx")
     (description "This package provides a pytest fixture to mock httpx
@@ -2621,7 +2632,8 @@ (define-public python-vulture
                                       ;; skip test that uses python-pint
                                       ;; pint has many dependencies
                                       "not test_whitelists_with_python")))))))
-    (native-inputs (list python-pytest python-pytest-cov))
+    (native-inputs (list python-pytest python-pytest-cov
+                         python-setuptools python-wheel))
     (propagated-inputs (list python-toml))
     (home-page "https://github.com/jendrikseipp/vulture")
     (synopsis "Find dead Python code")
diff --git a/gnu/packages/python-compression.scm b/gnu/packages/python-compression.scm
index 000d42524d..9d8ea8b156 100644
--- a/gnu/packages/python-compression.scm
+++ b/gnu/packages/python-compression.scm
@@ -64,7 +64,9 @@ (define-public python-multivolumefile
            python-hypothesis
            python-pyannotate
            python-pytest
-           python-pytest-cov))
+           python-pytest-cov
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/miurahr/multivolume")
     (synopsis "Treat multiple files as one")
     (description "MultiVolumefile is a Python library that provides a
@@ -89,7 +91,9 @@ (define-public python-pybcj
            python-hypothesis
            python-pytest
            python-pytest-cov
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://codeberg.org/miurahr/pybcj")
     (synopsis "BCJ filter library")
     (description "In data compression, BCJ, short for Branch-Call-Jump, refers
@@ -149,6 +153,7 @@ (define-public python-brotlicffi
                (setenv "USE_SHARED_BROTLI" "1"))))))
     (propagated-inputs (list python-cffi))
     (inputs (list brotli))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/python-hyper/brotlicffi")
     (synopsis "Python CFFI bindings to the Brotli library")
     (description "This package provides Python CFFI bindings to the Brotli
@@ -170,7 +175,9 @@ (define-public python-inflate64
     (native-inputs
      (list python-pyannotate
            python-pytest
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://pypi.org/project/inflate64/")
     (synopsis "deflate64 compression/decompression library")
     (description "The @code{inflate64} package provides @code{Deflater} and
@@ -199,7 +206,7 @@ (define-public python-isal
          (add-after 'unpack 'use-dynamic-linking
            (lambda _ (setenv "PYTHON_ISAL_LINK_DYNAMIC" "1"))))))
     (inputs (list isa-l))
-    (native-inputs (list python-cython))
+    (native-inputs (list python-cython python-setuptools python-wheel))
     (home-page "https://github.com/pycompression/python-isal")
     (synopsis "Python bindings for the ISA-L compression library")
     (description
@@ -225,7 +232,9 @@ (define-public python-pyppmd
            python-pytest-benchmark
            python-pytest-cov
            python-pytest-timeout
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/miurahr/pyppmd")
     (synopsis "PPMd compression/decompression library")
     (description "Pyppmd provides classes and functions for compressing and
@@ -287,6 +296,7 @@ (define-public python-py7zr
            python-texttable))
     (native-inputs
      (list python-setuptools
+           python-wheel
            python-setuptools-scm
            python-coverage
            python-coveralls
diff --git a/gnu/packages/python-crypto.scm b/gnu/packages/python-crypto.scm
index 44babe5c8c..fb4cc63097 100644
--- a/gnu/packages/python-crypto.scm
+++ b/gnu/packages/python-crypto.scm
@@ -298,7 +298,9 @@ (define-public python-keyring
     (native-inputs
      (list python-toml
            python-pytest
-           python-setuptools-scm))
+           python-setuptools
+           python-setuptools-scm
+           python-wheel))
     (propagated-inputs
      (list python-importlib-metadata
            python-jaraco-classes
@@ -530,7 +532,9 @@ (define-public python-cryptography
            python-pretend
            python-pytest                ;for subtests
            python-pytest-benchmark
-           python-pytest-subtests))
+           python-pytest-subtests
+           python-setuptools
+           python-wheel))
     (inputs (list python-cryptography-rust))
     (propagated-inputs (list python-cffi))
     (home-page "https://github.com/pyca/cryptography")
@@ -1038,7 +1042,7 @@ (define-public python-m2crypto
        ;; certificates.
        #:tests? #f))
     (inputs (list openssl))
-    (native-inputs (list swig))
+    (native-inputs (list swig python-setuptools python-wheel))
     (home-page "https://gitlab.com/m2crypto/m2crypto")
     (synopsis "Python crypto and TLS toolkit")
     (description "@code{M2Crypto} is a complete Python wrapper for OpenSSL
@@ -1218,7 +1222,8 @@ (define-public python-service-identity
                (base32
                 "0d4x84crbz0a17d8gi90z6zlxwm9pslc65rx0cdw2797ra360v3f"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-idna python-pytest))
+    (native-inputs (list python-idna python-pytest python-setuptools
+                         python-wheel))
     (propagated-inputs (list python-attrs python-cryptography python-pyasn1
                              python-pyasn1-modules python-six))
     (home-page "https://service-identity.readthedocs.io/")
@@ -1449,6 +1454,8 @@ (define-public python-trustme
            python-pytest
            python-pytest-cov
            python-service-identity
+           python-setuptools
+           python-wheel
            python-zipp))
     (propagated-inputs
      (list python-cryptography
diff --git a/gnu/packages/python-science.scm b/gnu/packages/python-science.scm
index 86ba4209fd..538a3c2f0e 100644
--- a/gnu/packages/python-science.scm
+++ b/gnu/packages/python-science.scm
@@ -191,9 +191,12 @@ (define-public python-scikit-allel
              (invoke "python" "setup.py" "build_ext" "--inplace"))))))
     (propagated-inputs
      (list python-dask
-           python-numpy))
+           python-numpy
+           python-click))
     (native-inputs
      (list python-cython
+           python-setuptools
+           python-wheel
            ;; The following are all needed for the tests
            htslib
            python-h5py
@@ -340,7 +343,7 @@ (define-public python-scikit-optimize
            python-scikit-learn
            python-scipy))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://scikit-optimize.github.io/")
     (synopsis "Sequential model-based optimization toolbox")
     (description "Scikit-Optimize, or @code{skopt}, is a simple and efficient
@@ -743,7 +746,7 @@ (define-public python-xarray
                 "1339fz5gxkizq02h6vn19546x9p4c3nd9ipzpcg39h7gwhg26yi6"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-setuptools-scm python-pytest))
+     (list python-setuptools python-setuptools-scm python-pytest python-wheel))
     (propagated-inputs
      (list python-numpy python-pandas))
     (home-page "https://github.com/pydata/xarray")
@@ -840,7 +843,8 @@ (define-public python-pytensor
                         "--ignore" "tests/tensor/"
                         "--ignore" "tests/sandbox/"
                         "--ignore" "tests/sparse/sandbox/")))))))
-    (native-inputs (list python-cython python-pytest python-versioneer))
+    (native-inputs (list python-cython python-pytest python-versioneer
+                         python-setuptools python-wheel))
     (propagated-inputs (list python-cons
                              python-etuples
                              python-filelock
@@ -1585,7 +1589,9 @@ (define-public python-plotnine
            python-mock
            python-pandas
            python-pytest python-pytest-cov
-           tzdata-for-tests))
+           tzdata-for-tests
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/has2k1/plotnine")
     (synopsis "Grammar of Graphics for Python")
     (description
@@ -2035,7 +2041,8 @@ (define-public python-libneuroml
                 "0mrm4rd6x1sm6hkvhk20mkqp9q53sl3lbvq6hqzyymkw1iqq6bhy"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-lxml python-six))
-    (native-inputs (list python-pytest python-numpy python-tables))
+    (native-inputs (list python-pytest python-numpy python-tables
+                         python-setuptools python-wheel))
     (home-page "https://libneuroml.readthedocs.org/en/latest/")
     (synopsis
      "Python library for working with NeuroML descriptions of neuronal models")
diff --git a/gnu/packages/python-web.scm b/gnu/packages/python-web.scm
index 17c9563cfd..1389a15934 100644
--- a/gnu/packages/python-web.scm
+++ b/gnu/packages/python-web.scm
@@ -304,7 +304,8 @@ (define-public python-aiosignal
     (build-system pyproject-build-system)
     (arguments (list #:test-flags #~(list "tests")))
     (propagated-inputs (list python-frozenlist))
-    (native-inputs (list python-pytest python-pytest-asyncio python-pytest-cov))
+    (native-inputs (list python-pytest python-pytest-asyncio python-pytest-cov
+                         python-setuptools python-wheel))
     (home-page "https://github.com/aio-libs/aiosignal")
     (synopsis "Callback manager for Python @code{asyncio} projects")
     (description "This Python module provides @code{Signal}, an abstraction to
@@ -601,7 +602,9 @@ (define-public python-asgi-csrf
                          python-httpx
                          python-pytest
                          python-pytest-asyncio
-                         python-starlette))
+                         python-starlette
+                         python-setuptools
+                         python-wheel))
     (home-page "https://github.com/simonw/asgi-csrf")
     (synopsis "ASGI middleware for protecting against CSRF attacks")
     (description "This Asynchronous Server Gateway Interface (ASGI)
@@ -642,7 +645,9 @@ (define-public python-asgi-lifespan
     (native-inputs (list python-pytest
                          python-pytest-asyncio
                          python-pytest-trio
-                         python-starlette))
+                         python-starlette
+                         python-setuptools
+                         python-wheel))
     (propagated-inputs (list python-sniffio))
     (home-page "https://github.com/florimondmanca/asgi-lifespan")
     (synopsis "Programmatic startup/shutdown of ASGI apps")
@@ -764,7 +769,9 @@ (define-public python-aws-xray-sdk
            python-pytest-benchmark
            python-requests
            python-sqlalchemy
-           python-webtest))
+           python-webtest
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-aiohttp
            python-botocore
@@ -1101,7 +1108,9 @@ (define-public python-httplib2
            python-pytest-randomly
            python-pytest-timeout
            python-pytest-xdist
-           python-six))
+           python-six
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/httplib2/httplib2")
     (synopsis "Comprehensive HTTP client library")
     (description
@@ -1419,7 +1428,7 @@ (define-public python-h11
         (base32 "17b97c56y31hi0n0x0cb5a87x4xr9jijf2y06pzj0977k6zgn6cg"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/python-hyper/h11")
     (synopsis "Pure-Python, bring-your-own-I/O implementation of HTTP/1.1")
     (description
@@ -1825,7 +1834,9 @@ (define-public python-omnipath
            python-pytest-mock
            python-requests-mock
            python-setuptools-scm
-           python-tox))
+           python-tox
+           python-setuptools
+           python-wheel))
     (home-page "https://omnipathdb.org/")
     (synopsis "Python client for the OmniPath web service")
     (description "This package provides a Python client for the OmniPath web
@@ -1855,7 +1866,7 @@ (define-public python-openai
     (propagated-inputs (list python-aiohttp python-requests python-tqdm
                              python-typing-extensions))
     (native-inputs (list python-black python-pytest python-pytest-asyncio
-                         python-pytest-mock))
+                         python-pytest-mock python-setuptools python-wheel))
     (home-page "https://github.com/openai/openai-python")
     (synopsis "Python client library for the OpenAI API")
     (description "This package provides a Python client library for the
@@ -2060,7 +2071,7 @@ (define-public python-cssutils
                                      "and not encutils "
                                      "and not website.logging")))))))
     (native-inputs
-     (list python-pytest python-jaraco-test))
+     (list python-pytest python-jaraco-test python-setuptools python-wheel))
     (home-page "https://github.com/jaraco/cssutils")
     (synopsis
       "CSS Cascading Style Sheets library for Python")
@@ -2243,7 +2254,7 @@ (define-public python-wsproto
         (base32 "0rahm1j9danv1l6i6as80acwv16ycihxkhrvwjiqh9drxhk5ymmd"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (propagated-inputs
      (list python-h11))
     (home-page "https://github.com/python-hyper/wsproto/")
@@ -3728,7 +3739,7 @@ (define-public python-flask-compress
                 "178jzz6jxlxllcjqamzh5q7ahfh90m5cl1il9vmjs3xhz65z35pf"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-brotli python-flask))
-    (native-inputs (list python-setuptools-scm))
+    (native-inputs (list python-setuptools-scm python-setuptools python-wheel))
     (home-page "https://github.com/colour-science/flask-compress")
     (synopsis "Compress responses in a Flask app")
     (description
@@ -3751,6 +3762,7 @@ (define-public python-flask-seasurf
     (arguments
      (list #:tests? #false)) ;there are none
     (propagated-inputs (list python-flask))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/maxcountryman/flask-seasurf/")
     (synopsis "CSRF extension for Flask")
     (description "SeaSurf is a Flask extension for preventing cross-site
@@ -4182,7 +4194,7 @@ (define-public python-flask-jwt
                             "flask_jwt/__init__.py")
                (("access_token.decode\\('utf-8'\\)") "access_token")))))))
     (propagated-inputs (list python-flask python-pyjwt))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/mattupstate/flask-jwt")
     (synopsis "JWT token authentication for Flask apps")
     (description "This package implements JWT token authentication for Flask
@@ -4209,7 +4221,9 @@ (define-public python-flask-restful
     (native-inputs
      (list python-blinker
            python-mock
-           python-pytest))
+           python-pytest
+           python-setuptools
+           python-wheel))
     (home-page "https://www.github.com/flask-restful/flask-restful/")
     (synopsis "Flask module for creating REST APIs")
     (description
@@ -4870,9 +4884,9 @@ (define-public python-paste
            (delete-file "tests/test_proxy.py") #t))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest python-nose))
+     (list python-pytest python-wheel))
     (propagated-inputs
-     (list python-six))
+     (list python-six python-setuptools))
     (home-page "https://pythonpaste.readthedocs.io/")
     (synopsis
      "Python web development tools, focusing on WSGI")
@@ -5148,6 +5162,7 @@ (define-public whoogle-search
            python-wcwidth
            python-werkzeug
            python-dotenv))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/benbusby/whoogle-search")
     (synopsis "Self-hosted, ad-free, privacy-respecting metasearch engine")
     (description
@@ -5989,7 +6004,9 @@ (define-public python-httpcore
            python-pytest-httpbin
            python-pytest-trio
            python-uvicorn
-           python-trustme))
+           python-setuptools
+           python-trustme
+           python-wheel))
     (propagated-inputs
      (list python-anyio
            python-certifi
@@ -6023,7 +6040,7 @@ (define-public python-httpcore-bootstrap
    (package/inherit python-httpcore
      (name "python-httpcore-bootstrap")
      (arguments (list #:tests? #f))
-     (native-inputs '()))))
+     (native-inputs (list python-setuptools python-wheel)))))
 
 (define-public python-httpx
   (package
@@ -6067,7 +6084,9 @@ (define-public python-httpx
            python-trio
            python-trio-typing
            python-trustme
-           python-uvicorn))
+           python-uvicorn
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-charset-normalizer
            python-brotli
@@ -6299,7 +6318,7 @@ (define-public python-rapidjson
                                 "/include/rapidjson" "'"))
                 (("if not os.path.isdir.*") "if False:")))))))
     (native-inputs
-     (list rapidjson python-pytest python-pytz))
+     (list rapidjson python-pytest python-pytz python-setuptools python-wheel))
     (home-page "https://github.com/python-rapidjson/python-rapidjson")
     (synopsis "Python wrapper around rapidjson")
     (description "This package provides a python wrapper around rapidjson.")
diff --git a/gnu/packages/python-xyz.scm b/gnu/packages/python-xyz.scm
index b8e8097c7b..eee0e5f744 100644
--- a/gnu/packages/python-xyz.scm
+++ b/gnu/packages/python-xyz.scm
@@ -307,7 +307,8 @@ (define-public python-janus
         (base32 "04hnrdcf03g1s0x3sr72sh9gnszz6kyfsl9dg8a4n0zvvhn6z5yz"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest python-pytest-cov python-pytest-asyncio))
+     (list python-pytest python-pytest-cov python-pytest-asyncio
+           python-setuptools python-wheel))
     (propagated-inputs (list python-typing-extensions))
     (home-page "https://github.com/aio-libs/janus/")
     (synopsis
@@ -579,6 +580,7 @@ (define-public python-fit-nbinom
                 "12v8l5i35vjbpvh5i4lw29ys6vpr3z7pysyrx33hxaq09zr015mx"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-numpy python-scipy))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/joachimwolff/fit_nbinom")
     (synopsis "Negative binomial maximum likelihood estimator")
     (description "This package provides an implementation in Python using
@@ -597,7 +599,8 @@ (define-public python-docrep
                 "0523jrzjj29kxpdllmfhrfj9kysi9mphp2m7ippjkn5b07i1g2pd"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-six))
-    (native-inputs (list python-pytest python-pytest-runner))
+    (native-inputs (list python-pytest python-pytest-runner
+                         python-setuptools python-wheel))
     (home-page "https://github.com/Chilipp/docrep")
     (synopsis "Python package for docstring repetition")
     (description "Docrep is the documentation repetition module.  This module
@@ -655,6 +658,7 @@ (define-public python-adjusttext
                 "05zf0xn7ab40dan213fwbp1z4rybih8dphf9mzb2ddmbafxq41mv"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-matplotlib python-numpy))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/Phlya/adjustText")
     (synopsis "Adjust text position in matplotlib plots to minimize overlaps")
     (description
@@ -772,6 +776,7 @@ (define-public python-colored
                 "196ins0m7f90xz5dw764dlx060ziqbcydqzzq40b4ir5858baf3r"))))
     (build-system pyproject-build-system)
     (arguments (list #:tests? #false)) ;the tests are not run automatically
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://gitlab.com/dslackw/colored")
     (synopsis "Simple library for color and formatting to terminal")
     (description "This is a very simple Python library for color and
@@ -794,7 +799,8 @@ (define-public python-colorful
         (base32 "1fcz5v8b318a3dsdha4c874jsf3wmcw3f25bv2csixclyzacli98"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-coverage python-flake8 python-pytest))
+     (list python-coverage python-flake8 python-pytest python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-colorama))
     (home-page "http://github.com/timofurrer/colorful")
@@ -1101,7 +1107,8 @@ (define-public python-pdoc
                (base32
                 "0gxkw607nrd67ck4w8jri9vfrm5g60qvp8b134m8zkiphbxjnx0l"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-pytest python-jinja2 python-pygments))
+    (native-inputs (list python-pytest python-jinja2 python-pygments
+                         python-setuptools python-wheel))
     (arguments
      (list
       ;; Some tests fail, presumably because of slight version mismatches of
@@ -1202,6 +1209,7 @@ (define-public python-plotext
                 "1gpy1z2i4vq1mcrhysxahz4339pbl9rzk58rf5m5gf5ym9xji6ii"))))
     (build-system pyproject-build-system)
     (arguments (list #:tests? #false)) ;there are none
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/piccolomo/plotext")
     (synopsis "Plots in the terminal")
     (description "Plotext lets you plot directly to the terminal.")
@@ -1331,6 +1339,8 @@ (define-public python-skranger
            python-matplotlib
            python-pandas
            python-poetry-core
+           python-setuptools
+           python-wheel
            python-pytest))
     (home-page "https://github.com/crflynn/skranger")
     (synopsis "Python bindings for C++ ranger random forests")
@@ -1399,7 +1409,7 @@ (define-public python-tenacity
     (build-system pyproject-build-system)
     (native-inputs
      (list python-setuptools-scm python-tornado-6 python-typeguard
-           python-pytest))
+           python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/jd/tenacity")
     (synopsis "Retrying library for python")
     (description "Tenacity is a general-purpose python library to simplify the
@@ -1527,7 +1537,7 @@ (define-public python-glymur
               ;; which is patched above.
               (delete-file "tests/test_config.py"))))))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (inputs
      (list openjpeg  ; glymur/lib/openjp2.py
            libtiff)) ; glymur/lib/tiff.py
@@ -1796,7 +1806,7 @@ (define-public python-rasterio
                              python-click-plugins
                              python-cligj
                              python-numpy
-                             python-setuptools
+                             python-setuptools ; For pkg_resources.
                              python-snuggs))
     (native-inputs (list gdal
                          python-boto3
@@ -1805,7 +1815,8 @@ (define-public python-rasterio
                          python-packaging
                          python-pytest
                          python-pytest-cov
-                         python-shapely))
+                         python-shapely
+                         python-wheel))
     (home-page "https://github.com/rasterio/rasterio")
     (synopsis "Fast and direct raster I/O for use with Numpy and SciPy")
     (description "This package implements fast and direct raster I/O for use
@@ -1833,7 +1844,7 @@ (define-public python-shapely
              (invoke "python" "setup.py" "build_ext" "--inplace"))))))
     (native-inputs
      (list python-cython python-matplotlib python-pytest
-           python-pytest-cov))
+           python-pytest-cov python-setuptools python-wheel))
     (inputs
      (list geos))
     (propagated-inputs
@@ -2119,7 +2130,9 @@ (define-public python-h5netcdf
     (native-inputs
      (list python-netcdf4
            python-pytest
-           python-setuptools-scm))
+           python-setuptools
+           python-setuptools-scm
+           python-wheel))
     (propagated-inputs
      (list python-h5py python-packaging))
     (home-page "https://h5netcdf.org")
@@ -2414,7 +2427,7 @@ (define-public python-license-expression
         (base32 "0lvshl2fhwa568d3y3vmx45hdp8gk5w9yl3b2q5d66r5vqn1sfwl"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-setuptools-scm python-pytest))
+     (list python-setuptools-scm python-pytest python-setuptools python-wheel))
     (propagated-inputs
      (list python-boolean.py))
     (home-page "https://github.com/nexB/license-expression")
@@ -2537,7 +2550,8 @@ (define-public python-filelock-3.5
         (base32
          "058av1r760ws7z6qffsjpqa39fmdxw0s1wnyr7p50y3zclg6cyqk"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-pytest python-setuptools-scm))
+    (native-inputs (list python-pytest python-setuptools-scm
+                         python-setuptools python-wheel))
     (home-page "https://github.com/tox-dev/py-filelock")
     (synopsis "Platform independent file lock")
     (description "@code{filelock} contains a single module implementing
@@ -2798,7 +2812,8 @@ (define-public python-can
          ;; These tests fail with "OSError: [Errno 19] No such device".
          "-k" "not BasicTestUdpMulticastBusIPv")))
     (propagated-inputs
-     (list python-msgpack python-typing-extensions python-wrapt))
+     (list python-msgpack python-typing-extensions python-wrapt
+           python-setuptools))
     (native-inputs
      (list ;; python-canalystii ; Not packed yet
            python-codecov
@@ -2811,7 +2826,8 @@ (define-public python-can
            python-pytest
            python-pytest-cov
            python-pytest-runner
-           python-pytest-timeout))
+           python-pytest-timeout
+           python-wheel))
     (home-page "https://github.com/hardbyte/python-can")
     (synopsis "Controller Area Network (CAN) interface module for Python")
     (description "This package defines the @code{can} module, which provides
@@ -2912,6 +2928,7 @@ (define-public python-optparse-pretty
         (base32 "1920wbh2b7a8qn7zx2iiqbcdaax335l81a73x9pp8h11yzs2jdmh"))))
     (build-system pyproject-build-system)
     (arguments (list #:tests? #false))  ;There are none
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/gvalkov/optparse-pretty")
     (synopsis "Compact help formatter for optparse")
     (description
@@ -3156,6 +3173,7 @@ (define-public python-fastprogress
                         ;; XXX: Fails with: "In procedure utime: No such file
                         ;; or directory".
                         (delete 'ensure-no-mtimes-pre-1980))))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/fastai/fastprogress")
     (synopsis "Progress bar for Jupyter Notebook and console")
     (description
@@ -3389,6 +3407,8 @@ (define-public python-omero-py
            python-pytest
            python-pytest-rerunfailures
            python-pytest-xdist
+           python-setuptools
+           python-wheel
            unzip
            (origin
              (method url-fetch)
@@ -3679,7 +3699,9 @@ (define-public python-jsonargparse
            python-pytest-subtests
            python-responses
            python-tox
-           python-types-requests))
+           python-types-requests
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/omni-us/jsonargparse/")
     (synopsis "Implement minimal boilerplate CLIs derived from type hints")
     (description
@@ -3884,7 +3906,7 @@ (define-public python-palettable
                (base32
                 "1a4h0jpsr3wjciqg9a5kslxv65d3qqgmqgkpai4cl77wlpcxfk89"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://jiffyclub.github.io/palettable/")
     (synopsis "Color palettes for Python")
     (description "Palettable (formerly brewer2mpl) is a library of color
@@ -3965,7 +3987,8 @@ (define-public python-parsley
                            (string-append "not test_sending_empty_netstring"
                             " and not test_sending_one_netstring"
                             " and not test_sending_two_netstrings"))))
-    (native-inputs (list python-pytest python-twisted))
+    (native-inputs (list python-pytest python-twisted python-setuptools
+                         python-wheel))
     (home-page "https://launchpad.net/parsley")
     (synopsis "Parsing and pattern matching Python library")
     (description
@@ -4069,7 +4092,9 @@ (define-public python-portalocker
                          python-pytest-cov
                          python-pytest-mypy
                          python-pytest-timeout
-                         python-redis))
+                         python-redis
+                         python-setuptools
+                         python-wheel))
     (home-page "https://github.com/WoLpH/portalocker")
     (synopsis "Python library for file locking")
     (description "Portalocker is a library to provide an easy API to file
@@ -4159,7 +4184,9 @@ (define-public python-extension-helpers
           python-pytest
           python-pytest-astropy
           python-pytest-cov
-          python-setuptools-scm))
+          python-setuptools-scm
+          python-setuptools
+          python-wheel))
   (home-page "https://extension-helpers.readthedocs.io")
   (synopsis "Astropy ecosystem utilities for building and installing packages")
   (description
@@ -4633,12 +4660,14 @@ (define-public python-clickhouse-connect
            python-orjson
            python-pandas
            python-pytz
+           python-setuptools ; For pkg_resources.
            python-sqlalchemy
            python-urllib3
            python-zstandard))
     (native-inputs
      (list python-cython
-           python-pytest))
+           python-pytest
+           python-wheel))
     (home-page "https://github.com/ClickHouse/clickhouse-connect")
     (synopsis
      "ClickHouse database core driver for Python, Pandas, and Superset")
@@ -4683,7 +4712,7 @@ (define-public python-cloup
                 "05c6cjpnf9s72gyn5dckxbmd8rf2kgdzfsl7pqzrnc1lcdl13zmv"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-click))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/janLuke/cloup")
     (synopsis "Extension library for python-click")
     (description
@@ -4787,7 +4816,8 @@ (define-public python-jsonschema
               (when tests?
                 (setenv "JSON_SCHEMA_TEST_SUITE" "json")
                 (invoke "trial" "jsonschema")))))))
-    (native-inputs (list python-setuptools-scm python-twisted))
+    (native-inputs (list python-setuptools-scm python-twisted
+                         python-setuptools python-wheel))
     (propagated-inputs
      (list python-attrs
            python-importlib-metadata
@@ -4990,7 +5020,9 @@ (define-public python-pyjwt
            python-cryptography
            python-pytest
            python-sphinx
-           python-sphinx-rtd-theme))
+           python-sphinx-rtd-theme
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/progrium/pyjwt")
     (synopsis "JSON Web Token implementation in Python")
     (description
@@ -5237,7 +5269,7 @@ (define-public python-uc-micro-py
                (base32
                 "17f55gi55rg47nm88fn3f8851ph03dgykdp011lxr3j6hk18lyfv"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/tsutsu3/uc.micro-py")
     (synopsis "Unicode data files for linkify-it-py projects")
     (description "This package contains a micro subset of Unicode data files
@@ -5259,7 +5291,7 @@ (define-public python-linkify-it-py
                 "0fg0a1lc8xbb62y9krxcp708ll58dxcwm8i7rrwpkd5sh2229f6x"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-uc-micro-py))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/tsutsu3/linkify-it-py")
     (synopsis "Links recognition library with full Unicode support")
     (description "This is Python port of
@@ -5284,7 +5316,7 @@ (define-public python-makefun
                 "19a8dga8rnmjn5gy1cy1wdi28swbkdkypwbqikbxil6ynqcg3c20"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest python-setuptools-scm))
+     (list python-pytest python-setuptools python-setuptools-scm python-wheel))
     (home-page "https://github.com/smarie/python-makefun")
     (synopsis "Library to dynamically create python functions")
     (description "@code{makefun} helps create functions dynamically with a
@@ -5867,7 +5899,9 @@ (define-public python-docx
            python-flake8
            python-mock
            python-pyparsing
-           python-pytest))
+           python-pytest
+           python-setuptools
+           python-wheel))
     (propagated-inputs
      (list python-lxml))
     (home-page "https://github.com/python-openxml/python-docx/")
@@ -6025,6 +6059,7 @@ (define-public python-pygtrie
                (base32
                 "1qm4xdmzd4q5pc9h5gjdpr5m7lg06k8dvqnjn7d07d3fhani8d90"))))
     (build-system pyproject-build-system)
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/mina86/pygtrie")
     (synopsis "Pure Python trie data structure implementation")
     (description
@@ -7447,7 +7482,7 @@ (define-public python-pygit2
         (base32 "1v34xdvh6i5cn5srwicvp0i2kvv8fzsv0v9p72ng081nsczmhgvr"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-cffi libgit2))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/libgit2/pygit2")
     (synopsis "Python bindings for libgit2")
     (description "Pygit2 is a set of Python bindings to the libgit2 shared library.")
@@ -7685,7 +7720,9 @@ (define-public python-norns
                (base32
                 "1r1lcq59v6l75wkbp7mypanr69a6fv6m58v6dw3v6b4vwz5nqg0z"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-appdirs python-nose python-pyyaml))
+    (propagated-inputs (list python-appdirs python-nose python-pyyaml
+                             python-setuptools))
+    (native-inputs (list python-wheel))
     (home-page "https://github.com/simonvh/norns")
     (synopsis "Simple YAML-based config module")
     (description "This package provides a simple YAML-based config module.")
@@ -9006,9 +9043,10 @@ (define-public python-imageio
               (delete-file "tests/test_freeimage.py"))))))
     (inputs (list freeimage))
     (propagated-inputs
-     (list python-imageio-ffmpeg python-numpy python-pillow python-psutil))
+     (list python-imageio-ffmpeg python-numpy python-pillow python-psutil
+           python-setuptools))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-wheel))
     (home-page "https://imageio.github.io/")
     (synopsis "Library for reading and writing a wide range of image data")
     (description
@@ -10083,7 +10121,9 @@ (define-public python-jaraco-test
                          python-pytest-cov
                          python-pytest-enabler
                          python-pytest-flake8
-                         python-pytest-mypy))
+                         python-pytest-mypy
+                         python-setuptools
+                         python-wheel))
     (home-page "https://github.com/jaraco/jaraco.test")
     (synopsis "Testing support by jaraco")
     (description "This package provides testing support by jaraco.")
@@ -10441,6 +10481,7 @@ (define-public python-jupyter-client-bootstrap
             python-pyzmq
             python-tornado-6
             python-traitlets))
+     (native-inputs (list python-setuptools python-wheel))
      (home-page "https://jupyter.org/")
      (synopsis "Jupyter protocol implementation and client libraries")
      (description
@@ -10478,7 +10519,9 @@ (define-public python-jupyter-client
              python-pytest-timeout
              python-async-generator
              python-ipython
-             python-ipykernel-bootstrap))
+             python-ipykernel-bootstrap
+             python-setuptools
+             python-wheel))
       (properties (alist-delete 'hidden? (package-properties base))))))
 
 (define-public python-ipykernel
@@ -10559,6 +10602,8 @@ (define-public python-ipykernel
            ;; avoids that.
            python-pytest-bootstrap
            python-pytest-timeout
+           python-setuptools
+           python-wheel
            tini))
     (home-page "https://ipython.org")
     (synopsis "IPython Kernel for Jupyter")
@@ -10578,7 +10623,7 @@ (define-public python-ipykernel-bootstrap
                          ;; left out here to break the cycle.
                          #:phases #~(modify-phases %standard-phases
                                       (delete 'sanity-check))))
-        (native-inputs '())
+        (native-inputs (list python-setuptools python-wheel))
         (propagated-inputs
          (modify-inputs (package-propagated-inputs parent)
            (replace "python-jupyter-client" python-jupyter-client-bootstrap)
@@ -10648,7 +10693,9 @@ (define-public python-pubmed-parser
            python-unidecode))
     (native-inputs
      (list python-pytest
-           python-pytest-cov))
+           python-pytest-cov
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/titipata/pubmed_parser")
     (synopsis "Parser for Pubmed Open-Access Subset and MEDLINE XML repository")
     (description
@@ -11875,6 +11922,7 @@ (define-public python-qnorm
                  "{ name = \"Maarten van der Sande\", email = \"[email-masked]\"}")
                 (("license = \"MIT\"") "license = { file = \"LICENSE\"}")))))))
     (propagated-inputs (list python-numba python-numpy python-pandas))
+    (native-inputs (list python-setuptools python-wheel python-toml))
     (home-page "https://github.com/Maarten-vd-Sande/qnorm")
     (synopsis "Quantile normalization")
     (description "This tool implements quantile normalization. It properly
@@ -11935,7 +11983,9 @@ (define-public python-singledispatch
     (build-system pyproject-build-system)
     (native-inputs
      (list python-pytest
-           python-six)) ; required for conversion, not at run-time
+           python-setuptools
+           python-six ; required for conversion, not at run-time
+           python-wheel))
     (home-page
      "https://docs.python.org/3/library/functools.html#functools.singledispatch")
     (synopsis "Backport of singledispatch feature from Python 3.4")
@@ -12363,7 +12413,8 @@ (define-public python-pyproject-metadata
          "00zahgw9zjfqwf0218bj5k732aibnn68cq1p8f0wmbirb7fy5k31"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-packaging))
-    (native-inputs (list python-pypa-build python-pytest python-tomli))
+    (native-inputs (list python-pypa-build python-pytest python-setuptools
+                         python-tomli python-wheel))
     (home-page "https://github.com/FFY00/python-pyproject-metadata")
     (synopsis "Dataclass for PEP 621 metadata")
     (description "This project does not implement the parsing of
@@ -13134,7 +13185,8 @@ (define-public python-pyftpdlib
     ;; Using Pytest instead of the Makefile causes the command line tests to
     ;; fail on unknown Pytest arguments.
     (arguments (list #:test-flags #~(list "-k" "not TestCommandLineParser")))
-    (native-inputs (list python-psutil python-pytest))
+    (native-inputs (list python-psutil python-pytest python-setuptools
+                         python-wheel))
     (propagated-inputs (list python-pyopenssl python-pysendfile))
     (home-page "https://github.com/giampaolo/pyftpdlib/")
     (synopsis "Asynchronous and scalable Python FTP server library")
@@ -13881,7 +13933,8 @@ (define-public python-snuggs
                 "0yv1wayrw9g6k0c2f721kha7wsv0s1fdlxpf5x7f34iqzq9z272h"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-numpy python-pyparsing))
-    (native-inputs (list python-hypothesis python-pytest))
+    (native-inputs (list python-hypothesis python-pytest python-setuptools
+                         python-wheel))
     (home-page "https://github.com/mapbox/snuggs")
     (synopsis "Snuggs are S-expressions for Numpy")
     (description "Snuggs are S-expressions for Numpy.")
@@ -14326,7 +14379,7 @@ (define-public python-path-bootstrap
      (build-system pyproject-build-system)
      (arguments
       (list #:tests? #f))
-     (native-inputs (list python-setuptools-scm))
+     (native-inputs (list python-setuptools python-setuptools-scm python-wheel))
      (home-page "https://github.com/jaraco/path")
      (synopsis "Object-oriented file system path manipulation library")
      (description "@code{path} (formerly @code{path.py}) implements path
@@ -14932,7 +14985,9 @@ (define-public python-fastjsonschema
            python-pylint
            python-pytest
            python-pytest-benchmark
-           python-pytest-cache))
+           python-pytest-cache
+           python-setuptools
+           python-wheel))
     (home-page
      "https://github.com/horejsek/python-fastjsonschema")
     (synopsis
@@ -15015,7 +15070,7 @@ (define-public python-bleach
             (delete-file-recursively "bleach/_vendor/html5lib")))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-html5lib python-tinycss2 python-webencodings))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/mozilla/bleach")
     (synopsis "Whitelist-based HTML-sanitizing tool")
     (description "Bleach is an easy whitelist-based HTML-sanitizing tool.")
@@ -15551,7 +15606,7 @@ (define-public python-chardet
         (base32
          "1r9ixxnish9j3dq4h0z0cwlkr4f5lgi6d8mhbzw59hbbjlmp2qhd"))))
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (build-system pyproject-build-system)
     (arguments
      (list #:test-flags
@@ -15752,7 +15807,7 @@ (define-public python-confection
     (build-system pyproject-build-system)
     (propagated-inputs (list python-pydantic python-srsly
                              python-typing-extensions))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/explosion/confection")
     (synopsis "Config system for Python")
     (description "Confection is a lightweight library that offers a
@@ -15816,7 +15871,8 @@ (define-public python-omegaconf
     (propagated-inputs (list java-antlr4-runtime-python
                              python-pydevd
                              python-pyyaml))
-    (native-inputs (list icedtea antlr4 python-pytest python-pytest-mock))
+    (native-inputs (list icedtea antlr4 python-pytest python-pytest-mock
+                         python-setuptools python-wheel))
     (home-page "https://github.com/omry/omegaconf")
     (synopsis "Flexible configuration system")
     (description "OmegaConf is a hierarchical configuration system and
@@ -15836,7 +15892,7 @@ (define-public python-configargparse
                 "17vky4ihicbf7nggg30xs7h3g5rxzwgch8vilnnrvdaacszkq2qv"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-mock python-pytest))
+     (list python-mock python-pytest python-setuptools python-wheel))
     (synopsis "Replacement for argparse")
     (description "A drop-in replacement for argparse that allows options to also
 be set via config files and/or environment variables.")
@@ -16951,6 +17007,7 @@ (define-public python-xdg
                (base32
                 "14hwk9j5zjc8rvirw95mrb07zdnpjaxjx2mj3rnq8pnlyaa809r4"))))
     (build-system pyproject-build-system)
+    (arguments (list #:tests? #f)) ; No tests in PyPi tarball.
     (native-inputs
      (list python-poetry-core))
     (home-page "https://github.com/srstevenson/xdg-base-dirs")
@@ -17433,7 +17490,8 @@ (define-public python-cesium
            python-scikit-learn
            python-scipy
            python-toolz))
-    (native-inputs (list python-cython python-pytest python-setuptools-scm))
+    (native-inputs (list python-cython python-pytest python-setuptools-scm
+                         python-setuptools python-wheel))
     (home-page "https://pypi.org/project/cesium/")
     (synopsis "Library for time-series feature extraction and processing")
     (description
@@ -17510,7 +17568,8 @@ (define-public python-pyqtgraph
                  (lambda _
                    (setenv "QT_QPA_PLATFORM" "offscreen"))))))
     (native-inputs
-     (list python-pytest python-pytest-cov python-pytest-xdist))
+     (list python-pytest python-pytest-cov python-pytest-xdist
+           python-setuptools python-wheel))
     (inputs
      (list qtbase-5))
     (propagated-inputs
@@ -17699,7 +17758,9 @@ (define-public python-codespell
             python-pytest
             python-pytest-cov
             python-pytest-dependency
-            python-tomli))
+            python-tomli
+            python-setuptools
+            python-wheel))
     (arguments
      `(#:phases
        (modify-phases %standard-phases
@@ -18823,7 +18884,9 @@ (define-public python-inflect
                          python-pytest-flake8
                          python-pytest-mypy
                          ;; For the version number
-                         python-setuptools-scm))
+                         python-setuptools-scm
+                         python-setuptools
+                         python-wheel))
     (home-page "https://github.com/jaraco/inflect")
     (synopsis "Correctly generate plurals, singular nouns, ordinals, indefinite articles")
     (description
@@ -20873,7 +20936,8 @@ (define-public python-xopen
     (propagated-inputs
      (list pigz python-isal python-typing-extensions))
     (native-inputs
-     (list python-pytest python-pytest-timeout python-setuptools-scm))
+     (list python-pytest python-pytest-timeout python-setuptools-scm
+           python-setuptools python-wheel))
     (home-page "https://github.com/marcelm/xopen/")
     (synopsis "Open compressed files transparently")
     (description "This module provides an @code{xopen} function that works
@@ -20957,6 +21021,7 @@ (define-public python-chevron
                 "1gqfh00ics2k1sm5g46l3bi8cl5fc5d1cwzh1ylvcxvdvypklqc7"))))
     (build-system pyproject-build-system)
     (arguments (list #:tests? #false)) ;there are none
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/noahmorrison/chevron")
     (synopsis "Mustache templating language renderer")
     (description "This package provides a Python implementation of the
@@ -21009,7 +21074,9 @@ (define-public python-duckdb
            python-pyarrow
            python-pytest
            python-pytest-runner
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://www.duckdb.org")
     (synopsis "DuckDB embedded database")
     (description "DuckDB is an in-process SQL OLAP database management
@@ -21868,6 +21935,7 @@ (define-public python-diff-cover
            python-jinja2
            python-pluggy
            python-pygments
+           python-setuptools ; For pkg_resources.
            python-tomli))
     (native-inputs
      (list python-flake8
@@ -22082,7 +22150,8 @@ (define-public python-gtts
      ;; Disable tests that require internet.
      (list #:test-flags '(list "-k" "not net")))
     (native-inputs
-     (list python-pytest python-testfixtures python-click python-requests))
+     (list python-pytest python-testfixtures python-click python-requests
+           python-setuptools python-wheel))
     (home-page "https://github.com/pndurette/gTTS")
     (synopsis "Google Translate text-to-speech interface")
     (description
@@ -22574,7 +22643,9 @@ (define-public python-marshmallow
            python-mypy
            python-pytest
            python-pytz
-           python-simplejson))
+           python-simplejson
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/marshmallow-code/marshmallow")
     (synopsis "Convert complex datatypes to and from native Python datatypes")
     (description "@code{marshmallow} provides a library for converting complex
@@ -22636,7 +22707,9 @@ (define-public python-apispec
            python-marshmallow
            python-mypy
            python-pytest
-           python-pyyaml))
+           python-pyyaml
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/marshmallow-code/apispec")
     (synopsis "Swagger/OpenAPI specification generator")
     (description "@code{python-apispec} is a pluggable API specification
@@ -22655,7 +22728,7 @@ (define-public python-apispec-webframeworks
                (base32
                 "1wyw30402xq2a8icrsjmy9v43jyvawcjd85ccb2zicqlg4k5pcqd"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-apispec))
+    (propagated-inputs (list python-apispec python-setuptools))
     (native-inputs
      (list python-bottle
            python-flake8
@@ -22665,7 +22738,8 @@ (define-public python-apispec-webframeworks
            python-pytest
            python-pyyaml
            python-tornado
-           python-tox))
+           python-tox
+           python-wheel))
     (home-page "https://github.com/marshmallow-code/apispec-webframeworks")
     (synopsis "Web framework plugins for apispec")
     (description "This package provides plugins for using @code{apispec} with
@@ -23354,7 +23428,9 @@ (define-public python-tempora
            python-pytest-mypy
            python-setuptools-scm
            python-types-freezegun
-           python-types-pytz))
+           python-types-pytz
+           python-setuptools
+           python-wheel))
     (propagated-inputs (list python-jaraco-functools python-pytz))
     (home-page "https://github.com/jaraco/tempora")
     (synopsis "Python date and time objects and routines")
@@ -23530,7 +23606,9 @@ (define-public python-zict
      (list python-pytest
            python-pytest-asyncio
            python-pytest-repeat
-           python-pytest-timeout))
+           python-pytest-timeout
+           python-setuptools
+           python-wheel))
     (home-page "https://zict.readthedocs.io/en/latest/")
     (synopsis "Composable mutable mapping tools")
     (description "This package provides abstract @code{MutableMapping} classes
@@ -23716,7 +23794,7 @@ (define-public python-whatever
         (base32 "1q7ajgqjfivxqsqgnhp4lc4p6jxyh4zprcsdbpd6dw54inaf0av5"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/Suor/whatever")
     (synopsis "Make anonymous functions by partial application of operators")
     (description "@code{whatever} provides an easy way to make anonymous
@@ -23738,7 +23816,7 @@ (define-public python-funcy
        (file-name (git-file-name name version))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest python-whatever))
+     (list python-pytest python-whatever python-setuptools python-wheel))
     (home-page "https://github.com/Suor/funcy")
     (synopsis "Functional tools")
     (description "@code{funcy} is a library that provides functional tools.
@@ -24126,7 +24204,7 @@ (define-public python-pybloom-live
                 "040i6bjqvl33j30v865shsk30s3h7f16pqwiaj5kig857dfmqm4r"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-bitarray python-xxhash))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/joseph-fox/python-bloomfilter")
     (synopsis "Bloom filter")
     (description "This package provides a scalable Bloom filter implemented in
@@ -25063,7 +25141,8 @@ (define-public python-numcodecs
            python-msgpack
            python-typing-extensions))
     (native-inputs
-     (list python-cython python-pytest python-setuptools-scm))
+     (list python-cython python-pytest python-setuptools-scm
+           python-setuptools python-wheel))
     (home-page "https://github.com/zarr-developers/numcodecs")
     (synopsis "Buffer compression and transformation codecs")
     (description
@@ -25169,6 +25248,7 @@ (define-public python-anndata
            python-pandas
            python-scipy
            python-scikit-learn
+           python-setuptools ; For pkg_resources.
            python-zarr))
     (native-inputs
      (list python-boltons
@@ -25206,7 +25286,7 @@ (define-public python-dill
                (with-directory-excursion "/tmp"
                  (invoke "nosetests" "-v"))))))))
     (native-inputs
-     (list python-nose))
+     (list python-nose python-setuptools python-wheel))
     (home-page "https://pypi.org/project/dill/")
     (synopsis "Serialize all of Python")
     (description "Dill extends Python's @code{pickle} module for serializing
@@ -25258,6 +25338,7 @@ (define-public python-multiprocess
                (invoke "python" "-m" "multiprocess.tests")))))))
     (propagated-inputs
      (list python-dill))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://pypi.org/project/multiprocess/")
     (synopsis "Multiprocessing and multithreading in Python")
     (description
@@ -25619,7 +25700,8 @@ (define-public python-fsspec
     (propagated-inputs
      (list python-aiohttp python-libarchive-c python-requests python-tqdm))
     (native-inputs
-     (list python-pytest python-pytest-mock python-numpy))
+     (list python-pytest python-pytest-mock python-numpy python-setuptools
+           python-wheel))
     (home-page "https://github.com/intake/filesystem_spec")
     (synopsis "File-system specification")
     (description "The purpose of this package is to produce a template or
@@ -25845,6 +25927,7 @@ (define-public python-absl-py
         (base32
          "0ga3b0m8lfsv1m3260p83lhis52yvz3d42q8gip4gfj823849hnj"))))
     (build-system pyproject-build-system)
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/abseil/abseil-py")
     (synopsis "Abseil Python common libraries")
     (description
@@ -26149,7 +26232,8 @@ (define-public python-trio-typing
        (sha256
         (base32 "15wa66cs165wawh4pi808ac43n67b8jqddi5ppdcbkj5gfi68hpi"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-attrs python-pytest))
+    (native-inputs (list python-attrs python-pytest python-setuptools
+                         python-wheel))
     (propagated-inputs
      (list python-mypy python-mypy-extensions python-trio
            python-typing-extensions))
@@ -27291,7 +27375,7 @@ (define-public python-funcparserlib
              (when tests?
                (invoke "python" "-m" "unittest" "discover" "-v")))))))
     ;; ModuleNotFoundError: No module named 'poetry'
-    (native-inputs (list python-poetry-core))
+    (native-inputs (list python-poetry-core python-six))
     (home-page "https://github.com/vlasovskikh/funcparserlib")
     (synopsis
      "Recursive descent parsing library based on functional combinators")
@@ -27575,7 +27659,9 @@ (define-public python-watchgod
            python-pytest-cov
            python-pytest-mock
            python-pytest-sugar
-           python-pytest-toolbox))
+           python-pytest-toolbox
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/samuelcolvin/watchgod")
     (synopsis "Simple, modern file watching and code reload in Python")
     (description
@@ -28838,7 +28924,7 @@ (define-public python-blessed
       ;; Avoid python-pytest-coverage
       #:test-flags '(list "-c /dev/null")))
     (propagated-inputs (list python-six python-wcwidth))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/jquast/blessed")
     (synopsis "Wrapper around terminal capabilities")
     (description
@@ -29105,7 +29191,8 @@ (define-public python-dateparser
      (list python-dateutil python-pytz python-regex python-ruamel.yaml
            python-tzlocal))
     (native-inputs
-     (list python-flake8 python-pytest python-parameterized tzdata-for-tests))
+     (list python-flake8 python-pytest python-parameterized tzdata-for-tests
+           python-setuptools python-wheel))
     (arguments
      `(#:phases
        (modify-phases %standard-phases
@@ -29802,7 +29889,9 @@ (define-public python-loguru
                          python-sphinx
                          python-sphinx-autobuild
                          python-sphinx-rtd-theme
-                         python-tox))
+                         python-tox
+                         python-setuptools
+                         python-wheel))
     (home-page "https://github.com/Delgan/loguru")
     (synopsis "Python logging made (stupidly) simple")
     (description "Python logging made (stupidly) simple")
@@ -31896,6 +31985,7 @@ (define-public python-types-requests
                 "0cas3cjkhrvsz2rmqnhqiihy9j79wxi9xbih8jk0p9r48c2q3iyy"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-types-urllib3))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/python/typeshed")
     (synopsis "Typing stubs for requests")
     (description "This package provides typing stubs for requests.")
@@ -31928,6 +32018,7 @@ (define-public python-types-urllib3
                (base32
                 "0zcipjdnbnc8ymk8mh9n5mypa0qr03rqj98lbmahldcdrrap6md1"))))
     (build-system pyproject-build-system)
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/python/typeshed")
     (synopsis "Typing stubs for urllib3")
     (description "This package provides typing stubs for urllib3.")
@@ -31945,6 +32036,7 @@ (define-public python-typeshed-client
                 "1vdwp1jjg27b22qxgm49v21nb8vm1iki3bfsm0fnq2rsz5alfwz2"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-importlib-resources))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/JelleZijlstra/typeshed_client")
     (synopsis "Library for accessing stubs in typeshed")
     (description
@@ -32349,7 +32441,9 @@ (define-public python-srsly
            python-pytest-timeout
            python-mock
            python-numpy
-           python-psutil))
+           python-psutil
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/explosion/srsly")
     (synopsis "Serialization utilities for Python")
     (description "This package bundles some of the best Python serialization
@@ -32578,7 +32672,7 @@ (define-public python-iteround
                (base32 "1irw4sqrsgpc0ibxdv1hlx0d0jr1fqs48bj6mpfylzqkag9ywlfj"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list python-pytest))
+     (list python-pytest python-setuptools python-wheel))
     (home-page "https://github.com/cgdeboer/iteround")
     (synopsis "Sum-safe rounding for Iterables")
     (description "Iteround is a standard library sum-safe rounding library for
@@ -32692,7 +32786,10 @@ (define-public python-deepdiff
            python-mock
            python-numpy
            python-pytest
-           python-pyyaml))
+           python-pyyaml
+           python-setuptools
+           python-wheel
+           python-toml))
     (home-page "https://github.com/seperman/deepdiff")
     (synopsis "Deep difference and search of any Python object/data")
     (description
@@ -32721,7 +32818,9 @@ (define-public python-deepmerge
               (setenv "SETUPTOOLS_SCM_PRETEND_VERSION" #$version))))))
     (native-inputs
      (list python-setuptools-scm
-           python-pytest))
+           python-pytest
+           python-setuptools
+           python-wheel))
     (home-page "https://deepmerge.readthedocs.io/en/latest/")
     (synopsis "Merge nested data structures")
     (description
@@ -33146,7 +33245,7 @@ (define-public python-cogapp
                (base32
                 "1c0xx3p3lzrlyqhmccyq9c50f8v9pqk2992gb4nl50h2yy1m3s8v"))))
     (build-system pyproject-build-system)
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (home-page "https://nedbatchelder.com/code/cog")
     (synopsis "Content generation tool that leverages Python")
     (description "Cog is a file generation tool.  It allows using pieces of
@@ -33556,7 +33655,10 @@ (define-public staticsite
            python-ruamel.yaml
            python-slugify
            python-tornado
-           tzdata-for-tests))
+           tzdata-for-tests
+           python-setuptools
+           python-wheel
+           python-toml))
     (home-page "https://github.com/spanezz/staticsite")
     (synopsis "Static site generator")
     (description "Statistic is a static site generator based on Markdown and
@@ -33632,8 +33734,9 @@ (define-public python-zbarlight
                (base32
                 "1v5c9bim8af6g8kgxp2dhm96n5vkr8sqi56w0bdh1xy49v03lw3g"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-pillow))
+    (propagated-inputs (list python-pillow python-setuptools))
     (inputs (list zbar))
+    (native-inputs (list python-wheel))
     (home-page "https://github.com/Polyconseil/zbarlight")
     (synopsis "Simple Python wrapper for the zbar barcode library")
     (description "Zbarlight is a simple wrapper for the zbar library.  It can
@@ -33652,6 +33755,7 @@ (define-public python-zeroc-ice
                 "0bqkrjxp2fbz34x3wxkxji39kxinypzg8q2994sibiay29mpipxb"))))
     (build-system pyproject-build-system)
     (inputs (list openssl))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://zeroc.com")
     (synopsis "RPC framework")
     (description
diff --git a/gnu/packages/qt.scm b/gnu/packages/qt.scm
index e8654eee44..08bc47fded 100644
--- a/gnu/packages/qt.scm
+++ b/gnu/packages/qt.scm
@@ -3220,7 +3220,8 @@ (define-public python-sip
     (native-inputs
      (list python-wrapper))
     (propagated-inputs
-     (list python-toml python-packaging python-ply))
+     (list python-toml python-packaging python-ply python-setuptools
+           python-wheel))
     (home-page "https://www.riverbankcomputing.com/software/sip/intro")
     (synopsis "Python binding creator for C and C++ libraries")
     (description
diff --git a/gnu/packages/rpc.scm b/gnu/packages/rpc.scm
index a88c79f8b9..83c951c337 100644
--- a/gnu/packages/rpc.scm
+++ b/gnu/packages/rpc.scm
@@ -253,7 +253,7 @@ (define-public python-grpcio
     (inputs
      (list abseil-cpp-20211102.0 c-ares grpc-for-python-grpcio openssl re2 zlib))
     (native-inputs
-     (list python-cython))
+     (list python-cython python-setuptools python-wheel))
     (propagated-inputs
      (list python-six))
     (home-page "https://grpc.io")
diff --git a/gnu/packages/serialization.scm b/gnu/packages/serialization.scm
index 9c114aaf39..6d6c4386c9 100644
--- a/gnu/packages/serialization.scm
+++ b/gnu/packages/serialization.scm
@@ -656,7 +656,9 @@ (define-public python-msgspec
                          python-mypy
                          python-pytest
                          python-setuptools-scm
-                         python-versioneer))
+                         python-versioneer
+                         python-setuptools
+                         python-wheel))
     (propagated-inputs (list python-pyyaml python-tomli python-tomli-w))
     (home-page "https://jcristharif.com/msgspec/")
     (synopsis "Fast serialization/validation library")
diff --git a/gnu/packages/sphinx.scm b/gnu/packages/sphinx.scm
index 173accd768..5d70da8fec 100644
--- a/gnu/packages/sphinx.scm
+++ b/gnu/packages/sphinx.scm
@@ -371,7 +371,9 @@ (define-public python-sphinx-panels
     (propagated-inputs (list python-docutils-0.15 python-sphinx-4))
     (native-inputs
      (list python-pytest
-           python-pytest-regressions))
+           python-pytest-regressions
+           python-setuptools
+           python-wheel))
     (home-page "https://github.com/executablebooks/sphinx-panels")
     (synopsis "Sphinx extension for creating panels in a grid layout")
     (description
diff --git a/gnu/packages/statistics.scm b/gnu/packages/statistics.scm
index dc620b13a1..9b314c3773 100644
--- a/gnu/packages/statistics.scm
+++ b/gnu/packages/statistics.scm
@@ -2049,7 +2049,7 @@ (define-public python-vega-datasets
                  ;; Remove dataset with unclear license.
                  (lambda _
                    (delete-file "vega_datasets/_data/la-riots.csv"))))))
-    (native-inputs (list python-pytest))
+    (native-inputs (list python-pytest python-setuptools python-wheel))
     (propagated-inputs (list python-pandas))
     (home-page "https://github.com/altair-viz/vega_datasets")
     (synopsis "Example datasets used by Vega-related projects")
@@ -2082,6 +2082,7 @@ (define-public python-altair
                              python-jsonschema
                              python-numpy
                              python-pandas
+                             python-setuptools
                              python-toolz
                              python-typing-extensions))
     (native-inputs (list python-black
@@ -2115,7 +2116,8 @@ (define-public python-hdmedians
            (lambda _
              ;; Cython extensions have to be built before running the tests.
              (invoke "python" "setup.py" "build_ext" "--inplace"))))))
-    (propagated-inputs (list python-cython python-numpy))
+    (propagated-inputs (list python-cython python-numpy python-setuptools
+                             python-wheel))
     (native-inputs (list python-nose))
     (home-page "http://github.com/daleroberts/hdmedians")
     (synopsis "High-dimensional medians")
@@ -2165,7 +2167,9 @@ (define-public python-arviz
                              python-scipy
                              python-typing-extensions
                              python-xarray
-                             python-xarray-einstats))
+                             python-xarray-einstats
+                             python-setuptools
+                             python-wheel))
     (home-page "https://github.com/arviz-devs/arviz")
     (synopsis "Exploratory analysis of Bayesian models")
     (description
@@ -2307,7 +2311,9 @@ (define-public python-statsmodels
            python-pytest
            python-pytest-randomly
            python-pytest-xdist
-           python-setuptools-scm))
+           python-setuptools-scm
+           python-setuptools
+           python-wheel))
     (home-page "https://statsmodels.sourceforge.net/")
     (synopsis "Statistical modeling and econometrics in Python")
     (description
diff --git a/gnu/packages/terminals.scm b/gnu/packages/terminals.scm
index 081ef19af1..a336b33d34 100644
--- a/gnu/packages/terminals.scm
+++ b/gnu/packages/terminals.scm
@@ -99,6 +99,7 @@ (define-module (gnu packages terminals)
   #:use-module (gnu packages popt)
   #:use-module (gnu packages protobuf)
   #:use-module (gnu packages python)
+  #:use-module (gnu packages python-build)
   #:use-module (gnu packages python-web)
   #:use-module (gnu packages python-xyz)
   #:use-module (gnu packages qt)
@@ -244,8 +245,8 @@ (define-public asciinema
                    (substitute* "tests/pty_test.py"
                      (("python3") (search-input-file inputs "/bin/python3"))))))))
     (native-inputs
-     ;; For tests.
-     (list python-pytest))
+     (list python-pytest ; For tests.
+           python-setuptools python-wheel))
     (home-page "https://asciinema.org")
     (synopsis "Terminal session recorder")
     (description
diff --git a/gnu/packages/time.scm b/gnu/packages/time.scm
index 5c8f65a1e6..33b0457af2 100644
--- a/gnu/packages/time.scm
+++ b/gnu/packages/time.scm
@@ -245,6 +245,7 @@ (define-public python-dateutils
                 "1wg3f3imjq3snvjccv64h5498pqv9xz664xhni7bsh8mnay91p83"))))
     (build-system pyproject-build-system)
     (propagated-inputs (list python-dateutil python-pytz))
+    (native-inputs (list python-setuptools python-wheel))
     (home-page "https://github.com/jmcantrell/python-dateutils")
     (synopsis "Various utilities for working with date and datetime objects")
     (description
@@ -328,7 +329,7 @@ (define-public python-timezonefinder
       ;; It's optional, remove this constrain where python-pytz is updated.
       #:test-flags #~(list "-k" "not test_with_pytz")))
     (native-inputs
-     (list python-poetry-core python-pytest))
+     (list python-poetry-core python-pytest python-setuptools))
     (propagated-inputs
      (list python-cffi python-h3-3 python-numba python-numpy python-pytz))
     (home-page "https://timezonefinder.michelfe.it/gui")
diff --git a/gnu/packages/tor.scm b/gnu/packages/tor.scm
index 8ae2118188..ec4fb58f38 100644
--- a/gnu/packages/tor.scm
+++ b/gnu/packages/tor.scm
@@ -50,6 +50,7 @@ (define-module (gnu packages tor)
   #:use-module (gnu packages glib)
   #:use-module (gnu packages pkg-config)
   #:use-module (gnu packages python)
+  #:use-module (gnu packages python-build)
   #:use-module (gnu packages python-check)
   #:use-module (gnu packages python-crypto)
   #:use-module (gnu packages python-web)
@@ -446,7 +447,8 @@ (define-public tractor
          "1542g6alycwlmvndxcijzn4d5lgycmxxb78gqd8qwgm9kw0fnr3q"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list (list glib "bin")))       ; for glib-compile-schemas.
+     (list python-setuptools python-wheel
+           (list glib "bin")))       ; for glib-compile-schemas.
     (inputs
      (list python-fire
            python-psutil
diff --git a/gnu/packages/tree-sitter.scm b/gnu/packages/tree-sitter.scm
index 117c2acb0c..f4e897fbc5 100644
--- a/gnu/packages/tree-sitter.scm
+++ b/gnu/packages/tree-sitter.scm
@@ -29,6 +29,7 @@ (define-module (gnu packages tree-sitter)
   #:use-module (gnu packages graphviz)
   #:use-module (gnu packages icu4c)
   #:use-module (gnu packages node)
+  #:use-module (gnu packages python-build)
   #:use-module (guix build-system cargo)
   #:use-module (guix build-system gnu)
   #:use-module (guix build-system pyproject)
@@ -86,7 +87,8 @@ (define-public python-tree-sitter
                     name)))))))))
     (inputs (list tree-sitter))
     (native-inputs
-     (list tree-sitter-python tree-sitter-javascript))
+     (list tree-sitter-python tree-sitter-javascript
+           python-setuptools python-wheel))
     (home-page "https://github.com/tree-sitter/py-tree-sitter")
     (synopsis "Python bindings to the Tree-sitter parsing library")
     (description "This package provides Python bindings to the
diff --git a/gnu/packages/video.scm b/gnu/packages/video.scm
index 801c2602e4..ea3dd7c793 100644
--- a/gnu/packages/video.scm
+++ b/gnu/packages/video.scm
@@ -5051,7 +5051,7 @@ (define-public gaupol
                 "1z9j3r9pm4rdynlmhgsgnwnnaqw5274yfy4kyillgd77msnpbhaw"))))
     (build-system pyproject-build-system)
     (native-inputs
-     (list gettext-minimal pkg-config))
+     (list gettext-minimal pkg-config python-setuptools python-wheel))
     (inputs
      (list python-pygobject
            gtk+
diff --git a/gnu/packages/vim.scm b/gnu/packages/vim.scm
index d58b022123..a0c738e768 100644
--- a/gnu/packages/vim.scm
+++ b/gnu/packages/vim.scm
@@ -65,6 +65,7 @@ (define-module (gnu packages vim)
   #:use-module (gnu packages perl)
   #:use-module (gnu packages pkg-config)
   #:use-module (gnu packages python)
+  #:use-module (gnu packages python-build)
   #:use-module (gnu packages python-xyz)
   #:use-module (gnu packages ruby)
   #:use-module (gnu packages serialization)
@@ -935,7 +936,8 @@ (define-public python-neovim-remote
                (base32
                 "00kxlb3f1k7iaxzpsr07scavmnyg8c1jmicmr13mfk2lcdac6g2b"))))
     (build-system pyproject-build-system)
-    (propagated-inputs (list python-psutil python-pynvim))
+    (propagated-inputs (list python-psutil python-pynvim python-setuptools))
+    (native-inputs (list python-wheel))
     (home-page "https://github.com/mhinz/neovim-remote")
     (synopsis "Control nvim processes using `nvr` commandline tool")
     (description "This package provide a `nvr` command, which can open File in
diff --git a/guix/build-system/pyproject.scm b/guix/build-system/pyproject.scm
index 2a2c3af3f3..94b9d79692 100644
--- a/guix/build-system/pyproject.scm
+++ b/guix/build-system/pyproject.scm
@@ -52,7 +52,12 @@ (define (default-python)
   "Return the default Python package."
   ;; Lazily resolve the binding to avoid a circular dependency.
   (let ((python (resolve-interface '(gnu packages python))))
-    (module-ref python 'python-toolchain)))
+    ;; We are using python-sans-pip-wrapper, because it does not contain
+    ;; setuptools. This allows us to skip the dependency on setuptools for
+    ;; packages which don’t need it. And it allows us to more easily swap
+    ;; out setuptools if a different version is required.
+    ;; Using python-toolchain here might cause dependency cycles.
+    (module-ref python 'python-sans-pip-wrapper)))
 
 (define sanity-check.py
   (search-auxiliary-file "python/sanity-check.py"))
-- 
2.41.0

>From f4697d0da0018e66ae759a9495a82f364cad5ccd Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Sat, 13 May 2023 15:31:06 +0200
Subject: [PATCH 2/8] build-system/python: Ignore symlinks when changing mtime.

* guix/build/python-build-system.scm (ensure-no-mtimes-pre-1980): Ignore
'symlink.
---
 guix/build/python-build-system.scm | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/guix/build/python-build-system.scm b/guix/build/python-build-system.scm
index aa04664b25..8e18d6d0df 100644
--- a/guix/build/python-build-system.scm
+++ b/guix/build/python-build-system.scm
@@ -37,6 +37,7 @@ (define-module (guix build python-build-system)
   #:use-module (srfi srfi-26)
   #:export (%standard-phases
             add-installed-pythonpath
+            ensure-no-mtimes-pre-1980
             site-packages
             python-version
             python-build))
@@ -270,7 +271,8 @@ (define* (ensure-no-mtimes-pre-1980 #:rest _)
   ;; timestamps before 1980.
   (let ((early-1980 315619200))  ; 1980-01-02 UTC
     (ftw "." (lambda (file stat flag)
-               (unless (<= early-1980 (stat:mtime stat))
+               (unless (or (<= early-1980 (stat:mtime stat))
+                           (eq? (stat:type stat) 'symlink))
                  (utime file early-1980 early-1980))
                #t))))
 
-- 
2.41.0

>From a0d4c891e6cf3522c6769e82d888d906445363f5 Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Sat, 13 May 2023 15:31:22 +0200
Subject: [PATCH 3/8] gnu: criu: Change file mtimes to fix build.

* gnu/packages/virtualization.scm (criu)[arguments]: Add
python-build-system to #:modules and #:imported modules, add phase
'ensure-no-mtimes-pre-1980.
---
 gnu/packages/virtualization.scm | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/gnu/packages/virtualization.scm b/gnu/packages/virtualization.scm
index 9556fbc61e..04059fc73e 100644
--- a/gnu/packages/virtualization.scm
+++ b/gnu/packages/virtualization.scm
@@ -143,6 +143,7 @@ (define-module (gnu packages virtualization)
   #:use-module (guix git-download)
   #:use-module (guix gexp)
   #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix modules)
   #:use-module (guix packages)
   #:use-module (guix utils)
   #:use-module (srfi srfi-1)
@@ -1750,8 +1751,12 @@ (define-public criu
              (string-append "XMLTO="
                             (search-input-file %build-inputs
                                                "/bin/xmlto")))
-       #:modules ((ice-9 ftw)
-                  ,@%gnu-build-system-modules)
+      #:modules ((guix build gnu-build-system)
+                  (guix build utils)
+                  ((guix build python-build-system)
+                   #:select (ensure-no-mtimes-pre-1980)))
+      #:imported-modules ,(append %gnu-build-system-modules
+                                 %python-build-system-modules)
        #:phases
        (modify-phases %standard-phases
          (delete 'configure)            ; no configure script
@@ -1774,17 +1779,8 @@ (define-public criu
              (substitute* "criu/include/plugin.h"
                (("/var") (string-append (assoc-ref outputs "out"))))
              ))
-         ;; TODO: use
-         ;; (@@ (guix build python-build-system) ensure-no-mtimes-pre-1980)
-         ;; when it no longer throws due to trying to call UTIME on symlinks.
          (add-after 'unpack 'ensure-no-mtimes-pre-1980
-           (lambda _
-             (let ((early-1980 315619200))  ; 1980-01-02 UTC
-               (ftw "." (lambda (file stat flag)
-                          (unless (or (<= early-1980 (stat:mtime stat))
-                                      (eq? (stat:type stat) 'symlink))
-                            (utime file early-1980 early-1980))
-                          #t)))))
+                    ensure-no-mtimes-pre-1980)
          (add-before 'build 'fix-symlink
            (lambda* (#:key inputs #:allow-other-keys)
              ;; The file 'images/google/protobuf/descriptor.proto' points to
-- 
2.41.0

>From 824c6ea30573c3a02f33058bd1739be4cd980da2 Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Sat, 13 May 2023 16:20:23 +0200
Subject: [PATCH 4/8] gnu: sssd: Change file mtimes to fix build.

* gnu/packages/sssd.scm (sssd)[arguments]: Add
python-build-system to #:modules and #:imported modules, add phase
'ensure-no-mtimes-pre-1980.
---
 gnu/packages/sssd.scm | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/gnu/packages/sssd.scm b/gnu/packages/sssd.scm
index 251275312f..85c6875f4f 100644
--- a/gnu/packages/sssd.scm
+++ b/gnu/packages/sssd.scm
@@ -31,6 +31,7 @@ (define-module (gnu packages sssd)
   #:use-module (guix utils)
   #:use-module (guix build utils)
   #:use-module (guix build-system gnu)
+  #:use-module (guix build-system python)
   #:use-module (gnu packages)
   #:use-module (gnu packages)
   #:use-module (gnu packages adns)
@@ -186,10 +187,16 @@ (define-public sssd
               (string-append "--with-xml-catalog-path="
                              #$(this-package-native-input "docbook-xml")
                              "/xml/dtd/docbook/catalog.xml"))
+      #:modules '((guix build gnu-build-system)
+                  (guix build utils)
+                  ((guix build python-build-system)
+                   #:select (ensure-no-mtimes-pre-1980)))
+      #:imported-modules (append %gnu-build-system-modules
+                                 %python-build-system-modules)
       #:phases
       #~(modify-phases %standard-phases
           (add-after 'unpack 'ensure-no-mtimes-pre-1980
-            (@@ (guix build python-build-system) ensure-no-mtimes-pre-1980))
+                     ensure-no-mtimes-pre-1980)
           (add-after 'patch-source-shebangs 'patch-more-shebangs
             (lambda _
               (substitute* '("src/tools/analyzer/sss_analyze"
-- 
2.41.0

>From 72d66e838ce9d3d2182c570ee3088063e372fcdd Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Sun, 23 Jul 2023 11:20:03 +0200
Subject: [PATCH 5/8] guix: toml: Add TOML parser.

* guix/build/toml.scm: New file.
* tests/toml.scm: New file.
* Makefile.am: Register new files.
---
 Makefile.am         |   2 +
 guix/build/toml.scm | 478 ++++++++++++++++++++++++++++++++++++++++++++
 tests/toml.scm      | 442 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 922 insertions(+)
 create mode 100644 guix/build/toml.scm
 create mode 100644 tests/toml.scm

diff --git a/Makefile.am b/Makefile.am
index d76bfd2522..b5b3ccd241 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -271,6 +271,7 @@ MODULES =					\
   guix/build/bournish.scm			\
   guix/build/qt-utils.scm			\
   guix/build/make-bootstrap.scm			\
+  guix/build/toml.scm			\
   guix/search-paths.scm				\
   guix/packages.scm				\
   guix/import/cabal.scm				\
@@ -572,6 +573,7 @@ SCM_TESTS =					\
   tests/system.scm				\
   tests/style.scm				\
   tests/texlive.scm				\
+  tests/toml.scm				\
   tests/transformations.scm			\
   tests/ui.scm					\
   tests/union.scm				\
diff --git a/guix/build/toml.scm b/guix/build/toml.scm
new file mode 100644
index 0000000000..d5ea01d001
--- /dev/null
+++ b/guix/build/toml.scm
@@ -0,0 +1,478 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Lars-Dominik Braun <[email-masked]>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+;; This is a TOML parser adapted from the ABNF for v1.0.0 from
+;; https://github.com/toml-lang/toml/blob/1.0.0/toml.abnf
+;; The PEG grammar tries to follow the ABNF as closely as possible with
+;; few deviations commented.
+;;
+;; The semantics are defined in https://toml.io/en/v1.0.0
+;; Currently unimplemented:
+;; - Array of Tables
+
+(define-module (guix build toml)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 peg)
+  #:use-module (ice-9 textual-ports)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-19)
+  #:use-module (srfi srfi-35)
+  #:export (parse-toml parse-toml-file recursive-assoc-ref &file-not-consumed &already-defined))
+
+(define-condition-type &toml-error &error toml-error?)
+(define-condition-type &file-not-consumed &toml-error file-not-consumed?)
+(define-condition-type &already-defined &toml-error already-defined?)
+
+;; Overall Structure
+(define-peg-pattern toml-file body (and expression
+                                        (* (and ignore-newline expression))))
+(define-peg-pattern expression body (or
+                                      (and ws keyval ws (? comment))
+                                      (and ws table ws (? comment))
+                                      (and ws (? comment))))
+
+;; Whitespace
+(define-peg-pattern ws none (* wschar))
+(define-peg-pattern wschar body (or " " "\t"))
+
+;; Newline
+(define-peg-pattern newline body (or "\n" "\r\n"))
+;; This newline’s content is ignored, so we don’t need a bunch of (ignore newline).
+(define-peg-pattern ignore-newline none newline)
+
+;; Comment
+(define-peg-pattern non-ascii body (or (range #\x80 #\xd7ff)
+                                       (range #\xe000 #\x10ffff)))
+(define-peg-pattern non-eol body (or "\t" (range #\x20 #\x7f) non-ascii))
+
+(define-peg-pattern comment none (and "#" (* non-eol)))
+
+;; Key-Value pairs
+(define-peg-pattern keyval all (and key keyval-sep val))
+
+(define-peg-pattern key body (or dotted-key
+                                 simple-key))
+(define-peg-pattern simple-key all (or quoted-key
+                                       unquoted-key))
+(define-peg-pattern unquoted-key body (+ (or (range #\A #\Z)
+                                             (range #\a #\z)
+                                             (range #\0 #\9)
+                                             "-"
+                                             "_")))
+(define-peg-pattern quoted-key all (or basic-string
+                                       literal-string))
+(define-peg-pattern dotted-key body (and simple-key
+                                         (+ (and dot-sep simple-key))))
+(define-peg-pattern dot-sep none (and ws "." ws))
+(define-peg-pattern keyval-sep none (and ws "=" ws))
+
+(define-peg-pattern val body (or string
+                                 boolean
+                                 array
+                                 inline-table
+                                 date-time
+                                 float
+                                 integer))
+
+;; String
+(define-peg-pattern string all (or ml-basic-string
+                                   basic-string
+                                   ml-literal-string
+                                   literal-string))
+
+;; Basic String
+(define-peg-pattern basic-string body (and (ignore "\"")
+                                           (* basic-char)
+                                           (ignore "\"")))
+(define-peg-pattern basic-char body (or basic-unescaped escaped))
+(define-peg-pattern basic-unescaped body (or wschar
+                                             "\x21"
+                                             (range #\x23 #\x5B)
+                                             (range #\x5D #\x7E)
+                                             non-ascii))
+(define-peg-pattern escaped all (and
+                                 (ignore "\\")
+                                 (or "\"" "\\" "b" "f" "n" "r" "t"
+                                     (and (ignore "u")
+                                          HEXDIG HEXDIG HEXDIG HEXDIG)
+                                     (and (ignore "U")
+                                          HEXDIG HEXDIG HEXDIG HEXDIG
+                                          HEXDIG HEXDIG HEXDIG HEXDIG))))
+
+;; Multiline Basic String
+(define-peg-pattern ml-basic-string body (and
+                                           ml-basic-string-delim
+                                          (? ignore-newline)
+                                          ml-basic-body
+                                          ml-basic-string-delim))
+(define-peg-pattern ml-basic-string-delim none "\"\"\"")
+(define-peg-pattern ml-basic-body body (and
+                                         (* mlb-content)
+                                         (* (and mlb-quotes (+ mlb-content)))
+                                         (? mlb-quotes-final)))
+
+(define-peg-pattern mlb-content body (or mlb-char newline mlb-escaped-nl))
+(define-peg-pattern mlb-char body (or mlb-unescaped escaped))
+(define-peg-pattern mlb-quotes body (or "\"\"" "\""))
+;; We need to convince the parser to backtrack here, thus the additional followed-by rule.
+(define-peg-pattern mlb-quotes-final body (or (and "\"\"" (followed-by 
+                                                           ml-basic-string-delim))
+                                              (and "\"" (followed-by
+                                                         ml-basic-string-delim))))
+(define-peg-pattern mlb-unescaped body (or wschar
+                                           "\x21"
+                                           (range #\x23 #\x5B)
+                                           (range #\x5D #\x7E)
+                                           non-ascii))
+;; Escaped newlines and following whitespace are removed from the output.
+(define-peg-pattern mlb-escaped-nl none (and "\\" ws newline
+                                             (* (or wschar newline))))
+
+;; Literal String
+(define-peg-pattern literal-string body (and (ignore "'")
+                                             (* literal-char)
+                                             (ignore "'")))
+(define-peg-pattern literal-char body (or "\x09"
+                                          (range #\x20 #\x26)
+                                          (range #\x28 #\x7E)
+                                          non-ascii))
+
+;; Multiline Literal String
+(define-peg-pattern ml-literal-string body (and
+                                            ml-literal-string-delim
+                                            (? ignore-newline)
+                                            ml-literal-body
+                                            ml-literal-string-delim))
+(define-peg-pattern ml-literal-string-delim none "'''")
+(define-peg-pattern ml-literal-body body (and
+                                          (* mll-content)
+                                          (* (and mll-quotes (+ mll-content)))
+                                          (? mll-quotes-final)))
+
+(define-peg-pattern mll-content body (or mll-char newline))
+(define-peg-pattern mll-char body (or "\x09"
+                                      (range #\x20 #\x26)
+                                      (range #\x28 #\x7E)
+                                      non-ascii))
+(define-peg-pattern mll-quotes body (or "''" "'"))
+;; We need to convince the parser to backtrack here, thus the additional followed-by rule.
+(define-peg-pattern mll-quotes-final body (or (and "''" (followed-by
+                                                         ml-literal-string-delim))
+                                              (and "'" (followed-by
+                                                        ml-literal-string-delim))))
+
+;; Integer
+(define-peg-pattern integer all (or hex-int oct-int bin-int dec-int))
+
+(define-peg-pattern digit1-9 body (range #\1 #\9))
+(define-peg-pattern digit0-7 body (range #\0 #\7))
+(define-peg-pattern digit0-1 body (range #\0 #\1))
+(define-peg-pattern DIGIT body (range #\0 #\9))
+(define-peg-pattern HEXDIG body (or DIGIT
+                                    (range #\a #\f)
+                                    (range #\A #\F)))
+
+(define-peg-pattern dec-int all (and (? (or "-" "+")) unsigned-dec-int))
+(define-peg-pattern unsigned-dec-int body (or (and digit1-9 (+ (or DIGIT (and (ignore "_") DIGIT))))
+                                              DIGIT))
+
+(define-peg-pattern hex-int all (and (ignore "0x")
+                                     HEXDIG
+                                     (* (or HEXDIG (and (ignore "_") HEXDIG)))))
+(define-peg-pattern oct-int all (and (ignore "0o")
+                                     digit0-7
+                                     (* (or digit0-7 (and (ignore "_") digit0-7)))))
+(define-peg-pattern bin-int all (and (ignore "0b")
+                                     digit0-1
+                                     (* (or digit0-1 (and (ignore "_") digit0-1)))))
+
+;; Float
+(define-peg-pattern float all (or
+                                (and float-int-part (or exp (and frac (? exp))))
+                                special-float))
+(define-peg-pattern float-int-part body dec-int)
+(define-peg-pattern frac body (and "." zero-prefixable-int))
+(define-peg-pattern zero-prefixable-int body (and DIGIT (* (or DIGIT (and (ignore "_") DIGIT)))))
+
+(define-peg-pattern exp body (and (or "e" "E") float-exp-part))
+(define-peg-pattern float-exp-part body (and (? (or "-" "+")) zero-prefixable-int))
+(define-peg-pattern special-float body (and (? (or "-" "+")) (or "inf" "nan")))
+
+;; Boolean
+(define-peg-pattern boolean all (or "true" "false"))
+
+;; Date and Time (as defined in RFC 3339)
+
+(define-peg-pattern date-time body (or offset-date-time
+                                       local-date-time
+                                       local-date
+                                       local-time))
+
+(define-peg-pattern date-fullyear all (and DIGIT DIGIT DIGIT DIGIT))
+(define-peg-pattern date-month all (and DIGIT DIGIT))  ; 01-12
+(define-peg-pattern date-mday all (and DIGIT DIGIT))  ; 01-28, 01-29, 01-30, 01-31 based on month/year
+(define-peg-pattern time-delim none (or "T" "t" " ")) ; T, t, or space
+(define-peg-pattern time-hour all (and DIGIT DIGIT))  ; 00-23
+(define-peg-pattern time-minute all (and DIGIT DIGIT))  ; 00-59
+(define-peg-pattern time-second all (and DIGIT DIGIT))  ; 00-58, 00-59, 00-60 based on leap second rules
+(define-peg-pattern time-secfrac all (and (ignore ".") (+ DIGIT)))
+(define-peg-pattern time-numoffset body (and (or "+" "-")
+                                             time-hour
+                                             (ignore ":")
+                                             time-minute))
+(define-peg-pattern time-offset all (or "Z" time-numoffset))
+
+(define-peg-pattern partial-time body (and time-hour
+                                           (ignore ":")
+                                           time-minute
+                                           (ignore ":")
+                                           time-second
+                                           (? time-secfrac)))
+(define-peg-pattern full-date body (and date-fullyear
+                                        (ignore "-")
+                                        date-month
+                                        (ignore "-")
+                                        date-mday))
+(define-peg-pattern full-time body (and partial-time time-offset))
+
+;; Offset Date-Time
+(define-peg-pattern offset-date-time all (and full-date time-delim full-time))
+
+;; Local Date-Time
+(define-peg-pattern local-date-time all (and full-date time-delim partial-time))
+
+;; Local Date
+(define-peg-pattern local-date all full-date)
+
+;; Local Time
+(define-peg-pattern local-time all partial-time)
+
+;; Array
+(define-peg-pattern array all (and (ignore "[")
+                                   (? array-values)
+                                   (ignore ws-comment-newline)
+                                   (ignore "]")))
+
+(define-peg-pattern array-values body (or
+                                       (and ws-comment-newline
+                                            val
+                                            ws-comment-newline
+                                            (ignore ",")
+                                            array-values)
+                                       (and ws-comment-newline
+                                            val
+                                            ws-comment-newline
+                                            (ignore (? ",")))))
+
+(define-peg-pattern ws-comment-newline none (* (or wschar (and (? comment) ignore-newline))))
+
+;; Table
+(define-peg-pattern table all (or array-table
+                                  std-table))
+
+;; Standard Table
+(define-peg-pattern std-table all (and (ignore "[") ws key ws (ignore "]")))
+(define-peg-pattern array-table all (and (ignore "[[") ws key ws (ignore "]]")))
+
+;; Inline Table
+(define-peg-pattern inline-table all (and (ignore "{")
+                                          (* ws)
+                                          (? inline-table-keyvals)
+                                          (* ws)
+                                          (ignore "}")))
+(define-peg-pattern inline-table-sep none (and ws "," ws))
+(define-peg-pattern inline-table-keyvals body (and keyval
+                                                   (? (and inline-table-sep inline-table-keyvals))))
+
+
+;; Parsing
+
+(define (recursive-acons key value alist)
+  "Add a VALUE to ALIST of alists descending into keys according to the
+list in KEY. For instance of KEY is (a b) this would create
+alist[a][b] = value."
+  (match key
+    (((? string? key))
+     (if (assoc-ref alist key)
+       (raise (condition (&already-defined)))
+       (alist-cons key value alist)))
+    ((elem rest ...) (match (assoc-ref alist elem)
+                       (#f
+                         (acons elem (recursive-acons rest value '()) alist))
+                       (old-value
+                         (acons elem (recursive-acons rest value old-value) (alist-delete elem alist)))))
+    (() alist)))
+
+(define (recursive-assoc-ref alist key)
+  "Retrieve a value from ALIST of alists, descending into each value of
+the list KEY. For instance a KEY (a b) would retrieve alist[a][b]."
+  (match key
+    (((? string? key)) (assoc-ref alist key))
+    ((elem rest ...) (recursive-assoc-ref (assoc-ref alist elem) rest))))
+
+(define (eval-toml-file parse-tree)
+  "Convert toml parse tree to alist."
+
+  (define (assoc-ref->number alist key)
+    (and=> (and=> (assq-ref alist key) car) string->number))
+
+  (define (eval-date rest)
+    (let ((args (keyword-flatten '(date-fullyear
+                                   date-month
+                                   date-mday
+                                   time-hour
+                                   time-minute
+                                   time-second
+                                   time-secfrac
+                                   time-offset)
+                                 rest)))
+      (make-date
+       (assoc-ref->number args 'time-secfrac)
+       (assoc-ref->number args 'time-second)
+       (assoc-ref->number args 'time-minute)
+       (assoc-ref->number args 'time-hour)
+       (assoc-ref->number args 'date-mday)
+       (assoc-ref->number args 'date-month)
+       (assoc-ref->number args 'date-fullyear)
+       (match (assq-ref args 'time-offset)
+         (("Z") 0)
+         ((sign ('time-hour hour) ('time-minute minute))
+          (* (+
+               (* (string->number (string-append sign hour)) 60)
+               (string->number minute)) 60))
+         (#f #f)))))
+
+  (define (eval-value value)
+    "Evaluate right-hand-side of 'keyval token (i.e., a value)."
+    (match value
+      (('boolean "true")
+       #t)
+      (('boolean "false")
+       #f)
+      (('integer ('dec-int int))
+       (string->number int 10))
+      (('integer ('hex-int int))
+       (string->number int 16))
+      (('integer ('oct-int int))
+       (string->number int 8))
+      (('integer ('bin-int int))
+       (string->number int 2))
+      (('float ('dec-int int) b)
+       (string->number (string-append int b) 10))
+      (('float other)
+       (match other
+         ("inf" +inf.0)
+         ("+inf" +inf.0)
+         ("-inf" -inf.0)
+         ("nan" +nan.0)
+         ("+nan" +nan.0)
+         ("-nan" -nan.0)))
+      (('offset-date-time rest ...)
+       (eval-date rest))
+      (('local-date-time rest ...)
+       (eval-date rest))
+      (('local-date rest ...)
+       (eval-date rest))
+      (('local-time rest ...)
+       (eval-date rest))
+      (('string str ...)
+       (apply string-append
+              (map (match-lambda
+                    (('escaped "\"") "\"")
+                    (('escaped "\\") "\\")
+                    (('escaped "b") "\b")
+                    (('escaped "t") "\t")
+                    (('escaped "n") "\n")
+                    (('escaped (? (lambda (x) (>= (string-length x) 4)) u))
+                     (list->string (list (integer->char (string->number u 16)))))
+                    ((? string? s) s))
+                   (keyword-flatten '(escaped) str))))
+      ('string "")
+      (('array tails ...)
+       (map eval-value (keyword-flatten '(boolean integer float string array
+                                          inline-table offset-date-time
+                                          local-date-time local-date
+                                          local-time)
+                                        tails)))
+      ('array (list))
+      (('inline-table tails ...)
+       (eval (keyword-flatten '(keyval) tails) '() '()))))
+
+  (define (ensure-list value)
+    (if (list? value)
+        value
+        (list value)))
+
+  (define (simple-key->list keys)
+     (map
+      (match-lambda
+        (('simple-key 'quoted-key) "")
+        (('simple-key ('quoted-key k)) k)
+        (('simple-key (? string? k)) k)
+        (other (raise-exception `(invalid-simple-key ,other))))
+      (keyword-flatten '(simple-key) keys)))
+  
+  (define (skip-keyval tails)
+    "Skip key-value pairs in tails until the next table."
+    (match tails
+      ((('keyval key val) tails ...)
+       (skip-keyval tails))
+      (('keyval keyval)
+       '())
+      (other other)))
+
+  (define (eval parse-tree current-table result)
+    "Evaluate toml file body."
+
+    (match parse-tree
+      ((('table ('std-table names ...)) tails ...)
+       (eval tails (simple-key->list names) result))
+      ((('table ('array-table names ...)) tails ...)
+       ;; Not implemented.
+       (eval (skip-keyval tails) '() result))
+      ((('keyval key val) tails ...)
+       (recursive-acons
+        (append current-table (ensure-list (simple-key->list key)))
+        (eval-value val)
+        (eval tails current-table result)))
+      (('keyval key val)
+       (recursive-acons
+        (append current-table (ensure-list (simple-key->list key)))
+        (eval-value val)
+        result))
+      (()
+       '())))
+  
+  (eval parse-tree '() '()))
+
+(define (parse-toml str)
+  "Parse and evaluate toml document from string STR."
+
+  (let* ((match (match-pattern toml-file str))
+         (end (peg:end match))
+         (tree (peg:tree match))
+         (flat-tree (keyword-flatten '(table keyval) tree)))
+    (if (eq? end (string-length str))
+      (eval-toml-file flat-tree)
+      (raise (condition (&file-not-consumed))))))
+
+(define (parse-toml-file file)
+  "Parse and evaluate toml document from file FILE."
+
+  (parse-toml (call-with-input-file file get-string-all)))
+
diff --git a/tests/toml.scm b/tests/toml.scm
new file mode 100644
index 0000000000..cd731cd2f0
--- /dev/null
+++ b/tests/toml.scm
@@ -0,0 +1,442 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2023 Lars-Dominik Braun <[email-masked]>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-toml)
+  #:use-module (guix build toml)
+  #:use-module (guix tests)
+  #:use-module (srfi srfi-19) ; For datetime.
+  #:use-module (srfi srfi-64)
+  #:use-module (ice-9 match))
+
+(test-begin "toml")
+
+;; Tests taken from https://toml.io/en/v1.0.0
+
+(test-error "parse-toml: Unspecified key"
+  &file-not-consumed
+  (parse-toml "key = # INVALID"))
+
+(test-error "parse-toml: Missing EOL"
+  &file-not-consumed
+  (parse-toml "first = \"Tom\" last = \"Preston-Werner\" # INVALID"))
+
+(test-equal "parse-toml: Bare keys"
+  '(("key" . "value") ("bare_key" . "value") ("bare-key" . "value") ("1234" . "value"))
+  (parse-toml "key = \"value\"
+bare_key = \"value\"
+bare-key = \"value\"
+1234 = \"value\""))
+
+(test-equal "parse-toml: Quoted keys"
+  '(("127.0.0.1" . "value")
+    ("character encoding" . "value")
+    ("ʎǝʞ" . "value")
+    ("key2" . "value")
+    ("quoted \"value\"" . "value"))
+  (parse-toml "\"127.0.0.1\" = \"value\"
+\"character encoding\" = \"value\"
+\"ʎǝʞ\" = \"value\"
+'key2' = \"value\"
+'quoted \"value\"' = \"value\""))
+
+(test-equal "parse-toml: No key"
+  #f
+  (parse-toml "= \"no key name\""))
+
+(test-equal "parse-toml: Empty key"
+  '(("" . "blank"))
+  (parse-toml "\"\" = \"blank\""))
+
+(test-equal "parse-toml: Dotted keys"
+  '(("name" . "Orange")
+    ("physical" ("color" . "orange")
+                ("shape" . "round"))
+    ("site" ("google.com" . #t)))
+  (parse-toml "name = \"Orange\"
+physical.color = \"orange\"
+physical.shape = \"round\"
+site.\"google.com\" = true"))
+
+(test-equal "parse-toml: Dotted keys with whitespace"
+  '(("fruit" ("name" . "banana") ("color" . "yellow") ("flavor" . "banana")))
+  (parse-toml "fruit.name = \"banana\"     # this is best practice
+fruit. color = \"yellow\"    # same as fruit.color
+fruit . flavor = \"banana\"   # same as fruit.flavor"))
+
+(test-error "parse-toml: Multiple keys"
+  &already-defined
+  (parse-toml "name = \"Tom\"
+name = \"Pradyun\""))
+
+(test-equal "parse-toml: Implicit tables"
+  '(("fruit" ("apple" ("smooth" . #t)) ("orange" . 2)))
+  (parse-toml "fruit.apple.smooth = true
+fruit.orange = 2"))
+
+(test-error "parse-toml: Write to value"
+  &already-defined
+  (parse-toml "fruit.apple = 1
+fruit.apple.smooth = true"))
+
+(test-equal "parse-toml: String"
+  '(("str" . "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."))
+  (parse-toml "str = \"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""))
+
+(test-equal "parse-toml: Empty string"
+  '(("str1" . "")
+    ("str2" . "")
+    ("str3" . "")
+    ("str4" . ""))
+  (parse-toml "str1 = \"\"
+str2 = ''
+str3 = \"\"\"\"\"\"
+str4 = ''''''"))
+
+(test-equal "parse-toml: Multi-line basic strings"
+  '(("str1" . "Roses are red\nViolets are blue")
+    ("str2" . "The quick brown fox jumps over the lazy dog.")
+    ("str3" . "The quick brown fox jumps over the lazy dog.")
+    ("str4" . "Here are two quotation marks: \"\". Simple enough.")
+    ("str5" . "Here are three quotation marks: \"\"\".")
+    ("str6" . "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".")
+    ("str7" . "\"This,\" she said, \"is just a pointless statement.\""))
+  (parse-toml "str1 = \"\"\"
+Roses are red
+Violets are blue\"\"\"
+              
+str2 = \"\"\"
+The quick brown \\
+
+
+  fox jumps over \\
+    the lazy dog.\"\"\"
+
+str3 = \"\"\"\\
+       The quick brown \\
+       fox jumps over \\
+       the lazy dog.\\
+       \"\"\"
+              
+str4 = \"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"
+# str5 = \"\"\"Here are three quotation marks: \"\"\".\"\"\"  # INVALID
+str5 = \"\"\"Here are three quotation marks: \"\"\\\".\"\"\"
+str6 = \"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"
+
+# \"This,\" she said, \"is just a pointless statement.\"
+str7 = \"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\""))
+
+(test-equal "parse-toml: Literal string"
+  '(("winpath" . "C:\\Users\\nodejs\\templates")
+    ("winpath2" . "\\\\ServerX\\admin$\\system32\\")
+    ("quoted" . "Tom \"Dubs\" Preston-Werner")
+    ("regex" . "<\\i\\c*\\s*>"))
+  (parse-toml "winpath  = 'C:\\Users\\nodejs\\templates'
+winpath2 = '\\\\ServerX\\admin$\\system32\\'
+quoted   = 'Tom \"Dubs\" Preston-Werner'
+regex    = '<\\i\\c*\\s*>'"))
+
+(test-equal "parse-toml: Multi-line literal strings"
+  '(("regex2" . "I [dw]on't need \\d{2} apples")
+    ("lines" . "The first newline is\ntrimmed in raw strings.\n   All other whitespace\n   is preserved.\n")
+    ("quot15" . "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"")
+    ("apos15" . "Here are fifteen apostrophes: '''''''''''''''")
+    ("str" . "'That,' she said, 'is still pointless.'"))
+  (parse-toml "regex2 = '''I [dw]on't need \\d{2} apples'''
+lines  = '''
+The first newline is
+trimmed in raw strings.
+   All other whitespace
+   is preserved.
+'''
+quot15 = '''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"'''
+
+# apos15 = '''Here are fifteen apostrophes: ''''''''''''''''''  # INVALID
+apos15 = \"Here are fifteen apostrophes: '''''''''''''''\"
+
+# 'That,' she said, 'is still pointless.'
+str = ''''That,' she said, 'is still pointless.''''"))
+
+(test-equal "parse-toml: Decimal integer"
+  '(("int1" . 99) ("int2" . 42) ("int3" . 0) ("int4" . -17))
+  (parse-toml "int1 = +99
+int2 = 42
+int3 = 0
+int4 = -17"))
+
+(test-equal "parse-toml: Decimal integer underscores"
+ '(("int5" . 1000) ("int6" . 5349221) ("int7" . 5349221) ("int8" . 12345))
+ (parse-toml "int5 = 1_000
+int6 = 5_349_221
+int7 = 53_49_221  # Indian number system grouping
+int8 = 1_2_3_4_5  # VALID but discouraged"))
+
+(test-equal "parse-toml: Hexadecimal"
+ `(("hex1" . ,#xdeadbeef) ("hex2" . ,#xdeadbeef) ("hex3" . ,#xdeadbeef))
+ (parse-toml "hex1 = 0xDEADBEEF
+hex2 = 0xdeadbeef
+hex3 = 0xdead_beef"))
+
+(test-equal "parse-toml: Octal"
+ `(("oct1" . ,#o01234567) ("oct2" . #o755))
+ (parse-toml "oct1 = 0o01234567
+oct2 = 0o755"))
+
+(test-equal "parse-toml: Binary"
+ `(("bin1" . ,#b11010110))
+ (parse-toml "bin1 = 0b11010110"))
+
+(test-equal "parse-toml: Float"
+ '(("flt1" . 1.0)
+   ("flt2" . 3.1415)
+   ("flt3" . -0.01)
+   ("flt4" . 5e+22)
+   ("flt5" . 1e06)
+   ("flt6" . -2e-2)
+   ("flt7" . 6.626e-34)
+   ("flt8" . 224617.445991228))
+ (parse-toml "# fractional
+flt1 = +1.0
+flt2 = 3.1415
+flt3 = -0.01
+
+# exponent
+flt4 = 5e+22
+flt5 = 1e06
+flt6 = -2E-2
+
+# both
+flt7 = 6.626e-34
+             
+flt8 = 224_617.445_991_228"))
+
+(test-equal "parse-toml: Float"
+ '(("sf1" . +inf.0)
+   ("sf2" . +inf.0)
+   ("sf3" . -inf.0)
+   ("sf4" . +nan.0)
+   ("sf5" . +nan.0)
+   ("sf6" . -nan.0))
+ (parse-toml "# infinity
+sf1 = inf  # positive infinity
+sf2 = +inf # positive infinity
+sf3 = -inf # negative infinity
+
+# not a number
+sf4 = nan  # actual sNaN/qNaN encoding is implementation-specific
+sf5 = +nan # same as `nan`
+sf6 = -nan # valid, actual encoding is implementation-specific"))
+
+(test-equal "parse-toml: Boolean"
+ '(("bool1" . #t)
+   ("bool2" . #f))
+ (parse-toml "bool1 = true
+bool2 = false"))
+
+(test-equal "parse-toml: Offset date-time"
+ `(("odt1" . ,(make-date #f 0 32 7 27 5 1979 0))
+   ("odt2" . ,(make-date #f 0 32 0 27 5 1979 (* -7 60 60)))
+   ("odt3" . ,(make-date 999999 0 32 0 27 5 1979 (* 7 60 60)))
+   ("odt4" . ,(make-date #f 0 32 7 27 5 1979 0)))
+ (parse-toml "odt1 = 1979-05-27T07:32:00Z
+odt2 = 1979-05-27T00:32:00-07:00
+odt3 = 1979-05-27T00:32:00.999999+07:00
+odt4 = 1979-05-27 07:32:00Z"))
+
+(test-equal "parse-toml: Local date-time"
+ `(("ldt1" . ,(make-date #f 0 32 7 27 5 1979 #f))
+   ("ldt2" . ,(make-date 999999 0 32 0 27 5 1979 #f)))
+ (parse-toml "ldt1 = 1979-05-27T07:32:00
+ldt2 = 1979-05-27T00:32:00.999999"))
+
+(test-equal "parse-toml: Local date"
+ `(("ld1" . ,(make-date #f #f #f #f 27 5 1979 #f)))
+ (parse-toml "ld1 = 1979-05-27"))
+
+(test-equal "parse-toml: Local time"
+ `(("lt1" . ,(make-date #f 0 32 7 #f #f #f #f))
+   ("lt2" . ,(make-date 999999 0 32 0 #f #f #f #f)))
+ (parse-toml "lt1 = 07:32:00
+lt2 = 00:32:00.999999"))
+
+(test-equal "parse-toml: Arrays"
+ '(("integers" 1 2 3)
+   ("colors" "red" "yellow" "green")
+   ("nested_arrays_of_ints" (1 2) (3 4 5))
+   ("nested_mixed_array" (1 2) ("a" "b" "c"))
+   ("string_array" "all" "strings")
+   ("numbers" 0.1 0.2 0.5 1 2 5)
+   ("contributors" "Foo Bar <[email-masked]>" (("name" . "Baz Qux") ("email" . "[email-masked]") ("url" . "https://example.com/bazqux")))
+   ("integers2" 1 2 3)
+   ("integers3" 1 2))
+ (parse-toml "integers = [ 1, 2, 3 ]
+colors = [ \"red\", \"yellow\", \"green\" ]
+nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ]
+nested_mixed_array = [ [ 1, 2 ], [\"a\", \"b\", \"c\"] ]
+string_array = [ \"all\", 'strings' ]
+
+# Mixed-type arrays are allowed
+numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]
+contributors = [
+  \"Foo Bar <[email-masked]>\",
+  { name = \"Baz Qux\", email = \"[email-masked]\", url = \"https://example.com/bazqux\" }
+]
+             
+integers2 = [
+  1, 2, 3
+]
+
+integers3 = [
+  1,
+  2, # this is ok
+]"))
+
+(test-equal "parse-toml: Tables"
+ '(("table-1" ("key1" . "some string")
+              ("key2" . 123))
+   ("table-2" ("key1" . "another string")
+              ("key2" . 456)))
+ (parse-toml "[table-1]
+key1 = \"some string\"
+key2 = 123
+
+[table-2]
+key1 = \"another string\"
+key2 = 456"))
+
+
+(test-equal "parse-toml: Dotted table"
+ '(("dog" ("tater.man" ("type" ("name" . "pug")))))
+ (parse-toml "[dog.\"tater.man\"]
+type.name = \"pug\""))
+
+
+(test-equal "parse-toml: Dotted table with whitespace"
+ '(("a" ("b" ("c" ("x" . 1))))
+   ("d" ("e" ("f" ("x" . 1))))
+   ("g" ("h" ("i" ("x" . 1))))
+   ("j" ("ʞ" ("l" ("x" . 1)))))
+ (parse-toml "[a.b.c]            # this is best practice
+x=1
+[ d.e.f ]          # same as [d.e.f]
+x=1
+[ g . h . i ]    # same as [g.h.i]
+x=1
+[ j . \"ʞ\" . 'l' ]  # same as [j.\"ʞ\".'l']
+x=1"))
+
+;; XXX: technically this is not allowed, but we permit it.
+(test-equal "parse-toml: Multiple tables"
+ '(("fruit" ("apple" . "red") ("orange" . "orange")))
+ (parse-toml "[fruit]
+apple = \"red\"
+
+[fruit]
+orange = \"orange\""))
+
+(test-equal "parse-toml: Assignment to non-table"
+ #f
+ (parse-toml "[fruit]
+apple = \"red\"
+
+[fruit.apple]
+texture = \"smooth\""))
+
+(test-equal "parse-toml: Dotted keys create tables"
+ '(("fruit" ("apple" ("color" . "red") ("taste" ("sweet" . #t)))))
+ (parse-toml "fruit.apple.color = \"red\"
+fruit.apple.taste.sweet = true"))
+
+(test-equal "parse-toml: Inline tables"
+ '(("name" ("first" . "Tom") ("last" . "Preston-Werner"))
+   ("point" ("x" . 1) ("y" . 2))
+   ("animal" ("type" ("name" . "pug"))))
+ (parse-toml "name = { first = \"Tom\", last = \"Preston-Werner\" }
+point = { x = 1, y = 2 }
+animal = { type.name = \"pug\" }"))
+
+(test-error "parse-toml: Invalid assignment to inline table"
+ #t
+ (parse-toml "[product]
+type = { name = \"Nail\" }
+type.edible = false  # INVALID"))
+
+;; We do not catch this semantic error yet.
+(test-expect-fail 1)
+(test-error "parse-toml: Invalid assignment to implicit table"
+ #f
+ (parse-toml "[product]
+type.name = \"Nail\"
+type = { edible = false }  # INVALID"))
+
+;; Not implemented.
+(test-expect-fail 1)
+(test-equal "parse-toml: Array of tables"
+ '(("products" (("name" . "Hammer") ("sku" . 738594937))
+               ()
+               (("name" . "Nail") ("sku" . 284758393) ("color" . "gray"))))
+ (parse-toml "[[products]]
+name = \"Hammer\"
+sku = 738594937
+
+[[products]]  # empty table within the array
+
+[[products]]
+name = \"Nail\"
+sku = 284758393
+
+color = \"gray\""))
+
+;; Not implemented.
+(test-expect-fail 1)
+(test-equal "parse-toml: Array of tables"
+ '(("fruits" ((("name" . "apple")
+               ("physical" (("color" . "red") ("shape" . "round")))
+               ("varieties" ((("name" . "red delicious")) (("name" . "granny smith")))))
+              (("name" . "banana")
+               ("varieties" (((("name" . "plantain")))))))))
+ (parse-toml "[[fruits]]
+name = \"apple\"
+
+[fruits.physical]  # subtable
+color = \"red\"
+shape = \"round\"
+
+[[fruits.varieties]]  # nested array of tables
+name = \"red delicious\"
+
+[[fruits.varieties]]
+name = \"granny smith\"
+
+
+[[fruits]]
+name = \"banana\"
+
+[[fruits.varieties]]
+name = \"plantain\""))
+
+;; Not implemented.
+(test-expect-fail 1)
+(test-error "parse-toml: Assignment to statically defined array"
+ #f
+ (parse-toml "fruits = []
+
+[[fruits]]
+x=1"))
+
+(test-end "toml")
+
-- 
2.41.0

>From 8e079b48b7c07c07360db8eb6305e8044d86dd87 Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Sun, 23 Jul 2023 11:22:03 +0200
Subject: [PATCH 6/8] build-system/pyproject: Use TOML parser.

More reliable than regular expressions.

* guix/build-system/pyproject.scm (%pyproject-build-system-modules): Add (guix build toml).
(pyproject-build): Add argument #:backend-path.
* guix/build/pyproject-build-system.scm (build): Add support for
auto-detected and override backend-path.
* gnu/packages/python-build.scm (python-tomli)[arguments]: Remove
'add-self-to-path, because it is not necessary any more.
(python-poetry-core): Same.
(python-hatchling): Same.
(python-pdm-backend): Same.
---
 gnu/packages/python-build.scm         | 30 ++------------
 guix/build-system/pyproject.scm       |  3 ++
 guix/build/pyproject-build-system.scm | 56 +++++++++++++++------------
 3 files changed, 39 insertions(+), 50 deletions(-)

diff --git a/gnu/packages/python-build.scm b/gnu/packages/python-build.scm
index faaffbcea5..4a8138dcae 100644
--- a/gnu/packages/python-build.scm
+++ b/gnu/packages/python-build.scm
@@ -178,13 +178,7 @@ (define-public python-tomli
         (base32 "0kwazq3i18rphcr8gak4fgzdcj5w5bbn4k4j2l6ma32gj496qlny"))))
     (build-system pyproject-build-system)
     (arguments
-     `(#:tests? #f                      ;disabled to avoid extra dependencies
-       #:phases
-       (modify-phases %standard-phases
-         (add-before 'build 'add-self-to-path
-           (lambda _
-             ;; The build system of tomli requires... tomli.
-             (setenv "PYTHONPATH" "src"))))))
+     `(#:tests? #f))                      ;disabled to avoid extra dependencies
     (native-inputs (list python-flit-core-bootstrap python-six-bootstrap))
     (home-page "https://github.com/hukkin/tomli")
     (synopsis "Small and fast TOML parser")
@@ -505,13 +499,7 @@ (define-public python-poetry-core
         (base32 "053c8dw632p7jkhjb51k0wcx6hdw4r3lk97mds76df653qxnqmf6"))))
     (build-system pyproject-build-system)
     (arguments
-     `(#:tests? #f                      ;disabled to avoid extra dependencies
-       #:phases
-       (modify-phases %standard-phases
-         (add-before 'build 'add-self-to-path
-           (lambda _
-             ;; The build system requires itself.
-             (setenv "PYTHONPATH" "src"))))))
+     `(#:tests? #f))                      ;disabled to avoid extra dependencies
     (home-page "https://github.com/python-poetry/poetry-core")
     (synopsis "Poetry PEP 517 build back-end")
     (description
@@ -651,12 +639,7 @@ (define-public python-hatchling
                 "1nn5cyc9fgrbawz38drfkl2s588k2gn3yqdm2cldbx9zy0fsjbj6"))))
     (build-system pyproject-build-system)
     (arguments
-     (list #:tests? #f                  ;to keep dependencies to a minimum
-           #:phases #~(modify-phases %standard-phases
-                        (add-before 'build 'add-src-to-path
-                          ;; Hatchling uses itself to build itself.
-                          (lambda _
-                            (setenv "PYTHONPATH" "src"))))))
+     (list #:tests? #f))                  ;to keep dependencies to a minimum
     (propagated-inputs (list python-editables
                              python-packaging-bootstrap
                              python-pathspec
@@ -730,12 +713,7 @@ (define-public python-pdm-backend
     (build-system pyproject-build-system)
     (arguments
      (list
-      #:tests? #f ; Depends on pytest, which we cannot import into this module.
-      #:phases
-      #~(modify-phases %standard-phases
-          (add-after 'unpack 'set-pythonpath
-            (lambda _
-              (setenv "PYTHONPATH" (string-append (getcwd) "/src")))))))
+      #:tests? #f)) ; Depends on pytest, which we cannot import into this module.
     (home-page "https://pdm-backend.fming.dev/")
     (synopsis
      "PEP 517 build backend for PDM")
diff --git a/guix/build-system/pyproject.scm b/guix/build-system/pyproject.scm
index 94b9d79692..585117cbf0 100644
--- a/guix/build-system/pyproject.scm
+++ b/guix/build-system/pyproject.scm
@@ -46,6 +46,7 @@ (define %pyproject-build-system-modules
   ;; Build-side modules imported by default.
   `((guix build pyproject-build-system)
     (guix build json)
+    (guix build toml)
     ,@%python-build-system-modules))
 
 (define (default-python)
@@ -93,6 +94,7 @@ (define* (pyproject-build name inputs
                           #:key source
                           (tests? #t)
                           (configure-flags ''())
+                          (backend-path #f)
                           (build-backend #f)
                           (test-backend #f)
                           (test-flags ''())
@@ -116,6 +118,7 @@ (define* (pyproject-build name inputs
                  #:source #+source
                  #:configure-flags #$configure-flags
                  #:system #$system
+                 #:backend-path #$backend-path
                  #:build-backend #$build-backend
                  #:test-backend #$test-backend
                  #:test-flags #$test-flags
diff --git a/guix/build/pyproject-build-system.scm b/guix/build/pyproject-build-system.scm
index c69ccc9d64..a1919eacf6 100644
--- a/guix/build/pyproject-build-system.scm
+++ b/guix/build/pyproject-build-system.scm
@@ -21,11 +21,13 @@ (define-module (guix build pyproject-build-system)
   #:use-module ((guix build python-build-system) #:prefix python:)
   #:use-module (guix build utils)
   #:use-module (guix build json)
+  #:use-module (guix build toml)
   #:use-module (ice-9 match)
   #:use-module (ice-9 ftw)
   #:use-module (ice-9 format)
   #:use-module (ice-9 rdelim)
   #:use-module (ice-9 regex)
+  #:use-module (ice-9 textual-ports)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
@@ -60,8 +62,8 @@ (define-module (guix build pyproject-build-system)
 ;;; wheel and expected to be created by the installing utility.
 ;;; TODO: Add support for PEP-621 entry points.
 ;;;
-;;; Caveats:
-;;; - There is no support for in-tree build backends.
+;;; This module also supports in-tree build backends, which can be
+;;; overridden by #:backend-path.
 ;;;
 ;;; Code:
 ;;;
@@ -86,23 +88,23 @@ (define-condition-type &cannot-extract-multiple-wheels &python-build-error
 ;; Raised, when no wheel has been built by the build system.
 (define-condition-type &no-wheels-built &python-build-error no-wheels-built?)
 
-(define* (build #:key outputs build-backend configure-flags #:allow-other-keys)
+(define* (build #:key outputs build-backend backend-path configure-flags #:allow-other-keys)
   "Build a given Python package."
 
-  (define (pyproject.toml->build-backend file)
-    "Look up the build backend in a pyproject.toml file."
-    (call-with-input-file file
-      (lambda (in)
-        (let loop
-          ((line (read-line in 'concat)))
-          (if (eof-object? line) #f
-              (let ((m (string-match "build-backend = [\"'](.+)[\"']" line)))
-                (if m
-                    (match:substring m 1)
-                    (loop (read-line in 'concat)))))))))
-
   (let* ((wheel-output (assoc-ref outputs "wheel"))
          (wheel-dir (if wheel-output wheel-output "dist"))
+         (pyproject.toml (if (file-exists? "pyproject.toml")
+                             (parse-toml-file "pyproject.toml")
+                             '()))
+         ;; backend-path is prepended to sys.path, so in-tree backends can be
+         ;; found. We assume toml is json-compatible and do not encode the resulting
+         ;; JSON list expression.
+         (auto-backend-path (recursive-assoc-ref
+                             pyproject.toml
+                             '("build-system" "backend-path")))
+         (use-backend-path (call-with-output-string
+                            (cut write-json
+                             (or backend-path auto-backend-path '()) <>)))
          ;; There is no easy way to get data from Guile into Python via
          ;; s-expressions, but we have JSON serialization already, which Python
          ;; also supports out-of-the-box.
@@ -111,10 +113,9 @@ (define* (build #:key outputs build-backend configure-flags #:allow-other-keys)
          ;; python-setuptools’ default backend supports setup.py *and*
          ;; pyproject.toml. Allow overriding this automatic detection via
          ;; build-backend.
-         (auto-build-backend (if (file-exists? "pyproject.toml")
-                                 (pyproject.toml->build-backend
-                                  "pyproject.toml")
-                                 #f))
+         (auto-build-backend (recursive-assoc-ref
+                              pyproject.toml
+                              '("build-system" "build-backend")))
          ;; Use build system detection here and not in importer, because a) we
          ;; have alot of legacy packages and b) the importer cannot update arbitrary
          ;; fields in case a package switches its build system.
@@ -122,15 +123,22 @@ (define* (build #:key outputs build-backend configure-flags #:allow-other-keys)
                                 auto-build-backend
                                 "setuptools.build_meta")))
     (format #t
-     "Using '~a' to build wheels, auto-detected '~a', override '~a'.~%"
-     use-build-backend auto-build-backend build-backend)
+     (string-append
+      "Using '~a' to build wheels, auto-detected '~a', override '~a'.~%"
+      "Prepending '~a' to sys.path, auto-detected '~a', override '~a'.~%")
+     use-build-backend auto-build-backend build-backend
+     use-backend-path auto-backend-path backend-path)
     (mkdir-p wheel-dir)
     ;; Call the PEP 517 build function, which drops a .whl into wheel-dir.
     (invoke "python" "-c"
      "import sys, importlib, json
-config_settings = json.loads (sys.argv[3])
-builder = importlib.import_module(sys.argv[1])
-builder.build_wheel(sys.argv[2], config_settings=config_settings)"
+backend_path = json.loads (sys.argv[1]) or []
+backend_path.extend (sys.path)
+sys.path = backend_path
+config_settings = json.loads (sys.argv[4])
+builder = importlib.import_module(sys.argv[2])
+builder.build_wheel(sys.argv[3], config_settings=config_settings)"
+     use-backend-path
      use-build-backend
      wheel-dir
      config-settings)))
-- 
2.41.0

>From e987a9e28ec0d8d1b8ecdb2486eb12eae270a49d Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Tue, 25 Jul 2023 18:26:58 +0200
Subject: [PATCH 7/8] gnu: python-pytest-xdist: Disable failing test.

* gnu/packages/check.scm (python-pytest-xdist)[arguments]: Skip failing
test.
---
 gnu/packages/check.scm | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/gnu/packages/check.scm b/gnu/packages/check.scm
index 691fe67605..2e1cda27b1 100644
--- a/gnu/packages/check.scm
+++ b/gnu/packages/check.scm
@@ -1631,7 +1631,10 @@ (define-public python-pytest-xdist
            (lambda* (#:key tests? #:allow-other-keys)
              (when tests?
                (invoke "pytest" "-vv"
-                       "-n" (number->string (parallel-job-count)))))))))
+                       "-n" (number->string (parallel-job-count))
+                       ;; Fails with OSError: cannot send to <Channel id=1 closed>
+                       ;; on foreign distribution.
+                       "-k" "not test_internal_errors_propagate_to_controller")))))))
     (native-inputs (list python-setuptools-scm python-filelock python-pytest))
     (propagated-inputs (list python-execnet python-pytest-forked))
     (home-page "https://github.com/pytest-dev/pytest-xdist")
-- 
2.41.0

>From b3726639df72aa3943d8e403e3c2b9a6cde05421 Mon Sep 17 00:00:00 2001
Message-ID: <[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Lars-Dominik Braun <[email-masked]>
Date: Sun, 30 Jul 2023 13:36:37 +0200
Subject: [PATCH 8/8] guix: pyproject-build-system: Default configure-flags to
 empty dictionary.

PEP 517 specifies it should be a dictionary and thus meson-python cannot
handle an empty list.

Fixes: <https://issues.guix.gnu.org/62781>
---
 gnu/packages/build-tools.scm    | 10 ++--------
 gnu/packages/python-science.scm |  5 -----
 guix/build-system/pyproject.scm |  2 +-
 3 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/gnu/packages/build-tools.scm b/gnu/packages/build-tools.scm
index a365cca849..09a8a175e8 100644
--- a/gnu/packages/build-tools.scm
+++ b/gnu/packages/build-tools.scm
@@ -355,10 +355,7 @@ (define-public meson-python
                 "1hpjw9qj6ff8ixjs0pz7qysc8v57jxgaf5n1p6bqm9bh3mc3wnrx"))))
     (build-system pyproject-build-system)
     (arguments
-     ;; The project is configured to use itself to build ('mesonpy') and fails;
-     ;; use another PEP 517 build system.
-     (list #:build-backend "setuptools.build_meta"
-           #:test-flags #~(list "tests"
+     (list #:test-flags #~(list "tests"
                                 ;; The test_pep518 tries to install
                                 ;; dependencies from the network using pip.
                                 "-k" "not test_pep518")))
@@ -370,10 +367,7 @@ (define-public meson-python
            python-tomli
            python-wheel))
     (native-inputs
-     (list python-pypa-build
-           python-wheel
-
-           ;; For tests.
+     (list ;; For tests.
            git-minimal/pinned
            patchelf
            pkg-config
diff --git a/gnu/packages/python-science.scm b/gnu/packages/python-science.scm
index 538a3c2f0e..47890389b5 100644
--- a/gnu/packages/python-science.scm
+++ b/gnu/packages/python-science.scm
@@ -93,11 +93,6 @@ (define-public python-scipy
     (build-system pyproject-build-system)
     (arguments
      (list
-      ;; FIXME: The default 'mesonpy' build system doesn't seem to work with
-      ;; our pyproject-build-system, errors with: AttributeError: 'list'
-      ;; object has no attribute 'items' (see:
-      ;; https://issues.guix.gnu.org/62781).
-      #:build-backend "setuptools.build_meta"
       #:phases
       #~(modify-phases %standard-phases
           (replace 'check
diff --git a/guix/build-system/pyproject.scm b/guix/build-system/pyproject.scm
index 585117cbf0..c0e089eac7 100644
--- a/guix/build-system/pyproject.scm
+++ b/guix/build-system/pyproject.scm
@@ -93,7 +93,7 @@ (define* (lower name
 (define* (pyproject-build name inputs
                           #:key source
                           (tests? #t)
-                          (configure-flags ''())
+                          (configure-flags ''(@))
                           (backend-path #f)
                           (build-backend #f)
                           (test-backend #f)
-- 
2.41.0