|
1 #!/usr/bin/env python |
|
2 # -*- coding: Latin-1 -*- |
|
3 """Generate Python documentation in HTML or text for interactive use. |
|
4 |
|
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. |
|
7 |
|
8 Or, at the shell command line outside of Python: |
|
9 |
|
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. |
|
15 |
|
16 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines |
|
17 of all available modules. |
|
18 |
|
19 Run "pydoc -p <port>" to start an HTTP server on a given port on the |
|
20 local machine to generate documentation web pages. |
|
21 |
|
22 For platforms without a command line, "pydoc -g" starts the HTTP server |
|
23 and also pops up a little window for controlling it. |
|
24 |
|
25 Run "pydoc -w <name>" to write out the HTML documentation for a module |
|
26 to a file named "<name>.html". |
|
27 |
|
28 Module docs for core modules are assumed to be in |
|
29 |
|
30 http://www.python.org/doc/current/lib/ |
|
31 |
|
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. |
|
35 """ |
|
36 |
|
37 __author__ = "Ka-Ping Yee <ping@lfw.org>" |
|
38 __date__ = "26 February 2001" |
|
39 |
|
40 __version__ = "$Revision: 54366 $" |
|
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. |
|
45 """ |
|
46 |
|
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. |
|
54 |
|
55 import sys, imp, os, re, types, inspect, __builtin__, pkgutil |
|
56 from repr import Repr |
|
57 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip |
|
58 try: |
|
59 from collections import deque |
|
60 except ImportError: |
|
61 # Python 2.3 compatibility |
|
62 class deque(list): |
|
63 def popleft(self): |
|
64 return self.pop(0) |
|
65 |
|
66 # --------------------------------------------------------- common routines |
|
67 |
|
68 def pathdirs(): |
|
69 """Convert sys.path into a list of absolute, existing, unique paths.""" |
|
70 dirs = [] |
|
71 normdirs = [] |
|
72 for dir in sys.path: |
|
73 dir = os.path.abspath(dir or '.') |
|
74 normdir = os.path.normcase(dir) |
|
75 if normdir not in normdirs and os.path.isdir(dir): |
|
76 dirs.append(dir) |
|
77 normdirs.append(normdir) |
|
78 return dirs |
|
79 |
|
80 def getdoc(object): |
|
81 """Get the doc string or comments for an object.""" |
|
82 result = inspect.getdoc(object) or inspect.getcomments(object) |
|
83 return result and re.sub('^ *\n', '', rstrip(result)) or '' |
|
84 |
|
85 def splitdoc(doc): |
|
86 """Split a doc string into a synopsis line (if any) and the rest.""" |
|
87 lines = split(strip(doc), '\n') |
|
88 if len(lines) == 1: |
|
89 return lines[0], '' |
|
90 elif len(lines) >= 2 and not rstrip(lines[1]): |
|
91 return lines[0], join(lines[2:], '\n') |
|
92 return '', join(lines, '\n') |
|
93 |
|
94 def classname(object, modname): |
|
95 """Get a class name and qualify it with a module name if necessary.""" |
|
96 name = object.__name__ |
|
97 if object.__module__ != modname: |
|
98 name = object.__module__ + '.' + name |
|
99 return name |
|
100 |
|
101 def isdata(object): |
|
102 """Check if an object is of a type that probably means it's data.""" |
|
103 return not (inspect.ismodule(object) or inspect.isclass(object) or |
|
104 inspect.isroutine(object) or inspect.isframe(object) or |
|
105 inspect.istraceback(object) or inspect.iscode(object)) |
|
106 |
|
107 def replace(text, *pairs): |
|
108 """Do a series of global replacements on a string.""" |
|
109 while pairs: |
|
110 text = join(split(text, pairs[0]), pairs[1]) |
|
111 pairs = pairs[2:] |
|
112 return text |
|
113 |
|
114 def cram(text, maxlen): |
|
115 """Omit part of a string if needed to make it fit in a maximum length.""" |
|
116 if len(text) > maxlen: |
|
117 pre = max(0, (maxlen-3)//2) |
|
118 post = max(0, maxlen-3-pre) |
|
119 return text[:pre] + '...' + text[len(text)-post:] |
|
120 return text |
|
121 |
|
122 _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE) |
|
123 def stripid(text): |
|
124 """Remove the hexadecimal id from a Python object representation.""" |
|
125 # The behaviour of %p is implementation-dependent in terms of case. |
|
126 if _re_stripid.search(repr(Exception)): |
|
127 return _re_stripid.sub(r'\1', text) |
|
128 return text |
|
129 |
|
130 def _is_some_method(obj): |
|
131 return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj) |
|
132 |
|
133 def allmethods(cl): |
|
134 methods = {} |
|
135 for key, value in inspect.getmembers(cl, _is_some_method): |
|
136 methods[key] = 1 |
|
137 for base in cl.__bases__: |
|
138 methods.update(allmethods(base)) # all your base are belong to us |
|
139 for key in methods.keys(): |
|
140 methods[key] = getattr(cl, key) |
|
141 return methods |
|
142 |
|
143 def _split_list(s, predicate): |
|
144 """Split sequence s via predicate, and return pair ([true], [false]). |
|
145 |
|
146 The return value is a 2-tuple of lists, |
|
147 ([x for x in s if predicate(x)], |
|
148 [x for x in s if not predicate(x)]) |
|
149 """ |
|
150 |
|
151 yes = [] |
|
152 no = [] |
|
153 for x in s: |
|
154 if predicate(x): |
|
155 yes.append(x) |
|
156 else: |
|
157 no.append(x) |
|
158 return yes, no |
|
159 |
|
160 def visiblename(name, all=None): |
|
161 """Decide whether to show documentation on a variable.""" |
|
162 # Certain special names are redundant. |
|
163 if name in ('__builtins__', '__doc__', '__file__', '__path__', |
|
164 '__module__', '__name__', '__slots__'): return 0 |
|
165 # Private names are hidden, but special names are displayed. |
|
166 if name.startswith('__') and name.endswith('__'): return 1 |
|
167 if all is not None: |
|
168 # only document that which the programmer exported in __all__ |
|
169 return name in all |
|
170 else: |
|
171 return not name.startswith('_') |
|
172 |
|
173 def classify_class_attrs(object): |
|
174 """Wrap inspect.classify_class_attrs, with fixup for data descriptors.""" |
|
175 def fixup((name, kind, cls, value)): |
|
176 if inspect.isdatadescriptor(value): |
|
177 kind = 'data descriptor' |
|
178 return name, kind, cls, value |
|
179 return map(fixup, inspect.classify_class_attrs(object)) |
|
180 |
|
181 # ----------------------------------------------------- module manipulation |
|
182 |
|
183 def ispackage(path): |
|
184 """Guess whether a path refers to a package directory.""" |
|
185 if os.path.isdir(path): |
|
186 for ext in ('.py', '.pyc', '.pyo'): |
|
187 if os.path.isfile(os.path.join(path, '__init__' + ext)): |
|
188 return True |
|
189 return False |
|
190 |
|
191 def source_synopsis(file): |
|
192 line = file.readline() |
|
193 while line[:1] == '#' or not strip(line): |
|
194 line = file.readline() |
|
195 if not line: break |
|
196 line = strip(line) |
|
197 if line[:4] == 'r"""': line = line[1:] |
|
198 if line[:3] == '"""': |
|
199 line = line[3:] |
|
200 if line[-1:] == '\\': line = line[:-1] |
|
201 while not strip(line): |
|
202 line = file.readline() |
|
203 if not line: break |
|
204 result = strip(split(line, '"""')[0]) |
|
205 else: result = None |
|
206 return result |
|
207 |
|
208 def synopsis(filename, cache={}): |
|
209 """Get the one-line summary out of a module file.""" |
|
210 mtime = os.stat(filename).st_mtime |
|
211 lastupdate, result = cache.get(filename, (0, None)) |
|
212 if lastupdate < mtime: |
|
213 info = inspect.getmoduleinfo(filename) |
|
214 try: |
|
215 file = open(filename) |
|
216 except IOError: |
|
217 # module can't be opened, so skip it |
|
218 return None |
|
219 if info and 'b' in info[2]: # binary modules have to be imported |
|
220 try: module = imp.load_module('__temp__', file, filename, info[1:]) |
|
221 except: return None |
|
222 result = (module.__doc__ or '').splitlines()[0] |
|
223 del sys.modules['__temp__'] |
|
224 else: # text modules can be directly examined |
|
225 result = source_synopsis(file) |
|
226 file.close() |
|
227 cache[filename] = (mtime, result) |
|
228 return result |
|
229 |
|
230 class ErrorDuringImport(Exception): |
|
231 """Errors that occurred while trying to import something to document it.""" |
|
232 def __init__(self, filename, (exc, value, tb)): |
|
233 self.filename = filename |
|
234 self.exc = exc |
|
235 self.value = value |
|
236 self.tb = tb |
|
237 |
|
238 def __str__(self): |
|
239 exc = self.exc |
|
240 if type(exc) is types.ClassType: |
|
241 exc = exc.__name__ |
|
242 return 'problem in %s - %s: %s' % (self.filename, exc, self.value) |
|
243 |
|
244 def importfile(path): |
|
245 """Import a Python source file or compiled file given its path.""" |
|
246 magic = imp.get_magic() |
|
247 file = open(path, 'r') |
|
248 if file.read(len(magic)) == magic: |
|
249 kind = imp.PY_COMPILED |
|
250 else: |
|
251 kind = imp.PY_SOURCE |
|
252 file.close() |
|
253 filename = os.path.basename(path) |
|
254 name, ext = os.path.splitext(filename) |
|
255 file = open(path, 'r') |
|
256 try: |
|
257 module = imp.load_module(name, file, path, (ext, 'r', kind)) |
|
258 except: |
|
259 raise ErrorDuringImport(path, sys.exc_info()) |
|
260 file.close() |
|
261 return module |
|
262 |
|
263 def safeimport(path, forceload=0, cache={}): |
|
264 """Import a module; handle errors; return None if the module isn't found. |
|
265 |
|
266 If the module *is* found but an exception occurs, it's wrapped in an |
|
267 ErrorDuringImport exception and reraised. Unlike __import__, if a |
|
268 package path is specified, the module at the end of the path is returned, |
|
269 not the package at the beginning. If the optional 'forceload' argument |
|
270 is 1, we reload the module from disk (unless it's a dynamic extension).""" |
|
271 try: |
|
272 # If forceload is 1 and the module has been previously loaded from |
|
273 # disk, we always have to reload the module. Checking the file's |
|
274 # mtime isn't good enough (e.g. the module could contain a class |
|
275 # that inherits from another module that has changed). |
|
276 if forceload and path in sys.modules: |
|
277 if path not in sys.builtin_module_names: |
|
278 # Avoid simply calling reload() because it leaves names in |
|
279 # the currently loaded module lying around if they're not |
|
280 # defined in the new source file. Instead, remove the |
|
281 # module from sys.modules and re-import. Also remove any |
|
282 # submodules because they won't appear in the newly loaded |
|
283 # module's namespace if they're already in sys.modules. |
|
284 subs = [m for m in sys.modules if m.startswith(path + '.')] |
|
285 for key in [path] + subs: |
|
286 # Prevent garbage collection. |
|
287 cache[key] = sys.modules[key] |
|
288 del sys.modules[key] |
|
289 module = __import__(path) |
|
290 except: |
|
291 # Did the error occur before or after the module was found? |
|
292 (exc, value, tb) = info = sys.exc_info() |
|
293 if path in sys.modules: |
|
294 # An error occurred while executing the imported module. |
|
295 raise ErrorDuringImport(sys.modules[path].__file__, info) |
|
296 elif exc is SyntaxError: |
|
297 # A SyntaxError occurred before we could execute the module. |
|
298 raise ErrorDuringImport(value.filename, info) |
|
299 elif exc is ImportError and \ |
|
300 split(lower(str(value)))[:2] == ['no', 'module']: |
|
301 # The module was not found. |
|
302 return None |
|
303 else: |
|
304 # Some other error occurred during the importing process. |
|
305 raise ErrorDuringImport(path, sys.exc_info()) |
|
306 for part in split(path, '.')[1:]: |
|
307 try: module = getattr(module, part) |
|
308 except AttributeError: return None |
|
309 return module |
|
310 |
|
311 # ---------------------------------------------------- formatter base class |
|
312 |
|
313 class Doc: |
|
314 def document(self, object, name=None, *args): |
|
315 """Generate documentation for an object.""" |
|
316 args = (object, name) + args |
|
317 # 'try' clause is to attempt to handle the possibility that inspect |
|
318 # identifies something in a way that pydoc itself has issues handling; |
|
319 # think 'super' and how it is a descriptor (which raises the exception |
|
320 # by lacking a __name__ attribute) and an instance. |
|
321 if inspect.isgetsetdescriptor(object): return self.docdata(*args) |
|
322 if inspect.ismemberdescriptor(object): return self.docdata(*args) |
|
323 try: |
|
324 if inspect.ismodule(object): return self.docmodule(*args) |
|
325 if inspect.isclass(object): return self.docclass(*args) |
|
326 if inspect.isroutine(object): return self.docroutine(*args) |
|
327 except AttributeError: |
|
328 pass |
|
329 if isinstance(object, property): return self.docproperty(*args) |
|
330 return self.docother(*args) |
|
331 |
|
332 def fail(self, object, name=None, *args): |
|
333 """Raise an exception for unimplemented types.""" |
|
334 message = "don't know how to document object%s of type %s" % ( |
|
335 name and ' ' + repr(name), type(object).__name__) |
|
336 raise TypeError, message |
|
337 |
|
338 docmodule = docclass = docroutine = docother = docproperty = docdata = fail |
|
339 |
|
340 def getdocloc(self, object): |
|
341 """Return the location of module docs or None""" |
|
342 |
|
343 try: |
|
344 file = inspect.getabsfile(object) |
|
345 except TypeError: |
|
346 file = '(built-in)' |
|
347 |
|
348 docloc = os.environ.get("PYTHONDOCS", |
|
349 "http://www.python.org/doc/current/lib") |
|
350 basedir = os.path.join(sys.exec_prefix, "lib", |
|
351 "python"+sys.version[0:3]) |
|
352 if (isinstance(object, type(os)) and |
|
353 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp', |
|
354 'marshal', 'posix', 'signal', 'sys', |
|
355 'thread', 'zipimport') or |
|
356 (file.startswith(basedir) and |
|
357 not file.startswith(os.path.join(basedir, 'site-packages'))))): |
|
358 htmlfile = "module-%s.html" % object.__name__ |
|
359 if docloc.startswith("http://"): |
|
360 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile) |
|
361 else: |
|
362 docloc = os.path.join(docloc, htmlfile) |
|
363 else: |
|
364 docloc = None |
|
365 return docloc |
|
366 |
|
367 # -------------------------------------------- HTML documentation generator |
|
368 |
|
369 class HTMLRepr(Repr): |
|
370 """Class for safely making an HTML representation of a Python object.""" |
|
371 def __init__(self): |
|
372 Repr.__init__(self) |
|
373 self.maxlist = self.maxtuple = 20 |
|
374 self.maxdict = 10 |
|
375 self.maxstring = self.maxother = 100 |
|
376 |
|
377 def escape(self, text): |
|
378 return replace(text, '&', '&', '<', '<', '>', '>') |
|
379 |
|
380 def repr(self, object): |
|
381 return Repr.repr(self, object) |
|
382 |
|
383 def repr1(self, x, level): |
|
384 if hasattr(type(x), '__name__'): |
|
385 methodname = 'repr_' + join(split(type(x).__name__), '_') |
|
386 if hasattr(self, methodname): |
|
387 return getattr(self, methodname)(x, level) |
|
388 return self.escape(cram(stripid(repr(x)), self.maxother)) |
|
389 |
|
390 def repr_string(self, x, level): |
|
391 test = cram(x, self.maxstring) |
|
392 testrepr = repr(test) |
|
393 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): |
|
394 # Backslashes are only literal in the string and are never |
|
395 # needed to make any special characters, so show a raw string. |
|
396 return 'r' + testrepr[0] + self.escape(test) + testrepr[0] |
|
397 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)', |
|
398 r'<font color="#c040c0">\1</font>', |
|
399 self.escape(testrepr)) |
|
400 |
|
401 repr_str = repr_string |
|
402 |
|
403 def repr_instance(self, x, level): |
|
404 try: |
|
405 return self.escape(cram(stripid(repr(x)), self.maxstring)) |
|
406 except: |
|
407 return self.escape('<%s instance>' % x.__class__.__name__) |
|
408 |
|
409 repr_unicode = repr_string |
|
410 |
|
411 class HTMLDoc(Doc): |
|
412 """Formatter class for HTML documentation.""" |
|
413 |
|
414 # ------------------------------------------- HTML formatting utilities |
|
415 |
|
416 _repr_instance = HTMLRepr() |
|
417 repr = _repr_instance.repr |
|
418 escape = _repr_instance.escape |
|
419 |
|
420 def page(self, title, contents): |
|
421 """Format an HTML page.""" |
|
422 return ''' |
|
423 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> |
|
424 <html><head><title>Python: %s</title> |
|
425 </head><body bgcolor="#f0f0f8"> |
|
426 %s |
|
427 </body></html>''' % (title, contents) |
|
428 |
|
429 def heading(self, title, fgcol, bgcol, extras=''): |
|
430 """Format a page heading.""" |
|
431 return ''' |
|
432 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> |
|
433 <tr bgcolor="%s"> |
|
434 <td valign=bottom> <br> |
|
435 <font color="%s" face="helvetica, arial"> <br>%s</font></td |
|
436 ><td align=right valign=bottom |
|
437 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table> |
|
438 ''' % (bgcol, fgcol, title, fgcol, extras or ' ') |
|
439 |
|
440 def section(self, title, fgcol, bgcol, contents, width=6, |
|
441 prelude='', marginalia=None, gap=' '): |
|
442 """Format a section with a heading.""" |
|
443 if marginalia is None: |
|
444 marginalia = '<tt>' + ' ' * width + '</tt>' |
|
445 result = '''<p> |
|
446 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> |
|
447 <tr bgcolor="%s"> |
|
448 <td colspan=3 valign=bottom> <br> |
|
449 <font color="%s" face="helvetica, arial">%s</font></td></tr> |
|
450 ''' % (bgcol, fgcol, title) |
|
451 if prelude: |
|
452 result = result + ''' |
|
453 <tr bgcolor="%s"><td rowspan=2>%s</td> |
|
454 <td colspan=2>%s</td></tr> |
|
455 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap) |
|
456 else: |
|
457 result = result + ''' |
|
458 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap) |
|
459 |
|
460 return result + '\n<td width="100%%">%s</td></tr></table>' % contents |
|
461 |
|
462 def bigsection(self, title, *args): |
|
463 """Format a section with a big heading.""" |
|
464 title = '<big><strong>%s</strong></big>' % title |
|
465 return self.section(title, *args) |
|
466 |
|
467 def preformat(self, text): |
|
468 """Format literal preformatted text.""" |
|
469 text = self.escape(expandtabs(text)) |
|
470 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n', |
|
471 ' ', ' ', '\n', '<br>\n') |
|
472 |
|
473 def multicolumn(self, list, format, cols=4): |
|
474 """Format a list of items into a multi-column list.""" |
|
475 result = '' |
|
476 rows = (len(list)+cols-1)/cols |
|
477 for col in range(cols): |
|
478 result = result + '<td width="%d%%" valign=top>' % (100/cols) |
|
479 for i in range(rows*col, rows*col+rows): |
|
480 if i < len(list): |
|
481 result = result + format(list[i]) + '<br>\n' |
|
482 result = result + '</td>' |
|
483 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result |
|
484 |
|
485 def grey(self, text): return '<font color="#909090">%s</font>' % text |
|
486 |
|
487 def namelink(self, name, *dicts): |
|
488 """Make a link for an identifier, given name-to-URL mappings.""" |
|
489 for dict in dicts: |
|
490 if name in dict: |
|
491 return '<a href="%s">%s</a>' % (dict[name], name) |
|
492 return name |
|
493 |
|
494 def classlink(self, object, modname): |
|
495 """Make a link for a class.""" |
|
496 name, module = object.__name__, sys.modules.get(object.__module__) |
|
497 if hasattr(module, name) and getattr(module, name) is object: |
|
498 return '<a href="%s.html#%s">%s</a>' % ( |
|
499 module.__name__, name, classname(object, modname)) |
|
500 return classname(object, modname) |
|
501 |
|
502 def modulelink(self, object): |
|
503 """Make a link for a module.""" |
|
504 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__) |
|
505 |
|
506 def modpkglink(self, (name, path, ispackage, shadowed)): |
|
507 """Make a link for a module or package to display in an index.""" |
|
508 if shadowed: |
|
509 return self.grey(name) |
|
510 if path: |
|
511 url = '%s.%s.html' % (path, name) |
|
512 else: |
|
513 url = '%s.html' % name |
|
514 if ispackage: |
|
515 text = '<strong>%s</strong> (package)' % name |
|
516 else: |
|
517 text = name |
|
518 return '<a href="%s">%s</a>' % (url, text) |
|
519 |
|
520 def markup(self, text, escape=None, funcs={}, classes={}, methods={}): |
|
521 """Mark up some plain text, given a context of symbols to look for. |
|
522 Each context dictionary maps object names to anchor names.""" |
|
523 escape = escape or self.escape |
|
524 results = [] |
|
525 here = 0 |
|
526 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' |
|
527 r'RFC[- ]?(\d+)|' |
|
528 r'PEP[- ]?(\d+)|' |
|
529 r'(self\.)?(\w+))') |
|
530 while True: |
|
531 match = pattern.search(text, here) |
|
532 if not match: break |
|
533 start, end = match.span() |
|
534 results.append(escape(text[here:start])) |
|
535 |
|
536 all, scheme, rfc, pep, selfdot, name = match.groups() |
|
537 if scheme: |
|
538 url = escape(all).replace('"', '"') |
|
539 results.append('<a href="%s">%s</a>' % (url, url)) |
|
540 elif rfc: |
|
541 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) |
|
542 results.append('<a href="%s">%s</a>' % (url, escape(all))) |
|
543 elif pep: |
|
544 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep) |
|
545 results.append('<a href="%s">%s</a>' % (url, escape(all))) |
|
546 elif text[end:end+1] == '(': |
|
547 results.append(self.namelink(name, methods, funcs, classes)) |
|
548 elif selfdot: |
|
549 results.append('self.<strong>%s</strong>' % name) |
|
550 else: |
|
551 results.append(self.namelink(name, classes)) |
|
552 here = end |
|
553 results.append(escape(text[here:])) |
|
554 return join(results, '') |
|
555 |
|
556 # ---------------------------------------------- type-specific routines |
|
557 |
|
558 def formattree(self, tree, modname, parent=None): |
|
559 """Produce HTML for a class tree as given by inspect.getclasstree().""" |
|
560 result = '' |
|
561 for entry in tree: |
|
562 if type(entry) is type(()): |
|
563 c, bases = entry |
|
564 result = result + '<dt><font face="helvetica, arial">' |
|
565 result = result + self.classlink(c, modname) |
|
566 if bases and bases != (parent,): |
|
567 parents = [] |
|
568 for base in bases: |
|
569 parents.append(self.classlink(base, modname)) |
|
570 result = result + '(' + join(parents, ', ') + ')' |
|
571 result = result + '\n</font></dt>' |
|
572 elif type(entry) is type([]): |
|
573 result = result + '<dd>\n%s</dd>\n' % self.formattree( |
|
574 entry, modname, c) |
|
575 return '<dl>\n%s</dl>\n' % result |
|
576 |
|
577 def docmodule(self, object, name=None, mod=None, *ignored): |
|
578 """Produce HTML documentation for a module object.""" |
|
579 name = object.__name__ # ignore the passed-in name |
|
580 try: |
|
581 all = object.__all__ |
|
582 except AttributeError: |
|
583 all = None |
|
584 parts = split(name, '.') |
|
585 links = [] |
|
586 for i in range(len(parts)-1): |
|
587 links.append( |
|
588 '<a href="%s.html"><font color="#ffffff">%s</font></a>' % |
|
589 (join(parts[:i+1], '.'), parts[i])) |
|
590 linkedname = join(links + parts[-1:], '.') |
|
591 head = '<big><big><strong>%s</strong></big></big>' % linkedname |
|
592 try: |
|
593 path = inspect.getabsfile(object) |
|
594 url = path |
|
595 if sys.platform == 'win32': |
|
596 import nturl2path |
|
597 url = nturl2path.pathname2url(path) |
|
598 filelink = '<a href="file:%s">%s</a>' % (url, path) |
|
599 except TypeError: |
|
600 filelink = '(built-in)' |
|
601 info = [] |
|
602 if hasattr(object, '__version__'): |
|
603 version = str(object.__version__) |
|
604 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': |
|
605 version = strip(version[11:-1]) |
|
606 info.append('version %s' % self.escape(version)) |
|
607 if hasattr(object, '__date__'): |
|
608 info.append(self.escape(str(object.__date__))) |
|
609 if info: |
|
610 head = head + ' (%s)' % join(info, ', ') |
|
611 docloc = self.getdocloc(object) |
|
612 if docloc is not None: |
|
613 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals() |
|
614 else: |
|
615 docloc = '' |
|
616 result = self.heading( |
|
617 head, '#ffffff', '#7799ee', |
|
618 '<a href=".">index</a><br>' + filelink + docloc) |
|
619 |
|
620 modules = inspect.getmembers(object, inspect.ismodule) |
|
621 |
|
622 classes, cdict = [], {} |
|
623 for key, value in inspect.getmembers(object, inspect.isclass): |
|
624 # if __all__ exists, believe it. Otherwise use old heuristic. |
|
625 if (all is not None or |
|
626 (inspect.getmodule(value) or object) is object): |
|
627 if visiblename(key, all): |
|
628 classes.append((key, value)) |
|
629 cdict[key] = cdict[value] = '#' + key |
|
630 for key, value in classes: |
|
631 for base in value.__bases__: |
|
632 key, modname = base.__name__, base.__module__ |
|
633 module = sys.modules.get(modname) |
|
634 if modname != name and module and hasattr(module, key): |
|
635 if getattr(module, key) is base: |
|
636 if not key in cdict: |
|
637 cdict[key] = cdict[base] = modname + '.html#' + key |
|
638 funcs, fdict = [], {} |
|
639 for key, value in inspect.getmembers(object, inspect.isroutine): |
|
640 # if __all__ exists, believe it. Otherwise use old heuristic. |
|
641 if (all is not None or |
|
642 inspect.isbuiltin(value) or inspect.getmodule(value) is object): |
|
643 if visiblename(key, all): |
|
644 funcs.append((key, value)) |
|
645 fdict[key] = '#-' + key |
|
646 if inspect.isfunction(value): fdict[value] = fdict[key] |
|
647 data = [] |
|
648 for key, value in inspect.getmembers(object, isdata): |
|
649 if visiblename(key, all): |
|
650 data.append((key, value)) |
|
651 |
|
652 doc = self.markup(getdoc(object), self.preformat, fdict, cdict) |
|
653 doc = doc and '<tt>%s</tt>' % doc |
|
654 result = result + '<p>%s</p>\n' % doc |
|
655 |
|
656 if hasattr(object, '__path__'): |
|
657 modpkgs = [] |
|
658 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): |
|
659 modpkgs.append((modname, name, ispkg, 0)) |
|
660 modpkgs.sort() |
|
661 contents = self.multicolumn(modpkgs, self.modpkglink) |
|
662 result = result + self.bigsection( |
|
663 'Package Contents', '#ffffff', '#aa55cc', contents) |
|
664 elif modules: |
|
665 contents = self.multicolumn( |
|
666 modules, lambda (key, value), s=self: s.modulelink(value)) |
|
667 result = result + self.bigsection( |
|
668 'Modules', '#fffff', '#aa55cc', contents) |
|
669 |
|
670 if classes: |
|
671 classlist = map(lambda (key, value): value, classes) |
|
672 contents = [ |
|
673 self.formattree(inspect.getclasstree(classlist, 1), name)] |
|
674 for key, value in classes: |
|
675 contents.append(self.document(value, key, name, fdict, cdict)) |
|
676 result = result + self.bigsection( |
|
677 'Classes', '#ffffff', '#ee77aa', join(contents)) |
|
678 if funcs: |
|
679 contents = [] |
|
680 for key, value in funcs: |
|
681 contents.append(self.document(value, key, name, fdict, cdict)) |
|
682 result = result + self.bigsection( |
|
683 'Functions', '#ffffff', '#eeaa77', join(contents)) |
|
684 if data: |
|
685 contents = [] |
|
686 for key, value in data: |
|
687 contents.append(self.document(value, key)) |
|
688 result = result + self.bigsection( |
|
689 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n')) |
|
690 if hasattr(object, '__author__'): |
|
691 contents = self.markup(str(object.__author__), self.preformat) |
|
692 result = result + self.bigsection( |
|
693 'Author', '#ffffff', '#7799ee', contents) |
|
694 if hasattr(object, '__credits__'): |
|
695 contents = self.markup(str(object.__credits__), self.preformat) |
|
696 result = result + self.bigsection( |
|
697 'Credits', '#ffffff', '#7799ee', contents) |
|
698 |
|
699 return result |
|
700 |
|
701 def docclass(self, object, name=None, mod=None, funcs={}, classes={}, |
|
702 *ignored): |
|
703 """Produce HTML documentation for a class object.""" |
|
704 realname = object.__name__ |
|
705 name = name or realname |
|
706 bases = object.__bases__ |
|
707 |
|
708 contents = [] |
|
709 push = contents.append |
|
710 |
|
711 # Cute little class to pump out a horizontal rule between sections. |
|
712 class HorizontalRule: |
|
713 def __init__(self): |
|
714 self.needone = 0 |
|
715 def maybe(self): |
|
716 if self.needone: |
|
717 push('<hr>\n') |
|
718 self.needone = 1 |
|
719 hr = HorizontalRule() |
|
720 |
|
721 # List the mro, if non-trivial. |
|
722 mro = deque(inspect.getmro(object)) |
|
723 if len(mro) > 2: |
|
724 hr.maybe() |
|
725 push('<dl><dt>Method resolution order:</dt>\n') |
|
726 for base in mro: |
|
727 push('<dd>%s</dd>\n' % self.classlink(base, |
|
728 object.__module__)) |
|
729 push('</dl>\n') |
|
730 |
|
731 def spill(msg, attrs, predicate): |
|
732 ok, attrs = _split_list(attrs, predicate) |
|
733 if ok: |
|
734 hr.maybe() |
|
735 push(msg) |
|
736 for name, kind, homecls, value in ok: |
|
737 push(self.document(getattr(object, name), name, mod, |
|
738 funcs, classes, mdict, object)) |
|
739 push('\n') |
|
740 return attrs |
|
741 |
|
742 def spilldescriptors(msg, attrs, predicate): |
|
743 ok, attrs = _split_list(attrs, predicate) |
|
744 if ok: |
|
745 hr.maybe() |
|
746 push(msg) |
|
747 for name, kind, homecls, value in ok: |
|
748 push(self._docdescriptor(name, value, mod)) |
|
749 return attrs |
|
750 |
|
751 def spilldata(msg, attrs, predicate): |
|
752 ok, attrs = _split_list(attrs, predicate) |
|
753 if ok: |
|
754 hr.maybe() |
|
755 push(msg) |
|
756 for name, kind, homecls, value in ok: |
|
757 base = self.docother(getattr(object, name), name, mod) |
|
758 if callable(value) or inspect.isdatadescriptor(value): |
|
759 doc = getattr(value, "__doc__", None) |
|
760 else: |
|
761 doc = None |
|
762 if doc is None: |
|
763 push('<dl><dt>%s</dl>\n' % base) |
|
764 else: |
|
765 doc = self.markup(getdoc(value), self.preformat, |
|
766 funcs, classes, mdict) |
|
767 doc = '<dd><tt>%s</tt>' % doc |
|
768 push('<dl><dt>%s%s</dl>\n' % (base, doc)) |
|
769 push('\n') |
|
770 return attrs |
|
771 |
|
772 attrs = filter(lambda (name, kind, cls, value): visiblename(name), |
|
773 classify_class_attrs(object)) |
|
774 mdict = {} |
|
775 for key, kind, homecls, value in attrs: |
|
776 mdict[key] = anchor = '#' + name + '-' + key |
|
777 value = getattr(object, key) |
|
778 try: |
|
779 # The value may not be hashable (e.g., a data attr with |
|
780 # a dict or list value). |
|
781 mdict[value] = anchor |
|
782 except TypeError: |
|
783 pass |
|
784 |
|
785 while attrs: |
|
786 if mro: |
|
787 thisclass = mro.popleft() |
|
788 else: |
|
789 thisclass = attrs[0][2] |
|
790 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) |
|
791 |
|
792 if thisclass is __builtin__.object: |
|
793 attrs = inherited |
|
794 continue |
|
795 elif thisclass is object: |
|
796 tag = 'defined here' |
|
797 else: |
|
798 tag = 'inherited from %s' % self.classlink(thisclass, |
|
799 object.__module__) |
|
800 tag += ':<br>\n' |
|
801 |
|
802 # Sort attrs by name. |
|
803 try: |
|
804 attrs.sort(key=lambda t: t[0]) |
|
805 except TypeError: |
|
806 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0])) # 2.3 compat |
|
807 |
|
808 # Pump out the attrs, segregated by kind. |
|
809 attrs = spill('Methods %s' % tag, attrs, |
|
810 lambda t: t[1] == 'method') |
|
811 attrs = spill('Class methods %s' % tag, attrs, |
|
812 lambda t: t[1] == 'class method') |
|
813 attrs = spill('Static methods %s' % tag, attrs, |
|
814 lambda t: t[1] == 'static method') |
|
815 attrs = spilldescriptors('Data descriptors %s' % tag, attrs, |
|
816 lambda t: t[1] == 'data descriptor') |
|
817 attrs = spilldata('Data and other attributes %s' % tag, attrs, |
|
818 lambda t: t[1] == 'data') |
|
819 assert attrs == [] |
|
820 attrs = inherited |
|
821 |
|
822 contents = ''.join(contents) |
|
823 |
|
824 if name == realname: |
|
825 title = '<a name="%s">class <strong>%s</strong></a>' % ( |
|
826 name, realname) |
|
827 else: |
|
828 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % ( |
|
829 name, name, realname) |
|
830 if bases: |
|
831 parents = [] |
|
832 for base in bases: |
|
833 parents.append(self.classlink(base, object.__module__)) |
|
834 title = title + '(%s)' % join(parents, ', ') |
|
835 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict) |
|
836 doc = doc and '<tt>%s<br> </tt>' % doc |
|
837 |
|
838 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc) |
|
839 |
|
840 def formatvalue(self, object): |
|
841 """Format an argument default value as text.""" |
|
842 return self.grey('=' + self.repr(object)) |
|
843 |
|
844 def docroutine(self, object, name=None, mod=None, |
|
845 funcs={}, classes={}, methods={}, cl=None): |
|
846 """Produce HTML documentation for a function or method object.""" |
|
847 realname = object.__name__ |
|
848 name = name or realname |
|
849 anchor = (cl and cl.__name__ or '') + '-' + name |
|
850 note = '' |
|
851 skipdocs = 0 |
|
852 if inspect.ismethod(object): |
|
853 imclass = object.im_class |
|
854 if cl: |
|
855 if imclass is not cl: |
|
856 note = ' from ' + self.classlink(imclass, mod) |
|
857 else: |
|
858 if object.im_self is not None: |
|
859 note = ' method of %s instance' % self.classlink( |
|
860 object.im_self.__class__, mod) |
|
861 else: |
|
862 note = ' unbound %s method' % self.classlink(imclass,mod) |
|
863 object = object.im_func |
|
864 |
|
865 if name == realname: |
|
866 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname) |
|
867 else: |
|
868 if (cl and realname in cl.__dict__ and |
|
869 cl.__dict__[realname] is object): |
|
870 reallink = '<a href="#%s">%s</a>' % ( |
|
871 cl.__name__ + '-' + realname, realname) |
|
872 skipdocs = 1 |
|
873 else: |
|
874 reallink = realname |
|
875 title = '<a name="%s"><strong>%s</strong></a> = %s' % ( |
|
876 anchor, name, reallink) |
|
877 if inspect.isfunction(object): |
|
878 args, varargs, varkw, defaults = inspect.getargspec(object) |
|
879 argspec = inspect.formatargspec( |
|
880 args, varargs, varkw, defaults, formatvalue=self.formatvalue) |
|
881 if realname == '<lambda>': |
|
882 title = '<strong>%s</strong> <em>lambda</em> ' % name |
|
883 argspec = argspec[1:-1] # remove parentheses |
|
884 else: |
|
885 argspec = '(...)' |
|
886 |
|
887 decl = title + argspec + (note and self.grey( |
|
888 '<font face="helvetica, arial">%s</font>' % note)) |
|
889 |
|
890 if skipdocs: |
|
891 return '<dl><dt>%s</dt></dl>\n' % decl |
|
892 else: |
|
893 doc = self.markup( |
|
894 getdoc(object), self.preformat, funcs, classes, methods) |
|
895 doc = doc and '<dd><tt>%s</tt></dd>' % doc |
|
896 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) |
|
897 |
|
898 def _docdescriptor(self, name, value, mod): |
|
899 results = [] |
|
900 push = results.append |
|
901 |
|
902 if name: |
|
903 push('<dl><dt><strong>%s</strong></dt>\n' % name) |
|
904 if value.__doc__ is not None: |
|
905 doc = self.markup(getdoc(value), self.preformat) |
|
906 push('<dd><tt>%s</tt></dd>\n' % doc) |
|
907 push('</dl>\n') |
|
908 |
|
909 return ''.join(results) |
|
910 |
|
911 def docproperty(self, object, name=None, mod=None, cl=None): |
|
912 """Produce html documentation for a property.""" |
|
913 return self._docdescriptor(name, object, mod) |
|
914 |
|
915 def docother(self, object, name=None, mod=None, *ignored): |
|
916 """Produce HTML documentation for a data object.""" |
|
917 lhs = name and '<strong>%s</strong> = ' % name or '' |
|
918 return lhs + self.repr(object) |
|
919 |
|
920 def docdata(self, object, name=None, mod=None, cl=None): |
|
921 """Produce html documentation for a data descriptor.""" |
|
922 return self._docdescriptor(name, object, mod) |
|
923 |
|
924 def index(self, dir, shadowed=None): |
|
925 """Generate an HTML index for a directory of modules.""" |
|
926 modpkgs = [] |
|
927 if shadowed is None: shadowed = {} |
|
928 for importer, name, ispkg in pkgutil.iter_modules([dir]): |
|
929 modpkgs.append((name, '', ispkg, name in shadowed)) |
|
930 shadowed[name] = 1 |
|
931 |
|
932 modpkgs.sort() |
|
933 contents = self.multicolumn(modpkgs, self.modpkglink) |
|
934 return self.bigsection(dir, '#ffffff', '#ee77aa', contents) |
|
935 |
|
936 # -------------------------------------------- text documentation generator |
|
937 |
|
938 class TextRepr(Repr): |
|
939 """Class for safely making a text representation of a Python object.""" |
|
940 def __init__(self): |
|
941 Repr.__init__(self) |
|
942 self.maxlist = self.maxtuple = 20 |
|
943 self.maxdict = 10 |
|
944 self.maxstring = self.maxother = 100 |
|
945 |
|
946 def repr1(self, x, level): |
|
947 if hasattr(type(x), '__name__'): |
|
948 methodname = 'repr_' + join(split(type(x).__name__), '_') |
|
949 if hasattr(self, methodname): |
|
950 return getattr(self, methodname)(x, level) |
|
951 return cram(stripid(repr(x)), self.maxother) |
|
952 |
|
953 def repr_string(self, x, level): |
|
954 test = cram(x, self.maxstring) |
|
955 testrepr = repr(test) |
|
956 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): |
|
957 # Backslashes are only literal in the string and are never |
|
958 # needed to make any special characters, so show a raw string. |
|
959 return 'r' + testrepr[0] + test + testrepr[0] |
|
960 return testrepr |
|
961 |
|
962 repr_str = repr_string |
|
963 |
|
964 def repr_instance(self, x, level): |
|
965 try: |
|
966 return cram(stripid(repr(x)), self.maxstring) |
|
967 except: |
|
968 return '<%s instance>' % x.__class__.__name__ |
|
969 |
|
970 class TextDoc(Doc): |
|
971 """Formatter class for text documentation.""" |
|
972 |
|
973 # ------------------------------------------- text formatting utilities |
|
974 |
|
975 _repr_instance = TextRepr() |
|
976 repr = _repr_instance.repr |
|
977 |
|
978 def bold(self, text): |
|
979 """Format a string in bold by overstriking.""" |
|
980 return join(map(lambda ch: ch + '\b' + ch, text), '') |
|
981 |
|
982 def indent(self, text, prefix=' '): |
|
983 """Indent text by prepending a given prefix to each line.""" |
|
984 if not text: return '' |
|
985 lines = split(text, '\n') |
|
986 lines = map(lambda line, prefix=prefix: prefix + line, lines) |
|
987 if lines: lines[-1] = rstrip(lines[-1]) |
|
988 return join(lines, '\n') |
|
989 |
|
990 def section(self, title, contents): |
|
991 """Format a section with a given heading.""" |
|
992 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n' |
|
993 |
|
994 # ---------------------------------------------- type-specific routines |
|
995 |
|
996 def formattree(self, tree, modname, parent=None, prefix=''): |
|
997 """Render in text a class tree as returned by inspect.getclasstree().""" |
|
998 result = '' |
|
999 for entry in tree: |
|
1000 if type(entry) is type(()): |
|
1001 c, bases = entry |
|
1002 result = result + prefix + classname(c, modname) |
|
1003 if bases and bases != (parent,): |
|
1004 parents = map(lambda c, m=modname: classname(c, m), bases) |
|
1005 result = result + '(%s)' % join(parents, ', ') |
|
1006 result = result + '\n' |
|
1007 elif type(entry) is type([]): |
|
1008 result = result + self.formattree( |
|
1009 entry, modname, c, prefix + ' ') |
|
1010 return result |
|
1011 |
|
1012 def docmodule(self, object, name=None, mod=None): |
|
1013 """Produce text documentation for a given module object.""" |
|
1014 name = object.__name__ # ignore the passed-in name |
|
1015 synop, desc = splitdoc(getdoc(object)) |
|
1016 result = self.section('NAME', name + (synop and ' - ' + synop)) |
|
1017 |
|
1018 try: |
|
1019 all = object.__all__ |
|
1020 except AttributeError: |
|
1021 all = None |
|
1022 |
|
1023 try: |
|
1024 file = inspect.getabsfile(object) |
|
1025 except TypeError: |
|
1026 file = '(built-in)' |
|
1027 result = result + self.section('FILE', file) |
|
1028 |
|
1029 docloc = self.getdocloc(object) |
|
1030 if docloc is not None: |
|
1031 result = result + self.section('MODULE DOCS', docloc) |
|
1032 |
|
1033 if desc: |
|
1034 result = result + self.section('DESCRIPTION', desc) |
|
1035 |
|
1036 classes = [] |
|
1037 for key, value in inspect.getmembers(object, inspect.isclass): |
|
1038 # if __all__ exists, believe it. Otherwise use old heuristic. |
|
1039 if (all is not None |
|
1040 or (inspect.getmodule(value) or object) is object): |
|
1041 if visiblename(key, all): |
|
1042 classes.append((key, value)) |
|
1043 funcs = [] |
|
1044 for key, value in inspect.getmembers(object, inspect.isroutine): |
|
1045 # if __all__ exists, believe it. Otherwise use old heuristic. |
|
1046 if (all is not None or |
|
1047 inspect.isbuiltin(value) or inspect.getmodule(value) is object): |
|
1048 if visiblename(key, all): |
|
1049 funcs.append((key, value)) |
|
1050 data = [] |
|
1051 for key, value in inspect.getmembers(object, isdata): |
|
1052 if visiblename(key, all): |
|
1053 data.append((key, value)) |
|
1054 |
|
1055 if hasattr(object, '__path__'): |
|
1056 modpkgs = [] |
|
1057 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): |
|
1058 if ispkg: |
|
1059 modpkgs.append(modname + ' (package)') |
|
1060 else: |
|
1061 modpkgs.append(modname) |
|
1062 |
|
1063 modpkgs.sort() |
|
1064 result = result + self.section( |
|
1065 'PACKAGE CONTENTS', join(modpkgs, '\n')) |
|
1066 |
|
1067 if classes: |
|
1068 classlist = map(lambda (key, value): value, classes) |
|
1069 contents = [self.formattree( |
|
1070 inspect.getclasstree(classlist, 1), name)] |
|
1071 for key, value in classes: |
|
1072 contents.append(self.document(value, key, name)) |
|
1073 result = result + self.section('CLASSES', join(contents, '\n')) |
|
1074 |
|
1075 if funcs: |
|
1076 contents = [] |
|
1077 for key, value in funcs: |
|
1078 contents.append(self.document(value, key, name)) |
|
1079 result = result + self.section('FUNCTIONS', join(contents, '\n')) |
|
1080 |
|
1081 if data: |
|
1082 contents = [] |
|
1083 for key, value in data: |
|
1084 contents.append(self.docother(value, key, name, maxlen=70)) |
|
1085 result = result + self.section('DATA', join(contents, '\n')) |
|
1086 |
|
1087 if hasattr(object, '__version__'): |
|
1088 version = str(object.__version__) |
|
1089 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': |
|
1090 version = strip(version[11:-1]) |
|
1091 result = result + self.section('VERSION', version) |
|
1092 if hasattr(object, '__date__'): |
|
1093 result = result + self.section('DATE', str(object.__date__)) |
|
1094 if hasattr(object, '__author__'): |
|
1095 result = result + self.section('AUTHOR', str(object.__author__)) |
|
1096 if hasattr(object, '__credits__'): |
|
1097 result = result + self.section('CREDITS', str(object.__credits__)) |
|
1098 return result |
|
1099 |
|
1100 def docclass(self, object, name=None, mod=None): |
|
1101 """Produce text documentation for a given class object.""" |
|
1102 realname = object.__name__ |
|
1103 name = name or realname |
|
1104 bases = object.__bases__ |
|
1105 |
|
1106 def makename(c, m=object.__module__): |
|
1107 return classname(c, m) |
|
1108 |
|
1109 if name == realname: |
|
1110 title = 'class ' + self.bold(realname) |
|
1111 else: |
|
1112 title = self.bold(name) + ' = class ' + realname |
|
1113 if bases: |
|
1114 parents = map(makename, bases) |
|
1115 title = title + '(%s)' % join(parents, ', ') |
|
1116 |
|
1117 doc = getdoc(object) |
|
1118 contents = doc and [doc + '\n'] or [] |
|
1119 push = contents.append |
|
1120 |
|
1121 # List the mro, if non-trivial. |
|
1122 mro = deque(inspect.getmro(object)) |
|
1123 if len(mro) > 2: |
|
1124 push("Method resolution order:") |
|
1125 for base in mro: |
|
1126 push(' ' + makename(base)) |
|
1127 push('') |
|
1128 |
|
1129 # Cute little class to pump out a horizontal rule between sections. |
|
1130 class HorizontalRule: |
|
1131 def __init__(self): |
|
1132 self.needone = 0 |
|
1133 def maybe(self): |
|
1134 if self.needone: |
|
1135 push('-' * 70) |
|
1136 self.needone = 1 |
|
1137 hr = HorizontalRule() |
|
1138 |
|
1139 def spill(msg, attrs, predicate): |
|
1140 ok, attrs = _split_list(attrs, predicate) |
|
1141 if ok: |
|
1142 hr.maybe() |
|
1143 push(msg) |
|
1144 for name, kind, homecls, value in ok: |
|
1145 push(self.document(getattr(object, name), |
|
1146 name, mod, object)) |
|
1147 return attrs |
|
1148 |
|
1149 def spilldescriptors(msg, attrs, predicate): |
|
1150 ok, attrs = _split_list(attrs, predicate) |
|
1151 if ok: |
|
1152 hr.maybe() |
|
1153 push(msg) |
|
1154 for name, kind, homecls, value in ok: |
|
1155 push(self._docdescriptor(name, value, mod)) |
|
1156 return attrs |
|
1157 |
|
1158 def spilldata(msg, attrs, predicate): |
|
1159 ok, attrs = _split_list(attrs, predicate) |
|
1160 if ok: |
|
1161 hr.maybe() |
|
1162 push(msg) |
|
1163 for name, kind, homecls, value in ok: |
|
1164 if callable(value) or inspect.isdatadescriptor(value): |
|
1165 doc = getdoc(value) |
|
1166 else: |
|
1167 doc = None |
|
1168 push(self.docother(getattr(object, name), |
|
1169 name, mod, maxlen=70, doc=doc) + '\n') |
|
1170 return attrs |
|
1171 |
|
1172 attrs = filter(lambda (name, kind, cls, value): visiblename(name), |
|
1173 classify_class_attrs(object)) |
|
1174 while attrs: |
|
1175 if mro: |
|
1176 thisclass = mro.popleft() |
|
1177 else: |
|
1178 thisclass = attrs[0][2] |
|
1179 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) |
|
1180 |
|
1181 if thisclass is __builtin__.object: |
|
1182 attrs = inherited |
|
1183 continue |
|
1184 elif thisclass is object: |
|
1185 tag = "defined here" |
|
1186 else: |
|
1187 tag = "inherited from %s" % classname(thisclass, |
|
1188 object.__module__) |
|
1189 filter(lambda t: not t[0].startswith('_'), attrs) |
|
1190 |
|
1191 # Sort attrs by name. |
|
1192 attrs.sort() |
|
1193 |
|
1194 # Pump out the attrs, segregated by kind. |
|
1195 attrs = spill("Methods %s:\n" % tag, attrs, |
|
1196 lambda t: t[1] == 'method') |
|
1197 attrs = spill("Class methods %s:\n" % tag, attrs, |
|
1198 lambda t: t[1] == 'class method') |
|
1199 attrs = spill("Static methods %s:\n" % tag, attrs, |
|
1200 lambda t: t[1] == 'static method') |
|
1201 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs, |
|
1202 lambda t: t[1] == 'data descriptor') |
|
1203 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs, |
|
1204 lambda t: t[1] == 'data') |
|
1205 assert attrs == [] |
|
1206 attrs = inherited |
|
1207 |
|
1208 contents = '\n'.join(contents) |
|
1209 if not contents: |
|
1210 return title + '\n' |
|
1211 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n' |
|
1212 |
|
1213 def formatvalue(self, object): |
|
1214 """Format an argument default value as text.""" |
|
1215 return '=' + self.repr(object) |
|
1216 |
|
1217 def docroutine(self, object, name=None, mod=None, cl=None): |
|
1218 """Produce text documentation for a function or method object.""" |
|
1219 realname = object.__name__ |
|
1220 name = name or realname |
|
1221 note = '' |
|
1222 skipdocs = 0 |
|
1223 if inspect.ismethod(object): |
|
1224 imclass = object.im_class |
|
1225 if cl: |
|
1226 if imclass is not cl: |
|
1227 note = ' from ' + classname(imclass, mod) |
|
1228 else: |
|
1229 if object.im_self is not None: |
|
1230 note = ' method of %s instance' % classname( |
|
1231 object.im_self.__class__, mod) |
|
1232 else: |
|
1233 note = ' unbound %s method' % classname(imclass,mod) |
|
1234 object = object.im_func |
|
1235 |
|
1236 if name == realname: |
|
1237 title = self.bold(realname) |
|
1238 else: |
|
1239 if (cl and realname in cl.__dict__ and |
|
1240 cl.__dict__[realname] is object): |
|
1241 skipdocs = 1 |
|
1242 title = self.bold(name) + ' = ' + realname |
|
1243 if inspect.isfunction(object): |
|
1244 args, varargs, varkw, defaults = inspect.getargspec(object) |
|
1245 argspec = inspect.formatargspec( |
|
1246 args, varargs, varkw, defaults, formatvalue=self.formatvalue) |
|
1247 if realname == '<lambda>': |
|
1248 title = self.bold(name) + ' lambda ' |
|
1249 argspec = argspec[1:-1] # remove parentheses |
|
1250 else: |
|
1251 argspec = '(...)' |
|
1252 decl = title + argspec + note |
|
1253 |
|
1254 if skipdocs: |
|
1255 return decl + '\n' |
|
1256 else: |
|
1257 doc = getdoc(object) or '' |
|
1258 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n') |
|
1259 |
|
1260 def _docdescriptor(self, name, value, mod): |
|
1261 results = [] |
|
1262 push = results.append |
|
1263 |
|
1264 if name: |
|
1265 push(self.bold(name)) |
|
1266 push('\n') |
|
1267 doc = getdoc(value) or '' |
|
1268 if doc: |
|
1269 push(self.indent(doc)) |
|
1270 push('\n') |
|
1271 return ''.join(results) |
|
1272 |
|
1273 def docproperty(self, object, name=None, mod=None, cl=None): |
|
1274 """Produce text documentation for a property.""" |
|
1275 return self._docdescriptor(name, object, mod) |
|
1276 |
|
1277 def docdata(self, object, name=None, mod=None, cl=None): |
|
1278 """Produce text documentation for a data descriptor.""" |
|
1279 return self._docdescriptor(name, object, mod) |
|
1280 |
|
1281 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None): |
|
1282 """Produce text documentation for a data object.""" |
|
1283 repr = self.repr(object) |
|
1284 if maxlen: |
|
1285 line = (name and name + ' = ' or '') + repr |
|
1286 chop = maxlen - len(line) |
|
1287 if chop < 0: repr = repr[:chop] + '...' |
|
1288 line = (name and self.bold(name) + ' = ' or '') + repr |
|
1289 if doc is not None: |
|
1290 line += '\n' + self.indent(str(doc)) |
|
1291 return line |
|
1292 |
|
1293 # --------------------------------------------------------- user interfaces |
|
1294 |
|
1295 def pager(text): |
|
1296 """The first time this is called, determine what kind of pager to use.""" |
|
1297 global pager |
|
1298 pager = getpager() |
|
1299 pager(text) |
|
1300 |
|
1301 def getpager(): |
|
1302 """Decide what method to use for paging through text.""" |
|
1303 if type(sys.stdout) is not types.FileType: |
|
1304 return plainpager |
|
1305 if not sys.stdin.isatty() or not sys.stdout.isatty(): |
|
1306 return plainpager |
|
1307 if 'PAGER' in os.environ: |
|
1308 if sys.platform == 'win32': # pipes completely broken in Windows |
|
1309 return lambda text: tempfilepager(plain(text), os.environ['PAGER']) |
|
1310 elif os.environ.get('TERM') in ('dumb', 'emacs'): |
|
1311 return lambda text: pipepager(plain(text), os.environ['PAGER']) |
|
1312 else: |
|
1313 return lambda text: pipepager(text, os.environ['PAGER']) |
|
1314 if os.environ.get('TERM') in ('dumb', 'emacs'): |
|
1315 return plainpager |
|
1316 if sys.platform == 'win32' or sys.platform.startswith('os2'): |
|
1317 return lambda text: tempfilepager(plain(text), 'more <') |
|
1318 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: |
|
1319 return lambda text: pipepager(text, 'less') |
|
1320 |
|
1321 import tempfile |
|
1322 (fd, filename) = tempfile.mkstemp() |
|
1323 os.close(fd) |
|
1324 try: |
|
1325 if hasattr(os, 'system') and os.system('more %s' % filename) == 0: |
|
1326 return lambda text: pipepager(text, 'more') |
|
1327 else: |
|
1328 return ttypager |
|
1329 finally: |
|
1330 os.unlink(filename) |
|
1331 |
|
1332 def plain(text): |
|
1333 """Remove boldface formatting from text.""" |
|
1334 return re.sub('.\b', '', text) |
|
1335 |
|
1336 def pipepager(text, cmd): |
|
1337 """Page through text by feeding it to another program.""" |
|
1338 pipe = os.popen(cmd, 'w') |
|
1339 try: |
|
1340 pipe.write(text) |
|
1341 pipe.close() |
|
1342 except IOError: |
|
1343 pass # Ignore broken pipes caused by quitting the pager program. |
|
1344 |
|
1345 def tempfilepager(text, cmd): |
|
1346 """Page through text by invoking a program on a temporary file.""" |
|
1347 import tempfile |
|
1348 filename = tempfile.mktemp() |
|
1349 file = open(filename, 'w') |
|
1350 file.write(text) |
|
1351 file.close() |
|
1352 try: |
|
1353 os.system(cmd + ' ' + filename) |
|
1354 finally: |
|
1355 os.unlink(filename) |
|
1356 |
|
1357 def ttypager(text): |
|
1358 """Page through text on a text terminal.""" |
|
1359 lines = split(plain(text), '\n') |
|
1360 try: |
|
1361 import tty |
|
1362 fd = sys.stdin.fileno() |
|
1363 old = tty.tcgetattr(fd) |
|
1364 tty.setcbreak(fd) |
|
1365 getchar = lambda: sys.stdin.read(1) |
|
1366 except (ImportError, AttributeError): |
|
1367 tty = None |
|
1368 getchar = lambda: sys.stdin.readline()[:-1][:1] |
|
1369 |
|
1370 try: |
|
1371 r = inc = os.environ.get('LINES', 25) - 1 |
|
1372 sys.stdout.write(join(lines[:inc], '\n') + '\n') |
|
1373 while lines[r:]: |
|
1374 sys.stdout.write('-- more --') |
|
1375 sys.stdout.flush() |
|
1376 c = getchar() |
|
1377 |
|
1378 if c in ('q', 'Q'): |
|
1379 sys.stdout.write('\r \r') |
|
1380 break |
|
1381 elif c in ('\r', '\n'): |
|
1382 sys.stdout.write('\r \r' + lines[r] + '\n') |
|
1383 r = r + 1 |
|
1384 continue |
|
1385 if c in ('b', 'B', '\x1b'): |
|
1386 r = r - inc - inc |
|
1387 if r < 0: r = 0 |
|
1388 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n') |
|
1389 r = r + inc |
|
1390 |
|
1391 finally: |
|
1392 if tty: |
|
1393 tty.tcsetattr(fd, tty.TCSAFLUSH, old) |
|
1394 |
|
1395 def plainpager(text): |
|
1396 """Simply print unformatted text. This is the ultimate fallback.""" |
|
1397 sys.stdout.write(plain(text)) |
|
1398 |
|
1399 def describe(thing): |
|
1400 """Produce a short description of the given thing.""" |
|
1401 if inspect.ismodule(thing): |
|
1402 if thing.__name__ in sys.builtin_module_names: |
|
1403 return 'built-in module ' + thing.__name__ |
|
1404 if hasattr(thing, '__path__'): |
|
1405 return 'package ' + thing.__name__ |
|
1406 else: |
|
1407 return 'module ' + thing.__name__ |
|
1408 if inspect.isbuiltin(thing): |
|
1409 return 'built-in function ' + thing.__name__ |
|
1410 if inspect.isgetsetdescriptor(thing): |
|
1411 return 'getset descriptor %s.%s.%s' % ( |
|
1412 thing.__objclass__.__module__, thing.__objclass__.__name__, |
|
1413 thing.__name__) |
|
1414 if inspect.ismemberdescriptor(thing): |
|
1415 return 'member descriptor %s.%s.%s' % ( |
|
1416 thing.__objclass__.__module__, thing.__objclass__.__name__, |
|
1417 thing.__name__) |
|
1418 if inspect.isclass(thing): |
|
1419 return 'class ' + thing.__name__ |
|
1420 if inspect.isfunction(thing): |
|
1421 return 'function ' + thing.__name__ |
|
1422 if inspect.ismethod(thing): |
|
1423 return 'method ' + thing.__name__ |
|
1424 if type(thing) is types.InstanceType: |
|
1425 return 'instance of ' + thing.__class__.__name__ |
|
1426 return type(thing).__name__ |
|
1427 |
|
1428 def locate(path, forceload=0): |
|
1429 """Locate an object by name or dotted path, importing as necessary.""" |
|
1430 parts = [part for part in split(path, '.') if part] |
|
1431 module, n = None, 0 |
|
1432 while n < len(parts): |
|
1433 nextmodule = safeimport(join(parts[:n+1], '.'), forceload) |
|
1434 if nextmodule: module, n = nextmodule, n + 1 |
|
1435 else: break |
|
1436 if module: |
|
1437 object = module |
|
1438 for part in parts[n:]: |
|
1439 try: object = getattr(object, part) |
|
1440 except AttributeError: return None |
|
1441 return object |
|
1442 else: |
|
1443 if hasattr(__builtin__, path): |
|
1444 return getattr(__builtin__, path) |
|
1445 |
|
1446 # --------------------------------------- interactive interpreter interface |
|
1447 |
|
1448 text = TextDoc() |
|
1449 html = HTMLDoc() |
|
1450 |
|
1451 def resolve(thing, forceload=0): |
|
1452 """Given an object or a path to an object, get the object and its name.""" |
|
1453 if isinstance(thing, str): |
|
1454 object = locate(thing, forceload) |
|
1455 if not object: |
|
1456 raise ImportError, 'no Python documentation found for %r' % thing |
|
1457 return object, thing |
|
1458 else: |
|
1459 return thing, getattr(thing, '__name__', None) |
|
1460 |
|
1461 def doc(thing, title='Python Library Documentation: %s', forceload=0): |
|
1462 """Display text documentation, given an object or a path to an object.""" |
|
1463 try: |
|
1464 object, name = resolve(thing, forceload) |
|
1465 desc = describe(object) |
|
1466 module = inspect.getmodule(object) |
|
1467 if name and '.' in name: |
|
1468 desc += ' in ' + name[:name.rfind('.')] |
|
1469 elif module and module is not object: |
|
1470 desc += ' in module ' + module.__name__ |
|
1471 if not (inspect.ismodule(object) or |
|
1472 inspect.isclass(object) or |
|
1473 inspect.isroutine(object) or |
|
1474 inspect.isgetsetdescriptor(object) or |
|
1475 inspect.ismemberdescriptor(object) or |
|
1476 isinstance(object, property)): |
|
1477 # If the passed object is a piece of data or an instance, |
|
1478 # document its available methods instead of its value. |
|
1479 object = type(object) |
|
1480 desc += ' object' |
|
1481 pager(title % desc + '\n\n' + text.document(object, name)) |
|
1482 except (ImportError, ErrorDuringImport), value: |
|
1483 print value |
|
1484 |
|
1485 def writedoc(thing, forceload=0): |
|
1486 """Write HTML documentation to a file in the current directory.""" |
|
1487 try: |
|
1488 object, name = resolve(thing, forceload) |
|
1489 page = html.page(describe(object), html.document(object, name)) |
|
1490 file = open(name + '.html', 'w') |
|
1491 file.write(page) |
|
1492 file.close() |
|
1493 print 'wrote', name + '.html' |
|
1494 except (ImportError, ErrorDuringImport), value: |
|
1495 print value |
|
1496 |
|
1497 def writedocs(dir, pkgpath='', done=None): |
|
1498 """Write out HTML documentation for all modules in a directory tree.""" |
|
1499 if done is None: done = {} |
|
1500 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): |
|
1501 writedoc(modname) |
|
1502 return |
|
1503 |
|
1504 class Helper: |
|
1505 keywords = { |
|
1506 'and': 'BOOLEAN', |
|
1507 'as': 'with', |
|
1508 'assert': ('ref/assert', ''), |
|
1509 'break': ('ref/break', 'while for'), |
|
1510 'class': ('ref/class', 'CLASSES SPECIALMETHODS'), |
|
1511 'continue': ('ref/continue', 'while for'), |
|
1512 'def': ('ref/function', ''), |
|
1513 'del': ('ref/del', 'BASICMETHODS'), |
|
1514 'elif': 'if', |
|
1515 'else': ('ref/if', 'while for'), |
|
1516 'except': 'try', |
|
1517 'exec': ('ref/exec', ''), |
|
1518 'finally': 'try', |
|
1519 'for': ('ref/for', 'break continue while'), |
|
1520 'from': 'import', |
|
1521 'global': ('ref/global', 'NAMESPACES'), |
|
1522 'if': ('ref/if', 'TRUTHVALUE'), |
|
1523 'import': ('ref/import', 'MODULES'), |
|
1524 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'), |
|
1525 'is': 'COMPARISON', |
|
1526 'lambda': ('ref/lambdas', 'FUNCTIONS'), |
|
1527 'not': 'BOOLEAN', |
|
1528 'or': 'BOOLEAN', |
|
1529 'pass': ('ref/pass', ''), |
|
1530 'print': ('ref/print', ''), |
|
1531 'raise': ('ref/raise', 'EXCEPTIONS'), |
|
1532 'return': ('ref/return', 'FUNCTIONS'), |
|
1533 'try': ('ref/try', 'EXCEPTIONS'), |
|
1534 'while': ('ref/while', 'break continue if TRUTHVALUE'), |
|
1535 'with': ('ref/with', 'CONTEXTMANAGERS EXCEPTIONS yield'), |
|
1536 'yield': ('ref/yield', ''), |
|
1537 } |
|
1538 |
|
1539 topics = { |
|
1540 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'), |
|
1541 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'), |
|
1542 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'), |
|
1543 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'), |
|
1544 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'), |
|
1545 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'), |
|
1546 'INTEGER': ('ref/integers', 'int range'), |
|
1547 'FLOAT': ('ref/floating', 'float math'), |
|
1548 'COMPLEX': ('ref/imaginary', 'complex cmath'), |
|
1549 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), |
|
1550 'MAPPINGS': 'DICTIONARIES', |
|
1551 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'), |
|
1552 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'), |
|
1553 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'), |
|
1554 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'), |
|
1555 'FRAMEOBJECTS': 'TYPES', |
|
1556 'TRACEBACKS': 'TYPES', |
|
1557 'NONE': ('lib/bltin-null-object', ''), |
|
1558 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'), |
|
1559 'FILES': ('lib/bltin-file-objects', ''), |
|
1560 'SPECIALATTRIBUTES': ('lib/specialattrs', ''), |
|
1561 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'), |
|
1562 'MODULES': ('lib/typesmodules', 'import'), |
|
1563 'PACKAGES': 'import', |
|
1564 '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'), |
|
1565 'OPERATORS': 'EXPRESSIONS', |
|
1566 'PRECEDENCE': 'EXPRESSIONS', |
|
1567 'OBJECTS': ('ref/objects', 'TYPES'), |
|
1568 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), |
|
1569 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'), |
|
1570 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'), |
|
1571 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'), |
|
1572 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'), |
|
1573 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'), |
|
1574 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'), |
|
1575 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'), |
|
1576 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), |
|
1577 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), |
|
1578 'DYNAMICFEATURES': ('ref/dynamic-features', ''), |
|
1579 'SCOPING': 'NAMESPACES', |
|
1580 'FRAMES': 'NAMESPACES', |
|
1581 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'), |
|
1582 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'), |
|
1583 'CONVERSIONS': ('ref/conversions', 'COERCIONS'), |
|
1584 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'), |
|
1585 'SPECIALIDENTIFIERS': ('ref/id-classes', ''), |
|
1586 'PRIVATENAMES': ('ref/atom-identifiers', ''), |
|
1587 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), |
|
1588 'TUPLES': 'SEQUENCES', |
|
1589 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'), |
|
1590 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'), |
|
1591 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'), |
|
1592 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'), |
|
1593 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'), |
|
1594 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'), |
|
1595 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), |
|
1596 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'), |
|
1597 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'), |
|
1598 'CALLS': ('ref/calls', 'EXPRESSIONS'), |
|
1599 'POWER': ('ref/power', 'EXPRESSIONS'), |
|
1600 'UNARY': ('ref/unary', 'EXPRESSIONS'), |
|
1601 'BINARY': ('ref/binary', 'EXPRESSIONS'), |
|
1602 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'), |
|
1603 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'), |
|
1604 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'), |
|
1605 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'), |
|
1606 'ASSERTION': 'assert', |
|
1607 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'), |
|
1608 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'), |
|
1609 'DELETION': 'del', |
|
1610 'PRINTING': 'print', |
|
1611 'RETURNING': 'return', |
|
1612 'IMPORTING': 'import', |
|
1613 'CONDITIONAL': 'if', |
|
1614 'LOOPING': ('ref/compound', 'for while break continue'), |
|
1615 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'), |
|
1616 'DEBUGGING': ('lib/module-pdb', 'pdb'), |
|
1617 'CONTEXTMANAGERS': ('ref/context-managers', 'with'), |
|
1618 } |
|
1619 |
|
1620 def __init__(self, input, output): |
|
1621 self.input = input |
|
1622 self.output = output |
|
1623 self.docdir = None |
|
1624 execdir = os.path.dirname(sys.executable) |
|
1625 homedir = os.environ.get('PYTHONHOME') |
|
1626 for dir in [os.environ.get('PYTHONDOCS'), |
|
1627 homedir and os.path.join(homedir, 'doc'), |
|
1628 os.path.join(execdir, 'doc'), |
|
1629 '/usr/doc/python-docs-' + split(sys.version)[0], |
|
1630 '/usr/doc/python-' + split(sys.version)[0], |
|
1631 '/usr/doc/python-docs-' + sys.version[:3], |
|
1632 '/usr/doc/python-' + sys.version[:3], |
|
1633 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]: |
|
1634 if dir and os.path.isdir(os.path.join(dir, 'lib')): |
|
1635 self.docdir = dir |
|
1636 |
|
1637 def __repr__(self): |
|
1638 if inspect.stack()[1][3] == '?': |
|
1639 self() |
|
1640 return '' |
|
1641 return '<pydoc.Helper instance>' |
|
1642 |
|
1643 def __call__(self, request=None): |
|
1644 if request is not None: |
|
1645 self.help(request) |
|
1646 else: |
|
1647 self.intro() |
|
1648 self.interact() |
|
1649 self.output.write(''' |
|
1650 You are now leaving help and returning to the Python interpreter. |
|
1651 If you want to ask for help on a particular object directly from the |
|
1652 interpreter, you can type "help(object)". Executing "help('string')" |
|
1653 has the same effect as typing a particular string at the help> prompt. |
|
1654 ''') |
|
1655 |
|
1656 def interact(self): |
|
1657 self.output.write('\n') |
|
1658 while True: |
|
1659 try: |
|
1660 request = self.getline('help> ') |
|
1661 if not request: break |
|
1662 except (KeyboardInterrupt, EOFError): |
|
1663 break |
|
1664 request = strip(replace(request, '"', '', "'", '')) |
|
1665 if lower(request) in ('q', 'quit'): break |
|
1666 self.help(request) |
|
1667 |
|
1668 def getline(self, prompt): |
|
1669 """Read one line, using raw_input when available.""" |
|
1670 if self.input is sys.stdin: |
|
1671 return raw_input(prompt) |
|
1672 else: |
|
1673 self.output.write(prompt) |
|
1674 self.output.flush() |
|
1675 return self.input.readline() |
|
1676 |
|
1677 def help(self, request): |
|
1678 if type(request) is type(''): |
|
1679 if request == 'help': self.intro() |
|
1680 elif request == 'keywords': self.listkeywords() |
|
1681 elif request == 'topics': self.listtopics() |
|
1682 elif request == 'modules': self.listmodules() |
|
1683 elif request[:8] == 'modules ': |
|
1684 self.listmodules(split(request)[1]) |
|
1685 elif request in self.keywords: self.showtopic(request) |
|
1686 elif request in self.topics: self.showtopic(request) |
|
1687 elif request: doc(request, 'Help on %s:') |
|
1688 elif isinstance(request, Helper): self() |
|
1689 else: doc(request, 'Help on %s:') |
|
1690 self.output.write('\n') |
|
1691 |
|
1692 def intro(self): |
|
1693 self.output.write(''' |
|
1694 Welcome to Python %s! This is the online help utility. |
|
1695 |
|
1696 If this is your first time using Python, you should definitely check out |
|
1697 the tutorial on the Internet at http://www.python.org/doc/tut/. |
|
1698 |
|
1699 Enter the name of any module, keyword, or topic to get help on writing |
|
1700 Python programs and using Python modules. To quit this help utility and |
|
1701 return to the interpreter, just type "quit". |
|
1702 |
|
1703 To get a list of available modules, keywords, or topics, type "modules", |
|
1704 "keywords", or "topics". Each module also comes with a one-line summary |
|
1705 of what it does; to list the modules whose summaries contain a given word |
|
1706 such as "spam", type "modules spam". |
|
1707 ''' % sys.version[:3]) |
|
1708 |
|
1709 def list(self, items, columns=4, width=80): |
|
1710 items = items[:] |
|
1711 items.sort() |
|
1712 colw = width / columns |
|
1713 rows = (len(items) + columns - 1) / columns |
|
1714 for row in range(rows): |
|
1715 for col in range(columns): |
|
1716 i = col * rows + row |
|
1717 if i < len(items): |
|
1718 self.output.write(items[i]) |
|
1719 if col < columns - 1: |
|
1720 self.output.write(' ' + ' ' * (colw-1 - len(items[i]))) |
|
1721 self.output.write('\n') |
|
1722 |
|
1723 def listkeywords(self): |
|
1724 self.output.write(''' |
|
1725 Here is a list of the Python keywords. Enter any keyword to get more help. |
|
1726 |
|
1727 ''') |
|
1728 self.list(self.keywords.keys()) |
|
1729 |
|
1730 def listtopics(self): |
|
1731 self.output.write(''' |
|
1732 Here is a list of available topics. Enter any topic name to get more help. |
|
1733 |
|
1734 ''') |
|
1735 self.list(self.topics.keys()) |
|
1736 |
|
1737 def showtopic(self, topic): |
|
1738 if not self.docdir: |
|
1739 self.output.write(''' |
|
1740 Sorry, topic and keyword documentation is not available because the Python |
|
1741 HTML documentation files could not be found. If you have installed them, |
|
1742 please set the environment variable PYTHONDOCS to indicate their location. |
|
1743 |
|
1744 On the Microsoft Windows operating system, the files can be built by |
|
1745 running "hh -decompile . PythonNN.chm" in the C:\PythonNN\Doc> directory. |
|
1746 ''') |
|
1747 return |
|
1748 target = self.topics.get(topic, self.keywords.get(topic)) |
|
1749 if not target: |
|
1750 self.output.write('no documentation found for %s\n' % repr(topic)) |
|
1751 return |
|
1752 if type(target) is type(''): |
|
1753 return self.showtopic(target) |
|
1754 |
|
1755 filename, xrefs = target |
|
1756 filename = self.docdir + '/' + filename + '.html' |
|
1757 try: |
|
1758 file = open(filename) |
|
1759 except: |
|
1760 self.output.write('could not read docs from %s\n' % filename) |
|
1761 return |
|
1762 |
|
1763 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S) |
|
1764 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S) |
|
1765 document = re.sub(addrpat, '', re.sub(divpat, '', file.read())) |
|
1766 file.close() |
|
1767 |
|
1768 import htmllib, formatter, StringIO |
|
1769 buffer = StringIO.StringIO() |
|
1770 parser = htmllib.HTMLParser( |
|
1771 formatter.AbstractFormatter(formatter.DumbWriter(buffer))) |
|
1772 parser.start_table = parser.do_p |
|
1773 parser.end_table = lambda parser=parser: parser.do_p({}) |
|
1774 parser.start_tr = parser.do_br |
|
1775 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t') |
|
1776 parser.feed(document) |
|
1777 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ') |
|
1778 pager(' ' + strip(buffer) + '\n') |
|
1779 if xrefs: |
|
1780 buffer = StringIO.StringIO() |
|
1781 formatter.DumbWriter(buffer).send_flowing_data( |
|
1782 'Related help topics: ' + join(split(xrefs), ', ') + '\n') |
|
1783 self.output.write('\n%s\n' % buffer.getvalue()) |
|
1784 |
|
1785 def listmodules(self, key=''): |
|
1786 if key: |
|
1787 self.output.write(''' |
|
1788 Here is a list of matching modules. Enter any module name to get more help. |
|
1789 |
|
1790 ''') |
|
1791 apropos(key) |
|
1792 else: |
|
1793 self.output.write(''' |
|
1794 Please wait a moment while I gather a list of all available modules... |
|
1795 |
|
1796 ''') |
|
1797 modules = {} |
|
1798 def callback(path, modname, desc, modules=modules): |
|
1799 if modname and modname[-9:] == '.__init__': |
|
1800 modname = modname[:-9] + ' (package)' |
|
1801 if find(modname, '.') < 0: |
|
1802 modules[modname] = 1 |
|
1803 ModuleScanner().run(callback) |
|
1804 self.list(modules.keys()) |
|
1805 self.output.write(''' |
|
1806 Enter any module name to get more help. Or, type "modules spam" to search |
|
1807 for modules whose descriptions contain the word "spam". |
|
1808 ''') |
|
1809 |
|
1810 help = Helper(sys.stdin, sys.stdout) |
|
1811 |
|
1812 class Scanner: |
|
1813 """A generic tree iterator.""" |
|
1814 def __init__(self, roots, children, descendp): |
|
1815 self.roots = roots[:] |
|
1816 self.state = [] |
|
1817 self.children = children |
|
1818 self.descendp = descendp |
|
1819 |
|
1820 def next(self): |
|
1821 if not self.state: |
|
1822 if not self.roots: |
|
1823 return None |
|
1824 root = self.roots.pop(0) |
|
1825 self.state = [(root, self.children(root))] |
|
1826 node, children = self.state[-1] |
|
1827 if not children: |
|
1828 self.state.pop() |
|
1829 return self.next() |
|
1830 child = children.pop(0) |
|
1831 if self.descendp(child): |
|
1832 self.state.append((child, self.children(child))) |
|
1833 return child |
|
1834 |
|
1835 |
|
1836 class ModuleScanner: |
|
1837 """An interruptible scanner that searches module synopses.""" |
|
1838 |
|
1839 def run(self, callback, key=None, completer=None): |
|
1840 if key: key = lower(key) |
|
1841 self.quit = False |
|
1842 seen = {} |
|
1843 |
|
1844 for modname in sys.builtin_module_names: |
|
1845 if modname != '__main__': |
|
1846 seen[modname] = 1 |
|
1847 if key is None: |
|
1848 callback(None, modname, '') |
|
1849 else: |
|
1850 desc = split(__import__(modname).__doc__ or '', '\n')[0] |
|
1851 if find(lower(modname + ' - ' + desc), key) >= 0: |
|
1852 callback(None, modname, desc) |
|
1853 |
|
1854 for importer, modname, ispkg in pkgutil.walk_packages(): |
|
1855 if self.quit: |
|
1856 break |
|
1857 if key is None: |
|
1858 callback(None, modname, '') |
|
1859 else: |
|
1860 loader = importer.find_module(modname) |
|
1861 if hasattr(loader,'get_source'): |
|
1862 import StringIO |
|
1863 desc = source_synopsis( |
|
1864 StringIO.StringIO(loader.get_source(modname)) |
|
1865 ) or '' |
|
1866 if hasattr(loader,'get_filename'): |
|
1867 path = loader.get_filename(modname) |
|
1868 else: |
|
1869 path = None |
|
1870 else: |
|
1871 module = loader.load_module(modname) |
|
1872 desc = (module.__doc__ or '').splitlines()[0] |
|
1873 path = getattr(module,'__file__',None) |
|
1874 if find(lower(modname + ' - ' + desc), key) >= 0: |
|
1875 callback(path, modname, desc) |
|
1876 |
|
1877 if completer: |
|
1878 completer() |
|
1879 |
|
1880 def apropos(key): |
|
1881 """Print all the one-line module summaries that contain a substring.""" |
|
1882 def callback(path, modname, desc): |
|
1883 if modname[-9:] == '.__init__': |
|
1884 modname = modname[:-9] + ' (package)' |
|
1885 print modname, desc and '- ' + desc |
|
1886 try: import warnings |
|
1887 except ImportError: pass |
|
1888 else: warnings.filterwarnings('ignore') # ignore problems during import |
|
1889 ModuleScanner().run(callback, key) |
|
1890 |
|
1891 # --------------------------------------------------- web browser interface |
|
1892 |
|
1893 def serve(port, callback=None, completer=None): |
|
1894 import BaseHTTPServer, mimetools, select |
|
1895 |
|
1896 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. |
|
1897 class Message(mimetools.Message): |
|
1898 def __init__(self, fp, seekable=1): |
|
1899 Message = self.__class__ |
|
1900 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) |
|
1901 self.encodingheader = self.getheader('content-transfer-encoding') |
|
1902 self.typeheader = self.getheader('content-type') |
|
1903 self.parsetype() |
|
1904 self.parseplist() |
|
1905 |
|
1906 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
|
1907 def send_document(self, title, contents): |
|
1908 try: |
|
1909 self.send_response(200) |
|
1910 self.send_header('Content-Type', 'text/html') |
|
1911 self.end_headers() |
|
1912 self.wfile.write(html.page(title, contents)) |
|
1913 except IOError: pass |
|
1914 |
|
1915 def do_GET(self): |
|
1916 path = self.path |
|
1917 if path[-5:] == '.html': path = path[:-5] |
|
1918 if path[:1] == '/': path = path[1:] |
|
1919 if path and path != '.': |
|
1920 try: |
|
1921 obj = locate(path, forceload=1) |
|
1922 except ErrorDuringImport, value: |
|
1923 self.send_document(path, html.escape(str(value))) |
|
1924 return |
|
1925 if obj: |
|
1926 self.send_document(describe(obj), html.document(obj, path)) |
|
1927 else: |
|
1928 self.send_document(path, |
|
1929 'no Python documentation found for %s' % repr(path)) |
|
1930 else: |
|
1931 heading = html.heading( |
|
1932 '<big><big><strong>Python: Index of Modules</strong></big></big>', |
|
1933 '#ffffff', '#7799ee') |
|
1934 def bltinlink(name): |
|
1935 return '<a href="%s.html">%s</a>' % (name, name) |
|
1936 names = filter(lambda x: x != '__main__', |
|
1937 sys.builtin_module_names) |
|
1938 contents = html.multicolumn(names, bltinlink) |
|
1939 indices = ['<p>' + html.bigsection( |
|
1940 'Built-in Modules', '#ffffff', '#ee77aa', contents)] |
|
1941 |
|
1942 seen = {} |
|
1943 for dir in sys.path: |
|
1944 indices.append(html.index(dir, seen)) |
|
1945 contents = heading + join(indices) + '''<p align=right> |
|
1946 <font color="#909090" face="helvetica, arial"><strong> |
|
1947 pydoc</strong> by Ka-Ping Yee <ping@lfw.org></font>''' |
|
1948 self.send_document('Index of Modules', contents) |
|
1949 |
|
1950 def log_message(self, *args): pass |
|
1951 |
|
1952 class DocServer(BaseHTTPServer.HTTPServer): |
|
1953 def __init__(self, port, callback): |
|
1954 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost' |
|
1955 self.address = ('', port) |
|
1956 self.url = 'http://%s:%d/' % (host, port) |
|
1957 self.callback = callback |
|
1958 self.base.__init__(self, self.address, self.handler) |
|
1959 |
|
1960 def serve_until_quit(self): |
|
1961 import select |
|
1962 self.quit = False |
|
1963 while not self.quit: |
|
1964 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) |
|
1965 if rd: self.handle_request() |
|
1966 |
|
1967 def server_activate(self): |
|
1968 self.base.server_activate(self) |
|
1969 if self.callback: self.callback(self) |
|
1970 |
|
1971 DocServer.base = BaseHTTPServer.HTTPServer |
|
1972 DocServer.handler = DocHandler |
|
1973 DocHandler.MessageClass = Message |
|
1974 try: |
|
1975 try: |
|
1976 DocServer(port, callback).serve_until_quit() |
|
1977 except (KeyboardInterrupt, select.error): |
|
1978 pass |
|
1979 finally: |
|
1980 if completer: completer() |
|
1981 |
|
1982 # ----------------------------------------------------- graphical interface |
|
1983 |
|
1984 def gui(): |
|
1985 """Graphical interface (starts web server and pops up a control window).""" |
|
1986 class GUI: |
|
1987 def __init__(self, window, port=7464): |
|
1988 self.window = window |
|
1989 self.server = None |
|
1990 self.scanner = None |
|
1991 |
|
1992 import Tkinter |
|
1993 self.server_frm = Tkinter.Frame(window) |
|
1994 self.title_lbl = Tkinter.Label(self.server_frm, |
|
1995 text='Starting server...\n ') |
|
1996 self.open_btn = Tkinter.Button(self.server_frm, |
|
1997 text='open browser', command=self.open, state='disabled') |
|
1998 self.quit_btn = Tkinter.Button(self.server_frm, |
|
1999 text='quit serving', command=self.quit, state='disabled') |
|
2000 |
|
2001 self.search_frm = Tkinter.Frame(window) |
|
2002 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for') |
|
2003 self.search_ent = Tkinter.Entry(self.search_frm) |
|
2004 self.search_ent.bind('<Return>', self.search) |
|
2005 self.stop_btn = Tkinter.Button(self.search_frm, |
|
2006 text='stop', pady=0, command=self.stop, state='disabled') |
|
2007 if sys.platform == 'win32': |
|
2008 # Trying to hide and show this button crashes under Windows. |
|
2009 self.stop_btn.pack(side='right') |
|
2010 |
|
2011 self.window.title('pydoc') |
|
2012 self.window.protocol('WM_DELETE_WINDOW', self.quit) |
|
2013 self.title_lbl.pack(side='top', fill='x') |
|
2014 self.open_btn.pack(side='left', fill='x', expand=1) |
|
2015 self.quit_btn.pack(side='right', fill='x', expand=1) |
|
2016 self.server_frm.pack(side='top', fill='x') |
|
2017 |
|
2018 self.search_lbl.pack(side='left') |
|
2019 self.search_ent.pack(side='right', fill='x', expand=1) |
|
2020 self.search_frm.pack(side='top', fill='x') |
|
2021 self.search_ent.focus_set() |
|
2022 |
|
2023 font = ('helvetica', sys.platform == 'win32' and 8 or 10) |
|
2024 self.result_lst = Tkinter.Listbox(window, font=font, height=6) |
|
2025 self.result_lst.bind('<Button-1>', self.select) |
|
2026 self.result_lst.bind('<Double-Button-1>', self.goto) |
|
2027 self.result_scr = Tkinter.Scrollbar(window, |
|
2028 orient='vertical', command=self.result_lst.yview) |
|
2029 self.result_lst.config(yscrollcommand=self.result_scr.set) |
|
2030 |
|
2031 self.result_frm = Tkinter.Frame(window) |
|
2032 self.goto_btn = Tkinter.Button(self.result_frm, |
|
2033 text='go to selected', command=self.goto) |
|
2034 self.hide_btn = Tkinter.Button(self.result_frm, |
|
2035 text='hide results', command=self.hide) |
|
2036 self.goto_btn.pack(side='left', fill='x', expand=1) |
|
2037 self.hide_btn.pack(side='right', fill='x', expand=1) |
|
2038 |
|
2039 self.window.update() |
|
2040 self.minwidth = self.window.winfo_width() |
|
2041 self.minheight = self.window.winfo_height() |
|
2042 self.bigminheight = (self.server_frm.winfo_reqheight() + |
|
2043 self.search_frm.winfo_reqheight() + |
|
2044 self.result_lst.winfo_reqheight() + |
|
2045 self.result_frm.winfo_reqheight()) |
|
2046 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight |
|
2047 self.expanded = 0 |
|
2048 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) |
|
2049 self.window.wm_minsize(self.minwidth, self.minheight) |
|
2050 self.window.tk.willdispatch() |
|
2051 |
|
2052 import threading |
|
2053 threading.Thread( |
|
2054 target=serve, args=(port, self.ready, self.quit)).start() |
|
2055 |
|
2056 def ready(self, server): |
|
2057 self.server = server |
|
2058 self.title_lbl.config( |
|
2059 text='Python documentation server at\n' + server.url) |
|
2060 self.open_btn.config(state='normal') |
|
2061 self.quit_btn.config(state='normal') |
|
2062 |
|
2063 def open(self, event=None, url=None): |
|
2064 url = url or self.server.url |
|
2065 try: |
|
2066 import webbrowser |
|
2067 webbrowser.open(url) |
|
2068 except ImportError: # pre-webbrowser.py compatibility |
|
2069 if sys.platform == 'win32': |
|
2070 os.system('start "%s"' % url) |
|
2071 elif sys.platform == 'mac': |
|
2072 try: import ic |
|
2073 except ImportError: pass |
|
2074 else: ic.launchurl(url) |
|
2075 else: |
|
2076 rc = os.system('netscape -remote "openURL(%s)" &' % url) |
|
2077 if rc: os.system('netscape "%s" &' % url) |
|
2078 |
|
2079 def quit(self, event=None): |
|
2080 if self.server: |
|
2081 self.server.quit = 1 |
|
2082 self.window.quit() |
|
2083 |
|
2084 def search(self, event=None): |
|
2085 key = self.search_ent.get() |
|
2086 self.stop_btn.pack(side='right') |
|
2087 self.stop_btn.config(state='normal') |
|
2088 self.search_lbl.config(text='Searching for "%s"...' % key) |
|
2089 self.search_ent.forget() |
|
2090 self.search_lbl.pack(side='left') |
|
2091 self.result_lst.delete(0, 'end') |
|
2092 self.goto_btn.config(state='disabled') |
|
2093 self.expand() |
|
2094 |
|
2095 import threading |
|
2096 if self.scanner: |
|
2097 self.scanner.quit = 1 |
|
2098 self.scanner = ModuleScanner() |
|
2099 threading.Thread(target=self.scanner.run, |
|
2100 args=(self.update, key, self.done)).start() |
|
2101 |
|
2102 def update(self, path, modname, desc): |
|
2103 if modname[-9:] == '.__init__': |
|
2104 modname = modname[:-9] + ' (package)' |
|
2105 self.result_lst.insert('end', |
|
2106 modname + ' - ' + (desc or '(no description)')) |
|
2107 |
|
2108 def stop(self, event=None): |
|
2109 if self.scanner: |
|
2110 self.scanner.quit = 1 |
|
2111 self.scanner = None |
|
2112 |
|
2113 def done(self): |
|
2114 self.scanner = None |
|
2115 self.search_lbl.config(text='Search for') |
|
2116 self.search_lbl.pack(side='left') |
|
2117 self.search_ent.pack(side='right', fill='x', expand=1) |
|
2118 if sys.platform != 'win32': self.stop_btn.forget() |
|
2119 self.stop_btn.config(state='disabled') |
|
2120 |
|
2121 def select(self, event=None): |
|
2122 self.goto_btn.config(state='normal') |
|
2123 |
|
2124 def goto(self, event=None): |
|
2125 selection = self.result_lst.curselection() |
|
2126 if selection: |
|
2127 modname = split(self.result_lst.get(selection[0]))[0] |
|
2128 self.open(url=self.server.url + modname + '.html') |
|
2129 |
|
2130 def collapse(self): |
|
2131 if not self.expanded: return |
|
2132 self.result_frm.forget() |
|
2133 self.result_scr.forget() |
|
2134 self.result_lst.forget() |
|
2135 self.bigwidth = self.window.winfo_width() |
|
2136 self.bigheight = self.window.winfo_height() |
|
2137 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) |
|
2138 self.window.wm_minsize(self.minwidth, self.minheight) |
|
2139 self.expanded = 0 |
|
2140 |
|
2141 def expand(self): |
|
2142 if self.expanded: return |
|
2143 self.result_frm.pack(side='bottom', fill='x') |
|
2144 self.result_scr.pack(side='right', fill='y') |
|
2145 self.result_lst.pack(side='top', fill='both', expand=1) |
|
2146 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight)) |
|
2147 self.window.wm_minsize(self.minwidth, self.bigminheight) |
|
2148 self.expanded = 1 |
|
2149 |
|
2150 def hide(self, event=None): |
|
2151 self.stop() |
|
2152 self.collapse() |
|
2153 |
|
2154 import Tkinter |
|
2155 try: |
|
2156 root = Tkinter.Tk() |
|
2157 # Tk will crash if pythonw.exe has an XP .manifest |
|
2158 # file and the root has is not destroyed explicitly. |
|
2159 # If the problem is ever fixed in Tk, the explicit |
|
2160 # destroy can go. |
|
2161 try: |
|
2162 gui = GUI(root) |
|
2163 root.mainloop() |
|
2164 finally: |
|
2165 root.destroy() |
|
2166 except KeyboardInterrupt: |
|
2167 pass |
|
2168 |
|
2169 # -------------------------------------------------- command-line interface |
|
2170 |
|
2171 def ispath(x): |
|
2172 return isinstance(x, str) and find(x, os.sep) >= 0 |
|
2173 |
|
2174 def cli(): |
|
2175 """Command-line interface (looks at sys.argv to decide what to do).""" |
|
2176 import getopt |
|
2177 class BadUsage: pass |
|
2178 |
|
2179 # Scripts don't get the current directory in their path by default. |
|
2180 scriptdir = os.path.dirname(sys.argv[0]) |
|
2181 if scriptdir in sys.path: |
|
2182 sys.path.remove(scriptdir) |
|
2183 sys.path.insert(0, '.') |
|
2184 |
|
2185 try: |
|
2186 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w') |
|
2187 writing = 0 |
|
2188 |
|
2189 for opt, val in opts: |
|
2190 if opt == '-g': |
|
2191 gui() |
|
2192 return |
|
2193 if opt == '-k': |
|
2194 apropos(val) |
|
2195 return |
|
2196 if opt == '-p': |
|
2197 try: |
|
2198 port = int(val) |
|
2199 except ValueError: |
|
2200 raise BadUsage |
|
2201 def ready(server): |
|
2202 print 'pydoc server ready at %s' % server.url |
|
2203 def stopped(): |
|
2204 print 'pydoc server stopped' |
|
2205 serve(port, ready, stopped) |
|
2206 return |
|
2207 if opt == '-w': |
|
2208 writing = 1 |
|
2209 |
|
2210 if not args: raise BadUsage |
|
2211 for arg in args: |
|
2212 if ispath(arg) and not os.path.exists(arg): |
|
2213 print 'file %r does not exist' % arg |
|
2214 break |
|
2215 try: |
|
2216 if ispath(arg) and os.path.isfile(arg): |
|
2217 arg = importfile(arg) |
|
2218 if writing: |
|
2219 if ispath(arg) and os.path.isdir(arg): |
|
2220 writedocs(arg) |
|
2221 else: |
|
2222 writedoc(arg) |
|
2223 else: |
|
2224 help.help(arg) |
|
2225 except ErrorDuringImport, value: |
|
2226 print value |
|
2227 |
|
2228 except (getopt.error, BadUsage): |
|
2229 cmd = os.path.basename(sys.argv[0]) |
|
2230 print """pydoc - the Python documentation tool |
|
2231 |
|
2232 %s <name> ... |
|
2233 Show text documentation on something. <name> may be the name of a |
|
2234 Python keyword, topic, function, module, or package, or a dotted |
|
2235 reference to a class or function within a module or module in a |
|
2236 package. If <name> contains a '%s', it is used as the path to a |
|
2237 Python source file to document. If name is 'keywords', 'topics', |
|
2238 or 'modules', a listing of these things is displayed. |
|
2239 |
|
2240 %s -k <keyword> |
|
2241 Search for a keyword in the synopsis lines of all available modules. |
|
2242 |
|
2243 %s -p <port> |
|
2244 Start an HTTP server on the given port on the local machine. |
|
2245 |
|
2246 %s -g |
|
2247 Pop up a graphical interface for finding and serving documentation. |
|
2248 |
|
2249 %s -w <name> ... |
|
2250 Write out the HTML documentation for a module to a file in the current |
|
2251 directory. If <name> contains a '%s', it is treated as a filename; if |
|
2252 it names a directory, documentation is written for all the contents. |
|
2253 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep) |
|
2254 |
|
2255 if __name__ == '__main__': cli() |