pypa / setuptools

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

[BUG] PEP621: Warnings/errors when handling a `long_description` (`readme`) dynamically supplied from within `setup.py` #3244

Closed bskinn closed 2 years ago

bskinn commented 2 years ago

setuptools version

setuptools===61.3.1

Python version

Python 3.9.12

OS

Debian Linux bullseye (Debian 11)

Additional environment information

No response

Description

This is downstream of #1688 & the recent experimental PEP621 implementation.

For my Python projects on PyPI, I use a setuptools packaging workflow where I dynamically modify the contents of README.md (alternatively README.rst) as part of setup.py, before passing those contents into the long_description argument to setup(). One example of this is here, where I'm changing some documentation links -- as README sits in the repo, they point to the latest docs; when packaged for PyPI, though, I want the links point to a specific historical vx.y docs version.

I'm attempting to convert this workflow to PEP621. In doing so, it appears that it's currently impossible to configure setuptools to properly accept this state of affairs.

  1. If I don't declare readme in project.dynamic, I get a _WouldIgnoreField warning.
  2. If I do declare readme in project.dynamic, I get a KeyError from an attempted access to dynamic_cfg["readme"]
  3. If I attempt to declare project.dynamic = ["version", "long_description"], instead of ["version", "readme"], I get ValueError: invalid pyproject.toml config: project.dynamic[1]

Expected behavior

I would expect it to be necessary to declare my long_description as dynamic, since I am generating it dynamically within setup.py. Thus, (1.) above (and the future behavior of completely rejecting a dynamic readme when it is not declared as dynamic) is expected behavior.

I would expect (2.) to result in a successful build. I would guess that the PEP621 project.readme field, and in particular the readme element within dynamic_cfg, is not being correctly linked to/populated with the contents of long_description coming from setup.py.

Given that the pyproject.toml table key for project.dynamic expects to see readme and not long_description, the error in (3.) is expected.

How to Reproduce

Setup

  1. Clone and enter the demonstration repo at https://github.com/bskinn/setuptools-pep621-readme.
  2. Create a virtual environment for the repo and pip install build; or, just use an externally-installed version of build (e.g., via pipx).

Repro

  1. $ git checkout readme-static && python -m build
    • readme is not included in project.dynamic; (1.) above
  2. $ git checkout readme-dynamic && python -m build
    • readme is included in project.dynamic; (2.) above
  3. $ git checkout long-desc-in-dynamic && python -m build
    • long_description is included in project.dynamic, readme is omitted; (3.) above

Output


# skipping setup steps since they should be trivial

# Repro Step (1.)
$ git checkout readme-static && pm build
HEAD is now at 127ba90 Initial commit

* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools>=61, trove-classifiers, wheel)
* Getting dependencies for sdist...
/tmp/build-env-lntkqmmu/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:102: _ExperimentalProjectMetadata: Support for project metadata in `pyproject.toml` is still experimental and may be removed (or change) in future releases.
  warnings.warn(msg, _ExperimentalProjectMetadata)
/tmp/build-env-lntkqmmu/lib/python3.9/site-packages/setuptools/config/_apply_pyprojecttoml.py:93: _WouldIgnoreField: !!

    ##########################################################################
    # configuration would be ignored/result in error due to `pyproject.toml` #
    ##########################################################################

    The following seems to be defined outside of `pyproject.toml`:

    `readme = 'This is a test readme.'`

    According to the spec (see the link bellow), however, setuptools CANNOT
    consider this value unless 'readme' is listed as `dynamic`.

    https://packaging.python.org/en/latest/specifications/declaring-project-metadata/

    For the time being, `setuptools` will still consider the given value (as a
    **transitional** measure), but please note that future releases of setuptools will
    follow strictly the standard.

    To prevent this warning, you can list 'readme' under `dynamic` or alternatively
    remove the `[project]` table from your file and rely entirely on other means of
    configuration.

!!

  warnings.warn(msg, _WouldIgnoreField)
running egg_info
writing setuptools_pep621_readme.egg-info/PKG-INFO
writing dependency_links to setuptools_pep621_readme.egg-info/dependency_links.txt
writing top-level names to setuptools_pep621_readme.egg-info/top_level.txt
reading manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
adding license file 'LICENSE.txt'
writing manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
* Building sdist...
/tmp/build-env-lntkqmmu/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:102: _ExperimentalProjectMetadata: Support for project metadata in `pyproject.toml` is still experimental and may be removed (or change) in future releases.
  warnings.warn(msg, _ExperimentalProjectMetadata)
