Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: recipe_engine/third_party/setuptools/dist.py

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

Powered by Google App Engine
This is Rietveld 408576698