diff mbox series

[bug#70570,2/2] guix: pyproject-build-system: Ignore unwanted pytest flags.

Message ID 20240425160010.6243-2-ngraves@ngraves.fr
State New
Headers show
Series Python: Ignore unwanted development inputs. | expand

Commit Message

Nicolas Graves April 25, 2024, 3:59 p.m. UTC
* guix/build/pyproject-build-system.scm : Ignore unwanted pytest flags.

Change-Id: Ib9f1602e5af11227e5b7ce124f0f9be4fa2b78e4
---
 guix/build/pyproject-build-system.scm | 88 ++++++++++++++++++++++++++-
 1 file changed, 86 insertions(+), 2 deletions(-)

Comments

Lars-Dominik Braun April 26, 2024, 8:47 a.m. UTC | #1
Hi,

pretty smart idea to use a pytest plugin :)

> +;; Pytest plugin to filter out arguments to ignore.
> +(define pytest-default-ignore-alist

From the comment it’s not entirely clear to me what this list
does. It’s a map from pytest plugin name to it’s pytest command line
options, right?

> +;; Allow guix to ignore these options when underlying pytest package is not
> +;; an input. These flags are not necessary to properly run tests.
> +(define (pytest-ignore-options-plugin flags)
> +  "This function converts an list of flags in a string that can
> +  be instantiated as a python pytest plugin."> +(define (call-with-guix-pytest-plugin inputs thunk)

Same here. As far as I understand you want to emulate command line
options provided by pytest plugins, so pytest won’t fail if the plugin
is not present. And we only do that if the plugin is not an input to
avoid clashing command line options, right?

+    for option in options:
+        group.addoption(option, action='append', nargs='*')"

Not sure nargs='*' is a good idea, since it might consume positional
arguments intended for pytest. '?' would be a more conservative option,
especially since we cannot override this easily per-package.

> +  (let* ((former-path (getenv "PYTHONPATH"))> +    (dynamic-wind
> +      (lambda ()
> +        (setenv "PYTHONPATH"> +      (lambda ()
> +        (setenv "PYTHONPATH" former-path)

Isn’t it GUIX_PYTHONPATH?

Lars
Nicolas Graves April 26, 2024, 10:14 a.m. UTC | #2
On 2024-04-26 10:47, Lars-Dominik Braun wrote:

> Hi,
>
> pretty smart idea to use a pytest plugin :)
>
>> +;; Pytest plugin to filter out arguments to ignore.
>> +(define pytest-default-ignore-alist
>
> From the comment it’s not entirely clear to me what this list
> does. It’s a map from pytest plugin name to it’s pytest command line
> options, right?

Right, I'll update the command.

>
>> +;; Allow guix to ignore these options when underlying pytest package is not
>> +;; an input. These flags are not necessary to properly run tests.
>> +(define (pytest-ignore-options-plugin flags)
>> +  "This function converts an list of flags in a string that can
>> +  be instantiated as a python pytest plugin."
> …
>> +(define (call-with-guix-pytest-plugin inputs thunk)
>
> Same here. As far as I understand you want to emulate command line
> options provided by pytest plugins, so pytest won’t fail if the plugin
> is not present. And we only do that if the plugin is not an input to
> avoid clashing command line options, right?

Will do.
>
> +    for option in options:
> +        group.addoption(option, action='append', nargs='*')"
>
> Not sure nargs='*' is a good idea, since it might consume positional
> arguments intended for pytest. '?' would be a more conservative option,
> especially since we cannot override this easily per-package.

It works. I'll try with '?' then!

>
>> +  (let* ((former-path (getenv "PYTHONPATH"))
> …
>> +    (dynamic-wind
>> +      (lambda ()
>> +        (setenv "PYTHONPATH"
> …
>> +      (lambda ()
>> +        (setenv "PYTHONPATH" former-path)
>
> Isn’t it GUIX_PYTHONPATH?
>
 
I know it works this way. Could be GUIX_PYTHONPATH too, I'm not sure I
properly get the difference. Can test with GUIX_PYTHONPATH too, but in
any case it's just for the tests, so it should be cleared out
too. Should I?

> Lars
>
Nicolas Graves April 27, 2024, 4:09 p.m. UTC | #3
On 2024-04-26 12:14, Nicolas Graves wrote:

> On 2024-04-26 10:47, Lars-Dominik Braun wrote:
>
>> Hi,
>>
>> pretty smart idea to use a pytest plugin :)
>>
>>> +;; Pytest plugin to filter out arguments to ignore.
>>> +(define pytest-default-ignore-alist
>>
>> From the comment it’s not entirely clear to me what this list
>> does. It’s a map from pytest plugin name to it’s pytest command line
>> options, right?
>
> Right, I'll update the command.
>
>>
>>> +;; Allow guix to ignore these options when underlying pytest package is not
>>> +;; an input. These flags are not necessary to properly run tests.
>>> +(define (pytest-ignore-options-plugin flags)
>>> +  "This function converts an list of flags in a string that can
>>> +  be instantiated as a python pytest plugin."
>> …
>>> +(define (call-with-guix-pytest-plugin inputs thunk)
>>
>> Same here. As far as I understand you want to emulate command line
>> options provided by pytest plugins, so pytest won’t fail if the plugin
>> is not present. And we only do that if the plugin is not an input to
>> avoid clashing command line options, right?
>
> Will do.
>>
>> +    for option in options:
>> +        group.addoption(option, action='append', nargs='*')"
>>
>> Not sure nargs='*' is a good idea, since it might consume positional
>> arguments intended for pytest. '?' would be a more conservative option,
>> especially since we cannot override this easily per-package.
>
> It works. I'll try with '?' then!
>
>>
>>> +  (let* ((former-path (getenv "PYTHONPATH"))
>> …
>>> +    (dynamic-wind
>>> +      (lambda ()
>>> +        (setenv "PYTHONPATH"
>> …
>>> +      (lambda ()
>>> +        (setenv "PYTHONPATH" former-path)
>>
>> Isn’t it GUIX_PYTHONPATH?
>>
>  
> I know it works this way. Could be GUIX_PYTHONPATH too, I'm not sure I
> properly get the difference. Can test with GUIX_PYTHONPATH too, but in
> any case it's just for the tests, so it should be cleared out
> too. Should I?

Actually, it doesn't seem to work with a replacement from PYTHONPATH to
GUIX_PYTHONPATH, so I would rather keep it this way.

>
>> Lars
>>
diff mbox series

Patch

diff --git a/guix/build/pyproject-build-system.scm b/guix/build/pyproject-build-system.scm
index 947d240114..3ca3c76c1c 100644
--- a/guix/build/pyproject-build-system.scm
+++ b/guix/build/pyproject-build-system.scm
@@ -1,6 +1,7 @@ 
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2021 Lars-Dominik Braun <lars@6xq.net>
 ;;; Copyright © 2022 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2024 Nicolas Graves <ngraves@ngraves.fr>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -142,7 +143,86 @@  (define* (build #:key outputs build-backend backend-path configure-flags #:allow
      wheel-dir
      config-settings)))
 
-(define* (check #:key tests? test-backend test-flags #:allow-other-keys)
+;; Pytest plugin to filter out arguments to ignore.
+(define pytest-default-ignore-alist
+  '(("cov" . ("--cov" "--cov-reset" "--cov-report" "--cov-config"
+              "--no-cov-on-fail" "--no-cov" "--cov-fail-under"
+              "--cov-append" "--cov-branch" "--cov-context"))
+    ("mypy" . ("--mypy" "--mypy-config-file" "--mypy-ignore-missing-imports"))
+    ("isort" . ("--isort"))
+    ("flake8" . ("--flake8"))
+    ("black" . ("--black"))
+    ("flakes" . ("--flakes"))
+    ("pep8" . ("--pep8"))))
+
+;; Allow guix to ignore these options when underlying pytest package is not
+;; an input. These flags are not necessary to properly run tests.
+(define (pytest-ignore-options-plugin flags)
+  "This function converts an list of flags in a string that can
+  be instantiated as a python pytest plugin."
+  (format #f "\
+import pytest
+
+def pytest_addoption(parser):
+    group = parser.getgroup('guix','Guix ignored options')
+    options = [~{~s, ~}]
+    for option in options:
+        group.addoption(option, action='append', nargs='*')"
+          flags))
+
+(define (call-with-guix-pytest-plugin inputs thunk)
+  (let* ((former-path (getenv "PYTHONPATH"))
+         (input-names
+          (filter (match-lambda
+                    (((name . _) ...)
+                     (if (string-prefix? "python-pytest-" name)
+                         name
+                         #f))
+                    ( _ #f))
+                  inputs))
+         (filtered-flags
+          (filter identity
+                  (append-map
+                   (match-lambda
+                     ((group . flags)
+                      (if (member (string-append "python-pytest-" group)
+                                  input-names)
+                          (list #f)
+                          flags))
+                     (_ (list #f)))
+                   pytest-default-ignore-alist))))
+    (dynamic-wind
+      (lambda ()
+        (setenv "PYTHONPATH"
+                (string-append
+                 (if former-path
+                     (string-append former-path ":")
+                     "")
+                 ".guix-pytest"))
+        (setenv "PYTEST_PLUGINS"
+                (string-append
+                 (if (getenv "PYTEST_PLUGINS")
+                     (string-append former-path ",")
+                     "")
+                 "pytest_guix_plugin"))
+        (mkdir-p ".guix-pytest")
+        (with-output-to-file ".guix-pytest/__init__.py"
+          (lambda _ (display "")))
+        (with-output-to-file ".guix-pytest/pytest_guix_plugin.py"
+          (lambda _
+            (display (pytest-ignore-options-plugin filtered-flags)))))
+      thunk
+      (lambda ()
+        (setenv "PYTHONPATH" former-path)
+        (unsetenv "PYTEST_PLUGINS")
+        (when (file-exists? ".guix-pytest")
+          (delete-file-recursively ".guix-pytest"))))))
+
+(define-syntax-rule (with-guix-pytest-plugin inputs exp ...)
+  "Evaluate EXP in a context where the Guix pytest plugin is added."
+  (call-with-guix-pytest-plugin inputs (lambda () exp ...)))
+
+(define* (check #:key inputs tests? test-backend test-flags #:allow-other-keys)
   "Run the test suite of a given Python package."
   (if tests?
       ;; Unfortunately with PEP 517 there is no common method to specify test
@@ -165,7 +245,8 @@  (define* (check #:key tests? test-backend test-flags #:allow-other-keys)
         (format #t "Using ~a~%" use-test-backend)
         (match use-test-backend
           ('pytest
-           (apply invoke pytest "-vv" test-flags))
+           (with-guix-pytest-plugin inputs
+             (apply invoke pytest "-vv" test-flags)))
           ('nose
            (apply invoke nosetests "-v" test-flags))
           ('nose2
@@ -386,3 +467,6 @@  (define* (pyproject-build #:key inputs (phases %standard-phases)
   (apply python:python-build #:inputs inputs #:phases phases args))
 
 ;;; pyproject-build-system.scm ends here
+;;; Local Variables:
+;;; eval: (put 'with-guix-pytest-plugin 'scheme-indent-function 1)
+;;; End: