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