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