Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/setuptools/dist.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 # -*- coding: utf-8 -*- | |
| 2 __all__ = ['Distribution'] | |
| 3 | |
| 4 import io | |
| 5 import sys | |
| 6 import re | |
| 7 import os | |
| 8 import warnings | |
| 9 import numbers | |
| 10 import distutils.log | |
| 11 import distutils.core | |
| 12 import distutils.cmd | |
| 13 import distutils.dist | |
| 14 from distutils.util import strtobool | |
| 15 from distutils.debug import DEBUG | |
| 16 from distutils.fancy_getopt import translate_longopt | |
| 17 import itertools | |
| 18 | |
| 19 from collections import defaultdict | |
| 20 from email import message_from_file | |
| 21 | |
| 22 from distutils.errors import ( | |
| 23 DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, | |
| 24 ) | |
| 25 from distutils.util import rfc822_escape | |
| 26 from distutils.version import StrictVersion | |
| 27 | |
| 28 from setuptools.extern import six | |
| 29 from setuptools.extern import packaging | |
| 30 from setuptools.extern.six.moves import map, filter, filterfalse | |
| 31 | |
| 32 from . import SetuptoolsDeprecationWarning | |
| 33 | |
| 34 from setuptools.depends import Require | |
| 35 from setuptools import windows_support | |
| 36 from setuptools.monkey import get_unpatched | |
| 37 from setuptools.config import parse_configuration | |
| 38 import pkg_resources | |
| 39 | |
| 40 __import__('setuptools.extern.packaging.specifiers') | |
| 41 __import__('setuptools.extern.packaging.version') | |
| 42 | |
| 43 | |
| 44 def _get_unpatched(cls): | |
| 45 warnings.warn("Do not call this function", DistDeprecationWarning) | |
| 46 return get_unpatched(cls) | |
| 47 | |
| 48 | |
| 49 def get_metadata_version(self): | |
| 50 mv = getattr(self, 'metadata_version', None) | |
| 51 | |
| 52 if mv is None: | |
| 53 if self.long_description_content_type or self.provides_extras: | |
| 54 mv = StrictVersion('2.1') | |
| 55 elif (self.maintainer is not None or | |
| 56 self.maintainer_email is not None or | |
| 57 getattr(self, 'python_requires', None) is not None or | |
| 58 self.project_urls): | |
| 59 mv = StrictVersion('1.2') | |
| 60 elif (self.provides or self.requires or self.obsoletes or | |
| 61 self.classifiers or self.download_url): | |
| 62 mv = StrictVersion('1.1') | |
| 63 else: | |
| 64 mv = StrictVersion('1.0') | |
| 65 | |
| 66 self.metadata_version = mv | |
| 67 | |
| 68 return mv | |
| 69 | |
| 70 | |
| 71 def read_pkg_file(self, file): | |
| 72 """Reads the metadata values from a file object.""" | |
| 73 msg = message_from_file(file) | |
| 74 | |
| 75 def _read_field(name): | |
| 76 value = msg[name] | |
| 77 if value == 'UNKNOWN': | |
| 78 return None | |
| 79 return value | |
| 80 | |
| 81 def _read_list(name): | |
| 82 values = msg.get_all(name, None) | |
| 83 if values == []: | |
| 84 return None | |
| 85 return values | |
| 86 | |
| 87 self.metadata_version = StrictVersion(msg['metadata-version']) | |
| 88 self.name = _read_field('name') | |
| 89 self.version = _read_field('version') | |
| 90 self.description = _read_field('summary') | |
| 91 # we are filling author only. | |
| 92 self.author = _read_field('author') | |
| 93 self.maintainer = None | |
| 94 self.author_email = _read_field('author-email') | |
| 95 self.maintainer_email = None | |
| 96 self.url = _read_field('home-page') | |
| 97 self.license = _read_field('license') | |
| 98 | |
| 99 if 'download-url' in msg: | |
| 100 self.download_url = _read_field('download-url') | |
| 101 else: | |
| 102 self.download_url = None | |
| 103 | |
| 104 self.long_description = _read_field('description') | |
| 105 self.description = _read_field('summary') | |
| 106 | |
| 107 if 'keywords' in msg: | |
| 108 self.keywords = _read_field('keywords').split(',') | |
| 109 | |
| 110 self.platforms = _read_list('platform') | |
| 111 self.classifiers = _read_list('classifier') | |
| 112 | |
| 113 # PEP 314 - these fields only exist in 1.1 | |
| 114 if self.metadata_version == StrictVersion('1.1'): | |
| 115 self.requires = _read_list('requires') | |
| 116 self.provides = _read_list('provides') | |
| 117 self.obsoletes = _read_list('obsoletes') | |
| 118 else: | |
| 119 self.requires = None | |
| 120 self.provides = None | |
| 121 self.obsoletes = None | |
| 122 | |
| 123 | |
| 124 # Based on Python 3.5 version | |
| 125 def write_pkg_file(self, file): | |
| 126 """Write the PKG-INFO format data to a file object. | |
| 127 """ | |
| 128 version = self.get_metadata_version() | |
| 129 | |
| 130 if six.PY2: | |
| 131 def write_field(key, value): | |
| 132 file.write("%s: %s\n" % (key, self._encode_field(value))) | |
| 133 else: | |
| 134 def write_field(key, value): | |
| 135 file.write("%s: %s\n" % (key, value)) | |
| 136 | |
| 137 write_field('Metadata-Version', str(version)) | |
| 138 write_field('Name', self.get_name()) | |
| 139 write_field('Version', self.get_version()) | |
| 140 write_field('Summary', self.get_description()) | |
| 141 write_field('Home-page', self.get_url()) | |
| 142 | |
| 143 if version < StrictVersion('1.2'): | |
| 144 write_field('Author', self.get_contact()) | |
| 145 write_field('Author-email', self.get_contact_email()) | |
| 146 else: | |
| 147 optional_fields = ( | |
| 148 ('Author', 'author'), | |
| 149 ('Author-email', 'author_email'), | |
| 150 ('Maintainer', 'maintainer'), | |
| 151 ('Maintainer-email', 'maintainer_email'), | |
| 152 ) | |
| 153 | |
| 154 for field, attr in optional_fields: | |
| 155 attr_val = getattr(self, attr) | |
| 156 | |
| 157 if attr_val is not None: | |
| 158 write_field(field, attr_val) | |
| 159 | |
| 160 write_field('License', self.get_license()) | |
| 161 if self.download_url: | |
| 162 write_field('Download-URL', self.download_url) | |
| 163 for project_url in self.project_urls.items(): | |
| 164 write_field('Project-URL', '%s, %s' % project_url) | |
| 165 | |
| 166 long_desc = rfc822_escape(self.get_long_description()) | |
| 167 write_field('Description', long_desc) | |
| 168 | |
| 169 keywords = ','.join(self.get_keywords()) | |
| 170 if keywords: | |
| 171 write_field('Keywords', keywords) | |
| 172 | |
| 173 if version >= StrictVersion('1.2'): | |
| 174 for platform in self.get_platforms(): | |
| 175 write_field('Platform', platform) | |
| 176 else: | |
| 177 self._write_list(file, 'Platform', self.get_platforms()) | |
| 178 | |
| 179 self._write_list(file, 'Classifier', self.get_classifiers()) | |
| 180 | |
| 181 # PEP 314 | |
| 182 self._write_list(file, 'Requires', self.get_requires()) | |
| 183 self._write_list(file, 'Provides', self.get_provides()) | |
| 184 self._write_list(file, 'Obsoletes', self.get_obsoletes()) | |
| 185 | |
| 186 # Setuptools specific for PEP 345 | |
| 187 if hasattr(self, 'python_requires'): | |
| 188 write_field('Requires-Python', self.python_requires) | |
| 189 | |
| 190 # PEP 566 | |
| 191 if self.long_description_content_type: | |
| 192 write_field( | |
| 193 'Description-Content-Type', | |
| 194 self.long_description_content_type | |
| 195 ) | |
| 196 if self.provides_extras: | |
| 197 for extra in self.provides_extras: | |
| 198 write_field('Provides-Extra', extra) | |
| 199 | |
| 200 | |
| 201 sequence = tuple, list | |
| 202 | |
| 203 | |
| 204 def check_importable(dist, attr, value): | |
| 205 try: | |
| 206 ep = pkg_resources.EntryPoint.parse('x=' + value) | |
| 207 assert not ep.extras | |
| 208 except (TypeError, ValueError, AttributeError, AssertionError): | |
| 209 raise DistutilsSetupError( | |
| 210 "%r must be importable 'module:attrs' string (got %r)" | |
| 211 % (attr, value) | |
| 212 ) | |
| 213 | |
| 214 | |
| 215 def assert_string_list(dist, attr, value): | |
| 216 """Verify that value is a string list""" | |
| 217 try: | |
| 218 # verify that value is a list or tuple to exclude unordered | |
| 219 # or single-use iterables | |
| 220 assert isinstance(value, (list, tuple)) | |
| 221 # verify that elements of value are strings | |
| 222 assert ''.join(value) != value | |
| 223 except (TypeError, ValueError, AttributeError, AssertionError): | |
| 224 raise DistutilsSetupError( | |
| 225 "%r must be a list of strings (got %r)" % (attr, value) | |
| 226 ) | |
| 227 | |
| 228 | |
| 229 def check_nsp(dist, attr, value): | |
| 230 """Verify that namespace packages are valid""" | |
| 231 ns_packages = value | |
| 232 assert_string_list(dist, attr, ns_packages) | |
| 233 for nsp in ns_packages: | |
| 234 if not dist.has_contents_for(nsp): | |
| 235 raise DistutilsSetupError( | |
| 236 "Distribution contains no modules or packages for " + | |
| 237 "namespace package %r" % nsp | |
| 238 ) | |
| 239 parent, sep, child = nsp.rpartition('.') | |
| 240 if parent and parent not in ns_packages: | |
| 241 distutils.log.warn( | |
| 242 "WARNING: %r is declared as a package namespace, but %r" | |
| 243 " is not: please correct this in setup.py", nsp, parent | |
| 244 ) | |
| 245 | |
| 246 | |
| 247 def check_extras(dist, attr, value): | |
| 248 """Verify that extras_require mapping is valid""" | |
| 249 try: | |
| 250 list(itertools.starmap(_check_extra, value.items())) | |
| 251 except (TypeError, ValueError, AttributeError): | |
| 252 raise DistutilsSetupError( | |
| 253 "'extras_require' must be a dictionary whose values are " | |
| 254 "strings or lists of strings containing valid project/version " | |
| 255 "requirement specifiers." | |
| 256 ) | |
| 257 | |
| 258 | |
| 259 def _check_extra(extra, reqs): | |
| 260 name, sep, marker = extra.partition(':') | |
| 261 if marker and pkg_resources.invalid_marker(marker): | |
| 262 raise DistutilsSetupError("Invalid environment marker: " + marker) | |
| 263 list(pkg_resources.parse_requirements(reqs)) | |
| 264 | |
| 265 | |
| 266 def assert_bool(dist, attr, value): | |
| 267 """Verify that value is True, False, 0, or 1""" | |
| 268 if bool(value) != value: | |
| 269 tmpl = "{attr!r} must be a boolean value (got {value!r})" | |
| 270 raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) | |
| 271 | |
| 272 | |
| 273 def check_requirements(dist, attr, value): | |
| 274 """Verify that install_requires is a valid requirements list""" | |
| 275 try: | |
| 276 list(pkg_resources.parse_requirements(value)) | |
| 277 if isinstance(value, (dict, set)): | |
| 278 raise TypeError("Unordered types are not allowed") | |
| 279 except (TypeError, ValueError) as error: | |
| 280 tmpl = ( | |
| 281 "{attr!r} must be a string or list of strings " | |
| 282 "containing valid project/version requirement specifiers; {error}" | |
| 283 ) | |
| 284 raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) | |
| 285 | |
| 286 | |
| 287 def check_specifier(dist, attr, value): | |
| 288 """Verify that value is a valid version specifier""" | |
| 289 try: | |
| 290 packaging.specifiers.SpecifierSet(value) | |
| 291 except packaging.specifiers.InvalidSpecifier as error: | |
| 292 tmpl = ( | |
| 293 "{attr!r} must be a string " | |
| 294 "containing valid version specifiers; {error}" | |
| 295 ) | |
| 296 raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) | |
| 297 | |
| 298 | |
| 299 def check_entry_points(dist, attr, value): | |
| 300 """Verify that entry_points map is parseable""" | |
| 301 try: | |
| 302 pkg_resources.EntryPoint.parse_map(value) | |
| 303 except ValueError as e: | |
| 304 raise DistutilsSetupError(e) | |
| 305 | |
| 306 | |
| 307 def check_test_suite(dist, attr, value): | |
| 308 if not isinstance(value, six.string_types): | |
| 309 raise DistutilsSetupError("test_suite must be a string") | |
| 310 | |
| 311 | |
| 312 def check_package_data(dist, attr, value): | |
| 313 """Verify that value is a dictionary of package names to glob lists""" | |
| 314 if not isinstance(value, dict): | |
| 315 raise DistutilsSetupError( | |
| 316 "{!r} must be a dictionary mapping package names to lists of " | |
| 317 "string wildcard patterns".format(attr)) | |
| 318 for k, v in value.items(): | |
| 319 if not isinstance(k, six.string_types): | |
| 320 raise DistutilsSetupError( | |
| 321 "keys of {!r} dict must be strings (got {!r})" | |
| 322 .format(attr, k) | |
| 323 ) | |
| 324 assert_string_list(dist, 'values of {!r} dict'.format(attr), v) | |
| 325 | |
| 326 | |
| 327 def check_packages(dist, attr, value): | |
| 328 for pkgname in value: | |
| 329 if not re.match(r'\w+(\.\w+)*', pkgname): | |
| 330 distutils.log.warn( | |
| 331 "WARNING: %r not a valid package name; please use only " | |
| 332 ".-separated package names in setup.py", pkgname | |
| 333 ) | |
| 334 | |
| 335 | |
| 336 _Distribution = get_unpatched(distutils.core.Distribution) | |
| 337 | |
| 338 | |
| 339 class Distribution(_Distribution): | |
| 340 """Distribution with support for features, tests, and package data | |
| 341 | |
| 342 This is an enhanced version of 'distutils.dist.Distribution' that | |
| 343 effectively adds the following new optional keyword arguments to 'setup()': | |
| 344 | |
| 345 'install_requires' -- a string or sequence of strings specifying project | |
| 346 versions that the distribution requires when installed, in the format | |
| 347 used by 'pkg_resources.require()'. They will be installed | |
| 348 automatically when the package is installed. If you wish to use | |
| 349 packages that are not available in PyPI, or want to give your users an | |
| 350 alternate download location, you can add a 'find_links' option to the | |
| 351 '[easy_install]' section of your project's 'setup.cfg' file, and then | |
| 352 setuptools will scan the listed web pages for links that satisfy the | |
| 353 requirements. | |
| 354 | |
| 355 'extras_require' -- a dictionary mapping names of optional "extras" to the | |
| 356 additional requirement(s) that using those extras incurs. For example, | |
| 357 this:: | |
| 358 | |
| 359 extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) | |
| 360 | |
| 361 indicates that the distribution can optionally provide an extra | |
| 362 capability called "reST", but it can only be used if docutils and | |
| 363 reSTedit are installed. If the user installs your package using | |
| 364 EasyInstall and requests one of your extras, the corresponding | |
| 365 additional requirements will be installed if needed. | |
| 366 | |
| 367 'features' **deprecated** -- a dictionary mapping option names to | |
| 368 'setuptools.Feature' | |
| 369 objects. Features are a portion of the distribution that can be | |
| 370 included or excluded based on user options, inter-feature dependencies, | |
| 371 and availability on the current system. Excluded features are omitted | |
| 372 from all setup commands, including source and binary distributions, so | |
| 373 you can create multiple distributions from the same source tree. | |
| 374 Feature names should be valid Python identifiers, except that they may | |
| 375 contain the '-' (minus) sign. Features can be included or excluded | |
| 376 via the command line options '--with-X' and '--without-X', where 'X' is | |
| 377 the name of the feature. Whether a feature is included by default, and | |
| 378 whether you are allowed to control this from the command line, is | |
| 379 determined by the Feature object. See the 'Feature' class for more | |
| 380 information. | |
| 381 | |
| 382 'test_suite' -- the name of a test suite to run for the 'test' command. | |
| 383 If the user runs 'python setup.py test', the package will be installed, | |
| 384 and the named test suite will be run. The format is the same as | |
| 385 would be used on a 'unittest.py' command line. That is, it is the | |
| 386 dotted name of an object to import and call to generate a test suite. | |
| 387 | |
| 388 'package_data' -- a dictionary mapping package names to lists of filenames | |
| 389 or globs to use to find data files contained in the named packages. | |
| 390 If the dictionary has filenames or globs listed under '""' (the empty | |
| 391 string), those names will be searched for in every package, in addition | |
| 392 to any names for the specific package. Data files found using these | |
| 393 names/globs will be installed along with the package, in the same | |
| 394 location as the package. Note that globs are allowed to reference | |
| 395 the contents of non-package subdirectories, as long as you use '/' as | |
| 396 a path separator. (Globs are automatically converted to | |
| 397 platform-specific paths at runtime.) | |
| 398 | |
| 399 In addition to these new keywords, this class also has several new methods | |
| 400 for manipulating the distribution's contents. For example, the 'include()' | |
| 401 and 'exclude()' methods can be thought of as in-place add and subtract | |
| 402 commands that add or remove packages, modules, extensions, and so on from | |
| 403 the distribution. They are used by the feature subsystem to configure the | |
| 404 distribution for the included and excluded features. | |
| 405 """ | |
| 406 | |
| 407 _DISTUTILS_UNSUPPORTED_METADATA = { | |
| 408 'long_description_content_type': None, | |
| 409 'project_urls': dict, | |
| 410 'provides_extras': set, | |
| 411 } | |
| 412 | |
| 413 _patched_dist = None | |
| 414 | |
| 415 def patch_missing_pkg_info(self, attrs): | |
| 416 # Fake up a replacement for the data that would normally come from | |
| 417 # PKG-INFO, but which might not yet be built if this is a fresh | |
| 418 # checkout. | |
| 419 # | |
| 420 if not attrs or 'name' not in attrs or 'version' not in attrs: | |
| 421 return | |
| 422 key = pkg_resources.safe_name(str(attrs['name'])).lower() | |
| 423 dist = pkg_resources.working_set.by_key.get(key) | |
| 424 if dist is not None and not dist.has_metadata('PKG-INFO'): | |
| 425 dist._version = pkg_resources.safe_version(str(attrs['version'])) | |
| 426 self._patched_dist = dist | |
| 427 | |
| 428 def __init__(self, attrs=None): | |
| 429 have_package_data = hasattr(self, "package_data") | |
| 430 if not have_package_data: | |
| 431 self.package_data = {} | |
| 432 attrs = attrs or {} | |
| 433 if 'features' in attrs or 'require_features' in attrs: | |
| 434 Feature.warn_deprecated() | |
| 435 self.require_features = [] | |
| 436 self.features = {} | |
| 437 self.dist_files = [] | |
| 438 # Filter-out setuptools' specific options. | |
| 439 self.src_root = attrs.pop("src_root", None) | |
| 440 self.patch_missing_pkg_info(attrs) | |
| 441 self.dependency_links = attrs.pop('dependency_links', []) | |
| 442 self.setup_requires = attrs.pop('setup_requires', []) | |
| 443 for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): | |
| 444 vars(self).setdefault(ep.name, None) | |
| 445 _Distribution.__init__(self, { | |
| 446 k: v for k, v in attrs.items() | |
| 447 if k not in self._DISTUTILS_UNSUPPORTED_METADATA | |
| 448 }) | |
| 449 | |
| 450 # Fill-in missing metadata fields not supported by distutils. | |
| 451 # Note some fields may have been set by other tools (e.g. pbr) | |
| 452 # above; they are taken preferrentially to setup() arguments | |
| 453 for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): | |
| 454 for source in self.metadata.__dict__, attrs: | |
| 455 if option in source: | |
| 456 value = source[option] | |
| 457 break | |
| 458 else: | |
| 459 value = default() if default else None | |
| 460 setattr(self.metadata, option, value) | |
| 461 | |
| 462 if isinstance(self.metadata.version, numbers.Number): | |
| 463 # Some people apparently take "version number" too literally :) | |
| 464 self.metadata.version = str(self.metadata.version) | |
| 465 | |
| 466 if self.metadata.version is not None: | |
| 467 try: | |
| 468 ver = packaging.version.Version(self.metadata.version) | |
| 469 normalized_version = str(ver) | |
| 470 if self.metadata.version != normalized_version: | |
| 471 warnings.warn( | |
| 472 "Normalizing '%s' to '%s'" % ( | |
| 473 self.metadata.version, | |
| 474 normalized_version, | |
| 475 ) | |
| 476 ) | |
| 477 self.metadata.version = normalized_version | |
| 478 except (packaging.version.InvalidVersion, TypeError): | |
| 479 warnings.warn( | |
| 480 "The version specified (%r) is an invalid version, this " | |
| 481 "may not work as expected with newer versions of " | |
| 482 "setuptools, pip, and PyPI. Please see PEP 440 for more " | |
| 483 "details." % self.metadata.version | |
| 484 ) | |
| 485 self._finalize_requires() | |
| 486 | |
| 487 def _finalize_requires(self): | |
| 488 """ | |
| 489 Set `metadata.python_requires` and fix environment markers | |
| 490 in `install_requires` and `extras_require`. | |
| 491 """ | |
| 492 if getattr(self, 'python_requires', None): | |
| 493 self.metadata.python_requires = self.python_requires | |
| 494 | |
| 495 if getattr(self, 'extras_require', None): | |
| 496 for extra in self.extras_require.keys(): | |
| 497 # Since this gets called multiple times at points where the | |
| 498 # keys have become 'converted' extras, ensure that we are only | |
| 499 # truly adding extras we haven't seen before here. | |
| 500 extra = extra.split(':')[0] | |
| 501 if extra: | |
| 502 self.metadata.provides_extras.add(extra) | |
| 503 | |
| 504 self._convert_extras_requirements() | |
| 505 self._move_install_requirements_markers() | |
| 506 | |
| 507 def _convert_extras_requirements(self): | |
| 508 """ | |
| 509 Convert requirements in `extras_require` of the form | |
| 510 `"extra": ["barbazquux; {marker}"]` to | |
| 511 `"extra:{marker}": ["barbazquux"]`. | |
| 512 """ | |
| 513 spec_ext_reqs = getattr(self, 'extras_require', None) or {} | |
| 514 self._tmp_extras_require = defaultdict(list) | |
| 515 for section, v in spec_ext_reqs.items(): | |
| 516 # Do not strip empty sections. | |
| 517 self._tmp_extras_require[section] | |
| 518 for r in pkg_resources.parse_requirements(v): | |
| 519 suffix = self._suffix_for(r) | |
| 520 self._tmp_extras_require[section + suffix].append(r) | |
| 521 | |
| 522 @staticmethod | |
| 523 def _suffix_for(req): | |
| 524 """ | |
| 525 For a requirement, return the 'extras_require' suffix for | |
| 526 that requirement. | |
| 527 """ | |
| 528 return ':' + str(req.marker) if req.marker else '' | |
| 529 | |
| 530 def _move_install_requirements_markers(self): | |
| 531 """ | |
| 532 Move requirements in `install_requires` that are using environment | |
| 533 markers `extras_require`. | |
| 534 """ | |
| 535 | |
| 536 # divide the install_requires into two sets, simple ones still | |
| 537 # handled by install_requires and more complex ones handled | |
| 538 # by extras_require. | |
| 539 | |
| 540 def is_simple_req(req): | |
| 541 return not req.marker | |
| 542 | |
| 543 spec_inst_reqs = getattr(self, 'install_requires', None) or () | |
| 544 inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) | |
| 545 simple_reqs = filter(is_simple_req, inst_reqs) | |
| 546 complex_reqs = filterfalse(is_simple_req, inst_reqs) | |
| 547 self.install_requires = list(map(str, simple_reqs)) | |
| 548 | |
| 549 for r in complex_reqs: | |
| 550 self._tmp_extras_require[':' + str(r.marker)].append(r) | |
| 551 self.extras_require = dict( | |
| 552 (k, [str(r) for r in map(self._clean_req, v)]) | |
| 553 for k, v in self._tmp_extras_require.items() | |
| 554 ) | |
| 555 | |
| 556 def _clean_req(self, req): | |
| 557 """ | |
| 558 Given a Requirement, remove environment markers and return it. | |
| 559 """ | |
| 560 req.marker = None | |
| 561 return req | |
| 562 | |
| 563 def _parse_config_files(self, filenames=None): | |
| 564 """ | |
| 565 Adapted from distutils.dist.Distribution.parse_config_files, | |
| 566 this method provides the same functionality in subtly-improved | |
| 567 ways. | |
| 568 """ | |
| 569 from setuptools.extern.six.moves.configparser import ConfigParser | |
| 570 | |
| 571 # Ignore install directory options if we have a venv | |
| 572 if six.PY3 and sys.prefix != sys.base_prefix: | |
| 573 ignore_options = [ | |
| 574 'install-base', 'install-platbase', 'install-lib', | |
| 575 'install-platlib', 'install-purelib', 'install-headers', | |
| 576 'install-scripts', 'install-data', 'prefix', 'exec-prefix', | |
| 577 'home', 'user', 'root'] | |
| 578 else: | |
| 579 ignore_options = [] | |
| 580 | |
| 581 ignore_options = frozenset(ignore_options) | |
| 582 | |
| 583 if filenames is None: | |
| 584 filenames = self.find_config_files() | |
| 585 | |
| 586 if DEBUG: | |
| 587 self.announce("Distribution.parse_config_files():") | |
| 588 | |
| 589 parser = ConfigParser() | |
| 590 for filename in filenames: | |
| 591 with io.open(filename, encoding='utf-8') as reader: | |
| 592 if DEBUG: | |
| 593 self.announce(" reading {filename}".format(**locals())) | |
| 594 (parser.read_file if six.PY3 else parser.readfp)(reader) | |
| 595 for section in parser.sections(): | |
| 596 options = parser.options(section) | |
| 597 opt_dict = self.get_option_dict(section) | |
| 598 | |
| 599 for opt in options: | |
| 600 if opt != '__name__' and opt not in ignore_options: | |
| 601 val = self._try_str(parser.get(section, opt)) | |
| 602 opt = opt.replace('-', '_') | |
| 603 opt_dict[opt] = (filename, val) | |
| 604 | |
| 605 # Make the ConfigParser forget everything (so we retain | |
| 606 # the original filenames that options come from) | |
| 607 parser.__init__() | |
| 608 | |
| 609 # If there was a "global" section in the config file, use it | |
| 610 # to set Distribution options. | |
| 611 | |
| 612 if 'global' in self.command_options: | |
| 613 for (opt, (src, val)) in self.command_options['global'].items(): | |
| 614 alias = self.negative_opt.get(opt) | |
| 615 try: | |
| 616 if alias: | |
| 617 setattr(self, alias, not strtobool(val)) | |
| 618 elif opt in ('verbose', 'dry_run'): # ugh! | |
| 619 setattr(self, opt, strtobool(val)) | |
| 620 else: | |
| 621 setattr(self, opt, val) | |
| 622 except ValueError as msg: | |
| 623 raise DistutilsOptionError(msg) | |
| 624 | |
| 625 @staticmethod | |
| 626 def _try_str(val): | |
| 627 """ | |
| 628 On Python 2, much of distutils relies on string values being of | |
| 629 type 'str' (bytes) and not unicode text. If the value can be safely | |
| 630 encoded to bytes using the default encoding, prefer that. | |
| 631 | |
| 632 Why the default encoding? Because that value can be implicitly | |
| 633 decoded back to text if needed. | |
| 634 | |
| 635 Ref #1653 | |
| 636 """ | |
| 637 if six.PY3: | |
| 638 return val | |
| 639 try: | |
| 640 return val.encode() | |
| 641 except UnicodeEncodeError: | |
| 642 pass | |
| 643 return val | |
| 644 | |
| 645 def _set_command_options(self, command_obj, option_dict=None): | |
| 646 """ | |
| 647 Set the options for 'command_obj' from 'option_dict'. Basically | |
| 648 this means copying elements of a dictionary ('option_dict') to | |
| 649 attributes of an instance ('command'). | |
| 650 | |
| 651 'command_obj' must be a Command instance. If 'option_dict' is not | |
| 652 supplied, uses the standard option dictionary for this command | |
| 653 (from 'self.command_options'). | |
| 654 | |
| 655 (Adopted from distutils.dist.Distribution._set_command_options) | |
| 656 """ | |
| 657 command_name = command_obj.get_command_name() | |
| 658 if option_dict is None: | |
| 659 option_dict = self.get_option_dict(command_name) | |
| 660 | |
| 661 if DEBUG: | |
| 662 self.announce(" setting options for '%s' command:" % command_name) | |
| 663 for (option, (source, value)) in option_dict.items(): | |
| 664 if DEBUG: | |
| 665 self.announce(" %s = %s (from %s)" % (option, value, | |
| 666 source)) | |
| 667 try: | |
| 668 bool_opts = [translate_longopt(o) | |
| 669 for o in command_obj.boolean_options] | |
| 670 except AttributeError: | |
| 671 bool_opts = [] | |
| 672 try: | |
| 673 neg_opt = command_obj.negative_opt | |
| 674 except AttributeError: | |
| 675 neg_opt = {} | |
| 676 | |
| 677 try: | |
| 678 is_string = isinstance(value, six.string_types) | |
| 679 if option in neg_opt and is_string: | |
| 680 setattr(command_obj, neg_opt[option], not strtobool(value)) | |
| 681 elif option in bool_opts and is_string: | |
| 682 setattr(command_obj, option, strtobool(value)) | |
| 683 elif hasattr(command_obj, option): | |
| 684 setattr(command_obj, option, value) | |
| 685 else: | |
| 686 raise DistutilsOptionError( | |
| 687 "error in %s: command '%s' has no such option '%s'" | |
| 688 % (source, command_name, option)) | |
| 689 except ValueError as msg: | |
| 690 raise DistutilsOptionError(msg) | |
| 691 | |
| 692 def parse_config_files(self, filenames=None, ignore_option_errors=False): | |
| 693 """Parses configuration files from various levels | |
| 694 and loads configuration. | |
| 695 | |
| 696 """ | |
| 697 self._parse_config_files(filenames=filenames) | |
| 698 | |
| 699 parse_configuration(self, self.command_options, | |
| 700 ignore_option_errors=ignore_option_errors) | |
| 701 self._finalize_requires() | |
| 702 | |
| 703 def parse_command_line(self): | |
| 704 """Process features after parsing command line options""" | |
| 705 result = _Distribution.parse_command_line(self) | |
| 706 if self.features: | |
| 707 self._finalize_features() | |
| 708 return result | |
| 709 | |
| 710 def _feature_attrname(self, name): | |
| 711 """Convert feature name to corresponding option attribute name""" | |
| 712 return 'with_' + name.replace('-', '_') | |
| 713 | |
| 714 def fetch_build_eggs(self, requires): | |
| 715 """Resolve pre-setup requirements""" | |
| 716 resolved_dists = pkg_resources.working_set.resolve( | |
| 717 pkg_resources.parse_requirements(requires), | |
| 718 installer=self.fetch_build_egg, | |
| 719 replace_conflicting=True, | |
| 720 ) | |
| 721 for dist in resolved_dists: | |
| 722 pkg_resources.working_set.add(dist, replace=True) | |
| 723 return resolved_dists | |
| 724 | |
| 725 def finalize_options(self): | |
| 726 _Distribution.finalize_options(self) | |
| 727 if self.features: | |
| 728 self._set_global_opts_from_features() | |
| 729 | |
| 730 for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): | |
| 731 value = getattr(self, ep.name, None) | |
| 732 if value is not None: | |
| 733 ep.require(installer=self.fetch_build_egg) | |
| 734 ep.load()(self, ep.name, value) | |
| 735 if getattr(self, 'convert_2to3_doctests', None): | |
| 736 # XXX may convert to set here when we can rely on set being builtin | |
| 737 self.convert_2to3_doctests = [ | |
| 738 os.path.abspath(p) | |
| 739 for p in self.convert_2to3_doctests | |
| 740 ] | |
| 741 else: | |
| 742 self.convert_2to3_doctests = [] | |
| 743 | |
| 744 def get_egg_cache_dir(self): | |
| 745 egg_cache_dir = os.path.join(os.curdir, '.eggs') | |
| 746 if not os.path.exists(egg_cache_dir): | |
| 747 os.mkdir(egg_cache_dir) | |
| 748 windows_support.hide_file(egg_cache_dir) | |
| 749 readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') | |
| 750 with open(readme_txt_filename, 'w') as f: | |
| 751 f.write('This directory contains eggs that were downloaded ' | |
| 752 'by setuptools to build, test, and run plug-ins.\n\n') | |
| 753 f.write('This directory caches those eggs to prevent ' | |
| 754 'repeated downloads.\n\n') | |
| 755 f.write('However, it is safe to delete this directory.\n\n') | |
| 756 | |
| 757 return egg_cache_dir | |
| 758 | |
| 759 def fetch_build_egg(self, req): | |
| 760 """Fetch an egg needed for building""" | |
| 761 from setuptools.command.easy_install import easy_install | |
| 762 dist = self.__class__({'script_args': ['easy_install']}) | |
| 763 opts = dist.get_option_dict('easy_install') | |
| 764 opts.clear() | |
| 765 opts.update( | |
| 766 (k, v) | |
| 767 for k, v in self.get_option_dict('easy_install').items() | |
| 768 if k in ( | |
| 769 # don't use any other settings | |
| 770 'find_links', 'site_dirs', 'index_url', | |
| 771 'optimize', 'site_dirs', 'allow_hosts', | |
| 772 )) | |
| 773 if self.dependency_links: | |
| 774 links = self.dependency_links[:] | |
| 775 if 'find_links' in opts: | |
| 776 links = opts['find_links'][1] + links | |
| 777 opts['find_links'] = ('setup', links) | |
| 778 install_dir = self.get_egg_cache_dir() | |
| 779 cmd = easy_install( | |
| 780 dist, args=["x"], install_dir=install_dir, | |
| 781 exclude_scripts=True, | |
| 782 always_copy=False, build_directory=None, editable=False, | |
| 783 upgrade=False, multi_version=True, no_report=True, user=False | |
| 784 ) | |
| 785 cmd.ensure_finalized() | |
| 786 return cmd.easy_install(req) | |
| 787 | |
| 788 def _set_global_opts_from_features(self): | |
| 789 """Add --with-X/--without-X options based on optional features""" | |
| 790 | |
| 791 go = [] | |
| 792 no = self.negative_opt.copy() | |
| 793 | |
| 794 for name, feature in self.features.items(): | |
| 795 self._set_feature(name, None) | |
| 796 feature.validate(self) | |
| 797 | |
| 798 if feature.optional: | |
| 799 descr = feature.description | |
| 800 incdef = ' (default)' | |
| 801 excdef = '' | |
| 802 if not feature.include_by_default(): | |
| 803 excdef, incdef = incdef, excdef | |
| 804 | |
| 805 new = ( | |
| 806 ('with-' + name, None, 'include ' + descr + incdef), | |
| 807 ('without-' + name, None, 'exclude ' + descr + excdef), | |
| 808 ) | |
| 809 go.extend(new) | |
| 810 no['without-' + name] = 'with-' + name | |
| 811 | |
| 812 self.global_options = self.feature_options = go + self.global_options | |
| 813 self.negative_opt = self.feature_negopt = no | |
| 814 | |
| 815 def _finalize_features(self): | |
| 816 """Add/remove features and resolve dependencies between them""" | |
| 817 | |
| 818 # First, flag all the enabled items (and thus their dependencies) | |
| 819 for name, feature in self.features.items(): | |
| 820 enabled = self.feature_is_included(name) | |
| 821 if enabled or (enabled is None and feature.include_by_default()): | |
| 822 feature.include_in(self) | |
| 823 self._set_feature(name, 1) | |
| 824 | |
| 825 # Then disable the rest, so that off-by-default features don't | |
| 826 # get flagged as errors when they're required by an enabled feature | |
| 827 for name, feature in self.features.items(): | |
| 828 if not self.feature_is_included(name): | |
| 829 feature.exclude_from(self) | |
| 830 self._set_feature(name, 0) | |
| 831 | |
| 832 def get_command_class(self, command): | |
| 833 """Pluggable version of get_command_class()""" | |
| 834 if command in self.cmdclass: | |
| 835 return self.cmdclass[command] | |
| 836 | |
| 837 eps = pkg_resources.iter_entry_points('distutils.commands', command) | |
| 838 for ep in eps: | |
| 839 ep.require(installer=self.fetch_build_egg) | |
| 840 self.cmdclass[command] = cmdclass = ep.load() | |
| 841 return cmdclass | |
| 842 else: | |
| 843 return _Distribution.get_command_class(self, command) | |
| 844 | |
| 845 def print_commands(self): | |
| 846 for ep in pkg_resources.iter_entry_points('distutils.commands'): | |
| 847 if ep.name not in self.cmdclass: | |
| 848 # don't require extras as the commands won't be invoked | |
| 849 cmdclass = ep.resolve() | |
| 850 self.cmdclass[ep.name] = cmdclass | |
| 851 return _Distribution.print_commands(self) | |
| 852 | |
| 853 def get_command_list(self): | |
| 854 for ep in pkg_resources.iter_entry_points('distutils.commands'): | |
| 855 if ep.name not in self.cmdclass: | |
| 856 # don't require extras as the commands won't be invoked | |
| 857 cmdclass = ep.resolve() | |
| 858 self.cmdclass[ep.name] = cmdclass | |
| 859 return _Distribution.get_command_list(self) | |
| 860 | |
| 861 def _set_feature(self, name, status): | |
| 862 """Set feature's inclusion status""" | |
| 863 setattr(self, self._feature_attrname(name), status) | |
| 864 | |
| 865 def feature_is_included(self, name): | |
| 866 """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" | |
| 867 return getattr(self, self._feature_attrname(name)) | |
| 868 | |
| 869 def include_feature(self, name): | |
| 870 """Request inclusion of feature named 'name'""" | |
| 871 | |
| 872 if self.feature_is_included(name) == 0: | |
| 873 descr = self.features[name].description | |
| 874 raise DistutilsOptionError( | |
| 875 descr + " is required, but was excluded or is not available" | |
| 876 ) | |
| 877 self.features[name].include_in(self) | |
| 878 self._set_feature(name, 1) | |
| 879 | |
| 880 def include(self, **attrs): | |
| 881 """Add items to distribution that are named in keyword arguments | |
| 882 | |
| 883 For example, 'dist.include(py_modules=["x"])' would add 'x' to | |
| 884 the distribution's 'py_modules' attribute, if it was not already | |
| 885 there. | |
| 886 | |
| 887 Currently, this method only supports inclusion for attributes that are | |
| 888 lists or tuples. If you need to add support for adding to other | |
| 889 attributes in this or a subclass, you can add an '_include_X' method, | |
| 890 where 'X' is the name of the attribute. The method will be called with | |
| 891 the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' | |
| 892 will try to call 'dist._include_foo({"bar":"baz"})', which can then | |
| 893 handle whatever special inclusion logic is needed. | |
| 894 """ | |
| 895 for k, v in attrs.items(): | |
| 896 include = getattr(self, '_include_' + k, None) | |
| 897 if include: | |
| 898 include(v) | |
| 899 else: | |
| 900 self._include_misc(k, v) | |
| 901 | |
| 902 def exclude_package(self, package): | |
| 903 """Remove packages, modules, and extensions in named package""" | |
| 904 | |
| 905 pfx = package + '.' | |
| 906 if self.packages: | |
| 907 self.packages = [ | |
| 908 p for p in self.packages | |
| 909 if p != package and not p.startswith(pfx) | |
| 910 ] | |
| 911 | |
| 912 if self.py_modules: | |
| 913 self.py_modules = [ | |
| 914 p for p in self.py_modules | |
| 915 if p != package and not p.startswith(pfx) | |
| 916 ] | |
| 917 | |
| 918 if self.ext_modules: | |
| 919 self.ext_modules = [ | |
| 920 p for p in self.ext_modules | |
| 921 if p.name != package and not p.name.startswith(pfx) | |
| 922 ] | |
| 923 | |
| 924 def has_contents_for(self, package): | |
| 925 """Return true if 'exclude_package(package)' would do something""" | |
| 926 | |
| 927 pfx = package + '.' | |
| 928 | |
| 929 for p in self.iter_distribution_names(): | |
| 930 if p == package or p.startswith(pfx): | |
| 931 return True | |
| 932 | |
| 933 def _exclude_misc(self, name, value): | |
| 934 """Handle 'exclude()' for list/tuple attrs without a special handler""" | |
| 935 if not isinstance(value, sequence): | |
| 936 raise DistutilsSetupError( | |
| 937 "%s: setting must be a list or tuple (%r)" % (name, value) | |
| 938 ) | |
| 939 try: | |
| 940 old = getattr(self, name) | |
| 941 except AttributeError: | |
| 942 raise DistutilsSetupError( | |
| 943 "%s: No such distribution setting" % name | |
| 944 ) | |
| 945 if old is not None and not isinstance(old, sequence): | |
| 946 raise DistutilsSetupError( | |
| 947 name + ": this setting cannot be changed via include/exclude" | |
| 948 ) | |
| 949 elif old: | |
| 950 setattr(self, name, [item for item in old if item not in value]) | |
| 951 | |
| 952 def _include_misc(self, name, value): | |
| 953 """Handle 'include()' for list/tuple attrs without a special handler""" | |
| 954 | |
| 955 if not isinstance(value, sequence): | |
| 956 raise DistutilsSetupError( | |
| 957 "%s: setting must be a list (%r)" % (name, value) | |
| 958 ) | |
| 959 try: | |
| 960 old = getattr(self, name) | |
| 961 except AttributeError: | |
| 962 raise DistutilsSetupError( | |
| 963 "%s: No such distribution setting" % name | |
| 964 ) | |
| 965 if old is None: | |
| 966 setattr(self, name, value) | |
| 967 elif not isinstance(old, sequence): | |
| 968 raise DistutilsSetupError( | |
| 969 name + ": this setting cannot be changed via include/exclude" | |
| 970 ) | |
| 971 else: | |
| 972 new = [item for item in value if item not in old] | |
| 973 setattr(self, name, old + new) | |
| 974 | |
| 975 def exclude(self, **attrs): | |
| 976 """Remove items from distribution that are named in keyword arguments | |
| 977 | |
| 978 For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from | |
| 979 the distribution's 'py_modules' attribute. Excluding packages uses | |
| 980 the 'exclude_package()' method, so all of the package's contained | |
| 981 packages, modules, and extensions are also excluded. | |
| 982 | |
| 983 Currently, this method only supports exclusion from attributes that are | |
| 984 lists or tuples. If you need to add support for excluding from other | |
| 985 attributes in this or a subclass, you can add an '_exclude_X' method, | |
| 986 where 'X' is the name of the attribute. The method will be called with | |
| 987 the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' | |
| 988 will try to call 'dist._exclude_foo({"bar":"baz"})', which can then | |
| 989 handle whatever special exclusion logic is needed. | |
| 990 """ | |
| 991 for k, v in attrs.items(): | |
| 992 exclude = getattr(self, '_exclude_' + k, None) | |
| 993 if exclude: | |
| 994 exclude(v) | |
| 995 else: | |
| 996 self._exclude_misc(k, v) | |
| 997 | |
| 998 def _exclude_packages(self, packages): | |
| 999 if not isinstance(packages, sequence): | |
| 1000 raise DistutilsSetupError( | |
| 1001 "packages: setting must be a list or tuple (%r)" % (packages,) | |
| 1002 ) | |
| 1003 list(map(self.exclude_package, packages)) | |
| 1004 | |
| 1005 def _parse_command_opts(self, parser, args): | |
| 1006 # Remove --with-X/--without-X options when processing command args | |
| 1007 self.global_options = self.__class__.global_options | |
| 1008 self.negative_opt = self.__class__.negative_opt | |
| 1009 | |
| 1010 # First, expand any aliases | |
| 1011 command = args[0] | |
| 1012 aliases = self.get_option_dict('aliases') | |
| 1013 while command in aliases: | |
| 1014 src, alias = aliases[command] | |
| 1015 del aliases[command] # ensure each alias can expand only once! | |
| 1016 import shlex | |
| 1017 args[:1] = shlex.split(alias, True) | |
| 1018 command = args[0] | |
| 1019 | |
| 1020 nargs = _Distribution._parse_command_opts(self, parser, args) | |
| 1021 | |
| 1022 # Handle commands that want to consume all remaining arguments | |
| 1023 cmd_class = self.get_command_class(command) | |
| 1024 if getattr(cmd_class, 'command_consumes_arguments', None): | |
| 1025 self.get_option_dict(command)['args'] = ("command line", nargs) | |
| 1026 if nargs is not None: | |
| 1027 return [] | |
| 1028 | |
| 1029 return nargs | |
| 1030 | |
| 1031 def get_cmdline_options(self): | |
| 1032 """Return a '{cmd: {opt:val}}' map of all command-line options | |
| 1033 | |
| 1034 Option names are all long, but do not include the leading '--', and | |
| 1035 contain dashes rather than underscores. If the option doesn't take | |
| 1036 an argument (e.g. '--quiet'), the 'val' is 'None'. | |
| 1037 | |
| 1038 Note that options provided by config files are intentionally excluded. | |
| 1039 """ | |
| 1040 | |
| 1041 d = {} | |
| 1042 | |
| 1043 for cmd, opts in self.command_options.items(): | |
| 1044 | |
| 1045 for opt, (src, val) in opts.items(): | |
| 1046 | |
| 1047 if src != "command line": | |
| 1048 continue | |
| 1049 | |
| 1050 opt = opt.replace('_', '-') | |
| 1051 | |
| 1052 if val == 0: | |
| 1053 cmdobj = self.get_command_obj(cmd) | |
| 1054 neg_opt = self.negative_opt.copy() | |
| 1055 neg_opt.update(getattr(cmdobj, 'negative_opt', {})) | |
| 1056 for neg, pos in neg_opt.items(): | |
| 1057 if pos == opt: | |
| 1058 opt = neg | |
| 1059 val = None | |
| 1060 break | |
| 1061 else: | |
| 1062 raise AssertionError("Shouldn't be able to get here") | |
| 1063 | |
| 1064 elif val == 1: | |
| 1065 val = None | |
| 1066 | |
| 1067 d.setdefault(cmd, {})[opt] = val | |
| 1068 | |
| 1069 return d | |
| 1070 | |
| 1071 def iter_distribution_names(self): | |
| 1072 """Yield all packages, modules, and extension names in distribution""" | |
| 1073 | |
| 1074 for pkg in self.packages or (): | |
| 1075 yield pkg | |
| 1076 | |
| 1077 for module in self.py_modules or (): | |
| 1078 yield module | |
| 1079 | |
| 1080 for ext in self.ext_modules or (): | |
| 1081 if isinstance(ext, tuple): | |
| 1082 name, buildinfo = ext | |
| 1083 else: | |
| 1084 name = ext.name | |
| 1085 if name.endswith('module'): | |
| 1086 name = name[:-6] | |
| 1087 yield name | |
| 1088 | |
| 1089 def handle_display_options(self, option_order): | |
| 1090 """If there were any non-global "display-only" options | |
| 1091 (--help-commands or the metadata display options) on the command | |
| 1092 line, display the requested info and return true; else return | |
| 1093 false. | |
| 1094 """ | |
| 1095 import sys | |
| 1096 | |
| 1097 if six.PY2 or self.help_commands: | |
| 1098 return _Distribution.handle_display_options(self, option_order) | |
| 1099 | |
| 1100 # Stdout may be StringIO (e.g. in tests) | |
| 1101 if not isinstance(sys.stdout, io.TextIOWrapper): | |
| 1102 return _Distribution.handle_display_options(self, option_order) | |
| 1103 | |
| 1104 # Don't wrap stdout if utf-8 is already the encoding. Provides | |
| 1105 # workaround for #334. | |
| 1106 if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): | |
| 1107 return _Distribution.handle_display_options(self, option_order) | |
| 1108 | |
| 1109 # Print metadata in UTF-8 no matter the platform | |
| 1110 encoding = sys.stdout.encoding | |
| 1111 errors = sys.stdout.errors | |
| 1112 newline = sys.platform != 'win32' and '\n' or None | |
| 1113 line_buffering = sys.stdout.line_buffering | |
| 1114 | |
| 1115 sys.stdout = io.TextIOWrapper( | |
| 1116 sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) | |
| 1117 try: | |
| 1118 return _Distribution.handle_display_options(self, option_order) | |
| 1119 finally: | |
| 1120 sys.stdout = io.TextIOWrapper( | |
| 1121 sys.stdout.detach(), encoding, errors, newline, line_buffering) | |
| 1122 | |
| 1123 | |
| 1124 class Feature: | |
| 1125 """ | |
| 1126 **deprecated** -- The `Feature` facility was never completely implemented | |
| 1127 or supported, `has reported issues | |
| 1128 <https://github.com/pypa/setuptools/issues/58>`_ and will be removed in | |
| 1129 a future version. | |
| 1130 | |
| 1131 A subset of the distribution that can be excluded if unneeded/wanted | |
| 1132 | |
| 1133 Features are created using these keyword arguments: | |
| 1134 | |
| 1135 'description' -- a short, human readable description of the feature, to | |
| 1136 be used in error messages, and option help messages. | |
| 1137 | |
| 1138 'standard' -- if true, the feature is included by default if it is | |
| 1139 available on the current system. Otherwise, the feature is only | |
| 1140 included if requested via a command line '--with-X' option, or if | |
| 1141 another included feature requires it. The default setting is 'False'. | |
| 1142 | |
| 1143 'available' -- if true, the feature is available for installation on the | |
| 1144 current system. The default setting is 'True'. | |
| 1145 | |
| 1146 'optional' -- if true, the feature's inclusion can be controlled from the | |
| 1147 command line, using the '--with-X' or '--without-X' options. If | |
| 1148 false, the feature's inclusion status is determined automatically, | |
| 1149 based on 'availabile', 'standard', and whether any other feature | |
| 1150 requires it. The default setting is 'True'. | |
| 1151 | |
| 1152 'require_features' -- a string or sequence of strings naming features | |
| 1153 that should also be included if this feature is included. Defaults to | |
| 1154 empty list. May also contain 'Require' objects that should be | |
| 1155 added/removed from the distribution. | |
| 1156 | |
| 1157 'remove' -- a string or list of strings naming packages to be removed | |
| 1158 from the distribution if this feature is *not* included. If the | |
| 1159 feature *is* included, this argument is ignored. This argument exists | |
| 1160 to support removing features that "crosscut" a distribution, such as | |
| 1161 defining a 'tests' feature that removes all the 'tests' subpackages | |
| 1162 provided by other features. The default for this argument is an empty | |
| 1163 list. (Note: the named package(s) or modules must exist in the base | |
| 1164 distribution when the 'setup()' function is initially called.) | |
| 1165 | |
| 1166 other keywords -- any other keyword arguments are saved, and passed to | |
| 1167 the distribution's 'include()' and 'exclude()' methods when the | |
| 1168 feature is included or excluded, respectively. So, for example, you | |
| 1169 could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be | |
| 1170 added or removed from the distribution as appropriate. | |
| 1171 | |
| 1172 A feature must include at least one 'requires', 'remove', or other | |
| 1173 keyword argument. Otherwise, it can't affect the distribution in any way. | |
| 1174 Note also that you can subclass 'Feature' to create your own specialized | |
| 1175 feature types that modify the distribution in other ways when included or | |
| 1176 excluded. See the docstrings for the various methods here for more detail. | |
| 1177 Aside from the methods, the only feature attributes that distributions look | |
| 1178 at are 'description' and 'optional'. | |
| 1179 """ | |
| 1180 | |
| 1181 @staticmethod | |
| 1182 def warn_deprecated(): | |
| 1183 msg = ( | |
| 1184 "Features are deprecated and will be removed in a future " | |
| 1185 "version. See https://github.com/pypa/setuptools/issues/65." | |
| 1186 ) | |
| 1187 warnings.warn(msg, DistDeprecationWarning, stacklevel=3) | |
| 1188 | |
| 1189 def __init__( | |
| 1190 self, description, standard=False, available=True, | |
| 1191 optional=True, require_features=(), remove=(), **extras): | |
| 1192 self.warn_deprecated() | |
| 1193 | |
| 1194 self.description = description | |
| 1195 self.standard = standard | |
| 1196 self.available = available | |
| 1197 self.optional = optional | |
| 1198 if isinstance(require_features, (str, Require)): | |
| 1199 require_features = require_features, | |
| 1200 | |
| 1201 self.require_features = [ | |
| 1202 r for r in require_features if isinstance(r, str) | |
| 1203 ] | |
| 1204 er = [r for r in require_features if not isinstance(r, str)] | |
| 1205 if er: | |
| 1206 extras['require_features'] = er | |
| 1207 | |
| 1208 if isinstance(remove, str): | |
| 1209 remove = remove, | |
| 1210 self.remove = remove | |
| 1211 self.extras = extras | |
| 1212 | |
| 1213 if not remove and not require_features and not extras: | |
| 1214 raise DistutilsSetupError( | |
| 1215 "Feature %s: must define 'require_features', 'remove', or " | |
| 1216 "at least one of 'packages', 'py_modules', etc." | |
| 1217 ) | |
| 1218 | |
| 1219 def include_by_default(self): | |
| 1220 """Should this feature be included by default?""" | |
| 1221 return self.available and self.standard | |
| 1222 | |
| 1223 def include_in(self, dist): | |
| 1224 """Ensure feature and its requirements are included in distribution | |
| 1225 | |
| 1226 You may override this in a subclass to perform additional operations on | |
| 1227 the distribution. Note that this method may be called more than once | |
| 1228 per feature, and so should be idempotent. | |
| 1229 | |
| 1230 """ | |
| 1231 | |
| 1232 if not self.available: | |
| 1233 raise DistutilsPlatformError( | |
| 1234 self.description + " is required, " | |
| 1235 "but is not available on this platform" | |
| 1236 ) | |
| 1237 | |
| 1238 dist.include(**self.extras) | |
| 1239 | |
| 1240 for f in self.require_features: | |
| 1241 dist.include_feature(f) | |
| 1242 | |
| 1243 def exclude_from(self, dist): | |
| 1244 """Ensure feature is excluded from distribution | |
| 1245 | |
| 1246 You may override this in a subclass to perform additional operations on | |
| 1247 the distribution. This method will be called at most once per | |
| 1248 feature, and only after all included features have been asked to | |
| 1249 include themselves. | |
| 1250 """ | |
| 1251 | |
| 1252 dist.exclude(**self.extras) | |
| 1253 | |
| 1254 if self.remove: | |
| 1255 for item in self.remove: | |
| 1256 dist.exclude_package(item) | |
| 1257 | |
| 1258 def validate(self, dist): | |
| 1259 """Verify that feature makes sense in context of distribution | |
| 1260 | |
| 1261 This method is called by the distribution just before it parses its | |
| 1262 command line. It checks to ensure that the 'remove' attribute, if any, | |
| 1263 contains only valid package/module names that are present in the base | |
| 1264 distribution when 'setup()' is called. You may override it in a | |
| 1265 subclass to perform any other required validation of the feature | |
| 1266 against a target distribution. | |
| 1267 """ | |
| 1268 | |
| 1269 for item in self.remove: | |
| 1270 if not dist.has_contents_for(item): | |
| 1271 raise DistutilsSetupError( | |
| 1272 "%s wants to be able to remove %s, but the distribution" | |
| 1273 " doesn't contain any packages or modules under %s" | |
| 1274 % (self.description, item, item) | |
| 1275 ) | |
| 1276 | |
| 1277 | |
| 1278 class DistDeprecationWarning(SetuptoolsDeprecationWarning): | |
| 1279 """Class for warning about deprecations in dist in | |
| 1280 setuptools. Not ignored by default, unlike DeprecationWarning.""" |
