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