[Python-checkins] python/nondist/sandbox/setuptools/setuptools __init__.py, 1.21, 1.22 dist.py, 1.17, 1.18 extension.py, 1.1, 1.2

pje@users.sourceforge.net pje at users.sourceforge.net
Sat Aug 6 20:46:30 CEST 2005


Update of /cvsroot/python/python/nondist/sandbox/setuptools/setuptools
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8052/setuptools

Modified Files:
	__init__.py dist.py extension.py 
Log Message:
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.


Index: __init__.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/__init__.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -d -r1.21 -r1.22
--- __init__.py	18 Jul 2005 02:06:15 -0000	1.21
+++ __init__.py	6 Aug 2005 18:46:27 -0000	1.22
@@ -1,6 +1,6 @@
 """Extensions to the 'distutils' for large or complex distributions"""
+from setuptools.dist import Distribution, Feature, _get_unpatched
 import distutils.core, setuptools.command
-from setuptools.dist import Distribution, Feature
 from setuptools.extension import Extension
 from setuptools.depends import Require
 from distutils.core import Command as _Command
@@ -39,17 +39,9 @@
         out = [item for item in out if not fnmatchcase(item,pat)]
     return out
 
-def setup(**attrs):
-    """Do package setup
-
-    This function takes the same arguments as 'distutils.core.setup()', except
-    that the default distribution class is 'setuptools.dist.Distribution'.  See
-    that class' documentation for details on the new keyword arguments that it
-    makes available via this function.
-    """
-    attrs.setdefault("distclass",Distribution)
-    return distutils.core.setup(**attrs)
+setup = distutils.core.setup
     
+_Command = _get_unpatched(_Command)
 
 class Command(_Command):
     __doc__ = _Command.__doc__
@@ -68,6 +60,14 @@
             setattr(cmd,k,v)    # update command with keywords
         return cmd
 
+import distutils.core
+distutils.core.Command = Command    # we can't patch distutils.cmd, alas
+
+
+
+
+
+
 
 
 

Index: dist.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/dist.py,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- dist.py	25 Jul 2005 03:12:51 -0000	1.17
+++ dist.py	6 Aug 2005 18:46:27 -0000	1.18
@@ -9,36 +9,118 @@
 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]
+import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd
+import os
 
-    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 _get_unpatched(cls):
+    """Protect against re-patching the distutils if reloaded
 
-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)
+    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
 
-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])
+_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 @@
         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 @@
             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 @@
                 "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 @@
 
 
 
-    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 @@
 
 
 
+    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 @@
         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
 
 
 

Index: extension.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools/extension.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- extension.py	19 Mar 2004 20:53:14 -0000	1.1
+++ extension.py	6 Aug 2005 18:46:27 -0000	1.2
@@ -7,6 +7,9 @@
 
     # Pyrex isn't around, so fix up the sources
 
+    from dist import _get_unpatched
+    _Extension = _get_unpatched(_Extension)
+
     class Extension(_Extension):
 
         """Extension that uses '.c' files in place of '.pyx' files"""
@@ -21,7 +24,14 @@
                     sources.append(s)
             self.sources = sources
 
+    import sys, distutils.core, distutils.extension
+    distutils.core.Extension = Extension
+    distutils.extension.Extension = Extension
+    if 'distutils.command.build_ext' in sys.modules:
+        sys.modules['distutils.command.build_ext'].Extension = Extension
+
 else:
 
     # Pyrex is here, just use regular extension type
     Extension = _Extension
+



More information about the Python-checkins mailing list