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