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