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