Imported Upstream version 61.1.0 upstream/61.1.0
authorJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:47 +0000 (17:02 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:47 +0000 (17:02 +0900)
18 files changed:
.bumpversion.cfg
CHANGES.rst
docs/userguide/dependency_management.rst
docs/userguide/pyproject_config.rst
docs/userguide/quickstart.rst
setup.cfg
setuptools/__init__.py
setuptools/_vendor/_validate_pyproject/NOTICE
setuptools/config/expand.py
setuptools/config/pyprojecttoml.py
setuptools/discovery.py
setuptools/tests/config/test_apply_pyprojecttoml.py
setuptools/tests/config/test_expand.py
setuptools/tests/config/test_pyprojecttoml.py
setuptools/tests/config/test_setupcfg.py
setuptools/tests/integration/test_pip_install_sdist.py
setuptools/tests/test_config_discovery.py
setuptools/tests/test_setuptools.py

index 1b6f189f330b719ca3ccbedb236167a6d4e7ec0f..30fa709d5d70bedb63e2a4a4b75aeb24c79d5143 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 61.0.0
+current_version = 61.1.0
 commit = True
 tag = True
 
index 3c6dc17a25ea6586e9c82ca82550d48cf8b75f09..2c76dad4ae833e02ad551590dc8480d0505e0979 100644 (file)
@@ -1,3 +1,41 @@
+v61.1.0
+-------
+
+
+Deprecations
+^^^^^^^^^^^^
+* #3206: Changed ``setuptools.convert_path`` to an internal function that is not exposed
+  as part of setuptools API.
+  Future releases of ``setuptools`` are likely to remove this function.
+
+Changes
+^^^^^^^
+* #3202: Changed behaviour of auto-discovery to not explicitly expand ``package_dir``
+  for flat-layouts and to not use relative paths starting with ``./``.
+* #3203: Prevented ``pyproject.toml`` parsing from overwriting
+  ``dist.include_package_data`` explicitly set in ``setup.py`` with default
+  value.
+* #3208: Added a warning for non existing files listed with the ``file`` directive in
+  ``setup.cfg`` and ``pyproject.toml``.
+* #3208: Added a default value for dynamic ``classifiers`` in ``pyproject.toml`` when
+  files are missing and errors being ignored.
+* #3211: Disabled auto-discovery when distribution class has a ``configuration``
+  attribute (e.g. when the ``setup.py`` script contains ``setup(...,
+  configuration=...)``).  This is done to ensure extension-only packages created
+  with ``numpy.distutils.misc_util.Configuration`` are not broken by the safe
+  guard
+  behaviour to avoid accidental multiple top-level packages in a flat-layout.
+
+  .. note::
+     Users that don't set ``packages``, ``py_modules``, or ``configuration`` are
+     still likely to observe the auto-discovery behavior, which may halt the
+     build if the project contains multiple directories and/or multiple Python
+     files directly under the project root.
+
+     To disable auto-discovery please explicitly set either ``packages`` or
+     ``py_modules``. Alternatively you can also configure :ref:`custom-discovery`.
+
+
 v61.0.0
 -------
 
@@ -26,6 +64,15 @@ Breaking Changes
 
   You can check details about the automatic discovery (and how to configure a
   different behaviour) in :doc:`/userguide/package_discovery`.
+* #3067: If the file ``pyproject.toml`` exists and it includes project
+  metadata/config (via ``[project]`` table or ``[tool.setuptools]``),
+  a series of new behaviors that are not backward compatible may take place:
+
+  - The default value of ``include_package_data`` will be considered to be ``True``.
+  - Setuptools will attempt to validate the ``pyproject.toml`` file according
+    to PEP 621 specification.
+  - The values specified in ``pyproject.toml`` will take precedence over those
+    specified in ``setup.cfg`` or ``setup.py``.
 
 Changes
 ^^^^^^^
@@ -63,6 +110,8 @@ Changes
 * #3066: Added vendored dependencies for :pypi:`tomli`, :pypi:`validate-pyproject`.
 
   These dependencies are used to read ``pyproject.toml`` files and validate them.
+* #3067: **[EXPERIMENTAL]** When using ``pyproject.toml`` metadata,
+  the default value of ``include_package_data`` is changed to ``True``.
 * #3068: **[EXPERIMENTAL]** Add support for ``pyproject.toml`` configuration
   (as introduced by :pep:`621`). Configuration parameters not covered by
   standards are handled in the ``[tool.setuptools]`` sub-table.
index 85545b7c175fbbd82f192738a1ca6117d6bfa693..279f794da127e90eb40d262760780eed09f560fe 100644 (file)
@@ -397,7 +397,7 @@ fail later).
 Python requirement
 ==================
 In some cases, you might need to specify the minimum required python version.
