0001import sys
0002import pydoc
0003import os.path as path
0004import rst
0005import pudge
0006
0007from inspect import *
0008
0009meta_names = ['all', 'author', 'copyright', 'date', 'license', 'revision',
0010 'version']
0011meta_names = set(['__%s__' % n for n in meta_names])
0012
0013def ispackage(obj):
0014 if ismodule(obj) and hasattr(obj, '__file__'):
0015 file = obj.__file__
0016 ext = ['.py', '.pyc', '.pyo']
0017 for e in ext:
0018 if file.endswith('__init__' + e):
0019 return 1
0020 return 0
0021
0022def qualified_name(obj):
0023 if ismodule(obj):
0024 return obj.__name__
0025 elif isclass(obj) or isfunction(obj):
0026 return obj.__module__ + '.' + obj.__name__
0027 elif ismethod(obj):
0028 return qualified_name(obj.im_class) + obj.func_name
0029
0030
0031class Browser(object):
0032 def __init__(self, module_names):
0033 self.module_names = module_names
0034 self.cache = {}
0035
0036 def find(self, name, contained=0):
0037 """
0038 Locate an object by it's qualified name.
0039
0040 ``name`` is the qualified name of the package, module, class,
0041 routine, or data (e.g. 'package.module.Class').
0042 """
0043 if contained and not self.is_inside(name):
0044 return
0045 cache = self.cache
0046 if not name:
0047 return None
0048 if cache.has_key(name):
0049 return cache[name]
0050 rslt = None
0051 if '.' in name:
0052 components = name.split('.')
0053 module_name = components[0]
0054 components = components[1:]
0055 module = self.find(module_name)
0056 if module:
0057 rslt = module.find('.'.join(components))
0058 else:
0059 try:
0060 rslt = Object(name, __import__(name), None, 0, self)
0061 except ImportError:
0062 pass
0063 cache[name] = rslt
0064 return rslt
0065
0066 def is_inside(self, name):
0067 for m in self.module_names:
0068 if name.startswith(m):
0069 return 1
0070
0071 def modules(self, recursive=0):
0072 """Iterate over top level packages and modules."""
0073 if recursive:
0074 modules = []
0075 for m in self.module_names:
0076 m = self.find(m)
0077 if m:
0078 modules.append(m)
0079 for m in m.modules(recursive=1):
0080 modules.append(m)
0081
0082 return modules
0083 else:
0084 return [self.find(m) for m in self.module_names if m]
0085
0086
0087def Object(name, obj, parent, module, browser):
0088 cls = None
0089 if ismodule(obj):
0090 cls = ispackage(obj) and Package or Module
0091 elif isclass(obj):
0092 cls = Class
0093 elif isroutine(obj):
0094 cls = Routine
0095 elif not (isframe(obj) or istraceback(obj) or iscode(obj)):
0096 cls = Attribute
0097 if cls is not None:
0098 return cls(name, obj, parent, module, browser)
0099
0100
0101class Name(object):
0102
0103 exclude_members = set(['__doc__', '__dict__'])
0104
0105 def __init__(self, name, obj, parent, module, browser):
0106 self.name = name
0107 self.obj = obj
0108 self.parent = parent
0109 self.in_module = module
0110 self.browser = browser
0111 self._initialize()
0112
0113
0114 def relative_file(self, ext='.py'):
0115 real = self.real_module
0116 if real:
0117 relfile = real.__name__.replace('.', '/')
0118 if ispackage(real):
0119 relfile += '/__init__'
0120 if ext:
0121 relfile += ext
0122 return relfile
0123
0124 def qualified_name(self):
0125 names = [self.name]
0126 p = self.parent
0127 while p:
0128 names.insert(0, p.name)
0129 p = p.parent
0130 return '.'.join(names)
0131
0132 def moduleless_name(self):
0133 names = [self.name]
0134 p = self.parent
0135 while not p.ismodule():
0136 names.insert(0, p.name)
0137 p = p.parent
0138 return '.'.join(names)
0139
0140 def source_lines(self):
0141 try:
0142 (line, code) = getsourcelines(self.obj)
0143 except (IOError, TypeError):
0144 pass
0145 else:
0146 if isinstance(line, list):
0147
0148 (line, code) = (code, line)
0149 if code:
0150 return (line, line + len(code))
0151 return (0, 0)
0152
0153 def isvisible(self):
0154 name = self.name
0155 if name.startswith('__') and name.endswith('__'):
0156 return 1
0157 try:
0158 all = self.parent.obj.__all__
0159 except AttributeError:
0160 pass
0161 else:
0162 return name in all
0163 if name.startswith('_'):
0164 return 0
0165 return not self.isalias()
0166
0167 def ispackage(self):
0168 return ispackage(self.obj)
0169
0170 def ismodule(self):
0171 return ismodule(self.obj)
0172
0173 def isclass(self):
0174 return isclass(self.obj)
0175
0176 def isroutine(self):
0177 return isroutine(self.obj)
0178
0179 def isdata(self):
0180 return pydoc.isdata(self.obj)
0181
0182 def isalias(self):
0183 if self.in_module == 0:
0184 return 0
0185 return self.actual_module != self.in_module
0186
0187 def ismeta(self):
0188 return self.name in meta_names
0189
0190 def hasdoc(self):
0191 if self.doc(strip=0):
0192 return 1
0193
0194 def doc(self, strip=1, blurbless=0, html=0):
0195 if not self.obj:
0196 return ''
0197 ds = getattr(self.obj, '__doc__', None)
0198 if ds and strip:
0199 ds = rst.trim(ds)
0200 if ds and blurbless:
0201 parts = ds.split('\n\n', 1)
0202 if len(parts) > 1:
0203 ds = parts[1].strip()
0204 else:
0205 return ''
0206 if ds and html:
0207 ds = rst.to_html(ds)
0208 return ds or ''
0209
0210 def blurb(self, html=1):
0211 ds = self.doc()
0212 if ds:
0213 parts = ds.split('\n\n')
0214 ds = parts[0]
0215 if ds and html:
0216 ds = rst.to_html(ds).strip()
0217 if ds.startswith('<p>') and ds.endswith('</p>'):
0218 ds = ds[3:-4]
0219 return ds
0220
0221 def html(self, blurbless=0):
0222 from warnings import warn
0223 warn("Name.html() is deprecated. Use Name.doc(html=1) instead.",
0224 DeprecationWarning, stacklevel=2)
0225 return self.doc(blurbless=blurbless, html=1)
0226
0227 def formatargs(self):
0228 return _format_args(self.obj)
0229
0230 def filtered_members(self):
0231 excludes = self.exclude_members
0232 for n, v in getmembers(self.obj):
0233 if n not in excludes:
0234 yield n, v
0235
0236 def members(self):
0237 if not hasattr(self, '_members'):
0238 members = self._load_members()
0239 self.__dict__['members'] = members
0240 return members
0241 members = property(members)
0242
0243 def members_with(self, predicate):
0244 for m in self.members.values():
0245 if predicate(m):
0246 yield m
0247
0248 def members_with_any(self, *predicates):
0249 predicates = ['is' + p for p in predicates]
0250 members = self.members.values()
0251 for m in self.members.values():
0252 for p in predicates:
0253 if getattr(m, p)():
0254 yield m
0255 break
0256
0257 def members_with_all(self, *predicates):
0258 predicates = ['is' + p for p in predicates]
0259 members = self.members.values()
0260 for m in self.members.values():
0261 for p in predicates:
0262 if not getattr(m, p)():
0263 break
0264 else:
0265 yield m
0266
0267 def classes(self, visible_only=1):
0268 return self.all(visible_only, predicate=lambda m: m.isclass())
0269
0270 def routines(self, visible_only=1):
0271 return self.all(visible_only, predicate=lambda m: m.isroutine())
0272
0273 def modules(self, visible_only=1, recursive=0):
0274 for m in self.all(visible_only, predicate=lambda m: m.ismodule()):
0275 yield m
0276 if recursive:
0277 for m in m.modules(visible_only, 1):
0278 yield m
0279
0280 def attributes(self, visible_only=1, meta=0):
0281 predicate = lambda m: m.isdata() and (meta or not m.ismeta())
0282 return self.all(visible_only, predicate)
0283
0284 def all(self, visible_only=1, predicate=None):
0285 for m in self.members.values():
0286 if (visible_only and not m.isvisible()) or (predicate and not predicate(m)):
0288 continue
0289 yield m
0290
0291 def find(self, name):
0292 parts = name.split('.', 1)
0293 member_name, rest = parts[0], parts[1:]
0294 members = self.members
0295 if members.has_key(member_name):
0296 member = members[member_name]
0297 if rest:
0298 return member.find(rest[0])
0299 return member
0300 else:
0301 pudge.log.debug('%r not found in %r', member_name, self.qualified_name())
0302
0303 def _initialize(self):
0304 pass
0305
0306 def _load_members(self):
0307 Object = self._member_object
0308 members = {}
0309 for name, value in self.filtered_members():
0310 member = Object(name, value)
0311 members[name] = member
0312 return members
0313
0314 def _member_object(self, name, obj):
0315 return Object(name, obj, self, self.in_module, self.browser)
0316
0317
0318class Module(Name):
0319 exclude_members = Name.exclude_members.union(['__builtins__',
0320 '__file__',
0321 '__name__',
0322 '__path__'])
0323 type_name = 'module'
0324
0325 def _initialize(self):
0326 name = self.obj.__name__
0327 if '.' in name:
0328 self.actual_module = sys.modules[name.rsplit('.', 1)[0]]
0329 else:
0330 self.actual_module = None
0331 self.real_module = self.obj
0332
0333 def _member_object(self, name, obj):
0334 return Object(name, obj, self, self.obj, self.browser)
0335
0336
0337class Package(Module):
0338
0339 def _fill_modules_and_packages(self, members):
0340 object = self.obj
0341 names = dict(members)
0342 qname = self.qualified_name()
0343 from pydoc import safeimport, ispackage
0344
0345 for file in os.listdir(object.__path__[0]):
0346 p = path.join(object.__path__[0], file)
0347 modname = getmodulename(file)
0348 if modname == '__init__':
0349 continue
0350 if modname and file.endswith('.py'):
0351 if modname in names:
0352 value = names[modname]
0353
0354 if not ismodule(value):
0355
0356 pudge.log.warn('%s.%s hides module with same name',
0357 qname, modname)
0358 continue
0359 module = safeimport('%s.%s' % (qname, modname))
0360 elif ispackage(p):
0361 module = safeimport('%s.%s' % (qname, file))
0362 modname = file
0363 else:
0364 continue
0365 members.append((modname, module))
0366
0367 def filtered_members(self):
0368 sup = super(Package, self)
0369 members = list(sup.filtered_members())
0370 self._fill_modules_and_packages(members)
0371 return members
0372
0373
0374
0375class Class(Name):
0376 type_name = 'class'
0377
0378 exclude_members = Name.exclude_members.union(
0379 ['__class__', '__delattr__', '__getattribute__',
0380 '__hash__', '__module__', '__new__', '__reduce__',
0381 '__reduce_ex__', '__repr__', '__setattr__', '__str__',
0382 '__weakref__', '__metaclass__', '__getattr__'])
0383
0384 def _initialize(self):
0385 self.actual_module = sys.modules[self.obj.__module__]
0386 self.real_module = self.actual_module
0387
0388class Routine(Name):
0389 type_name = 'routine'
0390 def _initialize(self):
0391 obj = self.obj
0392 if hasattr(obj, '__module__'):
0393
0394 look_in = obj
0395 elif hasattr(obj, 'im_class'):
0396
0397 look_in = obj.im_class
0398 elif hasattr(obj, '__objclass__'):
0399
0400 look_in = obj.__objclass__
0401 modname = getattr(look_in, '__module__', None)
0402 self.actual_module = modname and sys.modules[modname]
0403 self.real_module = self.actual_module
0404
0405 def ismethod(self):
0406 return ismethod(self.obj)
0407
0408 def isfunction(self):
0409 return isfunction(self.obj)
0410
0411 def ismethoddescriptor(self):
0412 return ismethoddescriptor(self.obj)
0413
0414 def _load_members(self):
0415 return {}
0416
0417class Attribute(Name):
0418 type_name = 'attribute'
0419
0420 def _initialize(self):
0421 self.actual_module = self.in_module
0422 self.real_module = self.actual_module
0423
0424 def _load_members(self):
0425 return {}
0426
0427 def doc(self, strip=1, blurbless=0, html=0):
0428 return ''
0429
0430
0431def _format_args(obj):
0432 try:
0433 (args, varargs, varkw, defaults) = getargspec(obj)
0434 except TypeError:
0435 return "(...)"
0436 rslt = ['(']
0437 pos = 0
0438 positional_arg_count = len(args) - len(defaults or [])
0439 for name in args:
0440 if pos > 0:
0441 rslt.append(', ')
0442 rslt.append(name)
0443 default_pos = pos - positional_arg_count
0444 if default_pos >= 0:
0445 rslt.append('=')
0446 value = defaults[default_pos]
0447 rslt.append(repr(defaults[default_pos]))
0448 pos+=1
0449 rslt.append(')')
0450 return ''.join(rslt)