diff options
| author | PJ Eby <distutils-sig@python.org> | 2005-08-06 18:46:28 +0000 |
|---|---|---|
| committer | PJ Eby <distutils-sig@python.org> | 2005-08-06 18:46:28 +0000 |
| commit | 8a29467d941a7983d5f6eadc5c0e1624417944b6 (patch) | |
| tree | b270afe3a01c9bead94060de3c3adfa590bd933f /setuptools/dist.py | |
| parent | a762d97ea517f64a405d82ad7acaa85d3eb30c39 (diff) | |
| download | python-setuptools-git-8a29467d941a7983d5f6eadc5c0e1624417944b6.tar.gz | |
Enhanced setuptools infrastructure to support distutils extensions that
can be plugged in at setup() time to define new setup() arguments or
distutils commands. This allows modularization and reuse of distutils
extensions in a way that was previously not possible.
--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041180
Diffstat (limited to 'setuptools/dist.py')
| -rw-r--r-- | setuptools/dist.py | 296 |
1 files changed, 189 insertions, 107 deletions
diff --git a/setuptools/dist.py b/setuptools/dist.py index 40234b4e..6d226d68 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -9,36 +9,118 @@ 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 - -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): - self.cmdclass[command] = cmdclass = ep.load() - return cmdclass - else: - return _old_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 _old_print_commands(self) - -for meth in 'print_commands', 'get_command_class': - if getattr(_Distribution,meth).im_func.func_globals is not globals(): - globals()['_old_'+meth] = getattr(_Distribution,meth) - setattr(_Distribution, meth, globals()[meth]) +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 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: + for name in dist.iter_distribution_names(): + if name.startswith(nsp+'.'): break + else: + 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_install_requires(dist, attr, value): + """Verify that install_requires is a valid requirements list""" + try: + list(pkg_resources.parse_requirements(value)) + except (TypeError,ValueError): + raise DistutilsSetupError( + "'install_requires' must be a string or list of strings " + "containing valid project/version requirement specifiers" + ) + +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") + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -125,16 +207,19 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} + self.features = {} - self.test_suite = None self.requires = [] - self.install_requires = [] - self.extras_require = {} self.dist_files = [] - self.zip_safe = None - self.namespace_packages = None - self.eager_resources = None - self.entry_points = None + + if attrs and 'setup_requires' in attrs: + # Make sure we have any eggs needed to interpret '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) @@ -145,20 +230,17 @@ class Distribution(_Distribution): 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) @@ -174,49 +256,34 @@ class Distribution(_Distribution): "setuptools. Please remove it from your setup script." ) - try: - list(pkg_resources.parse_requirements(self.install_requires)) - except (TypeError,ValueError): - raise DistutilsSetupError( - "'install_requires' must be a string or list of strings " - "containing valid project/version requirement specifiers" - ) + 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: - for k,v in self.extras_require.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." + cmd = self._egg_fetcher + except AttributeError: + from setuptools.command.easy_install import easy_install + cmd = easy_install( + self.__class__({'script_args':['easy_install']}), + args="x", install_dir=os.curdir, exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False ) + cmd.ensure_finalized() + cmd.zip_ok = None # override any setup.cfg setting for these + cmd.build_directory = None + self._egg_fetcher = cmd - for attr in 'namespace_packages','eager_resources': - value = getattr(self,attr,None) - if value is not None: - try: - assert ''.join(value)!=value - except (TypeError,ValueError,AttributeError,AssertionError): - raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr,value) - ) + return cmd.easy_install(req) - for nsp in self.namespace_packages or (): - for name in iter_distribution_names(self): - if name.startswith(nsp+'.'): break - else: - raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp - ) - if self.entry_points is not None: - try: - pkg_resources.EntryPoint.parse_map(self.entry_points) - except ValueError, e: - raise DistutilsSetupError(e) def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -244,22 +311,7 @@ class Distribution(_Distribution): - 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) @@ -274,12 +326,42 @@ class Distribution(_Distribution): + 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) @@ -572,25 +654,25 @@ class Distribution(_Distribution): return d -def iter_distribution_names(distribution): - """Yield all packages, modules, and extensions declared by distribution""" - - for pkg in distribution.packages or (): - yield pkg - - for module in distribution.py_modules or (): - yield module - - for ext in distribution.ext_modules or (): - if isinstance(ext,tuple): - name,buildinfo = ext - yield name - else: - yield ext.name + 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 |
