aboutsummaryrefslogtreecommitdiffstats
path: root/setuptools/build_meta.py
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/build_meta.py')
-rw-r--r--setuptools/build_meta.py252
1 files changed, 163 insertions, 89 deletions
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index c883d92f..10c4b528 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -26,6 +26,7 @@ bug reports or API stability):
Again, this is not a formal definition! Just a "taste" of the module.
"""
+import io
import os
import sys
import tokenize
@@ -34,7 +35,18 @@ import contextlib
import setuptools
import distutils
+from setuptools.py31compat import TemporaryDirectory
+from pkg_resources import parse_requirements
+from pkg_resources.py31compat import makedirs
+
+__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):
@@ -43,7 +55,9 @@ class SetupRequirementsError(BaseException):
class Distribution(setuptools.dist.Distribution):
def fetch_build_eggs(self, specifiers):
- raise SetupRequirementsError(specifiers)
+ specifier_list = list(map(str, parse_requirements(specifiers)))
+
+ raise SetupRequirementsError(specifier_list)
@classmethod
@contextlib.contextmanager
@@ -74,110 +88,170 @@ 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 _file_with_extension(directory, extension):
+ matching = (
+ f for f in os.listdir(directory)
+ if f.endswith(extension)
+ )
+ file, = matching
+ return file
-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')]
+def _open_setup_script(setup_script):
+ if not os.path.exists(setup_script):
+ # Supply a default setup.py
+ return io.StringIO(u"from setuptools import setup; setup()")
- 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
+ return getattr(tokenize, 'open', open)(setup_script)
- 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)
+class _BuildMetaBackend(object):
- return dist_infos[0]
+ 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)
-def _file_with_extension(directory, extension):
- matching = (
- f for f in os.listdir(directory)
- if f.endswith(extension)
- )
- file, = matching
- return file
+ 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__'
+
+ with _open_setup_script(__file__) as f:
+ code = f.read().replace(r'\r\n', r'\n')
+
+ 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_with_temp_dir(self, setup_command, result_extension,
+ result_directory, config_settings):
+ config_settings = self._fix_config(config_settings)
+ result_directory = os.path.abspath(result_directory)
+
+ # Build in a temporary directory, then copy to the target.
+ makedirs(result_directory, exist_ok=True)
+ with TemporaryDirectory(dir=result_directory) as tmp_dist_dir:
+ sys.argv = (sys.argv[:1] + setup_command +
+ ['--dist-dir', tmp_dist_dir] +
+ config_settings["--global-option"])
+ self.run_setup()
+
+ result_basename = _file_with_extension(tmp_dist_dir, result_extension)
+ result_path = os.path.join(result_directory, result_basename)
+ if os.path.exists(result_path):
+ # os.rename will fail overwriting on non-Unix.
+ os.remove(result_path)
+ os.rename(os.path.join(tmp_dist_dir, result_basename), result_path)
+
+ return result_basename
+
+
+ def build_wheel(self, wheel_directory, config_settings=None,
+ metadata_directory=None):
+ return self._build_with_temp_dir(['bdist_wheel'], '.whl',
+ wheel_directory, config_settings)
+
+ def build_sdist(self, sdist_directory, config_settings=None):
+ return self._build_with_temp_dir(['sdist', '--formats', 'gztar'],
+ '.tar.gz', sdist_directory,
+ config_settings)
+
+
+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)
-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)
+ 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()