0001
0002
0003import textwrap
0004import os
0005import pkg_resources
0006from command import Command, BadCommand
0007import fnmatch
0008import re
0009import traceback
0010from cStringIO import StringIO
0011import inspect
0012import types
0013
0014class EntryPointCommand(Command):
0015
0016 usage = "ENTRY_POINT"
0017 summary = "Show information about entry points"
0018
0019 description = """\
0020 Shows information about one or many entry points (you can use
0021 wildcards for entry point names). Entry points are used for Egg
0022 plugins, and are named resources -- like an application, template
0023 plugin, or other resource. Entry points have a [group] which
0024 defines what kind of object they describe, and inside groups each
0025 entry point is named.
0026 """
0027
0028 max_args = 2
0029
0030 parser = Command.standard_parser(verbose=False)
0031 parser.add_option('--list', '-l',
0032 dest='list_entry_points',
0033 action='store_true',
0034 help='List all the kinds of entry points on the system')
0035 parser.add_option('--egg', '-e',
0036 dest='show_egg',
0037 help="Show all the entry points for the given Egg")
0038 parser.add_option('--regex',
0039 dest='use_regex',
0040 action='store_true',
0041 help="Make pattern match as regular expression, not just a wildcard pattern")
0042
0043 def command(self):
0044 if self.options.list_entry_points:
0045 return self.list_entry_points()
0046 if self.options.show_egg:
0047 return self.show_egg(self.options.show_egg)
0048 if not self.args:
0049 raise BadCommand("You must give an entry point (or --list)")
0050 pattern = self.get_pattern(self.args[0])
0051 groups = self.get_groups_by_pattern(pattern)
0052 if not groups:
0053 raise BadCommand('No group matched %s' % self.args[0])
0054 ep_pat = None
0055 if len(self.args) > 1:
0056 ep_pat = self.get_pattern(self.args[1])
0057 for group in groups:
0058 desc = self.get_group_description(group)
0059 print '[%s]' % group
0060 if desc:
0061 print self.wrap(desc)
0062 print
0063 by_dist = {}
0064 self.print_entry_points_by_group(group, ep_pat)
0065
0066 def print_entry_points_by_group(self, group, ep_pat):
0067 env = pkg_resources.Environment()
0068 project_names = list(env)
0069 project_names.sort()
0070 for project_name in project_names:
0071 dists = list(env[project_name])
0072 assert dists
0073 dist = dists[0]
0074 entries = dist.get_entry_map(group).values()
0075 if ep_pat:
0076 entries = [e for e in entries
0077 if ep_pat.search(e.name)]
0078 if not entries:
0079 continue
0080 if len(dists) > 1:
0081 print '%s (+ %i older versions)' % (
0082 dist, len(dists)-1)
0083 else:
0084 print '%s' % dist
0085 entries.sort(lambda a, b: cmp(a.name, b.name))
0086 for entry in entries:
0087 print self._ep_description(entry)
0088 desc = self.get_entry_point_description(entry, group)
0089 if desc and desc.description:
0090 print self.wrap(desc.description, indent=4)
0091
0092 def show_egg(self, egg_name):
0093 group_pat = None
0094 if self.args:
0095 group_pat = self.get_pattern(self.args[0])
0096 ep_pat = None
0097 if len(self.args) > 1:
0098 ep_pat = self.get_pattern(self.args[1])
0099 if egg_name.startswith('egg:'):
0100 egg_name = egg_name[4:]
0101 dist = pkg_resources.get_distribution(egg_name)
0102 entry_map = dist.get_entry_map()
0103 entry_groups = entry_map.items()
0104 entry_groups.sort()
0105 for group, points in entry_groups:
0106 if group_pat and not group_pat.search(group):
0107 continue
0108 print '[%s]' % group
0109 points = points.items()
0110 points.sort()
0111 for name, entry in points:
0112 if ep_pat:
0113 if not ep_pat.search(name):
0114 continue
0115 print self._ep_description(entry)
0116 desc = self.get_entry_point_description(entry, group)
0117 if desc and desc.description:
0118 print self.wrap(desc.description, indent=2)
0119 print
0120
0121 def wrap(self, text, indent=0):
0122 text = dedent(text)
0123 width = int(os.environ.get('COLUMNS', 70)) - indent
0124 text = '\n'.join([line.rstrip() for line in text.splitlines()])
0125 paras = text.split('\n\n')
0126 new_paras = []
0127 for para in paras:
0128 if para.lstrip() == para:
0129
0130 para = '\n'.join(textwrap.wrap(para, width))
0131 new_paras.append(para)
0132 text = '\n\n'.join(new_paras)
0133 lines = [' '*indent + line
0134 for line in text.splitlines()]
0135 return '\n'.join(lines)
0136
0137 def _ep_description(self, ep, pad_name=None):
0138 name = ep.name
0139 if pad_name is not None:
0140 name = name + ' '*(pad_name-len(name))
0141 dest = ep.module_name
0142 if ep.attrs:
0143 dest = dest + ':' + '.'.join(ep.attrs)
0144 return '%s = %s' % (name, dest)
0145
0146 def get_pattern(self, s):
0147 if not s:
0148 return None
0149 if self.options.use_regex:
0150 return re.compile(s)
0151 else:
0152 return re.compile(fnmatch.translate(s), re.I)
0153
0154 def list_entry_points(self):
0155 pattern = self.get_pattern(self.args and self.args[0])
0156 groups = self.get_groups_by_pattern(pattern)
0157 print '%i entry point groups found:' % len(groups)
0158 for group in groups:
0159 desc = self.get_group_description(group)
0160 print '[%s]' % group
0161 if desc:
0162 if hasattr(desc, 'description'):
0163 desc = desc.description
0164 print self.wrap(desc, indent=2)
0165
0166 def get_groups_by_pattern(self, pattern):
0167 env = pkg_resources.Environment()
0168 eps = {}
0169 for project_name in env:
0170 for dist in env[project_name]:
0171 for name in pkg_resources.get_entry_map(dist):
0172 if pattern and not pattern.search(name):
0173 continue
0174 if (not pattern
0175 and name.startswith('paste.description.')):
0176 continue
0177 eps[name] = None
0178 eps = eps.keys()
0179 eps.sort()
0180 return eps
0181
0182 def get_group_description(self, group):
0183 for entry in pkg_resources.iter_entry_points('paste.entry_point_description'):
0184 if entry.name == group:
0185 ep = entry.load()
0186 if hasattr(ep, 'description'):
0187 return ep.description
0188 else:
0189 return ep
0190 return None
0191
0192 def get_entry_point_description(self, ep, group):
0193 try:
0194 return self._safe_get_entry_point_description(ep, group)
0195 except Exception, e:
0196 out = StringIO()
0197 traceback.print_exc(file=out)
0198 return ErrorDescription(e, out.getvalue())
0199
0200 def _safe_get_entry_point_description(self, ep, group):
0201 ep.dist.activate()
0202 meta_group = 'paste.description.'+group
0203 meta = ep.dist.get_entry_info(meta_group, ep.name)
0204 if not meta:
0205 generic = list(pkg_resources.iter_entry_points(
0206 meta_group, 'generic'))
0207 if not generic:
0208 return super_generic(ep.load())
0209
0210 obj = generic[0].load()
0211 desc = obj(ep, group)
0212 else:
0213 desc = meta.load()
0214 return desc
0215
0216class EntryPointDescription(object):
0217
0218 def __init__(self, group):
0219 self.group = group
0220
0221
0222
0223
0224class SuperGeneric(object):
0225
0226 def __init__(self, doc_object):
0227 self.doc_object = doc_object
0228 self.description = dedent(self.doc_object.__doc__)
0229 try:
0230 if isinstance(self.doc_object, (type, types.ClassType)):
0231 func = self.doc_object.__init__.im_func
0232 elif (hasattr(self.doc_object, '__call__')
0233 and not isinstance(self.doc_object, types.FunctionType)):
0234 func = self.doc_object.__call__
0235 else:
0236 func = self.doc_object
0237 if hasattr(func, '__paste_sig__'):
0238 sig = func.__paste_sig__
0239 else:
0240 sig = inspect.getargspec(func)
0241 sig = inspect.formatargspec(*sig)
0242 except TypeError:
0243 sig = None
0244 if sig:
0245 if self.description:
0246 self.description = '%s\n\n%s' % (
0247 sig, self.description)
0248 else:
0249 self.description = sig
0250
0251def dedent(s):
0252 if s is None:
0253 return s
0254 s = s.strip('\n').strip('\r')
0255 return textwrap.dedent(s)
0256
0257def super_generic(obj):
0258 desc = SuperGeneric(obj)
0259 if not desc.description:
0260 return None
0261 return desc
0262
0263class ErrorDescription(object):
0264
0265 def __init__(self, exc, tb):
0266 self.exc = exc
0267 self.tb = '\n'.join(tb)
0268 self.description = 'Error loading: %s' % exc