forked from mirrors/gecko-dev
		
	 fb2f4c03cb
			
		
	
	
		fb2f4c03cb
		
	
	
	
	
		
			
			68.0.0 is the last version of setuptools that still supports Python 3.7, currently our minimum supported Python version. (It's dropped at 68.1.0 as it's not considered as a breaking change, which is surprising: https://setuptools.pypa.io/en/stable/history.html) Differential Revision: https://phabricator.services.mozilla.com/D190674
		
			
				
	
	
		
			515 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			515 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """A PEP 517 interface to setuptools
 | |
| 
 | |
| Previously, when a user or a command line tool (let's call it a "frontend")
 | |
| needed to make a request of setuptools to take a certain action, for
 | |
| example, generating a list of installation requirements, the frontend would
 | |
| would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line.
 | |
| 
 | |
| PEP 517 defines a different method of interfacing with setuptools. Rather
 | |
| than calling "setup.py" directly, the frontend should:
 | |
| 
 | |
|   1. Set the current directory to the directory with a setup.py file
 | |
|   2. Import this module into a safe python interpreter (one in which
 | |
|      setuptools can potentially set global variables or crash hard).
 | |
|   3. Call one of the functions defined in PEP 517.
 | |
| 
 | |
| What each function does is defined in PEP 517. However, here is a "casual"
 | |
| definition of the functions (this definition should not be relied on for
 | |
| bug reports or API stability):
 | |
| 
 | |
|   - `build_wheel`: build a wheel in the folder and return the basename
 | |
|   - `get_requires_for_build_wheel`: get the `setup_requires` to build
 | |
|   - `prepare_metadata_for_build_wheel`: get the `install_requires`
 | |
|   - `build_sdist`: build an sdist in the folder and return the basename
 | |
|   - `get_requires_for_build_sdist`: get the `setup_requires` to build
 | |
| 
 | |
| Again, this is not a formal definition! Just a "taste" of the module.
 | |
| """
 | |
| 
 | |
| import io
 | |
| import os
 | |
| import shlex
 | |
| import sys
 | |
| import tokenize
 | |
| import shutil
 | |
| import contextlib
 | |
| import tempfile
 | |
| import warnings
 | |
| from pathlib import Path
 | |
| from typing import Dict, Iterator, List, Optional, Union
 | |
| 
 | |
| import setuptools
 | |
| import distutils
 | |
| from . import errors
 | |
| from ._path import same_path
 | |
| from ._reqs import parse_strings
 | |
| from .warnings import SetuptoolsDeprecationWarning
 | |
| from distutils.util import strtobool
 | |
| 
 | |
| 
 | |
| __all__ = ['get_requires_for_build_sdist',
 | |
|            'get_requires_for_build_wheel',
 | |
|            'prepare_metadata_for_build_wheel',
 | |
|            'build_wheel',
 | |
|            'build_sdist',
 | |
|            'get_requires_for_build_editable',
 | |
|            'prepare_metadata_for_build_editable',
 | |
|            'build_editable',
 | |
|            '__legacy__',
 | |
|            'SetupRequirementsError']
 | |
| 
 | |
| SETUPTOOLS_ENABLE_FEATURES = os.getenv("SETUPTOOLS_ENABLE_FEATURES", "").lower()
 | |
| LEGACY_EDITABLE = "legacy-editable" in SETUPTOOLS_ENABLE_FEATURES.replace("_", "-")
 | |
| 
 | |
| 
 | |
| class SetupRequirementsError(BaseException):
 | |
|     def __init__(self, specifiers):
 | |
|         self.specifiers = specifiers
 | |
| 
 | |
| 
 | |
| class Distribution(setuptools.dist.Distribution):
 | |
|     def fetch_build_eggs(self, specifiers):
 | |
|         specifier_list = list(parse_strings(specifiers))
 | |
| 
 | |
|         raise SetupRequirementsError(specifier_list)
 | |
| 
 | |
|     @classmethod
 | |
|     @contextlib.contextmanager
 | |
|     def patch(cls):
 | |
|         """
 | |
|         Replace
 | |
|         distutils.dist.Distribution with this class
 | |
|         for the duration of this context.
 | |
|         """
 | |
|         orig = distutils.core.Distribution
 | |
|         distutils.core.Distribution = cls
 | |
|         try:
 | |
|             yield
 | |
|         finally:
 | |
|             distutils.core.Distribution = orig
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def no_install_setup_requires():
 | |
|     """Temporarily disable installing setup_requires
 | |
| 
 | |
|     Under PEP 517, the backend reports build dependencies to the frontend,
 | |
|     and the frontend is responsible for ensuring they're installed.
 | |
|     So setuptools (acting as a backend) should not try to install them.
 | |
|     """
 | |
|     orig = setuptools._install_setup_requires
 | |
|     setuptools._install_setup_requires = lambda attrs: None
 | |
|     try:
 | |
|         yield
 | |
|     finally:
 | |
|         setuptools._install_setup_requires = orig
 | |
| 
 | |
| 
 | |
| 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 _file_with_extension(directory, extension):
 | |
|     matching = (
 | |
|         f for f in os.listdir(directory)
 | |
|         if f.endswith(extension)
 | |
|     )
 | |
|     try:
 | |
|         file, = matching
 | |
|     except ValueError:
 | |
|         raise ValueError(
 | |
|             'No distribution was found. Ensure that `setup.py` '
 | |
|             'is not empty and that it calls `setup()`.')
 | |
|     return file
 | |
| 
 | |
| 
 | |
| 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()")
 | |
| 
 | |
|     return getattr(tokenize, 'open', open)(setup_script)
 | |
| 
 | |
| 
 | |
| @contextlib.contextmanager
 | |
| def suppress_known_deprecation():
 | |
|     with warnings.catch_warnings():
 | |
|         warnings.filterwarnings('ignore', 'setup.py install is deprecated')
 | |
|         yield
 | |
| 
 | |
| 
 | |
| _ConfigSettings = Optional[Dict[str, Union[str, List[str], None]]]
 | |
| """
 | |
| Currently the user can run::
 | |
| 
 | |
|     pip install -e . --config-settings key=value
 | |
|     python -m build -C--key=value -C key=value
 | |
| 
 | |
| - pip will pass both key and value as strings and overwriting repeated keys
 | |
|   (pypa/pip#11059).
 | |
| - build will accumulate values associated with repeated keys in a list.
 | |
|   It will also accept keys with no associated value.
 | |
|   This means that an option passed by build can be ``str | list[str] | None``.
 | |
| - PEP 517 specifies that ``config_settings`` is an optional dict.
 | |
| """
 | |
| 
 | |
| 
 | |
| class _ConfigSettingsTranslator:
 | |
|     """Translate ``config_settings`` into distutils-style command arguments.
 | |
|     Only a limited number of options is currently supported.
 | |
|     """
 | |
|     # See pypa/setuptools#1928 pypa/setuptools#2491
 | |
| 
 | |
|     def _get_config(self, key: str, config_settings: _ConfigSettings) -> List[str]:
 | |
|         """
 | |
|         Get the value of a specific key in ``config_settings`` as a list of strings.
 | |
| 
 | |
|         >>> fn = _ConfigSettingsTranslator()._get_config
 | |
|         >>> fn("--global-option", None)
 | |
|         []
 | |
|         >>> fn("--global-option", {})
 | |
|         []
 | |
|         >>> fn("--global-option", {'--global-option': 'foo'})
 | |
|         ['foo']
 | |
|         >>> fn("--global-option", {'--global-option': ['foo']})
 | |
|         ['foo']
 | |
|         >>> fn("--global-option", {'--global-option': 'foo'})
 | |
|         ['foo']
 | |
|         >>> fn("--global-option", {'--global-option': 'foo bar'})
 | |
|         ['foo', 'bar']
 | |
|         """
 | |
|         cfg = config_settings or {}
 | |
|         opts = cfg.get(key) or []
 | |
|         return shlex.split(opts) if isinstance(opts, str) else opts
 | |
| 
 | |
|     def _valid_global_options(self):
 | |
|         """Global options accepted by setuptools (e.g. quiet or verbose)."""
 | |
|         options = (opt[:2] for opt in setuptools.dist.Distribution.global_options)
 | |
|         return {flag for long_and_short in options for flag in long_and_short if flag}
 | |
| 
 | |
|     def _global_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
 | |
|         """
 | |
|         Let the user specify ``verbose`` or ``quiet`` + escape hatch via
 | |
|         ``--global-option``.
 | |
|         Note: ``-v``, ``-vv``, ``-vvv`` have similar effects in setuptools,
 | |
|         so we just have to cover the basic scenario ``-v``.
 | |
| 
 | |
|         >>> fn = _ConfigSettingsTranslator()._global_args
 | |
|         >>> list(fn(None))
 | |
|         []
 | |
|         >>> list(fn({"verbose": "False"}))
 | |
|         ['-q']
 | |
|         >>> list(fn({"verbose": "1"}))
 | |
|         ['-v']
 | |
|         >>> list(fn({"--verbose": None}))
 | |
|         ['-v']
 | |
|         >>> list(fn({"verbose": "true", "--global-option": "-q --no-user-cfg"}))
 | |
|         ['-v', '-q', '--no-user-cfg']
 | |
|         >>> list(fn({"--quiet": None}))
 | |
|         ['-q']
 | |
|         """
 | |
|         cfg = config_settings or {}
 | |
|         falsey = {"false", "no", "0", "off"}
 | |
|         if "verbose" in cfg or "--verbose" in cfg:
 | |
|             level = str(cfg.get("verbose") or cfg.get("--verbose") or "1")
 | |
|             yield ("-q" if level.lower() in falsey else "-v")
 | |
|         if "quiet" in cfg or "--quiet" in cfg:
 | |
|             level = str(cfg.get("quiet") or cfg.get("--quiet") or "1")
 | |
|             yield ("-v" if level.lower() in falsey else "-q")
 | |
| 
 | |
|         valid = self._valid_global_options()
 | |
|         args = self._get_config("--global-option", config_settings)
 | |
|         yield from (arg for arg in args if arg.strip("-") in valid)
 | |
| 
 | |
|     def __dist_info_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
 | |
|         """
 | |
|         The ``dist_info`` command accepts ``tag-date`` and ``tag-build``.
 | |
| 
 | |
|         .. warning::
 | |
|            We cannot use this yet as it requires the ``sdist`` and ``bdist_wheel``
 | |
|            commands run in ``build_sdist`` and ``build_wheel`` to re-use the egg-info
 | |
|            directory created in ``prepare_metadata_for_build_wheel``.
 | |
| 
 | |
|         >>> fn = _ConfigSettingsTranslator()._ConfigSettingsTranslator__dist_info_args
 | |
|         >>> list(fn(None))
 | |
|         []
 | |
|         >>> list(fn({"tag-date": "False"}))
 | |
|         ['--no-date']
 | |
|         >>> list(fn({"tag-date": None}))
 | |
|         ['--no-date']
 | |
|         >>> list(fn({"tag-date": "true", "tag-build": ".a"}))
 | |
|         ['--tag-date', '--tag-build', '.a']
 | |
|         """
 | |
|         cfg = config_settings or {}
 | |
|         if "tag-date" in cfg:
 | |
|             val = strtobool(str(cfg["tag-date"] or "false"))
 | |
|             yield ("--tag-date" if val else "--no-date")
 | |
|         if "tag-build" in cfg:
 | |
|             yield from ["--tag-build", str(cfg["tag-build"])]
 | |
| 
 | |
|     def _editable_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
 | |
|         """
 | |
|         The ``editable_wheel`` command accepts ``editable-mode=strict``.
 | |
| 
 | |
|         >>> fn = _ConfigSettingsTranslator()._editable_args
 | |
|         >>> list(fn(None))
 | |
|         []
 | |
|         >>> list(fn({"editable-mode": "strict"}))
 | |
|         ['--mode', 'strict']
 | |
|         """
 | |
|         cfg = config_settings or {}
 | |
|         mode = cfg.get("editable-mode") or cfg.get("editable_mode")
 | |
|         if not mode:
 | |
|             return
 | |
|         yield from ["--mode", str(mode)]
 | |
| 
 | |
|     def _arbitrary_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
 | |
|         """
 | |
|         Users may expect to pass arbitrary lists of arguments to a command
 | |
|         via "--global-option" (example provided in PEP 517 of a "escape hatch").
 | |
| 
 | |
|         >>> fn = _ConfigSettingsTranslator()._arbitrary_args
 | |
|         >>> list(fn(None))
 | |
|         []
 | |
|         >>> list(fn({}))
 | |
|         []
 | |
|         >>> list(fn({'--build-option': 'foo'}))
 | |
|         ['foo']
 | |
|         >>> list(fn({'--build-option': ['foo']}))
 | |
|         ['foo']
 | |
|         >>> list(fn({'--build-option': 'foo'}))
 | |
|         ['foo']
 | |
|         >>> list(fn({'--build-option': 'foo bar'}))
 | |
|         ['foo', 'bar']
 | |
|         >>> warnings.simplefilter('error', SetuptoolsDeprecationWarning)
 | |
|         >>> list(fn({'--global-option': 'foo'}))  # doctest: +IGNORE_EXCEPTION_DETAIL
 | |
|         Traceback (most recent call last):
 | |
|         SetuptoolsDeprecationWarning: ...arguments given via `--global-option`...
 | |
|         """
 | |
|         args = self._get_config("--global-option", config_settings)
 | |
|         global_opts = self._valid_global_options()
 | |
|         bad_args = []
 | |
| 
 | |
|         for arg in args:
 | |
|             if arg.strip("-") not in global_opts:
 | |
|                 bad_args.append(arg)
 | |
|                 yield arg
 | |
| 
 | |
|         yield from self._get_config("--build-option", config_settings)
 | |
| 
 | |
|         if bad_args:
 | |
|             SetuptoolsDeprecationWarning.emit(
 | |
|                 "Incompatible `config_settings` passed to build backend.",
 | |
|                 f"""
 | |
|                 The arguments {bad_args!r} were given via `--global-option`.
 | |
|                 Please use `--build-option` instead,
 | |
|                 `--global-option` is reserved for flags like `--verbose` or `--quiet`.
 | |
|                 """,
 | |
|                 due_date=(2023, 9, 26),  # Warning introduced in v64.0.1, 11/Aug/2022.
 | |
|             )
 | |
| 
 | |
| 
 | |
| class _BuildMetaBackend(_ConfigSettingsTranslator):
 | |
|     def _get_build_requires(self, config_settings, requirements):
 | |
|         sys.argv = [
 | |
|             *sys.argv[:1],
 | |
|             *self._global_args(config_settings),
 | |
|             "egg_info",
 | |
|             *self._arbitrary_args(config_settings),
 | |
|         ]
 | |
|         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__ = os.path.abspath(setup_script)
 | |
|         __name__ = '__main__'
 | |
| 
 | |
|         with _open_setup_script(__file__) as f:
 | |
|             code = f.read().replace(r'\r\n', r'\n')
 | |
| 
 | |
|         exec(code, locals())
 | |
| 
 | |
|     def get_requires_for_build_wheel(self, config_settings=None):
 | |
|         return self._get_build_requires(config_settings, requirements=['wheel'])
 | |
| 
 | |
|     def get_requires_for_build_sdist(self, config_settings=None):
 | |
|         return self._get_build_requires(config_settings, requirements=[])
 | |
| 
 | |
|     def _bubble_up_info_directory(self, metadata_directory: str, suffix: str) -> str:
 | |
|         """
 | |
|         PEP 517 requires that the .dist-info directory be placed in the
 | |
|         metadata_directory. To comply, we MUST copy the directory to the root.
 | |
| 
 | |
|         Returns the basename of the info directory, e.g. `proj-0.0.0.dist-info`.
 | |
|         """
 | |
|         info_dir = self._find_info_directory(metadata_directory, suffix)
 | |
|         if not same_path(info_dir.parent, metadata_directory):
 | |
|             shutil.move(str(info_dir), metadata_directory)
 | |
|             # PEP 517 allow other files and dirs to exist in metadata_directory
 | |
|         return info_dir.name
 | |
| 
 | |
|     def _find_info_directory(self, metadata_directory: str, suffix: str) -> Path:
 | |
|         for parent, dirs, _ in os.walk(metadata_directory):
 | |
|             candidates = [f for f in dirs if f.endswith(suffix)]
 | |
| 
 | |
|             if len(candidates) != 0 or len(dirs) != 1:
 | |
|                 assert len(candidates) == 1, f"Multiple {suffix} directories found"
 | |
|                 return Path(parent, candidates[0])
 | |
| 
 | |
|         msg = f"No {suffix} directory found in {metadata_directory}"
 | |
|         raise errors.InternalError(msg)
 | |
| 
 | |
|     def prepare_metadata_for_build_wheel(self, metadata_directory,
 | |
|                                          config_settings=None):
 | |
|         sys.argv = [
 | |
|             *sys.argv[:1],
 | |
|             *self._global_args(config_settings),
 | |
|             "dist_info",
 | |
|             "--output-dir", metadata_directory,
 | |
|             "--keep-egg-info",
 | |
|         ]
 | |
|         with no_install_setup_requires():
 | |
|             self.run_setup()
 | |
| 
 | |
|         self._bubble_up_info_directory(metadata_directory, ".egg-info")
 | |
|         return self._bubble_up_info_directory(metadata_directory, ".dist-info")
 | |
| 
 | |
|     def _build_with_temp_dir(self, setup_command, result_extension,
 | |
|                              result_directory, config_settings):
 | |
|         result_directory = os.path.abspath(result_directory)
 | |
| 
 | |
|         # Build in a temporary directory, then copy to the target.
 | |
|         os.makedirs(result_directory, exist_ok=True)
 | |
|         temp_opts = {"prefix": ".tmp-", "dir": result_directory}
 | |
|         with tempfile.TemporaryDirectory(**temp_opts) as tmp_dist_dir:
 | |
|             sys.argv = [
 | |
|                 *sys.argv[:1],
 | |
|                 *self._global_args(config_settings),
 | |
|                 *setup_command,
 | |
|                 "--dist-dir", tmp_dist_dir,
 | |
|                 *self._arbitrary_args(config_settings),
 | |
|             ]
 | |
|             with no_install_setup_requires():
 | |
|                 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):
 | |
|         with suppress_known_deprecation():
 | |
|             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)
 | |
| 
 | |
|     def _get_dist_info_dir(self, metadata_directory: Optional[str]) -> Optional[str]:
 | |
|         if not metadata_directory:
 | |
|             return None
 | |
|         dist_info_candidates = list(Path(metadata_directory).glob("*.dist-info"))
 | |
|         assert len(dist_info_candidates) <= 1
 | |
|         return str(dist_info_candidates[0]) if dist_info_candidates else None
 | |
| 
 | |
|     if not LEGACY_EDITABLE:
 | |
| 
 | |
|         # PEP660 hooks:
 | |
|         # build_editable
 | |
|         # get_requires_for_build_editable
 | |
|         # prepare_metadata_for_build_editable
 | |
|         def build_editable(
 | |
|             self, wheel_directory, config_settings=None, metadata_directory=None
 | |
|         ):
 | |
|             # XXX can or should we hide our editable_wheel command normally?
 | |
|             info_dir = self._get_dist_info_dir(metadata_directory)
 | |
|             opts = ["--dist-info-dir", info_dir] if info_dir else []
 | |
|             cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)]
 | |
|             with suppress_known_deprecation():
 | |
|                 return self._build_with_temp_dir(
 | |
|                     cmd, ".whl", wheel_directory, config_settings
 | |
|                 )
 | |
| 
 | |
|         def get_requires_for_build_editable(self, config_settings=None):
 | |
|             return self.get_requires_for_build_wheel(config_settings)
 | |
| 
 | |
|         def prepare_metadata_for_build_editable(self, metadata_directory,
 | |
|                                                 config_settings=None):
 | |
|             return self.prepare_metadata_for_build_wheel(
 | |
|                 metadata_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)
 | |
| 
 | |
|         # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to
 | |
|         # get the directory of the source code. They expect it to refer to the
 | |
|         # setup.py script.
 | |
|         sys_argv_0 = sys.argv[0]
 | |
|         sys.argv[0] = setup_script
 | |
| 
 | |
|         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
 | |
|             sys.argv[0] = sys_argv_0
 | |
| 
 | |
| 
 | |
| # 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
 | |
| 
 | |
| if not LEGACY_EDITABLE:
 | |
|     get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
 | |
|     prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable
 | |
|     build_editable = _BACKEND.build_editable
 | |
| 
 | |
| 
 | |
| # The legacy backend
 | |
| __legacy__ = _BuildMetaLegacyBackend()
 |