blob: 8cc1205a0c08967080afc8fa0734d935000431ca [file] [log] [blame]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001#!/usr/bin/env python
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002"""Generate Python documentation in HTML or text for interactive use.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00003
Ka-Ping Yeedd175342001-02-27 14:43:46 +00004In the Python interpreter, do "from pydoc import help" to provide online
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00005help. Calling help(thing) on a Python object documents the object.
6
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00007Or, at the shell command line outside of Python:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00008
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00009Run "pydoc <name>" to show documentation on something. <name> may be
10the name of a function, module, package, or a dotted reference to a
11class or function within a module or module in a package. If the
12argument contains a path segment delimiter (e.g. slash on Unix,
13backslash on Windows) it is treated as the path to a Python source file.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000014
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000015Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
16of all available modules.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000017
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000018Run "pydoc -p <port>" to start an HTTP server on a given port on the
19local machine to generate documentation web pages.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000020
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000021For platforms without a command line, "pydoc -g" starts the HTTP server
22and also pops up a little window for controlling it.
23
24Run "pydoc -w <name>" to write out the HTML documentation for a module
25to a file named "<name>.html".
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000026"""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000027
28__author__ = "Ka-Ping Yee <ping@lfw.org>"
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000029__date__ = "26 February 2001"
Ka-Ping Yee09d7d9a2001-02-27 22:43:48 +000030__version__ = "$Revision$"
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000031__credits__ = """Guido van Rossum, for an excellent programming language.
32Tommy Burnette, the original creator of manpy.
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000033Paul Prescod, for all his work on onlinehelp.
34Richard Chamberlain, for the first implementation of textdoc.
35
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000036Mynd you, møøse bites Kan be pretty nasti..."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000037
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000038# Note: this module is designed to deploy instantly and run under any
39# version of Python from 1.5 and up. That's why it's a single file and
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000040# some 2.0 features (like string methods) are conspicuously absent.
41
42# Known bugs that can't be fixed here:
43# - imp.load_module() cannot be prevented from clobbering existing
44# loaded modules, so calling synopsis() on a binary module file
45# changes the contents of any existing module with the same name.
46# - If the __file__ attribute on a module is a relative path and
47# the current directory is changed with os.chdir(), an incorrect
48# path will be displayed.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000049
Ka-Ping Yeedd175342001-02-27 14:43:46 +000050import sys, imp, os, stat, re, types, inspect
51from repr import Repr
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000052from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
Ka-Ping Yeedd175342001-02-27 14:43:46 +000053
54# --------------------------------------------------------- common routines
55
Ka-Ping Yeedd175342001-02-27 14:43:46 +000056def pathdirs():
57 """Convert sys.path into a list of absolute, existing, unique paths."""
58 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000059 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000060 for dir in sys.path:
61 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000062 normdir = os.path.normcase(dir)
63 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000064 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000065 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000066 return dirs
67
68def getdoc(object):
69 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000070 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +000071 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000072
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000073def splitdoc(doc):
74 """Split a doc string into a synopsis line (if any) and the rest."""
75 lines = split(strip(doc), '\n')
76 if len(lines) == 1:
77 return lines[0], ''
78 elif len(lines) >= 2 and not rstrip(lines[1]):
79 return lines[0], join(lines[2:], '\n')
80 return '', join(lines, '\n')
81
Ka-Ping Yeedd175342001-02-27 14:43:46 +000082def classname(object, modname):
83 """Get a class name and qualify it with a module name if necessary."""
84 name = object.__name__
85 if object.__module__ != modname:
86 name = object.__module__ + '.' + name
87 return name
88
Ka-Ping Yeedec96e92001-04-13 09:55:49 +000089def isdata(object):
90 """Check if an object is of a type that probably means it's data."""
91 return not (inspect.ismodule(object) or inspect.isclass(object) or
92 inspect.isroutine(object) or inspect.isframe(object) or
93 inspect.istraceback(object) or inspect.iscode(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +000094
95def replace(text, *pairs):
96 """Do a series of global replacements on a string."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +000097 while pairs:
98 text = join(split(text, pairs[0]), pairs[1])
99 pairs = pairs[2:]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000100 return text
101
102def cram(text, maxlen):
103 """Omit part of a string if needed to make it fit in a maximum length."""
104 if len(text) > maxlen:
105 pre = max(0, (maxlen-3)/2)
106 post = max(0, maxlen-3-pre)
107 return text[:pre] + '...' + text[len(text)-post:]
108 return text
109
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000110def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000111 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000112 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000113 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
114 if re.search(pattern, repr(Exception)):
115 return re.sub(pattern, '>', text)
116 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000117
Tim Peters536d2262001-09-20 05:13:38 +0000118def _is_some_method(object):
119 return inspect.ismethod(object) or inspect.ismethoddescriptor(object)
120
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000121def allmethods(cl):
122 methods = {}
Tim Peters536d2262001-09-20 05:13:38 +0000123 for key, value in inspect.getmembers(cl, _is_some_method):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000124 methods[key] = 1
125 for base in cl.__bases__:
126 methods.update(allmethods(base)) # all your base are belong to us
127 for key in methods.keys():
128 methods[key] = getattr(cl, key)
129 return methods
130
Tim Peters28355492001-09-23 21:29:55 +0000131def _split_class_attrs(attrs, predicate):
132 yes = []
133 no = []
134 for tuple in attrs:
135 if predicate(tuple):
136 yes.append(tuple)
137 else:
138 no.append(tuple)
139 return yes, no
140
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000141# ----------------------------------------------------- module manipulation
142
143def ispackage(path):
144 """Guess whether a path refers to a package directory."""
145 if os.path.isdir(path):
146 for ext in ['.py', '.pyc', '.pyo']:
147 if os.path.isfile(os.path.join(path, '__init__' + ext)):
148 return 1
149
150def synopsis(filename, cache={}):
151 """Get the one-line summary out of a module file."""
152 mtime = os.stat(filename)[stat.ST_MTIME]
153 lastupdate, result = cache.get(filename, (0, None))
154 if lastupdate < mtime:
155 info = inspect.getmoduleinfo(filename)
156 file = open(filename)
157 if info and 'b' in info[2]: # binary modules have to be imported
158 try: module = imp.load_module('__temp__', file, filename, info[1:])
159 except: return None
160 result = split(module.__doc__ or '', '\n')[0]
161 del sys.modules['__temp__']
162 else: # text modules can be directly examined
163 line = file.readline()
164 while line[:1] == '#' or not strip(line):
165 line = file.readline()
166 if not line: break
167 line = strip(line)
168 if line[:4] == 'r"""': line = line[1:]
169 if line[:3] == '"""':
170 line = line[3:]
171 if line[-1:] == '\\': line = line[:-1]
172 while not strip(line):
173 line = file.readline()
174 if not line: break
175 result = strip(split(line, '"""')[0])
176 else: result = None
177 file.close()
178 cache[filename] = (mtime, result)
179 return result
180
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000181class ErrorDuringImport(Exception):
182 """Errors that occurred while trying to import something to document it."""
183 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000184 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000185 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000186 self.value = value
187 self.tb = tb
188
189 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000190 exc = self.exc
191 if type(exc) is types.ClassType:
192 exc = exc.__name__
193 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000194
195def importfile(path):
196 """Import a Python source file or compiled file given its path."""
197 magic = imp.get_magic()
198 file = open(path, 'r')
199 if file.read(len(magic)) == magic:
200 kind = imp.PY_COMPILED
201 else:
202 kind = imp.PY_SOURCE
203 file.close()
204 filename = os.path.basename(path)
205 name, ext = os.path.splitext(filename)
206 file = open(path, 'r')
207 try:
208 module = imp.load_module(name, file, path, (ext, 'r', kind))
209 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000210 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000211 file.close()
212 return module
213
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000214def safeimport(path, forceload=0, cache={}):
215 """Import a module; handle errors; return None if the module isn't found.
216
217 If the module *is* found but an exception occurs, it's wrapped in an
218 ErrorDuringImport exception and reraised. Unlike __import__, if a
219 package path is specified, the module at the end of the path is returned,
220 not the package at the beginning. If the optional 'forceload' argument
221 is 1, we reload the module from disk (unless it's a dynamic extension)."""
222 if forceload and sys.modules.has_key(path):
223 # This is the only way to be sure. Checking the mtime of the file
224 # isn't good enough (e.g. what if the module contains a class that
225 # inherits from another module that has changed?).
226 if path not in sys.builtin_module_names:
227 # Python never loads a dynamic extension a second time from the
228 # same path, even if the file is changed or missing. Deleting
229 # the entry in sys.modules doesn't help for dynamic extensions,
230 # so we're not even going to try to keep them up to date.
231 info = inspect.getmoduleinfo(sys.modules[path].__file__)
232 if info[3] != imp.C_EXTENSION:
233 cache[path] = sys.modules[path] # prevent module from clearing
234 del sys.modules[path]
235 try:
236 module = __import__(path)
237 except:
238 # Did the error occur before or after the module was found?
239 (exc, value, tb) = info = sys.exc_info()
240 if sys.modules.has_key(path):
241 # An error occured while executing the imported module.
242 raise ErrorDuringImport(sys.modules[path].__file__, info)
243 elif exc is SyntaxError:
244 # A SyntaxError occurred before we could execute the module.
245 raise ErrorDuringImport(value.filename, info)
246 elif exc is ImportError and \
247 split(lower(str(value)))[:2] == ['no', 'module']:
248 # The module was not found.
249 return None
250 else:
251 # Some other error occurred during the importing process.
252 raise ErrorDuringImport(path, sys.exc_info())
253 for part in split(path, '.')[1:]:
254 try: module = getattr(module, part)
255 except AttributeError: return None
256 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000257
258# ---------------------------------------------------- formatter base class
259
260class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000261 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000262 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000263 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000264 if inspect.ismodule(object): return apply(self.docmodule, args)
265 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000266 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000267 return apply(self.docother, args)
268
269 def fail(self, object, name=None, *args):
270 """Raise an exception for unimplemented types."""
271 message = "don't know how to document object%s of type %s" % (
272 name and ' ' + repr(name), type(object).__name__)
273 raise TypeError, message
274
275 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000276
277# -------------------------------------------- HTML documentation generator
278
279class HTMLRepr(Repr):
280 """Class for safely making an HTML representation of a Python object."""
281 def __init__(self):
282 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000283 self.maxlist = self.maxtuple = 20
284 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000285 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000286
287 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000288 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000289
290 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000291 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000292
293 def repr1(self, x, level):
294 methodname = 'repr_' + join(split(type(x).__name__), '_')
295 if hasattr(self, methodname):
296 return getattr(self, methodname)(x, level)
297 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000298 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000299
300 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000301 test = cram(x, self.maxstring)
302 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000303 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000304 # Backslashes are only literal in the string and are never
305 # needed to make any special characters, so show a raw string.
306 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000307 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000308 r'<font color="#c040c0">\1</font>',
309 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000310
311 def repr_instance(self, x, level):
312 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000313 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000314 except:
315 return self.escape('<%s instance>' % x.__class__.__name__)
316
317 repr_unicode = repr_string
318
319class HTMLDoc(Doc):
320 """Formatter class for HTML documentation."""
321
322 # ------------------------------------------- HTML formatting utilities
323
324 _repr_instance = HTMLRepr()
325 repr = _repr_instance.repr
326 escape = _repr_instance.escape
327
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000328 def page(self, title, contents):
329 """Format an HTML page."""
330 return '''
331<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000332<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000333<style type="text/css"><!--
Ka-Ping Yeed03f8fe2001-04-13 15:04:32 +0000334TT { font-family: lucidatypewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000335--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000336%s
337</body></html>''' % (title, contents)
338
339 def heading(self, title, fgcol, bgcol, extras=''):
340 """Format a page heading."""
341 return '''
342<table width="100%%" cellspacing=0 cellpadding=2 border=0>
343<tr bgcolor="%s">
344<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000345><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000346><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000347><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000348 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
349
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000350 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000351 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
352 """Format a section with a heading."""
353 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000354 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000355 result = '''
356<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
357<tr bgcolor="%s">
358<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
359><font color="%s" face="helvetica, arial">%s</font></td></tr>
360 ''' % (bgcol, fgcol, title)
361 if prelude:
362 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000363<tr bgcolor="%s"><td rowspan=2>%s</td>
364<td colspan=2>%s</td></tr>
365<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
366 else:
367 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000368<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000369
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000370 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000371
372 def bigsection(self, title, *args):
373 """Format a section with a big heading."""
374 title = '<big><strong>%s</strong></big>' % title
375 return apply(self.section, (title,) + args)
376
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000377 def preformat(self, text):
378 """Format literal preformatted text."""
379 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000380 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
381 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000382
383 def multicolumn(self, list, format, cols=4):
384 """Format a list of items into a multi-column list."""
385 result = ''
386 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000387 for col in range(cols):
388 result = result + '<td width="%d%%" valign=top>' % (100/cols)
389 for i in range(rows*col, rows*col+rows):
390 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000391 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000392 result = result + '</td>'
393 return '<table width="100%%"><tr>%s</tr></table>' % result
394
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000395 def small(self, text): return '<small>%s</small>' % text
396 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000397
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000398 def namelink(self, name, *dicts):
399 """Make a link for an identifier, given name-to-URL mappings."""
400 for dict in dicts:
401 if dict.has_key(name):
402 return '<a href="%s">%s</a>' % (dict[name], name)
403 return name
404
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000405 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000406 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000407 name, module = object.__name__, sys.modules.get(object.__module__)
408 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000409 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000410 module.__name__, name, classname(object, modname))
411 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000412
413 def modulelink(self, object):
414 """Make a link for a module."""
415 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
416
417 def modpkglink(self, (name, path, ispackage, shadowed)):
418 """Make a link for a module or package to display in an index."""
419 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000420 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000421 if path:
422 url = '%s.%s.html' % (path, name)
423 else:
424 url = '%s.html' % name
425 if ispackage:
426 text = '<strong>%s</strong>&nbsp;(package)' % name
427 else:
428 text = name
429 return '<a href="%s">%s</a>' % (url, text)
430
431 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
432 """Mark up some plain text, given a context of symbols to look for.
433 Each context dictionary maps object names to anchor names."""
434 escape = escape or self.escape
435 results = []
436 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000437 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
438 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000439 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000440 r'(self\.)?(\w+))\b')
441 while 1:
442 match = pattern.search(text, here)
443 if not match: break
444 start, end = match.span()
445 results.append(escape(text[here:start]))
446
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000447 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000448 if scheme:
449 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000450 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000451 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
452 results.append('<a href="%s">%s</a>' % (url, escape(all)))
453 elif pep:
454 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000455 results.append('<a href="%s">%s</a>' % (url, escape(all)))
456 elif text[end:end+1] == '(':
457 results.append(self.namelink(name, methods, funcs, classes))
458 elif selfdot:
459 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000460 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000461 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000462 here = end
463 results.append(escape(text[here:]))
464 return join(results, '')
465
466 # ---------------------------------------------- type-specific routines
467
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000468 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000469 """Produce HTML for a class tree as given by inspect.getclasstree()."""
470 result = ''
471 for entry in tree:
472 if type(entry) is type(()):
473 c, bases = entry
474 result = result + '<dt><font face="helvetica, arial"><small>'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000475 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000476 if bases and bases != (parent,):
477 parents = []
478 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000479 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000480 result = result + '(' + join(parents, ', ') + ')'
481 result = result + '\n</small></font></dt>'
482 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000483 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000484 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000485 return '<dl>\n%s</dl>\n' % result
486
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000487 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000488 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000489 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000490 parts = split(name, '.')
491 links = []
492 for i in range(len(parts)-1):
493 links.append(
494 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
495 (join(parts[:i+1], '.'), parts[i]))
496 linkedname = join(links + parts[-1:], '.')
497 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000498 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000499 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000500 url = path
501 if sys.platform == 'win32':
502 import nturl2path
503 url = nturl2path.pathname2url(path)
504 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000505 except TypeError:
506 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000507 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000508 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000509 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000510 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
511 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000512 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000513 if hasattr(object, '__date__'):
514 info.append(self.escape(str(object.__date__)))
515 if info:
516 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000517 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000518 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
519
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000520 modules = inspect.getmembers(object, inspect.ismodule)
521
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000522 classes, cdict = [], {}
523 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000524 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000525 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000526 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000527 for key, value in classes:
528 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000529 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000530 module = sys.modules.get(modname)
531 if modname != name and module and hasattr(module, key):
532 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000533 if not cdict.has_key(key):
534 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000535 funcs, fdict = [], {}
536 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000537 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000538 funcs.append((key, value))
539 fdict[key] = '#-' + key
540 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000541 data = []
542 for key, value in inspect.getmembers(object, isdata):
543 if key not in ['__builtins__', '__doc__']:
544 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000545
546 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
547 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000548 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000549
550 if hasattr(object, '__path__'):
551 modpkgs = []
552 modnames = []
553 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000554 path = os.path.join(object.__path__[0], file)
555 modname = inspect.getmodulename(file)
556 if modname and modname not in modnames:
557 modpkgs.append((modname, name, 0, 0))
558 modnames.append(modname)
559 elif ispackage(path):
560 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000561 modpkgs.sort()
562 contents = self.multicolumn(modpkgs, self.modpkglink)
563 result = result + self.bigsection(
564 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000565 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000566 contents = self.multicolumn(
567 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000568 result = result + self.bigsection(
569 'Modules', '#fffff', '#aa55cc', contents)
570
571 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000572 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000573 contents = [
574 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000575 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000576 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000577 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000578 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000579 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000580 contents = []
581 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000582 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000583 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000584 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000585 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000586 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000587 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000588 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000589 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000590 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000591 if hasattr(object, '__author__'):
592 contents = self.markup(str(object.__author__), self.preformat)
593 result = result + self.bigsection(
594 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000595 if hasattr(object, '__credits__'):
596 contents = self.markup(str(object.__credits__), self.preformat)
597 result = result + self.bigsection(
598 'Credits', '#ffffff', '#7799ee', contents)
599
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000600 return result
601
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000602 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000603 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000604 realname = object.__name__
605 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000606 bases = object.__bases__
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000607
Tim Petersb47879b2001-09-24 04:47:19 +0000608 contents = []
609 push = contents.append
610
611 def spill(msg, attrs, predicate):
612 ok, attrs = _split_class_attrs(attrs, predicate)
613 if ok:
614 push(msg)
615 for name, kind, homecls, value in ok:
616 push(self.document(getattr(object, name), name, mod,
617 funcs, classes, mdict, object))
618 push('\n')
619 return attrs
620
621 # pydoc can't make any reasonable sense of properties on its own,
622 # and it doesn't appear that the getter, setter and del'er methods
623 # are discoverable. For now, just pump out their names.
624 def spillproperties(msg, attrs):
625 ok, attrs = _split_class_attrs(attrs, lambda t: t[1] == 'property')
626 if ok:
627 push(msg)
628 for name, kind, homecls, value in ok:
629 push('<dl><dt><strong>%s</strong></dl>\n' % name)
630 return attrs
631
632 def spilldata(msg, attrs):
633 ok, attrs = _split_class_attrs(attrs, lambda t: t[1] == 'data')
634 if ok:
635 push(msg)
636 for name, kind, homecls, value in ok:
637 base = self.docother(getattr(object, name), name, mod)
638 doc = getattr(value, "__doc__", None)
639 if doc is None:
640 push('<dl><dt>%s</dl>\n' % base)
641 else:
642 doc = self.markup(getdoc(value), self.preformat,
643 funcs, classes, mdict)
644 doc = '<dd>' + self.small('<tt>%s</tt>' % doc)
645 push('<dl><dt>%s%s</dl>\n' % (base, doc))
646 push('\n')
647 return attrs
648
649 attrs = inspect.classify_class_attrs(object)
650 mdict = {}
651 for key, kind, homecls, value in attrs:
652 mdict[key] = anchor = '#' + name + '-' + key
653 value = getattr(object, key)
654 try:
655 # The value may not be hashable (e.g., a data attr with
656 # a dict or list value).
657 mdict[value] = anchor
658 except TypeError:
659 pass
660
661 # All attrs defined in this class come first.
662 attrs, inherited = _split_class_attrs(attrs,
663 lambda t: t[2] is object)
664 # Sort inherited attrs by name of defining class.
665 inherited.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
666
667 thisclass = object
668 while attrs or inherited:
669 if thisclass is object:
670 tag = "defined here"
671 else:
672 tag = "inherited from class %s" % self.classlink(thisclass,
673 object.__module__)
674 tag += ':<br>\n'
675
676 # Sort attrs by name.
677 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
678
679 # Pump out the attrs, segregated by kind.
680 attrs = spill("Methods %s" % tag, attrs,
681 lambda t: t[1] == 'method')
682 attrs = spill("Class methods %s" % tag, attrs,
683 lambda t: t[1] == 'class method')
684 attrs = spill("Static methods %s" % tag, attrs,
685 lambda t: t[1] == 'static method')
686 attrs = spillproperties("Properties %s" % tag, attrs)
687 attrs = spilldata("Data %s" % tag, attrs)
688 assert attrs == []
689
690 # Split off the attributes inherited from the next class (note
691 # that inherited remains sorted by class name).
692 if inherited:
693 attrs = inherited
694 thisclass = attrs[0][2]
695 attrs, inherited = _split_class_attrs(attrs,
696 lambda t: t[2] is thisclass)
697
698 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000699
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000700 if name == realname:
701 title = '<a name="%s">class <strong>%s</strong></a>' % (
702 name, realname)
703 else:
704 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
705 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000706 if bases:
707 parents = []
708 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000709 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000710 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000711 doc = self.markup(
712 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000713 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
714 self.small('&nbsp;'))
715 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000716
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000717 def formatvalue(self, object):
718 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000719 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000720
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000721 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000722 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000723 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000724 realname = object.__name__
725 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000726 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000727 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000728 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000729 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000730 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000731 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000732 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000733 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000734 skipdocs = 1
735 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000736 if object.im_self:
737 note = ' method of %s instance' % self.classlink(
738 object.im_self.__class__, mod)
739 else:
740 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000741 object = object.im_func
742
743 if name == realname:
744 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
745 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000746 if (cl and cl.__dict__.has_key(realname) and
747 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000748 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000749 cl.__name__ + '-' + realname, realname)
750 skipdocs = 1
751 else:
752 reallink = realname
753 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
754 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000755 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000756 args, varargs, varkw, defaults = inspect.getargspec(object)
757 argspec = inspect.formatargspec(
758 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000759 if realname == '<lambda>':
760 decl = '<em>lambda</em>'
761 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000762 else:
763 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000764
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000765 decl = title + argspec + (note and self.small(self.grey(
766 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000767
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000768 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000769 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000770 else:
771 doc = self.markup(
772 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000773 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
774 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000775
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000776 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000777 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000778 lhs = name and '<strong>%s</strong> = ' % name or ''
779 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000780
781 def index(self, dir, shadowed=None):
782 """Generate an HTML index for a directory of modules."""
783 modpkgs = []
784 if shadowed is None: shadowed = {}
785 seen = {}
786 files = os.listdir(dir)
787
788 def found(name, ispackage,
789 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
790 if not seen.has_key(name):
791 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
792 seen[name] = 1
793 shadowed[name] = 1
794
795 # Package spam/__init__.py takes precedence over module spam.py.
796 for file in files:
797 path = os.path.join(dir, file)
798 if ispackage(path): found(file, 1)
799 for file in files:
800 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000801 if os.path.isfile(path):
802 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000803 if modname: found(modname, 0)
804
805 modpkgs.sort()
806 contents = self.multicolumn(modpkgs, self.modpkglink)
807 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
808
809# -------------------------------------------- text documentation generator
810
811class TextRepr(Repr):
812 """Class for safely making a text representation of a Python object."""
813 def __init__(self):
814 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000815 self.maxlist = self.maxtuple = 20
816 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000817 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000818
819 def repr1(self, x, level):
820 methodname = 'repr_' + join(split(type(x).__name__), '_')
821 if hasattr(self, methodname):
822 return getattr(self, methodname)(x, level)
823 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000824 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000825
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000826 def repr_string(self, x, level):
827 test = cram(x, self.maxstring)
828 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000829 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000830 # Backslashes are only literal in the string and are never
831 # needed to make any special characters, so show a raw string.
832 return 'r' + testrepr[0] + test + testrepr[0]
833 return testrepr
834
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000835 def repr_instance(self, x, level):
836 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000837 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000838 except:
839 return '<%s instance>' % x.__class__.__name__
840
841class TextDoc(Doc):
842 """Formatter class for text documentation."""
843
844 # ------------------------------------------- text formatting utilities
845
846 _repr_instance = TextRepr()
847 repr = _repr_instance.repr
848
849 def bold(self, text):
850 """Format a string in bold by overstriking."""
851 return join(map(lambda ch: ch + '\b' + ch, text), '')
852
853 def indent(self, text, prefix=' '):
854 """Indent text by prepending a given prefix to each line."""
855 if not text: return ''
856 lines = split(text, '\n')
857 lines = map(lambda line, prefix=prefix: prefix + line, lines)
858 if lines: lines[-1] = rstrip(lines[-1])
859 return join(lines, '\n')
860
861 def section(self, title, contents):
862 """Format a section with a given heading."""
863 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
864
865 # ---------------------------------------------- type-specific routines
866
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000867 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000868 """Render in text a class tree as returned by inspect.getclasstree()."""
869 result = ''
870 for entry in tree:
871 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000872 c, bases = entry
873 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000874 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000875 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000876 result = result + '(%s)' % join(parents, ', ')
877 result = result + '\n'
878 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000879 result = result + self.formattree(
880 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000881 return result
882
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000883 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000884 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000885 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000886 synop, desc = splitdoc(getdoc(object))
887 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000888
889 try:
890 file = inspect.getabsfile(object)
891 except TypeError:
892 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000893 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000894 if desc:
895 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000896
897 classes = []
898 for key, value in inspect.getmembers(object, inspect.isclass):
899 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000900 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000901 funcs = []
902 for key, value in inspect.getmembers(object, inspect.isroutine):
903 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000904 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000905 data = []
906 for key, value in inspect.getmembers(object, isdata):
907 if key not in ['__builtins__', '__doc__']:
908 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000909
910 if hasattr(object, '__path__'):
911 modpkgs = []
912 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000913 path = os.path.join(object.__path__[0], file)
914 modname = inspect.getmodulename(file)
915 if modname and modname not in modpkgs:
916 modpkgs.append(modname)
917 elif ispackage(path):
918 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000919 modpkgs.sort()
920 result = result + self.section(
921 'PACKAGE CONTENTS', join(modpkgs, '\n'))
922
923 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000924 classlist = map(lambda (key, value): value, classes)
925 contents = [self.formattree(
926 inspect.getclasstree(classlist, 1), name)]
927 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000928 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000929 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000930
931 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000932 contents = []
933 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000934 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000935 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000936
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000937 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000938 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000939 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000940 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000941 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000942
943 if hasattr(object, '__version__'):
944 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000945 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
946 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000947 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000948 if hasattr(object, '__date__'):
949 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000950 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000951 result = result + self.section('AUTHOR', str(object.__author__))
952 if hasattr(object, '__credits__'):
953 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000954 return result
955
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000956 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000957 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000958 realname = object.__name__
959 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000960 bases = object.__bases__
961
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000962 if name == realname:
963 title = 'class ' + self.bold(realname)
964 else:
965 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000966 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000967 def makename(c, m=object.__module__): return classname(c, m)
968 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000969 title = title + '(%s)' % join(parents, ', ')
970
971 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +0000972 contents = doc and [doc + '\n'] or []
973 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000974
Tim Peters28355492001-09-23 21:29:55 +0000975 def spill(msg, attrs, predicate):
976 ok, attrs = _split_class_attrs(attrs, predicate)
977 if ok:
978 push(msg)
979 for name, kind, homecls, value in ok:
980 push(self.document(getattr(object, name),
981 name, mod, object))
982 return attrs
983
984 # pydoc can't make any reasonable sense of properties on its own,
985 # and it doesn't appear that the getter, setter and del'er methods
986 # are discoverable. For now, just pump out their names.
987 def spillproperties(msg, attrs):
988 ok, attrs = _split_class_attrs(attrs, lambda t: t[1] == 'property')
989 if ok:
990 push(msg)
991 for name, kind, homecls, value in ok:
992 push(name + '\n')
993 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +0000994
Tim Peters28355492001-09-23 21:29:55 +0000995 def spilldata(msg, attrs):
996 ok, attrs = _split_class_attrs(attrs, lambda t: t[1] == 'data')
997 if ok:
998 push(msg)
999 for name, kind, homecls, value in ok:
1000 doc = getattr(value, "__doc__", None)
1001 push(self.docother(getattr(object, name),
1002 name, mod, 70, doc) + '\n')
1003 return attrs
1004
1005 attrs = inspect.classify_class_attrs(object)
1006
1007 # All attrs defined in this class come first.
1008 attrs, inherited = _split_class_attrs(attrs,
1009 lambda t: t[2] is object)
1010 # Sort inherited attrs by name of defining class.
1011 inherited.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
1012
1013 thisclass = object
1014 while attrs or inherited:
1015 if thisclass is object:
1016 tag = "defined here"
1017 else:
1018 tag = "inherited from class %s" % classname(thisclass,
1019 object.__module__)
1020
1021 # Sort attrs by name.
1022 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1023
1024 # Pump out the attrs, segregated by kind.
1025 attrs = spill("Methods %s:\n" % tag, attrs,
1026 lambda t: t[1] == 'method')
1027 attrs = spill("Class methods %s:\n" % tag, attrs,
1028 lambda t: t[1] == 'class method')
1029 attrs = spill("Static methods %s:\n" % tag, attrs,
1030 lambda t: t[1] == 'static method')
1031 attrs = spillproperties("Properties %s:\n" % tag, attrs)
1032 attrs = spilldata("Data %s:\n" % tag, attrs)
1033 assert attrs == []
1034
1035 # Split off the attributes inherited from the next class (note
1036 # that inherited remains sorted by class name).
1037 if inherited:
1038 attrs = inherited
1039 thisclass = attrs[0][2]
1040 attrs, inherited = _split_class_attrs(attrs,
1041 lambda t: t[2] is thisclass)
1042
1043 contents = '\n'.join(contents)
1044 if not contents:
1045 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001046 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1047
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001048 def formatvalue(self, object):
1049 """Format an argument default value as text."""
1050 return '=' + self.repr(object)
1051
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001052 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001053 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001054 realname = object.__name__
1055 name = name or realname
1056 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001057 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001058 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001059 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001060 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001061 if imclass is not cl:
1062 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001063 skipdocs = 1
1064 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001065 if object.im_self:
1066 note = ' method of %s instance' % classname(
1067 object.im_self.__class__, mod)
1068 else:
1069 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001070 object = object.im_func
1071
1072 if name == realname:
1073 title = self.bold(realname)
1074 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001075 if (cl and cl.__dict__.has_key(realname) and
1076 cl.__dict__[realname] is object):
1077 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001078 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001079 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001080 args, varargs, varkw, defaults = inspect.getargspec(object)
1081 argspec = inspect.formatargspec(
1082 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001083 if realname == '<lambda>':
1084 title = 'lambda'
1085 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001086 else:
1087 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001088 decl = title + argspec + note
1089
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001090 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001091 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001092 else:
1093 doc = getdoc(object) or ''
1094 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001095
Tim Peters28355492001-09-23 21:29:55 +00001096 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001097 """Produce text documentation for a data object."""
1098 repr = self.repr(object)
1099 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001100 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001101 chop = maxlen - len(line)
1102 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001103 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001104 if doc is not None:
1105 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001106 return line
1107
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001108# --------------------------------------------------------- user interfaces
1109
1110def pager(text):
1111 """The first time this is called, determine what kind of pager to use."""
1112 global pager
1113 pager = getpager()
1114 pager(text)
1115
1116def getpager():
1117 """Decide what method to use for paging through text."""
1118 if type(sys.stdout) is not types.FileType:
1119 return plainpager
1120 if not sys.stdin.isatty() or not sys.stdout.isatty():
1121 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001122 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001123 return plainpager
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001124 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001125 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001126 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1127 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1128 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001129 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001130 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001131 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001132 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001133 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001134 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001135
1136 import tempfile
1137 filename = tempfile.mktemp()
1138 open(filename, 'w').close()
1139 try:
1140 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1141 return lambda text: pipepager(text, 'more')
1142 else:
1143 return ttypager
1144 finally:
1145 os.unlink(filename)
1146
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001147def plain(text):
1148 """Remove boldface formatting from text."""
1149 return re.sub('.\b', '', text)
1150
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001151def pipepager(text, cmd):
1152 """Page through text by feeding it to another program."""
1153 pipe = os.popen(cmd, 'w')
1154 try:
1155 pipe.write(text)
1156 pipe.close()
1157 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001158 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001159
1160def tempfilepager(text, cmd):
1161 """Page through text by invoking a program on a temporary file."""
1162 import tempfile
1163 filename = tempfile.mktemp()
1164 file = open(filename, 'w')
1165 file.write(text)
1166 file.close()
1167 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001168 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001169 finally:
1170 os.unlink(filename)
1171
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001172def ttypager(text):
1173 """Page through text on a text terminal."""
1174 lines = split(plain(text), '\n')
1175 try:
1176 import tty
1177 fd = sys.stdin.fileno()
1178 old = tty.tcgetattr(fd)
1179 tty.setcbreak(fd)
1180 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001181 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001182 tty = None
1183 getchar = lambda: sys.stdin.readline()[:-1][:1]
1184
1185 try:
1186 r = inc = os.environ.get('LINES', 25) - 1
1187 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1188 while lines[r:]:
1189 sys.stdout.write('-- more --')
1190 sys.stdout.flush()
1191 c = getchar()
1192
1193 if c in ['q', 'Q']:
1194 sys.stdout.write('\r \r')
1195 break
1196 elif c in ['\r', '\n']:
1197 sys.stdout.write('\r \r' + lines[r] + '\n')
1198 r = r + 1
1199 continue
1200 if c in ['b', 'B', '\x1b']:
1201 r = r - inc - inc
1202 if r < 0: r = 0
1203 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1204 r = r + inc
1205
1206 finally:
1207 if tty:
1208 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1209
1210def plainpager(text):
1211 """Simply print unformatted text. This is the ultimate fallback."""
1212 sys.stdout.write(plain(text))
1213
1214def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001215 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001216 if inspect.ismodule(thing):
1217 if thing.__name__ in sys.builtin_module_names:
1218 return 'built-in module ' + thing.__name__
1219 if hasattr(thing, '__path__'):
1220 return 'package ' + thing.__name__
1221 else:
1222 return 'module ' + thing.__name__
1223 if inspect.isbuiltin(thing):
1224 return 'built-in function ' + thing.__name__
1225 if inspect.isclass(thing):
1226 return 'class ' + thing.__name__
1227 if inspect.isfunction(thing):
1228 return 'function ' + thing.__name__
1229 if inspect.ismethod(thing):
1230 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001231 if type(thing) is types.InstanceType:
1232 return 'instance of ' + thing.__class__.__name__
1233 return type(thing).__name__
1234
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001235def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001236 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001237 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001238 module, n = None, 0
1239 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001240 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001241 if nextmodule: module, n = nextmodule, n + 1
1242 else: break
1243 if module:
1244 object = module
1245 for part in parts[n:]:
1246 try: object = getattr(object, part)
1247 except AttributeError: return None
1248 return object
1249 else:
1250 import __builtin__
1251 if hasattr(__builtin__, path):
1252 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001253
1254# --------------------------------------- interactive interpreter interface
1255
1256text = TextDoc()
1257html = HTMLDoc()
1258
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001259def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001260 """Display text documentation, given an object or a path to an object."""
1261 suffix, name = '', None
1262 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001263 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001264 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001265 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001266 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001267 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001268 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001269 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001270 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001271 parts = split(thing, '.')
1272 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1273 name = parts[-1]
1274 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001275
1276 desc = describe(thing)
1277 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001278 if not suffix and module and module is not thing:
1279 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001280 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001281
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001282def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001283 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001284 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001285 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001286 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001287 print value
1288 else:
1289 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001290 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001291 html.document(object, object.__name__))
1292 file = open(key + '.html', 'w')
1293 file.write(page)
1294 file.close()
1295 print 'wrote', key + '.html'
1296 else:
1297 print 'no Python documentation found for %s' % repr(key)
1298
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001299def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001300 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001301 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001302 for file in os.listdir(dir):
1303 path = os.path.join(dir, file)
1304 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001305 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001306 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001307 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001308 if modname:
1309 modname = pkgpath + modname
1310 if not done.has_key(modname):
1311 done[modname] = 1
1312 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001313
1314class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001315 keywords = {
1316 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001317 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001318 'break': ('ref/break', 'while for'),
1319 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1320 'continue': ('ref/continue', 'while for'),
1321 'def': ('ref/function', ''),
1322 'del': ('ref/del', 'BASICMETHODS'),
1323 'elif': 'if',
1324 'else': ('ref/if', 'while for'),
1325 'except': 'try',
1326 'exec': ('ref/exec', ''),
1327 'finally': 'try',
1328 'for': ('ref/for', 'break continue while'),
1329 'from': 'import',
1330 'global': ('ref/global', 'NAMESPACES'),
1331 'if': ('ref/if', 'TRUTHVALUE'),
1332 'import': ('ref/import', 'MODULES'),
1333 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1334 'is': 'COMPARISON',
1335 'lambda': ('ref/lambda', 'FUNCTIONS'),
1336 'not': 'BOOLEAN',
1337 'or': 'BOOLEAN',
1338 'pass': 'PASS',
1339 'print': ('ref/print', ''),
1340 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001341 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001342 'try': ('ref/try', 'EXCEPTIONS'),
1343 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1344 }
1345
1346 topics = {
1347 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001348 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001349 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1350 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001351 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001352 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1353 'INTEGER': ('ref/integers', 'int range'),
1354 'FLOAT': ('ref/floating', 'float math'),
1355 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001356 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001357 'MAPPINGS': 'DICTIONARIES',
1358 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1359 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1360 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001361 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001362 'FRAMEOBJECTS': 'TYPES',
1363 'TRACEBACKS': 'TYPES',
1364 'NONE': ('lib/bltin-null-object', ''),
1365 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1366 'FILES': ('lib/bltin-file-objects', ''),
1367 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1368 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1369 'MODULES': ('lib/typesmodules', 'import'),
1370 'PACKAGES': 'import',
1371 'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'),
1372 'OPERATORS': 'EXPRESSIONS',
1373 'PRECEDENCE': 'EXPRESSIONS',
1374 'OBJECTS': ('ref/objects', 'TYPES'),
1375 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001376 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1377 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1378 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1379 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1380 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1381 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1382 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001383 'EXECUTION': ('ref/execframes', ''),
1384 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1385 'SCOPING': 'NAMESPACES',
1386 'FRAMES': 'NAMESPACES',
1387 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1388 'COERCIONS': 'CONVERSIONS',
1389 'CONVERSIONS': ('ref/conversions', ''),
1390 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1391 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001392 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001393 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1394 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001395 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001396 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001397 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001398 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001399 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1400 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001401 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1402 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1403 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1404 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1405 'POWER': ('ref/power', 'EXPRESSIONS'),
1406 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1407 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1408 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1409 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1410 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001411 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001412 'ASSERTION': 'assert',
1413 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001414 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001415 'DELETION': 'del',
1416 'PRINTING': 'print',
1417 'RETURNING': 'return',
1418 'IMPORTING': 'import',
1419 'CONDITIONAL': 'if',
1420 'LOOPING': ('ref/compound', 'for while break continue'),
1421 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001422 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001423 }
1424
1425 def __init__(self, input, output):
1426 self.input = input
1427 self.output = output
1428 self.docdir = None
1429 execdir = os.path.dirname(sys.executable)
1430 homedir = os.environ.get('PYTHONHOME')
1431 for dir in [os.environ.get('PYTHONDOCS'),
1432 homedir and os.path.join(homedir, 'doc'),
1433 os.path.join(execdir, 'doc'),
1434 '/usr/doc/python-docs-' + split(sys.version)[0],
1435 '/usr/doc/python-' + split(sys.version)[0],
1436 '/usr/doc/python-docs-' + sys.version[:3],
1437 '/usr/doc/python-' + sys.version[:3]]:
1438 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1439 self.docdir = dir
1440
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001441 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001442 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001443 self()
1444 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001445 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001446
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001447 def __call__(self, request=None):
1448 if request is not None:
1449 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001450 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001451 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001452 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001453 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001454You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001455If you want to ask for help on a particular object directly from the
1456interpreter, you can type "help(object)". Executing "help('string')"
1457has the same effect as typing a particular string at the help> prompt.
1458''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001459
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001460 def interact(self):
1461 self.output.write('\n')
1462 while 1:
1463 self.output.write('help> ')
1464 self.output.flush()
1465 try:
1466 request = self.input.readline()
1467 if not request: break
1468 except KeyboardInterrupt: break
1469 request = strip(replace(request, '"', '', "'", ''))
1470 if lower(request) in ['q', 'quit']: break
1471 self.help(request)
1472
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001473 def help(self, request):
1474 if type(request) is type(''):
1475 if request == 'help': self.intro()
1476 elif request == 'keywords': self.listkeywords()
1477 elif request == 'topics': self.listtopics()
1478 elif request == 'modules': self.listmodules()
1479 elif request[:8] == 'modules ':
1480 self.listmodules(split(request)[1])
1481 elif self.keywords.has_key(request): self.showtopic(request)
1482 elif self.topics.has_key(request): self.showtopic(request)
1483 elif request: doc(request, 'Help on %s:')
1484 elif isinstance(request, Helper): self()
1485 else: doc(request, 'Help on %s:')
1486 self.output.write('\n')
1487
1488 def intro(self):
1489 self.output.write('''
1490Welcome to Python %s! This is the online help utility.
1491
1492If this is your first time using Python, you should definitely check out
1493the tutorial on the Internet at http://www.python.org/doc/tut/.
1494
1495Enter the name of any module, keyword, or topic to get help on writing
1496Python programs and using Python modules. To quit this help utility and
1497return to the interpreter, just type "quit".
1498
1499To get a list of available modules, keywords, or topics, type "modules",
1500"keywords", or "topics". Each module also comes with a one-line summary
1501of what it does; to list the modules whose summaries contain a given word
1502such as "spam", type "modules spam".
1503''' % sys.version[:3])
1504
1505 def list(self, items, columns=4, width=80):
1506 items = items[:]
1507 items.sort()
1508 colw = width / columns
1509 rows = (len(items) + columns - 1) / columns
1510 for row in range(rows):
1511 for col in range(columns):
1512 i = col * rows + row
1513 if i < len(items):
1514 self.output.write(items[i])
1515 if col < columns - 1:
1516 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1517 self.output.write('\n')
1518
1519 def listkeywords(self):
1520 self.output.write('''
1521Here is a list of the Python keywords. Enter any keyword to get more help.
1522
1523''')
1524 self.list(self.keywords.keys())
1525
1526 def listtopics(self):
1527 self.output.write('''
1528Here is a list of available topics. Enter any topic name to get more help.
1529
1530''')
1531 self.list(self.topics.keys())
1532
1533 def showtopic(self, topic):
1534 if not self.docdir:
1535 self.output.write('''
1536Sorry, topic and keyword documentation is not available because the Python
1537HTML documentation files could not be found. If you have installed them,
1538please set the environment variable PYTHONDOCS to indicate their location.
1539''')
1540 return
1541 target = self.topics.get(topic, self.keywords.get(topic))
1542 if not target:
1543 self.output.write('no documentation found for %s\n' % repr(topic))
1544 return
1545 if type(target) is type(''):
1546 return self.showtopic(target)
1547
1548 filename, xrefs = target
1549 filename = self.docdir + '/' + filename + '.html'
1550 try:
1551 file = open(filename)
1552 except:
1553 self.output.write('could not read docs from %s\n' % filename)
1554 return
1555
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001556 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1557 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001558 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1559 file.close()
1560
1561 import htmllib, formatter, StringIO
1562 buffer = StringIO.StringIO()
1563 parser = htmllib.HTMLParser(
1564 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1565 parser.start_table = parser.do_p
1566 parser.end_table = lambda parser=parser: parser.do_p({})
1567 parser.start_tr = parser.do_br
1568 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1569 parser.feed(document)
1570 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1571 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001572 if xrefs:
1573 buffer = StringIO.StringIO()
1574 formatter.DumbWriter(buffer).send_flowing_data(
1575 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1576 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001577
1578 def listmodules(self, key=''):
1579 if key:
1580 self.output.write('''
1581Here is a list of matching modules. Enter any module name to get more help.
1582
1583''')
1584 apropos(key)
1585 else:
1586 self.output.write('''
1587Please wait a moment while I gather a list of all available modules...
1588
1589''')
1590 modules = {}
1591 def callback(path, modname, desc, modules=modules):
1592 if modname and modname[-9:] == '.__init__':
1593 modname = modname[:-9] + ' (package)'
1594 if find(modname, '.') < 0:
1595 modules[modname] = 1
1596 ModuleScanner().run(callback)
1597 self.list(modules.keys())
1598 self.output.write('''
1599Enter any module name to get more help. Or, type "modules spam" to search
1600for modules whose descriptions contain the word "spam".
1601''')
1602
1603help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001604
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001605class Scanner:
1606 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001607 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001608 self.roots = roots[:]
1609 self.state = []
1610 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001611 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001612
1613 def next(self):
1614 if not self.state:
1615 if not self.roots:
1616 return None
1617 root = self.roots.pop(0)
1618 self.state = [(root, self.children(root))]
1619 node, children = self.state[-1]
1620 if not children:
1621 self.state.pop()
1622 return self.next()
1623 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001624 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001625 self.state.append((child, self.children(child)))
1626 return child
1627
1628class ModuleScanner(Scanner):
1629 """An interruptible scanner that searches module synopses."""
1630 def __init__(self):
1631 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001632 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1633 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001634
1635 def submodules(self, (dir, package)):
1636 children = []
1637 for file in os.listdir(dir):
1638 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001639 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001640 children.append((path, package + (package and '.') + file))
1641 else:
1642 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001643 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001644 return children
1645
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001646 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001647 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001648 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001649 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001650 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001651
Ka-Ping Yee66246962001-04-12 11:59:50 +00001652 def run(self, callback, key=None, completer=None):
1653 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001654 self.quit = 0
1655 seen = {}
1656
1657 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001658 if modname != '__main__':
1659 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001660 if key is None:
1661 callback(None, modname, '')
1662 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001663 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001664 if find(lower(modname + ' - ' + desc), key) >= 0:
1665 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001666
1667 while not self.quit:
1668 node = self.next()
1669 if not node: break
1670 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001671 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001672 if os.path.isfile(path) and modname:
1673 modname = package + (package and '.') + modname
1674 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001675 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001676 if key is None:
1677 callback(path, modname, '')
1678 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001679 desc = synopsis(path) or ''
1680 if find(lower(modname + ' - ' + desc), key) >= 0:
1681 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001682 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001683
1684def apropos(key):
1685 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001686 def callback(path, modname, desc):
1687 if modname[-9:] == '.__init__':
1688 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001689 print modname, desc and '- ' + desc
1690 try: import warnings
1691 except ImportError: pass
1692 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001693 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001694
1695# --------------------------------------------------- web browser interface
1696
Ka-Ping Yee66246962001-04-12 11:59:50 +00001697def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001698 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001699
1700 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1701 class Message(mimetools.Message):
1702 def __init__(self, fp, seekable=1):
1703 Message = self.__class__
1704 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1705 self.encodingheader = self.getheader('content-transfer-encoding')
1706 self.typeheader = self.getheader('content-type')
1707 self.parsetype()
1708 self.parseplist()
1709
1710 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1711 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001712 try:
1713 self.send_response(200)
1714 self.send_header('Content-Type', 'text/html')
1715 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001716 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001717 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001718
1719 def do_GET(self):
1720 path = self.path
1721 if path[-5:] == '.html': path = path[:-5]
1722 if path[:1] == '/': path = path[1:]
1723 if path and path != '.':
1724 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001725 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001726 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001727 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001728 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001729 if obj:
1730 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001731 else:
1732 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001733'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001734 else:
1735 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001736'<big><big><strong>Python: Index of Modules</strong></big></big>',
1737'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001738 def bltinlink(name):
1739 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001740 names = filter(lambda x: x != '__main__',
1741 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001742 contents = html.multicolumn(names, bltinlink)
1743 indices = ['<p>' + html.bigsection(
1744 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1745
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001746 seen = {}
1747 for dir in pathdirs():
1748 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001749 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001750<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001751pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001752 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001753
1754 def log_message(self, *args): pass
1755
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001756 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001757 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001758 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001759 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001760 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001761 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001762 self.base.__init__(self, self.address, self.handler)
1763
1764 def serve_until_quit(self):
1765 import select
1766 self.quit = 0
1767 while not self.quit:
1768 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1769 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001770
1771 def server_activate(self):
1772 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001773 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001774
1775 DocServer.base = BaseHTTPServer.HTTPServer
1776 DocServer.handler = DocHandler
1777 DocHandler.MessageClass = Message
1778 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001779 try:
1780 DocServer(port, callback).serve_until_quit()
1781 except (KeyboardInterrupt, select.error):
1782 pass
1783 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001784 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001785
1786# ----------------------------------------------------- graphical interface
1787
1788def gui():
1789 """Graphical interface (starts web server and pops up a control window)."""
1790 class GUI:
1791 def __init__(self, window, port=7464):
1792 self.window = window
1793 self.server = None
1794 self.scanner = None
1795
1796 import Tkinter
1797 self.server_frm = Tkinter.Frame(window)
1798 self.title_lbl = Tkinter.Label(self.server_frm,
1799 text='Starting server...\n ')
1800 self.open_btn = Tkinter.Button(self.server_frm,
1801 text='open browser', command=self.open, state='disabled')
1802 self.quit_btn = Tkinter.Button(self.server_frm,
1803 text='quit serving', command=self.quit, state='disabled')
1804
1805 self.search_frm = Tkinter.Frame(window)
1806 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1807 self.search_ent = Tkinter.Entry(self.search_frm)
1808 self.search_ent.bind('<Return>', self.search)
1809 self.stop_btn = Tkinter.Button(self.search_frm,
1810 text='stop', pady=0, command=self.stop, state='disabled')
1811 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001812 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001813 self.stop_btn.pack(side='right')
1814
1815 self.window.title('pydoc')
1816 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1817 self.title_lbl.pack(side='top', fill='x')
1818 self.open_btn.pack(side='left', fill='x', expand=1)
1819 self.quit_btn.pack(side='right', fill='x', expand=1)
1820 self.server_frm.pack(side='top', fill='x')
1821
1822 self.search_lbl.pack(side='left')
1823 self.search_ent.pack(side='right', fill='x', expand=1)
1824 self.search_frm.pack(side='top', fill='x')
1825 self.search_ent.focus_set()
1826
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001827 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001828 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001829 self.result_lst.bind('<Button-1>', self.select)
1830 self.result_lst.bind('<Double-Button-1>', self.goto)
1831 self.result_scr = Tkinter.Scrollbar(window,
1832 orient='vertical', command=self.result_lst.yview)
1833 self.result_lst.config(yscrollcommand=self.result_scr.set)
1834
1835 self.result_frm = Tkinter.Frame(window)
1836 self.goto_btn = Tkinter.Button(self.result_frm,
1837 text='go to selected', command=self.goto)
1838 self.hide_btn = Tkinter.Button(self.result_frm,
1839 text='hide results', command=self.hide)
1840 self.goto_btn.pack(side='left', fill='x', expand=1)
1841 self.hide_btn.pack(side='right', fill='x', expand=1)
1842
1843 self.window.update()
1844 self.minwidth = self.window.winfo_width()
1845 self.minheight = self.window.winfo_height()
1846 self.bigminheight = (self.server_frm.winfo_reqheight() +
1847 self.search_frm.winfo_reqheight() +
1848 self.result_lst.winfo_reqheight() +
1849 self.result_frm.winfo_reqheight())
1850 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1851 self.expanded = 0
1852 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1853 self.window.wm_minsize(self.minwidth, self.minheight)
1854
1855 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001856 threading.Thread(
1857 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001858
1859 def ready(self, server):
1860 self.server = server
1861 self.title_lbl.config(
1862 text='Python documentation server at\n' + server.url)
1863 self.open_btn.config(state='normal')
1864 self.quit_btn.config(state='normal')
1865
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001866 def open(self, event=None, url=None):
1867 url = url or self.server.url
1868 try:
1869 import webbrowser
1870 webbrowser.open(url)
1871 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001872 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001873 os.system('start "%s"' % url)
1874 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001875 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001876 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001877 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001878 else:
1879 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1880 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001881
1882 def quit(self, event=None):
1883 if self.server:
1884 self.server.quit = 1
1885 self.window.quit()
1886
1887 def search(self, event=None):
1888 key = self.search_ent.get()
1889 self.stop_btn.pack(side='right')
1890 self.stop_btn.config(state='normal')
1891 self.search_lbl.config(text='Searching for "%s"...' % key)
1892 self.search_ent.forget()
1893 self.search_lbl.pack(side='left')
1894 self.result_lst.delete(0, 'end')
1895 self.goto_btn.config(state='disabled')
1896 self.expand()
1897
1898 import threading
1899 if self.scanner:
1900 self.scanner.quit = 1
1901 self.scanner = ModuleScanner()
1902 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001903 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001904
1905 def update(self, path, modname, desc):
1906 if modname[-9:] == '.__init__':
1907 modname = modname[:-9] + ' (package)'
1908 self.result_lst.insert('end',
1909 modname + ' - ' + (desc or '(no description)'))
1910
1911 def stop(self, event=None):
1912 if self.scanner:
1913 self.scanner.quit = 1
1914 self.scanner = None
1915
1916 def done(self):
1917 self.scanner = None
1918 self.search_lbl.config(text='Search for')
1919 self.search_lbl.pack(side='left')
1920 self.search_ent.pack(side='right', fill='x', expand=1)
1921 if sys.platform != 'win32': self.stop_btn.forget()
1922 self.stop_btn.config(state='disabled')
1923
1924 def select(self, event=None):
1925 self.goto_btn.config(state='normal')
1926
1927 def goto(self, event=None):
1928 selection = self.result_lst.curselection()
1929 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001930 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001931 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001932
1933 def collapse(self):
1934 if not self.expanded: return
1935 self.result_frm.forget()
1936 self.result_scr.forget()
1937 self.result_lst.forget()
1938 self.bigwidth = self.window.winfo_width()
1939 self.bigheight = self.window.winfo_height()
1940 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1941 self.window.wm_minsize(self.minwidth, self.minheight)
1942 self.expanded = 0
1943
1944 def expand(self):
1945 if self.expanded: return
1946 self.result_frm.pack(side='bottom', fill='x')
1947 self.result_scr.pack(side='right', fill='y')
1948 self.result_lst.pack(side='top', fill='both', expand=1)
1949 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1950 self.window.wm_minsize(self.minwidth, self.bigminheight)
1951 self.expanded = 1
1952
1953 def hide(self, event=None):
1954 self.stop()
1955 self.collapse()
1956
1957 import Tkinter
1958 try:
1959 gui = GUI(Tkinter.Tk())
1960 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001961 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001962 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001963
1964# -------------------------------------------------- command-line interface
1965
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001966def ispath(x):
1967 return type(x) is types.StringType and find(x, os.sep) >= 0
1968
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001969def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001970 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001971 import getopt
1972 class BadUsage: pass
1973
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001974 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001975 scriptdir = os.path.dirname(sys.argv[0])
1976 if scriptdir in sys.path:
1977 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001978 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001979
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001980 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001981 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001982 writing = 0
1983
1984 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001985 if opt == '-g':
1986 gui()
1987 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001988 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001989 apropos(val)
1990 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001991 if opt == '-p':
1992 try:
1993 port = int(val)
1994 except ValueError:
1995 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001996 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001997 print 'pydoc server ready at %s' % server.url
1998 def stopped():
1999 print 'pydoc server stopped'
2000 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002001 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002002 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002003 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002004
2005 if not args: raise BadUsage
2006 for arg in args:
2007 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002008 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002009 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002010 if writing:
2011 if ispath(arg) and os.path.isdir(arg):
2012 writedocs(arg)
2013 else:
2014 writedoc(arg)
2015 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002016 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002017 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002018 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002019
2020 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002021 cmd = sys.argv[0]
2022 print """pydoc - the Python documentation tool
2023
2024%s <name> ...
2025 Show text documentation on something. <name> may be the name of a
2026 function, module, or package, or a dotted reference to a class or
2027 function within a module or module in a package. If <name> contains
2028 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002029
2030%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002031 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002032
2033%s -p <port>
2034 Start an HTTP server on the given port on the local machine.
2035
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002036%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002037 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002038
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002039%s -w <name> ...
2040 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002041 directory. If <name> contains a '%s', it is treated as a filename; if
2042 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002043""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002044
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002045if __name__ == '__main__': cli()