1__all__ = ['Distribution'] 2 3import re 4import os 5import warnings 6import numbers 7import distutils.log 8import distutils.core 9import distutils.cmd 10import distutils.dist 11from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, 12 DistutilsSetupError) 13from distutils.util import rfc822_escape 14 15import six 16from six.moves import map 17import packaging 18 19from setuptools.depends import Require 20from setuptools import windows_support 21from setuptools.monkey import get_unpatched 22from setuptools.config import parse_configuration 23import pkg_resources 24from .py36compat import Distribution_parse_config_files 25 26 27def _get_unpatched(cls): 28 warnings.warn("Do not call this function", DeprecationWarning) 29 return get_unpatched(cls) 30 31 32# Based on Python 3.5 version 33def write_pkg_file(self, file): 34 """Write the PKG-INFO format data to a file object. 35 """ 36 version = '1.0' 37 if (self.provides or self.requires or self.obsoletes or 38 self.classifiers or self.download_url): 39 version = '1.1' 40 # Setuptools specific for PEP 345 41 if hasattr(self, 'python_requires'): 42 version = '1.2' 43 44 file.write('Metadata-Version: %s\n' % version) 45 file.write('Name: %s\n' % self.get_name()) 46 file.write('Version: %s\n' % self.get_version()) 47 file.write('Summary: %s\n' % self.get_description()) 48 file.write('Home-page: %s\n' % self.get_url()) 49 file.write('Author: %s\n' % self.get_contact()) 50 file.write('Author-email: %s\n' % self.get_contact_email()) 51 file.write('License: %s\n' % self.get_license()) 52 if self.download_url: 53 file.write('Download-URL: %s\n' % self.download_url) 54 55 long_desc = rfc822_escape(self.get_long_description()) 56 file.write('Description: %s\n' % long_desc) 57 58 keywords = ','.join(self.get_keywords()) 59 if keywords: 60 file.write('Keywords: %s\n' % keywords) 61 62 self._write_list(file, 'Platform', self.get_platforms()) 63 self._write_list(file, 'Classifier', self.get_classifiers()) 64 65 # PEP 314 66 self._write_list(file, 'Requires', self.get_requires()) 67 self._write_list(file, 'Provides', self.get_provides()) 68 self._write_list(file, 'Obsoletes', self.get_obsoletes()) 69 70 # Setuptools specific for PEP 345 71 if hasattr(self, 'python_requires'): 72 file.write('Requires-Python: %s\n' % self.python_requires) 73 74 75# from Python 3.4 76def write_pkg_info(self, base_dir): 77 """Write the PKG-INFO file into the release tree. 78 """ 79 with open(os.path.join(base_dir, 'PKG-INFO'), 'w', 80 encoding='UTF-8') as pkg_info: 81 self.write_pkg_file(pkg_info) 82 83 84sequence = tuple, list 85 86 87def check_importable(dist, attr, value): 88 try: 89 ep = pkg_resources.EntryPoint.parse('x=' + value) 90 assert not ep.extras 91 except (TypeError, ValueError, AttributeError, AssertionError): 92 raise DistutilsSetupError( 93 "%r must be importable 'module:attrs' string (got %r)" 94 % (attr, value) 95 ) 96 97 98def assert_string_list(dist, attr, value): 99 """Verify that value is a string list or None""" 100 try: 101 assert ''.join(value) != value 102 except (TypeError, ValueError, AttributeError, AssertionError): 103 raise DistutilsSetupError( 104 "%r must be a list of strings (got %r)" % (attr, value) 105 ) 106 107 108def check_nsp(dist, attr, value): 109 """Verify that namespace packages are valid""" 110 ns_packages = value 111 assert_string_list(dist, attr, ns_packages) 112 for nsp in ns_packages: 113 if not dist.has_contents_for(nsp): 114 raise DistutilsSetupError( 115 "Distribution contains no modules or packages for " + 116 "namespace package %r" % nsp 117 ) 118 parent, sep, child = nsp.rpartition('.') 119 if parent and parent not in ns_packages: 120 distutils.log.warn( 121 "WARNING: %r is declared as a package namespace, but %r" 122 " is not: please correct this in setup.py", nsp, parent 123 ) 124 125 126def check_extras(dist, attr, value): 127 """Verify that extras_require mapping is valid""" 128 try: 129 for k, v in value.items(): 130 if ':' in k: 131 k, m = k.split(':', 1) 132 if pkg_resources.invalid_marker(m): 133 raise DistutilsSetupError("Invalid environment marker: " + m) 134 list(pkg_resources.parse_requirements(v)) 135 except (TypeError, ValueError, AttributeError): 136 raise DistutilsSetupError( 137 "'extras_require' must be a dictionary whose values are " 138 "strings or lists of strings containing valid project/version " 139 "requirement specifiers." 140 ) 141 142 143def assert_bool(dist, attr, value): 144 """Verify that value is True, False, 0, or 1""" 145 if bool(value) != value: 146 tmpl = "{attr!r} must be a boolean value (got {value!r})" 147 raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) 148 149 150def check_requirements(dist, attr, value): 151 """Verify that install_requires is a valid requirements list""" 152 try: 153 list(pkg_resources.parse_requirements(value)) 154 except (TypeError, ValueError) as error: 155 tmpl = ( 156 "{attr!r} must be a string or list of strings " 157 "containing valid project/version requirement specifiers; {error}" 158 ) 159 raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) 160 161 162def check_specifier(dist, attr, value): 163 """Verify that value is a valid version specifier""" 164 try: 165 packaging.specifiers.SpecifierSet(value) 166 except packaging.specifiers.InvalidSpecifier as error: 167 tmpl = ( 168 "{attr!r} must be a string or list of strings " 169 "containing valid version specifiers; {error}" 170 ) 171 raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) 172 173 174def check_entry_points(dist, attr, value): 175 """Verify that entry_points map is parseable""" 176 try: 177 pkg_resources.EntryPoint.parse_map(value) 178 except ValueError as e: 179 raise DistutilsSetupError(e) 180 181 182def check_test_suite(dist, attr, value): 183 if not isinstance(value, six.string_types): 184 raise DistutilsSetupError("test_suite must be a string") 185 186 187def check_package_data(dist, attr, value): 188 """Verify that value is a dictionary of package names to glob lists""" 189 if isinstance(value, dict): 190 for k, v in value.items(): 191 if not isinstance(k, str): 192 break 193 try: 194 iter(v) 195 except TypeError: 196 break 197 else: 198 return 199 raise DistutilsSetupError( 200 attr + " must be a dictionary mapping package names to lists of " 201 "wildcard patterns" 202 ) 203 204 205def check_packages(dist, attr, value): 206 for pkgname in value: 207 if not re.match(r'\w+(\.\w+)*', pkgname): 208 distutils.log.warn( 209 "WARNING: %r not a valid package name; please use only " 210 ".-separated package names in setup.py", pkgname 211 ) 212 213 214_Distribution = get_unpatched(distutils.core.Distribution) 215 216 217class Distribution(Distribution_parse_config_files, _Distribution): 218 """Distribution with support for features, tests, and package data 219 220 This is an enhanced version of 'distutils.dist.Distribution' that 221 effectively adds the following new optional keyword arguments to 'setup()': 222 223 'install_requires' -- a string or sequence of strings specifying project 224 versions that the distribution requires when installed, in the format 225 used by 'pkg_resources.require()'. They will be installed 226 automatically when the package is installed. If you wish to use 227 packages that are not available in PyPI, or want to give your users an 228 alternate download location, you can add a 'find_links' option to the 229 '[easy_install]' section of your project's 'setup.cfg' file, and then 230 setuptools will scan the listed web pages for links that satisfy the 231 requirements. 232 233 'extras_require' -- a dictionary mapping names of optional "extras" to the 234 additional requirement(s) that using those extras incurs. For example, 235 this:: 236 237 extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) 238 239 indicates that the distribution can optionally provide an extra 240 capability called "reST", but it can only be used if docutils and 241 reSTedit are installed. If the user installs your package using 242 EasyInstall and requests one of your extras, the corresponding 243 additional requirements will be installed if needed. 244 245 'features' **deprecated** -- a dictionary mapping option names to 246 'setuptools.Feature' 247 objects. Features are a portion of the distribution that can be 248 included or excluded based on user options, inter-feature dependencies, 249 and availability on the current system. Excluded features are omitted 250 from all setup commands, including source and binary distributions, so 251 you can create multiple distributions from the same source tree. 252 Feature names should be valid Python identifiers, except that they may 253 contain the '-' (minus) sign. Features can be included or excluded 254 via the command line options '--with-X' and '--without-X', where 'X' is 255 the name of the feature. Whether a feature is included by default, and 256 whether you are allowed to control this from the command line, is 257 determined by the Feature object. See the 'Feature' class for more 258 information. 259 260 'test_suite' -- the name of a test suite to run for the 'test' command. 261 If the user runs 'python setup.py test', the package will be installed, 262 and the named test suite will be run. The format is the same as 263 would be used on a 'unittest.py' command line. That is, it is the 264 dotted name of an object to import and call to generate a test suite. 265 266 'package_data' -- a dictionary mapping package names to lists of filenames 267 or globs to use to find data files contained in the named packages. 268 If the dictionary has filenames or globs listed under '""' (the empty 269 string), those names will be searched for in every package, in addition 270 to any names for the specific package. Data files found using these 271 names/globs will be installed along with the package, in the same 272 location as the package. Note that globs are allowed to reference 273 the contents of non-package subdirectories, as long as you use '/' as 274 a path separator. (Globs are automatically converted to 275 platform-specific paths at runtime.) 276 277 In addition to these new keywords, this class also has several new methods 278 for manipulating the distribution's contents. For example, the 'include()' 279 and 'exclude()' methods can be thought of as in-place add and subtract 280 commands that add or remove packages, modules, extensions, and so on from 281 the distribution. They are used by the feature subsystem to configure the 282 distribution for the included and excluded features. 283 """ 284 285 _patched_dist = None 286 287 def patch_missing_pkg_info(self, attrs): 288 # Fake up a replacement for the data that would normally come from 289 # PKG-INFO, but which might not yet be built if this is a fresh 290 # checkout. 291 # 292 if not attrs or 'name' not in attrs or 'version' not in attrs: 293 return 294 key = pkg_resources.safe_name(str(attrs['name'])).lower() 295 dist = pkg_resources.working_set.by_key.get(key) 296 if dist is not None and not dist.has_metadata('PKG-INFO'): 297 dist._version = pkg_resources.safe_version(str(attrs['version'])) 298 self._patched_dist = dist 299 300 def __init__(self, attrs=None): 301 have_package_data = hasattr(self, "package_data") 302 if not have_package_data: 303 self.package_data = {} 304 _attrs_dict = attrs or {} 305 if 'features' in _attrs_dict or 'require_features' in _attrs_dict: 306 Feature.warn_deprecated() 307 self.require_features = [] 308 self.features = {} 309 self.dist_files = [] 310 self.src_root = attrs and attrs.pop("src_root", None) 311 self.patch_missing_pkg_info(attrs) 312 # Make sure we have any eggs needed to interpret 'attrs' 313 if attrs is not None: 314 self.dependency_links = attrs.pop('dependency_links', []) 315 assert_string_list(self, 'dependency_links', self.dependency_links) 316 if attrs and 'setup_requires' in attrs: 317 self.fetch_build_eggs(attrs['setup_requires']) 318 for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): 319 vars(self).setdefault(ep.name, None) 320 _Distribution.__init__(self, attrs) 321 if isinstance(self.metadata.version, numbers.Number): 322 # Some people apparently take "version number" too literally :) 323 self.metadata.version = str(self.metadata.version) 324 325 if self.metadata.version is not None: 326 try: 327 ver = packaging.version.Version(self.metadata.version) 328 normalized_version = str(ver) 329 if self.metadata.version != normalized_version: 330 warnings.warn( 331 "Normalizing '%s' to '%s'" % ( 332 self.metadata.version, 333 normalized_version, 334 ) 335 ) 336 self.metadata.version = normalized_version 337 except (packaging.version.InvalidVersion, TypeError): 338 warnings.warn( 339 "The version specified (%r) is an invalid version, this " 340 "may not work as expected with newer versions of " 341 "setuptools, pip, and PyPI. Please see PEP 440 for more " 342 "details." % self.metadata.version 343 ) 344 if getattr(self, 'python_requires', None): 345 self.metadata.python_requires = self.python_requires 346 347 def parse_config_files(self, filenames=None): 348 """Parses configuration files from various levels 349 and loads configuration. 350 351 """ 352 _Distribution.parse_config_files(self, filenames=filenames) 353 354 parse_configuration(self, self.command_options) 355 356 def parse_command_line(self): 357 """Process features after parsing command line options""" 358 result = _Distribution.parse_command_line(self) 359 if self.features: 360 self._finalize_features() 361 return result 362 363 def _feature_attrname(self, name): 364 """Convert feature name to corresponding option attribute name""" 365 return 'with_' + name.replace('-', '_') 366 367 def fetch_build_eggs(self, requires): 368 """Resolve pre-setup requirements""" 369 resolved_dists = pkg_resources.working_set.resolve( 370 pkg_resources.parse_requirements(requires), 371 installer=self.fetch_build_egg, 372 replace_conflicting=True, 373 ) 374 for dist in resolved_dists: 375 pkg_resources.working_set.add(dist, replace=True) 376 return resolved_dists 377 378 def finalize_options(self): 379 _Distribution.finalize_options(self) 380 if self.features: 381 self._set_global_opts_from_features() 382 383 for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): 384 value = getattr(self, ep.name, None) 385 if value is not None: 386 ep.require(installer=self.fetch_build_egg) 387 ep.load()(self, ep.name, value) 388 if getattr(self, 'convert_2to3_doctests', None): 389 # XXX may convert to set here when we can rely on set being builtin 390 self.convert_2to3_doctests = [os.path.abspath(p) for p in self.convert_2to3_doctests] 391 else: 392 self.convert_2to3_doctests = [] 393 394 def get_egg_cache_dir(self): 395 egg_cache_dir = os.path.join(os.curdir, '.eggs') 396 if not os.path.exists(egg_cache_dir): 397 os.mkdir(egg_cache_dir) 398 windows_support.hide_file(egg_cache_dir) 399 readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') 400 with open(readme_txt_filename, 'w') as f: 401 f.write('This directory contains eggs that were downloaded ' 402 'by setuptools to build, test, and run plug-ins.\n\n') 403 f.write('This directory caches those eggs to prevent ' 404 'repeated downloads.\n\n') 405 f.write('However, it is safe to delete this directory.\n\n') 406 407 return egg_cache_dir 408 409 def fetch_build_egg(self, req): 410 """Fetch an egg needed for building""" 411 412 try: 413 cmd = self._egg_fetcher 414 cmd.package_index.to_scan = [] 415 except AttributeError: 416 from setuptools.command.easy_install import easy_install 417 dist = self.__class__({'script_args': ['easy_install']}) 418 dist.parse_config_files() 419 opts = dist.get_option_dict('easy_install') 420 keep = ( 421 'find_links', 'site_dirs', 'index_url', 'optimize', 422 'site_dirs', 'allow_hosts' 423 ) 424 for key in list(opts): 425 if key not in keep: 426 del opts[key] # don't use any other settings 427 if self.dependency_links: 428 links = self.dependency_links[:] 429 if 'find_links' in opts: 430 links = opts['find_links'][1].split() + links 431 opts['find_links'] = ('setup', links) 432 install_dir = self.get_egg_cache_dir() 433 cmd = easy_install( 434 dist, args=["x"], install_dir=install_dir, exclude_scripts=True, 435 always_copy=False, build_directory=None, editable=False, 436 upgrade=False, multi_version=True, no_report=True, user=False 437 ) 438 cmd.ensure_finalized() 439 self._egg_fetcher = cmd 440 return cmd.easy_install(req) 441 442 def _set_global_opts_from_features(self): 443 """Add --with-X/--without-X options based on optional features""" 444 445 go = [] 446 no = self.negative_opt.copy() 447 448 for name, feature in self.features.items(): 449 self._set_feature(name, None) 450 feature.validate(self) 451 452 if feature.optional: 453 descr = feature.description 454 incdef = ' (default)' 455 excdef = '' 456 if not feature.include_by_default(): 457 excdef, incdef = incdef, excdef 458 459 go.append(('with-' + name, None, 'include ' + descr + incdef)) 460 go.append(('without-' + name, None, 'exclude ' + descr + excdef)) 461 no['without-' + name] = 'with-' + name 462 463 self.global_options = self.feature_options = go + self.global_options 464 self.negative_opt = self.feature_negopt = no 465 466 def _finalize_features(self): 467 """Add/remove features and resolve dependencies between them""" 468 469 # First, flag all the enabled items (and thus their dependencies) 470 for name, feature in self.features.items(): 471 enabled = self.feature_is_included(name) 472 if enabled or (enabled is None and feature.include_by_default()): 473 feature.include_in(self) 474 self._set_feature(name, 1) 475 476 # Then disable the rest, so that off-by-default features don't 477 # get flagged as errors when they're required by an enabled feature 478 for name, feature in self.features.items(): 479 if not self.feature_is_included(name): 480 feature.exclude_from(self) 481 self._set_feature(name, 0) 482 483 def get_command_class(self, command): 484 """Pluggable version of get_command_class()""" 485 if command in self.cmdclass: 486 return self.cmdclass[command] 487 488 for ep in pkg_resources.iter_entry_points('distutils.commands', command): 489 ep.require(installer=self.fetch_build_egg) 490 self.cmdclass[command] = cmdclass = ep.load() 491 return cmdclass 492 else: 493 return _Distribution.get_command_class(self, command) 494 495 def print_commands(self): 496 for ep in pkg_resources.iter_entry_points('distutils.commands'): 497 if ep.name not in self.cmdclass: 498 # don't require extras as the commands won't be invoked 499 cmdclass = ep.resolve() 500 self.cmdclass[ep.name] = cmdclass 501 return _Distribution.print_commands(self) 502 503 def get_command_list(self): 504 for ep in pkg_resources.iter_entry_points('distutils.commands'): 505 if ep.name not in self.cmdclass: 506 # don't require extras as the commands won't be invoked 507 cmdclass = ep.resolve() 508 self.cmdclass[ep.name] = cmdclass 509 return _Distribution.get_command_list(self) 510 511 def _set_feature(self, name, status): 512 """Set feature's inclusion status""" 513 setattr(self, self._feature_attrname(name), status) 514 515 def feature_is_included(self, name): 516 """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" 517 return getattr(self, self._feature_attrname(name)) 518 519 def include_feature(self, name): 520 """Request inclusion of feature named 'name'""" 521 522 if self.feature_is_included(name) == 0: 523 descr = self.features[name].description 524 raise DistutilsOptionError( 525 descr + " is required, but was excluded or is not available" 526 ) 527 self.features[name].include_in(self) 528 self._set_feature(name, 1) 529 530 def include(self, **attrs): 531 """Add items to distribution that are named in keyword arguments 532 533 For example, 'dist.exclude(py_modules=["x"])' would add 'x' to 534 the distribution's 'py_modules' attribute, if it was not already 535 there. 536 537 Currently, this method only supports inclusion for attributes that are 538 lists or tuples. If you need to add support for adding to other 539 attributes in this or a subclass, you can add an '_include_X' method, 540 where 'X' is the name of the attribute. The method will be called with 541 the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' 542 will try to call 'dist._include_foo({"bar":"baz"})', which can then 543 handle whatever special inclusion logic is needed. 544 """ 545 for k, v in attrs.items(): 546 include = getattr(self, '_include_' + k, None) 547 if include: 548 include(v) 549 else: 550 self._include_misc(k, v) 551 552 def exclude_package(self, package): 553 """Remove packages, modules, and extensions in named package""" 554 555 pfx = package + '.' 556 if self.packages: 557 self.packages = [ 558 p for p in self.packages 559 if p != package and not p.startswith(pfx) 560 ] 561 562 if self.py_modules: 563 self.py_modules = [ 564 p for p in self.py_modules 565 if p != package and not p.startswith(pfx) 566 ] 567 568 if self.ext_modules: 569 self.ext_modules = [ 570 p for p in self.ext_modules 571 if p.name != package and not p.name.startswith(pfx) 572 ] 573 574 def has_contents_for(self, package): 575 """Return true if 'exclude_package(package)' would do something""" 576 577 pfx = package + '.' 578 579 for p in self.iter_distribution_names(): 580 if p == package or p.startswith(pfx): 581 return True 582 583 def _exclude_misc(self, name, value): 584 """Handle 'exclude()' for list/tuple attrs without a special handler""" 585 if not isinstance(value, sequence): 586 raise DistutilsSetupError( 587 "%s: setting must be a list or tuple (%r)" % (name, value) 588 ) 589 try: 590 old = getattr(self, name) 591 except AttributeError: 592 raise DistutilsSetupError( 593 "%s: No such distribution setting" % name 594 ) 595 if old is not None and not isinstance(old, sequence): 596 raise DistutilsSetupError( 597 name + ": this setting cannot be changed via include/exclude" 598 ) 599 elif old: 600 setattr(self, name, [item for item in old if item not in value]) 601 602 def _include_misc(self, name, value): 603 """Handle 'include()' for list/tuple attrs without a special handler""" 604 605 if not isinstance(value, sequence): 606 raise DistutilsSetupError( 607 "%s: setting must be a list (%r)" % (name, value) 608 ) 609 try: 610 old = getattr(self, name) 611 except AttributeError: 612 raise DistutilsSetupError( 613 "%s: No such distribution setting" % name 614 ) 615 if old is None: 616 setattr(self, name, value) 617 elif not isinstance(old, sequence): 618 raise DistutilsSetupError( 619 name + ": this setting cannot be changed via include/exclude" 620 ) 621 else: 622 setattr(self, name, old + [item for item in value if item not in old]) 623 624 def exclude(self, **attrs): 625 """Remove items from distribution that are named in keyword arguments 626 627 For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from 628 the distribution's 'py_modules' attribute. Excluding packages uses 629 the 'exclude_package()' method, so all of the package's contained 630 packages, modules, and extensions are also excluded. 631 632 Currently, this method only supports exclusion from attributes that are 633 lists or tuples. If you need to add support for excluding from other 634 attributes in this or a subclass, you can add an '_exclude_X' method, 635 where 'X' is the name of the attribute. The method will be called with 636 the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' 637 will try to call 'dist._exclude_foo({"bar":"baz"})', which can then 638 handle whatever special exclusion logic is needed. 639 """ 640 for k, v in attrs.items(): 641 exclude = getattr(self, '_exclude_' + k, None) 642 if exclude: 643 exclude(v) 644 else: 645 self._exclude_misc(k, v) 646 647 def _exclude_packages(self, packages): 648 if not isinstance(packages, sequence): 649 raise DistutilsSetupError( 650 "packages: setting must be a list or tuple (%r)" % (packages,) 651 ) 652 list(map(self.exclude_package, packages)) 653 654 def _parse_command_opts(self, parser, args): 655 # Remove --with-X/--without-X options when processing command args 656 self.global_options = self.__class__.global_options 657 self.negative_opt = self.__class__.negative_opt 658 659 # First, expand any aliases 660 command = args[0] 661 aliases = self.get_option_dict('aliases') 662 while command in aliases: 663 src, alias = aliases[command] 664 del aliases[command] # ensure each alias can expand only once! 665 import shlex 666 args[:1] = shlex.split(alias, True) 667 command = args[0] 668 669 nargs = _Distribution._parse_command_opts(self, parser, args) 670 671 # Handle commands that want to consume all remaining arguments 672 cmd_class = self.get_command_class(command) 673 if getattr(cmd_class, 'command_consumes_arguments', None): 674 self.get_option_dict(command)['args'] = ("command line", nargs) 675 if nargs is not None: 676 return [] 677 678 return nargs 679 680 def get_cmdline_options(self): 681 """Return a '{cmd: {opt:val}}' map of all command-line options 682 683 Option names are all long, but do not include the leading '--', and 684 contain dashes rather than underscores. If the option doesn't take 685 an argument (e.g. '--quiet'), the 'val' is 'None'. 686 687 Note that options provided by config files are intentionally excluded. 688 """ 689 690 d = {} 691 692 for cmd, opts in self.command_options.items(): 693 694 for opt, (src, val) in opts.items(): 695 696 if src != "command line": 697 continue 698 699 opt = opt.replace('_', '-') 700 701 if val == 0: 702 cmdobj = self.get_command_obj(cmd) 703 neg_opt = self.negative_opt.copy() 704 neg_opt.update(getattr(cmdobj, 'negative_opt', {})) 705 for neg, pos in neg_opt.items(): 706 if pos == opt: 707 opt = neg 708 val = None 709 break 710 else: 711 raise AssertionError("Shouldn't be able to get here") 712 713 elif val == 1: 714 val = None 715 716 d.setdefault(cmd, {})[opt] = val 717 718 return d 719 720 def iter_distribution_names(self): 721 """Yield all packages, modules, and extension names in distribution""" 722 723 for pkg in self.packages or (): 724 yield pkg 725 726 for module in self.py_modules or (): 727 yield module 728 729 for ext in self.ext_modules or (): 730 if isinstance(ext, tuple): 731 name, buildinfo = ext 732 else: 733 name = ext.name 734 if name.endswith('module'): 735 name = name[:-6] 736 yield name 737 738 def handle_display_options(self, option_order): 739 """If there were any non-global "display-only" options 740 (--help-commands or the metadata display options) on the command 741 line, display the requested info and return true; else return 742 false. 743 """ 744 import sys 745 746 if six.PY2 or self.help_commands: 747 return _Distribution.handle_display_options(self, option_order) 748 749 # Stdout may be StringIO (e.g. in tests) 750 import io 751 if not isinstance(sys.stdout, io.TextIOWrapper): 752 return _Distribution.handle_display_options(self, option_order) 753 754 # Don't wrap stdout if utf-8 is already the encoding. Provides 755 # workaround for #334. 756 if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): 757 return _Distribution.handle_display_options(self, option_order) 758 759 # Print metadata in UTF-8 no matter the platform 760 encoding = sys.stdout.encoding 761 errors = sys.stdout.errors 762 newline = sys.platform != 'win32' and '\n' or None 763 line_buffering = sys.stdout.line_buffering 764 765 sys.stdout = io.TextIOWrapper( 766 sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) 767 try: 768 return _Distribution.handle_display_options(self, option_order) 769 finally: 770 sys.stdout = io.TextIOWrapper( 771 sys.stdout.detach(), encoding, errors, newline, line_buffering) 772 773 774class Feature: 775 """ 776 **deprecated** -- The `Feature` facility was never completely implemented 777 or supported, `has reported issues 778 <https://github.com/pypa/setuptools/issues/58>`_ and will be removed in 779 a future version. 780 781 A subset of the distribution that can be excluded if unneeded/wanted 782 783 Features are created using these keyword arguments: 784 785 'description' -- a short, human readable description of the feature, to 786 be used in error messages, and option help messages. 787 788 'standard' -- if true, the feature is included by default if it is 789 available on the current system. Otherwise, the feature is only 790 included if requested via a command line '--with-X' option, or if 791 another included feature requires it. The default setting is 'False'. 792 793 'available' -- if true, the feature is available for installation on the 794 current system. The default setting is 'True'. 795 796 'optional' -- if true, the feature's inclusion can be controlled from the 797 command line, using the '--with-X' or '--without-X' options. If 798 false, the feature's inclusion status is determined automatically, 799 based on 'availabile', 'standard', and whether any other feature 800 requires it. The default setting is 'True'. 801 802 'require_features' -- a string or sequence of strings naming features 803 that should also be included if this feature is included. Defaults to 804 empty list. May also contain 'Require' objects that should be 805 added/removed from the distribution. 806 807 'remove' -- a string or list of strings naming packages to be removed 808 from the distribution if this feature is *not* included. If the 809 feature *is* included, this argument is ignored. This argument exists 810 to support removing features that "crosscut" a distribution, such as 811 defining a 'tests' feature that removes all the 'tests' subpackages 812 provided by other features. The default for this argument is an empty 813 list. (Note: the named package(s) or modules must exist in the base 814 distribution when the 'setup()' function is initially called.) 815 816 other keywords -- any other keyword arguments are saved, and passed to 817 the distribution's 'include()' and 'exclude()' methods when the 818 feature is included or excluded, respectively. So, for example, you 819 could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be 820 added or removed from the distribution as appropriate. 821 822 A feature must include at least one 'requires', 'remove', or other 823 keyword argument. Otherwise, it can't affect the distribution in any way. 824 Note also that you can subclass 'Feature' to create your own specialized 825 feature types that modify the distribution in other ways when included or 826 excluded. See the docstrings for the various methods here for more detail. 827 Aside from the methods, the only feature attributes that distributions look 828 at are 'description' and 'optional'. 829 """ 830 831 @staticmethod 832 def warn_deprecated(): 833 warnings.warn( 834 "Features are deprecated and will be removed in a future " 835 "version. See https://github.com/pypa/setuptools/issues/65.", 836 DeprecationWarning, 837 stacklevel=3, 838 ) 839 840 def __init__(self, description, standard=False, available=True, 841 optional=True, require_features=(), remove=(), **extras): 842 self.warn_deprecated() 843 844 self.description = description 845 self.standard = standard 846 self.available = available 847 self.optional = optional 848 if isinstance(require_features, (str, Require)): 849 require_features = require_features, 850 851 self.require_features = [ 852 r for r in require_features if isinstance(r, str) 853 ] 854 er = [r for r in require_features if not isinstance(r, str)] 855 if er: 856 extras['require_features'] = er 857 858 if isinstance(remove, str): 859 remove = remove, 860 self.remove = remove 861 self.extras = extras 862 863 if not remove and not require_features and not extras: 864 raise DistutilsSetupError( 865 "Feature %s: must define 'require_features', 'remove', or at least one" 866 " of 'packages', 'py_modules', etc." 867 ) 868 869 def include_by_default(self): 870 """Should this feature be included by default?""" 871 return self.available and self.standard 872 873 def include_in(self, dist): 874 """Ensure feature and its requirements are included in distribution 875 876 You may override this in a subclass to perform additional operations on 877 the distribution. Note that this method may be called more than once 878 per feature, and so should be idempotent. 879 880 """ 881 882 if not self.available: 883 raise DistutilsPlatformError( 884 self.description + " is required, " 885 "but is not available on this platform" 886 ) 887 888 dist.include(**self.extras) 889 890 for f in self.require_features: 891 dist.include_feature(f) 892 893 def exclude_from(self, dist): 894 """Ensure feature is excluded from distribution 895 896 You may override this in a subclass to perform additional operations on 897 the distribution. This method will be called at most once per 898 feature, and only after all included features have been asked to 899 include themselves. 900 """ 901 902 dist.exclude(**self.extras) 903 904 if self.remove: 905 for item in self.remove: 906 dist.exclude_package(item) 907 908 def validate(self, dist): 909 """Verify that feature makes sense in context of distribution 910 911 This method is called by the distribution just before it parses its 912 command line. It checks to ensure that the 'remove' attribute, if any, 913 contains only valid package/module names that are present in the base 914 distribution when 'setup()' is called. You may override it in a 915 subclass to perform any other required validation of the feature 916 against a target distribution. 917 """ 918 919 for item in self.remove: 920 if not dist.has_contents_for(item): 921 raise DistutilsSetupError( 922 "%s wants to be able to remove %s, but the distribution" 923 " doesn't contain any packages or modules under %s" 924 % (self.description, item, item) 925 ) 926