Skip to content

[FR] Allow glob values (or similar) for ext-modules in pyproject.toml #5092

@sakgoyal

Description

@sakgoyal

setuptools version

80.9.0

Python version

3.13 and 3.14

OS

Windows 10 64bit

Additional environment information

No response

Description

Setting the ext-modules in the pyproject.toml causes the parser to crash and installation fails. I also get errors in the editor. but the documentation specifies this is allowed (but experimental)

Expected behavior

It does not crash and it is equivilant to the following in setup.py:

from glob import glob
from setuptools import Extension, setup

setup(
    ext_modules=[
    Extension('pypblib.pblib', sources=
            glob('./Modules/pblib/*.cpp') +
            glob('./External/pblib/*.cpp') +
            glob('./External/pblib/encoder/*.cpp'),
        include_dirs=['./External/pblib', './Modules/pblib'],
        extra_compile_args=get_compiler_args(),
        language='c++'
    )

How to Reproduce

This causes the install to crash

[tool.setuptools]
ext-modules = [
  { name = "pypblib.pblib",
    sources = [
      "Modules/pblib/*.cpp",
      "External/pblib/*.cpp",
      "External/pblib/encoder/*.cpp",
    ],
    include-dirs = ["External/pblib", "Modules/pblib"],
    language = "c++"
  }
]

Output

For python 3.13:

pip install -e .
Defaulting to user installation because normal site-packages is not writeable
Obtaining file:///C:/Users/Saksham/Documents/projects/pypblib
ERROR: Exception:
Traceback (most recent call last):
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\cli\base_command.py", line 107, in _run_wrapper
    status = _inner_run()
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\cli\base_command.py", line 98, in _inner_run
    return self.run(options, args)
           ~~~~~~~~^^^^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\cli\req_command.py", line 71, in wrapper
    return func(self, options, args)
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\commands\install.py", line 393, in run
    requirement_set = resolver.resolve(
        reqs, check_supported_wheels=not options.target_dir
    )
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 79, in resolve
    collected = self.factory.collect_root_requirements(root_reqs)
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 538, in collect_root_requirements
    reqs = list(
        self._make_requirements_from_install_req(
    ...<2 lines>...
        )
    )
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 494, in _make_requirements_from_install_req
    cand = self._make_base_candidate_from_link(
        ireq.link,
    ...<2 lines>...
        version=None,
    )
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 205, in _make_base_candidate_from_link
    self._editable_candidate_cache[link] = EditableCandidate(
                                           ~~~~~~~~~~~~~~~~~^
        link,
        ^^^^^
    ...<3 lines>...
        version=version,
        ^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 334, in __init__
    super().__init__(
    ~~~~~~~~~~~~~~~~^
        link=link,
        ^^^^^^^^^^
    ...<4 lines>...
        version=version,
        ^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 162, in __init__
    self.dist = self._prepare()
                ~~~~~~~~~~~~~^^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 239, in _prepare
    dist = self._prepare_distribution()
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 344, in _prepare_distribution
    return self._factory.preparer.prepare_editable_requirement(self._ireq)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\operations\prepare.py", line 708, in prepare_editable_requirement
    dist = _get_prepared_distribution(
        req,
    ...<3 lines>...
        self.check_build_deps,
    )
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\operations\prepare.py", line 77, in _get_prepared_distribution
    abstract_dist.prepare_distribution_metadata(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        build_env_installer, build_isolation, check_build_deps
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\distributions\sdist.py", line 42, in prepare_distribution_metadata
    self.req.load_pyproject_toml()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\req\req_install.py", line 515, in load_pyproject_toml
    pyproject_toml_data = load_pyproject_toml(
        self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self)
    )
  File "C:\Users\Saksham\AppData\Roaming\Python\Python313\site-packages\pip\_internal\pyproject.py", line 67, in load_pyproject_toml
    pp_toml = tomllib.loads(f.read())
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 102, in loads
    pos = key_value_rule(src, pos, out, header, parse_float)
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 326, in key_value_rule
    pos, key, value = parse_key_value_pair(src, pos, parse_float)
                      ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 369, in parse_key_value_pair
    pos, value = parse_value(src, pos, parse_float)
                 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 616, in parse_value
    return parse_array(src, pos, parse_float)
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 420, in parse_array
    pos, val = parse_value(src, pos, parse_float)
               ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 620, in parse_value
    return parse_inline_table(src, pos, parse_float)
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 445, in parse_inline_table
    pos, key, value = parse_key_value_pair(src, pos, parse_float)
                      ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 360, in parse_key_value_pair
    pos, key = parse_key(src, pos)
               ~~~~~~~~~^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 374, in parse_key
    pos, key_part = parse_key_part(src, pos)
                    ~~~~~~~~~~~~~~^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\tomllib\_parser.py", line 404, in parse_key_part
    raise suffixed_err(src, pos, "Invalid initial character for a key part")
tomllib.TOMLDecodeError: Invalid initial character for a key part (at line 35, column 28)

For python 3.14:

py -V:3.14 -m pip install -e .
Obtaining file:///C:/Users/Saksham/Documents/projects/pypblib
ERROR: Exception:
Traceback (most recent call last):
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\cli\base_command.py", line 107, in _run_wrapper
    status = _inner_run()
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\cli\base_command.py", line 98, in _inner_run
    return self.run(options, args)
           ~~~~~~~~^^^^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\cli\req_command.py", line 71, in wrapper
    return func(self, options, args)
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\commands\install.py", line 393, in run
    requirement_set = resolver.resolve(
        reqs, check_supported_wheels=not options.target_dir
    )
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 79, in resolve
    collected = self.factory.collect_root_requirements(root_reqs)
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 538, in collect_root_requirements
    reqs = list(
        self._make_requirements_from_install_req(
    ...<2 lines>...
        )
    )
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 494, in _make_requirements_from_install_req
    cand = self._make_base_candidate_from_link(
        ireq.link,
    ...<2 lines>...
        version=None,
    )
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 205, in _make_base_candidate_from_link
    self._editable_candidate_cache[link] = EditableCandidate(
                                           ~~~~~~~~~~~~~~~~~^
        link,
        ^^^^^
    ...<3 lines>...
        version=version,
        ^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 334, in __init__
    super().__init__(
    ~~~~~~~~~~~~~~~~^
        link=link,
        ^^^^^^^^^^
    ...<4 lines>...
        version=version,
        ^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 162, in __init__
    self.dist = self._prepare()
                ~~~~~~~~~~~~~^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 239, in _prepare
    dist = self._prepare_distribution()
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 344, in _prepare_distribution
    return self._factory.preparer.prepare_editable_requirement(self._ireq)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\operations\prepare.py", line 708, in prepare_editable_requirement
    dist = _get_prepared_distribution(
        req,
    ...<3 lines>...
        self.check_build_deps,
    )
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\operations\prepare.py", line 77, in _get_prepared_distribution
    abstract_dist.prepare_distribution_metadata(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        build_env_installer, build_isolation, check_build_deps
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\distributions\sdist.py", line 42, in prepare_distribution_metadata
    self.req.load_pyproject_toml()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\req\req_install.py", line 515, in load_pyproject_toml
    pyproject_toml_data = load_pyproject_toml(
        self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self)
    )
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\site-packages\pip\_internal\pyproject.py", line 67, in load_pyproject_toml
    pp_toml = tomllib.loads(f.read())
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 174, in loads
    pos = key_value_rule(src, pos, out, header, parse_float)
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 403, in key_value_rule
    pos, key, value = parse_key_value_pair(src, pos, parse_float)
                      ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 446, in parse_key_value_pair
    pos, value = parse_value(src, pos, parse_float)
                 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 695, in parse_value
    return parse_array(src, pos, parse_float)
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 497, in parse_array
    pos, val = parse_value(src, pos, parse_float)
               ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 699, in parse_value
    return parse_inline_table(src, pos, parse_float)
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 522, in parse_inline_table
    pos, key, value = parse_key_value_pair(src, pos, parse_float)
                      ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 437, in parse_key_value_pair
    pos, key = parse_key(src, pos)
               ~~~~~~~~~^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 451, in parse_key
    pos, key_part = parse_key_part(src, pos)
                    ~~~~~~~~~~~~~~^^^^^^^^^^
  File "C:\Users\Saksham\AppData\Local\Python\pythoncore-3.14-64\Lib\tomllib\_parser.py", line 481, in parse_key_part
    raise TOMLDecodeError("Invalid initial character for a key part", src, pos)
tomllib.TOMLDecodeError: Invalid initial character for a key part (at line 35, column 28)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions