Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python: lots of linting/pytest/tox fixes #19054

Merged
merged 15 commits into from
Jul 4, 2023
Merged

python: lots of linting/pytest/tox fixes #19054

merged 15 commits into from
Jul 4, 2023

Conversation

allisonkarlitskaya
Copy link
Member

  • make pytest work on Python 3.6
  • add mypy to test/static-code and run it also on test/pytest
  • drop flake8, use pycodestyle for missing ruff rules
  • improve tox configuration

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

test/static-code Outdated
Comment on lines 38 to 39
command -v pycodestyle >/dev/null || skip 'no pycodestyle'
find_python_files | xargs -r -0 pycodestyle --select=E1,E2,E3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not that happy about this. Having .flake8 had the big advantage that vim ALE and friends used the same configuration as the tests. pycodestyle does not have such a facility.

Could this work with opting into that subset of rules with flake8 instead? Then we also won't need to change the dependency again in all our projects, tasks container, etc. There's nothing wrong with flake8 per-se, is it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: flake8 implements the rules in question internally by using pycodestyle. The flake8 package depends on pycodestyle (and a whole lot of other stuff), so if you have flake8 you already have pycodestyle.

We could also add a setup.cfg with the pycodestyle config in it...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also add a setup.cfg with the pycodestyle config in it...

That'd be great! I'm aware of the flake8 → pycodestyle dependency, I'm just worried about a more declarative config.

test/static-code Show resolved Hide resolved
src/cockpit/superuser.py Show resolved Hide resolved
pyproject.toml Show resolved Hide resolved
pyproject.toml Show resolved Hide resolved
runs-on: ubuntu-latest
permissions: {}
container:
image: registry.fedoraproject.org/fedora:38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm -- :latest ? I know it's more prone to "spontaneous" failures when Fedora updates, but (1) venv already isolates most of that, and (2) we won't forget to actually keep it up to date.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Happy to let this fail on us in the future if something changes, rather than forgetting that it exists.

@allisonkarlitskaya allisonkarlitskaya force-pushed the pycodestyle branch 4 times, most recently from 502dfed to f6948a7 Compare July 3, 2023 18:00
Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks good by and large, just wrangling with the container now.

test/pytest/conftest.py Outdated Show resolved Hide resolved
runs-on: ubuntu-latest
permissions: {}
container:
image: quay.io/cockpit/tasks:latest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rather use the unittests container? I'm a bit hesitant to pull tox into the tasks container, as it's so annoyingly big:

total download size: 147 M
Installed size: 721 M

This also caused the failure.

if you move it into the unit-tests container, then the whole workflow should move to unit-tests.yaml as a new job, and also into unit-tests-refresh.yaml. FTR, I'm also fine with your previous approach of just apt installing it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for pkg in systemd_ctypes ferny bei; do
test -e "src/cockpit/_vendor/${pkg}/__init__.py" || skip "no ${pkg}"
done
mypy --no-error-summary src/cockpit test/pytest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we disable mypy caching iirc that gave some issues when running it separate, or is that solved or do we already do that in pyproject.toml

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue was being caused by an oddity in systemd_ctypes. That's resolved now. I don't see any reason to keep the cache disabled, since it saves quite a bit of time...

isolated_build = True
envlist = site
labels =
venv = py311-lint, py3{6,7,8,9,10,11,12}-pytest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay for testing on 3.12! \o/ Ideally we should remember to automatically add 3.13 etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is possible, unfortunately :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spend a few minutes researching this and it seems that there indeed isn't a "py-latest".

@allisonkarlitskaya allisonkarlitskaya force-pushed the pycodestyle branch 4 times, most recently from 68066b4 to a946072 Compare July 4, 2023 10:31
@allisonkarlitskaya allisonkarlitskaya added the no-test For doc/workflow changes, or experiments which don't need a full CI run, label Jul 4, 2023
@allisonkarlitskaya allisonkarlitskaya force-pushed the pycodestyle branch 2 times, most recently from b8abf45 to a0d0cac Compare July 4, 2023 11:09
Our current flake8 config overlaps our ruff config.  Since we want to
slowly move away from flake8, enable only the pycodestyle rules that we
don't get from ruff.

Once ruff implements the outstanding rules, we can drop flake8.
I'm not sure why I ever used this name, but the tool is called
"vulture".
We're actually really close to having working mypy including the unit
tests in test/pytest.

The biggest change here is that we stopped using the return value of
Peer.do_connect_transport() a while ago.  Properly change that to be
`None` everywhere.
We might bring this back some day in a different form, but linting
definitely doesn't belong in the same directory as the unit tests.
We only use this from one place anymore, so let's just open-code it
instead.

This has a side-effect of unbreaking some unit tests on older Python
versions.
This was our last remaining user of unittest.TestCase in test/pytest/.
The unittest.IsolatedAsyncioTestCase API was added in Python 3.8, which
clears one more hurdle for running unit tests against older Python
versions.

