diff mbox series

[v2,11/15] iotests: split linters.py out from 297

Message ID 20211019144918.3159078-12-jsnow@redhat.com
State New
Headers show
Series python/iotests: Run iotest linters during Python CI | expand

Commit Message

John Snow Oct. 19, 2021, 2:49 p.m. UTC
Now, 297 is just the iotests-specific incantations and linters.py is as
minimal as I can think to make it. The only remaining element in here
that ought to be configuration and not code is the list of skip files,
but they're still numerous enough that repeating them for mypy and
pylint configurations both would be ... a hassle.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 tests/qemu-iotests/297        | 72 +++++----------------------------
 tests/qemu-iotests/linters.py | 76 +++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+), 61 deletions(-)
 create mode 100644 tests/qemu-iotests/linters.py

Comments

Hanna Czenczek Oct. 26, 2021, 10:51 a.m. UTC | #1
On 19.10.21 16:49, John Snow wrote:
> Now, 297 is just the iotests-specific incantations and linters.py is as
> minimal as I can think to make it. The only remaining element in here
> that ought to be configuration and not code is the list of skip files,
> but they're still numerous enough that repeating them for mypy and
> pylint configurations both would be ... a hassle.
>
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>   tests/qemu-iotests/297        | 72 +++++----------------------------
>   tests/qemu-iotests/linters.py | 76 +++++++++++++++++++++++++++++++++++
>   2 files changed, 87 insertions(+), 61 deletions(-)
>   create mode 100644 tests/qemu-iotests/linters.py

Reviewed-by: Hanna Reitz <hreitz@redhat.com>

I wonder about `check_linter()`, though.  By not moving it to 
linters.py, we can’t use it in its entry point, and so the Python test 
infrastructure will have a strong dependency on these linters. Though 
then again, it probably already does, and I suppose that’s one of the 
points hindering us from running this from make check?

Hanna
John Snow Oct. 26, 2021, 6:30 p.m. UTC | #2
On Tue, Oct 26, 2021 at 6:51 AM Hanna Reitz <hreitz@redhat.com> wrote:

> On 19.10.21 16:49, John Snow wrote:
> > Now, 297 is just the iotests-specific incantations and linters.py is as
> > minimal as I can think to make it. The only remaining element in here
> > that ought to be configuration and not code is the list of skip files,
> > but they're still numerous enough that repeating them for mypy and
> > pylint configurations both would be ... a hassle.
> >
> > Signed-off-by: John Snow <jsnow@redhat.com>
> > ---
> >   tests/qemu-iotests/297        | 72 +++++----------------------------
> >   tests/qemu-iotests/linters.py | 76 +++++++++++++++++++++++++++++++++++
> >   2 files changed, 87 insertions(+), 61 deletions(-)
> >   create mode 100644 tests/qemu-iotests/linters.py
>
> Reviewed-by: Hanna Reitz <hreitz@redhat.com>
>
>
Thanks!


> I wonder about `check_linter()`, though.  By not moving it to
> linters.py, we can’t use it in its entry point, and so the Python test
> infrastructure will have a strong dependency on these linters. Though
> then again, it probably already does, and I suppose that’s one of the
> points hindering us from running this from make check?
>
>
That one is left behind because it uses iotests API to skip a test.
Environment setup that guarantees we won't *need* to skip the test is
handled by the virtual environment setup magic in qemu/python/Makefile.


> Hanna
>

More info than you require:

There's maybe about four ways you could run the python tests that might
make sense depending on who you are and what you're trying to accomplish;
they're documented in "make help" and again in qemu/python/README.rst;

(1) make check-dev -- makes a venv with whatever python you happen to have,
installs the latest packages, runs the tests
(2) make check-pipenv -- requires python 3.6 specifically, installs the
*oldest* packages, runs the tests
(3) make check-tox -- requires python 3.6 through 3.10, installs the newest
packages, runs the tests per each python version
(4) make check -- perform no setup at all, just run the tests in the
current environment. (Used directly by all three prior invocations)

