Commit 54bb8e40 authored by PJ Eby's avatar PJ Eby

Misc. bug fixes and doc additions. Add 'iter_entry_points()' API.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041153
parent 1c40632b
......@@ -647,7 +647,7 @@ Command-Line Options
"site" directory, he or she should create an ``altinstall.pth`` file in the
normal site-packages directory, containing this::
import os; addsitedir(os.path.expanduser('~/lib/python2.3'))
import os, site; site.addsitedir(os.path.expanduser('~/lib/python2.3'))
and a ``distutils.cfg`` file inside the ``distutils`` package directory,
containing this::
......
......@@ -42,7 +42,7 @@ from sets import ImmutableSet
__all__ = [
# Basic resource access and distribution/entry point discovery
'require', 'run_script', 'get_provider', 'get_distribution',
'load_entry_point', 'get_entry_map', 'get_entry_info',
'load_entry_point', 'get_entry_map', 'get_entry_info', 'iter_entry_points',
'resource_string', 'resource_stream', 'resource_filename',
'resource_listdir', 'resource_exists', 'resource_isdir',
......@@ -231,17 +231,17 @@ def get_distribution(dist):
raise TypeError("Expected string, Requirement, or Distribution", dist)
return dist
def load_entry_point(dist, kind, name):
"""Return the `name` entry point of `kind` for dist or raise ImportError"""
return get_distribution(dist).load_entry_point(dist, kind, name)
def load_entry_point(dist, group, name):
"""Return `name` entry point of `group` for `dist` or raise ImportError"""
return get_distribution(dist).load_entry_point(group, name)
def get_entry_map(dist, kind=None):
"""Return the entry point map for `kind`, or the full entry map"""
return get_distribution(dist).get_entry_map(dist, kind)
def get_entry_map(dist, group=None):
"""Return the entry point map for `group`, or the full entry map"""
return get_distribution(dist).get_entry_map(group)
def get_entry_info(dist, kind, name):
"""Return the EntryPoint object for `kind`+`name`, or ``None``"""
return get_distribution(dist).get_entry_info(dist, kind, name)
def get_entry_info(dist, group, name):
"""Return the EntryPoint object for `group`+`name`, or ``None``"""
return get_distribution(dist).get_entry_info(group, name)
class IMetadataProvider:
......@@ -377,7 +377,6 @@ class WorkingSet(object):
for key in self.entry_keys[item]:
yield self.by_key[key]
def find(self, req):
"""Find a distribution matching requirement `req`
......@@ -394,19 +393,20 @@ class WorkingSet(object):
else:
return dist
def iter_entry_points(self, group, name=None):
"""Yield entry point objects from `group` matching `name`
If `name` is None, yields all entry points in `group` from all
distributions in the working set, otherwise only ones matching
both `group` and `name` are yielded (in distribution order).
"""
for dist in self:
entries = dist.get_entry_map(group)
if name is None:
for ep in entries.values():
yield ep
elif name in entries:
yield entries[name]
def add(self, dist, entry=None):
"""Add `dist` to working set, associated with `entry`
......@@ -1600,7 +1600,7 @@ def parse_version(s):
class EntryPoint(object):
"""Object representing an importable location"""
def __init__(self, name, module_name, attrs=(), extras=()):
def __init__(self, name, module_name, attrs=(), extras=(), dist=None):
if not MODULE(module_name):
raise ValueError("Invalid module name", module_name)
self.name = name
......@@ -1609,6 +1609,7 @@ class EntryPoint(object):
self.extras = Requirement.parse(
("x[%s]" % ','.join(extras)).lower()
).extras
self.dist = dist
def __str__(self):
s = "%s = %s" % (self.name, self.module_name)
......@@ -1621,7 +1622,8 @@ class EntryPoint(object):
def __repr__(self):
return "EntryPoint.parse(%r)" % str(self)
def load(self):
def load(self, require=True):
if require: self.require()
entry = __import__(self.module_name, globals(),globals(), ['__name__'])
for attr in self.attrs:
try:
......@@ -1630,16 +1632,14 @@ class EntryPoint(object):
raise ImportError("%r has no %r attribute" % (entry,attr))
return entry
def require(self):
if self.extras and not self.dist:
raise UnknownExtra("Can't require() without a distribution", self)
map(working_set.add,
working_set.resolve(self.dist.requires(self.extras)))
#@classmethod
def parse(cls, src):
def parse(cls, src, dist=None):
"""Parse a single entry point from string `src`
Entry point syntax follows the form::
......@@ -1668,7 +1668,7 @@ class EntryPoint(object):
src
)
else:
return cls(name.strip(), value.lstrip(), attrs, extras)
return cls(name.strip(), value.lstrip(), attrs, extras, dist)
parse = classmethod(parse)
......@@ -1680,11 +1680,12 @@ class EntryPoint(object):
#@classmethod
def parse_list(cls, section, contents):
def parse_list(cls, section, contents, dist=None):
if not MODULE(section):
raise ValueError("Invalid section name", section)
this = {}
for ep in map(cls.parse, yield_lines(contents)):
for line in yield_lines(contents):
ep = cls.parse(line, dist)
if ep.name in this:
raise ValueError("Duplicate entry point",section,ep.name)
this[ep.name]=ep
......@@ -1693,7 +1694,7 @@ class EntryPoint(object):
parse_list = classmethod(parse_list)
#@classmethod
def parse_map(cls, data):
def parse_map(cls, data, dist=None):
if isinstance(data,dict):
data = data.items()
else:
......@@ -1707,7 +1708,7 @@ class EntryPoint(object):
section = section.strip()
if section in maps:
raise ValueError("Duplicate section name", section)
maps[section] = cls.parse_list(section, contents)
maps[section] = cls.parse_list(section, contents, dist)
return maps
parse_map = classmethod(parse_map)
......@@ -1719,9 +1720,6 @@ class EntryPoint(object):
class Distribution(object):
"""Wrap an actual or potential sys.path entry w/metadata"""
def __init__(self,
......@@ -1886,31 +1884,31 @@ class Distribution(object):
def load_entry_point(self, kind, name):
"""Return the `name` entry point of `kind` or raise ImportError"""
ep = self.get_entry_info(kind,name)
def load_entry_point(self, group, name):
"""Return the `name` entry point of `group` or raise ImportError"""
ep = self.get_entry_info(group,name)
if ep is None:
raise ImportError("Entry point %r not found" % ((kind,name),))
if ep.extras:
# Ensure any needed extras get added to the working set
map(working_set.add, working_set.resolve(self.requires(ep.extras)))
raise ImportError("Entry point %r not found" % ((group,name),))
return ep.load()
def get_entry_map(self,kind=None):
"""Return the entry point map for `kind`, or the full entry map"""
def get_entry_map(self, group=None):
"""Return the entry point map for `group`, or the full entry map"""
try:
ep_map = self._ep_map
except AttributeError:
ep_map = self._ep_map = EntryPoint.parse_map(
self._get_metadata('entry_points.txt')
self._get_metadata('entry_points.txt'), self
)
if kind is not None:
return ep_map.get(kind,{})
if group is not None:
return ep_map.get(group,{})
return ep_map
def get_entry_info(self, kind, name):
"""Return the EntryPoint object for `kind`+`name`, or ``None``"""
return self.get_entry_map(kind).get(name)
def get_entry_info(self, group, name):
"""Return the EntryPoint object for `group`+`name`, or ``None``"""
return self.get_entry_map(group).get(name)
......@@ -2147,6 +2145,7 @@ _initialize(globals())
working_set = WorkingSet()
require = working_set.require
iter_entry_points = working_set.iter_entry_points
add_activation_listener = working_set.subscribe
# Activate all distributions already on sys.path, and ensure that
......@@ -2172,4 +2171,3 @@ add_activation_listener(lambda dist: dist.activate())
......@@ -72,6 +72,14 @@ You may receive a message telling you about an obsolete version of
setuptools being present; if so, you must be sure to delete it entirely, along
with the old ``pkg_resources`` module if it's present on ``sys.path``.
To get the in-development version of setuptools, run::
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/python login
cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/python \
co -d setuptools python/nondist/sandbox/setuptools
You can then install it using the usual "setup.py install" incantation.
Basic Use
=========
......
......@@ -16,20 +16,17 @@ def get_command_class(self, command):
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
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 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
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':
......@@ -39,6 +36,9 @@ for meth in 'print_commands', 'get_command_class':
sequence = tuple, list
class Distribution(_Distribution):
"""Distribution with support for features, tests, and package data
......
......@@ -216,16 +216,20 @@ class EntryPointTests(TestCase):
"foo = setuptools.tests.test_resources:EntryPointTests [x]"
)
def setUp(self):
self.dist = Distribution.from_filename(
"FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt','[x]')))
def testBasics(self):
ep = EntryPoint(
"foo", "setuptools.tests.test_resources", ["EntryPointTests"],
["x"]
["x"], self.dist
)
self.assertfields(ep)
def testParse(self):
s = "foo = setuptools.tests.test_resources:EntryPointTests [x]"
ep = EntryPoint.parse(s)
ep = EntryPoint.parse(s, self.dist)
self.assertfields(ep)
ep = EntryPoint.parse("bar baz= spammity[PING]")
......@@ -240,10 +244,6 @@ class EntryPointTests(TestCase):
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",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment