"""Unix 'man'-like features based on documentation strings. Provides the utilities apropos and help. Version 1 Written by Nathan Srebro, Technion, Haifa, Israel, August 1998. Versoin 1.1 Copyright by Nathan Srebro, Massachuests Institute of Technology, Cambridge, MA, USA. May 2001. nati@mit.edu http://www.ai.mit.edu/~nati/Python """ # Version 1.1: Fixed to work with Python 2.1, in which function # objects might have None __dict__ attributes. # __doc__ might be something other than string import re import sys import __main__,__builtin__ from types import * def apropos(query,argnames=1,unimported=0,print_args=0,re_flags=re.I|re.M): """Search for objects with name or documentation containning a regexp. apropos(query,argnames=1,unimported=0,print_args=0,re_flags=re.I|re.M) Searchs for objects (e.g. classes, functions, builtin functions, global variables, class methods) who's name or documentation contains the regular expression query (given in re module's format). For each such object, the name of the object (including module name and other hirarchial components) and the first line of the documentation is printed. If argnames is true (this is the default), function and method argument names are also searched for the query. If unimported is true, also unimported (hiden, begining with "_") objects will be checked. By default, they are not. If print_args is true, the argument names of matching functions and methods will also be printed. By default, they are printed only if the match is on the argument names (see argnames above). re_flags are flags for the regular expression and are passed to re.compile.""" cquery=re.compile(query,re_flags) done={} # First check the standardbuiltins _apropos_dict(cquery,"",__builtin__.__dict__,done,__builtin__,argnames,unimported,print_args) # Then check the buiiltin (i.e. binnary) modules. These can (hopefully) be # recognized as those which do not have a __builtins__ module or dict, which # is mandatory for python modules for module in sys.modules.values(): if ( not hasattr(module,"__builtins__") ) and module is not __builtin__: _apropos_module(cquery,module,done,argnames,unimported,print_args) # Next check the python modules for module in sys.modules.values(): if ( not done.has_key(id(module)) ) and module is not __main__: _apropos_module(cquery,module,done,argnames,unimported,print_args) # And finally check __main__ _apropos_dict(cquery,"",__main__.__dict__,done,__main__,argnames,unimported,print_args) def _apropos_module(cquery,module,done,argnames,unimported,print_args): if done.has_key(id(module)): return done[id(module)]=1 if cquery.search(module.__name__) or (module.__doc__ and cquery.search(module.__doc__)): print "Module %s: %s"%(module.__name__,_firstdocline(module)) _apropos_dict(cquery,module.__name__+".",module.__dict__,done,module,argnames,unimported,print_args) def _apropos_dict(cquery,prefix,dict,done,module,argnames,unimported,print_args): for name,object in dict.items(): if done.has_key(id(object)): continue # already dealt with tp=type(object) # Type methods are somewhat hard to find. We will look for them in the first encountered # object of that type (if none such object exists, we won't be able to find 'em). if not done.has_key(tp): done[tp]=1 # note that done[tp] != done[id(tp)] if hasattr(object,"__methods__"): methdict={} # build a method dictionary, somewhat simmilar to a class's dict for methname in object.__methods__: methdict[methname]=getattr(object,methname) _apropos_dict(cquery,`tp`+".",methdict,done,None,argnames,unimported,print_args) # These bound methods were just created, and will be shortly deleted, and so # thier ids may be reused (e.g. by bound methods created for a later type), and # so we delete them from 'done' for method in methdict.values(): try: del done[id(method)] except KeyError: pass del methdict if (not unimported) and name[0]=="_" and name[:2]!="__": continue # Unimported / hidden if tp is ModuleType: continue # modules dealt with seperately if module and ((tp is MethodType and object.im_func.func_globals is not module.__dict__) or (tp is FunctionType and object.func_globals is not module.__dict__) or (tp is ClassType and object.__module__ is not module.__name__)): continue # This belongs to a different module ! done[id(object)]=1 # If we got here, it means we'll actually look at the object if (tp is MethodType and object.im_self and not ( name != object.im_func.func_name and cquery.search(name))): continue # Bound method- will hopefully be reported as unbound methods try: docmatch = cquery.search(object.__doc__) except: docmatch = 0 if (cquery.search(name) or docmatch): if print_args and tp is FunctionType: # print name, args and doc print prefix+name+_niceargs(object)+": "+_firstdocline(object) elif print_args and tp is MethodType: # print name, args and doc print prefix+name+_niceargs(object.im_func)+": "+_firstdocline(object) else: # print name and doc only print prefix+name+": "+_firstdocline(object) elif argnames: # also check argument names if tp is FunctionType and _query_list(cquery,object.func_code.co_varnames): print prefix+name+_niceargs(object)+": "+_firstdocline(object) elif tp is MethodType and _query_list(cquery,object.im_func.func_code.co_varnames): print prefix+name+_niceargs(object.im_func)+": "+_firstdocline(object) if hasattr(object,"__dict__") and object.__dict__: # Maybe this should be changed to 'tp is ClassType' _apropos_dict(cquery,prefix+name+".",object.__dict__,done,module, argnames,unimported,print_args) def _firstdocline(object): "returns object's docstring up to first newline, or to a maximum of 79 charachters" import string try: d=object.__doc__[:80] return d[:string.find(d,"\n")] except: return "" def _niceargs(func): import string return "("+string.join(func.func_code.co_varnames,", ")+")" def _query_list(cquery,list): "Returns non-empty sequence if any string is list matches compiled regexp" return filter(lambda a,q=cquery:q.search(a),list) def help(x): print x.__doc__