Mercurial > repos > shellac > sam_consensus_v3
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.""" |