We continue to use unittest.mock instead of pytest-mock: it does what we
need, causes us no problems, and is available in the standard library
(instead of being an extra dependency that we'd need to install).
This feature isn't in any version of pytest-asyncio available for Python
3.6, so drop it from our config for now and add explicit decorators in
the places where they were missing.
Having a side-effecting statement like this at the top-level of a random
test file is very strange.

pytest-asyncio gives us the ability to override the `event_loop`
fixture, which not only gives us the ability to return a custom type of
loop, but also essentially gives us the ability to write a co-routine
automatically wrapping each individual async test case.

That's powerful: we can manually create the loop without messing with
global state, but we can also take this as an opportunity to do some of
the same polyfills that systemd_ctypes.async_run() would do (which is
even easier, given the `monkeypatch` fixture).

We also take the chance to ensure that our code for tracking task and
process leaks runs on every async testcase, instead of relying on our
current ad-hoc approach of trying to remember to do it.  This finds two
process leaks (of bash) in the subprocess tests, so fix those up.
The tests are now running with all Python versions since 3.7.
It's now possible to run the test/pytest unittests under Python 3.6.
Copy the same sort of tox setup that systemd_ctypes is using, with some
minor adjustments to testing dependencies and the like.

The biggest change here is that instead of running each linter tool as a
separate command, we run them all together via test/static-code.  The
main reason for that is that we have a lot of stand-alone executable
Python scripts (chmod +x, and not ending in .py) that we also want to
lint, and the linters don't know how to find those on their own.

As in systemd_ctypes, typing `tox p` now gives a fairly nice experience.

Note also: it's now possible to do a linter run with tools installed
from PyPI using something like:

    tox -e py311-lint

It's also possible to run pytest with older version of Python:

    pytest -e py36-pytest

It's also possible to run all of the venv-based environments like:

    tox p -m venv
@martinpitt martinpitt added the .github-changes Set by a reviewer just before landing to acknowledge that a PR changes github workflows label Jul 4, 2023
martinpitt
martinpitt previously approved these changes Jul 4, 2023
Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! The 3.6 compatibility test is really nice, and it allows us to drop our .github/workflows/pybridge-c8s.yml workflow (that can be a follow-up). I suppose we still can't run them in RHEL package builds due to missing python module dependencies, though?

test/pytest/conftest.py Outdated Show resolved Hide resolved
uses: actions/checkout@v3

- name: Download toxbox
run: podman pull ghcr.io/allisonkarlitskaya/toxbox
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that container definition ought to be in cockpit.git containers/, or just be built on the fly. So that it can co-evolve, and not spontaneously break. But it's straightforward enough, and we can change it later if it causes problems.

Improve our code for detecting and displaying leaked processes.

The display is improved by showing `ps f` output instead of pgrep
output.  That makes it easier to see the relationships of the processes
as well as process state (zombie, etc.)

Example output looks like this:

    test/pytest/conftest.py:48: Failed
    --------------------- Captured stdout teardown ---------------------
        PID TTY      STAT   TIME COMMAND
    1477658 pts/7    S+     0:00 /usr/bin/python3 -P /usr/bin/pytest
    1477677 ?        Zs     0:00  \_ [bash] <defunct>
    1477799 pts/7    R+     0:00  \_ ps f --pid=1477658 --ppid=1477658
    ===================== short test summary info ======================
    ERROR test/pytest/test_transport.py::TestSubprocessTransport::test_window_size - Failed: Some subprocesses still running!

The detection is improved by using waitid(WNOWAIT) in order to detect
leaked processes, instead of calling pgrep.  This is more efficient, and
avoids side-effects caused by spawning more subprocesses (ie: extra
SIGCHLD handling).  This found a bug, which we fix here.

It also means that in the "working" case, we don't depend on external
tools like `pgrep` or `ps` (although we use them in case of failures).
If the modules are already checked out, don't attempt to update them
again.  This doesn't work if the directory is read-only.
Run all of our python tests in all possible venvs on each PR.
@github-actions github-actions bot removed the .github-changes Set by a reviewer just before landing to acknowledge that a PR changes github workflows label Jul 4, 2023
@allisonkarlitskaya
Copy link
Member Author

Thanks! The 3.6 compatibility test is really nice, and it allows us to drop our .github/workflows/pybridge-c8s.yml workflow (that can be a follow-up). I suppose we still can't run them in RHEL package builds due to missing python module dependencies, though?

I see nothing that would stop us from running this on RHEL, at least in a venv with the testing dependencies from PyPI...

@martinpitt martinpitt added the .github-changes Set by a reviewer just before landing to acknowledge that a PR changes github workflows label Jul 4, 2023
Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@allisonkarlitskaya allisonkarlitskaya merged commit e830319 into main Jul 4, 2023
31 of 32 checks passed
@allisonkarlitskaya allisonkarlitskaya deleted the pycodestyle branch July 4, 2023 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
.github-changes Set by a reviewer just before landing to acknowledge that a PR changes github workflows no-test For doc/workflow changes, or experiments which don't need a full CI run,
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants