Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/setuptools/build_meta.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
| author | guerler |
|---|---|
| date | Mon, 27 Jul 2020 03:47:31 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:9e54283cc701 |
|---|---|
| 1 """A PEP 517 interface to setuptools | |
| 2 | |
| 3 Previously, when a user or a command line tool (let's call it a "frontend") | |
| 4 needed to make a request of setuptools to take a certain action, for | |
| 5 example, generating a list of installation requirements, the frontend would | |
| 6 would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line. | |
| 7 | |
| 8 PEP 517 defines a different method of interfacing with setuptools. Rather | |
| 9 than calling "setup.py" directly, the frontend should: | |
| 10 | |
| 11 1. Set the current directory to the directory with a setup.py file | |
| 12 2. Import this module into a safe python interpreter (one in which | |
| 13 setuptools can potentially set global variables or crash hard). | |
| 14 3. Call one of the functions defined in PEP 517. | |
| 15 | |
| 16 What each function does is defined in PEP 517. However, here is a "casual" | |
| 17 definition of the functions (this definition should not be relied on for | |
| 18 bug reports or API stability): | |
| 19 | |
| 20 - `build_wheel`: build a wheel in the folder and return the basename | |
| 21 - `get_requires_for_build_wheel`: get the `setup_requires` to build | |
| 22 - `prepare_metadata_for_build_wheel`: get the `install_requires` | |
| 23 - `build_sdist`: build an sdist in the folder and return the basename | |
| 24 - `get_requires_for_build_sdist`: get the `setup_requires` to build | |
| 25 | |
| 26 Again, this is not a formal definition! Just a "taste" of the module. | |
| 27 """ | |
| 28 | |
| 29 import io | |
| 30 import os | |
| 31 import sys | |
| 32 import tokenize | |
| 33 import shutil | |
| 34 import contextlib | |
| 35 | |
| 36 import setuptools | |
| 37 import distutils | |
| 38 from setuptools.py31compat import TemporaryDirectory | |
| 39 | |
| 40 from pkg_resources import parse_requirements | |
| 41 from pkg_resources.py31compat import makedirs | |
| 42 | |
| 43 __all__ = ['get_requires_for_build_sdist', | |
| 44 'get_requires_for_build_wheel', | |
| 45 'prepare_metadata_for_build_wheel', | |
| 46 'build_wheel', | |
| 47 'build_sdist', | |
| 48 '__legacy__', | |
| 49 'SetupRequirementsError'] | |
| 50 | |
| 51 | |
| 52 class SetupRequirementsError(BaseException): | |
| 53 def __init__(self, specifiers): | |
| 54 self.specifiers = specifiers | |
| 55 | |
| 56 | |
| 57 class Distribution(setuptools.dist.Distribution): | |
| 58 def fetch_build_eggs(self, specifiers): | |
| 59 specifier_list = list(map(str, parse_requirements(specifiers))) | |
| 60 | |
| 61 raise SetupRequirementsError(specifier_list) | |
| 62 | |
| 63 @classmethod | |
| 64 @contextlib.contextmanager | |
| 65 def patch(cls): | |
| 66 """ | |
| 67 Replace | |
| 68 distutils.dist.Distribution with this class | |
| 69 for the duration of this context. | |
| 70 """ | |
| 71 orig = distutils.core.Distribution | |
| 72 distutils.core.Distribution = cls | |
| 73 try: | |
| 74 yield | |
| 75 finally: | |
| 76 distutils.core.Distribution = orig | |
| 77 | |
| 78 | |
| 79 def _to_str(s): | |
| 80 """ | |
| 81 Convert a filename to a string (on Python 2, explicitly | |
| 82 a byte string, not Unicode) as distutils checks for the | |
| 83 exact type str. | |
| 84 """ | |
| 85 if sys.version_info[0] == 2 and not isinstance(s, str): | |
| 86 # Assume it's Unicode, as that's what the PEP says | |
| 87 # should be provided. | |
| 88 return s.encode(sys.getfilesystemencoding()) | |
| 89 return s | |
| 90 | |
| 91 | |
| 92 def _get_immediate_subdirectories(a_dir): | |
| 93 return [name for name in os.listdir(a_dir) | |
| 94 if os.path.isdir(os.path.join(a_dir, name))] | |
| 95 | |
| 96 | |
| 97 def _file_with_extension(directory, extension): | |
| 98 matching = ( | |
| 99 f for f in os.listdir(directory) | |
| 100 if f.endswith(extension) | |
| 101 ) | |
| 102 file, = matching | |
| 103 return file | |
| 104 | |
| 105 | |
| 106 def _open_setup_script(setup_script): | |
| 107 if not os.path.exists(setup_script): | |
| 108 # Supply a default setup.py | |
| 109 return io.StringIO(u"from setuptools import setup; setup()") | |
| 110 | |
| 111 return getattr(tokenize, 'open', open)(setup_script) | |
| 112 | |
| 113 | |
| 114 class _BuildMetaBackend(object): | |
| 115 | |
| 116 def _fix_config(self, config_settings): | |
| 117 config_settings = config_settings or {} | |
| 118 config_settings.setdefault('--global-option', []) | |
| 119 return config_settings | |
| 120 | |
| 121 def _get_build_requires(self, config_settings, requirements): | |
| 122 config_settings = self._fix_config(config_settings) | |
| 123 | |
| 124 sys.argv = sys.argv[:1] + ['egg_info'] + \ | |
| 125 config_settings["--global-option"] | |
| 126 try: | |
| 127 with Distribution.patch(): | |
| 128 self.run_setup() | |
| 129 except SetupRequirementsError as e: | |
| 130 requirements += e.specifiers | |
| 131 | |
| 132 return requirements | |
| 133 | |
| 134 def run_setup(self, setup_script='setup.py'): | |
| 135 # Note that we can reuse our build directory between calls | |
| 136 # Correctness comes first, then optimization later | |
| 137 __file__ = setup_script | |
| 138 __name__ = '__main__' | |
| 139 | |
| 140 with _open_setup_script(__file__) as f: | |
| 141 code = f.read().replace(r'\r\n', r'\n') | |
| 142 | |
| 143 exec(compile(code, __file__, 'exec'), locals()) | |
| 144 | |
| 145 def get_requires_for_build_wheel(self, config_settings=None): | |
| 146 config_settings = self._fix_config(config_settings) | |
| 147 return self._get_build_requires( | |
| 148 config_settings, requirements=['wheel']) | |
| 149 | |
| 150 def get_requires_for_build_sdist(self, config_settings=None): | |
| 151 config_settings = self._fix_config(config_settings) | |
| 152 return self._get_build_requires(config_settings, requirements=[]) | |
| 153 | |
| 154 def prepare_metadata_for_build_wheel(self, metadata_directory, | |
| 155 config_settings=None): | |
| 156 sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', | |
| 157 _to_str(metadata_directory)] | |
| 158 self.run_setup() | |
| 159 | |
| 160 dist_info_directory = metadata_directory | |
| 161 while True: | |
| 162 dist_infos = [f for f in os.listdir(dist_info_directory) | |
| 163 if f.endswith('.dist-info')] | |
| 164 | |
| 165 if ( | |
| 166 len(dist_infos) == 0 and | |
| 167 len(_get_immediate_subdirectories(dist_info_directory)) == 1 | |
| 168 ): | |
| 169 | |
| 170 dist_info_directory = os.path.join( | |
| 171 dist_info_directory, os.listdir(dist_info_directory)[0]) | |
| 172 continue | |
| 173 | |
| 174 assert len(dist_infos) == 1 | |
| 175 break | |
| 176 | |
| 177 # PEP 517 requires that the .dist-info directory be placed in the | |
| 178 # metadata_directory. To comply, we MUST copy the directory to the root | |
| 179 if dist_info_directory != metadata_directory: | |
| 180 shutil.move( | |
| 181 os.path.join(dist_info_directory, dist_infos[0]), | |
| 182 metadata_directory) | |
| 183 shutil.rmtree(dist_info_directory, ignore_errors=True) | |
| 184 | |
| 185 return dist_infos[0] | |
| 186 | |
| 187 def _build_with_temp_dir(self, setup_command, result_extension, | |
| 188 result_directory, config_settings): | |
| 189 config_settings = self._fix_config(config_settings) | |
| 190 result_directory = os.path.abspath(result_directory) | |
| 191 | |
| 192 # Build in a temporary directory, then copy to the target. | |
| 193 makedirs(result_directory, exist_ok=True) | |
| 194 with TemporaryDirectory(dir=result_directory) as tmp_dist_dir: | |
| 195 sys.argv = (sys.argv[:1] + setup_command + | |
| 196 ['--dist-dir', tmp_dist_dir] + | |
| 197 config_settings["--global-option"]) | |
| 198 self.run_setup() | |
| 199 | |
| 200 result_basename = _file_with_extension( | |
| 201 tmp_dist_dir, result_extension) | |
| 202 result_path = os.path.join(result_directory, result_basename) | |
| 203 if os.path.exists(result_path): | |
| 204 # os.rename will fail overwriting on non-Unix. | |
| 205 os.remove(result_path) | |
| 206 os.rename(os.path.join(tmp_dist_dir, result_basename), result_path) | |
| 207 | |
| 208 return result_basename | |
| 209 | |
| 210 def build_wheel(self, wheel_directory, config_settings=None, | |
| 211 metadata_directory=None): | |
| 212 return self._build_with_temp_dir(['bdist_wheel'], '.whl', | |
| 213 wheel_directory, config_settings) | |
| 214 | |
| 215 def build_sdist(self, sdist_directory, config_settings=None): | |
| 216 return self._build_with_temp_dir(['sdist', '--formats', 'gztar'], | |
| 217 '.tar.gz', sdist_directory, | |
| 218 config_settings) | |
| 219 | |
| 220 | |
| 221 class _BuildMetaLegacyBackend(_BuildMetaBackend): | |
| 222 """Compatibility backend for setuptools | |
| 223 | |
| 224 This is a version of setuptools.build_meta that endeavors | |
| 225 to maintain backwards | |
| 226 compatibility with pre-PEP 517 modes of invocation. It | |
| 227 exists as a temporary | |
| 228 bridge between the old packaging mechanism and the new | |
| 229 packaging mechanism, | |
| 230 and will eventually be removed. | |
| 231 """ | |
| 232 def run_setup(self, setup_script='setup.py'): | |
| 233 # In order to maintain compatibility with scripts assuming that | |
| 234 # the setup.py script is in a directory on the PYTHONPATH, inject | |
| 235 # '' into sys.path. (pypa/setuptools#1642) | |
| 236 sys_path = list(sys.path) # Save the original path | |
| 237 | |
| 238 script_dir = os.path.dirname(os.path.abspath(setup_script)) | |
| 239 if script_dir not in sys.path: | |
| 240 sys.path.insert(0, script_dir) | |
| 241 | |
| 242 # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to | |
| 243 # get the directory of the source code. They expect it to refer to the | |
| 244 # setup.py script. | |
| 245 sys_argv_0 = sys.argv[0] | |
| 246 sys.argv[0] = setup_script | |
| 247 | |
| 248 try: | |
| 249 super(_BuildMetaLegacyBackend, | |
| 250 self).run_setup(setup_script=setup_script) | |
| 251 finally: | |
| 252 # While PEP 517 frontends should be calling each hook in a fresh | |
| 253 # subprocess according to the standard (and thus it should not be | |
| 254 # strictly necessary to restore the old sys.path), we'll restore | |
| 255 # the original path so that the path manipulation does not persist | |
| 256 # within the hook after run_setup is called. | |
| 257 sys.path[:] = sys_path | |
| 258 sys.argv[0] = sys_argv_0 | |
| 259 | |
| 260 | |
| 261 # The primary backend | |
| 262 _BACKEND = _BuildMetaBackend() | |
| 263 | |
| 264 get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel | |
| 265 get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist | |
| 266 prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel | |
| 267 build_wheel = _BACKEND.build_wheel | |
| 268 build_sdist = _BACKEND.build_sdist | |
| 269 | |
| 270 | |
| 271 # The legacy backend | |
| 272 __legacy__ = _BuildMetaLegacyBackend() |
