Hi all, I can see that ```[pytest] extra_requirem...
# general
d
Hi all, I can see that
Copy code
[pytest]
extra_requirements.add = ["ipdb"]
Is scheduled to be dropped in
2.18
What’s the recommended way for using
ipdb
in tests moving forward? I’ve got the following in my
pants.toml
Copy code
[test]
extra_env_vars = ["PYTHONBREAKPOINT=ipdb.set_trace"]
And this in my
BUILD
Copy code
python_tests(
    name="tests0",
    dependencies=["//:reqs0#ipdb"]
)
In my dummy example I can see
ipdb
is a transitive dependency, so it’s included in the pex build
Copy code
➜  pants-test pants dependencies --transitive test_app.py
21:28:55.35 [INFO] Initializing scheduler...
21:28:58.44 [INFO] Scheduler initialized.
//:reqs0#Flask
//:reqs0#ipdb
//:reqs0#pytest
//main.py:root
//requirements.txt:reqs0
But when I set a breakpoint, it just gives me plain old
pdb
even though the
PYTHONBREAKPOINT
environment is present and
ipdb
is available
Copy code
pants test --debug :: -- -s
<OUTPUT REDACTED>
(Pdb) os.environ.get("PYTHONBREAKPOINT")
'ipdb.set_trace'
What am I missing
e
Well, did you read the doc / warning?: https://www.pantsbuild.org/docs/reference-pytest#extra_requirements It looks like the link does not render right, but that
Custom tool versions are now installed from named resolves, as described at [Lockfiles](doc:python-lockfiles).
should point to https://www.pantsbuild.org/docs/python-lockfiles#lockfiles-for-tools
d
I read the warning and don’t really understand what it wants me to do. From the above everything to me looks like
ipdb
should be working. The absence of explicitly doing something like:
Copy code
[python.resolves]
pytest = "3rdparty/python/pytest.lock"

[pytest]
install_from_resolve = "pytest"  # Use this resolve's lockfiles.
requirements =["//3rdparty/python:pytest"]  # Use these requirements from the lockfile.
Means it should use my default lockfile right? (where ipdb is present)
e
Definitely check out https://www.pantsbuild.org/docs/python-lockfiles#lockfiles-for-tools - have you created a custom tool lockfile like it directs?
Oh, ok - reading your response more closely...
So, two things: +
//3rdparty/python:pytest
looks like a bad target address - surely missing a #. + You're missing a requirement in that list on ipdb.
And, yeah - when debugging don't entertain extra variables. I'd use the custom lock file config you post 1st, then after getting that working perhaps fall back to the default lockfile and add in that extra dimension of how to tell Pants, etc.
d
Apologies that was just an example stolen from the docs. I’ve tried adding the following to my
pants.toml
Copy code
[pytest]
install_from_resolve = "python-default"
requirements = [
  "//:reqs0#pytest",
  "//:reqs0#ipdb",
]
Which is present in my
BUILD
Copy code
python_requirements(
    name="reqs0",
)
Setting a breakpoint in my test still gives me the same pdb debugger, but ipdb is importable:
Copy code
(Pdb) import ipdb
(Pdb) import os
(Pdb) os.environ.get("PYTHONBREAKPOINT")
'ipdb.set_trace'
(Pdb)
This is after running
Copy code
pants generate-lockfiles ::
e
So PYTHONBREAKPOINT is only respected for Python >= 3.7 (https://peps.python.org/pep-0553/), what does
sys.version_info
say?
d
Copy code
[python]
interpreter_constraints = ['==3.11.*']
Copy code
(Pdb) import sys
(Pdb) sys.version_info
sys.version_info(major=3, minor=11, micro=4, releaselevel='final', serial=0)
e
Ok, well, no clue. Would you agree at this point it does not look like a Pants issue? You have ipdb on sys.path, you have the right interpreter version, and you have the env var leaked.
"Note that as with all other PYTHON* environment variables, PYTHONBREAKPOINT is ignored when the interpreter is started with -E. This means the default behavior will occur (i.e. pdb.set_trace() will run). There was some discussion about alternatively treating PYTHONBREAKPOINT=0 when -E as in effect, but the opinions were inconclusive, so it was decided that this wasn’t special enough for a special case."
So ... Pants runs runs Python tools via Pex venvs which - I think all use
-E
.
Which would mean though that this never could have worked.
d
I’ve tried doing
pants export ::
and running pytest manually inside the virtualenv
Copy code
PYTHONBREAKPOINT=ipdb.set_trace pytest -s test_app.py
And this doesn’t work either, so I’m stumped. I don’t think this is a pants issue.
e
Look at the venv! Does it use
-E
? I bet the
pytest
script uses
-E
- check the shebang.
For
pex_binary
targets you can control this aspect of the shebang via: https://www.pantsbuild.org/docs/reference-pex_binary#codevenv_hermetic_scriptscode but I don't think this has ever been plumbed for Pants internal venvs, like the one it uses to run
pytest
. @dazzling-elephant-33766 you're claiming this used to work for you in Pants version X? What is X?
d
I think:
Copy code
[pytest]
extra_requirements.add = ["ipdb"]
worked in 2.16 but I’d have to double check. I’m currently on 2.17
e
Ok. And if you can
head -1 $(which pytest)
in the exported + activated venv, it would be nice to confirm
-E
in the shebang.
d
Copy code
(3.11.4) ➜  pants-test git:(main) ✗ head -1 $(which pytest)
#!/Users/jack/code/pants-test/dist/export/python/virtualenvs/python-default/3.11.4/bin/python3.11 -sE
Yeaaah … You nailed it
e
Ok, and based on that I'll be very surprised if this ever worked.
Ah, well, I guess if the internal venv Pex pytest is not run via script but instead via
PEX_SCRIPT=pytest python the.internal.pex ...
maybe? The internal way pytest is launched may or may not have changed. I've not been hacking on that stuff for a few years.
@dazzling-elephant-33766 try setting https://www.pantsbuild.org/docs/reference-pytest#entry_point I'm not sure what the value should be for your version of pytest, but that will change how the PEX runs (i.e. not by the
pytest
venv console script).
You can determine the entry point a few ways, but one is reading the venv console script contents.
Looks like I switched the entry point to a console script waaay back: https://github.com/pantsbuild/pants/pull/11649, 1st landed in 2.4.0.dev1
d
I must have misremembered this ever working 🤔 But the docs do have a lot of references to using ipdb with pytest, so makes me think this has worked at some point after 2.4.0, I’m unsure. I’m unsure what I’d be setting the
entry_point
of pytest to from your suggestion https://www.pantsbuild.org/docs/reference-pytest#entry_point iPart of me thinks I’ve misremembered this working, but then the docs have lots of references to using
e
Can you cat the
pytest
console script here?
But the docs do have a lot of references to using ipdb with pytest, so makes me think this has worked at some point after 2.4.0, I’m unsure.
That could definitely be true. It may be that the way the pytest PEX is executed behind the scenes has changed. I've not been involved recently, but I know enough to know there has been some shuffling in that general area.
For example - I have
pytest
set up using nox in a porject and I find:
Copy code
$ cat .nox/test-3-12/bin/pytest
#!/home/jsirois/dev/a-scie/lift/.nox/test-3-12/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from pytest import console_main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(console_main())
So the entrypoint is
pytest:console_main
for that
pytest
(version 7.4.2).
Another way:
Copy code
$ cat .nox/test-3-12/lib/python3.12/site-packages/pytest-7.4.2.dist-info/entry_points.txt
[console_scripts]
py.test = pytest:console_main
pytest = pytest:console_main
d
Copy code
#!/Users/jack/code/pants-test/dist/export/python/virtualenvs/python-default/3.11.4/bin/python3.11 -sE
# -*- coding: utf-8 -*-
import re
import sys
from pytest import console_main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(console_main())
But altering
Copy code
[pytest]
install_from_resolve = "python-default"
entry_point="pytest:console_main"
requirements = [
  "//:reqs0#pytest",
  "//:reqs0#ipdb",
]
doesn’t appear to change the behaviour
e
Ok, that was my one wild lazy guess at a workaround. I think this will require an issue filed + work.
h
To clarify one point - if you don't specify otherwise, Pants will install pytest from a default, built-in lockfile. If you want to use your default user-code lockfile you have to point to it:
Copy code
[pytest]
install_from_resolve = "python-default"
But also note that if you do this naively it means that your tests will depend on the entire lockfile, and be invalidated more often than you'd like. So you may also want to set
requirements =[...]
to just the actual needed requirement targets (pytest, ipdb and anything pytest plugins, for example)
And yeah, regarding the
-E
problem, filing an issue would be great, thanks.
1
d
Thank you both for your help: I have filed an issue here: https://github.com/pantsbuild/pants/issues/19977