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