In general, I assume that human beings that aren't working on Python stuff
actively will be using (1) at their interactive console, if they decide to
run any of these at all. It imposes the least pre-requirements while still
endeavoring to be a target that will "just work".
Options (2) and (3) are what power the CI tests 'check-python-pipenv' and
'check-python-tox', respectively; with(2) being the one that actually gates
the CI.
Option (4) is only really a convenience for bypassing the venv-setup stuff
if you want to construct your own (or not use one at all). So the tests
just assume that the tools they have available will work. It's kind of a
'power user' target.

The way the CI works at the moment is that it uses a "fedora:latest" base
image and installs python interpreters 3.6 through 3.10 inclusive, pipenv,
and tox. From there, it has enough juice to run the makefile targets which
take care of all of the actual linting dependencies and their different
versions to get a wider matrix on the version testing to ensure we're still
keeping compatibility with the 3.6 platform while also checking for new
problems that show up on the bleeding edge.

iotests 297 right now doesn't do any python environment setup at all, so we
can't guarantee that the linters are present, so we decide to allow the
test to be skipped. My big hesitation of adding this test directly into
'make check' is that I will need to do some environment probing to make
sure that the 'pylint' version isn't too old -- or otherwise set up a venv
in the build directory that can be used to run tests. I know we already set
one up for the acceptance tests, so maybe there's an opportunity to re-use
that venv, but I need to make sure that the dependencies between the two
sets of tests are aligned. I don't know if they agree, currently.

I think I probably want to minimize the number of different virtual python
environments we create during the build, so I will look into what problems
might exist in re-purposing the acceptance test venv. If that can get
squared away easily, there's likely no real big barrier to adding more
tests that rely on a python venv to get cooking during the normal
build/check process, including iotest 297.

--js
Hanna Czenczek Oct. 28, 2021, 10:34 a.m. UTC | #3
On 26.10.21 20:30, John Snow wrote:
>
>
> On Tue, Oct 26, 2021 at 6:51 AM Hanna Reitz <hreitz@redhat.com> wrote:
>
>     On 19.10.21 16:49, John Snow wrote:
>     > Now, 297 is just the iotests-specific incantations and
>     linters.py is as
>     > minimal as I can think to make it. The only remaining element in
>     here
>     > that ought to be configuration and not code is the list of skip
>     files,
>     > but they're still numerous enough that repeating them for mypy and
>     > pylint configurations both would be ... a hassle.
>     >
>     > Signed-off-by: John Snow <jsnow@redhat.com>
>     > ---
>     >   tests/qemu-iotests/297        | 72
>     +++++----------------------------
>     >   tests/qemu-iotests/linters.py | 76
>     +++++++++++++++++++++++++++++++++++
>     >   2 files changed, 87 insertions(+), 61 deletions(-)
>     >   create mode 100644 tests/qemu-iotests/linters.py
>
>     Reviewed-by: Hanna Reitz <hreitz@redhat.com>
>
>
> Thanks!
>
>     I wonder about `check_linter()`, though.  By not moving it to
>     linters.py, we can’t use it in its entry point, and so the Python
>     test
>     infrastructure will have a strong dependency on these linters. Though
>     then again, it probably already does, and I suppose that’s one of the
>     points hindering us from running this from make check?
>
>
> That one is left behind because it uses iotests API to skip a test. 
> Environment setup that guarantees we won't *need* to skip the test is 
> handled by the virtual environment setup magic in qemu/python/Makefile.
>
>     Hanna
>
>
> More info than you require:
>
> There's maybe about four ways you could run the python tests that 
> might make sense depending on who you are and what you're trying to 
> accomplish; they're documented in "make help" and again in 
> qemu/python/README.rst;
>
> (1) make check-dev -- makes a venv with whatever python you happen to 
> have, installs the latest packages, runs the tests
> (2) make check-pipenv -- requires python 3.6 specifically, installs 
> the *oldest* packages, runs the tests
> (3) make check-tox -- requires python 3.6 through 3.10, installs the 
> newest packages, runs the tests per each python version
> (4) make check -- perform no setup at all, just run the tests in the 
> current environment. (Used directly by all three prior invocations)

