[Python-checkins] CVS: python/dist/src/Lib pydoc.py,1.8,1.9
Ka-Ping Yee
ping@users.sourceforge.net
Thu, 01 Mar 2001 05:55:22 -0800
Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv13478
Modified Files:
pydoc.py
Log Message:
Docstring improvements.
Add checks for .pyo and .pyd.
Collapse docfunction, docmethod, docbuiltin into the one method docroutine.
Small formatting fixes.
Link the segments of a package path in the title.
Link to the source file only if it exists.
Allow modules (e.g. repr.py) to take precedence over built-ins (e.g. repr()).
Add interruptible synopsis scanner (so we can do searches in the background).
Make HTTP server quit.
Add small GUI for controlling the server and launching searches (like -k).
(Tested on Win2k, Win98, and Linux.)
Index: pydoc.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/pydoc.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -r1.8 -r1.9
*** pydoc.py 2001/03/01 00:24:32 1.8
--- pydoc.py 2001/03/01 13:55:20 1.9
***************
*** 2,22 ****
"""Generate Python documentation in HTML or text for interactive use.
- At the shell command line outside of Python, run "pydoc <name>" to show
- documentation on something. <name> may be the name of a Python function,
- module, package, or a dotted reference to a class or function within a
- module or module in a package. Alternatively, the argument can be the
- path to a Python source file.
-
- Or, at the shell prompt, run "pydoc -k <keyword>" to search for a keyword
- in the one-line descriptions of modules.
-
- Or, at the shell prompt, run "pydoc -p <port>" to start an HTTP server
- on a given port on the local machine to generate documentation web pages.
-
- Or, at the shell prompt, run "pydoc -w <name>" to write out the HTML
- documentation for a module to a file named "<name>.html".
-
In the Python interpreter, do "from pydoc import help" to provide online
! help. Calling help(thing) on a Python object documents the object."""
__author__ = "Ka-Ping Yee <ping@lfw.org>"
--- 2,27 ----
"""Generate Python documentation in HTML or text for interactive use.
In the Python interpreter, do "from pydoc import help" to provide online
! help. Calling help(thing) on a Python object documents the object.
!
! At the shell command line outside of Python:
! Run "pydoc <name>" to show documentation on something. <name> may be
! the name of a function, module, package, or a dotted reference to a
! class or function within a module or module in a package. If the
! argument contains a path segment delimiter (e.g. slash on Unix,
! backslash on Windows) it is treated as the path to a Python source file.
!
! Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
! of all available modules.
!
! Run "pydoc -p <port>" to start an HTTP server on a given port on the
! local machine to generate documentation web pages.
!
! For platforms without a command line, "pydoc -g" starts the HTTP server
! and also pops up a little window for controlling it.
!
! Run "pydoc -w <name>" to write out the HTML documentation for a module
! to a file named "<name>.html".
! """
__author__ = "Ka-Ping Yee <ping@lfw.org>"
***************
*** 30,33 ****
--- 35,42 ----
Mynd you, møøse bites Kan be pretty nasti..."""
+ # Note: this module is designed to deploy instantly and run under any
+ # version of Python from 1.5 and up. That's why it's a single file and
+ # some 2.0 features (like string methods) are conspicuously avoided.
+
import sys, imp, os, stat, re, types, inspect
from repr import Repr
***************
*** 60,75 ****
return result
- def index(dir):
- """Return a list of (module-name, synopsis) pairs for a directory tree."""
- results = []
- for entry in os.listdir(dir):
- path = os.path.join(dir, entry)
- if ispackage(path):
- results.extend(map(
- lambda (m, s), pkg=entry: (pkg + '.' + m, s), index(path)))
- elif os.path.isfile(path) and entry[-3:] == '.py':
- results.append((entry[:-3], synopsis(path)))
- return results
-
def pathdirs():
"""Convert sys.path into a list of absolute, existing, unique paths."""
--- 69,72 ----
***************
*** 133,137 ****
if lower(filename[-3:]) == '.py':
return filename[:-3]
! elif lower(filename[-4:]) == '.pyc':
return filename[:-4]
elif lower(filename[-11:]) == 'module.so':
--- 130,134 ----
if lower(filename[-3:]) == '.py':
return filename[:-3]
! elif lower(filename[-4:]) in ['.pyc', '.pyd', '.pyo']:
return filename[:-4]
elif lower(filename[-11:]) == 'module.so':
***************
*** 185,191 ****
if inspect.ismodule(object): return apply(self.docmodule, args)
if inspect.isclass(object): return apply(self.docclass, args)
! if inspect.ismethod(object): return apply(self.docmethod, args)
! if inspect.isbuiltin(object): return apply(self.docbuiltin, args)
! if inspect.isfunction(object): return apply(self.docfunction, args)
raise TypeError, "don't know how to document objects of type " + \
type(object).__name__
--- 182,186 ----
if inspect.ismodule(object): return apply(self.docmodule, args)
if inspect.isclass(object): return apply(self.docclass, args)
! if inspect.isroutine(object): return apply(self.docroutine, args)
raise TypeError, "don't know how to document objects of type " + \
type(object).__name__
***************
*** 259,267 ****
return """
<p><table width="100%%" cellspacing=0 cellpadding=0 border=0>
! <tr bgcolor="%s"><td colspan=3 valign=bottom><small><small><br></small></small
! ><font color="%s" face="helvetica, arial"> %s</font></td
><td align=right valign=bottom
! ><font color="%s" face="helvetica, arial"> %s</font></td></tr></table>
! """ % (bgcol, fgcol, title, fgcol, extras)
def section(self, title, fgcol, bgcol, contents, width=20,
--- 254,263 ----
return """
<p><table width="100%%" cellspacing=0 cellpadding=0 border=0>
! <tr bgcolor="%s"><td> </td>
! <td valign=bottom><small><small><br></small></small
! ><font color="%s" face="helvetica"><br> %s</font></td
><td align=right valign=bottom
! ><font color="%s" face="helvetica">%s</font></td><td> </td></tr></table>
! """ % (bgcol, fgcol, title, fgcol, extras or ' ')
def section(self, title, fgcol, bgcol, contents, width=20,
***************
*** 272,276 ****
result = """
<p><table width="100%%" cellspacing=0 cellpadding=0 border=0>
! <tr bgcolor="%s"><td colspan=3 valign=bottom><small><small><br></small></small
><font color="%s" face="helvetica, arial"> %s</font></td></tr>
""" % (bgcol, fgcol, title)
--- 268,273 ----
result = """
<p><table width="100%%" cellspacing=0 cellpadding=0 border=0>
! <tr bgcolor="%s"><td rowspan=2> </td>
! <td colspan=3 valign=bottom><small><small><br></small></small
><font color="%s" face="helvetica, arial"> %s</font></td></tr>
""" % (bgcol, fgcol, title)
***************
*** 292,303 ****
return apply(self.section, (title,) + args)
- def footer(self):
- return """
- <table width="100%"><tr><td align=right>
- <font face="helvetica, arial"><small><small>generated with
- <strong>htmldoc</strong> by Ka-Ping Yee</a></small></small></font>
- </td></tr></table>
- """
-
def namelink(self, name, *dicts):
"""Make a link for an identifier, given name-to-URL mappings."""
--- 289,292 ----
***************
*** 391,398 ****
"""Produce HTML documentation for a module object."""
name = object.__name__
! result = ''
! head = '<br><big><big><strong> %s</strong></big></big>' % name
try:
path = os.path.abspath(inspect.getfile(object))
filelink = '<a href="file:%s">%s</a>' % (path, path)
except TypeError:
--- 380,395 ----
"""Produce HTML documentation for a module object."""
name = object.__name__
! parts = split(name, '.')
! links = []
! for i in range(len(parts)-1):
! links.append(
! '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
! (join(parts[:i+1], '.'), parts[i]))
! linkedname = join(links + parts[-1:], '.')
! head = '<big><big><strong>%s</strong></big></big>' % linkedname
try:
path = os.path.abspath(inspect.getfile(object))
+ sourcepath = os.path.abspath(inspect.getsourcefile(object))
+ if os.path.isfile(sourcepath): path = sourcepath
filelink = '<a href="file:%s">%s</a>' % (path, path)
except TypeError:
***************
*** 408,412 ****
if info:
head = head + ' (%s)' % join(info, ', ')
! result = result + self.heading(
head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
--- 405,409 ----
if info:
head = head + ' (%s)' % join(info, ', ')
! result = self.heading(
head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
***************
*** 520,531 ****
doc = self.markup(getdoc(object), self.preformat,
funcs, classes, mdict)
! if doc: doc = '<small><tt>' + doc + '<br> </tt></small>'
return self.section(title, '#000000', '#ffc8d8', contents, 10, doc)
- def docmethod(self, object, funcs={}, classes={}, methods={}, clname=''):
- """Produce HTML documentation for a method object."""
- return self.document(
- object.im_func, funcs, classes, methods, clname)
-
def formatvalue(self, object):
"""Format an argument default value as text."""
--- 517,523 ----
doc = self.markup(getdoc(object), self.preformat,
funcs, classes, mdict)
! if doc: doc = '<small><tt>' + doc + '</tt></small>'
return self.section(title, '#000000', '#ffc8d8', contents, 10, doc)
def formatvalue(self, object):
"""Format an argument default value as text."""
***************
*** 533,548 ****
self.repr(object))
! def docfunction(self, object, funcs={}, classes={}, methods={}, clname=''):
! """Produce HTML documentation for a function object."""
! args, varargs, varkw, defaults = inspect.getargspec(object)
! argspec = inspect.formatargspec(
! args, varargs, varkw, defaults, formatvalue=self.formatvalue)
!
! if object.__name__ == '<lambda>':
! decl = '<em>lambda</em> ' + argspec[1:-1]
else:
! anchor = clname + '-' + object.__name__
! decl = '<a name="%s"\n><strong>%s</strong>%s</a>\n' % (
! anchor, object.__name__, argspec)
doc = self.markup(getdoc(object), self.preformat,
funcs, classes, methods)
--- 525,545 ----
self.repr(object))
! def docroutine(self, object, funcs={}, classes={}, methods={}, clname=''):
! """Produce HTML documentation for a function or method object."""
! if inspect.ismethod(object): object = object.im_func
! if inspect.isbuiltin(object):
! decl = '<a name="%s"><strong>%s</strong>(...)</a>\n' % (
! clname + '-' + object.__name__, object.__name__)
else:
! args, varargs, varkw, defaults = inspect.getargspec(object)
! argspec = inspect.formatargspec(
! args, varargs, varkw, defaults, formatvalue=self.formatvalue)
!
! if object.__name__ == '<lambda>':
! decl = '<em>lambda</em> ' + argspec[1:-1]
! else:
! anchor = clname + '-' + object.__name__
! decl = '<a name="%s"\n><strong>%s</strong>%s</a>\n' % (
! anchor, object.__name__, argspec)
doc = self.markup(getdoc(object), self.preformat,
funcs, classes, methods)
***************
*** 551,563 ****
return '<dl><dt>%s<dd><small>%s</small></dl>' % (decl, doc)
- def docbuiltin(self, object, *extras):
- """Produce HTML documentation for a built-in function."""
- return '<dl><dt><strong>%s</strong>(...)</dl>' % object.__name__
-
def page(self, object):
"""Produce a complete HTML page of documentation for an object."""
! return '''<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
! <html><title>Python: %s</title>
! <body bgcolor="#ffffff">
%s
</body></html>
--- 548,556 ----
return '<dl><dt>%s<dd><small>%s</small></dl>' % (decl, doc)
def page(self, object):
"""Produce a complete HTML page of documentation for an object."""
! return '''
! <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
! <html><title>Python: %s</title><body bgcolor="#ffffff">
%s
</body></html>
***************
*** 758,782 ****
return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
- def docmethod(self, object):
- """Produce text documentation for a method object."""
- return self.document(object.im_func)
-
def formatvalue(self, object):
"""Format an argument default value as text."""
return '=' + self.repr(object)
! def docfunction(self, object):
! """Produce text documentation for a function object."""
! try:
args, varargs, varkw, defaults = inspect.getargspec(object)
argspec = inspect.formatargspec(
args, varargs, varkw, defaults, formatvalue=self.formatvalue)
! except TypeError:
! argspec = '(...)'
!
! if object.__name__ == '<lambda>':
! decl = '<lambda> ' + argspec[1:-1]
! else:
! decl = self.bold(object.__name__) + argspec
doc = getdoc(object)
if doc:
--- 751,771 ----
return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
def formatvalue(self, object):
"""Format an argument default value as text."""
return '=' + self.repr(object)
! def docroutine(self, object):
! """Produce text documentation for a function or method object."""
! if inspect.ismethod(object): object = object.im_func
! if inspect.isbuiltin(object):
! decl = self.bold(object.__name__) + '(...)'
! else:
args, varargs, varkw, defaults = inspect.getargspec(object)
argspec = inspect.formatargspec(
args, varargs, varkw, defaults, formatvalue=self.formatvalue)
! if object.__name__ == '<lambda>':
! decl = '<lambda> ' + argspec[1:-1]
! else:
! decl = self.bold(object.__name__) + argspec
doc = getdoc(object)
if doc:
***************
*** 785,793 ****
return decl + '\n'
- def docbuiltin(self, object):
- """Produce text documentation for a built-in function object."""
- return (self.bold(object.__name__) + '(...)\n' +
- rstrip(self.indent(object.__doc__)) + '\n')
-
# --------------------------------------------------------- user interfaces
--- 774,777 ----
***************
*** 915,920 ****
if type(path) is not types.StringType:
return None, path
- if hasattr(__builtins__, path):
- return None, getattr(__builtins__, path)
parts = split(path, '.')
n = 1
--- 899,902 ----
***************
*** 925,929 ****
module = reload(module)
except:
! # Did the error occur before or after we found the module?
if sys.modules.has_key(path):
filename = sys.modules[path].__file__
--- 907,911 ----
module = reload(module)
except:
! # determine if error occurred before or after module was found
if sys.modules.has_key(path):
filename = sys.modules[path].__file__
***************
*** 943,946 ****
--- 925,930 ----
n = n + 1
continue
+ if hasattr(__builtins__, path):
+ return None, getattr(__builtins__, path)
return None, None
***************
*** 956,965 ****
path, x = locate(thing)
except DocImportError, value:
! print 'problem in %s - %s' % (value.filename, value.args)
return
if x:
thing = x
else:
! print 'could not find or import %s' % repr(thing)
return
--- 940,949 ----
path, x = locate(thing)
except DocImportError, value:
! print 'Problem in %s - %s' % (value.filename, value.args)
return
if x:
thing = x
else:
! print 'No Python documentation found for %s.' % repr(thing)
return
***************
*** 970,987 ****
pager('Help on %s:\n\n' % desc + text.document(thing))
- def writedocs(path, pkgpath=''):
- if os.path.isdir(path):
- dir = path
- for file in os.listdir(dir):
- path = os.path.join(dir, file)
- if os.path.isdir(path):
- writedocs(path, file + '.' + pkgpath)
- if os.path.isfile(path):
- writedocs(path, pkgpath)
- if os.path.isfile(path):
- modname = modulename(path)
- if modname:
- writedoc(pkgpath + modname)
-
def writedoc(key):
"""Write HTML documentation to a file in the current directory."""
--- 954,957 ----
***************
*** 1016,1046 ****
found = 1
else:
! print 'could not find or import %s' % repr(key)
def apropos(key):
"""Print all the one-line module summaries that contain a substring."""
! key = lower(key)
! for module in sys.builtin_module_names:
! desc = __import__(module).__doc__ or ''
! desc = split(desc, '\n')[0]
! if find(lower(module + ' ' + desc), key) >= 0:
! print module, '-', desc or '(no description)'
! modules = []
! for dir in pathdirs():
! for module, desc in index(dir):
! desc = desc or ''
! if module not in modules:
! modules.append(module)
! if find(lower(module + ' ' + desc), key) >= 0:
! desc = desc or '(no description)'
! if module[-9:] == '.__init__':
! print module[:-9], '(package) -', desc
! else:
! print module, '-', desc
# --------------------------------------------------- web browser interface
! def serve(address, callback=None):
! import BaseHTTPServer, mimetools
# Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
--- 986,1070 ----
found = 1
else:
! print 'No Python documentation found for %s.' % repr(key)
!
! class Scanner:
! """A generic tree iterator."""
! def __init__(self, roots, children, recurse):
! self.roots = roots[:]
! self.state = []
! self.children = children
! self.recurse = recurse
!
! def next(self):
! if not self.state:
! if not self.roots:
! return None
! root = self.roots.pop(0)
! self.state = [(root, self.children(root))]
! node, children = self.state[-1]
! if not children:
! self.state.pop()
! return self.next()
! child = children.pop(0)
! if self.recurse(child):
! self.state.append((child, self.children(child)))
! return child
!
! class ModuleScanner(Scanner):
! """An interruptible scanner that searches module synopses."""
! def __init__(self):
! roots = map(lambda dir: (dir, ''), pathdirs())
! Scanner.__init__(self, roots, self.submodules, self.ispackage)
!
! def submodules(self, (dir, package)):
! children = []
! for file in os.listdir(dir):
! path = os.path.join(dir, file)
! if ispackage(path):
! children.append((path, package + (package and '.') + file))
! else:
! children.append((path, package))
! children.sort()
! return children
+ def ispackage(self, (dir, package)):
+ return ispackage(dir)
+
+ def run(self, key, callback, completer=None):
+ self.quit = 0
+ seen = {}
+
+ for modname in sys.builtin_module_names:
+ seen[modname] = 1
+ desc = split(__import__(modname).__doc__ or '', '\n')[0]
+ if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
+ callback(None, modname, desc)
+
+ while not self.quit:
+ node = self.next()
+ if not node: break
+ path, package = node
+ modname = modulename(path)
+ if os.path.isfile(path) and modname:
+ modname = package + (package and '.') + modname
+ if not seen.has_key(modname):
+ seen[modname] = 1
+ desc = synopsis(path) or ''
+ if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
+ callback(path, modname, desc)
+ if completer: completer()
+
def apropos(key):
"""Print all the one-line module summaries that contain a substring."""
! def callback(path, modname, desc):
! if modname[-9:] == '.__init__':
! modname = modname[:-9] + ' (package)'
! print modname, '-', desc or '(no description)'
! ModuleScanner().run(key, callback)
# --------------------------------------------------- web browser interface
! def serve(port, callback=None):
! import BaseHTTPServer, mimetools, select
# Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
***************
*** 1056,1067 ****
class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def send_document(self, title, contents):
! self.send_response(200)
! self.send_header('Content-Type', 'text/html')
! self.end_headers()
! self.wfile.write(
! '''<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
! <html><title>Python: %s</title><body bgcolor="#ffffff">''' % title)
! self.wfile.write(contents)
! self.wfile.write('</body></html>')
def do_GET(self):
--- 1080,1093 ----
class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def send_document(self, title, contents):
! try:
! self.send_response(200)
! self.send_header('Content-Type', 'text/html')
! self.end_headers()
! self.wfile.write('''
! <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
! <html><title>Python: %s</title><body bgcolor="#ffffff">
! %s
! </body></html>''' % (title, contents))
! except IOError: pass
def do_GET(self):
***************
*** 1074,1078 ****
except DocImportError, value:
self.send_document(path, html.escape(
! 'problem with %s - %s' % (value.filename, value.args)))
return
if x:
--- 1100,1104 ----
except DocImportError, value:
self.send_document(path, html.escape(
! 'Problem in %s - %s' % (value.filename, value.args)))
return
if x:
***************
*** 1080,1090 ****
else:
self.send_document(path,
! 'There is no Python module or object named "%s".' % path)
else:
heading = html.heading(
! '<br><big><big><strong> '
! 'Python: Index of Modules'
! '</strong></big></big>',
! '#ffffff', '#7799ee')
builtins = []
for name in sys.builtin_module_names:
--- 1106,1114 ----
else:
self.send_document(path,
! 'No Python documentation found for %s.' % repr(path))
else:
heading = html.heading(
! '<big><big><strong>Python: Index of Modules</strong></big></big>',
! '#ffffff', '#7799ee')
builtins = []
for name in sys.builtin_module_names:
***************
*** 1094,1132 ****
for dir in pathdirs():
indices.append(html.index(dir, seen))
! self.send_document('Index of Modules', heading + join(indices))
def log_message(self, *args): pass
class DocServer(BaseHTTPServer.HTTPServer):
! def __init__(self, address, callback):
self.callback = callback
! self.base.__init__(self, address, self.handler)
def server_activate(self):
self.base.server_activate(self)
! if self.callback: self.callback()
DocServer.base = BaseHTTPServer.HTTPServer
DocServer.handler = DocHandler
DocHandler.MessageClass = Message
try:
! DocServer(address, callback).serve_forever()
except KeyboardInterrupt:
! print 'server stopped'
# -------------------------------------------------- command-line interface
def cli():
import getopt
class BadUsage: pass
try:
! opts, args = getopt.getopt(sys.argv[1:], 'k:p:w')
writing = 0
for opt, val in opts:
if opt == '-k':
! apropos(lower(val))
! break
if opt == '-p':
try:
--- 1118,1344 ----
for dir in pathdirs():
indices.append(html.index(dir, seen))
! contents = heading + join(indices) + """<p align=right>
! <small><small><font color="#909090" face="helvetica, arial"><strong>
! pydoc</strong> by Ka-Ping Yee <ping@lfw.org></font></small></small>"""
! self.send_document('Index of Modules', contents)
def log_message(self, *args): pass
class DocServer(BaseHTTPServer.HTTPServer):
! def __init__(self, port, callback):
! self.address = ('127.0.0.1', port)
! self.url = 'http://127.0.0.1:%d/' % port
self.callback = callback
! self.base.__init__(self, self.address, self.handler)
!
! def serve_until_quit(self):
! import select
! self.quit = 0
! while not self.quit:
! rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
! if rd: self.handle_request()
def server_activate(self):
self.base.server_activate(self)
! if self.callback: self.callback(self)
DocServer.base = BaseHTTPServer.HTTPServer
DocServer.handler = DocHandler
DocHandler.MessageClass = Message
+ try:
+ DocServer(port, callback).serve_until_quit()
+ except (KeyboardInterrupt, select.error):
+ pass
+ print 'server stopped'
+
+ # ----------------------------------------------------- graphical interface
+
+ def gui():
+ """Graphical interface (starts web server and pops up a control window)."""
+ class GUI:
+ def __init__(self, window, port=7464):
+ self.window = window
+ self.server = None
+ self.scanner = None
+
+ import Tkinter
+ self.server_frm = Tkinter.Frame(window)
+ self.title_lbl = Tkinter.Label(self.server_frm,
+ text='Starting server...\n ')
+ self.open_btn = Tkinter.Button(self.server_frm,
+ text='open browser', command=self.open, state='disabled')
+ self.quit_btn = Tkinter.Button(self.server_frm,
+ text='quit serving', command=self.quit, state='disabled')
+
+ self.search_frm = Tkinter.Frame(window)
+ self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
+ self.search_ent = Tkinter.Entry(self.search_frm)
+ self.search_ent.bind('<Return>', self.search)
+ self.stop_btn = Tkinter.Button(self.search_frm,
+ text='stop', pady=0, command=self.stop, state='disabled')
+ if sys.platform == 'win32':
+ # Attempting to hide and show this button crashes under Windows.
+ self.stop_btn.pack(side='right')
+
+ self.window.title('pydoc')
+ self.window.protocol('WM_DELETE_WINDOW', self.quit)
+ self.title_lbl.pack(side='top', fill='x')
+ self.open_btn.pack(side='left', fill='x', expand=1)
+ self.quit_btn.pack(side='right', fill='x', expand=1)
+ self.server_frm.pack(side='top', fill='x')
+
+ self.search_lbl.pack(side='left')
+ self.search_ent.pack(side='right', fill='x', expand=1)
+ self.search_frm.pack(side='top', fill='x')
+ self.search_ent.focus_set()
+
+ self.result_lst = Tkinter.Listbox(window,
+ font=('helvetica', 8), height=6)
+ self.result_lst.bind('<Button-1>', self.select)
+ self.result_lst.bind('<Double-Button-1>', self.goto)
+ self.result_scr = Tkinter.Scrollbar(window,
+ orient='vertical', command=self.result_lst.yview)
+ self.result_lst.config(yscrollcommand=self.result_scr.set)
+
+ self.result_frm = Tkinter.Frame(window)
+ self.goto_btn = Tkinter.Button(self.result_frm,
+ text='go to selected', command=self.goto)
+ self.hide_btn = Tkinter.Button(self.result_frm,
+ text='hide results', command=self.hide)
+ self.goto_btn.pack(side='left', fill='x', expand=1)
+ self.hide_btn.pack(side='right', fill='x', expand=1)
+
+ self.window.update()
+ self.minwidth = self.window.winfo_width()
+ self.minheight = self.window.winfo_height()
+ self.bigminheight = (self.server_frm.winfo_reqheight() +
+ self.search_frm.winfo_reqheight() +
+ self.result_lst.winfo_reqheight() +
+ self.result_frm.winfo_reqheight())
+ self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
+ self.expanded = 0
+ self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
+ self.window.wm_minsize(self.minwidth, self.minheight)
+
+ import threading
+ threading.Thread(target=serve, args=(port, self.ready)).start()
+
+ def ready(self, server):
+ self.server = server
+ self.title_lbl.config(
+ text='Python documentation server at\n' + server.url)
+ self.open_btn.config(state='normal')
+ self.quit_btn.config(state='normal')
+
+ def open(self, event=None):
+ import webbrowser
+ webbrowser.open(self.server.url)
+
+ def quit(self, event=None):
+ if self.server:
+ self.server.quit = 1
+ self.window.quit()
+
+ def search(self, event=None):
+ key = self.search_ent.get()
+ self.stop_btn.pack(side='right')
+ self.stop_btn.config(state='normal')
+ self.search_lbl.config(text='Searching for "%s"...' % key)
+ self.search_ent.forget()
+ self.search_lbl.pack(side='left')
+ self.result_lst.delete(0, 'end')
+ self.goto_btn.config(state='disabled')
+ self.expand()
+
+ import threading
+ if self.scanner:
+ self.scanner.quit = 1
+ self.scanner = ModuleScanner()
+ threading.Thread(target=self.scanner.run,
+ args=(key, self.update, self.done)).start()
+
+ def update(self, path, modname, desc):
+ if modname[-9:] == '.__init__':
+ modname = modname[:-9] + ' (package)'
+ self.result_lst.insert('end',
+ modname + ' - ' + (desc or '(no description)'))
+
+ def stop(self, event=None):
+ if self.scanner:
+ self.scanner.quit = 1
+ self.scanner = None
+
+ def done(self):
+ self.scanner = None
+ self.search_lbl.config(text='Search for')
+ self.search_lbl.pack(side='left')
+ self.search_ent.pack(side='right', fill='x', expand=1)
+ if sys.platform != 'win32': self.stop_btn.forget()
+ self.stop_btn.config(state='disabled')
+
+ def select(self, event=None):
+ self.goto_btn.config(state='normal')
+
+ def goto(self, event=None):
+ selection = self.result_lst.curselection()
+ if selection:
+ import webbrowser
+ modname = split(self.result_lst.get(selection[0]))[0]
+ webbrowser.open(self.server.url + modname + '.html')
+
+ def collapse(self):
+ if not self.expanded: return
+ self.result_frm.forget()
+ self.result_scr.forget()
+ self.result_lst.forget()
+ self.bigwidth = self.window.winfo_width()
+ self.bigheight = self.window.winfo_height()
+ self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
+ self.window.wm_minsize(self.minwidth, self.minheight)
+ self.expanded = 0
+
+ def expand(self):
+ if self.expanded: return
+ self.result_frm.pack(side='bottom', fill='x')
+ self.result_scr.pack(side='right', fill='y')
+ self.result_lst.pack(side='top', fill='both', expand=1)
+ self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
+ self.window.wm_minsize(self.minwidth, self.bigminheight)
+ self.expanded = 1
+
+ def hide(self, event=None):
+ self.stop()
+ self.collapse()
+
+ import Tkinter
try:
! gui = GUI(Tkinter.Tk())
! Tkinter.mainloop()
except KeyboardInterrupt:
! pass
# -------------------------------------------------- command-line interface
def cli():
+ """Command-line interface (looks at sys.argv to decide what to do)."""
import getopt
class BadUsage: pass
try:
! if sys.platform in ['mac', 'win', 'win32', 'nt'] and not sys.argv[1:]:
! # CLI-less platforms
! gui()
! return
!
! opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
writing = 0
for opt, val in opts:
+ if opt == '-g':
+ gui()
+ return
if opt == '-k':
! apropos(val)
! return
if opt == '-p':
try:
***************
*** 1134,1192 ****
except ValueError:
raise BadUsage
! def ready(port=port):
! print 'server ready at http://127.0.0.1:%d/' % port
! serve(('127.0.0.1', port), ready)
! break
if opt == '-w':
- if not args: raise BadUsage
writing = 1
- else:
- if args:
- for arg in args:
- try:
- if os.path.isfile(arg):
- arg = importfile(arg)
- if writing:
- if os.path.isdir(arg): writedocs(arg)
- else: writedoc(arg)
- else: man(arg)
- except DocImportError, value:
- print 'problem in %s - %s' % (
- value.filename, value.args)
- else:
- if sys.platform in ['mac', 'win', 'win32', 'nt']:
- # GUI platforms with threading
- import threading
- ready = threading.Event()
- address = ('127.0.0.1', 12346)
- threading.Thread(
- target=serve, args=(address, ready.set)).start()
- ready.wait()
- import webbrowser
- webbrowser.open('http://127.0.0.1:12346/')
- else:
- raise BadUsage
except (getopt.error, BadUsage):
! print """%s <name> ...
! Show documentation on something.
! <name> may be the name of a Python function, module, or package,
! or a dotted reference to a class or function within a module or
! module in a package, or the filename of a Python module to import.
%s -k <keyword>
! Search for a keyword in the synopsis lines of all modules.
%s -p <port>
Start an HTTP server on the given port on the local machine.
! %s -w <module> ...
! Write out the HTML documentation for a module to a file.
- %s -w <moduledir>
- Write out the HTML documentation for all modules in the tree
- under a given directory to files in the current directory.
- """ % ((sys.argv[0],) * 5)
- if __name__ == '__main__':
- cli()
--- 1346,1391 ----
except ValueError:
raise BadUsage
! def ready(server):
! print 'server ready at %s' % server.url
! serve(port, ready)
! return
if opt == '-w':
writing = 1
+ if not args: raise BadUsage
+ for arg in args:
+ try:
+ if find(arg, os.sep) >= 0 and os.path.isfile(arg):
+ arg = importfile(arg)
+ if writing: writedoc(arg)
+ else: man(arg)
+ except DocImportError, value:
+ print 'Problem in %s - %s' % (value.filename, value.args)
+
except (getopt.error, BadUsage):
! cmd = sys.argv[0]
! print """pydoc - the Python documentation tool
!
! %s <name> ...
! Show text documentation on something. <name> may be the name of a
! function, module, or package, or a dotted reference to a class or
! function within a module or module in a package. If <name> contains
! a '%s', it is used as the path to a Python source file to document.
%s -k <keyword>
! Search for a keyword in the synopsis lines of all available modules.
%s -p <port>
Start an HTTP server on the given port on the local machine.
+
+ %s -g
+ Pop up a graphical interface for serving and finding documentation.
+
+ %s -w <name> ...
+ Write out the HTML documentation for a module to a file in the current
+ directory. If <name> contains a '%s', it is treated as a filename.
+ """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
! if __name__ == '__main__': cli()