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