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