summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
authorAnderson Bravalheri <andersonbravalheri@gmail.com>2022-06-18 09:39:27 +0100
committerAnderson Bravalheri <andersonbravalheri@gmail.com>2022-06-18 09:39:27 +0100
commit84d5133b63dd6c2f64cc38ced7ec8f93f1725cac (patch)
tree11f6cf3d082a00b4b56047454e8cfb2076a2f1cc /setuptools
parentecdeb225804010e2f68c7ec5d72e39364873324d (diff)
downloadpython-setuptools-git-84d5133b63dd6c2f64cc38ced7ec8f93f1725cac.tar.gz
build_py: Allow get_outputs() to work without re-running egg-info
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/command/build_py.py17
-rw-r--r--setuptools/tests/test_build_py.py180
2 files changed, 132 insertions, 65 deletions
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index 9575cdf8..dab81327 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -29,6 +29,8 @@ class build_py(orig.build_py):
'py_modules' and 'packages' in the same setup operation.
"""
+ existing_egg_info_dir = None #: Private API, setuptools internal use only.
+
def finalize_options(self):
orig.build_py.finalize_options(self)
self.package_data = self.distribution.package_data
@@ -143,10 +145,19 @@ class build_py(orig.build_py):
# Locate package source directory
src_dirs[assert_relative(self.get_package_dir(package))] = package
- self.run_command('egg_info')
+ if (
+ getattr(self, 'existing_egg_info_dir', None)
+ and Path(self.existing_egg_info_dir, "SOURCES.txt").exists()
+ ):
+ manifest = Path(self.existing_egg_info_dir, "SOURCES.txt")
+ files = manifest.read_text(encoding="utf-8").splitlines()
+ else:
+ self.run_command('egg_info')
+ ei_cmd = self.get_finalized_command('egg_info')
+ files = ei_cmd.filelist.files
+
check = _IncludePackageDataAbuse()
- ei_cmd = self.get_finalized_command('egg_info')
- for path in ei_cmd.filelist.files:
+ for path in files:
d, f = os.path.split(assert_relative(path))
prev = None
oldf = f
diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py
index 13fa64de..87d4bcfe 100644
--- a/setuptools/tests/test_build_py.py
+++ b/setuptools/tests/test_build_py.py
@@ -1,10 +1,11 @@
import os
import stat
import shutil
+from pathlib import Path
+from unittest.mock import Mock
import pytest
import jaraco.path
-from path import Path
from setuptools import SetuptoolsDeprecationWarning
from setuptools.dist import Distribution
@@ -109,67 +110,122 @@ def test_executable_data(tmpdir_cwd):
"Script is not executable"
-def test_excluded_subpackages(tmp_path):
- files = {
- "setup.cfg": DALS("""
- [metadata]
- name = mypkg
- version = 42
+EXAMPLE_WITH_MANIFEST = {
+ "setup.cfg": DALS("""
+ [metadata]
+ name = mypkg
+ version = 42
- [options]
- include_package_data = True
- packages = find:
+ [options]
+ include_package_data = True
+ packages = find:
- [options.packages.find]
- exclude = *.tests*
- """),
- "mypkg": {
+ [options.packages.find]
+ exclude = *.tests*
+ """),
+ "mypkg": {
+ "__init__.py": "",
+ "resource_file.txt": "",
+ "tests": {
"__init__.py": "",
- "resource_file.txt": "",
- "tests": {
- "__init__.py": "",
- "test_mypkg.py": "",
- "test_file.txt": "",
- }
- },
- "MANIFEST.in": DALS("""
- global-include *.py *.txt
- global-exclude *.py[cod]
- prune dist
- prune build
- prune *.egg-info
- """)
- }
-
- with Path(tmp_path):
- jaraco.path.build(files)
- dist = Distribution({"script_name": "%PEP 517%"})
- dist.parse_config_files()
-
- build_py = dist.get_command_obj("build_py")
- msg = r"Python recognizes 'mypkg\.tests' as an importable package"
- with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
- # TODO: To fix #3260 we need some transition period to deprecate the
- # existing behavior of `include_package_data`. After the transition, we
- # should remove the warning and fix the behaviour.
- build_py.finalize_options()
- build_py.run()
-
- build_dir = Path(dist.get_command_obj("build_py").build_lib)
- assert (build_dir / "mypkg/__init__.py").exists()
- assert (build_dir / "mypkg/resource_file.txt").exists()
-
- # Setuptools is configured to ignore `mypkg.tests`, therefore the following
- # files/dirs should not be included in the distribution.
- for f in [
- "mypkg/tests/__init__.py",
- "mypkg/tests/test_mypkg.py",
- "mypkg/tests/test_file.txt",
- "mypkg/tests",
- ]:
- with pytest.raises(AssertionError):
- # TODO: Enforce the following assertion once #3260 is fixed
- # (remove context manager and the following xfail).
- assert not (build_dir / f).exists()
-
- pytest.xfail("#3260")
+ "test_mypkg.py": "",
+ "test_file.txt": "",
+ }
+ },
+ "MANIFEST.in": DALS("""
+ global-include *.py *.txt
+ global-exclude *.py[cod]
+ prune dist
+ prune build
+ prune *.egg-info
+ """)
+}
+
+
+def test_excluded_subpackages(tmpdir_cwd):
+ jaraco.path.build(EXAMPLE_WITH_MANIFEST)
+ dist = Distribution({"script_name": "%PEP 517%"})
+ dist.parse_config_files()
+
+ build_py = dist.get_command_obj("build_py")
+ msg = r"Python recognizes 'mypkg\.tests' as an importable package"
+ with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
+ # TODO: To fix #3260 we need some transition period to deprecate the
+ # existing behavior of `include_package_data`. After the transition, we
+ # should remove the warning and fix the behaviour.
+ build_py.finalize_options()
+ build_py.run()
+
+ build_dir = Path(dist.get_command_obj("build_py").build_lib)
+ assert (build_dir / "mypkg/__init__.py").exists()
+ assert (build_dir / "mypkg/resource_file.txt").exists()
+
+ # Setuptools is configured to ignore `mypkg.tests`, therefore the following
+ # files/dirs should not be included in the distribution.
+ for f in [
+ "mypkg/tests/__init__.py",
+ "mypkg/tests/test_mypkg.py",
+ "mypkg/tests/test_file.txt",
+ "mypkg/tests",
+ ]:
+ with pytest.raises(AssertionError):
+ # TODO: Enforce the following assertion once #3260 is fixed
+ # (remove context manager and the following xfail).
+ assert not (build_dir / f).exists()
+
+ pytest.xfail("#3260")
+
+
+@pytest.mark.filterwarnings("ignore::setuptools.SetuptoolsDeprecationWarning")
+def test_existing_egg_info(tmpdir_cwd, monkeypatch):
+ """When provided with the ``existing_egg_info_dir`` attribute, build_py should not
+ attempt to run egg_info again.
+ """
+ # == Pre-condition ==
+ # Generate an egg-info dir
+ jaraco.path.build(EXAMPLE_WITH_MANIFEST)
+ dist = Distribution({"script_name": "%PEP 517%"})
+ dist.parse_config_files()
+ assert dist.include_package_data
+
+ egg_info = dist.get_command_obj("egg_info")
+ dist.run_command("egg_info")
+ egg_info_dir = next(Path(egg_info.egg_base).glob("*.egg-info"))
+ assert egg_info_dir.is_dir()
+
+ # == Setup ==
+ build_py = dist.get_command_obj("build_py")
+ build_py.finalize_options()
+ egg_info = dist.get_command_obj("egg_info")
+ egg_info_run = Mock(side_effect=egg_info.run)
+ monkeypatch.setattr(egg_info, "run", egg_info_run)
+
+ # == Remove caches ==
+ # egg_info is called when build_py looks for data_files, which gets cached.
+ # We need to ensure it is not cached yet, otherwise it may impact on the tests
+ build_py.__dict__.pop('data_files', None)
+ dist.reinitialize_command(egg_info)
+
+ # == Sanity check ==
+ # Ensure that if existing_egg_info is not given, build_py attempts to run egg_info
+ build_py.existing_egg_info_dir = None
+ build_py.run()
+ egg_info_run.assert_called()
+
+ # == Remove caches ==
+ egg_info_run.reset_mock()
+ build_py.__dict__.pop('data_files', None)
+ dist.reinitialize_command(egg_info)
+
+ # == Actual test ==
+ # Ensure that if existing_egg_info_dir is given, egg_info doesn't run
+ build_py.existing_egg_info_dir = egg_info_dir
+ build_py.run()
+ egg_info_run.assert_not_called()
+ assert build_py.data_files
+
+ # Make sure the list of outputs is actually OK
+ outputs = map(lambda x: x.replace(os.sep, "/"), build_py.get_outputs())
+ assert outputs
+ example = str(Path(build_py.build_lib, "mypkg/__init__.py")).replace(os.sep, "/")
+ assert example in outputs