pip-tools
pip-tools copied to clipboard
Fix build deps compilation for `setuptools < 70.1.0`
See https://github.com/jazzband/pip-tools/pull/1681#issuecomment-2212541289 for context.
Contributor checklist
- [x] Included tests for the changes.
- [x] PR title is short, clear, and ready to be included in the user-facing changelog.
Maintainer checklist
- [x] Verified one of these labels is present:
backwards incompatible,feature,enhancement,deprecation,bug,dependency,docsorskip-changelogas they determine changelog listing. - [x] Assign the PR to an existing or new milestone for the target version (following Semantic Versioning).
Alternatively, it's even better to emulate such a situation with artificial package stubs so that it doesn't need to hit the network.
@webknjaz I reworked the PR; please have a look. Of course there are still the failing Windows tests, can we just drop that horrible Microsoft product ;-)?
Slightly changing the order of assertions should help see more details into the errors:
diff --git a/tests/test_cli_compile.py b/tests/test_cli_compile.py
index c5031fc..8db0038 100644
--- a/tests/test_cli_compile.py
+++ b/tests/test_cli_compile.py
@@ -787,13 +787,13 @@ def test_direct_reference_with_extras(runner):
"pip-tools[testing,coverage] @ git+https://github.com/jazzband/[email protected]"
)
out = runner.invoke(cli, ["-n", "--rebuild", "--no-build-isolation"])
- assert out.exit_code == 0
assert (
"pip-tools[coverage,testing] @ git+https://github.com/jazzband/[email protected]"
in out.stderr
)
assert "pytest==" in out.stderr
assert "pytest-cov==" in out.stderr
+ assert out.exit_code == 0
def test_input_file_without_extension(pip_conf, runner):
Generally, I prefer to compare out.stderr before checking the status code in tests; if stderr isn't the expected output, pytest will print it when failing the assertion.
Generally, I prefer to compare out.stderr before checking the status code in tests; if stderr isn't the expected output, pytest will print it when failing the assertion.
That won't be necessary if you'll follow the principle of a single assertion per test. Then pytest will print out everything. But personally, I have --showlocals in all configs so that the context is always printed..
@chrysle the windows failure is because of the deprecation warning in pip, right?
Could you elaborate on that? It seems that the resolver only found some pre-versions of a package, only I don't know much because the output is shortened.
Yeah, it's weird. Somebody needs to use https://github.com/marketplace/actions/debugging-with-tmate to see what's happening on Windows. The vars in CI seem to suggest that stderr is incomplete or something. Can it be some output buffering specific to windows? Do we need PYTHONUNBUFFERED=1 in tests?
@chrysle could you try rebasing?
Closed+reopened to re-trigger the CI as the logs got stale and garbage-collected.
Of course there are still the failing Windows tests
Interestingly, the “previous” tests pass, even on Windows. But the “latest” crash all over the place because of the relative vs. absolute path differences. “main” fails as well.
“previous” is set to pip==22.2.* currently.
Honestly, I'm leaning towards making the tests more stable by pinning pip in regular push/PR runs and only having it hit newer versions in nightlies...
I've converted this to draft to prevent accidental merging ahead of time. I'll undraft it once I'm done experimenting.
UPD: it looks like pyproject.toml stubs in the tests that don't have a [build-system] section cause build to crash on Windows trying to access the setuptools.build_meta:__legacy__ fallback.
Oh, it looks like specifying it manually makes another module non-importable: out.stderr="Backend 'setuptools.build_meta' is not available.\nFailed to parse C:\\Users\\runneradmin\\AppData\\Local\\Temp\\pytest-of-unknown\\pytest-0\\popen-gw2\\test_compile_build_targets_set0\\pyproject.toml\n". So that ain't it.
I bet there's some escaping issue with Windows paths and/or PYTHONPATH etc.
(documenting the troubleshooting env setup procedure)
Here's what I do upon connection to the windows job over tmate (== tmux over SSH):
.tox/piplatest-coverage/Scripts/pip install -e .
.tox/piplatest-coverage/Scripts/pytest --no-cov -n0 -svvvvv -l tests/test_cli_compile.py::test_compile_build_targets_setuptools_no_wheel_dep
.tox/piplatest-coverage/Scripts/pip-compile.exe --build-deps-for wheel C:/msys64/tmp/pytest-of-runneradmin/pytest-0/test_compile_build_targets_set0/pyproject.toml --output-file - -vvvvvv
This lets me populate the temporary files for inspection (the pytest command). It also allows me to edit files in-place (Ctrl+b c to create a new tmux tab; Ctrl+b n / Ctrl+b p / Ctrl+b 1 / Ctrl+b 2 to switch tabs) and rerun pytest or pip-compile directly.
Editing files is done via nano since vim does not play well with that windows/cygwin env, I haven't checked why.
Scrolling the terminal output: Ctrl+b PgUp.
.tox/piplatest-coverage/Scripts/pip-compile.exe --build-deps-for wheel C:/msys64/tmp/pytest-of-runneradmin/pytest-0/test_compile_build_targets_set0/pyproject.toml --output-file - -vvvvvv
Better repro without running pytest upfront:
$ .tox/piplatest-coverage/Scripts/pip-compile.exe --build-deps-for wheel tests/test_data/packages/small_fake_with_pyproject/pyproject.toml --output-file - -vvvvvv
I verified that setting the PIP_CONSTRAINT env var is exactly what breaks on Windows. Commenting it out makes the command pass.
# test-venv/Scripts/pip-compile --build-deps-for wheel tests/test_data/packages/small_fake_with_pyproject/pyproject.toml --output-file - -vvvvvv
Using pip-tools configuration defaults found in 'pyproject.toml'.
Creating isolated environment: venv+pip...
tmpfile.name='C:\\msys64\\tmp\\tmpti95poux'
Installing packages in isolated environment:
- setuptools < 70
Getting metadata for wheel...
Backend 'setuptools.build_meta' is not available.
Failed to parse D:\a\pip-tools\pip-tools\tests\test_data\packages\small_fake_with_pyproject\pyproject.toml
I wonder if reversing the slashes is needed or something.
Ah-ha!
I think I understand what's happening now! The temporary file is open and flushed, but the file descriptor remains open, which on Windows prevents other processes from accessing it. So when we call build, it calls pip install that sees PIP_CONSTRAINT, attempts to open it, and likely fails to install the build deps because of permission denied.
It seems like calling .close() upon .flush() (or should it be instead, even?) makes it accessible. We just have to use delete_on_close=False so that .close() does not also remove the file from disk right away. With delete=True (which is the default), the CM will clean it up on exit.
Some of this is mentioned @ https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile
Urgh.. looks like delete_on_close got introduced in Python 3.12. At least I now have an idea of a workaround. Will try to complete this PR today.