-This can be configured as shown in the example bellow.
+This can be configured as shown in the example below.
 
 .. tab:: setup.cfg
 
index 3988db2facb94de2dcd8baef09382af1a394a7d8..47c4511ebb4809f42a913cc7187b7c89e791e0d7 100644 (file)
@@ -29,7 +29,7 @@ a standard way of specifying *project metadata*.
 ``Setuptools`` has adopted this standard and will use the information contained
 in this file as an input in the build process.
 
-The example bellow illustrates how to write a ``pyproject.toml`` file that can
+The example below illustrates how to write a ``pyproject.toml`` file that can
 be used with ``setuptools``. It contains two TOML tables (identified by the
 ``[table-header]`` syntax): ``build-system`` and ``project``.
 The ``build-system`` table is used to tell the build frontend (e.g.
@@ -91,8 +91,8 @@ Key                       Value Type (TOML)           Notes
 ``zip-safe``              boolean                     If not specified, ``setuptools`` will try to guess
                                                       a reasonable default for the package
 ``eager-resources``       array
-``py-modules``            array                       See tip bellow
-``packages``              array or ``find`` directive See tip bellow
+``py-modules``            array                       See tip below
+``packages``              array or ``find`` directive See tip below
 ``package-dir``           table/inline-table          Used when explicitly listing ``packages``
 ``namespace-packages``    array                       Not necessary if you use :pep:`420`
 ``package-data``          table/inline-table          See :doc:`/userguide/datafiles`
index 3af8aaa8cec59e263722e1d1c16ba4bac7e2eea0..5be1078a6480a60e530a36d57fd5a9f8e61745df 100644 (file)
@@ -236,7 +236,7 @@ Dependency management
 =====================
 Packages built with ``setuptools`` can specify dependencies to be automatically
 installed when the package itself is installed.
-The example bellow show how to configure this kind of dependencies:
+The example below show how to configure this kind of dependencies:
 
 .. tab:: setup.cfg
 
index 6183185cdbd0819ac22c229beabbbf7843bc0ed1..e5c484bbf64cb555fe048b2cf7d7204ccbf3c659 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = setuptools
-version = 61.0.0
+version = 61.1.0
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages
index 187e7329f28ee3a803af86511338cf5da6303f13..502d2a2e1278a1bfe2b4a7b585ad9a9df94e3b3a 100644 (file)
@@ -3,11 +3,13 @@
 import functools
 import os
 import re
+import warnings
 
 import _distutils_hack.override  # noqa: F401
 
 import distutils.core
 from distutils.errors import DistutilsOptionError
+from distutils.util import convert_path as _convert_path
 
 from ._deprecation_warning import SetuptoolsDeprecationWarning
 
@@ -158,6 +160,19 @@ def findall(dir=os.curdir):
     return list(files)
 
 
+@functools.wraps(_convert_path)
+def convert_path(pathname):
+    from inspect import cleandoc
+
+    msg = """
+    The function `convert_path` is considered internal and not part of the public API.
+    Its direct usage by 3rd-party packages is considered deprecated and the function
+    may be removed in the future.
+    """
+    warnings.warn(cleandoc(msg), SetuptoolsDeprecationWarning)
+    return _convert_path(pathname)
+
+
 class sic(str):
     """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""
 
index fd64608bedb44d6bac55f108fd00348b2808707e..8ed8325e939da39b541065070dac6d86f7fb07bf 100644 (file)
@@ -31,7 +31,7 @@ by the same projects:
 - `__init__.py`
 - `fastjsonschema_validations.py`
 
-The relevant copyright notes and licenses are included bellow.
+The relevant copyright notes and licenses are included below.
 
 
 ***
index 3985040cbcffcfa95ffc755a10065e0167a055ba..ff9b2c9bdf64a5ac06b83f9b14136561a577d8be 100644 (file)
@@ -20,6 +20,7 @@ import importlib
 import io
 import os
 import sys
+import warnings
 from glob import iglob
 from configparser import ConfigParser
 from importlib.machinery import ModuleSpec
@@ -124,18 +125,25 @@ def read_files(filepaths: Union[str, bytes, Iterable[_Path]], root_dir=None) ->
 
     (By default ``root_dir`` is the current directory).
     """
-    if isinstance(filepaths, (str, bytes)):
-        filepaths = [filepaths]  # type: ignore
+    from setuptools.extern.more_itertools import always_iterable
 
     root_dir = os.path.abspath(root_dir or os.getcwd())
-    _filepaths = (os.path.join(root_dir, path) for path in filepaths)
+    _filepaths = (os.path.join(root_dir, path) for path in always_iterable(filepaths))
     return '\n'.join(
         _read_file(path)
-        for path in _filepaths
-        if _assert_local(path, root_dir) and os.path.isfile(path)
+        for path in _filter_existing_files(_filepaths)
+        if _assert_local(path, root_dir)
     )
 
 
+def _filter_existing_files(filepaths: Iterable[_Path]) -> Iterator[_Path]:
+    for path in filepaths:
+        if os.path.isfile(path):
+            yield path
+        else:
+            warnings.warn(f"File {path!r} cannot be found")
+
+
 def _read_file(filepath: Union[bytes, _Path]) -> str:
     with io.open(filepath, encoding='utf-8') as f:
         return f.read()
@@ -292,8 +300,8 @@ def find_packages(
 
     :rtype: list
     """
-
-    from setuptools.discovery import remove_nested_packages
+    from setuptools.discovery import construct_package_dir
+    from setuptools.extern.more_itertools import unique_everseen, always_iterable
 
     if namespaces:
         from setuptools.discovery import PEP420PackageFinder as PackageFinder
@@ -302,18 +310,18 @@ def find_packages(
 
     root_dir = root_dir or os.curdir
     where = kwargs.pop('where', ['.'])
-    if isinstance(where, str):
-        where = [where]
-
-    packages = []
+    packages: List[str] = []
     fill_package_dir = {} if fill_package_dir is None else fill_package_dir
-    for path in where:
-        pkgs = PackageFinder.find(_nest_path(root_dir, path), **kwargs)
+
+    for path in unique_everseen(always_iterable(where)):
+        package_path = _nest_path(root_dir, path)
+        pkgs = PackageFinder.find(package_path, **kwargs)
         packages.extend(pkgs)
-        if fill_package_dir.get("") != path:
-            parent_pkgs = remove_nested_packages(pkgs)
-            parent = {pkg: "/".join([path, *pkg.split(".")]) for pkg in parent_pkgs}
-            fill_package_dir.update(parent)
+        if pkgs and not (
+            fill_package_dir.get("") == path
+            or os.path.samefile(package_path, root_dir)
+        ):
+            fill_package_dir.update(construct_package_dir(pkgs, path))
 
     return packages
 
index bc76b1112ff33d72422e6cef2ee26b71cd6930d9..c7f8cb6e49dc20e1e3c83030a3a56f7adc0c09dc 100644 (file)
@@ -103,7 +103,10 @@ def read_configuration(
     # the default would be an improvement.
     # `ini2toml` backfills include_package_data=False when nothing is explicitly given,
     # therefore setting a default here is backwards compatible.
-    setuptools_table.setdefault("include-package-data", True)
+    if dist and getattr(dist, "include_package_data") is not None:
+        setuptools_table.setdefault("include-package-data", dist.include_package_data)
+    else:
+        setuptools_table.setdefault("include-package-data", True)
     # Persist changes:
     asdict["tool"] = tool_table
     tool_table["setuptools"] = setuptools_table
@@ -249,7 +252,7 @@ def _expand_all_dynamic(
 
     if "classifiers" in dynamic:
         value = _expand_dynamic(dynamic_cfg, "classifiers", pkg_dir, root_dir, ignore)
-        project_cfg["classifiers"] = value.splitlines()
+        project_cfg["classifiers"] = (value or "").splitlines()
 
 
 def _expand_dynamic(
index b787a0fdf05238e1064128a006919e37380dfd15..95c3c7f83ed4f2e60156c01fddd4e3bf2b6f32d2 100644 (file)
@@ -41,6 +41,7 @@ import itertools
 import os
 from fnmatch import fnmatchcase
 from glob import glob
+from pathlib import Path
 from typing import TYPE_CHECKING
 from typing import Callable, Dict, Iterator, Iterable, List, Optional, Tuple, Union
 
@@ -340,6 +341,8 @@ class ConfigDiscovery:
             self.dist.packages is not None
             or self.dist.py_modules is not None
             or ext_modules
+            or hasattr(self.dist, "configuration") and self.dist.configuration
+            # ^ Some projects use numpy.distutils.misc_util.Configuration
         )
 
     def _analyse_package_layout(self, ignore_ext_modules: bool) -> bool:
@@ -577,3 +580,9 @@ def find_package_path(name: str, package_dir: Dict[str, str], root_dir: _Path) -
 
     parent = package_dir.get("") or ""
     return os.path.join(root_dir, *parent.split("/"), *parts)
+
+
+def construct_package_dir(packages: List[str], package_path: _Path) -> Dict[str, str]:
+    parent_pkgs = remove_nested_packages(packages)
+    prefix = Path(package_path).parts
+    return {pkg: "/".join([*prefix, *pkg.split(".")]) for pkg in parent_pkgs}
index 38c9d1dccef5fc8433c437067584da094c28f6b5..044f801c083cd3b55af4b2c1e4bdebaa1966584b 100644 (file)
@@ -141,7 +141,7 @@ def _pep621_example_project(tmp_path, readme="README.rst"):
         text = text.replace(orig, subst)
     pyproject.write_text(text)
 
-    (tmp_path / "README.rst").write_text("hello world")
+    (tmp_path / readme).write_text("hello world")
     (tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---")
     (tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT)
     return pyproject
index d8078d0a8027165db0af57ae8429615711b80492..3a59edbb74158b16c5d921bb1f55a416d6cbd93a 100644 (file)
@@ -34,15 +34,19 @@ def test_glob_relative(tmp_path, monkeypatch):
 
 
 def test_read_files(tmp_path, monkeypatch):
+
+    dir_ = tmp_path / "dir_"
+    (tmp_path / "_dir").mkdir(exist_ok=True)
+    (tmp_path / "a.txt").touch()
     files = {
         "a.txt": "a",
         "dir1/b.txt": "b",
         "dir1/dir2/c.txt": "c"
     }
-    write_files(files, tmp_path)
+    write_files(files, dir_)
 
     with monkeypatch.context() as m:
-        m.chdir(tmp_path)
+        m.chdir(dir_)
         assert expand.read_files(list(files)) == "a\nb\nc"
 
         cannot_access_msg = r"Cannot access '.*\.\..a\.txt'"
@@ -50,9 +54,9 @@ def test_read_files(tmp_path, monkeypatch):
             expand.read_files(["../a.txt"])
 
     # Make sure the same APIs work outside cwd
-    assert expand.read_files(list(files), tmp_path) == "a\nb\nc"
+    assert expand.read_files(list(files), dir_) == "a\nb\nc"
     with pytest.raises(DistutilsOptionError, match=cannot_access_msg):
-        expand.read_files(["../a.txt"], tmp_path)
+        expand.read_files(["../a.txt"], dir_)
 
 
 class TestReadAttr:
index 0157b2adde17bc1b001f524f0ab273085dd898d1..1b5b90e202fac99d9f8ede567c1ee1fefd58f20a 100644 (file)
@@ -4,12 +4,18 @@ from inspect import cleandoc
 
 import pytest
 import tomli_w
+from path import Path as _Path
 
 from setuptools.config.pyprojecttoml import (
     read_configuration,
     expand_configuration,
     validate,
 )
+from setuptools.errors import OptionError
+
+
+import setuptools  # noqa -- force distutils.core to be patched
+import distutils.core
 
 EXAMPLE = """
 [project]
@@ -189,32 +195,63 @@ def test_expand_entry_point(tmp_path):
     assert "gui-scripts" not in expanded_project
 
 
-def test_dynamic_classifiers(tmp_path):
-    # Let's create a project example that has dynamic classifiers
-    # coming from a txt file.
-    create_example(tmp_path, "src")
-    classifiers = """\
-    Framework :: Flask
-    Programming Language :: Haskell
-    """
-    (tmp_path / "classifiers.txt").write_text(cleandoc(classifiers))
+class TestClassifiers:
+    def test_dynamic(self, tmp_path):
+        # Let's create a project example that has dynamic classifiers
+        # coming from a txt file.
+        create_example(tmp_path, "src")
+        classifiers = """\
+        Framework :: Flask
+        Programming Language :: Haskell
+        """
+        (tmp_path / "classifiers.txt").write_text(cleandoc(classifiers))
+
+        pyproject = tmp_path / "pyproject.toml"
+        config = read_configuration(pyproject, expand=False)
+        dynamic = config["project"]["dynamic"]
+        config["project"]["dynamic"] = list({*dynamic, "classifiers"})
+        dynamic_config = config["tool"]["setuptools"]["dynamic"]
+        dynamic_config["classifiers"] = {"file": "classifiers.txt"}
+
+        # When the configuration is expanded,
+        # each line of the file should be an different classifier.
+        validate(config, pyproject)
+        expanded = expand_configuration(config, tmp_path)
+
+        assert set(expanded["project"]["classifiers"]) == {
+            "Framework :: Flask",
+            "Programming Language :: Haskell",
+        }
 
-    pyproject = tmp_path / "pyproject.toml"
-    config = read_configuration(pyproject, expand=False)
-    dynamic = config["project"]["dynamic"]
-    config["project"]["dynamic"] = list({*dynamic, "classifiers"})
-    dynamic_config = config["tool"]["setuptools"]["dynamic"]
-    dynamic_config["classifiers"] = {"file": "classifiers.txt"}
+    def test_dynamic_without_config(self, tmp_path):
+        config = """
+        [project]
+        name = "myproj"
+        version = '42'
+        dynamic = ["classifiers"]
+        """
 
-    # When the configuration is expanded,
-    # each line of the file should be an different classifier.
-    validate(config, pyproject)
-    expanded = expand_configuration(config, tmp_path)
+        pyproject = tmp_path / "pyproject.toml"
+        pyproject.write_text(cleandoc(config))
+        with pytest.raises(OptionError, match="No configuration found"):
+            read_configuration(pyproject)
 
-    assert set(expanded["project"]["classifiers"]) == {
-        "Framework :: Flask",
-        "Programming Language :: Haskell",
-    }
+    def test_dynamic_without_file(self, tmp_path):
+        config = """
+        [project]
+        name = "myproj"
+        version = '42'
+        dynamic = ["classifiers"]
+
+        [tool.setuptools.dynamic]
+        classifiers = {file = ["classifiers.txt"]}
+        """
+
+        pyproject = tmp_path / "pyproject.toml"
+        pyproject.write_text(cleandoc(config))
+        with pytest.warns(UserWarning, match="File .*classifiers.txt. cannot be found"):
+            expanded = read_configuration(pyproject)
+        assert not expanded["project"]["classifiers"]
 
 
 @pytest.mark.parametrize(
@@ -292,3 +329,22 @@ def test_include_package_data_by_default(tmp_path, config):
 
     config = read_configuration(pyproject)
     assert config["tool"]["setuptools"]["include-package-data"] is True
+
+
+def test_include_package_data_in_setuppy(tmp_path):
+    """Builds with ``pyproject.toml`` should consider ``include_package_data`` set in
+    ``setup.py``.
+
+    See https://github.com/pypa/setuptools/issues/3197#issuecomment-1079023889
+    """
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text("[project]\nname = 'myproj'\nversion='42'\n")
+    setuppy = tmp_path / "setup.py"
+    setuppy.write_text("__import__('setuptools').setup(include_package_data=False)")
+
+    with _Path(tmp_path):
+        dist = distutils.core.run_setup("setup.py", {}, stop_after="config")
+
+    assert dist.get_name() == "myproj"
+    assert dist.get_version() == "42"
+    assert dist.include_package_data is False
index 8cd3ae7f6399b11aeae546d564b70082e25fcc8e..1f35f83630812aeba0afb90f3aaea393bfc36c0f 100644 (file)
@@ -185,9 +185,12 @@ class TestMetadata:
 
     def test_file_sandboxed(self, tmpdir):
 
-        fake_env(tmpdir, '[metadata]\n' 'long_description = file: ../../README\n')
+        tmpdir.ensure("README")
+        project = tmpdir.join('depth1', 'depth2')
+        project.ensure(dir=True)
+        fake_env(project, '[metadata]\n' 'long_description = file: ../../README\n')
 
-        with get_dist(tmpdir, parse=False) as dist:
+        with get_dist(project, parse=False) as dist:
             with pytest.raises(DistutilsOptionError):
                 dist.parse_config_files()  # file: out of sandbox
 
index 0177c22dd47fb22fad1ec82297a12c1c2fb1f6c1..9d11047bc5baea0419b5bad57a09ec754eab80f5 100644 (file)
@@ -53,7 +53,7 @@ EXAMPLES = [
     ("brotli", LATEST),  # not in the list but used by urllib3
 
     # When adding packages to this list, make sure they expose a `__version__`
-    # attribute, or modify the tests bellow
+    # attribute, or modify the tests below
 ]
 
 
index e6ed632e9fc7aec9182c257515fe480b9c4968c6..fac365f4107b12b86e7123d8f54af1f09f3b5344 100644 (file)
@@ -12,6 +12,7 @@ import setuptools  # noqa -- force distutils.core to be patched
 import distutils.core
 
 import pytest
+import jaraco.path
 from path import Path as _Path
 
 from .contexts import quiet
@@ -398,6 +399,115 @@ class TestWithCExtension:
             _get_dist(tmp_path, {})
 
 
+class TestWithPackageData:
+    def _simulate_package_with_data_files(self, tmp_path, src_root):
+        files = [
+            f"{src_root}/proj/__init__.py",
+            f"{src_root}/proj/file1.txt",
+            f"{src_root}/proj/nested/file2.txt",
+        ]
+        _populate_project_dir(tmp_path, files, {})
+
+        manifest = """
+            global-include *.py *.txt
+        """
+        (tmp_path / "MANIFEST.in").write_text(DALS(manifest))
+
+    EXAMPLE_SETUPCFG = """
+    [metadata]
+    name = proj
+    version = 42
+
+    [options]
+    include_package_data = True
+    """
+    EXAMPLE_PYPROJECT = """
+    [project]
+    name = "proj"
+    version = "42"
+    """
+
+    PYPROJECT_PACKAGE_DIR = """
+    [tool.setuptools]
+    package-dir = {"" = "src"}
+    """
+
+    @pytest.mark.parametrize(
+        "src_root, files",
+        [
+            (".", {"setup.cfg": DALS(EXAMPLE_SETUPCFG)}),
+            (".", {"pyproject.toml": DALS(EXAMPLE_PYPROJECT)}),
+            ("src", {"setup.cfg": DALS(EXAMPLE_SETUPCFG)}),
+            ("src", {"pyproject.toml": DALS(EXAMPLE_PYPROJECT)}),
+            (
+                "src",
+                {
+                    "setup.cfg": DALS(EXAMPLE_SETUPCFG) + DALS(
+                        """
+                        packages = find:
+                        package_dir =
+                            =src
+
+                        [options.packages.find]
+                        where = src
+                        """
+                    )
+                }
+            ),
+            (
+                "src",
+                {
+                    "pyproject.toml": DALS(EXAMPLE_PYPROJECT) + DALS(
+                        """
+                        [tool.setuptools]
+                        package-dir = {"" = "src"}
+                        """
+                    )
+                },
+            ),
+        ]
+    )
+    def test_include_package_data(self, tmp_path, src_root, files):
+        """
+        Make sure auto-discovery does not affect package include_package_data.
+        See issue #3196.
+        """
+        jaraco.path.build(files, prefix=str(tmp_path))
+        self._simulate_package_with_data_files(tmp_path, src_root)
+
+        expected = {
+            os.path.normpath(f"{src_root}/proj/file1.txt").replace(os.sep, "/"),
+            os.path.normpath(f"{src_root}/proj/nested/file2.txt").replace(os.sep, "/"),
+        }
+
+        _run_build(tmp_path)
+
+        sdist_files = get_sdist_members(next(tmp_path.glob("dist/*.tar.gz")))
+        print("~~~~~ sdist_members ~~~~~")
+        print('\n'.join(sdist_files))
+        assert sdist_files >= expected
+
+        wheel_files = get_wheel_members(next(tmp_path.glob("dist/*.whl")))
+        print("~~~~~ wheel_members ~~~~~")
+        print('\n'.join(wheel_files))
+        orig_files = {f.replace("src/", "").replace("lib/", "") for f in expected}
+        assert wheel_files >= orig_files
+
+
+def test_compatible_with_numpy_configuration(tmp_path):
+    files = [
+        "dir1/__init__.py",
+        "dir2/__init__.py",
+        "file.py",
+    ]
+    _populate_project_dir(tmp_path, files, {})
+    dist = Distribution({})
+    dist.configuration = object()
+    dist.set_defaults()
+    assert dist.py_modules is None
+    assert dist.packages is None
+
+
 def _populate_project_dir(root, files, options):
     # NOTE: Currently pypa/build will refuse to build the project if no
     # `pyproject.toml` or `setup.py` is found. So it is impossible to do
@@ -437,7 +547,7 @@ def _write_setupcfg(root, options):
 
 def _run_build(path, *flags):
     cmd = [sys.executable, "-m", "build", "--no-isolation", *flags, str(path)]
-    return run(cmd, env={'DISTUTILS_DEBUG': '1'})
+    return run(cmd, env={'DISTUTILS_DEBUG': ''})
 
 
 def _get_dist(dist_path, attrs):
index b97faf17bcba64d4d139a087b2f039baf238d2ef..0640f49da1182b7b1642f9f7a408059d857e3892 100644 (file)
@@ -303,3 +303,8 @@ def test_its_own_wheel_does_not_contain_tests(setuptools_wheel):
 
     for member in contents:
         assert '/tests/' not in member
+
+
+def test_convert_path_deprecated():
+    with pytest.warns(setuptools.SetuptoolsDeprecationWarning):
+        setuptools.convert_path('setuptools/tests')