AFAIU these are all to be run in build/python?  Isn’t any of them hooked 
up to the global `make check` in build?  Because...

> In general, I assume that human beings that aren't working on Python 
> stuff actively will be using (1) at their interactive console, if they 
> decide to run any of these at all.

...I’m just running `make check` in the build directory.

I would have hoped that this is hooked up to something that won’t fail 
because I don’t have some necessary tools installed or perhaps even 
because I have the wrong version of some tools installed (although the 
latter would be kind of questionable...).  Would be nice if the global 
`make check` had a summary on what was skipped...

> It imposes the least pre-requirements while still endeavoring to be a 
> target that will "just work".
> Options (2) and (3) are what power the CI tests 'check-python-pipenv' 
> and 'check-python-tox', respectively; with(2) being the one that 
> actually gates the CI.
> Option (4) is only really a convenience for bypassing the venv-setup 
> stuff if you want to construct your own (or not use one at all). So 
> the tests just assume that the tools they have available will work. 
> It's kind of a 'power user' target.
>
> The way the CI works at the moment is that it uses a "fedora:latest" 
> base image and installs python interpreters 3.6 through 3.10 
> inclusive, pipenv, and tox. From there, it has enough juice to run the 
> makefile targets which take care of all of the actual linting 
> dependencies and their different versions to get a wider matrix on the 
> version testing to ensure we're still keeping compatibility with the 
> 3.6 platform while also checking for new problems that show up on the 
> bleeding edge.

Perhaps it’s unreasonable, but I’d prefer if a local `make check` would 
already run most tests in some form or another and that I don’t have to 
push to gitlab and wait for the CI there.

I mean, I can of course also integrate a `cd python && make check-dev` 
invocation into my test scripts, but it doesn’t feel right to 
special-case one test area.

> iotests 297 right now doesn't do any python environment setup at all, 
> so we can't guarantee that the linters are present, so we decide to 
> allow the test to be skipped. My big hesitation of adding this test 
> directly into 'make check' is that I will need to do some environment 
> probing to make sure that the 'pylint' version isn't too old -- or 
> otherwise set up a venv in the build directory that can be used to run 
> tests. I know we already set one up for the acceptance tests, so maybe 
> there's an opportunity to re-use that venv, but I need to make sure 
> that the dependencies between the two sets of tests are aligned. I 
> don't know if they agree, currently.

I see.

> I think I probably want to minimize the number of different virtual 
> python environments we create during the build, so I will look into 
> what problems might exist in re-purposing the acceptance test venv. If 
> that can get squared away easily, there's likely no real big barrier 
> to adding more tests that rely on a python venv to get cooking during 
> the normal build/check process, including iotest 297.

OK, thanks for the explanation!

Hanna
John Snow Oct. 28, 2021, 4:27 p.m. UTC | #4
On Thu, Oct 28, 2021, 6:34 AM Hanna Reitz <hreitz@redhat.com> wrote:

> On 26.10.21 20:30, John Snow wrote:
> >
> >
> > On Tue, Oct 26, 2021 at 6:51 AM Hanna Reitz <hreitz@redhat.com> wrote:
> >
> >     On 19.10.21 16:49, John Snow wrote:
> >     > Now, 297 is just the iotests-specific incantations and
> >     linters.py is as
> >     > minimal as I can think to make it. The only remaining element in
> >     here
> >     > that ought to be configuration and not code is the list of skip
> >     files,
> >     > but they're still numerous enough that repeating them for mypy and
> >     > pylint configurations both would be ... a hassle.
> >     >
> >     > Signed-off-by: John Snow <jsnow@redhat.com>
> >     > ---
> >     >   tests/qemu-iotests/297        | 72
> >     +++++----------------------------
> >     >   tests/qemu-iotests/linters.py | 76
> >     +++++++++++++++++++++++++++++++++++
> >     >   2 files changed, 87 insertions(+), 61 deletions(-)
> >     >   create mode 100644 tests/qemu-iotests/linters.py
> >
> >     Reviewed-by: Hanna Reitz <hreitz@redhat.com>
> >
> >
> > Thanks!
> >
> >     I wonder about `check_linter()`, though.  By not moving it to
> >     linters.py, we can’t use it in its entry point, and so the Python
> >     test
> >     infrastructure will have a strong dependency on these linters. Though
> >     then again, it probably already does, and I suppose that’s one of the
> >     points hindering us from running this from make check?
> >
> >
> > That one is left behind because it uses iotests API to skip a test.
> > Environment setup that guarantees we won't *need* to skip the test is
> > handled by the virtual environment setup magic in qemu/python/Makefile.
> >
> >     Hanna
> >
> >
> > More info than you require:
> >
> > There's maybe about four ways you could run the python tests that
> > might make sense depending on who you are and what you're trying to
> > accomplish; they're documented in "make help" and again in
> > qemu/python/README.rst;
> >
> > (1) make check-dev -- makes a venv with whatever python you happen to
> > have, installs the latest packages, runs the tests
> > (2) make check-pipenv -- requires python 3.6 specifically, installs
> > the *oldest* packages, runs the tests
> > (3) make check-tox -- requires python 3.6 through 3.10, installs the
> > newest packages, runs the tests per each python version
> > (4) make check -- perform no setup at all, just run the tests in the
> > current environment. (Used directly by all three prior invocations)
>
> AFAIU these are all to be run in build/python?  Isn’t any of them hooked
> up to the global `make check` in build?  Because...
>

None of them are on make check, correct. Not yet. I'll try to make that
happen soon to drop 297.

They run out of the source tree directly, since they're checks on the
source and aren't actually related to a build of QEMU in any way.

(Y'know, yet. I'm not saying never.)


> In general, I assume that human beings that aren't working on Python
> > stuff actively will be using (1) at their interactive console, if they
> > decide to run any of these at all.
>
> ...I’m just running `make check` in the build directory.
>

Yeah, that's OK. I mean, I don't expect you to run them unless you were
submitting a series to specifically me.

("If they decide to run any of these at all" - I'm aware that very few
people would or are doing so. I consider the CI to be mostly for me as the
maintainer to make sure nothing regressed.)


> I would have hoped that this is hooked up to something that won’t fail
> because I don’t have some necessary tools installed or perhaps even
> because I have the wrong version of some tools installed (although the
> latter would be kind of questionable...).  Would be nice if the global
> `make check` had a summary on what was skipped...
>


I mean. These targets shouldn't fail unless you're missing some really
basic requirements. They're just not hooked in to the big "make check" yet.

In terms of environment probing and skipping the tests, though, I do need
another layer somewhere to manage that. Stuff I'll need to put in
configure/meson etc. I have to look into it.


> > It imposes the least pre-requirements while still endeavoring to be a
> > target that will "just work".
> > Options (2) and (3) are what power the CI tests 'check-python-pipenv'
> > and 'check-python-tox', respectively; with(2) being the one that
> > actually gates the CI.
> > Option (4) is only really a convenience for bypassing the venv-setup
> > stuff if you want to construct your own (or not use one at all). So
> > the tests just assume that the tools they have available will work.
> > It's kind of a 'power user' target.
> >
> > The way the CI works at the moment is that it uses a "fedora:latest"
> > base image and installs python interpreters 3.6 through 3.10
> > inclusive, pipenv, and tox. From there, it has enough juice to run the
> > makefile targets which take care of all of the actual linting
> > dependencies and their different versions to get a wider matrix on the
> > version testing to ensure we're still keeping compatibility with the
> > 3.6 platform while also checking for new problems that show up on the
> > bleeding edge.
>
> Perhaps it’s unreasonable, but I’d prefer if a local `make check` would
> already run most tests in some form or another and that I don’t have to
> push to gitlab and wait for the CI there.
>

Yep, understand. That's a requirement for me as well in order to drop 297.
On the list, I promise.


> I mean, I can of course also integrate a `cd python && make check-dev`
> invocation into my test scripts, but it doesn’t feel right to
> special-case one test area.
>

Don't worry, I don't expect that. It just took a lot of work to standardize
even that much of the test, so I went for the smaller thing instead of the
perfect thing. I'm still inching along to the perfect thing, but considered
the iotest cleanups I've done a requisite on that path.



> > iotests 297 right now doesn't do any python environment setup at all,
> > so we can't guarantee that the linters are present, so we decide to
> > allow the test to be skipped. My big hesitation of adding this test
> > directly into 'make check' is that I will need to do some environment
> > probing to make sure that the 'pylint' version isn't too old -- or
> > otherwise set up a venv in the build directory that can be used to run
> > tests. I know we already set one up for the acceptance tests, so maybe
> > there's an opportunity to re-use that venv, but I need to make sure
> > that the dependencies between the two sets of tests are aligned. I
> > don't know if they agree, currently.
>
> I see.
>
> > I think I probably want to minimize the number of different virtual
> > python environments we create during the build, so I will look into
> > what problems might exist in re-purposing the acceptance test venv. If
> > that can get squared away easily, there's likely no real big barrier
> > to adding more tests that rely on a python venv to get cooking during
> > the normal build/check process, including iotest 297.
>
> OK, thanks for the explanation!
>
> Hanna
>

Yep. I'll start trying to integrate this into make check and see where the
problems are. It should be safe to do during soft freeze, I think, since
it's just testing ...
diff mbox series

Patch

diff --git a/tests/qemu-iotests/297 b/tests/qemu-iotests/297
index b7d9d6077b3..ee78a627359 100755
--- a/tests/qemu-iotests/297
+++ b/tests/qemu-iotests/297
@@ -17,74 +17,24 @@ 
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
-import re
 import subprocess
 import sys
-from typing import List, Mapping, Optional
+from typing import List
 
 import iotests
+import linters
 
 
-# TODO: Empty this list!
-SKIP_FILES = (
-    '030', '040', '041', '044', '045', '055', '056', '057', '065', '093',
-    '096', '118', '124', '132', '136', '139', '147', '148', '149',
-    '151', '152', '155', '163', '165', '194', '196', '202',
-    '203', '205', '206', '207', '208', '210', '211', '212', '213', '216',
-    '218', '219', '224', '228', '234', '235', '236', '237', '238',
-    '240', '242', '245', '246', '248', '255', '256', '257', '258', '260',
-    '262', '264', '266', '274', '277', '280', '281', '295', '296', '298',
-    '299', '302', '303', '304', '307',
-    'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py'
-)
-
-
-def is_python_file(filename):
-    if not os.path.isfile(filename):
-        return False
-
-    if filename.endswith('.py'):
-        return True
-
-    with open(filename, encoding='utf-8') as f:
-        try:
-            first_line = f.readline()
-            return re.match('^#!.*python', first_line) is not None
-        except UnicodeDecodeError:  # Ignore binary files
-            return False
-
-
-def get_test_files() -> List[str]:
-    named_tests = [f'tests/{entry}' for entry in os.listdir('tests')]
-    check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES)
-    return list(filter(is_python_file, check_tests))
-
-
-def run_linter(
-        tool: str,
-        args: List[str],
-        env: Optional[Mapping[str, str]] = None,
-        suppress_output: bool = False,
-) -> None:
-    """
-    Run a python-based linting tool.
-
-    :param suppress_output: If True, suppress all stdout/stderr output.
-    :raise CalledProcessError: If the linter process exits with failure.
-    """
-    subprocess.run(
-        ('python3', '-m', tool, *args),
-        env=env,
-        check=True,
-        stdout=subprocess.PIPE if suppress_output else None,
-        stderr=subprocess.STDOUT if suppress_output else None,
-        universal_newlines=True,
-    )
+# Looking for something?
+#
+#  List of files to exclude from linting: linters.py
+#  mypy configuration:                    mypy.ini
+#  pylint configuration:                  pylintrc
 
 
 def check_linter(linter: str) -> bool:
     try:
