diff options
| author | Nicolas Graves <ngraves@ngraves.fr> | 2025-10-29 15:11:55 +0100 |
|---|---|---|
| committer | Sharlatan Hellseher <sharlatanus@gmail.com> | 2026-05-24 10:14:33 +0100 |
| commit | 4f4ee461796491afd092f0baa80df96537296f63 (patch) | |
| tree | 4f36fceb9679fd030909fd40ecdc8f1ea28d082f | |
| parent | 30b3cbc90f27c4d84306f949bba24989779c0992 (diff) | |
build-system/pyproject: Ignore selected pytest inputs.
This commit includes squashed changes from
https://codeberg.org/guix/guix/pulls/7220 and
https://codeberg.org/guix/guix/pulls/7338.
* gnu/packages/aux-files/python/pytest_guix.py: New file.
* Makefile.am: Record it.
* guix/build/pyproject-build-system.scm (check): Preload pytest_guix
plugin when available.
* guix/build-system/pyproject.scm (default-pytest-guix-plugin): New
package, generated from pytest_guix.py.
(lower): Add python-pytest-guix argument, and inject it if
python-pytest is in the native-inputs.
Change-Id: I13263b461e9962aad340347657b9c9685db63927
Signed-off-by: Sharlatan Hellseher <sharlatanus@gmail.com>
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | gnu/packages/aux-files/python/pytest_guix.py | 68 | ||||
| -rw-r--r-- | guix/build-system/pyproject.scm | 58 | ||||
| -rw-r--r-- | guix/build/pyproject-build-system.scm | 20 |
4 files changed, 135 insertions, 12 deletions
diff --git a/Makefile.am b/Makefile.am index 16ab2d317ec..79575c7dfdb 100644 --- a/Makefile.am +++ b/Makefile.am | |||
| @@ -488,6 +488,7 @@ AUX_FILES = \ | |||
| 488 | gnu/packages/aux-files/linux-libre/5.10-x86_64.conf \ | 488 | gnu/packages/aux-files/linux-libre/5.10-x86_64.conf \ |
| 489 | gnu/packages/aux-files/ovmf/51-edk2-ovmf-2m-raw-x64-nosb.json \ | 489 | gnu/packages/aux-files/ovmf/51-edk2-ovmf-2m-raw-x64-nosb.json \ |
| 490 | gnu/packages/aux-files/pack-audit.c \ | 490 | gnu/packages/aux-files/pack-audit.c \ |
| 491 | gnu/packages/aux-files/python/pytest_guix.py \ | ||
| 491 | gnu/packages/aux-files/python/sanity-check.py \ | 492 | gnu/packages/aux-files/python/sanity-check.py \ |
| 492 | gnu/packages/aux-files/python/sitecustomize.py \ | 493 | gnu/packages/aux-files/python/sitecustomize.py \ |
| 493 | gnu/packages/aux-files/renpy/renpy.in \ | 494 | gnu/packages/aux-files/renpy/renpy.in \ |
diff --git a/gnu/packages/aux-files/python/pytest_guix.py b/gnu/packages/aux-files/python/pytest_guix.py new file mode 100644 index 00000000000..750b1e99e99 --- /dev/null +++ b/gnu/packages/aux-files/python/pytest_guix.py | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | # GNU Guix --- Functional package management for GNU | ||
| 2 | # Copyright © 2025 Nicolas Graves <ngraves@ngraves.fr> | ||
| 3 | # | ||
| 4 | # This file is part of GNU Guix. | ||
| 5 | # | ||
| 6 | # GNU Guix is free software; you can redistribute it and/or modify it | ||
| 7 | # under the terms of the GNU General Public License as published by | ||
| 8 | # the Free Software Foundation; either version 3 of the License, or (at | ||
| 9 | # your option) any later version. | ||
| 10 | # | ||
| 11 | # GNU Guix is distributed in the hope that it will be useful, but | ||
| 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | # GNU General Public License for more details. | ||
| 15 | # | ||
| 16 | # You should have received a copy of the GNU General Public License | ||
| 17 | # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | |||
| 19 | import importlib.util | ||
| 20 | |||
| 21 | |||
| 22 | def pytest_addoption(parser): | ||
| 23 | """Add stub options to be ignored by pytest. | ||
| 24 | |||
| 25 | More precisely, inject all options provided in .pytest_guix_options.json, | ||
| 26 | except options whose plugin is indeed installed. | ||
| 27 | |||
| 28 | For example, if the json file records --cov: | ||
| 29 | if the pytest_cov module is installed, its --cov will be used. | ||
| 30 | otherwise, --cov is ignored (read by this parser, but nothing is done | ||
| 31 | with it). | ||
| 32 | |||
| 33 | This allows to remove development packages, which are not required at build | ||
| 34 | time while at the same time avoiding the need to adjust test options in | ||
| 35 | pyproject.toml or other configuration files. | ||
| 36 | """ | ||
| 37 | plugin_options = { | ||
| 38 | "cov": [ | ||
| 39 | "--cov", | ||
| 40 | "--cov-reset", | ||
| 41 | "--cov-report", | ||
| 42 | "--cov-config", | ||
| 43 | "--no-cov-on-fail", | ||
| 44 | "--no-cov", | ||
| 45 | "--cov-fail-under", | ||
| 46 | "--cov-append", | ||
| 47 | "--cov-branch", | ||
| 48 | "--cov-context", | ||
| 49 | ], | ||
| 50 | "mypy": ["--mypy", "--mypy-config-file", "--mypy-ignore-missing-imports"], | ||
| 51 | "isort": ["--isort"], | ||
| 52 | "flake8": ["--flake8"], | ||
| 53 | "black": ["--black"], | ||
| 54 | "flakes": ["--flakes"], | ||
| 55 | "pep8": ["--pep8"], | ||
| 56 | "html": ["--html", "--self-contained-html", "--css"], | ||
| 57 | } | ||
| 58 | |||
| 59 | group = parser.getgroup( | ||
| 60 | "guix", "Options ignored by the Guix pyproject-build-system" | ||
| 61 | ) | ||
| 62 | |||
| 63 | # Only add options for plugins that are not present. | ||
| 64 | for key, options in plugin_options.items(): | ||
| 65 | if importlib.util.find_spec(f"pytest_{key}") is None: | ||
| 66 | # Plugin not found, add stub options | ||
| 67 | for option in options: | ||
| 68 | group.addoption(option, action="append", nargs="?") | ||
diff --git a/guix/build-system/pyproject.scm b/guix/build-system/pyproject.scm index d8939a7fde8..d833be10b47 100644 --- a/guix/build-system/pyproject.scm +++ b/guix/build-system/pyproject.scm | |||
| @@ -23,15 +23,19 @@ | |||
| 23 | #:use-module (guix store) | 23 | #:use-module (guix store) |
| 24 | #:use-module (guix utils) | 24 | #:use-module (guix utils) |
| 25 | #:use-module (guix gexp) | 25 | #:use-module (guix gexp) |
| 26 | #:use-module ((guix licenses) #:prefix license:) | ||
| 26 | #:use-module (guix monads) | 27 | #:use-module (guix monads) |
| 27 | #:use-module (guix packages) | 28 | #:use-module (guix packages) |
| 28 | #:use-module (guix search-paths) | 29 | #:use-module (guix search-paths) |
| 29 | #:use-module (guix build-system) | 30 | #:use-module (guix build-system) |
| 30 | #:use-module (guix build-system gnu) | 31 | #:use-module (guix build-system gnu) |
| 31 | #:use-module (guix build-system python) | 32 | #:use-module (guix build-system python) |
| 33 | #:use-module (guix build-system trivial) | ||
| 32 | #:use-module (srfi srfi-1) | 34 | #:use-module (srfi srfi-1) |
| 35 | #:use-module (ice-9 match) | ||
| 33 | #:export (%pyproject-build-system-modules | 36 | #:export (%pyproject-build-system-modules |
| 34 | default-python | 37 | default-python |
| 38 | default-pytest-guix-plugin | ||
| 35 | default-sanity-check.py | 39 | default-sanity-check.py |
| 36 | pyproject-build | 40 | pyproject-build |
| 37 | pyproject-build-system | 41 | pyproject-build-system |
| @@ -62,6 +66,37 @@ | |||
| 62 | "Return the default guile-json package, resolved lazily." | 66 | "Return the default guile-json package, resolved lazily." |
| 63 | (@* (gnu packages guile) guile-json-4)) | 67 | (@* (gnu packages guile) guile-json-4)) |
| 64 | 68 | ||
| 69 | ;; Maybe try to upstream it at some point, it's currently flavored for guix | ||
| 70 | ;; but the idea itself is more general. | ||
| 71 | (define (default-pytest-guix-plugin python) | ||
| 72 | (let* ((effective (version-major+minor (package-version python))) | ||
| 73 | (site (string-append "lib/python" effective "/site-packages/"))) | ||
| 74 | (package | ||
| 75 | (name "python-pytest-guix") | ||
| 76 | (version "0.0.1") | ||
| 77 | (source (local-file (search-auxiliary-file "python/pytest_guix.py"))) | ||
| 78 | (build-system trivial-build-system) | ||
| 79 | (arguments | ||
| 80 | (list | ||
| 81 | #:modules '((guix build utils)) | ||
| 82 | #:builder | ||
| 83 | #~(begin | ||
| 84 | (use-modules (guix build utils)) | ||
| 85 | (let* ((site (string-append #$output "/" #$site)) | ||
| 86 | (dist (string-append site "pytest_guix-" #$version | ||
| 87 | ".dist.info"))) | ||
| 88 | (mkdir-p dist) | ||
| 89 | (copy-file #$source (string-append site "/pytest_guix.py")) | ||
| 90 | (call-with-output-file (string-append dist "/entry_points.txt") | ||
| 91 | (lambda (port) | ||
| 92 | (format port "[pytest11]~%guix=pytest_guix~%"))))))) | ||
| 93 | (home-page "https://guix.gnu.org/") | ||
| 94 | (synopsis "Ignore selected pytest options") | ||
| 95 | (description | ||
| 96 | "This package provides the script to cleanly ignore pytest options at the | ||
| 97 | build-system level.") | ||
| 98 | (license license:gpl3+)))) | ||
| 99 | |||
| 65 | ;; TODO: On the next iteration of python-team, migrate the sanity-check to | 100 | ;; TODO: On the next iteration of python-team, migrate the sanity-check to |
| 66 | ;; importlib_metadata instead of setuptools. | 101 | ;; importlib_metadata instead of setuptools. |
| 67 | (define (default-sanity-check.py) | 102 | (define (default-sanity-check.py) |
| @@ -69,13 +104,21 @@ | |||
| 69 | 104 | ||
| 70 | (define* (lower name | 105 | (define* (lower name |
| 71 | #:key source inputs native-inputs outputs system target | 106 | #:key source inputs native-inputs outputs system target |
| 107 | test-backend | ||
| 72 | (python (default-python)) | 108 | (python (default-python)) |
| 109 | (python-pytest-guix (default-pytest-guix-plugin python)) | ||
| 73 | (sanity-check.py (default-sanity-check.py)) | 110 | (sanity-check.py (default-sanity-check.py)) |
| 74 | #:allow-other-keys | 111 | #:allow-other-keys |
| 75 | #:rest arguments) | 112 | #:rest arguments) |
| 76 | "Return a bag for NAME." | 113 | "Return a bag for NAME." |
| 77 | (define private-keywords | 114 | (define private-keywords |
| 78 | '(#:target #:python #:inputs #:native-inputs #:sanity-check.py)) | 115 | '(#:target #:python #:inputs #:native-inputs |
| 116 | #:python-pytest-guix #:sanity-check.py)) | ||
| 117 | (define native-inputs-labels (map car native-inputs)) | ||
| 118 | (define has-pytest? | ||
| 119 | (or (member "python-pytest-bootstrap" native-inputs-labels) | ||
| 120 | (member "python-pytest" native-inputs-labels))) | ||
| 121 | |||
| 79 | 122 | ||
| 80 | (and (not target) ;XXX: no cross-compilation | 123 | (and (not target) ;XXX: no cross-compilation |
| 81 | (bag | 124 | (bag |
| @@ -88,9 +131,16 @@ | |||
| 88 | 131 | ||
| 89 | ;; Keep the standard inputs of 'gnu-build-system'. | 132 | ;; Keep the standard inputs of 'gnu-build-system'. |
| 90 | ,@(standard-packages))) | 133 | ,@(standard-packages))) |
| 91 | (build-inputs `(("python" ,python) | 134 | (build-inputs |
| 92 | ("sanity-check.py" ,sanity-check.py) | 135 | `(("python" ,python) |
| 93 | ,@native-inputs)) | 136 | ("sanity-check.py" ,sanity-check.py) |
| 137 | ,@(if (and has-pytest? | ||
| 138 | (match test-backend | ||
| 139 | ((or 'pytest-with-guix-plugin #f) #t) | ||
| 140 | (_ #f))) | ||
| 141 | `(("python-pytest-guix" ,python-pytest-guix)) | ||
| 142 | `()) | ||
| 143 | ,@native-inputs)) | ||
| 94 | (outputs (append outputs '(wheel))) | 144 | (outputs (append outputs '(wheel))) |
| 95 | (build pyproject-build) | 145 | (build pyproject-build) |
| 96 | (arguments (strip-keyword-arguments private-keywords arguments))))) | 146 | (arguments (strip-keyword-arguments private-keywords arguments))))) |
diff --git a/guix/build/pyproject-build-system.scm b/guix/build/pyproject-build-system.scm index 21f356c67e9..5685ddc033f 100644 --- a/guix/build/pyproject-build-system.scm +++ b/guix/build/pyproject-build-system.scm | |||
| @@ -314,7 +314,7 @@ without errors." | |||
| 314 | (with-directory-excursion "/tmp" | 314 | (with-directory-excursion "/tmp" |
| 315 | (invoke "python" sanity-check.py (site-packages inputs outputs))))) | 315 | (invoke "python" sanity-check.py (site-packages inputs outputs))))) |
| 316 | 316 | ||
| 317 | (define* (check #:key tests? test-backend test-flags #:allow-other-keys) | 317 | (define* (check #:key inputs tests? test-backend test-flags #:allow-other-keys) |
| 318 | "Run the test suite of a given Python package." | 318 | "Run the test suite of a given Python package." |
| 319 | (if tests? | 319 | (if tests? |
| 320 | ;; Unfortunately with PEP 517 there is no common method to specify test | 320 | ;; Unfortunately with PEP 517 there is no common method to specify test |
| @@ -330,20 +330,24 @@ without errors." | |||
| 330 | (tests-found (find-files "." "test.*\\.py$")) | 330 | (tests-found (find-files "." "test.*\\.py$")) |
| 331 | (use-test-backend | 331 | (use-test-backend |
| 332 | (or test-backend | 332 | (or test-backend |
| 333 | ;; Prefer pytest | 333 | ;; By order of preference. |
| 334 | (if pytest 'pytest #f) | 334 | (and (assoc-ref inputs "python-pytest-guix") |
| 335 | (if stestr 'stestr #f) | 335 | 'pytest-with-guix-plugin) |
| 336 | (if nosetests 'nose #f) | 336 | (and pytest 'pytest) |
| 337 | (if nose2 'nose2 #f) | 337 | (and stestr 'stestr) |
| 338 | (and nosetests 'nose) | ||
| 339 | (and nose2 'nose2) | ||
| 338 | ;; Fall back to setup.py. The command is deprecated, but is | 340 | ;; Fall back to setup.py. The command is deprecated, but is |
| 339 | ;; a superset of unittest, so should work for most packages. | 341 | ;; a superset of unittest, so should work for most packages. |
| 340 | ;; Keep it until setuptools removes `setup.py test'. | 342 | ;; Keep it until setuptools removes `setup.py test'. |
| 341 | ;; See https://setuptools.pypa.io/en/latest/deprecated/\ | 343 | ;; See https://setuptools.pypa.io/en/latest/deprecated/\ |
| 342 | ;; commands.html#test-build-package-and-run-a-unittest-suite | 344 | ;; commands.html#test-build-package-and-run-a-unittest-suite |
| 343 | (if have-setup-py 'setup.py #f) | 345 | (and have-setup-py 'setup.py) |
| 344 | (if tests-found 'unittest #f)))) | 346 | (and tests-found 'unittest)))) |
| 345 | (format #t "Using ~a~%" use-test-backend) | 347 | (format #t "Using ~a~%" use-test-backend) |
| 346 | (match use-test-backend | 348 | (match use-test-backend |
| 349 | ('pytest-with-guix-plugin | ||
| 350 | (apply invoke pytest "-vv" "-p" "pytest_guix" test-flags)) | ||
| 347 | ('pytest | 351 | ('pytest |
| 348 | (apply invoke pytest "-vv" test-flags)) | 352 | (apply invoke pytest "-vv" test-flags)) |
| 349 | ('nose | 353 | ('nose |
