-
Notifications
You must be signed in to change notification settings - Fork 146
support running and debugging pytest
for local tree
#859
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
base: main
Are you sure you want to change the base?
support running and debugging pytest
for local tree
#859
Conversation
cb8f5da
to
95b5937
Compare
I'm not sure I like where this is going. Testing $ python -m venv .venv
$ source .venv/bin/activate
$ pip install -e . |
95b5937
to
131b1c3
Compare
Note: A few other tests invoke the hardcoded For consistency and robustness, these tests should be refactored so that pytest passes even when |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #859 +/- ##
==========================================
- Coverage 84.34% 84.32% -0.02%
==========================================
Files 11 11
Lines 3366 3369 +3
==========================================
+ Hits 2839 2841 +2
- Misses 527 528 +1
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend you check https://docs.pytest.org/en/7.1.x/explanation/goodpractices.html#tests-outside-application-code too.
Installation in editable mode does not work for me:
Am I doing something wrong?
In this documentation it says:
So this is exactly what I want to do. I want to run In many other Python open-source projects, it’s possible to run
If the installed west version matches, then the tests should test against this installed package. The main problem seems to lay in current tests that call Running
|
So what I do to test (requires an environment!): $ python3.10 -m venv .venv
$ source .venv/bin/activate
$ pip install -e .
I get that, but I also want to make sure developers are running the version they're expecting to run. Maybe we should check
Right, but I'd like to avoid having to mess with the system path if possible. There should be some recommended practices for this, no? There are so many packages out there, we can't be the only with this problem. I guess that's why the myriad of task runners exist. |
Wouldn't it make sense to get rid of this subprocess completely and instead use
With this, pytest can completely ensure which modules are tested, because there are no other processes that have different PYTHONPATH.
The benefit of this would also be that the tests become fully debuggable. |
Yes please, but I guess that this is even more work than the current proposal?
Getting rid of the |
I will give it a try 👍 |
Just FYI that's technically an abstraction violation; nothing in |
But that doesn't mean we can't rely on it for testing, right? From a practical point of view it would solve some of the issues we have with testing/coverage. |
No, it's just white-box testing |
I use |
16981ae
to
fe1ec68
Compare
4d49829
to
9d9462c
Compare
Shorter commands are always nicer but it's IMHO not enough to justify a PR that big. Is there some bigger problem that this PR solves for you? I remember I used to struggle to use
|
I scanned the comments and found a couple problem descriptions that should have been in commit messages. And then some more:
Prototyping and experimenting is great, but once a PR leaves the draft status it should be clear 1. what is the problem solved 2. why and how that solution was chosen. For earlier stage discussions please use drafts, issues, discord,... Some good inspiration: |
13a8e62
to
abba370
Compare
As a developer, I want to easily run the project’s tests using This is matching to what pytest proposes under "Benefits". Debugging is also challenging under the current approach since many tests rely on I’m not sure how you currently debug tests in your IDE, but most IDEs offer native
The proposed change minimizes or eliminates the use of Additionally, it introduces flexibility by allowing developers to choose what is tested using standard
I hope these arguments are convincing. PS: Up to now, I’ve been developing That’s why I really, really hope you’ll accept this Pull Request — it is state-of-the-art and it will make development and debugging so much easier. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you cleanup your commits a bit, please?
- Use the body to describe the change and its rationale
- Don't add stuff that is removed in the next commit (
tool.pytest.ini_options
in pyproject.toml) - Split test commits from other changes
Thanks, I guess this is how this PR should have all started. Please write a more detailed commit message based on that and start a fresh, clean PR (referring to this one). So far this PR looks more like brainstorming. Brainstorming is great! It's just confusing when it happens in a non-draft PR.
See above :-)
I suspect this is a different issue that requires a different solution. If this can be solved at a different time, then it should. Should it? Clearly state which way each PR is going. FYI I've been using PUDB_TTY to avoid that problem |
62280a5
to
0d9bceb
Compare
It's not "pytest integration" and you may not even call this an "IDE" but FWIW this works fine with Emacs right now:
It works even remotely over ssh/Tramp. However this is only debugging the pytest code, NOT the actual west code for the subprocess reasons already discussed above. |
6690648
to
2b63f2b
Compare
pytest
directlypytest
for local tree
I'm having some trouble with getting this to work locally. My assumption was that I should now be able to do: $ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install -e .
$ pip install pytest
$ pytest However this fail, for example with a single test: (same with $ pytest tests/test_help.py::test_extension_help_and_dash_h
============================= test session starts ==============================
platform linux -- Python 3.12.11, pytest-8.4.2, pluggy-1.6.0
rootdir: /home/pdgendt/west
configfile: pyproject.toml
collected 1 item
tests/test_help.py F [100%]
=================================== FAILURES ===================================
________________________ test_extension_help_and_dash_h ________________________
west_init_tmpdir = local('/tmp/pytest-of-pdgendt/pytest-9/test_extension_help_and_dash_h0/workspace')
def test_extension_help_and_dash_h(west_init_tmpdir):
# Test "west help <command>" and "west <command> -h" for extension
# commands (west_init_tmpdir has a command with one).
> cmd('update')
/home/pdgendt/west/tests/test_help.py:28:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/pdgendt/west/tests/conftest.py:401: in cmd
_cmd(cmd, cwd, env)
/home/pdgendt/west/tests/conftest.py:386: in _cmd
raise e
/home/pdgendt/west/tests/conftest.py:383: in _cmd
main.main(cmd)
/home/pdgendt/west/src/west/app/main.py:1205: in main
app.run(argv or sys.argv[1:])
/home/pdgendt/west/src/west/app/main.py:284: in run
self.run_command(argv, early_args)
/home/pdgendt/west/src/west/app/main.py:588: in run_command
self.run_builtin(args, unknown)
/home/pdgendt/west/src/west/app/main.py:702: in run_builtin
self.cmd.run(args, unknown, self.topdir,
/home/pdgendt/west/src/west/commands.py:201: in run
self.do_run(args, unknown)
/home/pdgendt/west/src/west/app/project.py:1208: in do_run
self.update_all()
/home/pdgendt/west/src/west/app/project.py:1274: in update_all
self.update(project)
/home/pdgendt/west/src/west/app/project.py:1522: in update
self.update_submodules(project)
/home/pdgendt/west/src/west/app/project.py:1423: in update_submodules
self.die(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <west.app.project.Update object at 0x7a3b414c5e50>, exit_code = 1
args = ('Submodule status failed for project: Kconfiglib.',)
def die(self, *args, exit_code: int = 1) -> NoReturn:
'''Print a fatal error using err(), and abort the program.
:param args: sequence of arguments to print.
:param exit_code: return code the program should use when aborting.
Equivalent to ``die(*args, fatal=True)``, followed by an attempt to
abort with the given *exit_code*.'''
self.err(*args, fatal=True)
if self.verbosity >= Verbosity.DBG_EXTREME:
raise RuntimeError("die with -vvv or more shows a stack trace. "
"exit_code argument is ignored.")
else:
> sys.exit(exit_code)
E SystemExit: 1
/home/pdgendt/west/src/west/commands.py:525: SystemExit
---------------------------- Captured stdout setup -----------------------------
initializing session repositories in /tmp/pytest-of-pdgendt/pytest-9/session_repos0
Initialized empty Git repository in /tmp/pytest-of-pdgendt/pytest-9/session_repos0/Kconfiglib/.git/
[master (root-commit) 58e308a] initial c41dc662-4a8a-4334-9138-b53799fdadce
Initialized empty Git repository in /tmp/pytest-of-pdgendt/pytest-9/session_repos0/tagged_repo/.git/
[master (root-commit) e97846e] initial d762cb97-fd56-4abe-a5cb-1db47d829573
Initialized empty Git repository in /tmp/pytest-of-pdgendt/pytest-9/session_repos0/net-tools/.git/
[master (root-commit) 78efb82] initial baeb89cf-ef96-4f14-890d-525a290af83e
Initialized empty Git repository in /tmp/pytest-of-pdgendt/pytest-9/session_repos0/zephyr/.git/
[master (root-commit) 5368f34] initial f7d2e202-c0a3-435a-8bbe-cb6ac04b1adc
[master f340070] base zephyr commit
3 files changed, 2 insertions(+)
create mode 100644 CODEOWNERS
create mode 100644 include/header.h
create mode 100644 subsys/bluetooth/code.c
[zephyr 7e0c488] test kconfiglib commit
1 file changed, 1 insertion(+)
create mode 100644 kconfiglib.py
[master ef483c3] tagged_repo commit
1 file changed, 1 insertion(+)
create mode 100644 test.txt
[master 3375f40] test net-tools commit
3 files changed, 18 insertions(+)
create mode 100644 qemu-script.sh
create mode 100644 scripts/test.py
create mode 100644 scripts/west-commands.yml
finished initializing session repositories
[master 0f868d9] add manifest
1 file changed, 26 insertions(+)
create mode 100644 west.yml
---------------------------- Captured stderr setup -----------------------------
Switched to branch 'zephyr'
Cloning into 'Kconfiglib'...
done.
Cloning into 'tagged_repo'...
done.
Cloning into 'net-tools'...
done.
Cloning into 'zephyr'...
done.
Cloning into '/tmp/pytest-of-pdgendt/pytest-9/test_extension_help_and_dash_h0/workspace/.west/manifest-tmp'...
done.
----------------------------- Captured stderr call -----------------------------
Cloning into bare repository '/home/pdgendt/.cache/zephyrproject/Kconfiglib/0aa789fdcd0cd5b2a14b654f054b65ae'...
done.
Cloning into '/tmp/pytest-of-pdgendt/pytest-9/test_extension_help_and_dash_h0/workspace/subdir/Kconfiglib'...
done.
From /home/pdgendt/.cache/zephyrproject/Kconfiglib/0aa789fdcd0cd5b2a14b654f054b65ae
* branch zephyr -> FETCH_HEAD
HEAD is now at 7e0c488 test kconfiglib commit
FATAL ERROR: Submodule status failed for project: Kconfiglib.
=========================== short test summary info ============================
FAILED tests/test_help.py::test_extension_help_and_dash_h - SystemExit: 1
============================== 1 failed in 0.24s =============================== Running with EDIT: I actually have this on |
c886314
to
9745f8b
Compare
@thorsten-klein see #862 |
pytest
for local treepytest
for local tree
313f0ef
to
3d819af
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Enable running and debugging pytest directly against the local source tree without relying on subprocess-based invocations, improving IDE integration and consistency of environment handling.
- Replace subprocess-based test helpers with in-process CLI invocation (west.app.main), adding a subprocess variant only where needed
- Update tests to expect SystemExit, capture stderr explicitly, and add coverage for module execution and forall env vars
- Adjust main entrypoint to prepend src to sys.path when run as a script/module; update coverage paths and README instructions
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
File | Description |
---|---|
tests/test_project.py | Replace CalledProcessError with SystemExit, switch to cmd_subprocess where necessary, add test_forall_env_vars, and separate stderr assertions. |
tests/test_main.py | Add tests for version output via in-process and subprocess calls; add module-run test verifying sys.path injection and exit code. |
tests/test_help.py | Simplify help output checks by removing platform newline normalization (now captured consistently). |
tests/test_config.py | Migrate exception expectations from CalledProcessError to SystemExit and update cmd_raises usage. |
tests/test_alias.py | Update exception expectations, but assertions inspect exit code instead of error output. |
tests/conftest.py | Introduce _cmd, cmd, cmd_raises, cmd_subprocess helpers; capture stdout/stderr; run west.app.main directly; optional subprocess mode. |
src/west/app/main.py | Prepend src to sys.path when executed as main to ensure local modules are used. |
pyproject.toml | Fix coverage omit globs to match paths nested under any directory. |
README.rst | Document how to run pytest against installed package vs local source using pythonpath=src. |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
The previous test setup used subprocess calls, which did not allow to debug the tests. It also caused a mix of Python modules from the installed West package and the local source tree when running tests directly with `pytest`. The tests have been updated so that `pytest` can now be run directly and fully debugged. `pytest` can be run as follows: - `pytest` — runs tests against the installed package - `pytest -o pythonpath=src` — runs tests against the local source tree Within the tests following methods can be used to run west commands: - cmd(...): call main() with given west command and capture std (and optionally stderr) - cmd_raises(...): call main() with given west command and catch expected exception. The exception and stderr are returned by default. Optionally stdout can be captured. - cmd_subprocess(...): Run west command in a subprocess and capture stdout.
When west/app/main.py is executed directly, also the correct West modules must be imported. Therefore, the Python module search path is configured appropriately before importing any West modules.
Test that the Python module search path is prepended with correct local module path if west/app/main.py is executed directly.
3d819af
to
e24ae8e
Compare
Why?
I'm not sure how tests for the local source tree are currently debugged.
In my setup, I’m unable to run
pytest
directly on the local tree, and debugging is difficult because it’s not possible to step through sincesubprocess
is used in the current test setup.Proposed Changes
This change allows running
pytest
directly, instead of only being able to run it viauv run poe test
.pytest
can test either the installed West package or the local source tree, depending on how it’s invoked.This simplifies test execution and improves compatibility with
pytest
integrations (e.g. in IDEs).With this setup,
pytest
is fully in charge for setting up the Python environment, so testing becomes straightforward:Background
During this work, I faced that many tests used
subprocess
, which interfered with testing the local copy.Subprocesses do not inherit the full Python environment configured by
pytest
, causing a mix between modules from the installed West package and those from the local source tree. Therefore I removed the use of subprocesses in tests wherever possible. Where subprocesses are still used, the python module (west/app/main.py
) is called instead ofwest
whereby it is ensured now that its correct modules are used by prepending correct PYTHONPATH. With those changes only one Python environment managed bypytest
is used, ensuring consistent behavior and enabling proper debugging.