comparison env/lib/python3.9/site-packages/setuptools/dist.py @ 0:4f3585e2f14b draft default tip

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