Skip to content

Conversation

@abitrolly
Copy link
Contributor

When reporting underlying backend (setuptools) issues, the (setuptools) tracker requirement is to mention exact version of backend being used. This information is, however, absent from the build output.

This PR shows backed spec being used, without version:

$ touch pyproject.toml
$ python -m build
* Creating isolated environment: venv+pip... build:backend  setuptools.build_meta:__legacy__ build:python_executable  /tmp/build-env-098a9c2b/bin/python
* Installing packages in isolated environment:
  - setuptools >= 40.8.0 ...

Getting version requires running custom code in venv created by build after backend is installed and it is not clear how to do that yet. The code should extract first component from the spec, and append __version__ to it. At least that's the way of doing it for setuptools.

Fixes #959

When reporting underlying backend (setuptools) issues, the
(setuptools) tracker requirement is to mention exact version of
backend being used. This information is, however, absent from the
`build` output.

This commmit shows backed spec being used, without version:

    $ touch pyproject.toml
    $ python -m build
    * Creating isolated environment: venv+pip...
    build:backend  setuptools.build_meta:__legacy__
    build:python_executable  /tmp/build-env-098a9c2b/bin/python
    * Installing packages in isolated environment:
      - setuptools >= 40.8.0
    ...

Getting version requires running custom code in venv created by
`build` after backend is installed and it is not clear how to do
that yet. The code should extract first component from the spec,
and append `__version__` to it. At least that's the way of doing
it for `setuptools`.

Related to pypa#959
@abitrolly
Copy link
Contributor Author

JSON tests fail, because the debug output is unconditional,

There is some kind of logging framework in _ctx.py, obscured by "contextvars" and types https://github.com/pypa/build/blob/a40623b20de5d29c80b569283191d65e630f4fc0/src/build/_ctx.py

I couldn't make it work yet. Looks like verbosity is manually controlled, and there is a sole method log that only outputs INFO level messages.

_default_logger.log(logging.INFO, message, stacklevel=2)

Copy link
Member

@layday layday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should all probably be done in __main__, using _ctx.log with an origin.

    $ python -m build -v
    * Creating isolated environment: venv+pip...
    build:backend  setuptools.build_meta:__legacy__
    build:python_executable  /tmp/build-env-5yw5g814/bin/python
    ...
@abitrolly
Copy link
Contributor Author

@layday made it to use _ctx.log. Still need to investigate how to run a command inside build venv.

@abitrolly
Copy link
Contributor Author

Marking this as ready for review. Need to make sure that this output is acceptable before patching tests.

$ touch pyproject.toml
$ python -m build
...
* Building wheel...
  (using setuptools 80.9.0)
running bdist_wheel

@abitrolly abitrolly marked this pull request as ready for review January 1, 2026 21:09
Comment on lines 276 to 278
lib = self._backend.split('.')[0]
script = f'import {lib}; print({lib}.__version__)'
cmd = [self.python_executable, '-c', script]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use importlib.metadata.version (via _compat) with the dist name. A mapping of module to dist names is provided by importlib.metadata.packages_distributions. It'd be easier to just list the installed versions of all direct dependencies, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday _compat is unavailable inside venv. Is there any way to get it there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday I copied the _compat.importlib logic into script. That was the most straightforward solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday I got the dist name from the module name through packages_distributions.

          module = self._backend.split('.')[0]
          script = textwrap.dedent(f"""
              import sys
              try:
                  from importlib import metadata
              except ModuleNotFoundError:
                  # Python < (3, 10, 2)
                  import importlib_metadata as metadata
              lib = metadata.packages_distributions()['{module}'][0
  ]
              print(lib + ' ' + metadata.version(lib))
          """)

List direct dependencies from builder.build_system_requires should be possible too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the logic to print build requirements. Now the output is more suitable for reproduction.

* Building wheel...
    setuptools==80.9.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday purelib code doesn't work with path.

$ cd /tmp
$ python -m venv xxx
$ ls /tmp/xxx/lib/python3.13/site-packages
pip  pip-24.3.1.dist-info
$ python
>>> from importlib.metadata import distributions
>>> next(iter(distributions(name="pip"))).version
'24.3.1'
>>> next(iter(distributions(name="pip", path="/tmp/xxx/lib/python3.13/site-packages"))).version
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    next(iter(distributions(name="pip", path="/tmp/xxx/lib/python3.13/site-packages"))).version
    ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
StopIteration
>>>

For my runs _has_dependency is only called with empty kwargs, so maybe the purelib codepath is dead.

What to do now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path is a list[str].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday thanks. It works now. I had to pass env to the builder though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday it is done now, but tests are failing, and I don't get what is going on. If I run test_projectbuilder.py test_log scenario manually, it gives this.

$ cd tests/packages/test-flit
$ PYTHONPATH=../../../src python -m build
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - flit_core >=2,<4
* Getting build dependencies for sdist...
* Building sdist...
  flit_core==3.12.0
* Building wheel from sdist
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
  - flit_core >=2,<4
* Getting build dependencies for wheel...
* Building wheel...
  flit_core==3.12.0
Successfully built test_flit-1.0.0.tar.gz and test_flit-1.0.0-py2.py3-none-any.whl

But fixed test is missing all previous lines https://github.com/pypa/build/actions/runs/20720516961/job/59482742587?pr=969

E         Full diff:
E           [
E         -     (
E         -         'INFO',
E         -         'Getting build dependencies for sdist...',
E         -     ),
E         -     (
E         -         'INFO',
E         -         'Getting build dependencies for wheel...',
E         -     ),
E         -     (
E         -         'INFO',
E         -         'Getting metadata for wheel...',
E         -     ),
E         -     (
E         -         'INFO',
E         -         'Building sdist...',
E         -     ),
E               (
E                   'INFO',
E                   '  flit_core==3.12.0',
E               ),
E               (
E                   'INFO',
E         -         'Building wheel...',
E         -     ),
E         -     (
E         -         'INFO',
E                   '  flit_core==3.12.0',
E               ),
E           ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@layday the problem was that tests use default logger, which was skipping messages with origin. I addressed that, but also left some proposals for enhancements in #973

The only problem I can not solve with testing is how to mock package installation in tests/test_main.py::test_logging_output. For some asserts it uses ' setuptools==42.0.0' and for some 'setuptools==80.9.0'. I am not sure if the latest version line wouldn't drift if I add it to assert.

@abitrolly
Copy link
Contributor Author

Instead of detecting build backend module from its import path, then package name from the module name and then version from from package, just list installed versions of all build requirements.

* Building wheel...
    setuptools==80.9.0
```

@abitrolly abitrolly requested a review from layday January 2, 2026 09:22
importlib discovery API takes optional path to site-packages,
so it is possible to query versios of packages inside venv
installation without activating it first.

To pass purelib_dir to builder (so that it can query the versions
of installed build backend packages for debugging), the path saved
in env backend (pip or uv class), and then env reference is passed
and saved in the builder.
Default logger used in tests ignored origin
Tests use default logger that doesn't understand verbosity
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show build backend and backend commands in verbosity mode

2 participants