aboutsummaryrefslogtreecommitdiffstats
path: root/setuptools
diff options
context:
space:
mode:
authorPJ Eby <distutils-sig@python.org>2005-07-24 22:47:06 +0000
committerPJ Eby <distutils-sig@python.org>2005-07-24 22:47:06 +0000
commit1c40632b88d76aea178e751483645ec3d32c81d9 (patch)
tree2cc3fa1af58200d5199b30771053c527ef91bd93 /setuptools
parent8618cfa8ac93431ffcede4f3987b559449bbbcb8 (diff)
downloadexternal_python_setuptools-1c40632b88d76aea178e751483645ec3d32c81d9.tar.gz
external_python_setuptools-1c40632b88d76aea178e751483645ec3d32c81d9.tar.bz2
external_python_setuptools-1c40632b88d76aea178e751483645ec3d32c81d9.zip
Implement "entry points" for dynamic discovery of drivers and plugins.
Change setuptools to discover setup commands using an entry point group called "distutils.commands". Thanks to Ian Bicking for the suggestion that led to designing this super-cool feature. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041152
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/command/__init__.py13
-rwxr-xr-xsetuptools/command/egg_info.py38
-rw-r--r--setuptools/dist.py69
-rw-r--r--setuptools/tests/test_resources.py84
4 files changed, 160 insertions, 44 deletions
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index 29f3000d..a58b5344 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -1,17 +1,10 @@
-import distutils.command
-
__all__ = [
- 'test', 'develop', 'bdist_egg', 'saveopts', 'setopt', 'rotate', 'alias'
+ 'alias', 'bdist_egg', 'build_ext', 'build_py', 'depends', 'develop',
+ 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
+ 'sdist', 'setopt', 'test', 'upload',
]
-# Make our commands available as though they were part of the distutils
-
-distutils.command.__path__.extend(__path__)
-distutils.command.__all__.extend(
- [cmd for cmd in __all__ if cmd not in distutils.command.__all__]
- )
-
from distutils.command.bdist import bdist
if 'egg' not in bdist.format_commands:
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index a5418568..8577230f 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -8,7 +8,7 @@ from setuptools import Command
from distutils.errors import *
from distutils import log
from pkg_resources import parse_requirements, safe_name, \
- safe_version, yield_lines
+ safe_version, yield_lines, EntryPoint
from setuptools.dist import iter_distribution_names
class egg_info(Command):
@@ -95,7 +95,7 @@ class egg_info(Command):
metadata.write_pkg_info(self.egg_info)
finally:
metadata.name, metadata.version = oldname, oldver
-
+ self.write_entry_points()
self.write_requirements()
self.write_toplevel_names()
self.write_or_delete_dist_arg('namespace_packages')
@@ -183,23 +183,23 @@ class egg_info(Command):
if not self.dry_run:
os.unlink(filename)
+ def write_entry_points(self):
+ ep = getattr(self.distribution,'entry_points',None)
+ if ep is None:
+ return
+ epname = os.path.join(self.egg_info,"entry_points.txt")
+ log.info("writing %s", epname)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ if not self.dry_run:
+ f = open(epname, 'wt')
+ if isinstance(ep,basestring):
+ f.write(ep)
+ else:
+ for section, contents in ep.items():
+ if not isinstance(contents,basestring):
+ contents = EntryPoint.parse_list(section, contents)
+ contents = '\n'.join(map(str,contents.values()))
+ f.write('[%s]\n%s\n\n' % (section,contents))
+ f.close()
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 3c7ff852..a603ade0 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -11,6 +11,32 @@ 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 dist in pkg_resources.working_set:
+ if dist.get_entry_info('distutils.commands',command):
+ cmdclass = dist.load_entry_point('distutils.commands',command)
+ self.cmdclass[command] = cmdclass
+ return cmdclass
+ else:
+ return _old_get_command_class(self, command)
+
+def print_commands(self):
+ for dist in pkg_resources.working_set:
+ for cmd,ep in dist.get_entry_map('distutils.commands').items():
+ if cmd not in self.cmdclass:
+ cmdclass = ep.load() # don't require extras, we're not running
+ self.cmdclass[cmd] = 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])
+
sequence = tuple, list
class Distribution(_Distribution):
@@ -80,6 +106,21 @@ class Distribution(_Distribution):
distribution for the included and excluded features.
"""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
def __init__ (self, attrs=None):
have_package_data = hasattr(self, "package_data")
if not have_package_data:
@@ -93,15 +134,9 @@ class Distribution(_Distribution):
self.zip_safe = None
self.namespace_packages = None
self.eager_resources = None
+ self.entry_points = None
_Distribution.__init__(self,attrs)
- if not have_package_data:
- from setuptools.command.build_py import build_py
- self.cmdclass.setdefault('build_py',build_py)
- self.cmdclass.setdefault('build_ext',build_ext)
- self.cmdclass.setdefault('install',install)
- self.cmdclass.setdefault('install_lib',install_lib)
- self.cmdclass.setdefault('sdist',sdist)
def parse_command_line(self):
"""Process features after parsing command line options"""
@@ -121,6 +156,12 @@ class Distribution(_Distribution):
+
+
+
+
+
+
def finalize_options(self):
_Distribution.finalize_options(self)
@@ -171,6 +212,12 @@ class Distribution(_Distribution):
"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"""
@@ -197,12 +244,6 @@ class Distribution(_Distribution):
-
-
-
-
-
-
def _finalize_features(self):
"""Add/remove features and resolve dependencies between them"""
@@ -420,7 +461,7 @@ class Distribution(_Distribution):
src,alias = aliases[command]
del aliases[command] # ensure each alias can expand only once!
import shlex
- args[:1] = shlex.split(alias,True)
+ args[:1] = shlex.split(alias,True)
command = args[0]
nargs = _Distribution._parse_command_opts(self, parser, args)
diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py
index 3345311a..5392e59f 100644
--- a/setuptools/tests/test_resources.py
+++ b/setuptools/tests/test_resources.py
@@ -185,7 +185,7 @@ class DistroTests(TestCase):
d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(),
["fastcgi", "docgen"]
)
- self.assertRaises(InvalidOption, d.requires, ["foo"])
+ self.assertRaises(UnknownExtra, d.requires, ["foo"])
@@ -203,6 +203,88 @@ class DistroTests(TestCase):
+class EntryPointTests(TestCase):
+
+ def assertfields(self, ep):
+ self.assertEqual(ep.name,"foo")
+ self.assertEqual(ep.module_name,"setuptools.tests.test_resources")
+ self.assertEqual(ep.attrs, ("EntryPointTests",))
+ self.assertEqual(ep.extras, ("x",))
+ self.failUnless(ep.load() is EntryPointTests)
+ self.assertEqual(
+ str(ep),
+ "foo = setuptools.tests.test_resources:EntryPointTests [x]"
+ )
+
+ def testBasics(self):
+ ep = EntryPoint(
+ "foo", "setuptools.tests.test_resources", ["EntryPointTests"],
+ ["x"]
+ )
+ self.assertfields(ep)
+
+ def testParse(self):
+ s = "foo = setuptools.tests.test_resources:EntryPointTests [x]"
+ ep = EntryPoint.parse(s)
+ self.assertfields(ep)
+
+ ep = EntryPoint.parse("bar baz= spammity[PING]")
+ self.assertEqual(ep.name,"bar baz")
+ self.assertEqual(ep.module_name,"spammity")
+ self.assertEqual(ep.attrs, ())
+ self.assertEqual(ep.extras, ("ping",))
+
+ ep = EntryPoint.parse(" fizzly = wocka:foo")
+ self.assertEqual(ep.name,"fizzly")
+ self.assertEqual(ep.module_name,"wocka")
+ self.assertEqual(ep.attrs, ("foo",))
+ self.assertEqual(ep.extras, ())
+
+
+
+
+
+ def testRejects(self):
+ for ep in [
+ "foo", "x=1=2", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2",
+ ]:
+ try: EntryPoint.parse(ep)
+ except ValueError: pass
+ else: raise AssertionError("Should've been bad", ep)
+
+ def checkSubMap(self, m):
+ self.assertEqual(str(m),
+ "{"
+ "'feature2': EntryPoint.parse("
+ "'feature2 = another.module:SomeClass [extra1,extra2]'), "
+ "'feature1': EntryPoint.parse("
+ "'feature1 = somemodule:somefunction')"
+ "}"
+ )
+
+ submap_str = """
+ # define features for blah blah
+ feature1 = somemodule:somefunction
+ feature2 = another.module:SomeClass [extra1,extra2]
+ """
+
+ def testParseList(self):
+ self.checkSubMap(EntryPoint.parse_list("xyz", self.submap_str))
+ self.assertRaises(ValueError, EntryPoint.parse_list, "x a", "foo=bar")
+ self.assertRaises(ValueError, EntryPoint.parse_list, "x",
+ ["foo=baz", "foo=bar"])
+
+ def testParseMap(self):
+ m = EntryPoint.parse_map({'xyz':self.submap_str})
+ self.checkSubMap(m['xyz'])
+ self.assertEqual(m.keys(),['xyz'])
+ m = EntryPoint.parse_map("[xyz]\n"+self.submap_str)
+ self.checkSubMap(m['xyz'])
+ self.assertEqual(m.keys(),['xyz'])
+ self.assertRaises(ValueError, EntryPoint.parse_map, ["[xyz]", "[xyz]"])
+ self.assertRaises(ValueError, EntryPoint.parse_map, self.submap_str)
+
+
class RequirementsTests(TestCase):
def testBasics(self):