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 |
__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 |
) |
817 |
|