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