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