/tmp/build-env-lntkqmmu/lib/python3.9/site-packages/setuptools/config/_apply_pyprojecttoml.py:93: _WouldIgnoreField: !!

    ##########################################################################
    # configuration would be ignored/result in error due to `pyproject.toml` #
    ##########################################################################

    The following seems to be defined outside of `pyproject.toml`:

    `readme = 'This is a test readme.'`

    According to the spec (see the link bellow), however, setuptools CANNOT
    consider this value unless 'readme' is listed as `dynamic`.

    https://packaging.python.org/en/latest/specifications/declaring-project-metadata/

    For the time being, `setuptools` will still consider the given value (as a
    **transitional** measure), but please note that future releases of setuptools will
    follow strictly the standard.

    To prevent this warning, you can list 'readme' under `dynamic` or alternatively
    remove the `[project]` table from your file and rely entirely on other means of
    configuration.

!!

  warnings.warn(msg, _WouldIgnoreField)
running sdist
running egg_info
writing setuptools_pep621_readme.egg-info/PKG-INFO
writing dependency_links to setuptools_pep621_readme.egg-info/dependency_links.txt
writing top-level names to setuptools_pep621_readme.egg-info/top_level.txt
reading manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
adding license file 'LICENSE.txt'
writing manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
running check
creating setuptools-pep621-readme-0.1.dev1
creating setuptools-pep621-readme-0.1.dev1/setuptools_pep621_readme.egg-info
copying files to setuptools-pep621-readme-0.1.dev1...
copying LICENSE.txt -> setuptools-pep621-readme-0.1.dev1
copying README.md -> setuptools-pep621-readme-0.1.dev1
copying pyproject.toml -> setuptools-pep621-readme-0.1.dev1
copying setup.py -> setuptools-pep621-readme-0.1.dev1
copying setuptools_pep621_readme.egg-info/PKG-INFO -> setuptools-pep621-readme-0.1.dev1/setuptools_pep621_readme.egg-info
copying setuptools_pep621_readme.egg-info/SOURCES.txt -> setuptools-pep621-readme-0.1.dev1/setuptools_pep621_readme.egg-info
copying setuptools_pep621_readme.egg-info/dependency_links.txt -> setuptools-pep621-readme-0.1.dev1/setuptools_pep621_readme.egg-info
copying setuptools_pep621_readme.egg-info/top_level.txt -> setuptools-pep621-readme-0.1.dev1/setuptools_pep621_readme.egg-info
Writing setuptools-pep621-readme-0.1.dev1/setup.cfg
Creating tar archive
removing 'setuptools-pep621-readme-0.1.dev1' (and everything under it)
* Building wheel from sdist
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools>=61, trove-classifiers, wheel)
* Getting dependencies for wheel...
/tmp/build-env-n_v08h44/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:102: _ExperimentalProjectMetadata: Support for project metadata in `pyproject.toml` is still experimental and may be removed (or change) in future releases.
  warnings.warn(msg, _ExperimentalProjectMetadata)
/tmp/build-env-n_v08h44/lib/python3.9/site-packages/setuptools/config/_apply_pyprojecttoml.py:93: _WouldIgnoreField: !!

    ##########################################################################
    # configuration would be ignored/result in error due to `pyproject.toml` #
    ##########################################################################

    The following seems to be defined outside of `pyproject.toml`:

    `readme = 'This is a test readme.'`

    According to the spec (see the link bellow), however, setuptools CANNOT
    consider this value unless 'readme' is listed as `dynamic`.

    https://packaging.python.org/en/latest/specifications/declaring-project-metadata/

    For the time being, `setuptools` will still consider the given value (as a
    **transitional** measure), but please note that future releases of setuptools will
    follow strictly the standard.

    To prevent this warning, you can list 'readme' under `dynamic` or alternatively
    remove the `[project]` table from your file and rely entirely on other means of
    configuration.

!!

  warnings.warn(msg, _WouldIgnoreField)
