1#!/usr/bin/env python3 2"""Generate Python documentation in HTML or text for interactive use. 3 4At the Python interactive prompt, calling help(thing) on a Python object 5documents the object, and calling help() starts up an interactive 6help session. 7 8Or, at the shell command line outside of Python: 9 10Run "pydoc <name>" to show documentation on something. <name> may be 11the name of a function, module, package, or a dotted reference to a 12class or function within a module or module in a package. If the 13argument contains a path segment delimiter (e.g. slash on Unix, 14backslash on Windows) it is treated as the path to a Python source file. 15 16Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines 17of all available modules. 18 19Run "pydoc -n <hostname>" to start an HTTP server with the given 20hostname (default: localhost) on the local machine. 21 22Run "pydoc -p <port>" to start an HTTP server on the given port on the 23local machine. Port number 0 can be used to get an arbitrary unused port. 24 25Run "pydoc -b" to start an HTTP server on an arbitrary unused port and 26open a web browser to interactively browse documentation. Combine with 27the -n and -p options to control the hostname and port used. 28 29Run "pydoc -w <name>" to write out the HTML documentation for a module 30to a file named "<name>.html". 31 32Module docs for core modules are assumed to be in 33 34 https://docs.python.org/X.Y/library/ 35 36This can be overridden by setting the PYTHONDOCS environment variable 37to a different URL or to a local directory containing the Library 38Reference Manual pages. 39""" 40__all__ = ['help'] 41__author__ = "Ka-Ping Yee <[email protected]>" 42__date__ = "26 February 2001" 43 44__credits__ = """Guido van Rossum, for an excellent programming language. 45Tommy Burnette, the original creator of manpy. 46Paul Prescod, for all his work on onlinehelp. 47Richard Chamberlain, for the first implementation of textdoc. 48""" 49 50# Known bugs that can't be fixed here: 51# - synopsis() cannot be prevented from clobbering existing 52# loaded modules. 53# - If the __file__ attribute on a module is a relative path and 54# the current directory is changed with os.chdir(), an incorrect 55# path will be displayed. 56 57import __future__ 58import builtins 59import importlib._bootstrap 60import importlib._bootstrap_external 61import importlib.machinery 62import importlib.util 63import inspect 64import io 65import os 66import pkgutil 67import platform 68import re 69import sys 70import sysconfig 71import time 72import tokenize 73import urllib.parse 74import warnings 75from collections import deque 76from reprlib import Repr 77from traceback import format_exception_only 78 79 80# --------------------------------------------------------- common routines 81 82def 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 94def _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 104def _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 155def _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 170def _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 186def 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 191def 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 200def 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 207def 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 213def 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 220def 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) 229def 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 234def _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 247def 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 257def _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 276def 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 300def 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 311def 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 325def 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 333def 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 350def 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 389class ErrorDuringImport(Exception): 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 399def 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 417def 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 465class Doc: 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 523class HTMLRepr(Repr): 524 """Class for safely making an HTML representation of a Python object.""" 525 def __init__(self): 526 Repr.__init__(self) 527 self.maxlist = self.maxtuple = 20 528 self.maxdict = 10 529 self.maxstring = self.maxother = 100 530 531 def escape(self, text): 532 return replace(text, '&', '&', '<', '<', '>', '>') 533 534 def repr(self, object): 535 return Repr.repr(self, object) 536 537 def repr1(self, x, level): 538 if hasattr(type(x), '__name__'): 539 methodname = 'repr_' + '_'.join(type(x).__name__.split()) 540 if hasattr(self, methodname): 541 return getattr(self, methodname)(x, level) 542 return self.escape(cram(stripid(repr(x)), self.maxother)) 543 544 def repr_string(self, x, level): 545 test = cram(x, self.maxstring) 546 testrepr = repr(test) 547 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): 548 # Backslashes are only literal in the string and are never 549 # needed to make any special characters, so show a raw string. 550 return 'r' + testrepr[0] + self.escape(test) + testrepr[0] 551 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)', 552 r'<span class="repr">\1</span>', 553 self.escape(testrepr)) 554 555 repr_str = repr_string 556 557 def repr_instance(self, x, level): 558 try: 559 return self.escape(cram(stripid(repr(x)), self.maxstring)) 560 except: 561 return self.escape('<%s instance>' % x.__class__.__name__) 562 563 repr_unicode = repr_string 564 565class HTMLDoc(Doc): 566 """Formatter class for HTML documentation.""" 567 568 # ------------------------------------------- HTML formatting utilities 569 570 _repr_instance = HTMLRepr() 571 repr = _repr_instance.repr 572 escape = _repr_instance.escape 573 574 def page(self, title, contents): 575 """Format an HTML page.""" 576 return '''\ 577<!DOCTYPE html> 578<html lang="en"> 579<head> 580<meta charset="utf-8"> 581<title>Python: %s</title> 582</head><body> 583%s 584</body></html>''' % (title, contents) 585 586 def heading(self, title, extras=''): 587 """Format a page heading.""" 588 return ''' 589<table class="heading"> 590<tr class="heading-text decor"> 591<td class="title"> <br>%s</td> 592<td class="extra">%s</td></tr></table> 593 ''' % (title, extras or ' ') 594 595 def section(self, title, cls, contents, width=6, 596 prelude='', marginalia=None, gap=' '): 597 """Format a section with a heading.""" 598 if marginalia is None: 599 marginalia = '<span class="code">' + ' ' * width + '</span>' 600 result = '''<p> 601<table class="section"> 602<tr class="decor %s-decor heading-text"> 603<td class="section-title" colspan=3> <br>%s</td></tr> 604 ''' % (cls, title) 605 if prelude: 606 result = result + ''' 607<tr><td class="decor %s-decor" rowspan=2>%s</td> 608<td class="decor %s-decor" colspan=2>%s</td></tr> 609<tr><td>%s</td>''' % (cls, marginalia, cls, prelude, gap) 610 else: 611 result = result + ''' 612<tr><td class="decor %s-decor">%s</td><td>%s</td>''' % (cls, marginalia, gap) 613 614 return result + '\n<td class="singlecolumn">%s</td></tr></table>' % contents 615 616 def bigsection(self, title, *args): 617 """Format a section with a big heading.""" 618 title = '<strong class="bigsection">%s</strong>' % title 619 return self.section(title, *args) 620 621 def preformat(self, text): 622 """Format literal preformatted text.""" 623 text = self.escape(text.expandtabs()) 624 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n', 625 ' ', ' ', '\n', '<br>\n') 626 627 def multicolumn(self, list, format): 628 """Format a list of items into a multi-column list.""" 629 result = '' 630 rows = (len(list) + 3) // 4 631 for col in range(4): 632 result = result + '<td class="multicolumn">' 633 for i in range(rows*col, rows*col+rows): 634 if i < len(list): 635 result = result + format(list[i]) + '<br>\n' 636 result = result + '</td>' 637 return '<table><tr>%s</tr></table>' % result 638 639 def grey(self, text): return '<span class="grey">%s</span>' % text 640 641 def namelink(self, name, *dicts): 642 """Make a link for an identifier, given name-to-URL mappings.""" 643 for dict in dicts: 644 if name in dict: 645 return '<a href="%s">%s</a>' % (dict[name], name) 646 return name 647 648 def classlink(self, object, modname): 649 """Make a link for a class.""" 650 name, module = object.__name__, sys.modules.get(object.__module__) 651 if hasattr(module, name) and getattr(module, name) is object: 652 return '<a href="%s.html#%s">%s</a>' % ( 653 module.__name__, name, classname(object, modname)) 654 return classname(object, modname) 655 656 def modulelink(self, object): 657 """Make a link for a module.""" 658 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__) 659 660 def modpkglink(self, modpkginfo): 661 """Make a link for a module or package to display in an index.""" 662 name, path, ispackage, shadowed = modpkginfo 663 if shadowed: 664 return self.grey(name) 665 if path: 666 url = '%s.%s.html' % (path, name) 667 else: 668 url = '%s.html' % name 669 if ispackage: 670 text = '<strong>%s</strong> (package)' % name 671 else: 672 text = name 673 return '<a href="%s">%s</a>' % (url, text) 674 675 def filelink(self, url, path): 676 """Make a link to source file.""" 677 return '<a href="file:%s">%s</a>' % (url, path) 678 679 def markup(self, text, escape=None, funcs={}, classes={}, methods={}): 680 """Mark up some plain text, given a context of symbols to look for. 681 Each context dictionary maps object names to anchor names.""" 682 escape = escape or self.escape 683 results = [] 684 here = 0 685 pattern = re.compile(r'\b((http|https|ftp)://\S+[\w/]|' 686 r'RFC[- ]?(\d+)|' 687 r'PEP[- ]?(\d+)|' 688 r'(self\.)?(\w+))') 689 while True: 690 match = pattern.search(text, here) 691 if not match: break 692 start, end = match.span() 693 results.append(escape(text[here:start])) 694 695 all, scheme, rfc, pep, selfdot, name = match.groups() 696 if scheme: 697 url = escape(all).replace('"', '"') 698 results.append('<a href="%s">%s</a>' % (url, url)) 699 elif rfc: 700 url = 'https://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) 701 results.append('<a href="%s">%s</a>' % (url, escape(all))) 702 elif pep: 703 url = 'https://peps.python.org/pep-%04d/' % int(pep) 704 results.append('<a href="%s">%s</a>' % (url, escape(all))) 705 elif selfdot: 706 # Create a link for methods like 'self.method(...)' 707 # and use <strong> for attributes like 'self.attr' 708 if text[end:end+1] == '(': 709 results.append('self.' + self.namelink(name, methods)) 710 else: 711 results.append('self.<strong>%s</strong>' % name) 712 elif text[end:end+1] == '(': 713 results.append(self.namelink(name, methods, funcs, classes)) 714 else: 715 results.append(self.namelink(name, classes)) 716 here = end 717 results.append(escape(text[here:])) 718 return ''.join(results) 719 720 # ---------------------------------------------- type-specific routines 721 722 def formattree(self, tree, modname, parent=None): 723 """Produce HTML for a class tree as given by inspect.getclasstree().""" 724 result = '' 725 for entry in tree: 726 if type(entry) is type(()): 727 c, bases = entry 728 result = result + '<dt class="heading-text">' 729 result = result + self.classlink(c, modname) 730 if bases and bases != (parent,): 731 parents = [] 732 for base in bases: 733 parents.append(self.classlink(base, modname)) 734 result = result + '(' + ', '.join(parents) + ')' 735 result = result + '\n</dt>' 736 elif type(entry) is type([]): 737 result = result + '<dd>\n%s</dd>\n' % self.formattree( 738 entry, modname, c) 739 return '<dl>\n%s</dl>\n' % result 740 741 def docmodule(self, object, name=None, mod=None, *ignored): 742 """Produce HTML documentation for a module object.""" 743 name = object.__name__ # ignore the passed-in name 744 try: 745 all = object.__all__ 746 except AttributeError: 747 all = None 748 parts = name.split('.') 749 links = [] 750 for i in range(len(parts)-1): 751 links.append( 752 '<a href="%s.html" class="white">%s</a>' % 753 ('.'.join(parts[:i+1]), parts[i])) 754 linkedname = '.'.join(links + parts[-1:]) 755 head = '<strong class="title">%s</strong>' % linkedname 756 try: 757 path = inspect.getabsfile(object) 758 url = urllib.parse.quote(path) 759 filelink = self.filelink(url, path) 760 except TypeError: 761 filelink = '(built-in)' 762 info = [] 763 if hasattr(object, '__version__'): 764 version = str(object.__version__) 765 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': 766 version = version[11:-1].strip() 767 info.append('version %s' % self.escape(version)) 768 if hasattr(object, '__date__'): 769 info.append(self.escape(str(object.__date__))) 770 if info: 771 head = head + ' (%s)' % ', '.join(info) 772 docloc = self.getdocloc(object) 773 if docloc is not None: 774 docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals() 775 else: 776 docloc = '' 777 result = self.heading(head, '<a href=".">index</a><br>' + filelink + docloc) 778 779 modules = inspect.getmembers(object, inspect.ismodule) 780 781 classes, cdict = [], {} 782 for key, value in inspect.getmembers(object, inspect.isclass): 783 # if __all__ exists, believe it. Otherwise use old heuristic. 784 if (all is not None or 785 (inspect.getmodule(value) or object) is object): 786 if visiblename(key, all, object): 787 classes.append((key, value)) 788 cdict[key] = cdict[value] = '#' + key 789 for key, value in classes: 790 for base in value.__bases__: 791 key, modname = base.__name__, base.__module__ 792 module = sys.modules.get(modname) 793 if modname != name and module and hasattr(module, key): 794 if getattr(module, key) is base: 795 if not key in cdict: 796 cdict[key] = cdict[base] = modname + '.html#' + key 797 funcs, fdict = [], {} 798 for key, value in inspect.getmembers(object, inspect.isroutine): 799 # if __all__ exists, believe it. Otherwise use old heuristic. 800 if (all is not None or 801 inspect.isbuiltin(value) or inspect.getmodule(value) is object): 802 if visiblename(key, all, object): 803 funcs.append((key, value)) 804 fdict[key] = '#-' + key 805 if inspect.isfunction(value): fdict[value] = fdict[key] 806 data = [] 807 for key, value in inspect.getmembers(object, isdata): 808 if visiblename(key, all, object): 809 data.append((key, value)) 810 811 doc = self.markup(getdoc(object), self.preformat, fdict, cdict) 812 doc = doc and '<span class="code">%s</span>' % doc 813 result = result + '<p>%s</p>\n' % doc 814 815 if hasattr(object, '__path__'): 816 modpkgs = [] 817 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): 818 modpkgs.append((modname, name, ispkg, 0)) 819 modpkgs.sort() 820 contents = self.multicolumn(modpkgs, self.modpkglink) 821 result = result + self.bigsection( 822 'Package Contents', 'pkg-content', contents) 823 elif modules: 824 contents = self.multicolumn( 825 modules, lambda t: self.modulelink(t[1])) 826 result = result + self.bigsection( 827 'Modules', 'pkg-content', contents) 828 829 if classes: 830 classlist = [value for (key, value) in classes] 831 contents = [ 832 self.formattree(inspect.getclasstree(classlist, 1), name)] 833 for key, value in classes: 834 contents.append(self.document(value, key, name, fdict, cdict)) 835 result = result + self.bigsection( 836 'Classes', 'index', ' '.join(contents)) 837 if funcs: 838 contents = [] 839 for key, value in funcs: 840 contents.append(self.document(value, key, name, fdict, cdict)) 841 result = result + self.bigsection( 842 'Functions', 'functions', ' '.join(contents)) 843 if data: 844 contents = [] 845 for key, value in data: 846 contents.append(self.document(value, key)) 847 result = result + self.bigsection( 848 'Data', 'data', '<br>\n'.join(contents)) 849 if hasattr(object, '__author__'): 850 contents = self.markup(str(object.__author__), self.preformat) 851 result = result + self.bigsection('Author', 'author', contents) 852 if hasattr(object, '__credits__'): 853 contents = self.markup(str(object.__credits__), self.preformat) 854 result = result + self.bigsection('Credits', 'credits', contents) 855 856 return result 857 858 def docclass(self, object, name=None, mod=None, funcs={}, classes={}, 859 *ignored): 860 """Produce HTML documentation for a class object.""" 861 realname = object.__name__ 862 name = name or realname 863 bases = object.__bases__ 864 865 contents = [] 866 push = contents.append 867 868 # Cute little class to pump out a horizontal rule between sections. 869 class HorizontalRule: 870 def __init__(self): 871 self.needone = 0 872 def maybe(self): 873 if self.needone: 874 push('<hr>\n') 875 self.needone = 1 876 hr = HorizontalRule() 877 878 # List the mro, if non-trivial. 879 mro = deque(inspect.getmro(object)) 880 if len(mro) > 2: 881 hr.maybe() 882 push('<dl><dt>Method resolution order:</dt>\n') 883 for base in mro: 884 push('<dd>%s</dd>\n' % self.classlink(base, 885 object.__module__)) 886 push('</dl>\n') 887 888 def spill(msg, attrs, predicate): 889 ok, attrs = _split_list(attrs, predicate) 890 if ok: 891 hr.maybe() 892 push(msg) 893 for name, kind, homecls, value in ok: 894 try: 895 value = getattr(object, name) 896 except Exception: 897 # Some descriptors may meet a failure in their __get__. 898 # (bug #1785) 899 push(self.docdata(value, name, mod)) 900 else: 901 push(self.document(value, name, mod, 902 funcs, classes, mdict, object)) 903 push('\n') 904 return attrs 905 906 def spilldescriptors(msg, attrs, predicate): 907 ok, attrs = _split_list(attrs, predicate) 908 if ok: 909 hr.maybe() 910 push(msg) 911 for name, kind, homecls, value in ok: 912 push(self.docdata(value, name, mod)) 913 return attrs 914 915 def spilldata(msg, attrs, predicate): 916 ok, attrs = _split_list(attrs, predicate) 917 if ok: 918 hr.maybe() 919 push(msg) 920 for name, kind, homecls, value in ok: 921 base = self.docother(getattr(object, name), name, mod) 922 doc = getdoc(value) 923 if not doc: 924 push('<dl><dt>%s</dl>\n' % base) 925 else: 926 doc = self.markup(getdoc(value), self.preformat, 927 funcs, classes, mdict) 928 doc = '<dd><span class="code">%s</span>' % doc 929 push('<dl><dt>%s%s</dl>\n' % (base, doc)) 930 push('\n') 931 return attrs 932 933 attrs = [(name, kind, cls, value) 934 for name, kind, cls, value in classify_class_attrs(object) 935 if visiblename(name, obj=object)] 936 937 mdict = {} 938 for key, kind, homecls, value in attrs: 939 mdict[key] = anchor = '#' + name + '-' + key 940 try: 941 value = getattr(object, name) 942 except Exception: 943 # Some descriptors may meet a failure in their __get__. 944 # (bug #1785) 945 pass 946 try: 947 # The value may not be hashable (e.g., a data attr with 948 # a dict or list value). 949 mdict[value] = anchor 950 except TypeError: 951 pass 952 953 while attrs: 954 if mro: 955 thisclass = mro.popleft() 956 else: 957 thisclass = attrs[0][2] 958 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 959 960 if object is not builtins.object and thisclass is builtins.object: 961 attrs = inherited 962 continue 963 elif thisclass is object: 964 tag = 'defined here' 965 else: 966 tag = 'inherited from %s' % self.classlink(thisclass, 967 object.__module__) 968 tag += ':<br>\n' 969 970 sort_attributes(attrs, object) 971 972 # Pump out the attrs, segregated by kind. 973 attrs = spill('Methods %s' % tag, attrs, 974 lambda t: t[1] == 'method') 975 attrs = spill('Class methods %s' % tag, attrs, 976 lambda t: t[1] == 'class method') 977 attrs = spill('Static methods %s' % tag, attrs, 978 lambda t: t[1] == 'static method') 979 attrs = spilldescriptors("Readonly properties %s" % tag, attrs, 980 lambda t: t[1] == 'readonly property') 981 attrs = spilldescriptors('Data descriptors %s' % tag, attrs, 982 lambda t: t[1] == 'data descriptor') 983 attrs = spilldata('Data and other attributes %s' % tag, attrs, 984 lambda t: t[1] == 'data') 985 assert attrs == [] 986 attrs = inherited 987 988 contents = ''.join(contents) 989 990 if name == realname: 991 title = '<a name="%s">class <strong>%s</strong></a>' % ( 992 name, realname) 993 else: 994 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % ( 995 name, name, realname) 996 if bases: 997 parents = [] 998 for base in bases: 999 parents.append(self.classlink(base, object.__module__)) 1000 title = title + '(%s)' % ', '.join(parents) 1001 1002 decl = '' 1003 try: 1004 signature = inspect.signature(object) 1005 except (ValueError, TypeError): 1006 signature = None 1007 if signature: 1008 argspec = str(signature) 1009 if argspec and argspec != '()': 1010 decl = name + self.escape(argspec) + '\n\n' 1011 1012 doc = getdoc(object) 1013 if decl: 1014 doc = decl + (doc or '') 1015 doc = self.markup(doc, self.preformat, funcs, classes, mdict) 1016 doc = doc and '<span class="code">%s<br> </span>' % doc 1017 1018 return self.section(title, 'title', contents, 3, doc) 1019 1020 def formatvalue(self, object): 1021 """Format an argument default value as text.""" 1022 return self.grey('=' + self.repr(object)) 1023 1024 def docroutine(self, object, name=None, mod=None, 1025 funcs={}, classes={}, methods={}, cl=None): 1026 """Produce HTML documentation for a function or method object.""" 1027 realname = object.__name__ 1028 name = name or realname 1029 anchor = (cl and cl.__name__ or '') + '-' + name 1030 note = '' 1031 skipdocs = 0 1032 if _is_bound_method(object): 1033 imclass = object.__self__.__class__ 1034 if cl: 1035 if imclass is not cl: 1036 note = ' from ' + self.classlink(imclass, mod) 1037 else: 1038 if object.__self__ is not None: 1039 note = ' method of %s instance' % self.classlink( 1040 object.__self__.__class__, mod) 1041 else: 1042 note = ' unbound %s method' % self.classlink(imclass,mod) 1043 1044 if (inspect.iscoroutinefunction(object) or 1045 inspect.isasyncgenfunction(object)): 1046 asyncqualifier = 'async ' 1047 else: 1048 asyncqualifier = '' 1049 1050 if name == realname: 1051 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname) 1052 else: 1053 if cl and inspect.getattr_static(cl, realname, []) is object: 1054 reallink = '<a href="#%s">%s</a>' % ( 1055 cl.__name__ + '-' + realname, realname) 1056 skipdocs = 1 1057 else: 1058 reallink = realname 1059 title = '<a name="%s"><strong>%s</strong></a> = %s' % ( 1060 anchor, name, reallink) 1061 argspec = None 1062 if inspect.isroutine(object): 1063 try: 1064 signature = inspect.signature(object) 1065 except (ValueError, TypeError): 1066 signature = None 1067 if signature: 1068 argspec = str(signature) 1069 if realname == '<lambda>': 1070 title = '<strong>%s</strong> <em>lambda</em> ' % name 1071 # XXX lambda's won't usually have func_annotations['return'] 1072 # since the syntax doesn't support but it is possible. 1073 # So removing parentheses isn't truly safe. 1074 argspec = argspec[1:-1] # remove parentheses 1075 if not argspec: 1076 argspec = '(...)' 1077 1078 decl = asyncqualifier + title + self.escape(argspec) + (note and 1079 self.grey('<span class="heading-text">%s</span>' % note)) 1080 1081 if skipdocs: 1082 return '<dl><dt>%s</dt></dl>\n' % decl 1083 else: 1084 doc = self.markup( 1085 getdoc(object), self.preformat, funcs, classes, methods) 1086 doc = doc and '<dd><span class="code">%s</span></dd>' % doc 1087 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) 1088 1089 def docdata(self, object, name=None, mod=None, cl=None): 1090 """Produce html documentation for a data descriptor.""" 1091 results = [] 1092 push = results.append 1093 1094 if name: 1095 push('<dl><dt><strong>%s</strong></dt>\n' % name) 1096 doc = self.markup(getdoc(object), self.preformat) 1097 if doc: 1098 push('<dd><span class="code">%s</span></dd>\n' % doc) 1099 push('</dl>\n') 1100 1101 return ''.join(results) 1102 1103 docproperty = docdata 1104 1105 def docother(self, object, name=None, mod=None, *ignored): 1106 """Produce HTML documentation for a data object.""" 1107 lhs = name and '<strong>%s</strong> = ' % name or '' 1108 return lhs + self.repr(object) 1109 1110 def index(self, dir, shadowed=None): 1111 """Generate an HTML index for a directory of modules.""" 1112 modpkgs = [] 1113 if shadowed is None: shadowed = {} 1114 for importer, name, ispkg in pkgutil.iter_modules([dir]): 1115 if any((0xD800 <= ord(ch) <= 0xDFFF) for ch in name): 1116 # ignore a module if its name contains a surrogate character 1117 continue 1118 modpkgs.append((name, '', ispkg, name in shadowed)) 1119 shadowed[name] = 1 1120 1121 modpkgs.sort() 1122 contents = self.multicolumn(modpkgs, self.modpkglink) 1123 return self.bigsection(dir, 'index', contents) 1124 1125# -------------------------------------------- text documentation generator 1126 1127class TextRepr(Repr): 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 1159class TextDoc(Doc): 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 1211The following documentation is automatically generated from the Python 1212source files. It may be incomplete, incorrect or include features that 1213are considered implementation detail and may vary between Python 1214implementations. When in doubt, consult the module reference at the 1215location 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 HorizontalRule: 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 1545class _PlainTextDoc(TextDoc): 1546 """Subclass of TextDoc which overrides string styling""" 1547 def bold(self, text): 1548 return text 1549 1550# --------------------------------------------------------- user interfaces 1551 1552def 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 1558def 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 1594def plain(text): 1595 """Remove boldface formatting from text.""" 1596 return re.sub('.\b', '', text) 1597 1598def 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 1622def 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 1634def _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 1639def 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 1683def plainpager(text): 1684 """Simply print unformatted text. This is the ultimate fallback.""" 1685 sys.stdout.write(plain(_escape_stdout(text))) 1686 1687def 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 1714def 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 1735text = TextDoc() 1736plaintext = _PlainTextDoc() 1737html = HTMLDoc() 1738 1739def 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('''\ 1745No Python documentation found for %r. 1746Use help() to get the interactive help utility. 1747Use 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 1753def 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 1780def doc(thing, title='Python Library Documentation: %s', forceload=0, 1781 output=None): 1782 """Display text documentation, given an object or a path to an object.""" 1783 if output is None: 1784 pager(render_doc(thing, title, forceload)) 1785 else: 1786 output.write(render_doc(thing, title, forceload, plaintext)) 1787 1788def writedoc(thing, forceload=0): 1789 """Write HTML documentation to a file in the current directory.""" 1790 object, name = resolve(thing, forceload) 1791 page = html.page(describe(object), html.document(object, name)) 1792 with open(name + '.html', 'w', encoding='utf-8') as file: 1793 file.write(page) 1794 print('wrote', name + '.html') 1795 1796def writedocs(dir, pkgpath='', done=None): 1797 """Write out HTML documentation for all modules in a directory tree.""" 1798 if done is None: done = {} 1799 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): 1800 writedoc(modname) 1801 return 1802 1803class Helper: 1804 1805 # These dictionaries map a topic name to either an alias, or a tuple 1806 # (label, seealso-items). The "label" is the label of the corresponding 1807 # section in the .rst file under Doc/ and an index into the dictionary 1808 # in pydoc_data/topics.py. 1809 # 1810 # CAUTION: if you change one of these dictionaries, be sure to adapt the 1811 # list of needed labels in Doc/tools/extensions/pyspecific.py and 1812 # regenerate the pydoc_data/topics.py file by running 1813 # make pydoc-topics 1814 # in Doc/ and copying the output file into the Lib/ directory. 1815 1816 keywords = { 1817 'False': '', 1818 'None': '', 1819 'True': '', 1820 'and': 'BOOLEAN', 1821 'as': 'with', 1822 'assert': ('assert', ''), 1823 'async': ('async', ''), 1824 'await': ('await', ''), 1825 'break': ('break', 'while for'), 1826 'class': ('class', 'CLASSES SPECIALMETHODS'), 1827 'continue': ('continue', 'while for'), 1828 'def': ('function', ''), 1829 'del': ('del', 'BASICMETHODS'), 1830 'elif': 'if', 1831 'else': ('else', 'while for'), 1832 'except': 'try', 1833 'finally': 'try', 1834 'for': ('for', 'break continue while'), 1835 'from': 'import', 1836 'global': ('global', 'nonlocal NAMESPACES'), 1837 'if': ('if', 'TRUTHVALUE'), 1838 'import': ('import', 'MODULES'), 1839 'in': ('in', 'SEQUENCEMETHODS'), 1840 'is': 'COMPARISON', 1841 'lambda': ('lambda', 'FUNCTIONS'), 1842 'nonlocal': ('nonlocal', 'global NAMESPACES'), 1843 'not': 'BOOLEAN', 1844 'or': 'BOOLEAN', 1845 'pass': ('pass', ''), 1846 'raise': ('raise', 'EXCEPTIONS'), 1847 'return': ('return', 'FUNCTIONS'), 1848 'try': ('try', 'EXCEPTIONS'), 1849 'while': ('while', 'break continue if TRUTHVALUE'), 1850 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 1851 'yield': ('yield', ''), 1852 } 1853 # Either add symbols to this dictionary or to the symbols dictionary 1854 # directly: Whichever is easier. They are merged later. 1855 _strprefixes = [p + q for p in ('b', 'f', 'r', 'u') for q in ("'", '"')] 1856 _symbols_inverse = { 1857 'STRINGS' : ("'", "'''", '"', '"""', *_strprefixes), 1858 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', 1859 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), 1860 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), 1861 'UNARY' : ('-', '~'), 1862 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', 1863 '^=', '<<=', '>>=', '**=', '//='), 1864 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), 1865 'COMPLEX' : ('j', 'J') 1866 } 1867 symbols = { 1868 '%': 'OPERATORS FORMATTING', 1869 '**': 'POWER', 1870 ',': 'TUPLES LISTS FUNCTIONS', 1871 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', 1872 '...': 'ELLIPSIS', 1873 ':': 'SLICINGS DICTIONARYLITERALS', 1874 '@': 'def class', 1875 '\\': 'STRINGS', 1876 '_': 'PRIVATENAMES', 1877 '__': 'PRIVATENAMES SPECIALMETHODS', 1878 '`': 'BACKQUOTES', 1879 '(': 'TUPLES FUNCTIONS CALLS', 1880 ')': 'TUPLES FUNCTIONS CALLS', 1881 '[': 'LISTS SUBSCRIPTS SLICINGS', 1882 ']': 'LISTS SUBSCRIPTS SLICINGS' 1883 } 1884 for topic, symbols_ in _symbols_inverse.items(): 1885 for symbol in symbols_: 1886 topics = symbols.get(symbol, topic) 1887 if topic not in topics: 1888 topics = topics + ' ' + topic 1889 symbols[symbol] = topics 1890 del topic, symbols_, symbol, topics 1891 1892 topics = { 1893 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' 1894 'FUNCTIONS CLASSES MODULES FILES inspect'), 1895 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS ' 1896 'FORMATTING TYPES'), 1897 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), 1898 'FORMATTING': ('formatstrings', 'OPERATORS'), 1899 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' 1900 'FORMATTING TYPES'), 1901 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), 1902 'INTEGER': ('integers', 'int range'), 1903 'FLOAT': ('floating', 'float math'), 1904 'COMPLEX': ('imaginary', 'complex cmath'), 1905 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING range LISTS'), 1906 'MAPPINGS': 'DICTIONARIES', 1907 'FUNCTIONS': ('typesfunctions', 'def TYPES'), 1908 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), 1909 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), 1910 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), 1911 'FRAMEOBJECTS': 'TYPES', 1912 'TRACEBACKS': 'TYPES', 1913 'NONE': ('bltin-null-object', ''), 1914 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), 1915 'SPECIALATTRIBUTES': ('specialattrs', ''), 1916 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), 1917 'MODULES': ('typesmodules', 'import'), 1918 'PACKAGES': 'import', 1919 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' 1920 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' 1921 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' 1922 'LISTS DICTIONARIES'), 1923 'OPERATORS': 'EXPRESSIONS', 1924 'PRECEDENCE': 'EXPRESSIONS', 1925 'OBJECTS': ('objects', 'TYPES'), 1926 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' 1927 'CALLABLEMETHODS SEQUENCEMETHODS MAPPINGMETHODS ' 1928 'NUMBERMETHODS CLASSES'), 1929 'BASICMETHODS': ('customization', 'hash repr str SPECIALMETHODS'), 1930 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 1931 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), 1932 'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS ' 1933 'SPECIALMETHODS'), 1934 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 1935 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' 1936 'SPECIALMETHODS'), 1937 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 1938 'NAMESPACES': ('naming', 'global nonlocal ASSIGNMENT DELETION DYNAMICFEATURES'), 1939 'DYNAMICFEATURES': ('dynamic-features', ''), 1940 'SCOPING': 'NAMESPACES', 1941 'FRAMES': 'NAMESPACES', 1942 'EXCEPTIONS': ('exceptions', 'try except finally raise'), 1943 'CONVERSIONS': ('conversions', ''), 1944 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), 1945 'SPECIALIDENTIFIERS': ('id-classes', ''), 1946 'PRIVATENAMES': ('atom-identifiers', ''), 1947 'LITERALS': ('atom-literals', 'STRINGS NUMBERS TUPLELITERALS ' 1948 'LISTLITERALS DICTIONARYLITERALS'), 1949 'TUPLES': 'SEQUENCES', 1950 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), 1951 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), 1952 'LISTLITERALS': ('lists', 'LISTS LITERALS'), 1953 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 1954 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 1955 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), 1956 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS'), 1957 'SLICINGS': ('slicings', 'SEQUENCEMETHODS'), 1958 'CALLS': ('calls', 'EXPRESSIONS'), 1959 'POWER': ('power', 'EXPRESSIONS'), 1960 'UNARY': ('unary', 'EXPRESSIONS'), 1961 'BINARY': ('binary', 'EXPRESSIONS'), 1962 'SHIFTING': ('shifting', 'EXPRESSIONS'), 1963 'BITWISE': ('bitwise', 'EXPRESSIONS'), 1964 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), 1965 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), 1966 'ASSERTION': 'assert', 1967 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), 1968 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), 1969 'DELETION': 'del', 1970 'RETURNING': 'return', 1971 'IMPORTING': 'import', 1972 'CONDITIONAL': 'if', 1973 'LOOPING': ('compound', 'for while break continue'), 1974 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), 1975 'DEBUGGING': ('debugger', 'pdb'), 1976 'CONTEXTMANAGERS': ('context-managers', 'with'), 1977 } 1978 1979 def __init__(self, input=None, output=None): 1980 self._input = input 1981 self._output = output 1982 1983 @property 1984 def input(self): 1985 return self._input or sys.stdin 1986 1987 @property 1988 def output(self): 1989 return self._output or sys.stdout 1990 1991 def __repr__(self): 1992 if inspect.stack()[1][3] == '?': 1993 self() 1994 return '' 1995 return '<%s.%s instance>' % (self.__class__.__module__, 1996 self.__class__.__qualname__) 1997 1998 _GoInteractive = object() 1999 def __call__(self, request=_GoInteractive): 2000 if request is not self._GoInteractive: 2001 try: 2002 self.help(request) 2003 except ImportError as e: 2004 self.output.write(f'{e}\n') 2005 else: 2006 self.intro() 2007 self.interact() 2008 self.output.write(''' 2009You are now leaving help and returning to the Python interpreter. 2010If you want to ask for help on a particular object directly from the 2011interpreter, you can type "help(object)". Executing "help('string')" 2012has the same effect as typing a particular string at the help> prompt. 2013''') 2014 2015 def interact(self): 2016 self.output.write('\n') 2017 while True: 2018 try: 2019 request = self.getline('help> ') 2020 if not request: break 2021 except (KeyboardInterrupt, EOFError): 2022 break 2023 request = request.strip() 2024 2025 # Make sure significant trailing quoting marks of literals don't 2026 # get deleted while cleaning input 2027 if (len(request) > 2 and request[0] == request[-1] in ("'", '"') 2028 and request[0] not in request[1:-1]): 2029 request = request[1:-1] 2030 if request.lower() in ('q', 'quit'): break 2031 if request == 'help': 2032 self.intro() 2033 else: 2034 self.help(request) 2035 2036 def getline(self, prompt): 2037 """Read one line, using input() when appropriate.""" 2038 if self.input is sys.stdin: 2039 return input(prompt) 2040 else: 2041 self.output.write(prompt) 2042 self.output.flush() 2043 return self.input.readline() 2044 2045 def help(self, request): 2046 if type(request) is type(''): 2047 request = request.strip() 2048 if request == 'keywords': self.listkeywords() 2049 elif request == 'symbols': self.listsymbols() 2050 elif request == 'topics': self.listtopics() 2051 elif request == 'modules': self.listmodules() 2052 elif request[:8] == 'modules ': 2053 self.listmodules(request.split()[1]) 2054 elif request in self.symbols: self.showsymbol(request) 2055 elif request in ['True', 'False', 'None']: 2056 # special case these keywords since they are objects too 2057 doc(eval(request), 'Help on %s:') 2058 elif request in self.keywords: self.showtopic(request) 2059 elif request in self.topics: self.showtopic(request) 2060 elif request: doc(request, 'Help on %s:', output=self._output) 2061 else: doc(str, 'Help on %s:', output=self._output) 2062 elif isinstance(request, Helper): self() 2063 else: doc(request, 'Help on %s:', output=self._output) 2064 self.output.write('\n') 2065 2066 def intro(self): 2067 self.output.write(''' 2068Welcome to Python {0}'s help utility! 2069 2070If this is your first time using Python, you should definitely check out 2071the tutorial on the internet at https://docs.python.org/{0}/tutorial/. 2072 2073Enter the name of any module, keyword, or topic to get help on writing 2074Python programs and using Python modules. To quit this help utility and 2075return to the interpreter, just type "quit". 2076 2077To get a list of available modules, keywords, symbols, or topics, type 2078"modules", "keywords", "symbols", or "topics". Each module also comes 2079with a one-line summary of what it does; to list the modules whose name 2080or summary contain a given string such as "spam", type "modules spam". 2081'''.format('%d.%d' % sys.version_info[:2])) 2082 2083 def list(self, items, columns=4, width=80): 2084 items = list(sorted(items)) 2085 colw = width // columns 2086 rows = (len(items) + columns - 1) // columns 2087 for row in range(rows): 2088 for col in range(columns): 2089 i = col * rows + row 2090 if i < len(items): 2091 self.output.write(items[i]) 2092 if col < columns - 1: 2093 self.output.write(' ' + ' ' * (colw - 1 - len(items[i]))) 2094 self.output.write('\n') 2095 2096 def listkeywords(self): 2097 self.output.write(''' 2098Here is a list of the Python keywords. Enter any keyword to get more help. 2099 2100''') 2101 self.list(self.keywords.keys()) 2102 2103 def listsymbols(self): 2104 self.output.write(''' 2105Here is a list of the punctuation symbols which Python assigns special meaning 2106to. Enter any symbol to get more help. 2107 2108''') 2109 self.list(self.symbols.keys()) 2110 2111 def listtopics(self): 2112 self.output.write(''' 2113Here is a list of available topics. Enter any topic name to get more help. 2114 2115''') 2116 self.list(self.topics.keys()) 2117 2118 def showtopic(self, topic, more_xrefs=''): 2119 try: 2120 import pydoc_data.topics 2121 except ImportError: 2122 self.output.write(''' 2123Sorry, topic and keyword documentation is not available because the 2124module "pydoc_data.topics" could not be found. 2125''') 2126 return 2127 target = self.topics.get(topic, self.keywords.get(topic)) 2128 if not target: 2129 self.output.write('no documentation found for %s\n' % repr(topic)) 2130 return 2131 if type(target) is type(''): 2132 return self.showtopic(target, more_xrefs) 2133 2134 label, xrefs = target 2135 try: 2136 doc = pydoc_data.topics.topics[label] 2137 except KeyError: 2138 self.output.write('no documentation found for %s\n' % repr(topic)) 2139 return 2140 doc = doc.strip() + '\n' 2141 if more_xrefs: 2142 xrefs = (xrefs or '') + ' ' + more_xrefs 2143 if xrefs: 2144 import textwrap 2145 text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n' 2146 wrapped_text = textwrap.wrap(text, 72) 2147 doc += '\n%s\n' % '\n'.join(wrapped_text) 2148 pager(doc) 2149 2150 def _gettopic(self, topic, more_xrefs=''): 2151 """Return unbuffered tuple of (topic, xrefs). 2152 2153 If an error occurs here, the exception is caught and displayed by 2154 the url handler. 2155 2156 This function duplicates the showtopic method but returns its 2157 result directly so it can be formatted for display in an html page. 2158 """ 2159 try: 2160 import pydoc_data.topics 2161 except ImportError: 2162 return(''' 2163Sorry, topic and keyword documentation is not available because the 2164module "pydoc_data.topics" could not be found. 2165''' , '') 2166 target = self.topics.get(topic, self.keywords.get(topic)) 2167 if not target: 2168 raise ValueError('could not find topic') 2169 if isinstance(target, str): 2170 return self._gettopic(target, more_xrefs) 2171 label, xrefs = target 2172 doc = pydoc_data.topics.topics[label] 2173 if more_xrefs: 2174 xrefs = (xrefs or '') + ' ' + more_xrefs 2175 return doc, xrefs 2176 2177 def showsymbol(self, symbol): 2178 target = self.symbols[symbol] 2179 topic, _, xrefs = target.partition(' ') 2180 self.showtopic(topic, xrefs) 2181 2182 def listmodules(self, key=''): 2183 if key: 2184 self.output.write(''' 2185Here is a list of modules whose name or summary contains '{}'. 2186If there are any, enter a module name to get more help. 2187 2188'''.format(key)) 2189 apropos(key) 2190 else: 2191 self.output.write(''' 2192Please wait a moment while I gather a list of all available modules... 2193 2194''') 2195 modules = {} 2196 def callback(path, modname, desc, modules=modules): 2197 if modname and modname[-9:] == '.__init__': 2198 modname = modname[:-9] + ' (package)' 2199 if modname.find('.') < 0: 2200 modules[modname] = 1 2201 def onerror(modname): 2202 callback(None, modname, None) 2203 ModuleScanner().run(callback, onerror=onerror) 2204 self.list(modules.keys()) 2205 self.output.write(''' 2206Enter any module name to get more help. Or, type "modules spam" to search 2207for modules whose name or summary contain the string "spam". 2208''') 2209 2210help = Helper() 2211 2212class ModuleScanner: 2213 """An interruptible scanner that searches module synopses.""" 2214 2215 def run(self, callback, key=None, completer=None, onerror=None): 2216 if key: key = key.lower() 2217 self.quit = False 2218 seen = {} 2219 2220 for modname in sys.builtin_module_names: 2221 if modname != '__main__': 2222 seen[modname] = 1 2223 if key is None: 2224 callback(None, modname, '') 2225 else: 2226 name = __import__(modname).__doc__ or '' 2227 desc = name.split('\n')[0] 2228 name = modname + ' - ' + desc 2229 if name.lower().find(key) >= 0: 2230 callback(None, modname, desc) 2231 2232 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): 2233 if self.quit: 2234 break 2235 2236 if key is None: 2237 callback(None, modname, '') 2238 else: 2239 try: 2240 spec = pkgutil._get_spec(importer, modname) 2241 except SyntaxError: 2242 # raised by tests for bad coding cookies or BOM 2243 continue 2244 loader = spec.loader 2245 if hasattr(loader, 'get_source'): 2246 try: 2247 source = loader.get_source(modname) 2248 except Exception: 2249 if onerror: 2250 onerror(modname) 2251 continue 2252 desc = source_synopsis(io.StringIO(source)) or '' 2253 if hasattr(loader, 'get_filename'): 2254 path = loader.get_filename(modname) 2255 else: 2256 path = None 2257 else: 2258 try: 2259 module = importlib._bootstrap._load(spec) 2260 except ImportError: 2261 if onerror: 2262 onerror(modname) 2263 continue 2264 desc = module.__doc__.splitlines()[0] if module.__doc__ else '' 2265 path = getattr(module,'__file__',None) 2266 name = modname + ' - ' + desc 2267 if name.lower().find(key) >= 0: 2268 callback(path, modname, desc) 2269 2270 if completer: 2271 completer() 2272 2273def apropos(key): 2274 """Print all the one-line module summaries that contain a substring.""" 2275 def callback(path, modname, desc): 2276 if modname[-9:] == '.__init__': 2277 modname = modname[:-9] + ' (package)' 2278 print(modname, desc and '- ' + desc) 2279 def onerror(modname): 2280 pass 2281 with warnings.catch_warnings(): 2282 warnings.filterwarnings('ignore') # ignore problems during import 2283 ModuleScanner().run(callback, key, onerror=onerror) 2284 2285# --------------------------------------- enhanced web browser interface 2286 2287def _start_server(urlhandler, hostname, port): 2288 """Start an HTTP server thread on a specific port. 2289 2290 Start an HTML/text server thread, so HTML or text documents can be 2291 browsed dynamically and interactively with a web browser. Example use: 2292 2293 >>> import time 2294 >>> import pydoc 2295 2296 Define a URL handler. To determine what the client is asking 2297 for, check the URL and content_type. 2298 2299 Then get or generate some text or HTML code and return it. 2300 2301 >>> def my_url_handler(url, content_type): 2302 ... text = 'the URL sent was: (%s, %s)' % (url, content_type) 2303 ... return text 2304 2305 Start server thread on port 0. 2306 If you use port 0, the server will pick a random port number. 2307 You can then use serverthread.port to get the port number. 2308 2309 >>> port = 0 2310 >>> serverthread = pydoc._start_server(my_url_handler, port) 2311 2312 Check that the server is really started. If it is, open browser 2313 and get first page. Use serverthread.url as the starting page. 2314 2315 >>> if serverthread.serving: 2316 ... import webbrowser 2317 2318 The next two lines are commented out so a browser doesn't open if 2319 doctest is run on this module. 2320 2321 #... webbrowser.open(serverthread.url) 2322 #True 2323 2324 Let the server do its thing. We just need to monitor its status. 2325 Use time.sleep so the loop doesn't hog the CPU. 2326 2327 >>> starttime = time.monotonic() 2328 >>> timeout = 1 #seconds 2329 2330 This is a short timeout for testing purposes. 2331 2332 >>> while serverthread.serving: 2333 ... time.sleep(.01) 2334 ... if serverthread.serving and time.monotonic() - starttime > timeout: 2335 ... serverthread.stop() 2336 ... break 2337 2338 Print any errors that may have occurred. 2339 2340 >>> print(serverthread.error) 2341 None 2342 """ 2343 import http.server 2344 import email.message 2345 import select 2346 import threading 2347 2348 class DocHandler(http.server.BaseHTTPRequestHandler): 2349 2350 def do_GET(self): 2351 """Process a request from an HTML browser. 2352 2353 The URL received is in self.path. 2354 Get an HTML page from self.urlhandler and send it. 2355 """ 2356 if self.path.endswith('.css'): 2357 content_type = 'text/css' 2358 else: 2359 content_type = 'text/html' 2360 self.send_response(200) 2361 self.send_header('Content-Type', '%s; charset=UTF-8' % content_type) 2362 self.end_headers() 2363 self.wfile.write(self.urlhandler( 2364 self.path, content_type).encode('utf-8')) 2365 2366 def log_message(self, *args): 2367 # Don't log messages. 2368 pass 2369 2370 class DocServer(http.server.HTTPServer): 2371 2372 def __init__(self, host, port, callback): 2373 self.host = host 2374 self.address = (self.host, port) 2375 self.callback = callback 2376 self.base.__init__(self, self.address, self.handler) 2377 self.quit = False 2378 2379 def serve_until_quit(self): 2380 while not self.quit: 2381 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 2382 if rd: 2383 self.handle_request() 2384 self.server_close() 2385 2386 def server_activate(self): 2387 self.base.server_activate(self) 2388 if self.callback: 2389 self.callback(self) 2390 2391 class ServerThread(threading.Thread): 2392 2393 def __init__(self, urlhandler, host, port): 2394 self.urlhandler = urlhandler 2395 self.host = host 2396 self.port = int(port) 2397 threading.Thread.__init__(self) 2398 self.serving = False 2399 self.error = None 2400 2401 def run(self): 2402 """Start the server.""" 2403 try: 2404 DocServer.base = http.server.HTTPServer 2405 DocServer.handler = DocHandler 2406 DocHandler.MessageClass = email.message.Message 2407 DocHandler.urlhandler = staticmethod(self.urlhandler) 2408 docsvr = DocServer(self.host, self.port, self.ready) 2409 self.docserver = docsvr 2410 docsvr.serve_until_quit() 2411 except Exception as e: 2412 self.error = e 2413 2414 def ready(self, server): 2415 self.serving = True 2416 self.host = server.host 2417 self.port = server.server_port 2418 self.url = 'http://%s:%d/' % (self.host, self.port) 2419 2420 def stop(self): 2421 """Stop the server and this thread nicely""" 2422 self.docserver.quit = True 2423 self.join() 2424 # explicitly break a reference cycle: DocServer.callback 2425 # has indirectly a reference to ServerThread. 2426 self.docserver = None 2427 self.serving = False 2428 self.url = None 2429 2430 thread = ServerThread(urlhandler, hostname, port) 2431 thread.start() 2432 # Wait until thread.serving is True to make sure we are 2433 # really up before returning. 2434 while not thread.error and not thread.serving: 2435 time.sleep(.01) 2436 return thread 2437 2438 2439def _url_handler(url, content_type="text/html"): 2440 """The pydoc url handler for use with the pydoc server. 2441 2442 If the content_type is 'text/css', the _pydoc.css style 2443 sheet is read and returned if it exits. 2444 2445 If the content_type is 'text/html', then the result of 2446 get_html_page(url) is returned. 2447 """ 2448 class _HTMLDoc(HTMLDoc): 2449 2450 def page(self, title, contents): 2451 """Format an HTML page.""" 2452 css_path = "pydoc_data/_pydoc.css" 2453 css_link = ( 2454 '<link rel="stylesheet" type="text/css" href="%s">' % 2455 css_path) 2456 return '''\ 2457<!DOCTYPE> 2458<html lang="en"> 2459<head> 2460<meta charset="utf-8"> 2461<title>Pydoc: %s</title> 2462%s</head><body>%s<div style="clear:both;padding-top:.5em;">%s</div> 2463</body></html>''' % (title, css_link, html_navbar(), contents) 2464 2465 2466 html = _HTMLDoc() 2467 2468 def html_navbar(): 2469 version = html.escape("%s [%s, %s]" % (platform.python_version(), 2470 platform.python_build()[0], 2471 platform.python_compiler())) 2472 return """ 2473 <div style='float:left'> 2474 Python %s<br>%s 2475 </div> 2476 <div style='float:right'> 2477 <div style='text-align:center'> 2478 <a href="index.html">Module Index</a> 2479 : <a href="topics.html">Topics</a> 2480 : <a href="keywords.html">Keywords</a> 2481 </div> 2482 <div> 2483 <form action="get" style='display:inline;'> 2484 <input type=text name=key size=15> 2485 <input type=submit value="Get"> 2486 </form> 2487 <form action="search" style='display:inline;'> 2488 <input type=text name=key size=15> 2489 <input type=submit value="Search"> 2490 </form> 2491 </div> 2492 </div> 2493 """ % (version, html.escape(platform.platform(terse=True))) 2494 2495 def html_index(): 2496 """Module Index page.""" 2497 2498 def bltinlink(name): 2499 return '<a href="%s.html">%s</a>' % (name, name) 2500 2501 heading = html.heading( 2502 '<strong class="title">Index of Modules</strong>' 2503 ) 2504 names = [name for name in sys.builtin_module_names 2505 if name != '__main__'] 2506 contents = html.multicolumn(names, bltinlink) 2507 contents = [heading, '<p>' + html.bigsection( 2508 'Built-in Modules', 'index', contents)] 2509 2510 seen = {} 2511 for dir in sys.path: 2512 contents.append(html.index(dir, seen)) 2513 2514 contents.append( 2515 '<p align=right class="heading-text grey"><strong>pydoc</strong> by Ka-Ping Yee' 2516 '<[email protected]></p>') 2517 return 'Index of Modules', ''.join(contents) 2518 2519 def html_search(key): 2520 """Search results page.""" 2521 # scan for modules 2522 search_result = [] 2523 2524 def callback(path, modname, desc): 2525 if modname[-9:] == '.__init__': 2526 modname = modname[:-9] + ' (package)' 2527 search_result.append((modname, desc and '- ' + desc)) 2528 2529 with warnings.catch_warnings(): 2530 warnings.filterwarnings('ignore') # ignore problems during import 2531 def onerror(modname): 2532 pass 2533 ModuleScanner().run(callback, key, onerror=onerror) 2534 2535 # format page 2536 def bltinlink(name): 2537 return '<a href="%s.html">%s</a>' % (name, name) 2538 2539 results = [] 2540 heading = html.heading( 2541 '<strong class="title">Search Results</strong>', 2542 ) 2543 for name, desc in search_result: 2544 results.append(bltinlink(name) + desc) 2545 contents = heading + html.bigsection( 2546 'key = %s' % key, 'index', '<br>'.join(results)) 2547 return 'Search Results', contents 2548 2549 def html_topics(): 2550 """Index of topic texts available.""" 2551 2552 def bltinlink(name): 2553 return '<a href="topic?key=%s">%s</a>' % (name, name) 2554 2555 heading = html.heading( 2556 '<strong class="title">INDEX</strong>', 2557 ) 2558 names = sorted(Helper.topics.keys()) 2559 2560 contents = html.multicolumn(names, bltinlink) 2561 contents = heading + html.bigsection( 2562 'Topics', 'index', contents) 2563 return 'Topics', contents 2564 2565 def html_keywords(): 2566 """Index of keywords.""" 2567 heading = html.heading( 2568 '<strong class="title">INDEX</strong>', 2569 ) 2570 names = sorted(Helper.keywords.keys()) 2571 2572 def bltinlink(name): 2573 return '<a href="topic?key=%s">%s</a>' % (name, name) 2574 2575 contents = html.multicolumn(names, bltinlink) 2576 contents = heading + html.bigsection( 2577 'Keywords', 'index', contents) 2578 return 'Keywords', contents 2579 2580 def html_topicpage(topic): 2581 """Topic or keyword help page.""" 2582 buf = io.StringIO() 2583 htmlhelp = Helper(buf, buf) 2584 contents, xrefs = htmlhelp._gettopic(topic) 2585 if topic in htmlhelp.keywords: 2586 title = 'KEYWORD' 2587 else: 2588 title = 'TOPIC' 2589 heading = html.heading( 2590 '<strong class="title">%s</strong>' % title, 2591 ) 2592 contents = '<pre>%s</pre>' % html.markup(contents) 2593 contents = html.bigsection(topic , 'index', contents) 2594 if xrefs: 2595 xrefs = sorted(xrefs.split()) 2596 2597 def bltinlink(name): 2598 return '<a href="topic?key=%s">%s</a>' % (name, name) 2599 2600 xrefs = html.multicolumn(xrefs, bltinlink) 2601 xrefs = html.section('Related help topics: ', 'index', xrefs) 2602 return ('%s %s' % (title, topic), 2603 ''.join((heading, contents, xrefs))) 2604 2605 def html_getobj(url): 2606 obj = locate(url, forceload=1) 2607 if obj is None and url != 'None': 2608 raise ValueError('could not find object') 2609 title = describe(obj) 2610 content = html.document(obj, url) 2611 return title, content 2612 2613 def html_error(url, exc): 2614 heading = html.heading( 2615 '<strong class="title">Error</strong>', 2616 ) 2617 contents = '<br>'.join(html.escape(line) for line in 2618 format_exception_only(type(exc), exc)) 2619 contents = heading + html.bigsection(url, 'error', contents) 2620 return "Error - %s" % url, contents 2621 2622 def get_html_page(url): 2623 """Generate an HTML page for url.""" 2624 complete_url = url 2625 if url.endswith('.html'): 2626 url = url[:-5] 2627 try: 2628 if url in ("", "index"): 2629 title, content = html_index() 2630 elif url == "topics": 2631 title, content = html_topics() 2632 elif url == "keywords": 2633 title, content = html_keywords() 2634 elif '=' in url: 2635 op, _, url = url.partition('=') 2636 if op == "search?key": 2637 title, content = html_search(url) 2638 elif op == "topic?key": 2639 # try topics first, then objects. 2640 try: 2641 title, content = html_topicpage(url) 2642 except ValueError: 2643 title, content = html_getobj(url) 2644 elif op == "get?key": 2645 # try objects first, then topics. 2646 if url in ("", "index"): 2647 title, content = html_index() 2648 else: 2649 try: 2650 title, content = html_getobj(url) 2651 except ValueError: 2652 title, content = html_topicpage(url) 2653 else: 2654 raise ValueError('bad pydoc url') 2655 else: 2656 title, content = html_getobj(url) 2657 except Exception as exc: 2658 # Catch any errors and display them in an error page. 2659 title, content = html_error(complete_url, exc) 2660 return html.page(title, content) 2661 2662 if url.startswith('/'): 2663 url = url[1:] 2664 if content_type == 'text/css': 2665 path_here = os.path.dirname(os.path.realpath(__file__)) 2666 css_path = os.path.join(path_here, url) 2667 with open(css_path) as fp: 2668 return ''.join(fp.readlines()) 2669 elif content_type == 'text/html': 2670 return get_html_page(url) 2671 # Errors outside the url handler are caught by the server. 2672 raise TypeError('unknown content type %r for url %s' % (content_type, url)) 2673 2674 2675def browse(port=0, *, open_browser=True, hostname='localhost'): 2676 """Start the enhanced pydoc web server and open a web browser. 2677 2678 Use port '0' to start the server on an arbitrary port. 2679 Set open_browser to False to suppress opening a browser. 2680 """ 2681 import webbrowser 2682 serverthread = _start_server(_url_handler, hostname, port) 2683 if serverthread.error: 2684 print(serverthread.error) 2685 return 2686 if serverthread.serving: 2687 server_help_msg = 'Server commands: [b]rowser, [q]uit' 2688 if open_browser: 2689 webbrowser.open(serverthread.url) 2690 try: 2691 print('Server ready at', serverthread.url) 2692 print(server_help_msg) 2693 while serverthread.serving: 2694 cmd = input('server> ') 2695 cmd = cmd.lower() 2696 if cmd == 'q': 2697 break 2698 elif cmd == 'b': 2699 webbrowser.open(serverthread.url) 2700 else: 2701 print(server_help_msg) 2702 except (KeyboardInterrupt, EOFError): 2703 print() 2704 finally: 2705 if serverthread.serving: 2706 serverthread.stop() 2707 print('Server stopped') 2708 2709 2710# -------------------------------------------------- command-line interface 2711 2712def ispath(x): 2713 return isinstance(x, str) and x.find(os.sep) >= 0 2714 2715def _get_revised_path(given_path, argv0): 2716 """Ensures current directory is on returned path, and argv0 directory is not 2717 2718 Exception: argv0 dir is left alone if it's also pydoc's directory. 2719 2720 Returns a new path entry list, or None if no adjustment is needed. 2721 """ 2722 # Scripts may get the current directory in their path by default if they're 2723 # run with the -m switch, or directly from the current directory. 2724 # The interactive prompt also allows imports from the current directory. 2725 2726 # Accordingly, if the current directory is already present, don't make 2727 # any changes to the given_path 2728 if '' in given_path or os.curdir in given_path or os.getcwd() in given_path: 2729 return None 2730 2731 # Otherwise, add the current directory to the given path, and remove the 2732 # script directory (as long as the latter isn't also pydoc's directory. 2733 stdlib_dir = os.path.dirname(__file__) 2734 script_dir = os.path.dirname(argv0) 2735 revised_path = given_path.copy() 2736 if script_dir in given_path and not os.path.samefile(script_dir, stdlib_dir): 2737 revised_path.remove(script_dir) 2738 revised_path.insert(0, os.getcwd()) 2739 return revised_path 2740 2741 2742# Note: the tests only cover _get_revised_path, not _adjust_cli_path itself 2743def _adjust_cli_sys_path(): 2744 """Ensures current directory is on sys.path, and __main__ directory is not. 2745 2746 Exception: __main__ dir is left alone if it's also pydoc's directory. 2747 """ 2748 revised_path = _get_revised_path(sys.path, sys.argv[0]) 2749 if revised_path is not None: 2750 sys.path[:] = revised_path 2751 2752 2753def cli(): 2754 """Command-line interface (looks at sys.argv to decide what to do).""" 2755 import getopt 2756 class BadUsage(Exception): pass 2757 2758 _adjust_cli_sys_path() 2759 2760 try: 2761 opts, args = getopt.getopt(sys.argv[1:], 'bk:n:p:w') 2762 writing = False 2763 start_server = False 2764 open_browser = False 2765 port = 0 2766 hostname = 'localhost' 2767 for opt, val in opts: 2768 if opt == '-b': 2769 start_server = True 2770 open_browser = True 2771 if opt == '-k': 2772 apropos(val) 2773 return 2774 if opt == '-p': 2775 start_server = True 2776 port = val 2777 if opt == '-w': 2778 writing = True 2779 if opt == '-n': 2780 start_server = True 2781 hostname = val 2782 2783 if start_server: 2784 browse(port, hostname=hostname, open_browser=open_browser) 2785 return 2786 2787 if not args: raise BadUsage 2788 for arg in args: 2789 if ispath(arg) and not os.path.exists(arg): 2790 print('file %r does not exist' % arg) 2791 sys.exit(1) 2792 try: 2793 if ispath(arg) and os.path.isfile(arg): 2794 arg = importfile(arg) 2795 if writing: 2796 if ispath(arg) and os.path.isdir(arg): 2797 writedocs(arg) 2798 else: 2799 writedoc(arg) 2800 else: 2801 help.help(arg) 2802 except (ImportError, ErrorDuringImport) as value: 2803 print(value) 2804 sys.exit(1) 2805 2806 except (getopt.error, BadUsage): 2807 cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0] 2808 print("""pydoc - the Python documentation tool 2809 2810{cmd} <name> ... 2811 Show text documentation on something. <name> may be the name of a 2812 Python keyword, topic, function, module, or package, or a dotted 2813 reference to a class or function within a module or module in a 2814 package. If <name> contains a '{sep}', it is used as the path to a 2815 Python source file to document. If name is 'keywords', 'topics', 2816 or 'modules', a listing of these things is displayed. 2817 2818{cmd} -k <keyword> 2819 Search for a keyword in the synopsis lines of all available modules. 2820 2821{cmd} -n <hostname> 2822 Start an HTTP server with the given hostname (default: localhost). 2823 2824{cmd} -p <port> 2825 Start an HTTP server on the given port on the local machine. Port 2826 number 0 can be used to get an arbitrary unused port. 2827 2828{cmd} -b 2829 Start an HTTP server on an arbitrary unused port and open a web browser 2830 to interactively browse documentation. This option can be used in 2831 combination with -n and/or -p. 2832 2833{cmd} -w <name> ... 2834 Write out the HTML documentation for a module to a file in the current 2835 directory. If <name> contains a '{sep}', it is treated as a filename; if 2836 it names a directory, documentation is written for all the contents. 2837""".format(cmd=cmd, sep=os.sep)) 2838 2839if __name__ == '__main__': 2840 cli() 2841