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