python (3.11.7)

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