| __all__ = ['Distribution'] |
| |
| from distutils.core import Distribution as _Distribution |
| from setuptools.depends import Require |
| from setuptools.command.install import install |
| from setuptools.command.sdist import sdist |
| from setuptools.command.install_lib import install_lib |
| from distutils.errors import DistutilsOptionError, DistutilsPlatformError |
| from distutils.errors import DistutilsSetupError |
| import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd |
| import os |
| |
| def _get_unpatched(cls): |
| """Protect against re-patching the distutils if reloaded |
| |
| Also ensures that no other distutils extension monkeypatched the distutils |
| first. |
| """ |
| while cls.__module__.startswith('setuptools'): |
| cls, = cls.__bases__ |
| if not cls.__module__.startswith('distutils'): |
| raise AssertionError( |
| "distutils has already been patched by %r" % cls |
| ) |
| return cls |
| |
| _Distribution = _get_unpatched(_Distribution) |
| |
| sequence = tuple, list |
| |
| def check_importable(dist, attr, value): |
| try: |
| ep = pkg_resources.EntryPoint.parse('x='+value) |
| assert not ep.extras |
| except (TypeError,ValueError,AttributeError,AssertionError): |
| raise DistutilsSetupError( |
| "%r must be importable 'module:attrs' string (got %r)" |
| % (attr,value) |
| ) |
| |
| |
| def assert_string_list(dist, attr, value): |
| """Verify that value is a string list or None""" |
| try: |
| assert ''.join(value)!=value |
| except (TypeError,ValueError,AttributeError,AssertionError): |
| raise DistutilsSetupError( |
| "%r must be a list of strings (got %r)" % (attr,value) |
| ) |
| |
| def check_nsp(dist, attr, value): |
| """Verify that namespace packages are valid""" |
| assert_string_list(dist,attr,value) |
| |
| for nsp in value: |
| if not dist.has_contents_for(nsp): |
| raise DistutilsSetupError( |
| "Distribution contains no modules or packages for " + |
| "namespace package %r" % nsp |
| ) |
| |
| def check_extras(dist, attr, value): |
| """Verify that extras_require mapping is valid""" |
| try: |
| for k,v in value.items(): |
| list(pkg_resources.parse_requirements(v)) |
| except (TypeError,ValueError,AttributeError): |
| raise DistutilsSetupError( |
| "'extras_require' must be a dictionary whose values are " |
| "strings or lists of strings containing valid project/version " |
| "requirement specifiers." |
| ) |
| |
| def assert_bool(dist, attr, value): |
| """Verify that value is True, False, 0, or 1""" |
| if bool(value) != value: |
| raise DistutilsSetupError( |
| "%r must be a boolean value (got %r)" % (attr,value) |
| ) |
| |
| |
| |
| def check_requirements(dist, attr, value): |
| """Verify that install_requires is a valid requirements list""" |
| try: |
| list(pkg_resources.parse_requirements(value)) |
| except (TypeError,ValueError): |
| raise DistutilsSetupError( |
| "%r must be a string or list of strings " |
| "containing valid project/version requirement specifiers" % (attr,) |
| ) |
| |
| def check_entry_points(dist, attr, value): |
| """Verify that entry_points map is parseable""" |
| try: |
| pkg_resources.EntryPoint.parse_map(value) |
| except ValueError, e: |
| raise DistutilsSetupError(e) |
| |
| |
| def check_test_suite(dist, attr, value): |
| if not isinstance(value,basestring): |
| raise DistutilsSetupError("test_suite must be a string") |
| |
| |
| def check_package_data(dist, attr, value): |
| """Verify that value is a dictionary of package names to glob lists""" |
| if isinstance(value,dict): |
| for k,v in value.items(): |
| if not isinstance(k,str): break |
| try: iter(v) |
| except TypeError: |
| break |
| else: |
| return |
| raise DistutilsSetupError( |
| attr+" must be a dictionary mapping package names to lists of " |
| "wildcard patterns" |
| ) |
| |
| |
| |
| |
| class Distribution(_Distribution): |
| """Distribution with support for features, tests, and package data |
| |
| This is an enhanced version of 'distutils.dist.Distribution' that |
| effectively adds the following new optional keyword arguments to 'setup()': |
| |
| 'install_requires' -- a string or sequence of strings specifying project |
| versions that the distribution requires when installed, in the format |
| used by 'pkg_resources.require()'. They will be installed |
| automatically when the package is installed. If you wish to use |
| packages that are not available in PyPI, or want to give your users an |
| alternate download location, you can add a 'find_links' option to the |
| '[easy_install]' section of your project's 'setup.cfg' file, and then |
| setuptools will scan the listed web pages for links that satisfy the |
| requirements. |
| |
| 'extras_require' -- a dictionary mapping names of optional "extras" to the |
| additional requirement(s) that using those extras incurs. For example, |
| this:: |
| |
| extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) |
| |
| indicates that the distribution can optionally provide an extra |
| capability called "reST", but it can only be used if docutils and |
| reSTedit are installed. If the user installs your package using |
| EasyInstall and requests one of your extras, the corresponding |
| additional requirements will be installed if needed. |
| |
| 'features' -- a dictionary mapping option names to 'setuptools.Feature' |
| objects. Features are a portion of the distribution that can be |
| included or excluded based on user options, inter-feature dependencies, |
| and availability on the current system. Excluded features are omitted |
| from all setup commands, including source and binary distributions, so |
| you can create multiple distributions from the same source tree. |
| Feature names should be valid Python identifiers, except that they may |
| contain the '-' (minus) sign. Features can be included or excluded |
| via the command line options '--with-X' and '--without-X', where 'X' is |
| the name of the feature. Whether a feature is included by default, and |
| whether you are allowed to control this from the command line, is |
| determined by the Feature object. See the 'Feature' class for more |
| information. |
| |
| 'test_suite' -- the name of a test suite to run for the 'test' command. |
| If the user runs 'python setup.py test', the package will be installed, |
| and the named test suite will be run. The format is the same as |
| would be used on a 'unittest.py' command line. That is, it is the |
| dotted name of an object to import and call to generate a test suite. |
| |
| 'package_data' -- a dictionary mapping package names to lists of filenames |
| or globs to use to find data files contained in the named packages. |
| If the dictionary has filenames or globs listed under '""' (the empty |
| string), those names will be searched for in every package, in addition |
| to any names for the specific package. Data files found using these |
| names/globs will be installed along with the package, in the same |
| location as the package. Note that globs are allowed to reference |
| the contents of non-package subdirectories, as long as you use '/' as |
| a path separator. (Globs are automatically converted to |
| platform-specific paths at runtime.) |
| |
| In addition to these new keywords, this class also has several new methods |
| for manipulating the distribution's contents. For example, the 'include()' |
| and 'exclude()' methods can be thought of as in-place add and subtract |
| commands that add or remove packages, modules, extensions, and so on from |
| the distribution. They are used by the feature subsystem to configure the |
| distribution for the included and excluded features. |
| """ |
| |
| _patched_dist = None |
| |
| def patch_missing_pkg_info(self, attrs): |
| # Fake up a replacement for the data that would normally come from |
| # PKG-INFO, but which might not yet be built if this is a fresh |
| # checkout. |
| # |
| if not attrs or 'name' not in attrs or 'version' not in attrs: |
| return |
| key = pkg_resources.safe_name(str(attrs['name'])).lower() |
| dist = pkg_resources.working_set.by_key.get(key) |
| if dist is not None and not dist.has_metadata('PKG-INFO'): |
| dist._version = pkg_resources.safe_version(str(attrs['version'])) |
| self._patched_dist = dist |
| |
| def __init__ (self, attrs=None): |
| have_package_data = hasattr(self, "package_data") |
| if not have_package_data: |
| self.package_data = {} |
| self.require_features = [] |
| self.features = {} |
| self.dist_files = [] |
| self.patch_missing_pkg_info(attrs) |
| # Make sure we have any eggs needed to interpret 'attrs' |
| if attrs and 'dependency_links' in attrs: |
| self.dependency_links = attrs.pop('dependency_links') |
| assert_string_list(self,'dependency_links',self.dependency_links) |
| if attrs and 'setup_requires' in attrs: |
| self.fetch_build_eggs(attrs.pop('setup_requires')) |
| for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): |
| if not hasattr(self,ep.name): |
| setattr(self,ep.name,None) |
| _Distribution.__init__(self,attrs) |
| if isinstance(self.metadata.version, (int,long,float)): |
| # Some people apparently take "version number" too literally :) |
| self.metadata.version = str(self.metadata.version) |
| |
| def parse_command_line(self): |
| """Process features after parsing command line options""" |
| result = _Distribution.parse_command_line(self) |
| if self.features: |
| self._finalize_features() |
| return result |
| |
| def _feature_attrname(self,name): |
| """Convert feature name to corresponding option attribute name""" |
| return 'with_'+name.replace('-','_') |
| |
| def fetch_build_eggs(self, requires): |
| """Resolve pre-setup requirements""" |
| from pkg_resources import working_set, parse_requirements |
| for dist in working_set.resolve( |
| parse_requirements(requires), installer=self.fetch_build_egg |
| ): |
| working_set.add(dist) |
| |
| def finalize_options(self): |
| _Distribution.finalize_options(self) |
| if self.features: |
| self._set_global_opts_from_features() |
| |
| for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): |
| value = getattr(self,ep.name,None) |
| if value is not None: |
| ep.require(installer=self.fetch_build_egg) |
| ep.load()(self, ep.name, value) |
| |
| def fetch_build_egg(self, req): |
| """Fetch an egg needed for building""" |
| try: |
| cmd = self._egg_fetcher |
| except AttributeError: |
| from setuptools.command.easy_install import easy_install |
| dist = self.__class__({'script_args':['easy_install']}) |
| dist.parse_config_files() |
| opts = dist.get_option_dict('easy_install') |
| keep = ( |
| 'find_links', 'site_dirs', 'index_url', 'optimize', |
| 'site_dirs', 'allow_hosts' |
| ) |
| for key in opts.keys(): |
| if key not in keep: |
| del opts[key] # don't use any other settings |
| if self.dependency_links: |
| links = self.dependency_links[:] |
| if 'find_links' in opts: |
| links = opts['find_links'][1].split() + links |
| opts['find_links'] = ('setup', links) |
| cmd = easy_install( |
| dist, args=["x"], install_dir=os.curdir, exclude_scripts=True, |
| always_copy=False, build_directory=None, editable=False, |
| upgrade=False, multi_version=True, no_report = True |
| ) |
| cmd.ensure_finalized() |
| self._egg_fetcher = cmd |
| return cmd.easy_install(req) |
| |
| def _set_global_opts_from_features(self): |
| """Add --with-X/--without-X options based on optional features""" |
| |
| go = [] |
| no = self.negative_opt.copy() |
| |
| for name,feature in self.features.items(): |
| self._set_feature(name,None) |
| feature.validate(self) |
| |
| if feature.optional: |
| descr = feature.description |
| incdef = ' (default)' |
| excdef='' |
| if not feature.include_by_default(): |
| excdef, incdef = incdef, excdef |
| |
| go.append(('with-'+name, None, 'include '+descr+incdef)) |
| go.append(('without-'+name, None, 'exclude '+descr+excdef)) |
| no['without-'+name] = 'with-'+name |
| |
| self.global_options = self.feature_options = go + self.global_options |
| self.negative_opt = self.feature_negopt = no |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def _finalize_features(self): |
| """Add/remove features and resolve dependencies between them""" |
| |
| # First, flag all the enabled items (and thus their dependencies) |
| for name,feature in self.features.items(): |
| enabled = self.feature_is_included(name) |
| if enabled or (enabled is None and feature.include_by_default()): |
| feature.include_in(self) |
| self._set_feature(name,1) |
| |
| # Then disable the rest, so that off-by-default features don't |
| # get flagged as errors when they're required by an enabled feature |
| for name,feature in self.features.items(): |
| if not self.feature_is_included(name): |
| feature.exclude_from(self) |
| self._set_feature(name,0) |
| |
| |
| def get_command_class(self, command): |
| """Pluggable version of get_command_class()""" |
| if command in self.cmdclass: |
| return self.cmdclass[command] |
| |
| for ep in pkg_resources.iter_entry_points('distutils.commands',command): |
| ep.require(installer=self.fetch_build_egg) |
| self.cmdclass[command] = cmdclass = ep.load() |
| return cmdclass |
| else: |
| return _Distribution.get_command_class(self, command) |
| |
| def print_commands(self): |
| for ep in pkg_resources.iter_entry_points('distutils.commands'): |
| if ep.name not in self.cmdclass: |
| cmdclass = ep.load(False) # don't require extras, we're not running |
| self.cmdclass[ep.name] = cmdclass |
| return _Distribution.print_commands(self) |
| |
| |
| |
| |
| |
| def _set_feature(self,name,status): |
| """Set feature's inclusion status""" |
| setattr(self,self._feature_attrname(name),status) |
| |
| def feature_is_included(self,name): |
| """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" |
| return getattr(self,self._feature_attrname(name)) |
| |
| def include_feature(self,name): |
| """Request inclusion of feature named 'name'""" |
| |
| if self.feature_is_included(name)==0: |
| descr = self.features[name].description |
| raise DistutilsOptionError( |
| descr + " is required, but was excluded or is not available" |
| ) |
| self.features[name].include_in(self) |
| self._set_feature(name,1) |
| |
| def include(self,**attrs): |
| """Add items to distribution that are named in keyword arguments |
| |
| For example, 'dist.exclude(py_modules=["x"])' would add 'x' to |
| the distribution's 'py_modules' attribute, if it was not already |
| there. |
| |
| Currently, this method only supports inclusion for attributes that are |
| lists or tuples. If you need to add support for adding to other |
| attributes in this or a subclass, you can add an '_include_X' method, |
| where 'X' is the name of the attribute. The method will be called with |
| the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' |
| will try to call 'dist._include_foo({"bar":"baz"})', which can then |
| handle whatever special inclusion logic is needed. |
| """ |
| for k,v in attrs.items(): |
| include = getattr(self, '_include_'+k, None) |
| if include: |
| include(v) |
| else: |
| self._include_misc(k,v) |
| |
| def exclude_package(self,package): |
| """Remove packages, modules, and extensions in named package""" |
| |
| pfx = package+'.' |
| if self.packages: |
| self.packages = [ |
| p for p in self.packages |
| if p<>package and not p.startswith(pfx) |
| ] |
| |
| if self.py_modules: |
| self.py_modules = [ |
| p for p in self.py_modules |
| if p<>package and not p.startswith(pfx) |
| ] |
| |
| if self.ext_modules: |
| self.ext_modules = [ |
| p for p in self.ext_modules |
| if p.name<>package and not p.name.startswith(pfx) |
| ] |
| |
| |
| def has_contents_for(self,package): |
| """Return true if 'exclude_package(package)' would do something""" |
| |
| pfx = package+'.' |
| |
| for p in self.iter_distribution_names(): |
| if p==package or p.startswith(pfx): |
| return True |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def _exclude_misc(self,name,value): |
| """Handle 'exclude()' for list/tuple attrs without a special handler""" |
| if not isinstance(value,sequence): |
| raise DistutilsSetupError( |
| "%s: setting must be a list or tuple (%r)" % (name, value) |
| ) |
| try: |
| old = getattr(self,name) |
| except AttributeError: |
| raise DistutilsSetupError( |
| "%s: No such distribution setting" % name |
| ) |
| if old is not None and not isinstance(old,sequence): |
| raise DistutilsSetupError( |
| name+": this setting cannot be changed via include/exclude" |
| ) |
| elif old: |
| setattr(self,name,[item for item in old if item not in value]) |
| |
| def _include_misc(self,name,value): |
| """Handle 'include()' for list/tuple attrs without a special handler""" |
| |
| if not isinstance(value,sequence): |
| raise DistutilsSetupError( |
| "%s: setting must be a list (%r)" % (name, value) |
| ) |
| try: |
| old = getattr(self,name) |
| except AttributeError: |
| raise DistutilsSetupError( |
| "%s: No such distribution setting" % name |
| ) |
| if old is None: |
| setattr(self,name,value) |
| elif not isinstance(old,sequence): |
| raise DistutilsSetupError( |
| name+": this setting cannot be changed via include/exclude" |
| ) |
| else: |
| setattr(self,name,old+[item for item in value if item not in old]) |
| |
| def exclude(self,**attrs): |
| """Remove items from distribution that are named in keyword arguments |
| |
| For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from |
| the distribution's 'py_modules' attribute. Excluding packages uses |
| the 'exclude_package()' method, so all of the package's contained |
| packages, modules, and extensions are also excluded. |
| |
| Currently, this method only supports exclusion from attributes that are |
| lists or tuples. If you need to add support for excluding from other |
| attributes in this or a subclass, you can add an '_exclude_X' method, |
| where 'X' is the name of the attribute. The method will be called with |
| the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' |
| will try to call 'dist._exclude_foo({"bar":"baz"})', which can then |
| handle whatever special exclusion logic is needed. |
| """ |
| for k,v in attrs.items(): |
| exclude = getattr(self, '_exclude_'+k, None) |
| if exclude: |
| exclude(v) |
| else: |
| self._exclude_misc(k,v) |
| |
| def _exclude_packages(self,packages): |
| if not isinstance(packages,sequence): |
| raise DistutilsSetupError( |
| "packages: setting must be a list or tuple (%r)" % (packages,) |
| ) |
| map(self.exclude_package, packages) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def _parse_command_opts(self, parser, args): |
| # Remove --with-X/--without-X options when processing command args |
| self.global_options = self.__class__.global_options |
| self.negative_opt = self.__class__.negative_opt |
| |
| # First, expand any aliases |
| command = args[0] |
| aliases = self.get_option_dict('aliases') |
| while command in aliases: |
| src,alias = aliases[command] |
| del aliases[command] # ensure each alias can expand only once! |
| import shlex |
| args[:1] = shlex.split(alias,True) |
| command = args[0] |
| |
| nargs = _Distribution._parse_command_opts(self, parser, args) |
| |
| # Handle commands that want to consume all remaining arguments |
| cmd_class = self.get_command_class(command) |
| if getattr(cmd_class,'command_consumes_arguments',None): |
| self.get_option_dict(command)['args'] = ("command line", nargs) |
| if nargs is not None: |
| return [] |
| |
| return nargs |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def get_cmdline_options(self): |
| """Return a '{cmd: {opt:val}}' map of all command-line options |
| |
| Option names are all long, but do not include the leading '--', and |
| contain dashes rather than underscores. If the option doesn't take |
| an argument (e.g. '--quiet'), the 'val' is 'None'. |
| |
| Note that options provided by config files are intentionally excluded. |
| """ |
| |
| d = {} |
| |
| for cmd,opts in self.command_options.items(): |
| |
| for opt,(src,val) in opts.items(): |
| |
| if src != "command line": |
| continue |
| |
| opt = opt.replace('_','-') |
| |
| if val==0: |
| cmdobj = self.get_command_obj(cmd) |
| neg_opt = self.negative_opt.copy() |
| neg_opt.update(getattr(cmdobj,'negative_opt',{})) |
| for neg,pos in neg_opt.items(): |
| if pos==opt: |
| opt=neg |
| val=None |
| break |
| else: |
| raise AssertionError("Shouldn't be able to get here") |
| |
| elif val==1: |
| val = None |
| |
| d.setdefault(cmd,{})[opt] = val |
| |
| return d |
| |
| |
| def iter_distribution_names(self): |
| """Yield all packages, modules, and extension names in distribution""" |
| |
| for pkg in self.packages or (): |
| yield pkg |
| |
| for module in self.py_modules or (): |
| yield module |
| |
| for ext in self.ext_modules or (): |
| if isinstance(ext,tuple): |
| name,buildinfo = ext |
| yield name |
| else: |
| yield ext.name |
| |
| # Install it throughout the distutils |
| for module in distutils.dist, distutils.core, distutils.cmd: |
| module.Distribution = Distribution |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class Feature: |
| """A subset of the distribution that can be excluded if unneeded/wanted |
| |
| Features are created using these keyword arguments: |
| |
| 'description' -- a short, human readable description of the feature, to |
| be used in error messages, and option help messages. |
| |
| 'standard' -- if true, the feature is included by default if it is |
| available on the current system. Otherwise, the feature is only |
| included if requested via a command line '--with-X' option, or if |
| another included feature requires it. The default setting is 'False'. |
| |
| 'available' -- if true, the feature is available for installation on the |
| current system. The default setting is 'True'. |
| |
| 'optional' -- if true, the feature's inclusion can be controlled from the |
| command line, using the '--with-X' or '--without-X' options. If |
| false, the feature's inclusion status is determined automatically, |
| based on 'availabile', 'standard', and whether any other feature |
| requires it. The default setting is 'True'. |
| |
| 'require_features' -- a string or sequence of strings naming features |
| that should also be included if this feature is included. Defaults to |
| empty list. May also contain 'Require' objects that should be |
| added/removed from the distribution. |
| |
| 'remove' -- a string or list of strings naming packages to be removed |
| from the distribution if this feature is *not* included. If the |
| feature *is* included, this argument is ignored. This argument exists |
| to support removing features that "crosscut" a distribution, such as |
| defining a 'tests' feature that removes all the 'tests' subpackages |
| provided by other features. The default for this argument is an empty |
| list. (Note: the named package(s) or modules must exist in the base |
| distribution when the 'setup()' function is initially called.) |
| |
| other keywords -- any other keyword arguments are saved, and passed to |
| the distribution's 'include()' and 'exclude()' methods when the |
| feature is included or excluded, respectively. So, for example, you |
| could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be |
| added or removed from the distribution as appropriate. |
| |
| A feature must include at least one 'requires', 'remove', or other |
| keyword argument. Otherwise, it can't affect the distribution in any way. |
| Note also that you can subclass 'Feature' to create your own specialized |
| feature types that modify the distribution in other ways when included or |
| excluded. See the docstrings for the various methods here for more detail. |
| Aside from the methods, the only feature attributes that distributions look |
| at are 'description' and 'optional'. |
| """ |
| def __init__(self, description, standard=False, available=True, |
| optional=True, require_features=(), remove=(), **extras |
| ): |
| |
| self.description = description |
| self.standard = standard |
| self.available = available |
| self.optional = optional |
| if isinstance(require_features,(str,Require)): |
| require_features = require_features, |
| |
| self.require_features = [ |
| r for r in require_features if isinstance(r,str) |
| ] |
| er = [r for r in require_features if not isinstance(r,str)] |
| if er: extras['require_features'] = er |
| |
| if isinstance(remove,str): |
| remove = remove, |
| self.remove = remove |
| self.extras = extras |
| |
| if not remove and not require_features and not extras: |
| raise DistutilsSetupError( |
| "Feature %s: must define 'require_features', 'remove', or at least one" |
| " of 'packages', 'py_modules', etc." |
| ) |
| |
| def include_by_default(self): |
| """Should this feature be included by default?""" |
| return self.available and self.standard |
| |
| def include_in(self,dist): |
| |
| """Ensure feature and its requirements are included in distribution |
| |
| You may override this in a subclass to perform additional operations on |
| the distribution. Note that this method may be called more than once |
| per feature, and so should be idempotent. |
| |
| """ |
| |
| if not self.available: |
| raise DistutilsPlatformError( |
| self.description+" is required," |
| "but is not available on this platform" |
| ) |
| |
| dist.include(**self.extras) |
| |
| for f in self.require_features: |
| dist.include_feature(f) |
| |
| |
| |
| def exclude_from(self,dist): |
| |
| """Ensure feature is excluded from distribution |
| |
| You may override this in a subclass to perform additional operations on |
| the distribution. This method will be called at most once per |
| feature, and only after all included features have been asked to |
| include themselves. |
| """ |
| |
| dist.exclude(**self.extras) |
| |
| if self.remove: |
| for item in self.remove: |
| dist.exclude_package(item) |
| |
| |
| |
| def validate(self,dist): |
| |
| """Verify that feature makes sense in context of distribution |
| |
| This method is called by the distribution just before it parses its |
| command line. It checks to ensure that the 'remove' attribute, if any, |
| contains only valid package/module names that are present in the base |
| distribution when 'setup()' is called. You may override it in a |
| subclass to perform any other required validation of the feature |
| against a target distribution. |
| """ |
| |
| for item in self.remove: |
| if not dist.has_contents_for(item): |
| raise DistutilsSetupError( |
| "%s wants to be able to remove %s, but the distribution" |
| " doesn't contain any packages or modules under %s" |
| % (self.description, item, item) |
| ) |