aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Ganssle <pganssle@users.noreply.github.com>2019-02-05 12:48:01 -0500
committerGitHub <noreply@github.com>2019-02-05 12:48:01 -0500
commit369b8e05d17d4b28bc5bde72a38ebb4180db68e6 (patch)
treeb5a917f6755dd4fee499e48c4e4ef56d570ea10c
parent64e60fc32981a1615c35962a60297d264bf16734 (diff)
parente04a41e3129fa9945e15b16fd6d65cc212c1d946 (diff)
downloadexternal_python_setuptools-369b8e05d17d4b28bc5bde72a38ebb4180db68e6.tar.gz
external_python_setuptools-369b8e05d17d4b28bc5bde72a38ebb4180db68e6.tar.bz2
external_python_setuptools-369b8e05d17d4b28bc5bde72a38ebb4180db68e6.zip
Merge pull request #1652 from pganssle/build_meta_legacy
Add build_meta_legacy backend
-rw-r--r--changelog.d/1652.change.rst1
-rw-r--r--setuptools/build_meta.py226
-rw-r--r--setuptools/tests/test_build_meta.py293
3 files changed, 306 insertions, 214 deletions
diff --git a/changelog.d/1652.change.rst b/changelog.d/1652.change.rst
new file mode 100644
index 00000000..387d212c
--- /dev/null
+++ b/changelog.d/1652.change.rst
@@ -0,0 +1 @@
+Added the ``build_meta:__legacy__`` backend, a "compatibility mode" PEP 517 backend that can be used as the default when ``build-backend`` is left unspecified in ``pyproject.toml``.
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index c883d92f..70b7ab23 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -35,6 +35,13 @@ import contextlib
import setuptools
import distutils
+__all__ = ['get_requires_for_build_sdist',
+ 'get_requires_for_build_wheel',
+ 'prepare_metadata_for_build_wheel',
+ 'build_wheel',
+ 'build_sdist',
+ '__legacy__',
+ 'SetupRequirementsError']
class SetupRequirementsError(BaseException):
def __init__(self, specifiers):
@@ -74,81 +81,11 @@ def _to_str(s):
return s
-def _run_setup(setup_script='setup.py'):
- # Note that we can reuse our build directory between calls
- # Correctness comes first, then optimization later
- __file__ = setup_script
- __name__ = '__main__'
- f = getattr(tokenize, 'open', open)(__file__)
- code = f.read().replace('\\r\\n', '\\n')
- f.close()
- exec(compile(code, __file__, 'exec'), locals())
-
-
-def _fix_config(config_settings):
- config_settings = config_settings or {}
- config_settings.setdefault('--global-option', [])
- return config_settings
-
-
-def _get_build_requires(config_settings, requirements):
- config_settings = _fix_config(config_settings)
-
- sys.argv = sys.argv[:1] + ['egg_info'] + \
- config_settings["--global-option"]
- try:
- with Distribution.patch():
- _run_setup()
- except SetupRequirementsError as e:
- requirements += e.specifiers
-
- return requirements
-
-
def _get_immediate_subdirectories(a_dir):
return [name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))]
-def get_requires_for_build_wheel(config_settings=None):
- config_settings = _fix_config(config_settings)
- return _get_build_requires(config_settings, requirements=['wheel'])
-
-
-def get_requires_for_build_sdist(config_settings=None):
- config_settings = _fix_config(config_settings)
- return _get_build_requires(config_settings, requirements=[])
-
-
-def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
- sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', _to_str(metadata_directory)]
- _run_setup()
-
- dist_info_directory = metadata_directory
- while True:
- dist_infos = [f for f in os.listdir(dist_info_directory)
- if f.endswith('.dist-info')]
-
- if len(dist_infos) == 0 and \
- len(_get_immediate_subdirectories(dist_info_directory)) == 1:
- dist_info_directory = os.path.join(
- dist_info_directory, os.listdir(dist_info_directory)[0])
- continue
-
- assert len(dist_infos) == 1
- break
-
- # PEP 517 requires that the .dist-info directory be placed in the
- # metadata_directory. To comply, we MUST copy the directory to the root
- if dist_info_directory != metadata_directory:
- shutil.move(
- os.path.join(dist_info_directory, dist_infos[0]),
- metadata_directory)
- shutil.rmtree(dist_info_directory, ignore_errors=True)
-
- return dist_infos[0]
-
-
def _file_with_extension(directory, extension):
matching = (
f for f in os.listdir(directory)
@@ -158,26 +95,137 @@ def _file_with_extension(directory, extension):
return file
-def build_wheel(wheel_directory, config_settings=None,
- metadata_directory=None):
- config_settings = _fix_config(config_settings)
- wheel_directory = os.path.abspath(wheel_directory)
- sys.argv = sys.argv[:1] + ['bdist_wheel'] + \
- config_settings["--global-option"]
- _run_setup()
- if wheel_directory != 'dist':
- shutil.rmtree(wheel_directory)
- shutil.copytree('dist', wheel_directory)
+class _BuildMetaBackend(object):
+
+ def _fix_config(self, config_settings):
+ config_settings = config_settings or {}
+ config_settings.setdefault('--global-option', [])
+ return config_settings
+
+ def _get_build_requires(self, config_settings, requirements):
+ config_settings = self._fix_config(config_settings)
+
+ sys.argv = sys.argv[:1] + ['egg_info'] + \
+ config_settings["--global-option"]
+ try:
+ with Distribution.patch():
+ self.run_setup()
+ except SetupRequirementsError as e:
+ requirements += e.specifiers
+
+ return requirements
+
+ def run_setup(self, setup_script='setup.py'):
+ # Note that we can reuse our build directory between calls
+ # Correctness comes first, then optimization later
+ __file__ = setup_script
+ __name__ = '__main__'
+ f = getattr(tokenize, 'open', open)(__file__)
+ code = f.read().replace('\\r\\n', '\\n')
+ f.close()
+ exec(compile(code, __file__, 'exec'), locals())
+
+ def get_requires_for_build_wheel(self, config_settings=None):
+ config_settings = self._fix_config(config_settings)
+ return self._get_build_requires(config_settings, requirements=['wheel'])
+
+ def get_requires_for_build_sdist(self, config_settings=None):
+ config_settings = self._fix_config(config_settings)
+ return self._get_build_requires(config_settings, requirements=[])
+
+ def prepare_metadata_for_build_wheel(self, metadata_directory,
+ config_settings=None):
+ sys.argv = sys.argv[:1] + ['dist_info', '--egg-base',
+ _to_str(metadata_directory)]
+ self.run_setup()
+
+ dist_info_directory = metadata_directory
+ while True:
+ dist_infos = [f for f in os.listdir(dist_info_directory)
+ if f.endswith('.dist-info')]
+
+ if (len(dist_infos) == 0 and
+ len(_get_immediate_subdirectories(dist_info_directory)) == 1):
+
+ dist_info_directory = os.path.join(
+ dist_info_directory, os.listdir(dist_info_directory)[0])
+ continue
+
+ assert len(dist_infos) == 1
+ break
+
+ # PEP 517 requires that the .dist-info directory be placed in the
+ # metadata_directory. To comply, we MUST copy the directory to the root
+ if dist_info_directory != metadata_directory:
+ shutil.move(
+ os.path.join(dist_info_directory, dist_infos[0]),
+ metadata_directory)
+ shutil.rmtree(dist_info_directory, ignore_errors=True)
+
+ return dist_infos[0]
+
+ def build_wheel(self, wheel_directory, config_settings=None,
+ metadata_directory=None):
+ config_settings = self._fix_config(config_settings)
+ wheel_directory = os.path.abspath(wheel_directory)
+ sys.argv = sys.argv[:1] + ['bdist_wheel'] + \
+ config_settings["--global-option"]
+ self.run_setup()
+ if wheel_directory != 'dist':
+ shutil.rmtree(wheel_directory)
+ shutil.copytree('dist', wheel_directory)
+
+ return _file_with_extension(wheel_directory, '.whl')
+
+ def build_sdist(self, sdist_directory, config_settings=None):
+ config_settings = self._fix_config(config_settings)
+ sdist_directory = os.path.abspath(sdist_directory)
+ sys.argv = sys.argv[:1] + ['sdist', '--formats', 'gztar'] + \
+ config_settings["--global-option"] + \
+ ["--dist-dir", sdist_directory]
+ self.run_setup()
+
+ return _file_with_extension(sdist_directory, '.tar.gz')
+
+
+class _BuildMetaLegacyBackend(_BuildMetaBackend):
+ """Compatibility backend for setuptools
+
+ This is a version of setuptools.build_meta that endeavors to maintain backwards
+ compatibility with pre-PEP 517 modes of invocation. It exists as a temporary
+ bridge between the old packaging mechanism and the new packaging mechanism,
+ and will eventually be removed.
+ """
+ def run_setup(self, setup_script='setup.py'):
+ # In order to maintain compatibility with scripts assuming that
+ # the setup.py script is in a directory on the PYTHONPATH, inject
+ # '' into sys.path. (pypa/setuptools#1642)
+ sys_path = list(sys.path) # Save the original path
+
+ script_dir = os.path.dirname(os.path.abspath(setup_script))
+ if script_dir not in sys.path:
+ sys.path.insert(0, script_dir)
+
+ try:
+ super(_BuildMetaLegacyBackend,
+ self).run_setup(setup_script=setup_script)
+ finally:
+ # While PEP 517 frontends should be calling each hook in a fresh
+ # subprocess according to the standard (and thus it should not be
+ # strictly necessary to restore the old sys.path), we'll restore
+ # the original path so that the path manipulation does not persist
+ # within the hook after run_setup is called.
+ sys.path[:] = sys_path
- return _file_with_extension(wheel_directory, '.whl')
+# The primary backend
+_BACKEND = _BuildMetaBackend()
+get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel
+get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist
+prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
+build_wheel = _BACKEND.build_wheel
+build_sdist = _BACKEND.build_sdist
-def build_sdist(sdist_directory, config_settings=None):
- config_settings = _fix_config(config_settings)
- sdist_directory = os.path.abspath(sdist_directory)
- sys.argv = sys.argv[:1] + ['sdist', '--formats', 'gztar'] + \
- config_settings["--global-option"] + \
- ["--dist-dir", sdist_directory]
- _run_setup()
- return _file_with_extension(sdist_directory, '.tar.gz')
+# The legacy backend
+__legacy__ = _BuildMetaLegacyBackend()
diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py
index 82b44c89..6236b9f4 100644
--- a/setuptools/tests/test_build_meta.py
+++ b/setuptools/tests/test_build_meta.py
@@ -6,7 +6,6 @@ import tarfile
import pytest
-from setuptools.build_meta import build_sdist
from .files import build_files
from .textwrap import DALS
from . import py2_only
@@ -24,7 +23,6 @@ class BuildBackendBase:
self.env = env
self.backend_name = backend_name
-
class BuildBackend(BuildBackendBase):
"""PEP 517 Build Backend"""
@@ -44,12 +42,24 @@ class BuildBackend(BuildBackendBase):
class BuildBackendCaller(BuildBackendBase):
+ def __init__(self, *args, **kwargs):
+ super(BuildBackendCaller, self).__init__(*args, **kwargs)
+
+ (self.backend_name, _,
+ self.backend_obj) = self.backend_name.partition(':')
+
def __call__(self, name, *args, **kw):
"""Handles aribrary function invocations on the build backend."""
os.chdir(self.cwd)
os.environ.update(self.env)
mod = importlib.import_module(self.backend_name)
- return getattr(mod, name)(*args, **kw)
+
+ if self.backend_obj:
+ backend = getattr(mod, self.backend_obj)
+ else:
+ backend = mod
+
+ return getattr(backend, name)(*args, **kw)
defns = [
@@ -103,136 +113,169 @@ defns = [
]
-@pytest.fixture(params=defns)
-def build_backend(tmpdir, request):
- build_files(request.param, prefix=str(tmpdir))
- with tmpdir.as_cwd():
- yield BuildBackend(cwd='.')
-
-
-def test_get_requires_for_build_wheel(build_backend):
- actual = build_backend.get_requires_for_build_wheel()
- expected = ['six', 'wheel']
- assert sorted(actual) == sorted(expected)
-
-
-def test_get_requires_for_build_sdist(build_backend):
- actual = build_backend.get_requires_for_build_sdist()
- expected = ['six']
- assert sorted(actual) == sorted(expected)
-
-
-def test_build_wheel(build_backend):
- dist_dir = os.path.abspath('pip-wheel')
- os.makedirs(dist_dir)
- wheel_name = build_backend.build_wheel(dist_dir)
-
- assert os.path.isfile(os.path.join(dist_dir, wheel_name))
-
-
-def test_build_sdist(build_backend):
- dist_dir = os.path.abspath('pip-sdist')
- os.makedirs(dist_dir)
- sdist_name = build_backend.build_sdist(dist_dir)
-
- assert os.path.isfile(os.path.join(dist_dir, sdist_name))
-
-
-def test_prepare_metadata_for_build_wheel(build_backend):
- dist_dir = os.path.abspath('pip-dist-info')
- os.makedirs(dist_dir)
-
- dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir)
-
- assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA'))
-
-
-@py2_only
-def test_prepare_metadata_for_build_wheel_with_str(build_backend):
- dist_dir = os.path.abspath(str('pip-dist-info'))
- os.makedirs(dist_dir)
-
- dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir)
-
- assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA'))
-
-
-def test_build_sdist_explicit_dist(build_backend):
- # explicitly specifying the dist folder should work
- # the folder sdist_directory and the ``--dist-dir`` can be the same
- dist_dir = os.path.abspath('dist')
- sdist_name = build_backend.build_sdist(dist_dir)
- assert os.path.isfile(os.path.join(dist_dir, sdist_name))
-
-
-def test_build_sdist_version_change(build_backend):
- sdist_into_directory = os.path.abspath("out_sdist")
- os.makedirs(sdist_into_directory)
-
- sdist_name = build_backend.build_sdist(sdist_into_directory)
- assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name))
-
- # if the setup.py changes subsequent call of the build meta
- # should still succeed, given the
- # sdist_directory the frontend specifies is empty
- with open(os.path.abspath("setup.py"), 'rt') as file_handler:
- content = file_handler.read()
- with open(os.path.abspath("setup.py"), 'wt') as file_handler:
- file_handler.write(
- content.replace("version='0.0.0'", "version='0.0.1'"))
-
- shutil.rmtree(sdist_into_directory)
- os.makedirs(sdist_into_directory)
-
- sdist_name = build_backend.build_sdist("out_sdist")
- assert os.path.isfile(
- os.path.join(os.path.abspath("out_sdist"), sdist_name))
-
-
-def test_build_sdist_setup_py_exists(tmpdir_cwd):
- # If build_sdist is called from a script other than setup.py,
- # ensure setup.py is include
- build_files(defns[0])
- targz_path = build_sdist("temp")
- with tarfile.open(os.path.join("temp", targz_path)) as tar:
- assert any('setup.py' in name for name in tar.getnames())
-
-
-def test_build_sdist_setup_py_manifest_excluded(tmpdir_cwd):
- # Ensure that MANIFEST.in can exclude setup.py
- files = {
- 'setup.py': DALS("""
- __import__('setuptools').setup(
- name='foo',
- version='0.0.0',
- py_modules=['hello']
- )"""),
- 'hello.py': '',
- 'MANIFEST.in': DALS("""
- exclude setup.py
- """)
- }
-
- build_files(files)
- targz_path = build_sdist("temp")
- with tarfile.open(os.path.join("temp", targz_path)) as tar:
- assert not any('setup.py' in name for name in tar.getnames())
-
-
-def test_build_sdist_builds_targz_even_if_zip_indicated(tmpdir_cwd):
- files = {
+class TestBuildMetaBackend:
+ backend_name = 'setuptools.build_meta'
+
+ def get_build_backend(self):
+ return BuildBackend(cwd='.', backend_name=self.backend_name)
+
+ @pytest.fixture(params=defns)
+ def build_backend(self, tmpdir, request):
+ build_files(request.param, prefix=str(tmpdir))
+ with tmpdir.as_cwd():
+ yield self.get_build_backend()
+
+ def test_get_requires_for_build_wheel(self, build_backend):
+ actual = build_backend.get_requires_for_build_wheel()
+ expected = ['six', 'wheel']
+ assert sorted(actual) == sorted(expected)
+
+ def test_get_requires_for_build_sdist(self, build_backend):
+ actual = build_backend.get_requires_for_build_sdist()
+ expected = ['six']
+ assert sorted(actual) == sorted(expected)
+
+ def test_build_wheel(self, build_backend):
+ dist_dir = os.path.abspath('pip-wheel')
+ os.makedirs(dist_dir)
+ wheel_name = build_backend.build_wheel(dist_dir)
+
+ assert os.path.isfile(os.path.join(dist_dir, wheel_name))
+
+ def test_build_sdist(self, build_backend):
+ dist_dir = os.path.abspath('pip-sdist')
+ os.makedirs(dist_dir)
+ sdist_name = build_backend.build_sdist(dist_dir)
+
+ assert os.path.isfile(os.path.join(dist_dir, sdist_name))
+
+ def test_prepare_metadata_for_build_wheel(self, build_backend):
+ dist_dir = os.path.abspath('pip-dist-info')
+ os.makedirs(dist_dir)
+
+ dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir)
+
+ assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA'))
+
+ @py2_only
+ def test_prepare_metadata_for_build_wheel_with_str(self, build_backend):
+ dist_dir = os.path.abspath(str('pip-dist-info'))
+ os.makedirs(dist_dir)
+
+ dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir)
+
+ assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA'))
+
+ def test_build_sdist_explicit_dist(self, build_backend):
+ # explicitly specifying the dist folder should work
+ # the folder sdist_directory and the ``--dist-dir`` can be the same
+ dist_dir = os.path.abspath('dist')
+ sdist_name = build_backend.build_sdist(dist_dir)
+ assert os.path.isfile(os.path.join(dist_dir, sdist_name))
+
+ def test_build_sdist_version_change(self, build_backend):
+ sdist_into_directory = os.path.abspath("out_sdist")
+ os.makedirs(sdist_into_directory)
+
+ sdist_name = build_backend.build_sdist(sdist_into_directory)
+ assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name))
+
+ # if the setup.py changes subsequent call of the build meta
+ # should still succeed, given the
+ # sdist_directory the frontend specifies is empty
+ with open(os.path.abspath("setup.py"), 'rt') as file_handler:
+ content = file_handler.read()
+ with open(os.path.abspath("setup.py"), 'wt') as file_handler:
+ file_handler.write(
+ content.replace("version='0.0.0'", "version='0.0.1'"))
+
+ shutil.rmtree(sdist_into_directory)
+ os.makedirs(sdist_into_directory)
+
+ sdist_name = build_backend.build_sdist("out_sdist")
+ assert os.path.isfile(
+ os.path.join(os.path.abspath("out_sdist"), sdist_name))
+
+ def test_build_sdist_setup_py_exists(self, tmpdir_cwd):
+ # If build_sdist is called from a script other than setup.py,
+ # ensure setup.py is included
+ build_files(defns[0])
+
+ build_backend = self.get_build_backend()
+ targz_path = build_backend.build_sdist("temp")
+ with tarfile.open(os.path.join("temp", targz_path)) as tar:
+ assert any('setup.py' in name for name in tar.getnames())
+
+ def test_build_sdist_setup_py_manifest_excluded(self, tmpdir_cwd):
+ # Ensure that MANIFEST.in can exclude setup.py
+ files = {
+ 'setup.py': DALS("""
+ __import__('setuptools').setup(
+ name='foo',
+ version='0.0.0',
+ py_modules=['hello']
+ )"""),
+ 'hello.py': '',
+ 'MANIFEST.in': DALS("""
+ exclude setup.py
+ """)
+ }
+
+ build_files(files)
+
+ build_backend = self.get_build_backend()
+ targz_path = build_backend.build_sdist("temp")
+ with tarfile.open(os.path.join("temp", targz_path)) as tar:
+ assert not any('setup.py' in name for name in tar.getnames())
+
+ def test_build_sdist_builds_targz_even_if_zip_indicated(self, tmpdir_cwd):
+ files = {
+ 'setup.py': DALS("""
+ __import__('setuptools').setup(
+ name='foo',
+ version='0.0.0',
+ py_modules=['hello']
+ )"""),
+ 'hello.py': '',
+ 'setup.cfg': DALS("""
+ [sdist]
+ formats=zip
+ """)
+ }
+
+ build_files(files)
+
+ build_backend = self.get_build_backend()
+ build_backend.build_sdist("temp")
+
+ _relative_path_import_files = {
'setup.py': DALS("""
__import__('setuptools').setup(
name='foo',
- version='0.0.0',
+ version=__import__('hello').__version__,
py_modules=['hello']
)"""),
- 'hello.py': '',
+ 'hello.py': '__version__ = "0.0.0"',
'setup.cfg': DALS("""
[sdist]
formats=zip
""")
}
- build_files(files)
- build_sdist("temp")
+ def test_build_sdist_relative_path_import(self, tmpdir_cwd):
+ build_files(self._relative_path_import_files)
+ build_backend = self.get_build_backend()
+ with pytest.raises(ImportError):
+ build_backend.build_sdist("temp")
+
+
+class TestBuildMetaLegacyBackend(TestBuildMetaBackend):
+ backend_name = 'setuptools.build_meta:__legacy__'
+
+ # build_meta_legacy-specific tests
+ def test_build_sdist_relative_path_import(self, tmpdir_cwd):
+ # This must fail in build_meta, but must pass in build_meta_legacy
+ build_files(self._relative_path_import_files)
+
+ build_backend = self.get_build_backend()
+ build_backend.build_sdist("temp")