pip-tools icon indicating copy to clipboard operation
pip-tools copied to clipboard

Fix build deps compilation for `setuptools < 70.1.0`

Open chrysle opened this issue 1 year ago • 6 comments

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, docs or skip-changelog as they determine changelog listing.
  • [x] Assign the PR to an existing or new milestone for the target version (following Semantic Versioning).

chrysle avatar Jun 25 '24 08:06 chrysle

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 avatar Jul 07 '24 19:07 webknjaz

@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 ;-)?

chrysle avatar Jul 22 '24 08:07 chrysle

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.

WhyNotHugo avatar Aug 15 '24 07:08 WhyNotHugo

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..

webknjaz avatar Aug 15 '24 16:08 webknjaz

@chrysle the windows failure is because of the deprecation warning in pip, right?

webknjaz avatar Sep 05 '24 14:09 webknjaz

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.

chrysle avatar Sep 05 '24 16:09 chrysle

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?

webknjaz avatar Oct 25 '24 14:10 webknjaz

@chrysle could you try rebasing?

webknjaz avatar Dec 02 '24 07:12 webknjaz

Closed+reopened to re-trigger the CI as the logs got stale and garbage-collected.

webknjaz avatar Dec 04 '24 03:12 webknjaz

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...

webknjaz avatar Dec 04 '24 03:12 webknjaz

I've converted this to draft to prevent accidental merging ahead of time. I'll undraft it once I'm done experimenting.

webknjaz avatar Dec 04 '24 03:12 webknjaz

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.

webknjaz avatar Dec 10 '24 04:12 webknjaz

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.

webknjaz avatar Dec 10 '24 04:12 webknjaz

(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.

webknjaz avatar Dec 12 '24 00:12 webknjaz

.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

webknjaz avatar Dec 12 '24 01:12 webknjaz

I verified that setting the PIP_CONSTRAINT env var is exactly what breaks on Windows. Commenting it out makes the command pass.

webknjaz avatar Dec 12 '24 01:12 webknjaz

# 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.

webknjaz avatar Dec 12 '24 01:12 webknjaz

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

webknjaz avatar Dec 12 '24 02:12 webknjaz

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.

webknjaz avatar Dec 12 '24 02:12 webknjaz