pypa / setuptools

Official project repository for the Setuptools build system
https://pypi.org/project/setuptools/
MIT License
2.5k stars 1.19k forks source link

test_cygwin job failing after adding ruff #3921

Open jaraco opened 1 year ago

jaraco commented 1 year ago

In d2ec0473f8d4c25cc6f696e70ba110e1061e4dfe, I replaced pytest-flake8 with pytest-ruff (jaraco/skeleton#79). Unfortunately, adding ruff implies adding a dependency on Rust :( and the test_cygwin job is failing in CI because it doesn't have rust.

jaraco commented 1 year ago

In 51408ac9f34f8ca97c33f7f234160c502acb11c4, I disabled the cygwin job, but then the builds started failing on Python 3.12 where they didn't previously. I don't understand why. Does the codebase no longer work with Python 3.12 in non-main branches?

abravalheri commented 1 year ago

Hi @jaraco, I noticed that the skeleton project does not have any skeleton folder or skeleton.py that can be imported when the package is installed. Nevertheless, the docs still specify .. automodule:: skeleton.

Could it be the case a new version of sphinx (version 7.0.0 was released on Apr 29) behaves differently when it cannot import a package? Or maybe sphinx 7.0.0 has some conditional logic for automodule in Python 3.12?

If I run:

> docker run --rm -it python:3.12.0a7-bullseye /bin/bash
git clone https://github.com/jaraco/skeleton /tmp/skeleton
cd /tmp/skeleton
python3.12 -m venv .venv
.venv/bin/python -m pip install -e '.[docs,testing]'
cd docs
../.venv/bin/python -m sphinx -W --keep-going . _build/html
# ...
# WARNING: autodoc: failed to import module 'skeleton'; the following exception was raised:
# No module named 'skeleton'
# ...
# build finished with problems, 1 warning

I can see the error happening.

However if I manually add a skeleton directory before installing, Sphinx does not show an error:

> docker run --rm -it python:3.12.0a7-bullseye /bin/bash
git clone https://github.com/jaraco/skeleton /tmp/skeleton
cd /tmp/skeleton
mkdir skeleton
python3.12 -m venv .venv
.venv/bin/python -m pip install -e '.[docs,testing]'
cd docs
../.venv/bin/python -m sphinx -W --keep-going . _build/html
# ...
# build succeeded.
# The HTML pages are in _build/html.
jaraco commented 1 year ago

I se I must have confused myself. I didn't mean to be looking at the pipelines on skeleton but on Setuptools. Here I'm mainly concerned about making sure the Setuptools pipelines are passing.

Error is here:

``` .pkg: build_editable> python /opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/site-packages/pyproject_api/_backend.py True setuptools.build_meta python: packaging backend failed (code=1), with ValueError: invalid literal for int() with base 10: 'unknown' warning: no files found matching '*.py' under directory 'tests' warning: no files found matching '*.txt' under directory 'docs' warning: no files found matching '*.conf' under directory 'docs' warning: no files found matching '*.css' under directory 'docs' warning: no files found matching '*.css_t' under directory 'docs' warning: no files found matching 'Makefile' under directory 'docs' warning: no files found matching 'indexsidebar.html' under directory 'docs' warning: no files found matching 'msvc-build-launcher.cmd' Traceback (most recent call last): File "/home/runner/work/setuptools/setuptools/setuptools/command/editable_wheel.py", line 140, in run self._ensure_dist_info() File "/home/runner/work/setuptools/setuptools/setuptools/command/editable_wheel.py", line 159, in _ensure_dist_info dist_info.run() File "/home/runner/work/setuptools/setuptools/setuptools/command/dist_info.py", line 103, in run bdist_wheel = self.get_finalized_command('bdist_wheel') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/cmd.py", line 304, in get_finalized_command cmd_obj = self.distribution.get_command_obj(command, create) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/dist.py", line 860, in get_command_obj klass = self.get_command_class(command) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/dist.py", line 966, in get_command_class self.cmdclass[command] = cmdclass = ep.load() ^^^^^^^^^ File "/opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/importlib/metadata/__init__.py", line 203, in load module = import_module(match.group('module')) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/importlib/__init__.py", line 124, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 1334, in _gcd_import File "", line 1307, in _find_and_load File "", line 1278, in _find_and_load_unlocked File "", line 841, in _load_unlocked File "", line 1023, in exec_module File "", line 400, in _call_with_frames_removed File "/home/runner/work/setuptools/setuptools/.tox/.pkg/lib/python3.12/site-packages/wheel/bdist_wheel.py", line 55, in setuptools_major_version = int(setuptools.__version__.split(".")[0]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ValueError: invalid literal for int() with base 10: 'unknown' /home/runner/work/setuptools/setuptools/setuptools/command/editable_wheel.py:151: _DebuggingTips: An error happened while installing 'setuptools' in editable mode. ************************************************************************ The following steps are recommended to help debugging this problem: - Try to install the project normally, without using the editable mode. Does the error still persists? (If it does, try fixing the problem before attempting the editable mode). - If you are using binary extensions, make sure you have all OS-level dependencies installed (e.g. compilers, toolchains, binary libraries, ...). - Try the latest version of setuptools (maybe the error was already fixed). - If you (or your project dependencies) are using any setuptools extension or customization, make sure they support the editable mode. After following the steps above, if the problem still persist and you think this is related to how setuptools handles editable installations, please submit a reproducible example (see https://stackoverflow.com/help/minimal-reproducible-example) to: https://github.com/pypa/setuptools/issues More information about editable installs can be found in the docs: https://setuptools.pypa.io/en/latest/userguide/development_mode.html ************************************************************************ _DebuggingTips.warn(project) Traceback (most recent call last): File "/opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/site-packages/pyproject_api/_backend.py", line 90, in run outcome = backend_proxy(parsed_message["cmd"], **parsed_message["kwargs"]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/site-packages/pyproject_api/_backend.py", line 32, in __call__ return getattr(on_object, name)(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/build_meta.py", line 442, in build_editable return self._build_with_temp_dir( ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/build_meta.py", line 398, in _build_with_temp_dir self.run_setup() File "/home/runner/work/setuptools/setuptools/setuptools/build_meta.py", line 335, in run_setup exec(code, locals()) File "", line 87, in File "/home/runner/work/setuptools/setuptools/setuptools/__init__.py", line 108, in setup return distutils.core.setup(**attrs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/core.py", line 185, in setup return run_commands(dist) ^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/core.py", line 201, in run_commands dist.run_commands() File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/dist.py", line 969, in run_commands self.run_command(cmd) File "/home/runner/work/setuptools/setuptools/setuptools/dist.py", line 1221, in run_command super().run_command(command) File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/dist.py", line 988, in run_command cmd_obj.run() File "/home/runner/work/setuptools/setuptools/setuptools/command/editable_wheel.py", line 140, in run self._ensure_dist_info() File "/home/runner/work/setuptools/setuptools/setuptools/command/editable_wheel.py", line 159, in _ensure_dist_info dist_info.run() File "/home/runner/work/setuptools/setuptools/setuptools/command/dist_info.py", line 103, in run bdist_wheel = self.get_finalized_command('bdist_wheel') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/cmd.py", line 304, in get_finalized_command cmd_obj = self.distribution.get_command_obj(command, create) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/_distutils/dist.py", line 860, in get_command_obj klass = self.get_command_class(command) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/runner/work/setuptools/setuptools/setuptools/dist.py", line 966, in get_command_class self.cmdclass[command] = cmdclass = ep.load() ^^^^^^^^^ File "/opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/importlib/metadata/__init__.py", line 203, in load module = import_module(match.group('module')) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/hostedtoolcache/Python/3.12.0-alpha.7/x64/lib/python3.12/importlib/__init__.py", line 124, in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 1334, in _gcd_import File "", line 1307, in _find_and_load File "", line 1278, in _find_and_load_unlocked File "", line 841, in _load_unlocked File "", line 1023, in exec_module File "", line 400, in _call_with_frames_removed File "/home/runner/work/setuptools/setuptools/.tox/.pkg/lib/python3.12/site-packages/wheel/bdist_wheel.py", line 55, in setuptools_major_version = int(setuptools.__version__.split(".")[0]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ValueError: invalid literal for int() with base 10: 'unknown' Backend: run command build_editable with args {'wheel_directory': '.tox/.pkg/dist', 'config_settings': {'--build-option': []}, 'metadata_directory': None} ```
abravalheri commented 1 year ago

Regarding the errors in setuptools.__version__

(Ref: https://github.com/pypa/setuptools/actions/runs/4943028741/jobs/8837121715)

It is been a while that we are experiencing flaky tests erroring in things related to importlib.metadata. I think they first started appearing with the change in importlib-metadata v5.2.0 (vendored in setuptools v67.4.0).

@jaraco, after some investigation, I suspect that the problem is the following:

  1. The setuptools test suite runs with pytest-xdist (for the sake of speed)
  2. During the tests setuptools.build_meta is called by different tests to build setuptools itself.
  3. setuptools.build_meta calls egg_info a few times (for each one of the PEP 517 hooks, including get_requires*).
    • a) I suspect the .egg-info/PKG-INFO file is rewritten everytime egg_info is called
    • b) The egg_info.egg_base parameter is set to the project root (therefore there is no isolation for egg_info, if multiple builds happen concurrently, all of them will try to write the same files at the same time). This problem is related to https://github.com/pypa/setuptools/issues/3119.
  4. While the PKG-INFO file is rewritten, the tests will try to use importlib_metadata (e.g. to read version, entry-points, to find plugins for pytest/flake8, etc..), which in turn will try to read the PKG-INFO (because setuptools.egg-info is in sys.path, due to the flat layout in the repository).
  5. There is a race condition between egg_info and importlib_metadata which results in the PKG-INFO file being empty.

