· 6 years ago · Sep 19, 2019, 10:23 AM
1#! /usr/bin/python3.6
2"""Generate Python documentation in HTML or text for interactive use.
3
4At the Python interactive prompt, calling help(thing) on a Python object
5documents the object, and calling help() starts up an interactive
6help session.
7
8Or, at the shell command line outside of Python:
9
10Run "pydoc <name>" to show documentation on something. <name> may be
11the name of a function, module, package, or a dotted reference to a
12class or function within a module or module in a package. If the
13argument contains a path segment delimiter (e.g. slash on Unix,
14backslash on Windows) it is treated as the path to a Python source file.
15
16Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
17of all available modules.
18
19Run "pydoc -p <port>" to start an HTTP server on the given port on the
20local machine. Port number 0 can be used to get an arbitrary unused port.
21
22Run "pydoc -b" to start an HTTP server on an arbitrary unused port and
23open a Web browser to interactively browse documentation. The -p option
24can be used with the -b option to explicitly specify the server port.
25
26Run "pydoc -w <name>" to write out the HTML documentation for a module
27to a file named "<name>.html".
28
29Module docs for core modules are assumed to be in
30
31 /usr/share/doc/pythonX.Y/html/library
32
33if the pythonX.Y-doc package is installed or in
34
35 https://docs.python.org/X.Y/library/
36
37This can be overridden by setting the PYTHONDOCS environment variable
38to a different URL or to a local directory containing the Library
39Reference Manual pages.
40"""
41__all__ = ['help']
42__author__ = "Ka-Ping Yee <ping@lfw.org>"
43__date__ = "26 February 2001"
44
45__credits__ = """Guido van Rossum, for an excellent programming language.
46Tommy Burnette, the original creator of manpy.
47Paul Prescod, for all his work on onlinehelp.
48Richard Chamberlain, for the first implementation of textdoc.
49"""
50
51# Known bugs that can't be fixed here:
52# - synopsis() cannot be prevented from clobbering existing
53# loaded modules.
54# - If the __file__ attribute on a module is a relative path and
55# the current directory is changed with os.chdir(), an incorrect
56# path will be displayed.
57
58import builtins
59import importlib._bootstrap
60import importlib._bootstrap_external
61import importlib.machinery
62import importlib.util
63import inspect
64import io
65import os
66import pkgutil
67import platform
68import re
69import sys
70import time
71import tokenize
72import urllib.parse
73import warnings
74from collections import deque
75from reprlib import Repr
76from traceback import format_exception_only
77
78
79# --------------------------------------------------------- common routines
80
81def pathdirs():
82 """Convert sys.path into a list of absolute, existing, unique paths."""
83 dirs = []
84 normdirs = []
85 for dir in sys.path:
86 dir = os.path.abspath(dir or '.')
87 normdir = os.path.normcase(dir)
88 if normdir not in normdirs and os.path.isdir(dir):
89 dirs.append(dir)
90 normdirs.append(normdir)
91 return dirs
92
93def getdoc(object):
94 """Get the doc string or comments for an object."""
95 result = inspect.getdoc(object) or inspect.getcomments(object)
96 return result and re.sub('^ *\n', '', result.rstrip()) or ''
97
98def splitdoc(doc):
99 """Split a doc string into a synopsis line (if any) and the rest."""
100 lines = doc.strip().split('\n')
101 if len(lines) == 1:
102 return lines[0], ''
103 elif len(lines) >= 2 and not lines[1].rstrip():
104 return lines[0], '\n'.join(lines[2:])
105 return '', '\n'.join(lines)
106
107def classname(object, modname):
108 """Get a class name and qualify it with a module name if necessary."""
109 name = object.__name__
110 if object.__module__ != modname:
111 name = object.__module__ + '.' + name
112 return name
113
114def isdata(object):
115 """Check if an object is of a type that probably means it's data."""
116 return not (inspect.ismodule(object) or inspect.isclass(object) or
117 inspect.isroutine(object) or inspect.isframe(object) or
118 inspect.istraceback(object) or inspect.iscode(object))
119
120def replace(text, *pairs):
121 """Do a series of global replacements on a string."""
122 while pairs:
123 text = pairs[1].join(text.split(pairs[0]))
124 pairs = pairs[2:]
125 return text
126
127def cram(text, maxlen):
128 """Omit part of a string if needed to make it fit in a maximum length."""
129 if len(text) > maxlen:
130 pre = max(0, (maxlen-3)//2)
131 post = max(0, maxlen-3-pre)
132 return text[:pre] + '...' + text[len(text)-post:]
133 return text
134
135_re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
136def stripid(text):
137 """Remove the hexadecimal id from a Python object representation."""
138 # The behaviour of %p is implementation-dependent in terms of case.
139 return _re_stripid.sub(r'\1', text)
140
141def _is_some_method(obj):
142 return (inspect.isfunction(obj) or
143 inspect.ismethod(obj) or
144 inspect.isbuiltin(obj) or
145 inspect.ismethoddescriptor(obj))
146
147def _is_bound_method(fn):
148 """
149 Returns True if fn is a bound method, regardless of whether
150 fn was implemented in Python or in C.
151 """
152 if inspect.ismethod(fn):
153 return True
154 if inspect.isbuiltin(fn):
155 self = getattr(fn, '__self__', None)
156 return not (inspect.ismodule(self) or (self is None))
157 return False
158
159
160def allmethods(cl):
161 methods = {}
162 for key, value in inspect.getmembers(cl, _is_some_method):
163 methods[key] = 1
164 for base in cl.__bases__:
165 methods.update(allmethods(base)) # all your base are belong to us
166 for key in methods.keys():
167 methods[key] = getattr(cl, key)
168 return methods
169
170def _split_list(s, predicate):
171 """Split sequence s via predicate, and return pair ([true], [false]).
172
173 The return value is a 2-tuple of lists,
174 ([x for x in s if predicate(x)],
175 [x for x in s if not predicate(x)])
176 """
177
178 yes = []
179 no = []
180 for x in s:
181 if predicate(x):
182 yes.append(x)
183 else:
184 no.append(x)
185 return yes, no
186
187def visiblename(name, all=None, obj=None):
188 """Decide whether to show documentation on a variable."""
189 # Certain special names are redundant or internal.
190 # XXX Remove __initializing__?
191 if name in {'__author__', '__builtins__', '__cached__', '__credits__',
192 '__date__', '__doc__', '__file__', '__spec__',
193 '__loader__', '__module__', '__name__', '__package__',
194 '__path__', '__qualname__', '__slots__', '__version__'}:
195 return 0
196 # Private names are hidden, but special names are displayed.
197 if name.startswith('__') and name.endswith('__'): return 1
198 # Namedtuples have public fields and methods with a single leading underscore
199 if name.startswith('_') and hasattr(obj, '_fields'):
200 return True
201 if all is not None:
202 # only document that which the programmer exported in __all__
203 return name in all
204 else:
205 return not name.startswith('_')
206
207def classify_class_attrs(object):
208 """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
209 results = []
210 for (name, kind, cls, value) in inspect.classify_class_attrs(object):
211 if inspect.isdatadescriptor(value):
212 kind = 'data descriptor'
213 results.append((name, kind, cls, value))
214 return results
215
216def sort_attributes(attrs, object):
217 'Sort the attrs list in-place by _fields and then alphabetically by name'
218 # This allows data descriptors to be ordered according
219 # to a _fields attribute if present.
220 fields = getattr(object, '_fields', [])
221 try:
222 field_order = {name : i-len(fields) for (i, name) in enumerate(fields)}
223 except TypeError:
224 field_order = {}
225 keyfunc = lambda attr: (field_order.get(attr[0], 0), attr[0])
226 attrs.sort(key=keyfunc)
227
228# ----------------------------------------------------- module manipulation
229
230def ispackage(path):
231 """Guess whether a path refers to a package directory."""
232 if os.path.isdir(path):
233 for ext in ('.py', '.pyc'):
234 if os.path.isfile(os.path.join(path, '__init__' + ext)):
235 return True
236 return False
237
238def source_synopsis(file):
239 line = file.readline()
240 while line[:1] == '#' or not line.strip():
241 line = file.readline()
242 if not line: break
243 line = line.strip()
244 if line[:4] == 'r"""': line = line[1:]
245 if line[:3] == '"""':
246 line = line[3:]
247 if line[-1:] == '\\': line = line[:-1]
248 while not line.strip():
249 line = file.readline()
250 if not line: break
251 result = line.split('"""')[0].strip()
252 else: result = None
253 return result
254
255def synopsis(filename, cache={}):
256 """Get the one-line summary out of a module file."""
257 mtime = os.stat(filename).st_mtime
258 lastupdate, result = cache.get(filename, (None, None))
259 if lastupdate is None or lastupdate < mtime:
260 # Look for binary suffixes first, falling back to source.
261 if filename.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
262 loader_cls = importlib.machinery.SourcelessFileLoader
263 elif filename.endswith(tuple(importlib.machinery.EXTENSION_SUFFIXES)):
264 loader_cls = importlib.machinery.ExtensionFileLoader
265 else:
266 loader_cls = None
267 # Now handle the choice.
268 if loader_cls is None:
269 # Must be a source file.
270 try:
271 file = tokenize.open(filename)
272 except OSError:
273 # module can't be opened, so skip it
274 return None
275 # text modules can be directly examined
276 with file:
277 result = source_synopsis(file)
278 else:
279 # Must be a binary module, which has to be imported.
280 loader = loader_cls('__temp__', filename)
281 # XXX We probably don't need to pass in the loader here.
282 spec = importlib.util.spec_from_file_location('__temp__', filename,
283 loader=loader)
284 try:
285 module = importlib._bootstrap._load(spec)
286 except:
287 return None
288 del sys.modules['__temp__']
289 result = module.__doc__.splitlines()[0] if module.__doc__ else None
290 # Cache the result.
291 cache[filename] = (mtime, result)
292 return result
293
294class ErrorDuringImport(Exception):
295 """Errors that occurred while trying to import something to document it."""
296 def __init__(self, filename, exc_info):
297 self.filename = filename
298 self.exc, self.value, self.tb = exc_info
299
300 def __str__(self):
301 exc = self.exc.__name__
302 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
303
304def importfile(path):
305 """Import a Python source file or compiled file given its path."""
306 magic = importlib.util.MAGIC_NUMBER
307 with open(path, 'rb') as file:
308 is_bytecode = magic == file.read(len(magic))
309 filename = os.path.basename(path)
310 name, ext = os.path.splitext(filename)
311 if is_bytecode:
312 loader = importlib._bootstrap_external.SourcelessFileLoader(name, path)
313 else:
314 loader = importlib._bootstrap_external.SourceFileLoader(name, path)
315 # XXX We probably don't need to pass in the loader here.
316 spec = importlib.util.spec_from_file_location(name, path, loader=loader)
317 try:
318 return importlib._bootstrap._load(spec)
319 except:
320 raise ErrorDuringImport(path, sys.exc_info())
321
322def safeimport(path, forceload=0, cache={}):
323 """Import a module; handle errors; return None if the module isn't found.
324
325 If the module *is* found but an exception occurs, it's wrapped in an
326 ErrorDuringImport exception and reraised. Unlike __import__, if a
327 package path is specified, the module at the end of the path is returned,
328 not the package at the beginning. If the optional 'forceload' argument
329 is 1, we reload the module from disk (unless it's a dynamic extension)."""
330 try:
331 # If forceload is 1 and the module has been previously loaded from
332 # disk, we always have to reload the module. Checking the file's
333 # mtime isn't good enough (e.g. the module could contain a class
334 # that inherits from another module that has changed).
335 if forceload and path in sys.modules:
336 if path not in sys.builtin_module_names:
337 # Remove the module from sys.modules and re-import to try
338 # and avoid problems with partially loaded modules.
339 # Also remove any submodules because they won't appear
340 # in the newly loaded module's namespace if they're already
341 # in sys.modules.
342 subs = [m for m in sys.modules if m.startswith(path + '.')]
343 for key in [path] + subs:
344 # Prevent garbage collection.
345 cache[key] = sys.modules[key]
346 del sys.modules[key]
347 module = __import__(path)
348 except:
349 # Did the error occur before or after the module was found?
350 (exc, value, tb) = info = sys.exc_info()
351 if path in sys.modules:
352 # An error occurred while executing the imported module.
353 raise ErrorDuringImport(sys.modules[path].__file__, info)
354 elif exc is SyntaxError:
355 # A SyntaxError occurred before we could execute the module.
356 raise ErrorDuringImport(value.filename, info)
357 elif issubclass(exc, ImportError) and value.name == path:
358 # No such module in the path.
359 return None
360 else:
361 # Some other error occurred during the importing process.
362 raise ErrorDuringImport(path, sys.exc_info())
363 for part in path.split('.')[1:]:
364 try: module = getattr(module, part)
365 except AttributeError: return None
366 return module
367
368# ---------------------------------------------------- formatter base class
369
370class Doc:
371
372 PYTHONDOCS = os.environ.get("PYTHONDOCS",
373 "https://docs.python.org/%d.%d/library"
374 % sys.version_info[:2])
375
376 def document(self, object, name=None, *args):
377 """Generate documentation for an object."""
378 args = (object, name) + args
379 # 'try' clause is to attempt to handle the possibility that inspect
380 # identifies something in a way that pydoc itself has issues handling;
381 # think 'super' and how it is a descriptor (which raises the exception
382 # by lacking a __name__ attribute) and an instance.
383 if inspect.isgetsetdescriptor(object): return self.docdata(*args)
384 if inspect.ismemberdescriptor(object): return self.docdata(*args)
385 try:
386 if inspect.ismodule(object): return self.docmodule(*args)
387 if inspect.isclass(object): return self.docclass(*args)
388 if inspect.isroutine(object): return self.docroutine(*args)
389 except AttributeError:
390 pass
391 if isinstance(object, property): return self.docproperty(*args)
392 return self.docother(*args)
393
394 def fail(self, object, name=None, *args):
395 """Raise an exception for unimplemented types."""
396 message = "don't know how to document object%s of type %s" % (
397 name and ' ' + repr(name), type(object).__name__)
398 raise TypeError(message)
399
400 docmodule = docclass = docroutine = docother = docproperty = docdata = fail
401
402 def getdocloc(self, object,
403 basedir=os.path.join(sys.base_exec_prefix, "lib",
404 "python%d.%d" % sys.version_info[:2])):
405 """Return the location of module docs or None"""
406
407 try:
408 file = inspect.getabsfile(object)
409 except TypeError:
410 file = '(built-in)'
411
412 docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS)
413
414 basedir = os.path.normcase(basedir)
415 if (isinstance(object, type(os)) and
416 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
417 'marshal', 'posix', 'signal', 'sys',
418 '_thread', 'zipimport') or
419 (file.startswith(basedir) and
420 not file.startswith(os.path.join(basedir, 'dist-packages')) and
421 not file.startswith(os.path.join(basedir, 'site-packages')))) and
422 object.__name__ not in ('xml.etree', 'test.pydoc_mod')):
423 if docloc.startswith(("http://", "https://")):
424 docloc = "%s/%s" % (docloc.rstrip("/"), object.__name__.lower())
425 else:
426 docloc = os.path.join(docloc, object.__name__.lower() + ".html")
427 else:
428 docloc = None
429 return docloc
430
431# -------------------------------------------- HTML documentation generator
432
433class HTMLRepr(Repr):
434 """Class for safely making an HTML representation of a Python object."""
435 def __init__(self):
436 Repr.__init__(self)
437 self.maxlist = self.maxtuple = 20
438 self.maxdict = 10
439 self.maxstring = self.maxother = 100
440
441 def escape(self, text):
442 return replace(text, '&', '&', '<', '<', '>', '>')
443
444 def repr(self, object):
445 return Repr.repr(self, object)
446
447 def repr1(self, x, level):
448 if hasattr(type(x), '__name__'):
449 methodname = 'repr_' + '_'.join(type(x).__name__.split())
450 if hasattr(self, methodname):
451 return getattr(self, methodname)(x, level)
452 return self.escape(cram(stripid(repr(x)), self.maxother))
453
454 def repr_string(self, x, level):
455 test = cram(x, self.maxstring)
456 testrepr = repr(test)
457 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
458 # Backslashes are only literal in the string and are never
459 # needed to make any special characters, so show a raw string.
460 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
461 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
462 r'<font color="#c040c0">\1</font>',
463 self.escape(testrepr))
464
465 repr_str = repr_string
466
467 def repr_instance(self, x, level):
468 try:
469 return self.escape(cram(stripid(repr(x)), self.maxstring))
470 except:
471 return self.escape('<%s instance>' % x.__class__.__name__)
472
473 repr_unicode = repr_string
474
475class HTMLDoc(Doc):
476 """Formatter class for HTML documentation."""
477
478 # ------------------------------------------- HTML formatting utilities
479
480 _repr_instance = HTMLRepr()
481 repr = _repr_instance.repr
482 escape = _repr_instance.escape
483
484 def page(self, title, contents):
485 """Format an HTML page."""
486 return '''\
487<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
488<html><head><title>Python: %s</title>
489<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
490</head><body bgcolor="#f0f0f8">
491%s
492</body></html>''' % (title, contents)
493
494 def heading(self, title, fgcol, bgcol, extras=''):
495 """Format a page heading."""
496 return '''
497<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
498<tr bgcolor="%s">
499<td valign=bottom> <br>
500<font color="%s" face="helvetica, arial"> <br>%s</font></td
501><td align=right valign=bottom
502><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
503 ''' % (bgcol, fgcol, title, fgcol, extras or ' ')
504
505 def section(self, title, fgcol, bgcol, contents, width=6,
506 prelude='', marginalia=None, gap=' '):
507 """Format a section with a heading."""
508 if marginalia is None:
509 marginalia = '<tt>' + ' ' * width + '</tt>'
510 result = '''<p>
511<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
512<tr bgcolor="%s">
513<td colspan=3 valign=bottom> <br>
514<font color="%s" face="helvetica, arial">%s</font></td></tr>
515 ''' % (bgcol, fgcol, title)
516 if prelude:
517 result = result + '''
518<tr bgcolor="%s"><td rowspan=2>%s</td>
519<td colspan=2>%s</td></tr>
520<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
521 else:
522 result = result + '''
523<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
524
525 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
526
527 def bigsection(self, title, *args):
528 """Format a section with a big heading."""
529 title = '<big><strong>%s</strong></big>' % title
530 return self.section(title, *args)
531
532 def preformat(self, text):
533 """Format literal preformatted text."""
534 text = self.escape(text.expandtabs())
535 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
536 ' ', ' ', '\n', '<br>\n')
537
538 def multicolumn(self, list, format, cols=4):
539 """Format a list of items into a multi-column list."""
540 result = ''
541 rows = (len(list)+cols-1)//cols
542 for col in range(cols):
543 result = result + '<td width="%d%%" valign=top>' % (100//cols)
544 for i in range(rows*col, rows*col+rows):
545 if i < len(list):
546 result = result + format(list[i]) + '<br>\n'
547 result = result + '</td>'
548 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
549
550 def grey(self, text): return '<font color="#909090">%s</font>' % text
551
552 def namelink(self, name, *dicts):
553 """Make a link for an identifier, given name-to-URL mappings."""
554 for dict in dicts:
555 if name in dict:
556 return '<a href="%s">%s</a>' % (dict[name], name)
557 return name
558
559 def classlink(self, object, modname):
560 """Make a link for a class."""
561 name, module = object.__name__, sys.modules.get(object.__module__)
562 if hasattr(module, name) and getattr(module, name) is object:
563 return '<a href="%s.html#%s">%s</a>' % (
564 module.__name__, name, classname(object, modname))
565 return classname(object, modname)
566
567 def modulelink(self, object):
568 """Make a link for a module."""
569 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
570
571 def modpkglink(self, modpkginfo):
572 """Make a link for a module or package to display in an index."""
573 name, path, ispackage, shadowed = modpkginfo
574 if shadowed:
575 return self.grey(name)
576 if path:
577 url = '%s.%s.html' % (path, name)
578 else:
579 url = '%s.html' % name
580 if ispackage:
581 text = '<strong>%s</strong> (package)' % name
582 else:
583 text = name
584 return '<a href="%s">%s</a>' % (url, text)
585
586 def filelink(self, url, path):
587 """Make a link to source file."""
588 return '<a href="file:%s">%s</a>' % (url, path)
589
590 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
591 """Mark up some plain text, given a context of symbols to look for.
592 Each context dictionary maps object names to anchor names."""
593 escape = escape or self.escape
594 results = []
595 here = 0
596 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
597 r'RFC[- ]?(\d+)|'
598 r'PEP[- ]?(\d+)|'
599 r'(self\.)?(\w+))')
600 while True:
601 match = pattern.search(text, here)
602 if not match: break
603 start, end = match.span()
604 results.append(escape(text[here:start]))
605
606 all, scheme, rfc, pep, selfdot, name = match.groups()
607 if scheme:
608 url = escape(all).replace('"', '"')
609 results.append('<a href="%s">%s</a>' % (url, url))
610 elif rfc:
611 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
612 results.append('<a href="%s">%s</a>' % (url, escape(all)))
613 elif pep:
614 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
615 results.append('<a href="%s">%s</a>' % (url, escape(all)))
616 elif selfdot:
617 # Create a link for methods like 'self.method(...)'
618 # and use <strong> for attributes like 'self.attr'
619 if text[end:end+1] == '(':
620 results.append('self.' + self.namelink(name, methods))
621 else:
622 results.append('self.<strong>%s</strong>' % name)
623 elif text[end:end+1] == '(':
624 results.append(self.namelink(name, methods, funcs, classes))
625 else:
626 results.append(self.namelink(name, classes))
627 here = end
628 results.append(escape(text[here:]))
629 return ''.join(results)
630
631 # ---------------------------------------------- type-specific routines
632
633 def formattree(self, tree, modname, parent=None):
634 """Produce HTML for a class tree as given by inspect.getclasstree()."""
635 result = ''
636 for entry in tree:
637 if type(entry) is type(()):
638 c, bases = entry
639 result = result + '<dt><font face="helvetica, arial">'
640 result = result + self.classlink(c, modname)
641 if bases and bases != (parent,):
642 parents = []
643 for base in bases:
644 parents.append(self.classlink(base, modname))
645 result = result + '(' + ', '.join(parents) + ')'
646 result = result + '\n</font></dt>'
647 elif type(entry) is type([]):
648 result = result + '<dd>\n%s</dd>\n' % self.formattree(
649 entry, modname, c)
650 return '<dl>\n%s</dl>\n' % result
651
652 def docmodule(self, object, name=None, mod=None, *ignored):
653 """Produce HTML documentation for a module object."""
654 name = object.__name__ # ignore the passed-in name
655 try:
656 all = object.__all__
657 except AttributeError:
658 all = None
659 parts = name.split('.')
660 links = []
661 for i in range(len(parts)-1):
662 links.append(
663 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
664 ('.'.join(parts[:i+1]), parts[i]))
665 linkedname = '.'.join(links + parts[-1:])
666 head = '<big><big><strong>%s</strong></big></big>' % linkedname
667 try:
668 path = inspect.getabsfile(object)
669 url = urllib.parse.quote(path)
670 filelink = self.filelink(url, path)
671 except TypeError:
672 filelink = '(built-in)'
673 info = []
674 if hasattr(object, '__version__'):
675 version = str(object.__version__)
676 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
677 version = version[11:-1].strip()
678 info.append('version %s' % self.escape(version))
679 if hasattr(object, '__date__'):
680 info.append(self.escape(str(object.__date__)))
681 if info:
682 head = head + ' (%s)' % ', '.join(info)
683 docloc = self.getdocloc(object)
684 if docloc is not None:
685 docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals()
686 else:
687 docloc = ''
688 result = self.heading(
689 head, '#ffffff', '#7799ee',
690 '<a href=".">index</a><br>' + filelink + docloc)
691
692 modules = inspect.getmembers(object, inspect.ismodule)
693
694 classes, cdict = [], {}
695 for key, value in inspect.getmembers(object, inspect.isclass):
696 # if __all__ exists, believe it. Otherwise use old heuristic.
697 if (all is not None or
698 (inspect.getmodule(value) or object) is object):
699 if visiblename(key, all, object):
700 classes.append((key, value))
701 cdict[key] = cdict[value] = '#' + key
702 for key, value in classes:
703 for base in value.__bases__:
704 key, modname = base.__name__, base.__module__
705 module = sys.modules.get(modname)
706 if modname != name and module and hasattr(module, key):
707 if getattr(module, key) is base:
708 if not key in cdict:
709 cdict[key] = cdict[base] = modname + '.html#' + key
710 funcs, fdict = [], {}
711 for key, value in inspect.getmembers(object, inspect.isroutine):
712 # if __all__ exists, believe it. Otherwise use old heuristic.
713 if (all is not None or
714 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
715 if visiblename(key, all, object):
716 funcs.append((key, value))
717 fdict[key] = '#-' + key
718 if inspect.isfunction(value): fdict[value] = fdict[key]
719 data = []
720 for key, value in inspect.getmembers(object, isdata):
721 if visiblename(key, all, object):
722 data.append((key, value))
723
724 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
725 doc = doc and '<tt>%s</tt>' % doc
726 result = result + '<p>%s</p>\n' % doc
727
728 if hasattr(object, '__path__'):
729 modpkgs = []
730 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
731 modpkgs.append((modname, name, ispkg, 0))
732 modpkgs.sort()
733 contents = self.multicolumn(modpkgs, self.modpkglink)
734 result = result + self.bigsection(
735 'Package Contents', '#ffffff', '#aa55cc', contents)
736 elif modules:
737 contents = self.multicolumn(
738 modules, lambda t: self.modulelink(t[1]))
739 result = result + self.bigsection(
740 'Modules', '#ffffff', '#aa55cc', contents)
741
742 if classes:
743 classlist = [value for (key, value) in classes]
744 contents = [
745 self.formattree(inspect.getclasstree(classlist, 1), name)]
746 for key, value in classes:
747 contents.append(self.document(value, key, name, fdict, cdict))
748 result = result + self.bigsection(
749 'Classes', '#ffffff', '#ee77aa', ' '.join(contents))
750 if funcs:
751 contents = []
752 for key, value in funcs:
753 contents.append(self.document(value, key, name, fdict, cdict))
754 result = result + self.bigsection(
755 'Functions', '#ffffff', '#eeaa77', ' '.join(contents))
756 if data:
757 contents = []
758 for key, value in data:
759 contents.append(self.document(value, key))
760 result = result + self.bigsection(
761 'Data', '#ffffff', '#55aa55', '<br>\n'.join(contents))
762 if hasattr(object, '__author__'):
763 contents = self.markup(str(object.__author__), self.preformat)
764 result = result + self.bigsection(
765 'Author', '#ffffff', '#7799ee', contents)
766 if hasattr(object, '__credits__'):
767 contents = self.markup(str(object.__credits__), self.preformat)
768 result = result + self.bigsection(
769 'Credits', '#ffffff', '#7799ee', contents)
770
771 return result
772
773 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
774 *ignored):
775 """Produce HTML documentation for a class object."""
776 realname = object.__name__
777 name = name or realname
778 bases = object.__bases__
779
780 contents = []
781 push = contents.append
782
783 # Cute little class to pump out a horizontal rule between sections.
784 class HorizontalRule:
785 def __init__(self):
786 self.needone = 0
787 def maybe(self):
788 if self.needone:
789 push('<hr>\n')
790 self.needone = 1
791 hr = HorizontalRule()
792
793 # List the mro, if non-trivial.
794 mro = deque(inspect.getmro(object))
795 if len(mro) > 2:
796 hr.maybe()
797 push('<dl><dt>Method resolution order:</dt>\n')
798 for base in mro:
799 push('<dd>%s</dd>\n' % self.classlink(base,
800 object.__module__))
801 push('</dl>\n')
802
803 def spill(msg, attrs, predicate):
804 ok, attrs = _split_list(attrs, predicate)
805 if ok:
806 hr.maybe()
807 push(msg)
808 for name, kind, homecls, value in ok:
809 try:
810 value = getattr(object, name)
811 except Exception:
812 # Some descriptors may meet a failure in their __get__.
813 # (bug #1785)
814 push(self._docdescriptor(name, value, mod))
815 else:
816 push(self.document(value, name, mod,
817 funcs, classes, mdict, object))
818 push('\n')
819 return attrs
820
821 def spilldescriptors(msg, attrs, predicate):
822 ok, attrs = _split_list(attrs, predicate)
823 if ok:
824 hr.maybe()
825 push(msg)
826 for name, kind, homecls, value in ok:
827 push(self._docdescriptor(name, value, mod))
828 return attrs
829
830 def spilldata(msg, attrs, predicate):
831 ok, attrs = _split_list(attrs, predicate)
832 if ok:
833 hr.maybe()
834 push(msg)
835 for name, kind, homecls, value in ok:
836 base = self.docother(getattr(object, name), name, mod)
837 if callable(value) or inspect.isdatadescriptor(value):
838 doc = getattr(value, "__doc__", None)
839 else:
840 doc = None
841 if doc is None:
842 push('<dl><dt>%s</dl>\n' % base)
843 else:
844 doc = self.markup(getdoc(value), self.preformat,
845 funcs, classes, mdict)
846 doc = '<dd><tt>%s</tt>' % doc
847 push('<dl><dt>%s%s</dl>\n' % (base, doc))
848 push('\n')
849 return attrs
850
851 attrs = [(name, kind, cls, value)
852 for name, kind, cls, value in classify_class_attrs(object)
853 if visiblename(name, obj=object)]
854
855 mdict = {}
856 for key, kind, homecls, value in attrs:
857 mdict[key] = anchor = '#' + name + '-' + key
858 try:
859 value = getattr(object, name)
860 except Exception:
861 # Some descriptors may meet a failure in their __get__.
862 # (bug #1785)
863 pass
864 try:
865 # The value may not be hashable (e.g., a data attr with
866 # a dict or list value).
867 mdict[value] = anchor
868 except TypeError:
869 pass
870
871 while attrs:
872 if mro:
873 thisclass = mro.popleft()
874 else:
875 thisclass = attrs[0][2]
876 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
877
878 if thisclass is builtins.object:
879 attrs = inherited
880 continue
881 elif thisclass is object:
882 tag = 'defined here'
883 else:
884 tag = 'inherited from %s' % self.classlink(thisclass,
885 object.__module__)
886 tag += ':<br>\n'
887
888 sort_attributes(attrs, object)
889
890 # Pump out the attrs, segregated by kind.
891 attrs = spill('Methods %s' % tag, attrs,
892 lambda t: t[1] == 'method')
893 attrs = spill('Class methods %s' % tag, attrs,
894 lambda t: t[1] == 'class method')
895 attrs = spill('Static methods %s' % tag, attrs,
896 lambda t: t[1] == 'static method')
897 attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
898 lambda t: t[1] == 'data descriptor')
899 attrs = spilldata('Data and other attributes %s' % tag, attrs,
900 lambda t: t[1] == 'data')
901 assert attrs == []
902 attrs = inherited
903
904 contents = ''.join(contents)
905
906 if name == realname:
907 title = '<a name="%s">class <strong>%s</strong></a>' % (
908 name, realname)
909 else:
910 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
911 name, name, realname)
912 if bases:
913 parents = []
914 for base in bases:
915 parents.append(self.classlink(base, object.__module__))
916 title = title + '(%s)' % ', '.join(parents)
917 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
918 doc = doc and '<tt>%s<br> </tt>' % doc
919
920 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
921
922 def formatvalue(self, object):
923 """Format an argument default value as text."""
924 return self.grey('=' + self.repr(object))
925
926 def docroutine(self, object, name=None, mod=None,
927 funcs={}, classes={}, methods={}, cl=None):
928 """Produce HTML documentation for a function or method object."""
929 realname = object.__name__
930 name = name or realname
931 anchor = (cl and cl.__name__ or '') + '-' + name
932 note = ''
933 skipdocs = 0
934 if _is_bound_method(object):
935 imclass = object.__self__.__class__
936 if cl:
937 if imclass is not cl:
938 note = ' from ' + self.classlink(imclass, mod)
939 else:
940 if object.__self__ is not None:
941 note = ' method of %s instance' % self.classlink(
942 object.__self__.__class__, mod)
943 else:
944 note = ' unbound %s method' % self.classlink(imclass,mod)
945
946 if name == realname:
947 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
948 else:
949 if cl and inspect.getattr_static(cl, realname, []) is object:
950 reallink = '<a href="#%s">%s</a>' % (
951 cl.__name__ + '-' + realname, realname)
952 skipdocs = 1
953 else:
954 reallink = realname
955 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
956 anchor, name, reallink)
957 argspec = None
958 if inspect.isroutine(object):
959 try:
960 signature = inspect.signature(object)
961 except (ValueError, TypeError):
962 signature = None
963 if signature:
964 argspec = str(signature)
965 if realname == '<lambda>':
966 title = '<strong>%s</strong> <em>lambda</em> ' % name
967 # XXX lambda's won't usually have func_annotations['return']
968 # since the syntax doesn't support but it is possible.
969 # So removing parentheses isn't truly safe.
970 argspec = argspec[1:-1] # remove parentheses
971 if not argspec:
972 argspec = '(...)'
973
974 decl = title + self.escape(argspec) + (note and self.grey(
975 '<font face="helvetica, arial">%s</font>' % note))
976
977 if skipdocs:
978 return '<dl><dt>%s</dt></dl>\n' % decl
979 else:
980 doc = self.markup(
981 getdoc(object), self.preformat, funcs, classes, methods)
982 doc = doc and '<dd><tt>%s</tt></dd>' % doc
983 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
984
985 def _docdescriptor(self, name, value, mod):
986 results = []
987 push = results.append
988
989 if name:
990 push('<dl><dt><strong>%s</strong></dt>\n' % name)
991 if value.__doc__ is not None:
992 doc = self.markup(getdoc(value), self.preformat)
993 push('<dd><tt>%s</tt></dd>\n' % doc)
994 push('</dl>\n')
995
996 return ''.join(results)
997
998 def docproperty(self, object, name=None, mod=None, cl=None):
999 """Produce html documentation for a property."""
1000 return self._docdescriptor(name, object, mod)
1001
1002 def docother(self, object, name=None, mod=None, *ignored):
1003 """Produce HTML documentation for a data object."""
1004 lhs = name and '<strong>%s</strong> = ' % name or ''
1005 return lhs + self.repr(object)
1006
1007 def docdata(self, object, name=None, mod=None, cl=None):
1008 """Produce html documentation for a data descriptor."""
1009 return self._docdescriptor(name, object, mod)
1010
1011 def index(self, dir, shadowed=None):
1012 """Generate an HTML index for a directory of modules."""
1013 modpkgs = []
1014 if shadowed is None: shadowed = {}
1015 for importer, name, ispkg in pkgutil.iter_modules([dir]):
1016 if any((0xD800 <= ord(ch) <= 0xDFFF) for ch in name):
1017 # ignore a module if its name contains a surrogate character
1018 continue
1019 modpkgs.append((name, '', ispkg, name in shadowed))
1020 shadowed[name] = 1
1021
1022 modpkgs.sort()
1023 contents = self.multicolumn(modpkgs, self.modpkglink)
1024 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
1025
1026# -------------------------------------------- text documentation generator
1027
1028class TextRepr(Repr):
1029 """Class for safely making a text representation of a Python object."""
1030 def __init__(self):
1031 Repr.__init__(self)
1032 self.maxlist = self.maxtuple = 20
1033 self.maxdict = 10
1034 self.maxstring = self.maxother = 100
1035
1036 def repr1(self, x, level):
1037 if hasattr(type(x), '__name__'):
1038 methodname = 'repr_' + '_'.join(type(x).__name__.split())
1039 if hasattr(self, methodname):
1040 return getattr(self, methodname)(x, level)
1041 return cram(stripid(repr(x)), self.maxother)
1042
1043 def repr_string(self, x, level):
1044 test = cram(x, self.maxstring)
1045 testrepr = repr(test)
1046 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
1047 # Backslashes are only literal in the string and are never
1048 # needed to make any special characters, so show a raw string.
1049 return 'r' + testrepr[0] + test + testrepr[0]
1050 return testrepr
1051
1052 repr_str = repr_string
1053
1054 def repr_instance(self, x, level):
1055 try:
1056 return cram(stripid(repr(x)), self.maxstring)
1057 except:
1058 return '<%s instance>' % x.__class__.__name__
1059
1060class TextDoc(Doc):
1061 """Formatter class for text documentation."""
1062
1063 # ------------------------------------------- text formatting utilities
1064
1065 _repr_instance = TextRepr()
1066 repr = _repr_instance.repr
1067
1068 def bold(self, text):
1069 """Format a string in bold by overstriking."""
1070 return ''.join(ch + '\b' + ch for ch in text)
1071
1072 def indent(self, text, prefix=' '):
1073 """Indent text by prepending a given prefix to each line."""
1074 if not text: return ''
1075 lines = [prefix + line for line in text.split('\n')]
1076 if lines: lines[-1] = lines[-1].rstrip()
1077 return '\n'.join(lines)
1078
1079 def section(self, title, contents):
1080 """Format a section with a given heading."""
1081 clean_contents = self.indent(contents).rstrip()
1082 return self.bold(title) + '\n' + clean_contents + '\n\n'
1083
1084 # ---------------------------------------------- type-specific routines
1085
1086 def formattree(self, tree, modname, parent=None, prefix=''):
1087 """Render in text a class tree as returned by inspect.getclasstree()."""
1088 result = ''
1089 for entry in tree:
1090 if type(entry) is type(()):
1091 c, bases = entry
1092 result = result + prefix + classname(c, modname)
1093 if bases and bases != (parent,):
1094 parents = (classname(c, modname) for c in bases)
1095 result = result + '(%s)' % ', '.join(parents)
1096 result = result + '\n'
1097 elif type(entry) is type([]):
1098 result = result + self.formattree(
1099 entry, modname, c, prefix + ' ')
1100 return result
1101
1102 def docmodule(self, object, name=None, mod=None):
1103 """Produce text documentation for a given module object."""
1104 name = object.__name__ # ignore the passed-in name
1105 synop, desc = splitdoc(getdoc(object))
1106 result = self.section('NAME', name + (synop and ' - ' + synop))
1107 all = getattr(object, '__all__', None)
1108 docloc = self.getdocloc(object)
1109 if docloc is not None:
1110 result = result + self.section('MODULE REFERENCE', docloc + """
1111
1112The following documentation is automatically generated from the Python
1113source files. It may be incomplete, incorrect or include features that
1114are considered implementation detail and may vary between Python
1115implementations. When in doubt, consult the module reference at the
1116location listed above.
1117""")
1118
1119 if desc:
1120 result = result + self.section('DESCRIPTION', desc)
1121
1122 classes = []
1123 for key, value in inspect.getmembers(object, inspect.isclass):
1124 # if __all__ exists, believe it. Otherwise use old heuristic.
1125 if (all is not None
1126 or (inspect.getmodule(value) or object) is object):
1127 if visiblename(key, all, object):
1128 classes.append((key, value))
1129 funcs = []
1130 for key, value in inspect.getmembers(object, inspect.isroutine):
1131 # if __all__ exists, believe it. Otherwise use old heuristic.
1132 if (all is not None or
1133 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1134 if visiblename(key, all, object):
1135 funcs.append((key, value))
1136 data = []
1137 for key, value in inspect.getmembers(object, isdata):
1138 if visiblename(key, all, object):
1139 data.append((key, value))
1140
1141 modpkgs = []
1142 modpkgs_names = set()
1143 if hasattr(object, '__path__'):
1144 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
1145 modpkgs_names.add(modname)
1146 if ispkg:
1147 modpkgs.append(modname + ' (package)')
1148 else:
1149 modpkgs.append(modname)
1150
1151 modpkgs.sort()
1152 result = result + self.section(
1153 'PACKAGE CONTENTS', '\n'.join(modpkgs))
1154
1155 # Detect submodules as sometimes created by C extensions
1156 submodules = []
1157 for key, value in inspect.getmembers(object, inspect.ismodule):
1158 if value.__name__.startswith(name + '.') and key not in modpkgs_names:
1159 submodules.append(key)
1160 if submodules:
1161 submodules.sort()
1162 result = result + self.section(
1163 'SUBMODULES', '\n'.join(submodules))
1164
1165 if classes:
1166 classlist = [value for key, value in classes]
1167 contents = [self.formattree(
1168 inspect.getclasstree(classlist, 1), name)]
1169 for key, value in classes:
1170 contents.append(self.document(value, key, name))
1171 result = result + self.section('CLASSES', '\n'.join(contents))
1172
1173 if funcs:
1174 contents = []
1175 for key, value in funcs:
1176 contents.append(self.document(value, key, name))
1177 result = result + self.section('FUNCTIONS', '\n'.join(contents))
1178
1179 if data:
1180 contents = []
1181 for key, value in data:
1182 contents.append(self.docother(value, key, name, maxlen=70))
1183 result = result + self.section('DATA', '\n'.join(contents))
1184
1185 if hasattr(object, '__version__'):
1186 version = str(object.__version__)
1187 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1188 version = version[11:-1].strip()
1189 result = result + self.section('VERSION', version)
1190 if hasattr(object, '__date__'):
1191 result = result + self.section('DATE', str(object.__date__))
1192 if hasattr(object, '__author__'):
1193 result = result + self.section('AUTHOR', str(object.__author__))
1194 if hasattr(object, '__credits__'):
1195 result = result + self.section('CREDITS', str(object.__credits__))
1196 try:
1197 file = inspect.getabsfile(object)
1198 except TypeError:
1199 file = '(built-in)'
1200 result = result + self.section('FILE', file)
1201 return result
1202
1203 def docclass(self, object, name=None, mod=None, *ignored):
1204 """Produce text documentation for a given class object."""
1205 realname = object.__name__
1206 name = name or realname
1207 bases = object.__bases__
1208
1209 def makename(c, m=object.__module__):
1210 return classname(c, m)
1211
1212 if name == realname:
1213 title = 'class ' + self.bold(realname)
1214 else:
1215 title = self.bold(name) + ' = class ' + realname
1216 if bases:
1217 parents = map(makename, bases)
1218 title = title + '(%s)' % ', '.join(parents)
1219
1220 doc = getdoc(object)
1221 contents = doc and [doc + '\n'] or []
1222 push = contents.append
1223
1224 # List the mro, if non-trivial.
1225 mro = deque(inspect.getmro(object))
1226 if len(mro) > 2:
1227 push("Method resolution order:")
1228 for base in mro:
1229 push(' ' + makename(base))
1230 push('')
1231
1232 # Cute little class to pump out a horizontal rule between sections.
1233 class HorizontalRule:
1234 def __init__(self):
1235 self.needone = 0
1236 def maybe(self):
1237 if self.needone:
1238 push('-' * 70)
1239 self.needone = 1
1240 hr = HorizontalRule()
1241
1242 def spill(msg, attrs, predicate):
1243 ok, attrs = _split_list(attrs, predicate)
1244 if ok:
1245 hr.maybe()
1246 push(msg)
1247 for name, kind, homecls, value in ok:
1248 try:
1249 value = getattr(object, name)
1250 except Exception:
1251 # Some descriptors may meet a failure in their __get__.
1252 # (bug #1785)
1253 push(self._docdescriptor(name, value, mod))
1254 else:
1255 push(self.document(value,
1256 name, mod, object))
1257 return attrs
1258
1259 def spilldescriptors(msg, attrs, predicate):
1260 ok, attrs = _split_list(attrs, predicate)
1261 if ok:
1262 hr.maybe()
1263 push(msg)
1264 for name, kind, homecls, value in ok:
1265 push(self._docdescriptor(name, value, mod))
1266 return attrs
1267
1268 def spilldata(msg, attrs, predicate):
1269 ok, attrs = _split_list(attrs, predicate)
1270 if ok:
1271 hr.maybe()
1272 push(msg)
1273 for name, kind, homecls, value in ok:
1274 if callable(value) or inspect.isdatadescriptor(value):
1275 doc = getdoc(value)
1276 else:
1277 doc = None
1278 try:
1279 obj = getattr(object, name)
1280 except AttributeError:
1281 obj = homecls.__dict__[name]
1282 push(self.docother(obj, name, mod, maxlen=70, doc=doc) +
1283 '\n')
1284 return attrs
1285
1286 attrs = [(name, kind, cls, value)
1287 for name, kind, cls, value in classify_class_attrs(object)
1288 if visiblename(name, obj=object)]
1289
1290 while attrs:
1291 if mro:
1292 thisclass = mro.popleft()
1293 else:
1294 thisclass = attrs[0][2]
1295 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1296
1297 if thisclass is builtins.object:
1298 attrs = inherited
1299 continue
1300 elif thisclass is object:
1301 tag = "defined here"
1302 else:
1303 tag = "inherited from %s" % classname(thisclass,
1304 object.__module__)
1305
1306 sort_attributes(attrs, object)
1307
1308 # Pump out the attrs, segregated by kind.
1309 attrs = spill("Methods %s:\n" % tag, attrs,
1310 lambda t: t[1] == 'method')
1311 attrs = spill("Class methods %s:\n" % tag, attrs,
1312 lambda t: t[1] == 'class method')
1313 attrs = spill("Static methods %s:\n" % tag, attrs,
1314 lambda t: t[1] == 'static method')
1315 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
1316 lambda t: t[1] == 'data descriptor')
1317 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1318 lambda t: t[1] == 'data')
1319
1320 assert attrs == []
1321 attrs = inherited
1322
1323 contents = '\n'.join(contents)
1324 if not contents:
1325 return title + '\n'
1326 return title + '\n' + self.indent(contents.rstrip(), ' | ') + '\n'
1327
1328 def formatvalue(self, object):
1329 """Format an argument default value as text."""
1330 return '=' + self.repr(object)
1331
1332 def docroutine(self, object, name=None, mod=None, cl=None):
1333 """Produce text documentation for a function or method object."""
1334 realname = object.__name__
1335 name = name or realname
1336 note = ''
1337 skipdocs = 0
1338 if _is_bound_method(object):
1339 imclass = object.__self__.__class__
1340 if cl:
1341 if imclass is not cl:
1342 note = ' from ' + classname(imclass, mod)
1343 else:
1344 if object.__self__ is not None:
1345 note = ' method of %s instance' % classname(
1346 object.__self__.__class__, mod)
1347 else:
1348 note = ' unbound %s method' % classname(imclass,mod)
1349
1350 if name == realname:
1351 title = self.bold(realname)
1352 else:
1353 if cl and inspect.getattr_static(cl, realname, []) is object:
1354 skipdocs = 1
1355 title = self.bold(name) + ' = ' + realname
1356 argspec = None
1357
1358 if inspect.isroutine(object):
1359 try:
1360 signature = inspect.signature(object)
1361 except (ValueError, TypeError):
1362 signature = None
1363 if signature:
1364 argspec = str(signature)
1365 if realname == '<lambda>':
1366 title = self.bold(name) + ' lambda '
1367 # XXX lambda's won't usually have func_annotations['return']
1368 # since the syntax doesn't support but it is possible.
1369 # So removing parentheses isn't truly safe.
1370 argspec = argspec[1:-1] # remove parentheses
1371 if not argspec:
1372 argspec = '(...)'
1373 decl = title + argspec + note
1374
1375 if skipdocs:
1376 return decl + '\n'
1377 else:
1378 doc = getdoc(object) or ''
1379 return decl + '\n' + (doc and self.indent(doc).rstrip() + '\n')
1380
1381 def _docdescriptor(self, name, value, mod):
1382 results = []
1383 push = results.append
1384
1385 if name:
1386 push(self.bold(name))
1387 push('\n')
1388 doc = getdoc(value) or ''
1389 if doc:
1390 push(self.indent(doc))
1391 push('\n')
1392 return ''.join(results)
1393
1394 def docproperty(self, object, name=None, mod=None, cl=None):
1395 """Produce text documentation for a property."""
1396 return self._docdescriptor(name, object, mod)
1397
1398 def docdata(self, object, name=None, mod=None, cl=None):
1399 """Produce text documentation for a data descriptor."""
1400 return self._docdescriptor(name, object, mod)
1401
1402 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
1403 """Produce text documentation for a data object."""
1404 repr = self.repr(object)
1405 if maxlen:
1406 line = (name and name + ' = ' or '') + repr
1407 chop = maxlen - len(line)
1408 if chop < 0: repr = repr[:chop] + '...'
1409 line = (name and self.bold(name) + ' = ' or '') + repr
1410 if doc is not None:
1411 line += '\n' + self.indent(str(doc))
1412 return line
1413
1414class _PlainTextDoc(TextDoc):
1415 """Subclass of TextDoc which overrides string styling"""
1416 def bold(self, text):
1417 return text
1418
1419# --------------------------------------------------------- user interfaces
1420
1421def pager(text):
1422 """The first time this is called, determine what kind of pager to use."""
1423 global pager
1424 pager = getpager()
1425 pager(text)
1426
1427def getpager():
1428 """Decide what method to use for paging through text."""
1429 if not hasattr(sys.stdin, "isatty"):
1430 return plainpager
1431 if not hasattr(sys.stdout, "isatty"):
1432 return plainpager
1433 if not sys.stdin.isatty() or not sys.stdout.isatty():
1434 return plainpager
1435 use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER')
1436 if use_pager:
1437 if sys.platform == 'win32': # pipes completely broken in Windows
1438 return lambda text: tempfilepager(plain(text), use_pager)
1439 elif os.environ.get('TERM') in ('dumb', 'emacs'):
1440 return lambda text: pipepager(plain(text), use_pager)
1441 else:
1442 return lambda text: pipepager(text, use_pager)
1443 if os.environ.get('TERM') in ('dumb', 'emacs'):
1444 return plainpager
1445 if sys.platform == 'win32':
1446 return lambda text: tempfilepager(plain(text), 'more <')
1447 if hasattr(os, 'system') and os.system('(pager) 2>/dev/null') == 0:
1448 return lambda text: pipepager(text, 'pager')
1449 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1450 return lambda text: pipepager(text, 'less')
1451
1452 import tempfile
1453 (fd, filename) = tempfile.mkstemp()
1454 os.close(fd)
1455 try:
1456 if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
1457 return lambda text: pipepager(text, 'more')
1458 else:
1459 return ttypager
1460 finally:
1461 os.unlink(filename)
1462
1463def plain(text):
1464 """Remove boldface formatting from text."""
1465 return re.sub('.\b', '', text)
1466
1467def pipepager(text, cmd):
1468 """Page through text by feeding it to another program."""
1469 import subprocess
1470 proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE)
1471 try:
1472 with io.TextIOWrapper(proc.stdin, errors='backslashreplace') as pipe:
1473 try:
1474 pipe.write(text)
1475 except KeyboardInterrupt:
1476 # We've hereby abandoned whatever text hasn't been written,
1477 # but the pager is still in control of the terminal.
1478 pass
1479 except OSError:
1480 pass # Ignore broken pipes caused by quitting the pager program.
1481 while True:
1482 try:
1483 proc.wait()
1484 break
1485 except KeyboardInterrupt:
1486 # Ignore ctl-c like the pager itself does. Otherwise the pager is
1487 # left running and the terminal is in raw mode and unusable.
1488 pass
1489
1490def tempfilepager(text, cmd):
1491 """Page through text by invoking a program on a temporary file."""
1492 import tempfile
1493 filename = tempfile.mktemp()
1494 with open(filename, 'w', errors='backslashreplace') as file:
1495 file.write(text)
1496 try:
1497 os.system(cmd + ' "' + filename + '"')
1498 finally:
1499 os.unlink(filename)
1500
1501def _escape_stdout(text):
1502 # Escape non-encodable characters to avoid encoding errors later
1503 encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8'
1504 return text.encode(encoding, 'backslashreplace').decode(encoding)
1505
1506def ttypager(text):
1507 """Page through text on a text terminal."""
1508 lines = plain(_escape_stdout(text)).split('\n')
1509 try:
1510 import tty
1511 fd = sys.stdin.fileno()
1512 old = tty.tcgetattr(fd)
1513 tty.setcbreak(fd)
1514 getchar = lambda: sys.stdin.read(1)
1515 except (ImportError, AttributeError, io.UnsupportedOperation):
1516 tty = None
1517 getchar = lambda: sys.stdin.readline()[:-1][:1]
1518
1519 try:
1520 try:
1521 h = int(os.environ.get('LINES', 0))
1522 except ValueError:
1523 h = 0
1524 if h <= 1:
1525 h = 25
1526 r = inc = h - 1
1527 sys.stdout.write('\n'.join(lines[:inc]) + '\n')
1528 while lines[r:]:
1529 sys.stdout.write('-- more --')
1530 sys.stdout.flush()
1531 c = getchar()
1532
1533 if c in ('q', 'Q'):
1534 sys.stdout.write('\r \r')
1535 break
1536 elif c in ('\r', '\n'):
1537 sys.stdout.write('\r \r' + lines[r] + '\n')
1538 r = r + 1
1539 continue
1540 if c in ('b', 'B', '\x1b'):
1541 r = r - inc - inc
1542 if r < 0: r = 0
1543 sys.stdout.write('\n' + '\n'.join(lines[r:r+inc]) + '\n')
1544 r = r + inc
1545
1546 finally:
1547 if tty:
1548 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1549
1550def plainpager(text):
1551 """Simply print unformatted text. This is the ultimate fallback."""
1552 sys.stdout.write(plain(_escape_stdout(text)))
1553
1554def describe(thing):
1555 """Produce a short description of the given thing."""
1556 if inspect.ismodule(thing):
1557 if thing.__name__ in sys.builtin_module_names:
1558 return 'built-in module ' + thing.__name__
1559 if hasattr(thing, '__path__'):
1560 return 'package ' + thing.__name__
1561 else:
1562 return 'module ' + thing.__name__
1563 if inspect.isbuiltin(thing):
1564 return 'built-in function ' + thing.__name__
1565 if inspect.isgetsetdescriptor(thing):
1566 return 'getset descriptor %s.%s.%s' % (
1567 thing.__objclass__.__module__, thing.__objclass__.__name__,
1568 thing.__name__)
1569 if inspect.ismemberdescriptor(thing):
1570 return 'member descriptor %s.%s.%s' % (
1571 thing.__objclass__.__module__, thing.__objclass__.__name__,
1572 thing.__name__)
1573 if inspect.isclass(thing):
1574 return 'class ' + thing.__name__
1575 if inspect.isfunction(thing):
1576 return 'function ' + thing.__name__
1577 if inspect.ismethod(thing):
1578 return 'method ' + thing.__name__
1579 return type(thing).__name__
1580
1581def locate(path, forceload=0):
1582 """Locate an object by name or dotted path, importing as necessary."""
1583 parts = [part for part in path.split('.') if part]
1584 module, n = None, 0
1585 while n < len(parts):
1586 nextmodule = safeimport('.'.join(parts[:n+1]), forceload)
1587 if nextmodule: module, n = nextmodule, n + 1
1588 else: break
1589 if module:
1590 object = module
1591 else:
1592 object = builtins
1593 for part in parts[n:]:
1594 try:
1595 object = getattr(object, part)
1596 except AttributeError:
1597 return None
1598 return object
1599
1600# --------------------------------------- interactive interpreter interface
1601
1602text = TextDoc()
1603plaintext = _PlainTextDoc()
1604html = HTMLDoc()
1605
1606def resolve(thing, forceload=0):
1607 """Given an object or a path to an object, get the object and its name."""
1608 if isinstance(thing, str):
1609 object = locate(thing, forceload)
1610 if object is None:
1611 raise ImportError('''\
1612No Python documentation found for %r.
1613Use help() to get the interactive help utility.
1614Use help(str) for help on the str class.''' % thing)
1615 return object, thing
1616 else:
1617 name = getattr(thing, '__name__', None)
1618 return thing, name if isinstance(name, str) else None
1619
1620def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
1621 renderer=None):
1622 """Render text documentation, given an object or a path to an object."""
1623 if renderer is None:
1624 renderer = text
1625 object, name = resolve(thing, forceload)
1626 desc = describe(object)
1627 module = inspect.getmodule(object)
1628 if name and '.' in name:
1629 desc += ' in ' + name[:name.rfind('.')]
1630 elif module and module is not object:
1631 desc += ' in module ' + module.__name__
1632
1633 if not (inspect.ismodule(object) or
1634 inspect.isclass(object) or
1635 inspect.isroutine(object) or
1636 inspect.isgetsetdescriptor(object) or
1637 inspect.ismemberdescriptor(object) or
1638 isinstance(object, property)):
1639 # If the passed object is a piece of data or an instance,
1640 # document its available methods instead of its value.
1641 object = type(object)
1642 desc += ' object'
1643 return title % desc + '\n\n' + renderer.document(object, name)
1644
1645def doc(thing, title='Python Library Documentation: %s', forceload=0,
1646 output=None):
1647 """Display text documentation, given an object or a path to an object."""
1648 try:
1649 if output is None:
1650 pager(render_doc(thing, title, forceload))
1651 else:
1652 output.write(render_doc(thing, title, forceload, plaintext))
1653 except (ImportError, ErrorDuringImport) as value:
1654 print(value)
1655
1656def writedoc(thing, forceload=0):
1657 """Write HTML documentation to a file in the current directory."""
1658 try:
1659 object, name = resolve(thing, forceload)
1660 page = html.page(describe(object), html.document(object, name))
1661 with open(name + '.html', 'w', encoding='utf-8') as file:
1662 file.write(page)
1663 print('wrote', name + '.html')
1664 except (ImportError, ErrorDuringImport) as value:
1665 print(value)
1666
1667def writedocs(dir, pkgpath='', done=None):
1668 """Write out HTML documentation for all modules in a directory tree."""
1669 if done is None: done = {}
1670 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
1671 writedoc(modname)
1672 return
1673
1674class Helper:
1675
1676 # These dictionaries map a topic name to either an alias, or a tuple
1677 # (label, seealso-items). The "label" is the label of the corresponding
1678 # section in the .rst file under Doc/ and an index into the dictionary
1679 # in pydoc_data/topics.py.
1680 #
1681 # CAUTION: if you change one of these dictionaries, be sure to adapt the
1682 # list of needed labels in Doc/tools/pyspecific.py and
1683 # regenerate the pydoc_data/topics.py file by running
1684 # make pydoc-topics
1685 # in Doc/ and copying the output file into the Lib/ directory.
1686
1687 keywords = {
1688 'False': '',
1689 'None': '',
1690 'True': '',
1691 'and': 'BOOLEAN',
1692 'as': 'with',
1693 'assert': ('assert', ''),
1694 'break': ('break', 'while for'),
1695 'class': ('class', 'CLASSES SPECIALMETHODS'),
1696 'continue': ('continue', 'while for'),
1697 'def': ('function', ''),
1698 'del': ('del', 'BASICMETHODS'),
1699 'elif': 'if',
1700 'else': ('else', 'while for'),
1701 'except': 'try',
1702 'finally': 'try',
1703 'for': ('for', 'break continue while'),
1704 'from': 'import',
1705 'global': ('global', 'nonlocal NAMESPACES'),
1706 'if': ('if', 'TRUTHVALUE'),
1707 'import': ('import', 'MODULES'),
1708 'in': ('in', 'SEQUENCEMETHODS'),
1709 'is': 'COMPARISON',
1710 'lambda': ('lambda', 'FUNCTIONS'),
1711 'nonlocal': ('nonlocal', 'global NAMESPACES'),
1712 'not': 'BOOLEAN',
1713 'or': 'BOOLEAN',
1714 'pass': ('pass', ''),
1715 'raise': ('raise', 'EXCEPTIONS'),
1716 'return': ('return', 'FUNCTIONS'),
1717 'try': ('try', 'EXCEPTIONS'),
1718 'while': ('while', 'break continue if TRUTHVALUE'),
1719 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
1720 'yield': ('yield', ''),
1721 }
1722 # Either add symbols to this dictionary or to the symbols dictionary
1723 # directly: Whichever is easier. They are merged later.
1724 _strprefixes = [p + q for p in ('b', 'f', 'r', 'u') for q in ("'", '"')]
1725 _symbols_inverse = {
1726 'STRINGS' : ("'", "'''", '"', '"""', *_strprefixes),
1727 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&',
1728 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'),
1729 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'),
1730 'UNARY' : ('-', '~'),
1731 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=',
1732 '^=', '<<=', '>>=', '**=', '//='),
1733 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'),
1734 'COMPLEX' : ('j', 'J')
1735 }
1736 symbols = {
1737 '%': 'OPERATORS FORMATTING',
1738 '**': 'POWER',
1739 ',': 'TUPLES LISTS FUNCTIONS',
1740 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS',
1741 '...': 'ELLIPSIS',
1742 ':': 'SLICINGS DICTIONARYLITERALS',
1743 '@': 'def class',
1744 '\\': 'STRINGS',
1745 '_': 'PRIVATENAMES',
1746 '__': 'PRIVATENAMES SPECIALMETHODS',
1747 '`': 'BACKQUOTES',
1748 '(': 'TUPLES FUNCTIONS CALLS',
1749 ')': 'TUPLES FUNCTIONS CALLS',
1750 '[': 'LISTS SUBSCRIPTS SLICINGS',
1751 ']': 'LISTS SUBSCRIPTS SLICINGS'
1752 }
1753 for topic, symbols_ in _symbols_inverse.items():
1754 for symbol in symbols_:
1755 topics = symbols.get(symbol, topic)
1756 if topic not in topics:
1757 topics = topics + ' ' + topic
1758 symbols[symbol] = topics
1759
1760 topics = {
1761 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
1762 'FUNCTIONS CLASSES MODULES FILES inspect'),
1763 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS '
1764 'FORMATTING TYPES'),
1765 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
1766 'FORMATTING': ('formatstrings', 'OPERATORS'),
1767 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
1768 'FORMATTING TYPES'),
1769 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1770 'INTEGER': ('integers', 'int range'),
1771 'FLOAT': ('floating', 'float math'),
1772 'COMPLEX': ('imaginary', 'complex cmath'),
1773 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING range LISTS'),
1774 'MAPPINGS': 'DICTIONARIES',
1775 'FUNCTIONS': ('typesfunctions', 'def TYPES'),
1776 'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
1777 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
1778 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
1779 'FRAMEOBJECTS': 'TYPES',
1780 'TRACEBACKS': 'TYPES',
1781 'NONE': ('bltin-null-object', ''),
1782 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
1783 'SPECIALATTRIBUTES': ('specialattrs', ''),
1784 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
1785 'MODULES': ('typesmodules', 'import'),
1786 'PACKAGES': 'import',
1787 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
1788 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
1789 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
1790 'LISTS DICTIONARIES'),
1791 'OPERATORS': 'EXPRESSIONS',
1792 'PRECEDENCE': 'EXPRESSIONS',
1793 'OBJECTS': ('objects', 'TYPES'),
1794 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
1795 'CALLABLEMETHODS SEQUENCEMETHODS MAPPINGMETHODS '
1796 'NUMBERMETHODS CLASSES'),
1797 'BASICMETHODS': ('customization', 'hash repr str SPECIALMETHODS'),
1798 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1799 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
1800 'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS '
1801 'SPECIALMETHODS'),
1802 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
1803 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
1804 'SPECIALMETHODS'),
1805 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1806 'NAMESPACES': ('naming', 'global nonlocal ASSIGNMENT DELETION DYNAMICFEATURES'),
1807 'DYNAMICFEATURES': ('dynamic-features', ''),
1808 'SCOPING': 'NAMESPACES',
1809 'FRAMES': 'NAMESPACES',
1810 'EXCEPTIONS': ('exceptions', 'try except finally raise'),
1811 'CONVERSIONS': ('conversions', ''),
1812 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
1813 'SPECIALIDENTIFIERS': ('id-classes', ''),
1814 'PRIVATENAMES': ('atom-identifiers', ''),
1815 'LITERALS': ('atom-literals', 'STRINGS NUMBERS TUPLELITERALS '
1816 'LISTLITERALS DICTIONARYLITERALS'),
1817 'TUPLES': 'SEQUENCES',
1818 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
1819 'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
1820 'LISTLITERALS': ('lists', 'LISTS LITERALS'),
1821 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
1822 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
1823 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1824 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS'),
1825 'SLICINGS': ('slicings', 'SEQUENCEMETHODS'),
1826 'CALLS': ('calls', 'EXPRESSIONS'),
1827 'POWER': ('power', 'EXPRESSIONS'),
1828 'UNARY': ('unary', 'EXPRESSIONS'),
1829 'BINARY': ('binary', 'EXPRESSIONS'),
1830 'SHIFTING': ('shifting', 'EXPRESSIONS'),
1831 'BITWISE': ('bitwise', 'EXPRESSIONS'),
1832 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
1833 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
1834 'ASSERTION': 'assert',
1835 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
1836 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
1837 'DELETION': 'del',
1838 'RETURNING': 'return',
1839 'IMPORTING': 'import',
1840 'CONDITIONAL': 'if',
1841 'LOOPING': ('compound', 'for while break continue'),
1842 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
1843 'DEBUGGING': ('debugger', 'pdb'),
1844 'CONTEXTMANAGERS': ('context-managers', 'with'),
1845 }
1846
1847 def __init__(self, input=None, output=None):
1848 self._input = input
1849 self._output = output
1850
1851 input = property(lambda self: self._input or sys.stdin)
1852 output = property(lambda self: self._output or sys.stdout)
1853
1854 def __repr__(self):
1855 if inspect.stack()[1][3] == '?':
1856 self()
1857 return ''
1858 return '<%s.%s instance>' % (self.__class__.__module__,
1859 self.__class__.__qualname__)
1860
1861 _GoInteractive = object()
1862 def __call__(self, request=_GoInteractive):
1863 if request is not self._GoInteractive:
1864 self.help(request)
1865 else:
1866 self.intro()
1867 self.interact()
1868 self.output.write('''
1869You are now leaving help and returning to the Python interpreter.
1870If you want to ask for help on a particular object directly from the
1871interpreter, you can type "help(object)". Executing "help('string')"
1872has the same effect as typing a particular string at the help> prompt.
1873''')
1874
1875 def interact(self):
1876 self.output.write('\n')
1877 while True:
1878 try:
1879 request = self.getline('help> ')
1880 if not request: break
1881 except (KeyboardInterrupt, EOFError):
1882 break
1883 request = request.strip()
1884
1885 # Make sure significant trailing quoting marks of literals don't
1886 # get deleted while cleaning input
1887 if (len(request) > 2 and request[0] == request[-1] in ("'", '"')
1888 and request[0] not in request[1:-1]):
1889 request = request[1:-1]
1890 if request.lower() in ('q', 'quit'): break
1891 if request == 'help':
1892 self.intro()
1893 else:
1894 self.help(request)
1895
1896 def getline(self, prompt):
1897 """Read one line, using input() when appropriate."""
1898 if self.input is sys.stdin:
1899 return input(prompt)
1900 else:
1901 self.output.write(prompt)
1902 self.output.flush()
1903 return self.input.readline()
1904
1905 def help(self, request):
1906 if type(request) is type(''):
1907 request = request.strip()
1908 if request == 'keywords': self.listkeywords()
1909 elif request == 'symbols': self.listsymbols()
1910 elif request == 'topics': self.listtopics()
1911 elif request == 'modules': self.listmodules()
1912 elif request[:8] == 'modules ':
1913 self.listmodules(request.split()[1])
1914 elif request in self.symbols: self.showsymbol(request)
1915 elif request in ['True', 'False', 'None']:
1916 # special case these keywords since they are objects too
1917 doc(eval(request), 'Help on %s:')
1918 elif request in self.keywords: self.showtopic(request)
1919 elif request in self.topics: self.showtopic(request)
1920 elif request: doc(request, 'Help on %s:', output=self._output)
1921 else: doc(str, 'Help on %s:', output=self._output)
1922 elif isinstance(request, Helper): self()
1923 else: doc(request, 'Help on %s:', output=self._output)
1924 self.output.write('\n')
1925
1926 def intro(self):
1927 self.output.write('''
1928Welcome to Python {0}'s help utility!
1929
1930If this is your first time using Python, you should definitely check out
1931the tutorial on the Internet at https://docs.python.org/{0}/tutorial/.
1932
1933Enter the name of any module, keyword, or topic to get help on writing
1934Python programs and using Python modules. To quit this help utility and
1935return to the interpreter, just type "quit".
1936
1937To get a list of available modules, keywords, symbols, or topics, type
1938"modules", "keywords", "symbols", or "topics". Each module also comes
1939with a one-line summary of what it does; to list the modules whose name
1940or summary contain a given string such as "spam", type "modules spam".
1941'''.format('%d.%d' % sys.version_info[:2]))
1942
1943 def list(self, items, columns=4, width=80):
1944 items = list(sorted(items))
1945 colw = width // columns
1946 rows = (len(items) + columns - 1) // columns
1947 for row in range(rows):
1948 for col in range(columns):
1949 i = col * rows + row
1950 if i < len(items):
1951 self.output.write(items[i])
1952 if col < columns - 1:
1953 self.output.write(' ' + ' ' * (colw - 1 - len(items[i])))
1954 self.output.write('\n')
1955
1956 def listkeywords(self):
1957 self.output.write('''
1958Here is a list of the Python keywords. Enter any keyword to get more help.
1959
1960''')
1961 self.list(self.keywords.keys())
1962
1963 def listsymbols(self):
1964 self.output.write('''
1965Here is a list of the punctuation symbols which Python assigns special meaning
1966to. Enter any symbol to get more help.
1967
1968''')
1969 self.list(self.symbols.keys())
1970
1971 def listtopics(self):
1972 self.output.write('''
1973Here is a list of available topics. Enter any topic name to get more help.
1974
1975''')
1976 self.list(self.topics.keys())
1977
1978 def showtopic(self, topic, more_xrefs=''):
1979 try:
1980 import pydoc_data.topics
1981 except ImportError:
1982 self.output.write('''
1983Sorry, topic and keyword documentation is not available because the
1984module "pydoc_data.topics" could not be found.
1985''')
1986 return
1987 target = self.topics.get(topic, self.keywords.get(topic))
1988 if not target:
1989 self.output.write('no documentation found for %s\n' % repr(topic))
1990 return
1991 if type(target) is type(''):
1992 return self.showtopic(target, more_xrefs)
1993
1994 label, xrefs = target
1995 try:
1996 doc = pydoc_data.topics.topics[label]
1997 except KeyError:
1998 self.output.write('no documentation found for %s\n' % repr(topic))
1999 return
2000 doc = doc.strip() + '\n'
2001 if more_xrefs:
2002 xrefs = (xrefs or '') + ' ' + more_xrefs
2003 if xrefs:
2004 import textwrap
2005 text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n'
2006 wrapped_text = textwrap.wrap(text, 72)
2007 doc += '\n%s\n' % '\n'.join(wrapped_text)
2008 pager(doc)
2009
2010 def _gettopic(self, topic, more_xrefs=''):
2011 """Return unbuffered tuple of (topic, xrefs).
2012
2013 If an error occurs here, the exception is caught and displayed by
2014 the url handler.
2015
2016 This function duplicates the showtopic method but returns its
2017 result directly so it can be formatted for display in an html page.
2018 """
2019 try:
2020 import pydoc_data.topics
2021 except ImportError:
2022 return('''
2023Sorry, topic and keyword documentation is not available because the
2024module "pydoc_data.topics" could not be found.
2025''' , '')
2026 target = self.topics.get(topic, self.keywords.get(topic))
2027 if not target:
2028 raise ValueError('could not find topic')
2029 if isinstance(target, str):
2030 return self._gettopic(target, more_xrefs)
2031 label, xrefs = target
2032 doc = pydoc_data.topics.topics[label]
2033 if more_xrefs:
2034 xrefs = (xrefs or '') + ' ' + more_xrefs
2035 return doc, xrefs
2036
2037 def showsymbol(self, symbol):
2038 target = self.symbols[symbol]
2039 topic, _, xrefs = target.partition(' ')
2040 self.showtopic(topic, xrefs)
2041
2042 def listmodules(self, key=''):
2043 if key:
2044 self.output.write('''
2045Here is a list of modules whose name or summary contains '{}'.
2046If there are any, enter a module name to get more help.
2047
2048'''.format(key))
2049 apropos(key)
2050 else:
2051 self.output.write('''
2052Please wait a moment while I gather a list of all available modules...
2053
2054''')
2055 modules = {}
2056 def callback(path, modname, desc, modules=modules):
2057 if modname and modname[-9:] == '.__init__':
2058 modname = modname[:-9] + ' (package)'
2059 if modname.find('.') < 0:
2060 modules[modname] = 1
2061 def onerror(modname):
2062 callback(None, modname, None)
2063 ModuleScanner().run(callback, onerror=onerror)
2064 self.list(modules.keys())
2065 self.output.write('''
2066Enter any module name to get more help. Or, type "modules spam" to search
2067for modules whose name or summary contain the string "spam".
2068''')
2069
2070help = Helper()
2071
2072class ModuleScanner:
2073 """An interruptible scanner that searches module synopses."""
2074
2075 def run(self, callback, key=None, completer=None, onerror=None):
2076 if key: key = key.lower()
2077 self.quit = False
2078 seen = {}
2079
2080 for modname in sys.builtin_module_names:
2081 if modname != '__main__':
2082 seen[modname] = 1
2083 if key is None:
2084 callback(None, modname, '')
2085 else:
2086 name = __import__(modname).__doc__ or ''
2087 desc = name.split('\n')[0]
2088 name = modname + ' - ' + desc
2089 if name.lower().find(key) >= 0:
2090 callback(None, modname, desc)
2091
2092 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror):
2093 if self.quit:
2094 break
2095
2096 if key is None:
2097 callback(None, modname, '')
2098 else:
2099 try:
2100 spec = pkgutil._get_spec(importer, modname)
2101 except SyntaxError:
2102 # raised by tests for bad coding cookies or BOM
2103 continue
2104 loader = spec.loader
2105 if hasattr(loader, 'get_source'):
2106 try:
2107 source = loader.get_source(modname)
2108 except Exception:
2109 if onerror:
2110 onerror(modname)
2111 continue
2112 desc = source_synopsis(io.StringIO(source)) or ''
2113 if hasattr(loader, 'get_filename'):
2114 path = loader.get_filename(modname)
2115 else:
2116 path = None
2117 else:
2118 try:
2119 module = importlib._bootstrap._load(spec)
2120 except ImportError:
2121 if onerror:
2122 onerror(modname)
2123 continue
2124 desc = module.__doc__.splitlines()[0] if module.__doc__ else ''
2125 path = getattr(module,'__file__',None)
2126 name = modname + ' - ' + desc
2127 if name.lower().find(key) >= 0:
2128 callback(path, modname, desc)
2129
2130 if completer:
2131 completer()
2132
2133def apropos(key):
2134 """Print all the one-line module summaries that contain a substring."""
2135 def callback(path, modname, desc):
2136 if modname[-9:] == '.__init__':
2137 modname = modname[:-9] + ' (package)'
2138 print(modname, desc and '- ' + desc)
2139 def onerror(modname):
2140 pass
2141 with warnings.catch_warnings():
2142 warnings.filterwarnings('ignore') # ignore problems during import
2143 ModuleScanner().run(callback, key, onerror=onerror)
2144
2145# --------------------------------------- enhanced Web browser interface
2146
2147def _start_server(urlhandler, port):
2148 """Start an HTTP server thread on a specific port.
2149
2150 Start an HTML/text server thread, so HTML or text documents can be
2151 browsed dynamically and interactively with a Web browser. Example use:
2152
2153 >>> import time
2154 >>> import pydoc
2155
2156 Define a URL handler. To determine what the client is asking
2157 for, check the URL and content_type.
2158
2159 Then get or generate some text or HTML code and return it.
2160
2161 >>> def my_url_handler(url, content_type):
2162 ... text = 'the URL sent was: (%s, %s)' % (url, content_type)
2163 ... return text
2164
2165 Start server thread on port 0.
2166 If you use port 0, the server will pick a random port number.
2167 You can then use serverthread.port to get the port number.
2168
2169 >>> port = 0
2170 >>> serverthread = pydoc._start_server(my_url_handler, port)
2171
2172 Check that the server is really started. If it is, open browser
2173 and get first page. Use serverthread.url as the starting page.
2174
2175 >>> if serverthread.serving:
2176 ... import webbrowser
2177
2178 The next two lines are commented out so a browser doesn't open if
2179 doctest is run on this module.
2180
2181 #... webbrowser.open(serverthread.url)
2182 #True
2183
2184 Let the server do its thing. We just need to monitor its status.
2185 Use time.sleep so the loop doesn't hog the CPU.
2186
2187 >>> starttime = time.time()
2188 >>> timeout = 1 #seconds
2189
2190 This is a short timeout for testing purposes.
2191
2192 >>> while serverthread.serving:
2193 ... time.sleep(.01)
2194 ... if serverthread.serving and time.time() - starttime > timeout:
2195 ... serverthread.stop()
2196 ... break
2197
2198 Print any errors that may have occurred.
2199
2200 >>> print(serverthread.error)
2201 None
2202 """
2203 import http.server
2204 import email.message
2205 import select
2206 import threading
2207
2208 class DocHandler(http.server.BaseHTTPRequestHandler):
2209
2210 def do_GET(self):
2211 """Process a request from an HTML browser.
2212
2213 The URL received is in self.path.
2214 Get an HTML page from self.urlhandler and send it.
2215 """
2216 if self.path.endswith('.css'):
2217 content_type = 'text/css'
2218 else:
2219 content_type = 'text/html'
2220 self.send_response(200)
2221 self.send_header('Content-Type', '%s; charset=UTF-8' % content_type)
2222 self.end_headers()
2223 self.wfile.write(self.urlhandler(
2224 self.path, content_type).encode('utf-8'))
2225
2226 def log_message(self, *args):
2227 # Don't log messages.
2228 pass
2229
2230 class DocServer(http.server.HTTPServer):
2231
2232 def __init__(self, port, callback):
2233 self.host = 'localhost'
2234 self.address = (self.host, port)
2235 self.callback = callback
2236 self.base.__init__(self, self.address, self.handler)
2237 self.quit = False
2238
2239 def serve_until_quit(self):
2240 while not self.quit:
2241 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
2242 if rd:
2243 self.handle_request()
2244 self.server_close()
2245
2246 def server_activate(self):
2247 self.base.server_activate(self)
2248 if self.callback:
2249 self.callback(self)
2250
2251 class ServerThread(threading.Thread):
2252
2253 def __init__(self, urlhandler, port):
2254 self.urlhandler = urlhandler
2255 self.port = int(port)
2256 threading.Thread.__init__(self)
2257 self.serving = False
2258 self.error = None
2259
2260 def run(self):
2261 """Start the server."""
2262 try:
2263 DocServer.base = http.server.HTTPServer
2264 DocServer.handler = DocHandler
2265 DocHandler.MessageClass = email.message.Message
2266 DocHandler.urlhandler = staticmethod(self.urlhandler)
2267 docsvr = DocServer(self.port, self.ready)
2268 self.docserver = docsvr
2269 docsvr.serve_until_quit()
2270 except Exception as e:
2271 self.error = e
2272
2273 def ready(self, server):
2274 self.serving = True
2275 self.host = server.host
2276 self.port = server.server_port
2277 self.url = 'http://%s:%d/' % (self.host, self.port)
2278
2279 def stop(self):
2280 """Stop the server and this thread nicely"""
2281 self.docserver.quit = True
2282 self.join()
2283 # explicitly break a reference cycle: DocServer.callback
2284 # has indirectly a reference to ServerThread.
2285 self.docserver = None
2286 self.serving = False
2287 self.url = None
2288
2289 thread = ServerThread(urlhandler, port)
2290 thread.start()
2291 # Wait until thread.serving is True to make sure we are
2292 # really up before returning.
2293 while not thread.error and not thread.serving:
2294 time.sleep(.01)
2295 return thread
2296
2297
2298def _url_handler(url, content_type="text/html"):
2299 """The pydoc url handler for use with the pydoc server.
2300
2301 If the content_type is 'text/css', the _pydoc.css style
2302 sheet is read and returned if it exits.
2303
2304 If the content_type is 'text/html', then the result of
2305 get_html_page(url) is returned.
2306 """
2307 class _HTMLDoc(HTMLDoc):
2308
2309 def page(self, title, contents):
2310 """Format an HTML page."""
2311 css_path = "pydoc_data/_pydoc.css"
2312 css_link = (
2313 '<link rel="stylesheet" type="text/css" href="%s">' %
2314 css_path)
2315 return '''\
2316<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2317<html><head><title>Pydoc: %s</title>
2318<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
2319%s</head><body bgcolor="#f0f0f8">%s<div style="clear:both;padding-top:.5em;">%s</div>
2320</body></html>''' % (title, css_link, html_navbar(), contents)
2321
2322 def filelink(self, url, path):
2323 return '<a href="getfile?key=%s">%s</a>' % (url, path)
2324
2325
2326 html = _HTMLDoc()
2327
2328 def html_navbar():
2329 version = html.escape("%s [%s, %s]" % (platform.python_version(),
2330 platform.python_build()[0],
2331 platform.python_compiler()))
2332 return """
2333 <div style='float:left'>
2334 Python %s<br>%s
2335 </div>
2336 <div style='float:right'>
2337 <div style='text-align:center'>
2338 <a href="index.html">Module Index</a>
2339 : <a href="topics.html">Topics</a>
2340 : <a href="keywords.html">Keywords</a>
2341 </div>
2342 <div>
2343 <form action="get" style='display:inline;'>
2344 <input type=text name=key size=15>
2345 <input type=submit value="Get">
2346 </form>
2347 <form action="search" style='display:inline;'>
2348 <input type=text name=key size=15>
2349 <input type=submit value="Search">
2350 </form>
2351 </div>
2352 </div>
2353 """ % (version, html.escape(platform.platform(terse=True)))
2354
2355 def html_index():
2356 """Module Index page."""
2357
2358 def bltinlink(name):
2359 return '<a href="%s.html">%s</a>' % (name, name)
2360
2361 heading = html.heading(
2362 '<big><big><strong>Index of Modules</strong></big></big>',
2363 '#ffffff', '#7799ee')
2364 names = [name for name in sys.builtin_module_names
2365 if name != '__main__']
2366 contents = html.multicolumn(names, bltinlink)
2367 contents = [heading, '<p>' + html.bigsection(
2368 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
2369
2370 seen = {}
2371 for dir in sys.path:
2372 contents.append(html.index(dir, seen))
2373
2374 contents.append(
2375 '<p align=right><font color="#909090" face="helvetica,'
2376 'arial"><strong>pydoc</strong> by Ka-Ping Yee'
2377 '<ping@lfw.org></font>')
2378 return 'Index of Modules', ''.join(contents)
2379
2380 def html_search(key):
2381 """Search results page."""
2382 # scan for modules
2383 search_result = []
2384
2385 def callback(path, modname, desc):
2386 if modname[-9:] == '.__init__':
2387 modname = modname[:-9] + ' (package)'
2388 search_result.append((modname, desc and '- ' + desc))
2389
2390 with warnings.catch_warnings():
2391 warnings.filterwarnings('ignore') # ignore problems during import
2392 def onerror(modname):
2393 pass
2394 ModuleScanner().run(callback, key, onerror=onerror)
2395
2396 # format page
2397 def bltinlink(name):
2398 return '<a href="%s.html">%s</a>' % (name, name)
2399
2400 results = []
2401 heading = html.heading(
2402 '<big><big><strong>Search Results</strong></big></big>',
2403 '#ffffff', '#7799ee')
2404 for name, desc in search_result:
2405 results.append(bltinlink(name) + desc)
2406 contents = heading + html.bigsection(
2407 'key = %s' % key, '#ffffff', '#ee77aa', '<br>'.join(results))
2408 return 'Search Results', contents
2409
2410 def html_getfile(path):
2411 """Get and display a source file listing safely."""
2412 path = urllib.parse.unquote(path)
2413 with tokenize.open(path) as fp:
2414 lines = html.escape(fp.read())
2415 body = '<pre>%s</pre>' % lines
2416 heading = html.heading(
2417 '<big><big><strong>File Listing</strong></big></big>',
2418 '#ffffff', '#7799ee')
2419 contents = heading + html.bigsection(
2420 'File: %s' % path, '#ffffff', '#ee77aa', body)
2421 return 'getfile %s' % path, contents
2422
2423 def html_topics():
2424 """Index of topic texts available."""
2425
2426 def bltinlink(name):
2427 return '<a href="topic?key=%s">%s</a>' % (name, name)
2428
2429 heading = html.heading(
2430 '<big><big><strong>INDEX</strong></big></big>',
2431 '#ffffff', '#7799ee')
2432 names = sorted(Helper.topics.keys())
2433
2434 contents = html.multicolumn(names, bltinlink)
2435 contents = heading + html.bigsection(
2436 'Topics', '#ffffff', '#ee77aa', contents)
2437 return 'Topics', contents
2438
2439 def html_keywords():
2440 """Index of keywords."""
2441 heading = html.heading(
2442 '<big><big><strong>INDEX</strong></big></big>',
2443 '#ffffff', '#7799ee')
2444 names = sorted(Helper.keywords.keys())
2445
2446 def bltinlink(name):
2447 return '<a href="topic?key=%s">%s</a>' % (name, name)
2448
2449 contents = html.multicolumn(names, bltinlink)
2450 contents = heading + html.bigsection(
2451 'Keywords', '#ffffff', '#ee77aa', contents)
2452 return 'Keywords', contents
2453
2454 def html_topicpage(topic):
2455 """Topic or keyword help page."""
2456 buf = io.StringIO()
2457 htmlhelp = Helper(buf, buf)
2458 contents, xrefs = htmlhelp._gettopic(topic)
2459 if topic in htmlhelp.keywords:
2460 title = 'KEYWORD'
2461 else:
2462 title = 'TOPIC'
2463 heading = html.heading(
2464 '<big><big><strong>%s</strong></big></big>' % title,
2465 '#ffffff', '#7799ee')
2466 contents = '<pre>%s</pre>' % html.markup(contents)
2467 contents = html.bigsection(topic , '#ffffff','#ee77aa', contents)
2468 if xrefs:
2469 xrefs = sorted(xrefs.split())
2470
2471 def bltinlink(name):
2472 return '<a href="topic?key=%s">%s</a>' % (name, name)
2473
2474 xrefs = html.multicolumn(xrefs, bltinlink)
2475 xrefs = html.section('Related help topics: ',
2476 '#ffffff', '#ee77aa', xrefs)
2477 return ('%s %s' % (title, topic),
2478 ''.join((heading, contents, xrefs)))
2479
2480 def html_getobj(url):
2481 obj = locate(url, forceload=1)
2482 if obj is None and url != 'None':
2483 raise ValueError('could not find object')
2484 title = describe(obj)
2485 content = html.document(obj, url)
2486 return title, content
2487
2488 def html_error(url, exc):
2489 heading = html.heading(
2490 '<big><big><strong>Error</strong></big></big>',
2491 '#ffffff', '#7799ee')
2492 contents = '<br>'.join(html.escape(line) for line in
2493 format_exception_only(type(exc), exc))
2494 contents = heading + html.bigsection(url, '#ffffff', '#bb0000',
2495 contents)
2496 return "Error - %s" % url, contents
2497
2498 def get_html_page(url):
2499 """Generate an HTML page for url."""
2500 complete_url = url
2501 if url.endswith('.html'):
2502 url = url[:-5]
2503 try:
2504 if url in ("", "index"):
2505 title, content = html_index()
2506 elif url == "topics":
2507 title, content = html_topics()
2508 elif url == "keywords":
2509 title, content = html_keywords()
2510 elif '=' in url:
2511 op, _, url = url.partition('=')
2512 if op == "search?key":
2513 title, content = html_search(url)
2514 elif op == "getfile?key":
2515 title, content = html_getfile(url)
2516 elif op == "topic?key":
2517 # try topics first, then objects.
2518 try:
2519 title, content = html_topicpage(url)
2520 except ValueError:
2521 title, content = html_getobj(url)
2522 elif op == "get?key":
2523 # try objects first, then topics.
2524 if url in ("", "index"):
2525 title, content = html_index()
2526 else:
2527 try:
2528 title, content = html_getobj(url)
2529 except ValueError:
2530 title, content = html_topicpage(url)
2531 else:
2532 raise ValueError('bad pydoc url')
2533 else:
2534 title, content = html_getobj(url)
2535 except Exception as exc:
2536 # Catch any errors and display them in an error page.
2537 title, content = html_error(complete_url, exc)
2538 return html.page(title, content)
2539
2540 if url.startswith('/'):
2541 url = url[1:]
2542 if content_type == 'text/css':
2543 path_here = os.path.dirname(os.path.realpath(__file__))
2544 css_path = os.path.join(path_here, url)
2545 with open(css_path) as fp:
2546 return ''.join(fp.readlines())
2547 elif content_type == 'text/html':
2548 return get_html_page(url)
2549 # Errors outside the url handler are caught by the server.
2550 raise TypeError('unknown content type %r for url %s' % (content_type, url))
2551
2552
2553def browse(port=0, *, open_browser=True):
2554 """Start the enhanced pydoc Web server and open a Web browser.
2555
2556 Use port '0' to start the server on an arbitrary port.
2557 Set open_browser to False to suppress opening a browser.
2558 """
2559 import webbrowser
2560 serverthread = _start_server(_url_handler, port)
2561 if serverthread.error:
2562 print(serverthread.error)
2563 return
2564 if serverthread.serving:
2565 server_help_msg = 'Server commands: [b]rowser, [q]uit'
2566 if open_browser:
2567 webbrowser.open(serverthread.url)
2568 try:
2569 print('Server ready at', serverthread.url)
2570 print(server_help_msg)
2571 while serverthread.serving:
2572 cmd = input('server> ')
2573 cmd = cmd.lower()
2574 if cmd == 'q':
2575 break
2576 elif cmd == 'b':
2577 webbrowser.open(serverthread.url)
2578 else:
2579 print(server_help_msg)
2580 except (KeyboardInterrupt, EOFError):
2581 print()
2582 finally:
2583 if serverthread.serving:
2584 serverthread.stop()
2585 print('Server stopped')
2586
2587
2588# -------------------------------------------------- command-line interface
2589
2590def ispath(x):
2591 return isinstance(x, str) and x.find(os.sep) >= 0
2592
2593def cli():
2594 """Command-line interface (looks at sys.argv to decide what to do)."""
2595 import getopt
2596 class BadUsage(Exception): pass
2597
2598 # Scripts don't get the current directory in their path by default
2599 # unless they are run with the '-m' switch
2600 if '' not in sys.path:
2601 scriptdir = os.path.dirname(sys.argv[0])
2602 if scriptdir in sys.path:
2603 sys.path.remove(scriptdir)
2604 sys.path.insert(0, '.')
2605
2606 try:
2607 opts, args = getopt.getopt(sys.argv[1:], 'bk:p:w')
2608 writing = False
2609 start_server = False
2610 open_browser = False
2611 port = None
2612 for opt, val in opts:
2613 if opt == '-b':
2614 start_server = True
2615 open_browser = True
2616 if opt == '-k':
2617 apropos(val)
2618 return
2619 if opt == '-p':
2620 start_server = True
2621 port = val
2622 if opt == '-w':
2623 writing = True
2624
2625 if start_server:
2626 if port is None:
2627 port = 0
2628 browse(port, open_browser=open_browser)
2629 return
2630
2631 if not args: raise BadUsage
2632 for arg in args:
2633 if ispath(arg) and not os.path.exists(arg):
2634 print('file %r does not exist' % arg)
2635 break
2636 try:
2637 if ispath(arg) and os.path.isfile(arg):
2638 arg = importfile(arg)
2639 if writing:
2640 if ispath(arg) and os.path.isdir(arg):
2641 writedocs(arg)
2642 else:
2643 writedoc(arg)
2644 else:
2645 help.help(arg)
2646 except ErrorDuringImport as value:
2647 print(value)
2648
2649 except (getopt.error, BadUsage):
2650 cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0]
2651 print("""pydoc - the Python documentation tool
2652
2653{cmd} <name> ...
2654 Show text documentation on something. <name> may be the name of a
2655 Python keyword, topic, function, module, or package, or a dotted
2656 reference to a class or function within a module or module in a
2657 package. If <name> contains a '{sep}', it is used as the path to a
2658 Python source file to document. If name is 'keywords', 'topics',
2659 or 'modules', a listing of these things is displayed.
2660
2661{cmd} -k <keyword>
2662 Search for a keyword in the synopsis lines of all available modules.
2663
2664{cmd} -p <port>
2665 Start an HTTP server on the given port on the local machine. Port
2666 number 0 can be used to get an arbitrary unused port.
2667
2668{cmd} -b
2669 Start an HTTP server on an arbitrary unused port and open a Web browser
2670 to interactively browse documentation. The -p option can be used with
2671 the -b option to explicitly specify the server port.
2672
2673{cmd} -w <name> ...
2674 Write out the HTML documentation for a module to a file in the current
2675 directory. If <name> contains a '{sep}', it is treated as a filename; if
2676 it names a directory, documentation is written for all the contents.
2677""".format(cmd=cmd, sep=os.sep))
2678
2679if __name__ == '__main__':
2680 cli()