2 # -*- coding: Latin-1 -*-
3 """Generate Python documentation in HTML or text for interactive use.
5 In the Python interpreter, do "from pydoc import help" to provide online
6 help. Calling help(thing) on a Python object documents the object.
8 Or, at the shell command line outside of Python:
10 Run "pydoc <name>" to show documentation on something. <name> may be
11 the name of a function, module, package, or a dotted reference to a
12 class or function within a module or module in a package. If the
13 argument contains a path segment delimiter (e.g. slash on Unix,
14 backslash on Windows) it is treated as the path to a Python source file.
16 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
17 of all available modules.
19 Run "pydoc -p <port>" to start an HTTP server on a given port on the
20 local machine to generate documentation web pages.
22 For platforms without a command line, "pydoc -g" starts the HTTP server
23 and also pops up a little window for controlling it.
25 Run "pydoc -w <name>" to write out the HTML documentation for a module
26 to a file named "<name>.html".
28 Module docs for core modules are assumed to be in
30 http://www.python.org/doc/current/lib/
32 This can be overridden by setting the PYTHONDOCS environment variable
33 to a different URL or to a local directory containing the Library
34 Reference Manual pages.
37 __author__
= "Ka-Ping Yee <ping@lfw.org>"
38 __date__
= "26 February 2001"
40 __version__
= "$Revision$"
41 __credits__
= """Guido van Rossum, for an excellent programming language.
42 Tommy Burnette, the original creator of manpy.
43 Paul Prescod, for all his work on onlinehelp.
44 Richard Chamberlain, for the first implementation of textdoc.
47 # Known bugs that can't be fixed here:
48 # - imp.load_module() cannot be prevented from clobbering existing
49 # loaded modules, so calling synopsis() on a binary module file
50 # changes the contents of any existing module with the same name.
51 # - If the __file__ attribute on a module is a relative path and
52 # the current directory is changed with os.chdir(), an incorrect
53 # path will be displayed.
55 import sys
, imp
, os
, re
, types
, inspect
, __builtin__
57 from string
import expandtabs
, find
, join
, lower
, split
, strip
, rfind
, rstrip
58 from collections
import deque
60 # --------------------------------------------------------- common routines
63 """Convert sys.path into a list of absolute, existing, unique paths."""
67 dir = os
.path
.abspath(dir or '.')
68 normdir
= os
.path
.normcase(dir)
69 if normdir
not in normdirs
and os
.path
.isdir(dir):
71 normdirs
.append(normdir
)
75 """Get the doc string or comments for an object."""
76 result
= inspect
.getdoc(object) or inspect
.getcomments(object)
77 return result
and re
.sub('^ *\n', '', rstrip(result
)) or ''
80 """Split a doc string into a synopsis line (if any) and the rest."""
81 lines
= split(strip(doc
), '\n')
84 elif len(lines
) >= 2 and not rstrip(lines
[1]):
85 return lines
[0], join(lines
[2:], '\n')
86 return '', join(lines
, '\n')
88 def classname(object, modname
):
89 """Get a class name and qualify it with a module name if necessary."""
90 name
= object.__name
__
91 if object.__module
__ != modname
:
92 name
= object.__module
__ + '.' + name
96 """Check if an object is of a type that probably means it's data."""
97 return not (inspect
.ismodule(object) or inspect
.isclass(object) or
98 inspect
.isroutine(object) or inspect
.isframe(object) or
99 inspect
.istraceback(object) or inspect
.iscode(object))
101 def replace(text
, *pairs
):
102 """Do a series of global replacements on a string."""
104 text
= join(split(text
, pairs
[0]), pairs
[1])
108 def cram(text
, maxlen
):
109 """Omit part of a string if needed to make it fit in a maximum length."""
110 if len(text
) > maxlen
:
111 pre
= max(0, (maxlen
-3)//2)
112 post
= max(0, maxlen
-3-pre
)
113 return text
[:pre
] + '...' + text
[len(text
)-post
:]
116 _re_stripid
= re
.compile(r
' at 0x[0-9a-f]{6,16}(>+)$', re
.IGNORECASE
)
118 """Remove the hexadecimal id from a Python object representation."""
119 # The behaviour of %p is implementation-dependent in terms of case.
120 if _re_stripid
.search(repr(Exception)):
121 return _re_stripid
.sub(r
'\1', text
)
124 def _is_some_method(obj
):
125 return inspect
.ismethod(obj
) or inspect
.ismethoddescriptor(obj
)
129 for key
, value
in inspect
.getmembers(cl
, _is_some_method
):
131 for base
in cl
.__bases
__:
132 methods
.update(allmethods(base
)) # all your base are belong to us
133 for key
in methods
.keys():
134 methods
[key
] = getattr(cl
, key
)
137 def _split_list(s
, predicate
):
138 """Split sequence s via predicate, and return pair ([true], [false]).
140 The return value is a 2-tuple of lists,
141 ([x for x in s if predicate(x)],
142 [x for x in s if not predicate(x)])
154 def visiblename(name
, all
=None):
155 """Decide whether to show documentation on a variable."""
156 # Certain special names are redundant.
157 if name
in ('__builtins__', '__doc__', '__file__', '__path__',
158 '__module__', '__name__', '__slots__'): return 0
159 # Private names are hidden, but special names are displayed.
160 if name
.startswith('__') and name
.endswith('__'): return 1
162 # only document that which the programmer exported in __all__
165 return not name
.startswith('_')
167 def classify_class_attrs(object):
168 """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
169 def fixup((name
, kind
, cls
, value
)):
170 if inspect
.isdatadescriptor(value
):
171 kind
= 'data descriptor'
172 return name
, kind
, cls
, value
173 return map(fixup
, inspect
.classify_class_attrs(object))
175 # ----------------------------------------------------- module manipulation
178 """Guess whether a path refers to a package directory."""
179 if os
.path
.isdir(path
):
180 for ext
in ('.py', '.pyc', '.pyo'):
181 if os
.path
.isfile(os
.path
.join(path
, '__init__' + ext
)):
185 def synopsis(filename
, cache
={}):
186 """Get the one-line summary out of a module file."""
187 mtime
= os
.stat(filename
).st_mtime
188 lastupdate
, result
= cache
.get(filename
, (0, None))
189 if lastupdate
< mtime
:
190 info
= inspect
.getmoduleinfo(filename
)
191 file = open(filename
)
192 if info
and 'b' in info
[2]: # binary modules have to be imported
193 try: module
= imp
.load_module('__temp__', file, filename
, info
[1:])
195 result
= split(module
.__doc
__ or '', '\n')[0]
196 del sys
.modules
['__temp__']
197 else: # text modules can be directly examined
198 line
= file.readline()
199 while line
[:1] == '#' or not strip(line
):
200 line
= file.readline()
203 if line
[:4] == 'r"""': line
= line
[1:]
204 if line
[:3] == '"""':
206 if line
[-1:] == '\\': line
= line
[:-1]
207 while not strip(line
):
208 line
= file.readline()
210 result
= strip(split(line
, '"""')[0])
213 cache
[filename
] = (mtime
, result
)
216 class ErrorDuringImport(Exception):
217 """Errors that occurred while trying to import something to document it."""
218 def __init__(self
, filename
, (exc
, value
, tb
)):
219 self
.filename
= filename
226 if type(exc
) is types
.ClassType
:
228 return 'problem in %s - %s: %s' % (self
.filename
, exc
, self
.value
)
230 def importfile(path
):
231 """Import a Python source file or compiled file given its path."""
232 magic
= imp
.get_magic()
233 file = open(path
, 'r')
234 if file.read(len(magic
)) == magic
:
235 kind
= imp
.PY_COMPILED
239 filename
= os
.path
.basename(path
)
240 name
, ext
= os
.path
.splitext(filename
)
241 file = open(path
, 'r')
243 module
= imp
.load_module(name
, file, path
, (ext
, 'r', kind
))
245 raise ErrorDuringImport(path
, sys
.exc_info())
249 def safeimport(path
, forceload
=0, cache
={}):
250 """Import a module; handle errors; return None if the module isn't found.
252 If the module *is* found but an exception occurs, it's wrapped in an
253 ErrorDuringImport exception and reraised. Unlike __import__, if a
254 package path is specified, the module at the end of the path is returned,
255 not the package at the beginning. If the optional 'forceload' argument
256 is 1, we reload the module from disk (unless it's a dynamic extension)."""
258 # If forceload is 1 and the module has been previously loaded from
259 # disk, we always have to reload the module. Checking the file's
260 # mtime isn't good enough (e.g. the module could contain a class
261 # that inherits from another module that has changed).
262 if forceload
and path
in sys
.modules
:
263 if path
not in sys
.builtin_module_names
:
264 # Avoid simply calling reload() because it leaves names in
265 # the currently loaded module lying around if they're not
266 # defined in the new source file. Instead, remove the
267 # module from sys.modules and re-import. Also remove any
268 # submodules because they won't appear in the newly loaded
269 # module's namespace if they're already in sys.modules.
270 subs
= [m
for m
in sys
.modules
if m
.startswith(path
+ '.')]
271 for key
in [path
] + subs
:
272 # Prevent garbage collection.
273 cache
[key
] = sys
.modules
[key
]
275 module
= __import__(path
)
277 # Did the error occur before or after the module was found?
278 (exc
, value
, tb
) = info
= sys
.exc_info()
279 if path
in sys
.modules
:
280 # An error occurred while executing the imported module.
281 raise ErrorDuringImport(sys
.modules
[path
].__file
__, info
)
282 elif exc
is SyntaxError:
283 # A SyntaxError occurred before we could execute the module.
284 raise ErrorDuringImport(value
.filename
, info
)
285 elif exc
is ImportError and \
286 split(lower(str(value
)))[:2] == ['no', 'module']:
287 # The module was not found.
290 # Some other error occurred during the importing process.
291 raise ErrorDuringImport(path
, sys
.exc_info())
292 for part
in split(path
, '.')[1:]:
293 try: module
= getattr(module
, part
)
294 except AttributeError: return None
297 # ---------------------------------------------------- formatter base class
300 def document(self
, object, name
=None, *args
):
301 """Generate documentation for an object."""
302 args
= (object, name
) + args
303 # 'try' clause is to attempt to handle the possibility that inspect
304 # identifies something in a way that pydoc itself has issues handling;
305 # think 'super' and how it is a descriptor (which raises the exception
306 # by lacking a __name__ attribute) and an instance.
308 if inspect
.ismodule(object): return self
.docmodule(*args
)
309 if inspect
.isclass(object): return self
.docclass(*args
)
310 if inspect
.isroutine(object): return self
.docroutine(*args
)
311 except AttributeError:
313 if isinstance(object, property): return self
.docproperty(*args
)
314 return self
.docother(*args
)
316 def fail(self
, object, name
=None, *args
):
317 """Raise an exception for unimplemented types."""
318 message
= "don't know how to document object%s of type %s" % (
319 name
and ' ' + repr(name
), type(object).__name
__)
320 raise TypeError, message
322 docmodule
= docclass
= docroutine
= docother
= fail
324 def getdocloc(self
, object):
325 """Return the location of module docs or None"""
328 file = inspect
.getabsfile(object)
332 docloc
= os
.environ
.get("PYTHONDOCS",
333 "http://www.python.org/doc/current/lib")
334 basedir
= os
.path
.join(sys
.exec_prefix
, "lib",
335 "python"+sys
.version
[0:3])
336 if (isinstance(object, type(os
)) and
337 (object.__name
__ in ('errno', 'exceptions', 'gc', 'imp',
338 'marshal', 'posix', 'signal', 'sys',
339 'thread', 'zipimport') or
340 (file.startswith(basedir
) and
341 not file.startswith(os
.path
.join(basedir
, 'site-packages'))))):
342 htmlfile
= "module-%s.html" % object.__name
__
343 if docloc
.startswith("http://"):
344 docloc
= "%s/%s" % (docloc
.rstrip("/"), htmlfile
)
346 docloc
= os
.path
.join(docloc
, htmlfile
)
351 # -------------------------------------------- HTML documentation generator
353 class HTMLRepr(Repr
):
354 """Class for safely making an HTML representation of a Python object."""
357 self
.maxlist
= self
.maxtuple
= 20
359 self
.maxstring
= self
.maxother
= 100
361 def escape(self
, text
):
362 return replace(text
, '&', '&', '<', '<', '>', '>')
364 def repr(self
, object):
365 return Repr
.repr(self
, object)
367 def repr1(self
, x
, level
):
368 if hasattr(type(x
), '__name__'):
369 methodname
= 'repr_' + join(split(type(x
).__name
__), '_')
370 if hasattr(self
, methodname
):
371 return getattr(self
, methodname
)(x
, level
)
372 return self
.escape(cram(stripid(repr(x
)), self
.maxother
))
374 def repr_string(self
, x
, level
):
375 test
= cram(x
, self
.maxstring
)
376 testrepr
= repr(test
)
377 if '\\' in test
and '\\' not in replace(testrepr
, r
'\\', ''):
378 # Backslashes are only literal in the string and are never
379 # needed to make any special characters, so show a raw string.
380 return 'r' + testrepr
[0] + self
.escape(test
) + testrepr
[0]
381 return re
.sub(r
'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
382 r'<font color="#c040c0">\1</font>',
383 self
.escape(testrepr
))
385 repr_str
= repr_string
387 def repr_instance(self
, x
, level
):
389 return self
.escape(cram(stripid(repr(x
)), self
.maxstring
))
391 return self
.escape('<%s instance>' % x
.__class
__.__name
__)
393 repr_unicode
= repr_string
396 """Formatter class for HTML documentation."""
398 # ------------------------------------------- HTML formatting utilities
400 _repr_instance
= HTMLRepr()
401 repr = _repr_instance
.repr
402 escape
= _repr_instance
.escape
404 def page(self
, title
, contents
):
405 """Format an HTML page."""
407 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
408 <html><head><title>Python: %s</title>
409 </head><body bgcolor="#f0f0f8">
411 </body></html>''' % (title
, contents
)
413 def heading(self
, title
, fgcol
, bgcol
, extras
=''):
414 """Format a page heading."""
416 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
418 <td valign=bottom> <br>
419 <font color="%s" face="helvetica, arial"> <br>%s</font></td
420 ><td align=right valign=bottom
421 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
422 ''' % (bgcol
, fgcol
, title
, fgcol
, extras
or ' ')
424 def section(self
, title
, fgcol
, bgcol
, contents
, width
=6,
425 prelude
='', marginalia
=None, gap
=' '):
426 """Format a section with a heading."""
427 if marginalia
is None:
428 marginalia
= '<tt>' + ' ' * width
+ '</tt>'
430 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
432 <td colspan=3 valign=bottom> <br>
433 <font color="%s" face="helvetica, arial">%s</font></td></tr>
434 ''' % (bgcol
, fgcol
, title
)
436 result
= result
+ '''
437 <tr bgcolor="%s"><td rowspan=2>%s</td>
438 <td colspan=2>%s</td></tr>
439 <tr><td>%s</td>''' % (bgcol
, marginalia
, prelude
, gap
)
441 result
= result
+ '''
442 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol
, marginalia
, gap
)
444 return result
+ '\n<td width="100%%">%s</td></tr></table>' % contents
446 def bigsection(self
, title
, *args
):
447 """Format a section with a big heading."""
448 title
= '<big><strong>%s</strong></big>' % title
449 return self
.section(title
, *args
)
451 def preformat(self
, text
):
452 """Format literal preformatted text."""
453 text
= self
.escape(expandtabs(text
))
454 return replace(text
, '\n\n', '\n \n', '\n\n', '\n \n',
455 ' ', ' ', '\n', '<br>\n')
457 def multicolumn(self
, list, format
, cols
=4):
458 """Format a list of items into a multi-column list."""
460 rows
= (len(list)+cols
-1)/cols
461 for col
in range(cols
):
462 result
= result
+ '<td width="%d%%" valign=top>' % (100/cols
)
463 for i
in range(rows
*col
, rows
*col
+rows
):
465 result
= result
+ format(list[i
]) + '<br>\n'
466 result
= result
+ '</td>'
467 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
469 def grey(self
, text
): return '<font color="#909090">%s</font>' % text
471 def namelink(self
, name
, *dicts
):
472 """Make a link for an identifier, given name-to-URL mappings."""
475 return '<a href="%s">%s</a>' % (dict[name
], name
)
478 def classlink(self
, object, modname
):
479 """Make a link for a class."""
480 name
, module
= object.__name
__, sys
.modules
.get(object.__module
__)
481 if hasattr(module
, name
) and getattr(module
, name
) is object:
482 return '<a href="%s.html#%s">%s</a>' % (
483 module
.__name
__, name
, classname(object, modname
))
484 return classname(object, modname
)
486 def modulelink(self
, object):
487 """Make a link for a module."""
488 return '<a href="%s.html">%s</a>' % (object.__name
__, object.__name
__)
490 def modpkglink(self
, (name
, path
, ispackage
, shadowed
)):
491 """Make a link for a module or package to display in an index."""
493 return self
.grey(name
)
495 url
= '%s.%s.html' % (path
, name
)
497 url
= '%s.html' % name
499 text
= '<strong>%s</strong> (package)' % name
502 return '<a href="%s">%s</a>' % (url
, text
)
504 def markup(self
, text
, escape
=None, funcs
={}, classes
={}, methods
={}):
505 """Mark up some plain text, given a context of symbols to look for.
506 Each context dictionary maps object names to anchor names."""
507 escape
= escape
or self
.escape
510 pattern
= re
.compile(r
'\b((http|ftp)://\S+[\w/]|'
515 match
= pattern
.search(text
, here
)
517 start
, end
= match
.span()
518 results
.append(escape(text
[here
:start
]))
520 all
, scheme
, rfc
, pep
, selfdot
, name
= match
.groups()
522 url
= escape(all
).replace('"', '"')
523 results
.append('<a href="%s">%s</a>' % (url
, url
))
525 url
= 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc
)
526 results
.append('<a href="%s">%s</a>' % (url
, escape(all
)))
528 url
= 'http://www.python.org/peps/pep-%04d.html' % int(pep
)
529 results
.append('<a href="%s">%s</a>' % (url
, escape(all
)))
530 elif text
[end
:end
+1] == '(':
531 results
.append(self
.namelink(name
, methods
, funcs
, classes
))
533 results
.append('self.<strong>%s</strong>' % name
)
535 results
.append(self
.namelink(name
, classes
))
537 results
.append(escape(text
[here
:]))
538 return join(results
, '')
540 # ---------------------------------------------- type-specific routines
542 def formattree(self
, tree
, modname
, parent
=None):
543 """Produce HTML for a class tree as given by inspect.getclasstree()."""
546 if type(entry
) is type(()):
548 result
= result
+ '<dt><font face="helvetica, arial">'
549 result
= result
+ self
.classlink(c
, modname
)
550 if bases
and bases
!= (parent
,):
553 parents
.append(self
.classlink(base
, modname
))
554 result
= result
+ '(' + join(parents
, ', ') + ')'
555 result
= result
+ '\n</font></dt>'
556 elif type(entry
) is type([]):
557 result
= result
+ '<dd>\n%s</dd>\n' % self
.formattree(
559 return '<dl>\n%s</dl>\n' % result
561 def docmodule(self
, object, name
=None, mod
=None, *ignored
):
562 """Produce HTML documentation for a module object."""
563 name
= object.__name
__ # ignore the passed-in name
566 except AttributeError:
568 parts
= split(name
, '.')
570 for i
in range(len(parts
)-1):
572 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
573 (join(parts
[:i
+1], '.'), parts
[i
]))
574 linkedname
= join(links
+ parts
[-1:], '.')
575 head
= '<big><big><strong>%s</strong></big></big>' % linkedname
577 path
= inspect
.getabsfile(object)
579 if sys
.platform
== 'win32':
581 url
= nturl2path
.pathname2url(path
)
582 filelink
= '<a href="file:%s">%s</a>' % (url
, path
)
584 filelink
= '(built-in)'
586 if hasattr(object, '__version__'):
587 version
= str(object.__version
__)
588 if version
[:11] == '$' + 'Revision: ' and version
[-1:] == '$':
589 version
= strip(version
[11:-1])
590 info
.append('version %s' % self
.escape(version
))
591 if hasattr(object, '__date__'):
592 info
.append(self
.escape(str(object.__date
__)))
594 head
= head
+ ' (%s)' % join(info
, ', ')
595 docloc
= self
.getdocloc(object)
596 if docloc
is not None:
597 docloc
= '<br><a href="%(docloc)s">Module Docs</a>' % locals()
600 result
= self
.heading(
601 head
, '#ffffff', '#7799ee',
602 '<a href=".">index</a><br>' + filelink
+ docloc
)
604 modules
= inspect
.getmembers(object, inspect
.ismodule
)
606 classes
, cdict
= [], {}
607 for key
, value
in inspect
.getmembers(object, inspect
.isclass
):
608 # if __all__ exists, believe it. Otherwise use old heuristic.
609 if (all
is not None or
610 (inspect
.getmodule(value
) or object) is object):
611 if visiblename(key
, all
):
612 classes
.append((key
, value
))
613 cdict
[key
] = cdict
[value
] = '#' + key
614 for key
, value
in classes
:
615 for base
in value
.__bases
__:
616 key
, modname
= base
.__name
__, base
.__module
__
617 module
= sys
.modules
.get(modname
)
618 if modname
!= name
and module
and hasattr(module
, key
):
619 if getattr(module
, key
) is base
:
621 cdict
[key
] = cdict
[base
] = modname
+ '.html#' + key
622 funcs
, fdict
= [], {}
623 for key
, value
in inspect
.getmembers(object, inspect
.isroutine
):
624 # if __all__ exists, believe it. Otherwise use old heuristic.
625 if (all
is not None or
626 inspect
.isbuiltin(value
) or inspect
.getmodule(value
) is object):
627 if visiblename(key
, all
):
628 funcs
.append((key
, value
))
629 fdict
[key
] = '#-' + key
630 if inspect
.isfunction(value
): fdict
[value
] = fdict
[key
]
632 for key
, value
in inspect
.getmembers(object, isdata
):
633 if visiblename(key
, all
):
634 data
.append((key
, value
))
636 doc
= self
.markup(getdoc(object), self
.preformat
, fdict
, cdict
)
637 doc
= doc
and '<tt>%s</tt>' % doc
638 result
= result
+ '<p>%s</p>\n' % doc
640 if hasattr(object, '__path__'):
643 for file in os
.listdir(object.__path
__[0]):
644 path
= os
.path
.join(object.__path
__[0], file)
645 modname
= inspect
.getmodulename(file)
646 if modname
!= '__init__':
647 if modname
and modname
not in modnames
:
648 modpkgs
.append((modname
, name
, 0, 0))
649 modnames
.append(modname
)
650 elif ispackage(path
):
651 modpkgs
.append((file, name
, 1, 0))
653 contents
= self
.multicolumn(modpkgs
, self
.modpkglink
)
654 result
= result
+ self
.bigsection(
655 'Package Contents', '#ffffff', '#aa55cc', contents
)
657 contents
= self
.multicolumn(
658 modules
, lambda (key
, value
), s
=self
: s
.modulelink(value
))
659 result
= result
+ self
.bigsection(
660 'Modules', '#fffff', '#aa55cc', contents
)
663 classlist
= map(lambda (key
, value
): value
, classes
)
665 self
.formattree(inspect
.getclasstree(classlist
, 1), name
)]
666 for key
, value
in classes
:
667 contents
.append(self
.document(value
, key
, name
, fdict
, cdict
))
668 result
= result
+ self
.bigsection(
669 'Classes', '#ffffff', '#ee77aa', join(contents
))
672 for key
, value
in funcs
:
673 contents
.append(self
.document(value
, key
, name
, fdict
, cdict
))
674 result
= result
+ self
.bigsection(
675 'Functions', '#ffffff', '#eeaa77', join(contents
))
678 for key
, value
in data
:
679 contents
.append(self
.document(value
, key
))
680 result
= result
+ self
.bigsection(
681 'Data', '#ffffff', '#55aa55', join(contents
, '<br>\n'))
682 if hasattr(object, '__author__'):
683 contents
= self
.markup(str(object.__author
__), self
.preformat
)
684 result
= result
+ self
.bigsection(
685 'Author', '#ffffff', '#7799ee', contents
)
686 if hasattr(object, '__credits__'):
687 contents
= self
.markup(str(object.__credits
__), self
.preformat
)
688 result
= result
+ self
.bigsection(
689 'Credits', '#ffffff', '#7799ee', contents
)
693 def docclass(self
, object, name
=None, mod
=None, funcs
={}, classes
={},
695 """Produce HTML documentation for a class object."""
696 realname
= object.__name
__
697 name
= name
or realname
698 bases
= object.__bases
__
701 push
= contents
.append
703 # Cute little class to pump out a horizontal rule between sections.
704 class HorizontalRule
:
711 hr
= HorizontalRule()
713 # List the mro, if non-trivial.
714 mro
= deque(inspect
.getmro(object))
717 push('<dl><dt>Method resolution order:</dt>\n')
719 push('<dd>%s</dd>\n' % self
.classlink(base
,
723 def spill(msg
, attrs
, predicate
):
724 ok
, attrs
= _split_list(attrs
, predicate
)
728 for name
, kind
, homecls
, value
in ok
:
729 push(self
.document(getattr(object, name
), name
, mod
,
730 funcs
, classes
, mdict
, object))
734 def spilldescriptors(msg
, attrs
, predicate
):
735 ok
, attrs
= _split_list(attrs
, predicate
)
739 for name
, kind
, homecls
, value
in ok
:
740 push(self
._docdescriptor
(name
, value
, mod
))
743 def spilldata(msg
, attrs
, predicate
):
744 ok
, attrs
= _split_list(attrs
, predicate
)
748 for name
, kind
, homecls
, value
in ok
:
749 base
= self
.docother(getattr(object, name
), name
, mod
)
750 if callable(value
) or inspect
.isdatadescriptor(value
):
751 doc
= getattr(value
, "__doc__", None)
755 push('<dl><dt>%s</dl>\n' % base
)
757 doc
= self
.markup(getdoc(value
), self
.preformat
,
758 funcs
, classes
, mdict
)
759 doc
= '<dd><tt>%s</tt>' % doc
760 push('<dl><dt>%s%s</dl>\n' % (base
, doc
))
764 attrs
= filter(lambda (name
, kind
, cls
, value
): visiblename(name
),
765 classify_class_attrs(object))
767 for key
, kind
, homecls
, value
in attrs
:
768 mdict
[key
] = anchor
= '#' + name
+ '-' + key
769 value
= getattr(object, key
)
771 # The value may not be hashable (e.g., a data attr with
772 # a dict or list value).
773 mdict
[value
] = anchor
779 thisclass
= mro
.popleft()
781 thisclass
= attrs
[0][2]
782 attrs
, inherited
= _split_list(attrs
, lambda t
: t
[2] is thisclass
)
784 if thisclass
is __builtin__
.object:
787 elif thisclass
is object:
790 tag
= 'inherited from %s' % self
.classlink(thisclass
,
794 # Sort attrs by name.
795 attrs
.sort(key
=lambda t
: t
[0])
797 # Pump out the attrs, segregated by kind.
798 attrs
= spill('Methods %s' % tag
, attrs
,
799 lambda t
: t
[1] == 'method')
800 attrs
= spill('Class methods %s' % tag
, attrs
,
801 lambda t
: t
[1] == 'class method')
802 attrs
= spill('Static methods %s' % tag
, attrs
,
803 lambda t
: t
[1] == 'static method')
804 attrs
= spilldescriptors('Data descriptors %s' % tag
, attrs
,
805 lambda t
: t
[1] == 'data descriptor')
806 attrs
= spilldata('Data and other attributes %s' % tag
, attrs
,
807 lambda t
: t
[1] == 'data')
811 contents
= ''.join(contents
)
814 title
= '<a name="%s">class <strong>%s</strong></a>' % (
817 title
= '<strong>%s</strong> = <a name="%s">class %s</a>' % (
818 name
, name
, realname
)
822 parents
.append(self
.classlink(base
, object.__module
__))
823 title
= title
+ '(%s)' % join(parents
, ', ')
824 doc
= self
.markup(getdoc(object), self
.preformat
, funcs
, classes
, mdict
)
825 doc
= doc
and '<tt>%s<br> </tt>' % doc
827 return self
.section(title
, '#000000', '#ffc8d8', contents
, 3, doc
)
829 def formatvalue(self
, object):
830 """Format an argument default value as text."""
831 return self
.grey('=' + self
.repr(object))
833 def docroutine(self
, object, name
=None, mod
=None,
834 funcs
={}, classes
={}, methods
={}, cl
=None):
835 """Produce HTML documentation for a function or method object."""
836 realname
= object.__name
__
837 name
= name
or realname
838 anchor
= (cl
and cl
.__name
__ or '') + '-' + name
841 if inspect
.ismethod(object):
842 imclass
= object.im_class
844 if imclass
is not cl
:
845 note
= ' from ' + self
.classlink(imclass
, mod
)
848 note
= ' method of %s instance' % self
.classlink(
849 object.im_self
.__class
__, mod
)
851 note
= ' unbound %s method' % self
.classlink(imclass
,mod
)
852 object = object.im_func
855 title
= '<a name="%s"><strong>%s</strong></a>' % (anchor
, realname
)
857 if (cl
and realname
in cl
.__dict
__ and
858 cl
.__dict
__[realname
] is object):
859 reallink
= '<a href="#%s">%s</a>' % (
860 cl
.__name
__ + '-' + realname
, realname
)
864 title
= '<a name="%s"><strong>%s</strong></a> = %s' % (
865 anchor
, name
, reallink
)
866 if inspect
.isfunction(object):
867 args
, varargs
, varkw
, defaults
= inspect
.getargspec(object)
868 argspec
= inspect
.formatargspec(
869 args
, varargs
, varkw
, defaults
, formatvalue
=self
.formatvalue
)
870 if realname
== '<lambda>':
871 title
= '<strong>%s</strong> <em>lambda</em> ' % name
872 argspec
= argspec
[1:-1] # remove parentheses
876 decl
= title
+ argspec
+ (note
and self
.grey(
877 '<font face="helvetica, arial">%s</font>' % note
))
880 return '<dl><dt>%s</dt></dl>\n' % decl
883 getdoc(object), self
.preformat
, funcs
, classes
, methods
)
884 doc
= doc
and '<dd><tt>%s</tt></dd>' % doc
885 return '<dl><dt>%s</dt>%s</dl>\n' % (decl
, doc
)
887 def _docdescriptor(self
, name
, value
, mod
):
889 push
= results
.append
892 push('<dl><dt><strong>%s</strong></dt>\n' % name
)
893 if value
.__doc
__ is not None:
894 doc
= self
.markup(getdoc(value
), self
.preformat
)
895 push('<dd><tt>%s</tt></dd>\n' % doc
)
898 return ''.join(results
)
900 def docproperty(self
, object, name
=None, mod
=None, cl
=None):
901 """Produce html documentation for a property."""
902 return self
._docdescriptor
(name
, object, mod
)
904 def docother(self
, object, name
=None, mod
=None, *ignored
):
905 """Produce HTML documentation for a data object."""
906 lhs
= name
and '<strong>%s</strong> = ' % name
or ''
907 return lhs
+ self
.repr(object)
909 def index(self
, dir, shadowed
=None):
910 """Generate an HTML index for a directory of modules."""
912 if shadowed
is None: shadowed
= {}
914 files
= os
.listdir(dir)
916 def found(name
, ispackage
,
917 modpkgs
=modpkgs
, shadowed
=shadowed
, seen
=seen
):
919 modpkgs
.append((name
, '', ispackage
, name
in shadowed
))
923 # Package spam/__init__.py takes precedence over module spam.py.
925 path
= os
.path
.join(dir, file)
926 if ispackage(path
): found(file, 1)
928 path
= os
.path
.join(dir, file)
929 if os
.path
.isfile(path
):
930 modname
= inspect
.getmodulename(file)
931 if modname
: found(modname
, 0)
934 contents
= self
.multicolumn(modpkgs
, self
.modpkglink
)
935 return self
.bigsection(dir, '#ffffff', '#ee77aa', contents
)
937 # -------------------------------------------- text documentation generator
939 class TextRepr(Repr
):
940 """Class for safely making a text representation of a Python object."""
943 self
.maxlist
= self
.maxtuple
= 20
945 self
.maxstring
= self
.maxother
= 100
947 def repr1(self
, x
, level
):
948 if hasattr(type(x
), '__name__'):
949 methodname
= 'repr_' + join(split(type(x
).__name
__), '_')
950 if hasattr(self
, methodname
):
951 return getattr(self
, methodname
)(x
, level
)
952 return cram(stripid(repr(x
)), self
.maxother
)
954 def repr_string(self
, x
, level
):
955 test
= cram(x
, self
.maxstring
)
956 testrepr
= repr(test
)
957 if '\\' in test
and '\\' not in replace(testrepr
, r
'\\', ''):
958 # Backslashes are only literal in the string and are never
959 # needed to make any special characters, so show a raw string.
960 return 'r' + testrepr
[0] + test
+ testrepr
[0]
963 repr_str
= repr_string
965 def repr_instance(self
, x
, level
):
967 return cram(stripid(repr(x
)), self
.maxstring
)
969 return '<%s instance>' % x
.__class
__.__name
__
972 """Formatter class for text documentation."""
974 # ------------------------------------------- text formatting utilities
976 _repr_instance
= TextRepr()
977 repr = _repr_instance
.repr
979 def bold(self
, text
):
980 """Format a string in bold by overstriking."""
981 return join(map(lambda ch
: ch
+ '\b' + ch
, text
), '')
983 def indent(self
, text
, prefix
=' '):
984 """Indent text by prepending a given prefix to each line."""
985 if not text
: return ''
986 lines
= split(text
, '\n')
987 lines
= map(lambda line
, prefix
=prefix
: prefix
+ line
, lines
)
988 if lines
: lines
[-1] = rstrip(lines
[-1])
989 return join(lines
, '\n')
991 def section(self
, title
, contents
):
992 """Format a section with a given heading."""
993 return self
.bold(title
) + '\n' + rstrip(self
.indent(contents
)) + '\n\n'
995 # ---------------------------------------------- type-specific routines
997 def formattree(self
, tree
, modname
, parent
=None, prefix
=''):
998 """Render in text a class tree as returned by inspect.getclasstree()."""
1001 if type(entry
) is type(()):
1003 result
= result
+ prefix
+ classname(c
, modname
)
1004 if bases
and bases
!= (parent
,):
1005 parents
= map(lambda c
, m
=modname
: classname(c
, m
), bases
)
1006 result
= result
+ '(%s)' % join(parents
, ', ')
1007 result
= result
+ '\n'
1008 elif type(entry
) is type([]):
1009 result
= result
+ self
.formattree(
1010 entry
, modname
, c
, prefix
+ ' ')
1013 def docmodule(self
, object, name
=None, mod
=None):
1014 """Produce text documentation for a given module object."""
1015 name
= object.__name
__ # ignore the passed-in name
1016 synop
, desc
= splitdoc(getdoc(object))
1017 result
= self
.section('NAME', name
+ (synop
and ' - ' + synop
))
1020 all
= object.__all
__
1021 except AttributeError:
1025 file = inspect
.getabsfile(object)
1028 result
= result
+ self
.section('FILE', file)
1030 docloc
= self
.getdocloc(object)
1031 if docloc
is not None:
1032 result
= result
+ self
.section('MODULE DOCS', docloc
)
1035 result
= result
+ self
.section('DESCRIPTION', desc
)
1038 for key
, value
in inspect
.getmembers(object, inspect
.isclass
):
1039 # if __all__ exists, believe it. Otherwise use old heuristic.
1041 or (inspect
.getmodule(value
) or object) is object):
1042 if visiblename(key
, all
):
1043 classes
.append((key
, value
))
1045 for key
, value
in inspect
.getmembers(object, inspect
.isroutine
):
1046 # if __all__ exists, believe it. Otherwise use old heuristic.
1047 if (all
is not None or
1048 inspect
.isbuiltin(value
) or inspect
.getmodule(value
) is object):
1049 if visiblename(key
, all
):
1050 funcs
.append((key
, value
))
1052 for key
, value
in inspect
.getmembers(object, isdata
):
1053 if visiblename(key
, all
):
1054 data
.append((key
, value
))
1056 if hasattr(object, '__path__'):
1058 for file in os
.listdir(object.__path
__[0]):
1059 path
= os
.path
.join(object.__path
__[0], file)
1060 modname
= inspect
.getmodulename(file)
1061 if modname
!= '__init__':
1062 if modname
and modname
not in modpkgs
:
1063 modpkgs
.append(modname
)
1064 elif ispackage(path
):
1065 modpkgs
.append(file + ' (package)')
1067 result
= result
+ self
.section(
1068 'PACKAGE CONTENTS', join(modpkgs
, '\n'))
1071 classlist
= map(lambda (key
, value
): value
, classes
)
1072 contents
= [self
.formattree(
1073 inspect
.getclasstree(classlist
, 1), name
)]
1074 for key
, value
in classes
:
1075 contents
.append(self
.document(value
, key
, name
))
1076 result
= result
+ self
.section('CLASSES', join(contents
, '\n'))
1080 for key
, value
in funcs
:
1081 contents
.append(self
.document(value
, key
, name
))
1082 result
= result
+ self
.section('FUNCTIONS', join(contents
, '\n'))
1086 for key
, value
in data
:
1087 contents
.append(self
.docother(value
, key
, name
, maxlen
=70))
1088 result
= result
+ self
.section('DATA', join(contents
, '\n'))
1090 if hasattr(object, '__version__'):
1091 version
= str(object.__version
__)
1092 if version
[:11] == '$' + 'Revision: ' and version
[-1:] == '$':
1093 version
= strip(version
[11:-1])
1094 result
= result
+ self
.section('VERSION', version
)
1095 if hasattr(object, '__date__'):
1096 result
= result
+ self
.section('DATE', str(object.__date
__))
1097 if hasattr(object, '__author__'):
1098 result
= result
+ self
.section('AUTHOR', str(object.__author
__))
1099 if hasattr(object, '__credits__'):
1100 result
= result
+ self
.section('CREDITS', str(object.__credits
__))
1103 def docclass(self
, object, name
=None, mod
=None):
1104 """Produce text documentation for a given class object."""
1105 realname
= object.__name
__
1106 name
= name
or realname
1107 bases
= object.__bases
__
1109 def makename(c
, m
=object.__module
__):
1110 return classname(c
, m
)
1112 if name
== realname
:
1113 title
= 'class ' + self
.bold(realname
)
1115 title
= self
.bold(name
) + ' = class ' + realname
1117 parents
= map(makename
, bases
)
1118 title
= title
+ '(%s)' % join(parents
, ', ')
1120 doc
= getdoc(object)
1121 contents
= doc
and [doc
+ '\n'] or []
1122 push
= contents
.append
1124 # List the mro, if non-trivial.
1125 mro
= deque(inspect
.getmro(object))
1127 push("Method resolution order:")
1129 push(' ' + makename(base
))
1132 # Cute little class to pump out a horizontal rule between sections.
1133 class HorizontalRule
:
1140 hr
= HorizontalRule()
1142 def spill(msg
, attrs
, predicate
):
1143 ok
, attrs
= _split_list(attrs
, predicate
)
1147 for name
, kind
, homecls
, value
in ok
:
1148 push(self
.document(getattr(object, name
),
1152 def spilldescriptors(msg
, attrs
, predicate
):
1153 ok
, attrs
= _split_list(attrs
, predicate
)
1157 for name
, kind
, homecls
, value
in ok
:
1158 push(self
._docdescriptor
(name
, value
, mod
))
1161 def spilldata(msg
, attrs
, predicate
):
1162 ok
, attrs
= _split_list(attrs
, predicate
)
1166 for name
, kind
, homecls
, value
in ok
:
1167 if callable(value
) or inspect
.isdatadescriptor(value
):
1171 push(self
.docother(getattr(object, name
),
1172 name
, mod
, maxlen
=70, doc
=doc
) + '\n')
1175 attrs
= filter(lambda (name
, kind
, cls
, value
): visiblename(name
),
1176 classify_class_attrs(object))
1179 thisclass
= mro
.popleft()
1181 thisclass
= attrs
[0][2]
1182 attrs
, inherited
= _split_list(attrs
, lambda t
: t
[2] is thisclass
)
1184 if thisclass
is __builtin__
.object:
1187 elif thisclass
is object:
1188 tag
= "defined here"
1190 tag
= "inherited from %s" % classname(thisclass
,
1192 filter(lambda t
: not t
[0].startswith('_'), attrs
)
1194 # Sort attrs by name.
1197 # Pump out the attrs, segregated by kind.
1198 attrs
= spill("Methods %s:\n" % tag
, attrs
,
1199 lambda t
: t
[1] == 'method')
1200 attrs
= spill("Class methods %s:\n" % tag
, attrs
,
1201 lambda t
: t
[1] == 'class method')
1202 attrs
= spill("Static methods %s:\n" % tag
, attrs
,
1203 lambda t
: t
[1] == 'static method')
1204 attrs
= spilldescriptors("Data descriptors %s:\n" % tag
, attrs
,
1205 lambda t
: t
[1] == 'data descriptor')
1206 attrs
= spilldata("Data and other attributes %s:\n" % tag
, attrs
,
1207 lambda t
: t
[1] == 'data')
1211 contents
= '\n'.join(contents
)
1214 return title
+ '\n' + self
.indent(rstrip(contents
), ' | ') + '\n'
1216 def formatvalue(self
, object):
1217 """Format an argument default value as text."""
1218 return '=' + self
.repr(object)
1220 def docroutine(self
, object, name
=None, mod
=None, cl
=None):
1221 """Produce text documentation for a function or method object."""
1222 realname
= object.__name
__
1223 name
= name
or realname
1226 if inspect
.ismethod(object):
1227 imclass
= object.im_class
1229 if imclass
is not cl
:
1230 note
= ' from ' + classname(imclass
, mod
)
1233 note
= ' method of %s instance' % classname(
1234 object.im_self
.__class
__, mod
)
1236 note
= ' unbound %s method' % classname(imclass
,mod
)
1237 object = object.im_func
1239 if name
== realname
:
1240 title
= self
.bold(realname
)
1242 if (cl
and realname
in cl
.__dict
__ and
1243 cl
.__dict
__[realname
] is object):
1245 title
= self
.bold(name
) + ' = ' + realname
1246 if inspect
.isfunction(object):
1247 args
, varargs
, varkw
, defaults
= inspect
.getargspec(object)
1248 argspec
= inspect
.formatargspec(
1249 args
, varargs
, varkw
, defaults
, formatvalue
=self
.formatvalue
)
1250 if realname
== '<lambda>':
1252 argspec
= argspec
[1:-1] # remove parentheses
1255 decl
= title
+ argspec
+ note
1260 doc
= getdoc(object) or ''
1261 return decl
+ '\n' + (doc
and rstrip(self
.indent(doc
)) + '\n')
1263 def _docdescriptor(self
, name
, value
, mod
):
1265 push
= results
.append
1268 push(self
.bold(name
))
1270 doc
= getdoc(value
) or ''
1272 push(self
.indent(doc
))
1274 return ''.join(results
)
1276 def docproperty(self
, object, name
=None, mod
=None, cl
=None):
1277 """Produce text documentation for a property."""
1278 return self
._docdescriptor
(name
, object, mod
)
1280 def docother(self
, object, name
=None, mod
=None, parent
=None, maxlen
=None, doc
=None):
1281 """Produce text documentation for a data object."""
1282 repr = self
.repr(object)
1284 line
= (name
and name
+ ' = ' or '') + repr
1285 chop
= maxlen
- len(line
)
1286 if chop
< 0: repr = repr[:chop
] + '...'
1287 line
= (name
and self
.bold(name
) + ' = ' or '') + repr
1289 line
+= '\n' + self
.indent(str(doc
))
1292 # --------------------------------------------------------- user interfaces
1295 """The first time this is called, determine what kind of pager to use."""
1301 """Decide what method to use for paging through text."""
1302 if type(sys
.stdout
) is not types
.FileType
:
1304 if not sys
.stdin
.isatty() or not sys
.stdout
.isatty():
1306 if 'PAGER' in os
.environ
:
1307 if sys
.platform
== 'win32': # pipes completely broken in Windows
1308 return lambda text
: tempfilepager(plain(text
), os
.environ
['PAGER'])
1309 elif os
.environ
.get('TERM') in ('dumb', 'emacs'):
1310 return lambda text
: pipepager(plain(text
), os
.environ
['PAGER'])
1312 return lambda text
: pipepager(text
, os
.environ
['PAGER'])
1313 if os
.environ
.get('TERM') in ('dumb', 'emacs'):
1315 if sys
.platform
== 'win32' or sys
.platform
.startswith('os2'):
1316 return lambda text
: tempfilepager(plain(text
), 'more <')
1317 if hasattr(os
, 'system') and os
.system('(less) 2>/dev/null') == 0:
1318 return lambda text
: pipepager(text
, 'less')
1321 (fd
, filename
) = tempfile
.mkstemp()
1324 if hasattr(os
, 'system') and os
.system('more %s' % filename
) == 0:
1325 return lambda text
: pipepager(text
, 'more')
1332 """Remove boldface formatting from text."""
1333 return re
.sub('.\b', '', text
)
1335 def pipepager(text
, cmd
):
1336 """Page through text by feeding it to another program."""
1337 pipe
= os
.popen(cmd
, 'w')
1342 pass # Ignore broken pipes caused by quitting the pager program.
1344 def tempfilepager(text
, cmd
):
1345 """Page through text by invoking a program on a temporary file."""
1347 filename
= tempfile
.mktemp()
1348 file = open(filename
, 'w')
1352 os
.system(cmd
+ ' ' + filename
)
1357 """Page through text on a text terminal."""
1358 lines
= split(plain(text
), '\n')
1361 fd
= sys
.stdin
.fileno()
1362 old
= tty
.tcgetattr(fd
)
1364 getchar
= lambda: sys
.stdin
.read(1)
1365 except (ImportError, AttributeError):
1367 getchar
= lambda: sys
.stdin
.readline()[:-1][:1]
1370 r
= inc
= os
.environ
.get('LINES', 25) - 1
1371 sys
.stdout
.write(join(lines
[:inc
], '\n') + '\n')
1373 sys
.stdout
.write('-- more --')
1378 sys
.stdout
.write('\r \r')
1380 elif c
in ('\r', '\n'):
1381 sys
.stdout
.write('\r \r' + lines
[r
] + '\n')
1384 if c
in ('b', 'B', '\x1b'):
1387 sys
.stdout
.write('\n' + join(lines
[r
:r
+inc
], '\n') + '\n')
1392 tty
.tcsetattr(fd
, tty
.TCSAFLUSH
, old
)
1394 def plainpager(text
):
1395 """Simply print unformatted text. This is the ultimate fallback."""
1396 sys
.stdout
.write(plain(text
))
1398 def describe(thing
):
1399 """Produce a short description of the given thing."""
1400 if inspect
.ismodule(thing
):
1401 if thing
.__name
__ in sys
.builtin_module_names
:
1402 return 'built-in module ' + thing
.__name
__
1403 if hasattr(thing
, '__path__'):
1404 return 'package ' + thing
.__name
__
1406 return 'module ' + thing
.__name
__
1407 if inspect
.isbuiltin(thing
):
1408 return 'built-in function ' + thing
.__name
__
1409 if inspect
.isclass(thing
):
1410 return 'class ' + thing
.__name
__
1411 if inspect
.isfunction(thing
):
1412 return 'function ' + thing
.__name
__
1413 if inspect
.ismethod(thing
):
1414 return 'method ' + thing
.__name
__
1415 if type(thing
) is types
.InstanceType
:
1416 return 'instance of ' + thing
.__class
__.__name
__
1417 return type(thing
).__name
__
1419 def locate(path
, forceload
=0):
1420 """Locate an object by name or dotted path, importing as necessary."""
1421 parts
= [part
for part
in split(path
, '.') if part
]
1423 while n
< len(parts
):
1424 nextmodule
= safeimport(join(parts
[:n
+1], '.'), forceload
)
1425 if nextmodule
: module
, n
= nextmodule
, n
+ 1
1429 for part
in parts
[n
:]:
1430 try: object = getattr(object, part
)
1431 except AttributeError: return None
1434 if hasattr(__builtin__
, path
):
1435 return getattr(__builtin__
, path
)
1437 # --------------------------------------- interactive interpreter interface
1442 def resolve(thing
, forceload
=0):
1443 """Given an object or a path to an object, get the object and its name."""
1444 if isinstance(thing
, str):
1445 object = locate(thing
, forceload
)
1447 raise ImportError, 'no Python documentation found for %r' % thing
1448 return object, thing
1450 return thing
, getattr(thing
, '__name__', None)
1452 def doc(thing
, title
='Python Library Documentation: %s', forceload
=0):
1453 """Display text documentation, given an object or a path to an object."""
1455 object, name
= resolve(thing
, forceload
)
1456 desc
= describe(object)
1457 module
= inspect
.getmodule(object)
1458 if name
and '.' in name
:
1459 desc
+= ' in ' + name
[:name
.rfind('.')]
1460 elif module
and module
is not object:
1461 desc
+= ' in module ' + module
.__name
__
1462 if not (inspect
.ismodule(object) or
1463 inspect
.isclass(object) or
1464 inspect
.isroutine(object) or
1465 isinstance(object, property)):
1466 # If the passed object is a piece of data or an instance,
1467 # document its available methods instead of its value.
1468 object = type(object)
1470 pager(title
% desc
+ '\n\n' + text
.document(object, name
))
1471 except (ImportError, ErrorDuringImport
), value
:
1474 def writedoc(thing
, forceload
=0):
1475 """Write HTML documentation to a file in the current directory."""
1477 object, name
= resolve(thing
, forceload
)
1478 page
= html
.page(describe(object), html
.document(object, name
))
1479 file = open(name
+ '.html', 'w')
1482 print 'wrote', name
+ '.html'
1483 except (ImportError, ErrorDuringImport
), value
:
1486 def writedocs(dir, pkgpath
='', done
=None):
1487 """Write out HTML documentation for all modules in a directory tree."""
1488 if done
is None: done
= {}
1489 for file in os
.listdir(dir):
1490 path
= os
.path
.join(dir, file)
1492 writedocs(path
, pkgpath
+ file + '.', done
)
1493 elif os
.path
.isfile(path
):
1494 modname
= inspect
.getmodulename(path
)
1496 if modname
== '__init__':
1497 modname
= pkgpath
[:-1] # remove trailing period
1499 modname
= pkgpath
+ modname
1500 if modname
not in done
:
1507 'assert': ('ref/assert', ''),
1508 'break': ('ref/break', 'while for'),
1509 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1510 'continue': ('ref/continue', 'while for'),
1511 'def': ('ref/function', ''),
1512 'del': ('ref/del', 'BASICMETHODS'),
1514 'else': ('ref/if', 'while for'),
1516 'exec': ('ref/exec', ''),
1518 'for': ('ref/for', 'break continue while'),
1520 'global': ('ref/global', 'NAMESPACES'),
1521 'if': ('ref/if', 'TRUTHVALUE'),
1522 'import': ('ref/import', 'MODULES'),
1523 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1525 'lambda': ('ref/lambdas', 'FUNCTIONS'),
1528 'pass': ('ref/pass', ''),
1529 'print': ('ref/print', ''),
1530 'raise': ('ref/raise', 'EXCEPTIONS'),
1531 'return': ('ref/return', 'FUNCTIONS'),
1532 'try': ('ref/try', 'EXCEPTIONS'),
1533 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1534 'yield': ('ref/yield', ''),
1538 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1539 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1540 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1541 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1542 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1543 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1544 'INTEGER': ('ref/integers', 'int range'),
1545 'FLOAT': ('ref/floating', 'float math'),
1546 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1547 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1548 'MAPPINGS': 'DICTIONARIES',
1549 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1550 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1551 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1552 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1553 'FRAMEOBJECTS': 'TYPES',
1554 'TRACEBACKS': 'TYPES',
1555 'NONE': ('lib/bltin-null-object', ''),
1556 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1557 'FILES': ('lib/bltin-file-objects', ''),
1558 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1559 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1560 'MODULES': ('lib/typesmodules', 'import'),
1561 'PACKAGES': 'import',
1562 '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'),
1563 'OPERATORS': 'EXPRESSIONS',
1564 'PRECEDENCE': 'EXPRESSIONS',
1565 'OBJECTS': ('ref/objects', 'TYPES'),
1566 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1567 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1568 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1569 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1570 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1571 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1572 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1573 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1574 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1575 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1576 'DYNAMICFEATURES': ('ref/dynamic-features', ''),
1577 'SCOPING': 'NAMESPACES',
1578 'FRAMES': 'NAMESPACES',
1579 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1580 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1581 'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
1582 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1583 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1584 'PRIVATENAMES': ('ref/atom-identifiers', ''),
1585 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1586 'TUPLES': 'SEQUENCES',
1587 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1588 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1589 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1590 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1591 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1592 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1593 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1594 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1595 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1596 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1597 'POWER': ('ref/power', 'EXPRESSIONS'),
1598 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1599 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1600 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1601 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1602 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1603 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
1604 'ASSERTION': 'assert',
1605 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1606 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1608 'PRINTING': 'print',
1609 'RETURNING': 'return',
1610 'IMPORTING': 'import',
1611 'CONDITIONAL': 'if',
1612 'LOOPING': ('ref/compound', 'for while break continue'),
1613 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1614 'DEBUGGING': ('lib/module-pdb', 'pdb'),
1617 def __init__(self
, input, output
):
1619 self
.output
= output
1621 execdir
= os
.path
.dirname(sys
.executable
)
1622 homedir
= os
.environ
.get('PYTHONHOME')
1623 for dir in [os
.environ
.get('PYTHONDOCS'),
1624 homedir
and os
.path
.join(homedir
, 'doc'),
1625 os
.path
.join(execdir
, 'doc'),
1626 '/usr/doc/python-docs-' + split(sys
.version
)[0],
1627 '/usr/doc/python-' + split(sys
.version
)[0],
1628 '/usr/doc/python-docs-' + sys
.version
[:3],
1629 '/usr/doc/python-' + sys
.version
[:3],
1630 os
.path
.join(sys
.prefix
, 'Resources/English.lproj/Documentation')]:
1631 if dir and os
.path
.isdir(os
.path
.join(dir, 'lib')):
1635 if inspect
.stack()[1][3] == '?':
1638 return '<pydoc.Helper instance>'
1640 def __call__(self
, request
=None):
1641 if request
is not None:
1646 self
.output
.write('''
1647 You are now leaving help and returning to the Python interpreter.
1648 If you want to ask for help on a particular object directly from the
1649 interpreter, you can type "help(object)". Executing "help('string')"
1650 has the same effect as typing a particular string at the help> prompt.
1654 self
.output
.write('\n')
1657 request
= self
.getline('help> ')
1658 if not request
: break
1659 except (KeyboardInterrupt, EOFError):
1661 request
= strip(replace(request
, '"', '', "'", ''))
1662 if lower(request
) in ('q', 'quit'): break
1665 def getline(self
, prompt
):
1666 """Read one line, using raw_input when available."""
1667 if self
.input is sys
.stdin
:
1668 return raw_input(prompt
)
1670 self
.output
.write(prompt
)
1672 return self
.input.readline()
1674 def help(self
, request
):
1675 if type(request
) is type(''):
1676 if request
== 'help': self
.intro()
1677 elif request
== 'keywords': self
.listkeywords()
1678 elif request
== 'topics': self
.listtopics()
1679 elif request
== 'modules': self
.listmodules()
1680 elif request
[:8] == 'modules ':
1681 self
.listmodules(split(request
)[1])
1682 elif request
in self
.keywords
: self
.showtopic(request
)
1683 elif request
in self
.topics
: self
.showtopic(request
)
1684 elif request
: doc(request
, 'Help on %s:')
1685 elif isinstance(request
, Helper
): self()
1686 else: doc(request
, 'Help on %s:')
1687 self
.output
.write('\n')
1690 self
.output
.write('''
1691 Welcome to Python %s! This is the online help utility.
1693 If this is your first time using Python, you should definitely check out
1694 the tutorial on the Internet at http://www.python.org/doc/tut/.
1696 Enter the name of any module, keyword, or topic to get help on writing
1697 Python programs and using Python modules. To quit this help utility and
1698 return to the interpreter, just type "quit".
1700 To get a list of available modules, keywords, or topics, type "modules",
1701 "keywords", or "topics". Each module also comes with a one-line summary
1702 of what it does; to list the modules whose summaries contain a given word
1703 such as "spam", type "modules spam".
1704 ''' % sys
.version
[:3])
1706 def list(self
, items
, columns
=4, width
=80):
1709 colw
= width
/ columns
1710 rows
= (len(items
) + columns
- 1) / columns
1711 for row
in range(rows
):
1712 for col
in range(columns
):
1713 i
= col
* rows
+ row
1715 self
.output
.write(items
[i
])
1716 if col
< columns
- 1:
1717 self
.output
.write(' ' + ' ' * (colw
-1 - len(items
[i
])))
1718 self
.output
.write('\n')
1720 def listkeywords(self
):
1721 self
.output
.write('''
1722 Here is a list of the Python keywords. Enter any keyword to get more help.
1725 self
.list(self
.keywords
.keys())
1727 def listtopics(self
):
1728 self
.output
.write('''
1729 Here is a list of available topics. Enter any topic name to get more help.
1732 self
.list(self
.topics
.keys())
1734 def showtopic(self
, topic
):
1736 self
.output
.write('''
1737 Sorry, topic and keyword documentation is not available because the Python
1738 HTML documentation files could not be found. If you have installed them,
1739 please set the environment variable PYTHONDOCS to indicate their location.
1742 target
= self
.topics
.get(topic
, self
.keywords
.get(topic
))
1744 self
.output
.write('no documentation found for %s\n' % repr(topic
))
1746 if type(target
) is type(''):
1747 return self
.showtopic(target
)
1749 filename
, xrefs
= target
1750 filename
= self
.docdir
+ '/' + filename
+ '.html'
1752 file = open(filename
)
1754 self
.output
.write('could not read docs from %s\n' % filename
)
1757 divpat
= re
.compile('<div[^>]*navigat.*?</div.*?>', re
.I | re
.S
)
1758 addrpat
= re
.compile('<address.*?>.*?</address.*?>', re
.I | re
.S
)
1759 document
= re
.sub(addrpat
, '', re
.sub(divpat
, '', file.read()))
1762 import htmllib
, formatter
, StringIO
1763 buffer = StringIO
.StringIO()
1764 parser
= htmllib
.HTMLParser(
1765 formatter
.AbstractFormatter(formatter
.DumbWriter(buffer)))
1766 parser
.start_table
= parser
.do_p
1767 parser
.end_table
= lambda parser
=parser
: parser
.do_p({})
1768 parser
.start_tr
= parser
.do_br
1769 parser
.start_td
= parser
.start_th
= lambda a
, b
=buffer: b
.write('\t')
1770 parser
.feed(document
)
1771 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1772 pager(' ' + strip(buffer) + '\n')
1774 buffer = StringIO
.StringIO()
1775 formatter
.DumbWriter(buffer).send_flowing_data(
1776 'Related help topics: ' + join(split(xrefs
), ', ') + '\n')
1777 self
.output
.write('\n%s\n' % buffer.getvalue())
1779 def listmodules(self
, key
=''):
1781 self
.output
.write('''
1782 Here is a list of matching modules. Enter any module name to get more help.
1787 self
.output
.write('''
1788 Please wait a moment while I gather a list of all available modules...
1792 def callback(path
, modname
, desc
, modules
=modules
):
1793 if modname
and modname
[-9:] == '.__init__':
1794 modname
= modname
[:-9] + ' (package)'
1795 if find(modname
, '.') < 0:
1796 modules
[modname
] = 1
1797 ModuleScanner().run(callback
)
1798 self
.list(modules
.keys())
1799 self
.output
.write('''
1800 Enter any module name to get more help. Or, type "modules spam" to search
1801 for modules whose descriptions contain the word "spam".
1804 help = Helper(sys
.stdin
, sys
.stdout
)
1807 """A generic tree iterator."""
1808 def __init__(self
, roots
, children
, descendp
):
1809 self
.roots
= roots
[:]
1811 self
.children
= children
1812 self
.descendp
= descendp
1818 root
= self
.roots
.pop(0)
1819 self
.state
= [(root
, self
.children(root
))]
1820 node
, children
= self
.state
[-1]
1824 child
= children
.pop(0)
1825 if self
.descendp(child
):
1826 self
.state
.append((child
, self
.children(child
)))
1829 class ModuleScanner(Scanner
):
1830 """An interruptible scanner that searches module synopses."""
1832 roots
= map(lambda dir: (dir, ''), pathdirs())
1833 Scanner
.__init
__(self
, roots
, self
.submodules
, self
.isnewpackage
)
1834 self
.inodes
= map(lambda (dir, pkg
): os
.stat(dir).st_ino
, roots
)
1836 def submodules(self
, (dir, package
)):
1838 for file in os
.listdir(dir):
1839 path
= os
.path
.join(dir, file)
1841 children
.append((path
, package
+ (package
and '.') + file))
1843 children
.append((path
, package
))
1844 children
.sort() # so that spam.py comes before spam.pyc or spam.pyo
1847 def isnewpackage(self
, (dir, package
)):
1848 inode
= os
.path
.exists(dir) and os
.stat(dir).st_ino
1849 if not (os
.path
.islink(dir) and inode
in self
.inodes
):
1850 self
.inodes
.append(inode
) # detect circular symbolic links
1851 return ispackage(dir)
1854 def run(self
, callback
, key
=None, completer
=None):
1855 if key
: key
= lower(key
)
1859 for modname
in sys
.builtin_module_names
:
1860 if modname
!= '__main__':
1863 callback(None, modname
, '')
1865 desc
= split(__import__(modname
).__doc
__ or '', '\n')[0]
1866 if find(lower(modname
+ ' - ' + desc
), key
) >= 0:
1867 callback(None, modname
, desc
)
1869 while not self
.quit
:
1872 path
, package
= node
1873 modname
= inspect
.getmodulename(path
)
1874 if os
.path
.isfile(path
) and modname
:
1875 modname
= package
+ (package
and '.') + modname
1876 if not modname
in seen
:
1877 seen
[modname
] = 1 # if we see spam.py, skip spam.pyc
1879 callback(path
, modname
, '')
1881 desc
= synopsis(path
) or ''
1882 if find(lower(modname
+ ' - ' + desc
), key
) >= 0:
1883 callback(path
, modname
, desc
)
1884 if completer
: completer()
1887 """Print all the one-line module summaries that contain a substring."""
1888 def callback(path
, modname
, desc
):
1889 if modname
[-9:] == '.__init__':
1890 modname
= modname
[:-9] + ' (package)'
1891 print modname
, desc
and '- ' + desc
1892 try: import warnings
1893 except ImportError: pass
1894 else: warnings
.filterwarnings('ignore') # ignore problems during import
1895 ModuleScanner().run(callback
, key
)
1897 # --------------------------------------------------- web browser interface
1899 def serve(port
, callback
=None, completer
=None):
1900 import BaseHTTPServer
, mimetools
, select
1902 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1903 class Message(mimetools
.Message
):
1904 def __init__(self
, fp
, seekable
=1):
1905 Message
= self
.__class
__
1906 Message
.__bases
__[0].__bases
__[0].__init
__(self
, fp
, seekable
)
1907 self
.encodingheader
= self
.getheader('content-transfer-encoding')
1908 self
.typeheader
= self
.getheader('content-type')
1912 class DocHandler(BaseHTTPServer
.BaseHTTPRequestHandler
):
1913 def send_document(self
, title
, contents
):
1915 self
.send_response(200)
1916 self
.send_header('Content-Type', 'text/html')
1918 self
.wfile
.write(html
.page(title
, contents
))
1919 except IOError: pass
1923 if path
[-5:] == '.html': path
= path
[:-5]
1924 if path
[:1] == '/': path
= path
[1:]
1925 if path
and path
!= '.':
1927 obj
= locate(path
, forceload
=1)
1928 except ErrorDuringImport
, value
:
1929 self
.send_document(path
, html
.escape(str(value
)))
1932 self
.send_document(describe(obj
), html
.document(obj
, path
))
1934 self
.send_document(path
,
1935 'no Python documentation found for %s' % repr(path
))
1937 heading
= html
.heading(
1938 '<big><big><strong>Python: Index of Modules</strong></big></big>',
1939 '#ffffff', '#7799ee')
1940 def bltinlink(name
):
1941 return '<a href="%s.html">%s</a>' % (name
, name
)
1942 names
= filter(lambda x
: x
!= '__main__',
1943 sys
.builtin_module_names
)
1944 contents
= html
.multicolumn(names
, bltinlink
)
1945 indices
= ['<p>' + html
.bigsection(
1946 'Built-in Modules', '#ffffff', '#ee77aa', contents
)]
1949 for dir in pathdirs():
1950 indices
.append(html
.index(dir, seen
))
1951 contents
= heading
+ join(indices
) + '''<p align=right>
1952 <font color="#909090" face="helvetica, arial"><strong>
1953 pydoc</strong> by Ka-Ping Yee <ping@lfw.org></font>'''
1954 self
.send_document('Index of Modules', contents
)
1956 def log_message(self
, *args
): pass
1958 class DocServer(BaseHTTPServer
.HTTPServer
):
1959 def __init__(self
, port
, callback
):
1960 host
= (sys
.platform
== 'mac') and '127.0.0.1' or 'localhost'
1961 self
.address
= ('', port
)
1962 self
.url
= 'http://%s:%d/' % (host
, port
)
1963 self
.callback
= callback
1964 self
.base
.__init
__(self
, self
.address
, self
.handler
)
1966 def serve_until_quit(self
):
1969 while not self
.quit
:
1970 rd
, wr
, ex
= select
.select([self
.socket
.fileno()], [], [], 1)
1971 if rd
: self
.handle_request()
1973 def server_activate(self
):
1974 self
.base
.server_activate(self
)
1975 if self
.callback
: self
.callback(self
)
1977 DocServer
.base
= BaseHTTPServer
.HTTPServer
1978 DocServer
.handler
= DocHandler
1979 DocHandler
.MessageClass
= Message
1982 DocServer(port
, callback
).serve_until_quit()
1983 except (KeyboardInterrupt, select
.error
):
1986 if completer
: completer()
1988 # ----------------------------------------------------- graphical interface
1991 """Graphical interface (starts web server and pops up a control window)."""
1993 def __init__(self
, window
, port
=7464):
1994 self
.window
= window
1999 self
.server_frm
= Tkinter
.Frame(window
)
2000 self
.title_lbl
= Tkinter
.Label(self
.server_frm
,
2001 text
='Starting server...\n ')
2002 self
.open_btn
= Tkinter
.Button(self
.server_frm
,
2003 text
='open browser', command
=self
.open, state
='disabled')
2004 self
.quit_btn
= Tkinter
.Button(self
.server_frm
,
2005 text
='quit serving', command
=self
.quit
, state
='disabled')
2007 self
.search_frm
= Tkinter
.Frame(window
)
2008 self
.search_lbl
= Tkinter
.Label(self
.search_frm
, text
='Search for')
2009 self
.search_ent
= Tkinter
.Entry(self
.search_frm
)
2010 self
.search_ent
.bind('<Return>', self
.search
)
2011 self
.stop_btn
= Tkinter
.Button(self
.search_frm
,
2012 text
='stop', pady
=0, command
=self
.stop
, state
='disabled')
2013 if sys
.platform
== 'win32':
2014 # Trying to hide and show this button crashes under Windows.
2015 self
.stop_btn
.pack(side
='right')
2017 self
.window
.title('pydoc')
2018 self
.window
.protocol('WM_DELETE_WINDOW', self
.quit
)
2019 self
.title_lbl
.pack(side
='top', fill
='x')
2020 self
.open_btn
.pack(side
='left', fill
='x', expand
=1)
2021 self
.quit_btn
.pack(side
='right', fill
='x', expand
=1)
2022 self
.server_frm
.pack(side
='top', fill
='x')
2024 self
.search_lbl
.pack(side
='left')
2025 self
.search_ent
.pack(side
='right', fill
='x', expand
=1)
2026 self
.search_frm
.pack(side
='top', fill
='x')
2027 self
.search_ent
.focus_set()
2029 font
= ('helvetica', sys
.platform
== 'win32' and 8 or 10)
2030 self
.result_lst
= Tkinter
.Listbox(window
, font
=font
, height
=6)
2031 self
.result_lst
.bind('<Button-1>', self
.select
)
2032 self
.result_lst
.bind('<Double-Button-1>', self
.goto
)
2033 self
.result_scr
= Tkinter
.Scrollbar(window
,
2034 orient
='vertical', command
=self
.result_lst
.yview
)
2035 self
.result_lst
.config(yscrollcommand
=self
.result_scr
.set)
2037 self
.result_frm
= Tkinter
.Frame(window
)
2038 self
.goto_btn
= Tkinter
.Button(self
.result_frm
,
2039 text
='go to selected', command
=self
.goto
)
2040 self
.hide_btn
= Tkinter
.Button(self
.result_frm
,
2041 text
='hide results', command
=self
.hide
)
2042 self
.goto_btn
.pack(side
='left', fill
='x', expand
=1)
2043 self
.hide_btn
.pack(side
='right', fill
='x', expand
=1)
2045 self
.window
.update()
2046 self
.minwidth
= self
.window
.winfo_width()
2047 self
.minheight
= self
.window
.winfo_height()
2048 self
.bigminheight
= (self
.server_frm
.winfo_reqheight() +
2049 self
.search_frm
.winfo_reqheight() +
2050 self
.result_lst
.winfo_reqheight() +
2051 self
.result_frm
.winfo_reqheight())
2052 self
.bigwidth
, self
.bigheight
= self
.minwidth
, self
.bigminheight
2054 self
.window
.wm_geometry('%dx%d' % (self
.minwidth
, self
.minheight
))
2055 self
.window
.wm_minsize(self
.minwidth
, self
.minheight
)
2056 self
.window
.tk
.willdispatch()
2060 target
=serve
, args
=(port
, self
.ready
, self
.quit
)).start()
2062 def ready(self
, server
):
2063 self
.server
= server
2064 self
.title_lbl
.config(
2065 text
='Python documentation server at\n' + server
.url
)
2066 self
.open_btn
.config(state
='normal')
2067 self
.quit_btn
.config(state
='normal')
2069 def open(self
, event
=None, url
=None):
2070 url
= url
or self
.server
.url
2073 webbrowser
.open(url
)
2074 except ImportError: # pre-webbrowser.py compatibility
2075 if sys
.platform
== 'win32':
2076 os
.system('start "%s"' % url
)
2077 elif sys
.platform
== 'mac':
2079 except ImportError: pass
2080 else: ic
.launchurl(url
)
2082 rc
= os
.system('netscape -remote "openURL(%s)" &' % url
)
2083 if rc
: os
.system('netscape "%s" &' % url
)
2085 def quit(self
, event
=None):
2087 self
.server
.quit
= 1
2090 def search(self
, event
=None):
2091 key
= self
.search_ent
.get()
2092 self
.stop_btn
.pack(side
='right')
2093 self
.stop_btn
.config(state
='normal')
2094 self
.search_lbl
.config(text
='Searching for "%s"...' % key
)
2095 self
.search_ent
.forget()
2096 self
.search_lbl
.pack(side
='left')
2097 self
.result_lst
.delete(0, 'end')
2098 self
.goto_btn
.config(state
='disabled')
2103 self
.scanner
.quit
= 1
2104 self
.scanner
= ModuleScanner()
2105 threading
.Thread(target
=self
.scanner
.run
,
2106 args
=(self
.update
, key
, self
.done
)).start()
2108 def update(self
, path
, modname
, desc
):
2109 if modname
[-9:] == '.__init__':
2110 modname
= modname
[:-9] + ' (package)'
2111 self
.result_lst
.insert('end',
2112 modname
+ ' - ' + (desc
or '(no description)'))
2114 def stop(self
, event
=None):
2116 self
.scanner
.quit
= 1
2121 self
.search_lbl
.config(text
='Search for')
2122 self
.search_lbl
.pack(side
='left')
2123 self
.search_ent
.pack(side
='right', fill
='x', expand
=1)
2124 if sys
.platform
!= 'win32': self
.stop_btn
.forget()
2125 self
.stop_btn
.config(state
='disabled')
2127 def select(self
, event
=None):
2128 self
.goto_btn
.config(state
='normal')
2130 def goto(self
, event
=None):
2131 selection
= self
.result_lst
.curselection()
2133 modname
= split(self
.result_lst
.get(selection
[0]))[0]
2134 self
.open(url
=self
.server
.url
+ modname
+ '.html')
2137 if not self
.expanded
: return
2138 self
.result_frm
.forget()
2139 self
.result_scr
.forget()
2140 self
.result_lst
.forget()
2141 self
.bigwidth
= self
.window
.winfo_width()
2142 self
.bigheight
= self
.window
.winfo_height()
2143 self
.window
.wm_geometry('%dx%d' % (self
.minwidth
, self
.minheight
))
2144 self
.window
.wm_minsize(self
.minwidth
, self
.minheight
)
2148 if self
.expanded
: return
2149 self
.result_frm
.pack(side
='bottom', fill
='x')
2150 self
.result_scr
.pack(side
='right', fill
='y')
2151 self
.result_lst
.pack(side
='top', fill
='both', expand
=1)
2152 self
.window
.wm_geometry('%dx%d' % (self
.bigwidth
, self
.bigheight
))
2153 self
.window
.wm_minsize(self
.minwidth
, self
.bigminheight
)
2156 def hide(self
, event
=None):
2163 # Tk will crash if pythonw.exe has an XP .manifest
2164 # file and the root has is not destroyed explicitly.
2165 # If the problem is ever fixed in Tk, the explicit
2172 except KeyboardInterrupt:
2175 # -------------------------------------------------- command-line interface
2178 return isinstance(x
, str) and find(x
, os
.sep
) >= 0
2181 """Command-line interface (looks at sys.argv to decide what to do)."""
2183 class BadUsage
: pass
2185 # Scripts don't get the current directory in their path by default.
2186 scriptdir
= os
.path
.dirname(sys
.argv
[0])
2187 if scriptdir
in sys
.path
:
2188 sys
.path
.remove(scriptdir
)
2189 sys
.path
.insert(0, '.')
2192 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'gk:p:w')
2195 for opt
, val
in opts
:
2208 print 'pydoc server ready at %s' % server
.url
2210 print 'pydoc server stopped'
2211 serve(port
, ready
, stopped
)
2216 if not args
: raise BadUsage
2218 if ispath(arg
) and not os
.path
.exists(arg
):
2219 print 'file %r does not exist' % arg
2222 if ispath(arg
) and os
.path
.isfile(arg
):
2223 arg
= importfile(arg
)
2225 if ispath(arg
) and os
.path
.isdir(arg
):
2231 except ErrorDuringImport
, value
:
2234 except (getopt
.error
, BadUsage
):
2235 cmd
= os
.path
.basename(sys
.argv
[0])
2236 print """pydoc - the Python documentation tool
2239 Show text documentation on something. <name> may be the name of a
2240 Python keyword, topic, function, module, or package, or a dotted
2241 reference to a class or function within a module or module in a
2242 package. If <name> contains a '%s', it is used as the path to a
2243 Python source file to document. If name is 'keywords', 'topics',
2244 or 'modules', a listing of these things is displayed.
2247 Search for a keyword in the synopsis lines of all available modules.
2250 Start an HTTP server on the given port on the local machine.
2253 Pop up a graphical interface for finding and serving documentation.
2256 Write out the HTML documentation for a module to a file in the current
2257 directory. If <name> contains a '%s', it is treated as a filename; if
2258 it names a directory, documentation is written for all the contents.
2259 """ % (cmd
, os
.sep
, cmd
, cmd
, cmd
, cmd
, os
.sep
)
2261 if __name__
== '__main__': cli()