-        run_linter(linter, ['--version'], suppress_output=True)
+        linters.run_linter(linter, ['--version'], suppress_output=True)
     except subprocess.CalledProcessError:
         iotests.case_notrun(f"'{linter}' not found")
         return False
@@ -98,7 +48,7 @@  def test_pylint(files: List[str]) -> None:
     if not check_linter('pylint'):
         return
 
-    run_linter('pylint', files)
+    linters.run_linter('pylint', files)
 
 
 def test_mypy(files: List[str]) -> None:
@@ -111,11 +61,11 @@  def test_mypy(files: List[str]) -> None:
     env = os.environ.copy()
     env['MYPYPATH'] = env['PYTHONPATH']
 
-    run_linter('mypy', files, env=env, suppress_output=True)
+    linters.run_linter('mypy', files, env=env, suppress_output=True)
 
 
 def main() -> None:
-    files = get_test_files()
+    files = linters.get_test_files()
 
     iotests.logger.debug('Files to be checked:')
     iotests.logger.debug(', '.join(sorted(files)))
diff --git a/tests/qemu-iotests/linters.py b/tests/qemu-iotests/linters.py
new file mode 100644
index 00000000000..c515c7afe36
--- /dev/null
+++ b/tests/qemu-iotests/linters.py
@@ -0,0 +1,76 @@ 
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import re
+import subprocess
+from typing import List, Mapping, Optional
+
+
+# TODO: Empty this list!
+SKIP_FILES = (
+    '030', '040', '041', '044', '045', '055', '056', '057', '065', '093',
+    '096', '118', '124', '132', '136', '139', '147', '148', '149',
+    '151', '152', '155', '163', '165', '194', '196', '202',
+    '203', '205', '206', '207', '208', '210', '211', '212', '213', '216',
+    '218', '219', '224', '228', '234', '235', '236', '237', '238',
+    '240', '242', '245', '246', '248', '255', '256', '257', '258', '260',
+    '262', '264', '266', '274', '277', '280', '281', '295', '296', '298',
+    '299', '302', '303', '304', '307',
+    'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py'
+)
+
+
+def is_python_file(filename):
+    if not os.path.isfile(filename):
+        return False
+
+    if filename.endswith('.py'):
+        return True
+
+    with open(filename, encoding='utf-8') as f:
+        try:
+            first_line = f.readline()
+            return re.match('^#!.*python', first_line) is not None
+        except UnicodeDecodeError:  # Ignore binary files
+            return False
+
+
+def get_test_files() -> List[str]:
+    named_tests = [f'tests/{entry}' for entry in os.listdir('tests')]
+    check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES)
+    return list(filter(is_python_file, check_tests))
+
+
+def run_linter(
+        tool: str,
+        args: List[str],
+        env: Optional[Mapping[str, str]] = None,
+        suppress_output: bool = False,
+) -> None:
+    """
+    Run a python-based linting tool.
+
+    :param suppress_output: If True, suppress all stdout/stderr output.
+    :raise CalledProcessError: If the linter process exits with failure.
+    """
+    subprocess.run(
+        ('python3', '-m', tool, *args),
+        env=env,
+        check=True,
+        stdout=subprocess.PIPE if suppress_output else None,
+        stderr=subprocess.STDOUT if suppress_output else None,
+        universal_newlines=True,
+    )