ยปCore Development>Code coverage>Lib/pydoc.py

Python code coverage for Lib/pydoc.py

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