Properly qualify methods inherited from classes in other modules.

Fix so that docother() doesn't blow up.
Eliminate man() function since doc() and man() did nearly the same thing.
Various other code cleanup and refactoring to reduce duplication.
Simplify and rewrite freshimport() so modules are always up to date,
    even within packages (where reload() doesn't work).
Add finalization callback to the server (so that if the server fails to
    start for some reason, the main thread isn't left hanging).
This commit is contained in:
Ka-Ping Yee 2001-04-12 10:50:23 +00:00
parent 15d8927f7e
commit 9aa0d90947
1 changed files with 130 additions and 152 deletions

View File

@ -66,7 +66,7 @@ def synopsis(filename, cache={}):
result = split(module.__doc__ or '', '\n')[0] result = split(module.__doc__ or '', '\n')[0]
else: # text modules can be directly examined else: # text modules can be directly examined
line = file.readline() line = file.readline()
while line[:1] == '#' or strip(line) == '': while line[:1] == '#' or not strip(line):
line = file.readline() line = file.readline()
if not line: break if not line: break
line = strip(line) line = strip(line)
@ -125,8 +125,9 @@ def isconstant(object):
def replace(text, *pairs): def replace(text, *pairs):
"""Do a series of global replacements on a string.""" """Do a series of global replacements on a string."""
for old, new in pairs: while pairs:
text = join(split(text, old), new) text = join(split(text, pairs[0]), pairs[1])
pairs = pairs[2:]
return text return text
def cram(text, maxlen): def cram(text, maxlen):
@ -224,7 +225,7 @@ class HTMLRepr(Repr):
self.maxstring = self.maxother = 100 self.maxstring = self.maxother = 100
def escape(self, text): def escape(self, text):
return replace(text, ('&', '&amp;'), ('<', '&lt;'), ('>', '&gt;')) return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
def repr(self, object): def repr(self, object):
return Repr.repr(self, object) return Repr.repr(self, object)
@ -239,7 +240,7 @@ class HTMLRepr(Repr):
def repr_string(self, x, level): def repr_string(self, x, level):
test = cram(x, self.maxstring) test = cram(x, self.maxstring)
testrepr = repr(test) testrepr = repr(test)
if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')): if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
# Backslashes are only literal in the string and are never # Backslashes are only literal in the string and are never
# needed to make any special characters, so show a raw string. # needed to make any special characters, so show a raw string.
return 'r' + testrepr[0] + self.escape(test) + testrepr[0] return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
@ -316,8 +317,8 @@ TT { font-family: lucida console, lucida typewriter, courier }
def preformat(self, text): def preformat(self, text):
"""Format literal preformatted text.""" """Format literal preformatted text."""
text = self.escape(expandtabs(text)) text = self.escape(expandtabs(text))
return replace(text, ('\n\n', '\n \n'), ('\n\n', '\n \n'), return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
(' ', '&nbsp;'), ('\n', '<br>\n')) ' ', '&nbsp;', '\n', '<br>\n')
def multicolumn(self, list, format, cols=4): def multicolumn(self, list, format, cols=4):
"""Format a list of items into a multi-column list.""" """Format a list of items into a multi-column list."""
@ -343,9 +344,7 @@ TT { font-family: lucida console, lucida typewriter, courier }
def classlink(self, object, modname, *dicts): def classlink(self, object, modname, *dicts):
"""Make a link for a class.""" """Make a link for a class."""
name = object.__name__ name = classname(object, modname)
if object.__module__ != modname:
name = object.__module__ + '.' + name
for dict in dicts: for dict in dicts:
if dict.has_key(object): if dict.has_key(object):
return '<a href="%s">%s</a>' % (dict[object], name) return '<a href="%s">%s</a>' % (dict[object], name)
@ -425,7 +424,7 @@ TT { font-family: lucida console, lucida typewriter, courier }
entry, modname, classes, c) entry, modname, classes, c)
return '<dl>\n%s</dl>\n' % result return '<dl>\n%s</dl>\n' % result
def docmodule(self, object, name=None): def docmodule(self, object, name=None, mod=None):
"""Produce HTML documentation for a module object.""" """Produce HTML documentation for a module object."""
name = object.__name__ # ignore the passed-in name name = object.__name__ # ignore the passed-in name
parts = split(name, '.') parts = split(name, '.')
@ -509,13 +508,13 @@ TT { font-family: lucida console, lucida typewriter, courier }
contents = [self.formattree( contents = [self.formattree(
inspect.getclasstree(classlist, 1), name, cdict)] inspect.getclasstree(classlist, 1), name, cdict)]
for key, value in classes: for key, value in classes:
contents.append(self.document(value, key, fdict, cdict)) contents.append(self.document(value, key, name, fdict, cdict))
result = result + self.bigsection( result = result + self.bigsection(
'Classes', '#ffffff', '#ee77aa', join(contents)) 'Classes', '#ffffff', '#ee77aa', join(contents))
if funcs: if funcs:
contents = [] contents = []
for key, value in funcs: for key, value in funcs:
contents.append(self.document(value, key, fdict, cdict)) contents.append(self.document(value, key, name, fdict, cdict))
result = result + self.bigsection( result = result + self.bigsection(
'Functions', '#ffffff', '#eeaa77', join(contents)) 'Functions', '#ffffff', '#eeaa77', join(contents))
if constants: if constants:
@ -535,21 +534,20 @@ TT { font-family: lucida console, lucida typewriter, courier }
return result return result
def docclass(self, object, name=None, funcs={}, classes={}): def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
"""Produce HTML documentation for a class object.""" """Produce HTML documentation for a class object."""
realname = object.__name__ realname = object.__name__
name = name or realname name = name or realname
bases = object.__bases__ bases = object.__bases__
contents = '' contents = ''
methods, mdict = [], {} methods, mdict = allmethods(object).items(), {}
for key, value in allmethods(object).items():
methods.append((key, value))
mdict[key] = mdict[value] = '#' + name + '-' + key
methods.sort() methods.sort()
for key, value in methods:
mdict[key] = mdict[value] = '#' + name + '-' + key
for key, value in methods: for key, value in methods:
contents = contents + self.document( contents = contents + self.document(
value, key, funcs, classes, mdict, object) value, key, mod, funcs, classes, mdict, object)
if name == realname: if name == realname:
title = '<a name="%s">class <strong>%s</strong></a>' % ( title = '<a name="%s">class <strong>%s</strong></a>' % (
@ -573,7 +571,7 @@ TT { font-family: lucida console, lucida typewriter, courier }
"""Format an argument default value as text.""" """Format an argument default value as text."""
return self.small(self.grey('=' + self.repr(object))) return self.small(self.grey('=' + self.repr(object)))
def docroutine(self, object, name=None, def docroutine(self, object, name=None, mod=None,
funcs={}, classes={}, methods={}, cl=None): funcs={}, classes={}, methods={}, cl=None):
"""Produce HTML documentation for a function or method object.""" """Produce HTML documentation for a function or method object."""
realname = object.__name__ realname = object.__name__
@ -583,18 +581,16 @@ TT { font-family: lucida console, lucida typewriter, courier }
skipdocs = 0 skipdocs = 0
if inspect.ismethod(object): if inspect.ismethod(object):
if cl: if cl:
if object.im_class is not cl: imclass = object.im_class
base = object.im_class if imclass is not cl:
url = '#%s-%s' % (base.__name__, name) url = '%s.html#%s-%s' % (
basename = base.__name__ imclass.__module__, imclass.__name__, name)
if base.__module__ != cl.__module__: note = ' from <a href="%s">%s</a>' % (
url = base.__module__ + '.html' + url url, classname(imclass, mod))
basename = base.__module__ + '.' + basename
note = ' from <a href="%s">%s</a>' % (url, basename)
skipdocs = 1 skipdocs = 1
else: else:
note = (object.im_self and note = (object.im_self and
' method of ' + self.repr(object.im_self) or ' method of %s instance' + object.im_self.__class__ or
' unbound %s method' % object.im_class.__name__) ' unbound %s method' % object.im_class.__name__)
object = object.im_func object = object.im_func
@ -631,9 +627,10 @@ TT { font-family: lucida console, lucida typewriter, courier }
doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc) doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
return '<dl><dt>%s%s</dl>\n' % (decl, doc) return '<dl><dt>%s%s</dl>\n' % (decl, doc)
def docother(self, object, name=None): def docother(self, object, name=None, mod=None):
"""Produce HTML documentation for a data object.""" """Produce HTML documentation for a data object."""
return '<strong>%s</strong> = %s' % (name, self.repr(object)) lhs = name and '<strong>%s</strong> = ' % name or ''
return lhs + self.repr(object)
def index(self, dir, shadowed=None): def index(self, dir, shadowed=None):
"""Generate an HTML index for a directory of modules.""" """Generate an HTML index for a directory of modules."""
@ -682,7 +679,7 @@ class TextRepr(Repr):
def repr_string(self, x, level): def repr_string(self, x, level):
test = cram(x, self.maxstring) test = cram(x, self.maxstring)
testrepr = repr(test) testrepr = repr(test)
if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')): if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
# Backslashes are only literal in the string and are never # Backslashes are only literal in the string and are never
# needed to make any special characters, so show a raw string. # needed to make any special characters, so show a raw string.
return 'r' + testrepr[0] + test + testrepr[0] return 'r' + testrepr[0] + test + testrepr[0]
@ -736,7 +733,7 @@ class TextDoc(Doc):
entry, modname, c, prefix + ' ') entry, modname, c, prefix + ' ')
return result return result
def docmodule(self, object, name=None): def docmodule(self, object, name=None, mod=None):
"""Produce text documentation for a given module object.""" """Produce text documentation for a given module object."""
name = object.__name__ # ignore the passed-in name name = object.__name__ # ignore the passed-in name
synop, desc = splitdoc(getdoc(object)) synop, desc = splitdoc(getdoc(object))
@ -780,19 +777,19 @@ class TextDoc(Doc):
contents = [self.formattree( contents = [self.formattree(
inspect.getclasstree(classlist, 1), name)] inspect.getclasstree(classlist, 1), name)]
for key, value in classes: for key, value in classes:
contents.append(self.document(value, key)) contents.append(self.document(value, key, name))
result = result + self.section('CLASSES', join(contents, '\n')) result = result + self.section('CLASSES', join(contents, '\n'))
if funcs: if funcs:
contents = [] contents = []
for key, value in funcs: for key, value in funcs:
contents.append(self.document(value, key)) contents.append(self.document(value, key, name))
result = result + self.section('FUNCTIONS', join(contents, '\n')) result = result + self.section('FUNCTIONS', join(contents, '\n'))
if constants: if constants:
contents = [] contents = []
for key, value in constants: for key, value in constants:
contents.append(self.docother(value, key, 70)) contents.append(self.docother(value, key, name, 70))
result = result + self.section('CONSTANTS', join(contents, '\n')) result = result + self.section('CONSTANTS', join(contents, '\n'))
if hasattr(object, '__version__'): if hasattr(object, '__version__'):
@ -808,7 +805,7 @@ class TextDoc(Doc):
result = result + self.section('CREDITS', str(object.__credits__)) result = result + self.section('CREDITS', str(object.__credits__))
return result return result
def docclass(self, object, name=None): def docclass(self, object, name=None, mod=None):
"""Produce text documentation for a given class object.""" """Produce text documentation for a given class object."""
realname = object.__name__ realname = object.__name__
name = name or realname name = name or realname
@ -828,7 +825,7 @@ class TextDoc(Doc):
methods = allmethods(object).items() methods = allmethods(object).items()
methods.sort() methods.sort()
for key, value in methods: for key, value in methods:
contents = contents + '\n' + self.document(value, key, object) contents = contents + '\n' + self.document(value, key, mod, object)
if not contents: return title + '\n' if not contents: return title + '\n'
return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n' return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
@ -837,26 +834,22 @@ class TextDoc(Doc):
"""Format an argument default value as text.""" """Format an argument default value as text."""
return '=' + self.repr(object) return '=' + self.repr(object)
def docroutine(self, object, name=None, cl=None): def docroutine(self, object, name=None, mod=None, cl=None):
"""Produce text documentation for a function or method object.""" """Produce text documentation for a function or method object."""
realname = object.__name__ realname = object.__name__
name = name or realname name = name or realname
note = '' note = ''
skipdocs = 0 skipdocs = 0
if inspect.ismethod(object): if inspect.ismethod(object):
imclass = object.im_class
if cl: if cl:
if object.im_class is not cl: if imclass is not cl:
base = object.im_class note = ' from ' + classname(imclass, mod)
basename = base.__name__
if base.__module__ != cl.__module__:
basename = base.__module__ + '.' + basename
note = ' from %s' % basename
skipdocs = 1 skipdocs = 1
else: else:
if object.im_self: note = (object.im_self and
note = ' method of %s' % self.repr(object.im_self) ' method of %s instance' + object.im_self.__class__ or
else: ' unbound %s method' % classname(imclass, mod))
note = ' unbound %s method' % object.im_class.__name__
object = object.im_func object = object.im_func
if name == realname: if name == realname:
@ -883,14 +876,14 @@ class TextDoc(Doc):
doc = getdoc(object) or '' doc = getdoc(object) or ''
return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n') return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
def docother(self, object, name=None, maxlen=None): def docother(self, object, name=None, mod=None, maxlen=None):
"""Produce text documentation for a data object.""" """Produce text documentation for a data object."""
repr = self.repr(object) repr = self.repr(object)
if maxlen: if maxlen:
line = name + ' = ' + repr line = (name and name + ' = ' or '') + repr
chop = maxlen - len(line) chop = maxlen - len(line)
if chop < 0: repr = repr[:chop] + '...' if chop < 0: repr = repr[:chop] + '...'
line = self.bold(name) + ' = ' + repr line = (name and self.bold(name) + ' = ' or '') + repr
return line return line
# --------------------------------------------------------- user interfaces # --------------------------------------------------------- user interfaces
@ -1017,95 +1010,83 @@ def describe(thing):
return 'instance of ' + thing.__class__.__name__ return 'instance of ' + thing.__class__.__name__
return type(thing).__name__ return type(thing).__name__
def freshimport(name, cache={}): def freshimport(path, cache={}):
"""Import a module, reloading it if the source file has changed.""" """Import a module freshly from disk, making sure it's up to date."""
topmodule = __import__(name) if sys.modules.has_key(path):
module = None # This is the only way to be sure. Checking the mtime of the file
for component in split(name, '.'): # isn't good enough (e.g. what if the module contains a class that
if module == None: # inherits from another module that has changed?).
module = topmodule if path not in sys.builtin_module_names:
path = split(name, '.')[0] del sys.modules[path]
try:
module = __import__(path)
except:
# Did the error occur before or after the module was found?
(exc, value, tb) = info = sys.exc_info()
if sys.modules.has_key(path):
# An error occured while executing the imported module.
raise ErrorDuringImport(sys.modules[path].__file__, info)
elif exc is SyntaxError:
# A SyntaxError occurred before we could execute the module.
raise ErrorDuringImport(value.filename, info)
elif exc is ImportError and \
split(lower(str(value)))[:2] == ['no', 'module']:
# The module was not found.
return None
else: else:
module = getattr(module, component) # Some other error occurred during the importing process.
path = path + '.' + component raise ErrorDuringImport(path, sys.exc_info())
if hasattr(module, '__file__'): for part in split(path, '.')[1:]:
file = module.__file__ try: module = getattr(module, part)
if os.path.exists(file): except AttributeError: return None
info = (file, os.path.getmtime(file), os.path.getsize(file))
if cache.get(path) == info:
continue
module = reload(module)
file = module.__file__
if os.path.exists(file):
info = (file, os.path.getmtime(file), os.path.getsize(file))
cache[path] = info
return module return module
def locate(path): def locate(path):
"""Locate an object by name (or dotted path), importing as necessary.""" """Locate an object by name or dotted path, importing as necessary."""
if not path: # special case: imp.find_module('') strangely succeeds
return None
if type(path) is not types.StringType:
return path
parts = split(path, '.') parts = split(path, '.')
n = len(parts) module, n = None, 0
while n > 0: while n < len(parts):
path = join(parts[:n], '.') nextmodule = freshimport(join(parts[:n+1], '.'))
try: if nextmodule: module, n = nextmodule, n + 1
module = freshimport(path) else: break
except: if module:
# Did the error occur before or after the module was found? object = module
(exc, value, tb) = info = sys.exc_info() for part in parts[n:]:
if sys.modules.has_key(path): try: object = getattr(object, part)
# An error occured while executing the imported module. except AttributeError: return None
raise ErrorDuringImport(sys.modules[path].__file__, info) return object
elif exc is SyntaxError: else:
# A SyntaxError occurred before we could execute the module. import __builtin__
raise ErrorDuringImport(value.filename, info) if hasattr(__builtin__, path):
elif exc is ImportError and \ return getattr(__builtin__, path)
split(lower(str(value)))[:2] == ['no', 'module']:
# The module was not found.
n = n - 1
continue
else:
# Some other error occurred before executing the module.
raise ErrorDuringImport(path, sys.exc_info())
try:
x = module
for p in parts[n:]:
x = getattr(x, p)
return x
except AttributeError:
n = n - 1
continue
if hasattr(__builtins__, path):
return getattr(__builtins__, path)
return None
# --------------------------------------- interactive interpreter interface # --------------------------------------- interactive interpreter interface
text = TextDoc() text = TextDoc()
html = HTMLDoc() html = HTMLDoc()
def doc(thing): def doc(thing, title='Python Library Documentation: '):
"""Display documentation on an object (for interactive use).""" """Display text documentation, given an object or a path to an object."""
if type(thing) is type(""): suffix, name = '', None
if type(thing) is type(''):
try: try:
object = locate(thing) object = locate(thing)
except ErrorDuringImport, value: except ErrorDuringImport, value:
print value print value
return return
if object: if not object:
thing = object
else:
print 'no Python documentation found for %s' % repr(thing) print 'no Python documentation found for %s' % repr(thing)
return return
parts = split(thing, '.')
if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
name = parts[-1]
thing = object
desc = describe(thing) desc = describe(thing)
module = inspect.getmodule(thing) module = inspect.getmodule(thing)
if module and module is not thing: if not suffix and module and module is not thing:
desc = desc + ' in module ' + module.__name__ suffix = ' in module ' + module.__name__
pager('Help on %s:\n\n' % desc + text.document(thing)) pager(title + desc + suffix + '\n\n' + text.document(object, name))
def writedoc(key): def writedoc(key):
"""Write HTML documentation to a file in the current directory.""" """Write HTML documentation to a file in the current directory."""
@ -1154,25 +1135,13 @@ help(module) or call help('modulename').''' % sys.version[:3]
help = Helper() help = Helper()
def man(key):
"""Display documentation on an object in a form similar to man(1)."""
object = locate(key)
if object:
title = 'Python Library Documentation: ' + describe(object)
lastdot = rfind(key, '.')
if lastdot > 0: title = title + ' in ' + key[:lastdot]
pager('\n' + title + '\n\n' + text.document(object, key))
found = 1
else:
print 'no Python documentation found for %s' % repr(key)
class Scanner: class Scanner:
"""A generic tree iterator.""" """A generic tree iterator."""
def __init__(self, roots, children, recurse): def __init__(self, roots, children, descendp):
self.roots = roots[:] self.roots = roots[:]
self.state = [] self.state = []
self.children = children self.children = children
self.recurse = recurse self.descendp = descendp
def next(self): def next(self):
if not self.state: if not self.state:
@ -1185,7 +1154,7 @@ class Scanner:
self.state.pop() self.state.pop()
return self.next() return self.next()
child = children.pop(0) child = children.pop(0)
if self.recurse(child): if self.descendp(child):
self.state.append((child, self.children(child))) self.state.append((child, self.children(child)))
return child return child
@ -1203,13 +1172,14 @@ class ModuleScanner(Scanner):
children.append((path, package + (package and '.') + file)) children.append((path, package + (package and '.') + file))
else: else:
children.append((path, package)) children.append((path, package))
children.sort() children.sort() # so that spam.py comes before spam.pyc or spam.pyo
return children return children
def ispackage(self, (dir, package)): def ispackage(self, (dir, package)):
return ispackage(dir) return ispackage(dir)
def run(self, key, callback, completer=None): def run(self, key, callback, completer=None):
key = lower(key)
self.quit = 0 self.quit = 0
seen = {} seen = {}
@ -1217,7 +1187,7 @@ class ModuleScanner(Scanner):
if modname != '__main__': if modname != '__main__':
seen[modname] = 1 seen[modname] = 1
desc = split(freshimport(modname).__doc__ or '', '\n')[0] desc = split(freshimport(modname).__doc__ or '', '\n')[0]
if find(lower(modname + ' - ' + desc), lower(key)) >= 0: if find(lower(modname + ' - ' + desc), key) >= 0:
callback(None, modname, desc) callback(None, modname, desc)
while not self.quit: while not self.quit:
@ -1228,10 +1198,13 @@ class ModuleScanner(Scanner):
if os.path.isfile(path) and modname: if os.path.isfile(path) and modname:
modname = package + (package and '.') + modname modname = package + (package and '.') + modname
if not seen.has_key(modname): if not seen.has_key(modname):
seen[modname] = 1 seen[modname] = 1 # if we see spam.py, skip spam.pyc
desc = synopsis(path) or '' if key:
if find(lower(modname + ' - ' + desc), lower(key)) >= 0: desc = synopsis(path) or ''
callback(path, modname, desc) if find(lower(modname + ' - ' + desc), key) >= 0:
callback(path, modname, desc)
else:
callback(path, modname, '')
if completer: completer() if completer: completer()
def apropos(key): def apropos(key):
@ -1247,8 +1220,8 @@ def apropos(key):
# --------------------------------------------------- web browser interface # --------------------------------------------------- web browser interface
def serve(port, callback=None): def serve(port, callback=None, finalizer=None):
import BaseHTTPServer, mimetools, select import BaseHTTPServer, SocketServer, mimetools, select
# Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
class Message(mimetools.Message): class Message(mimetools.Message):
@ -1306,7 +1279,7 @@ pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
def log_message(self, *args): pass def log_message(self, *args): pass
class DocServer(BaseHTTPServer.HTTPServer): class DocServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
def __init__(self, port, callback): def __init__(self, port, callback):
host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost' host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
self.address = ('', port) self.address = ('', port)
@ -1329,10 +1302,12 @@ pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
DocServer.handler = DocHandler DocServer.handler = DocHandler
DocHandler.MessageClass = Message DocHandler.MessageClass = Message
try: try:
DocServer(port, callback).serve_until_quit() try:
except (KeyboardInterrupt, select.error): DocServer(port, callback).serve_until_quit()
pass except (KeyboardInterrupt, select.error):
print 'server stopped' pass
finally:
if finalizer: finalizer()
# ----------------------------------------------------- graphical interface # ----------------------------------------------------- graphical interface
@ -1404,7 +1379,8 @@ def gui():
self.window.wm_minsize(self.minwidth, self.minheight) self.window.wm_minsize(self.minwidth, self.minheight)
import threading import threading
threading.Thread(target=serve, args=(port, self.ready)).start() threading.Thread(
target=serve, args=(port, self.ready, self.quit)).start()
def ready(self, server): def ready(self, server):
self.server = server self.server = server
@ -1544,8 +1520,10 @@ def cli():
except ValueError: except ValueError:
raise BadUsage raise BadUsage
def ready(server): def ready(server):
print 'server ready at %s' % server.url print 'pydoc server ready at %s' % server.url
serve(port, ready) def stopped():
print 'pydoc server stopped'
serve(port, ready, stopped)
return return
if opt == '-w': if opt == '-w':
writing = 1 writing = 1
@ -1561,7 +1539,7 @@ def cli():
else: else:
writedoc(arg) writedoc(arg)
else: else:
man(arg) doc(arg)
except ErrorDuringImport, value: except ErrorDuringImport, value:
print value print value