running egg_info
writing setuptools_pep621_readme.egg-info/PKG-INFO
writing dependency_links to setuptools_pep621_readme.egg-info/dependency_links.txt
writing top-level names to setuptools_pep621_readme.egg-info/top_level.txt
reading manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
adding license file 'LICENSE.txt'
writing manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
* Installing packages in isolated environment... (wheel)
* Building wheel...
/tmp/build-env-n_v08h44/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:102: _ExperimentalProjectMetadata: Support for project metadata in `pyproject.toml` is still experimental and may be removed (or change) in future releases.
  warnings.warn(msg, _ExperimentalProjectMetadata)
/tmp/build-env-n_v08h44/lib/python3.9/site-packages/setuptools/config/_apply_pyprojecttoml.py:93: _WouldIgnoreField: !!

    ##########################################################################
    # configuration would be ignored/result in error due to `pyproject.toml` #
    ##########################################################################

    The following seems to be defined outside of `pyproject.toml`:

    `readme = 'This is a test readme.'`

    According to the spec (see the link bellow), however, setuptools CANNOT
    consider this value unless 'readme' is listed as `dynamic`.

    https://packaging.python.org/en/latest/specifications/declaring-project-metadata/

    For the time being, `setuptools` will still consider the given value (as a
    **transitional** measure), but please note that future releases of setuptools will
    follow strictly the standard.

    To prevent this warning, you can list 'readme' under `dynamic` or alternatively
    remove the `[project]` table from your file and rely entirely on other means of
    configuration.

!!

  warnings.warn(msg, _WouldIgnoreField)
running bdist_wheel
running build
installing to build/bdist.linux-x86_64/wheel
running install
running install_egg_info
running egg_info
writing setuptools_pep621_readme.egg-info/PKG-INFO
writing dependency_links to setuptools_pep621_readme.egg-info/dependency_links.txt
writing top-level names to setuptools_pep621_readme.egg-info/top_level.txt
reading manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
adding license file 'LICENSE.txt'
writing manifest file 'setuptools_pep621_readme.egg-info/SOURCES.txt'
Copying setuptools_pep621_readme.egg-info to build/bdist.linux-x86_64/wheel/setuptools_pep621_readme-0.1.dev1-py3.9.egg-info
running install_scripts
adding license file "LICENSE.txt" (matched pattern "LICEN[CS]E*")
creating build/bdist.linux-x86_64/wheel/setuptools_pep621_readme-0.1.dev1.dist-info/WHEEL
creating '/.../setuptools-pep621-readme/dist/tmp8qw1jvw5/setuptools_pep621_readme-0.1.dev1-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'setuptools_pep621_readme-0.1.dev1.dist-info/LICENSE.txt'
adding 'setuptools_pep621_readme-0.1.dev1.dist-info/METADATA'
adding 'setuptools_pep621_readme-0.1.dev1.dist-info/WHEEL'
adding 'setuptools_pep621_readme-0.1.dev1.dist-info/top_level.txt'
adding 'setuptools_pep621_readme-0.1.dev1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built setuptools-pep621-readme-0.1.dev1.tar.gz and setuptools_pep621_readme-0.1.dev1-py3-none-any.whl

# Repro Step (2.)
$ git checkout readme-dynamic && python -m build
Previous HEAD position was 127ba90 Initial commit
HEAD is now at 84b342f Change to dynamic `readme`

HEAD is now at 84b342f Change to dynamic `readme`
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools>=61, trove-classifiers, wheel)
* Getting dependencies for sdist...
/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:102: _ExperimentalProjectMetadata: Support for project metadata in `pyproject.toml` is still experimental and may be removed (or change) in future releases.
  warnings.warn(msg, _ExperimentalProjectMetadata)