Right now we don't have a way to avoid running egg_info, and moreover when I tried to change setuptools.build_meta to set egg_base to a temporary directory, I had bootstrapping issues.

The bootstrap issues occur because bootstrap.egg-info/entry_points.txt only have the minimum necessary to run egg_info, and the whole bootstrapping process accidentally rely on the fact that egg_info will generate another setuptools.egg-info/entry_points.txt in the project root file before other PEP 517 hooks are called (so when build_sdist and build_wheel are called, importlib_metadata.entry_points can find the final entrypoints specified by setuptools in its setup.cfg).

In https://github.com/pypa/setuptools/pull/3904, when I was investigating this problem I tried to improve the way the PKG-INFO files are generated to be more "atomic" (https://github.com/pypa/setuptools/pull/3904/commits/ae8ec5c3f4cfc7f14783b4dd58c436a7ba168138). This should be an improvement but it is not the whole solution.

I also run into the setuptools.__version__ problem and added a workaround in #3915.

jaraco commented 1 year ago

I also run into the setuptools.__version__ problem and added a workaround in #3915.

Aha. I was sure I'd synced to the latest main before working on this issue, but it seems I had not (51408ac9 is based on an old parent).

bootstrapping process accidentally rely on

I wouldn't say it's accidental. I'd intended for the bootstrapping to provide the minimum customization to allow setuptools to run normally (and minimize duplication).