Traceback (most recent call last):
  File "/.../setuptools-pep621-readme/env/lib/python3.9/site-packages/pep517/in_process/_in_process.py", line 363, in <module>
    main()
  File "/.../setuptools-pep621-readme/env/lib/python3.9/site-packages/pep517/in_process/_in_process.py", line 345, in main
    json_out['return_val'] = hook(**hook_input['kwargs'])
  File "/.../setuptools-pep621-readme/env/lib/python3.9/site-packages/pep517/in_process/_in_process.py", line 297, in get_requires_for_build_sdist
    return hook(config_settings)
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/build_meta.py", line 181, in get_requires_for_build_sdist
    return self._get_build_requires(config_settings, requirements=[])
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/build_meta.py", line 159, in _get_build_requires
    self.run_setup()
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/build_meta.py", line 174, in run_setup
    exec(compile(code, __file__, 'exec'), locals())
  File "setup.py", line 12, in <module>
    setup(
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/__init__.py", line 87, in setup
    return distutils.core.setup(**attrs)
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/_distutils/core.py", line 122, in setup
    dist.parse_config_files()
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/dist.py", line 854, in parse_config_files
    pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 54, in apply_configuration
    config = read_configuration(filepath, True, ignore_option_errors, dist)
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 134, in read_configuration
    return expand_configuration(asdict, root_dir, ignore_option_errors, dist)
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 189, in expand_configuration
    return _ConfigExpander(config, root_dir, ignore_option_errors, dist).expand()
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 236, in expand
    self._expand_all_dynamic(dist, package_dir)
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 279, in _expand_all_dynamic
    readme=self._obtain_readme(dist),
  File "/tmp/build-env-uvtxu5az/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 323, in _obtain_readme
    "content-type": dynamic_cfg["readme"].get("content-type", "text/x-rst"),
KeyError: 'readme'

# Repro Step (3.)
$ git checkout long-desc-in-dynamic && python -m build
Previous HEAD position was 127ba90 Initial commit
HEAD is now at c5b9d79 Switch to `long_description` in `dynamic`
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools>=61, trove-classifiers, wheel)
* Getting dependencies for sdist...
/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py:102: _ExperimentalProjectMetadata: Support for project metadata in `pyproject.toml` is still experimental and may be removed (or change) in future releases.
  warnings.warn(msg, _ExperimentalProjectMetadata)
configuration error: `project.dynamic[1]` must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']
GIVEN VALUE:
    "long_description"

OFFENDING RULE: 'enum'

DEFINITION:
    {
        "enum": [
            "version",
            "description",
            "readme",
            "requires-python",
            "license",
            "authors",
            "maintainers",
            "keywords",
            "classifiers",
            "urls",
            "scripts",
            "gui-scripts",
            "entry-points",
            "dependencies",
            "optional-dependencies"
        ]
    }
Traceback (most recent call last):
  File "/.../setuptools-pep621-readme/env/lib/python3.9/site-packages/pep517/in_process/_in_process.py", line 363, in <module>
    main()
  File "/.../setuptools-pep621-readme/env/lib/python3.9/site-packages/pep517/in_process/_in_process.py", line 345, in main
    json_out['return_val'] = hook(**hook_input['kwargs'])
  File "/.../setuptools-pep621-readme/env/lib/python3.9/site-packages/pep517/in_process/_in_process.py", line 297, in get_requires_for_build_sdist
    return hook(config_settings)
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/build_meta.py", line 181, in get_requires_for_build_sdist
    return self._get_build_requires(config_settings, requirements=[])
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/build_meta.py", line 159, in _get_build_requires
    self.run_setup()
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/build_meta.py", line 174, in run_setup
    exec(compile(code, __file__, 'exec'), locals())
  File "setup.py", line 12, in <module>
    setup(
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/__init__.py", line 87, in setup
    return distutils.core.setup(**attrs)
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/_distutils/core.py", line 122, in setup
    dist.parse_config_files()
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/dist.py", line 854, in parse_config_files
    pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 54, in apply_configuration
    config = read_configuration(filepath, True, ignore_option_errors, dist)
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 120, in read_configuration
    validate(subset, filepath)
  File "/tmp/build-env-yhivke4e/lib/python3.9/site-packages/setuptools/config/pyprojecttoml.py", line 43, in validate
    raise error from None
ValueError: invalid pyproject.toml config: `project.dynamic[1]`

ERROR Backend subproccess exited when trying to invoke get_requires_for_build_sdist
bskinn commented 2 years ago

cc @abravalheri

abravalheri commented 2 years ago

Hi @bskinn, thank you very much for reporting this, the option in (2) should succeed. I think that is the compliant/preferred way forward. I will have a look on this (probably tomorrow evening).

bskinn commented 2 years ago

Thanks for the super-quick fix, @abravalheri! v62 builds great in configuration (2).

abravalheri commented 2 years ago

Glad to know it is working @bskinn, thank you very much for the feedback.