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 = "%s/%s" % (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 = 'http://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 try: 1599 with io.TextIOWrapper(proc.stdin, errors='backslashreplace') as pipe: 1600 try: 1601 pipe.write(text) 1602 except KeyboardInterrupt: 1603 # We've hereby abandoned whatever text hasn't been written, 1604 # but the pager is still in control of the terminal. 1605 pass 1606 except OSError: 1607 pass # Ignore broken pipes caused by quitting the pager program. 1608 while True: 1609 try: 1610 proc.wait() 1611 break 1612 except KeyboardInterrupt: 1613 # Ignore ctl-c like the pager itself does. Otherwise the pager is 1614 # left running and the terminal is in raw mode and unusable. 1615 pass 1616 1617def tempfilepager(text, cmd): 1618 """Page through text by invoking a program on a temporary file.""" 1619 import tempfile 1620 filename = tempfile.mktemp() 1621 with open(filename, 'w', errors='backslashreplace') as file: 1622 file.write(text) 1623 try: 1624 os.system(cmd + ' "' + filename + '"') 1625 finally: 1626 os.unlink(filename) 1627 1628def _escape_stdout(text): 1629 # Escape non-encodable characters to avoid encoding errors later 1630 encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8' 1631 return text.encode(encoding, 'backslashreplace').decode(encoding) 1632 1633def ttypager(text): 1634 """Page through text on a text terminal.""" 1635 lines = plain(_escape_stdout(text)).split('\n') 1636 try: 1637 import tty 1638 fd = sys.stdin.fileno() 1639 old = tty.tcgetattr(fd) 1640 tty.setcbreak(fd) 1641 getchar = lambda: sys.stdin.read(1) 1642 except (ImportError, AttributeError, io.UnsupportedOperation): 1643 tty = None 1644 getchar = lambda: sys.stdin.readline()[:-1][:1] 1645 1646 try: 1647 try: 1648 h = int(os.environ.get('LINES', 0)) 1649 except ValueError: 1650 h = 0 1651 if h <= 1: 1652 h = 25 1653 r = inc = h - 1 1654 sys.stdout.write('\n'.join(lines[:inc]) + '\n') 1655 while lines[r:]: 1656 sys.stdout.write('-- more --') 1657 sys.stdout.flush() 1658 c = getchar() 1659 1660 if c in ('q', 'Q'): 1661 sys.stdout.write('\r \r') 1662 break 1663 elif c in ('\r', '\n'): 1664 sys.stdout.write('\r \r' + lines[r] + '\n') 1665 r = r + 1 1666 continue 1667 if c in ('b', 'B', '\x1b'): 1668 r = r - inc - inc 1669 if r < 0: r = 0 1670 sys.stdout.write('\n' + '\n'.join(lines[r:r+inc]) + '\n') 1671 r = r + inc 1672 1673 finally: 1674 if tty: 1675 tty.tcsetattr(fd, tty.TCSAFLUSH, old) 1676 1677def plainpager(text): 1678 """Simply print unformatted text. This is the ultimate fallback.""" 1679 sys.stdout.write(plain(_escape_stdout(text))) 1680 1681def describe(thing): 1682 """Produce a short description of the given thing.""" 1683 if inspect.ismodule(thing): 1684 if thing.__name__ in sys.builtin_module_names: 1685 return 'built-in module ' + thing.__name__ 1686 if hasattr(thing, '__path__'): 1687 return 'package ' + thing.__name__ 1688 else: 1689 return 'module ' + thing.__name__ 1690 if inspect.isbuiltin(thing): 1691 return 'built-in function ' + thing.__name__ 1692 if inspect.isgetsetdescriptor(thing): 1693 return 'getset descriptor %s.%s.%s' % ( 1694 thing.__objclass__.__module__, thing.__objclass__.__name__, 1695 thing.__name__) 1696 if inspect.ismemberdescriptor(thing): 1697 return 'member descriptor %s.%s.%s' % ( 1698 thing.__objclass__.__module__, thing.__objclass__.__name__, 1699 thing.__name__) 1700 if inspect.isclass(thing): 1701 return 'class ' + thing.__name__ 1702 if inspect.isfunction(thing): 1703 return 'function ' + thing.__name__ 1704 if inspect.ismethod(thing): 1705 return 'method ' + thing.__name__ 1706 return type(thing).__name__ 1707 1708def locate(path, forceload=0): 1709 """Locate an object by name or dotted path, importing as necessary.""" 1710 parts = [part for part in path.split('.') if part] 1711 module, n = None, 0 1712 while n < len(parts): 1713 nextmodule = safeimport('.'.join(parts[:n+1]), forceload) 1714 if nextmodule: module, n = nextmodule, n + 1 1715 else: break 1716 if module: 1717 object = module 1718 else: 1719 object = builtins 1720 for part in parts[n:]: 1721 try: 1722 object = getattr(object, part) 1723 except AttributeError: 1724 return None 1725 return object 1726 1727# --------------------------------------- interactive interpreter interface 1728 1729text = TextDoc() 1730plaintext = _PlainTextDoc() 1731html = HTMLDoc() 1732 1733def resolve(thing, forceload=0): 1734 """Given an object or a path to an object, get the object and its name.""" 1735 if isinstance(thing, str): 1736 object = locate(thing, forceload) 1737 if object is None: 1738 raise ImportError('''\ 1739No Python documentation found for %r. 1740Use help() to get the interactive help utility. 1741Use help(str) for help on the str class.''' % thing) 1742 return object, thing 1743 else: 1744 name = getattr(thing, '__name__', None) 1745 return thing, name if isinstance(name, str) else None 1746 1747def render_doc(thing, title='Python Library Documentation: %s', forceload=0, 1748 renderer=None): 1749 """Render text documentation, given an object or a path to an object.""" 1750 if renderer is None: 1751 renderer = text 1752 object, name = resolve(thing, forceload) 1753 desc = describe(object) 1754 module = inspect.getmodule(object) 1755 if name and '.' in name: 1756 desc += ' in ' + name[:name.rfind('.')] 1757 elif module and module is not object: 1758 desc += ' in module ' + module.__name__ 1759 1760 if not (inspect.ismodule(object) or 1761 inspect.isclass(object) or 1762 inspect.isroutine(object) or 1763 inspect.isdatadescriptor(object) or 1764 _getdoc(object)): 1765 # If the passed object is a piece of data or an instance, 1766 # document its available methods instead of its value. 1767 if hasattr(object, '__origin__'): 1768 object = object.__origin__ 1769 else: 1770 object = type(object) 1771 desc += ' object' 1772 return title % desc + '\n\n' + renderer.document(object, name) 1773 1774def doc(thing, title='Python Library Documentation: %s', forceload=0, 1775 output=None): 1776 """Display text documentation, given an object or a path to an object.""" 1777 try: 1778 if output is None: 1779 pager(render_doc(thing, title, forceload)) 1780 else: 1781 output.write(render_doc(thing, title, forceload, plaintext)) 1782 except (ImportError, ErrorDuringImport) as value: 1783 print(value) 1784 1785def writedoc(thing, forceload=0): 1786 """Write HTML documentation to a file in the current directory.""" 1787 try: 1788 object, name = resolve(thing, forceload) 1789 page = html.page(describe(object), html.document(object, name)) 1790 with open(name + '.html', 'w', encoding='utf-8') as file: 1791 file.write(page) 1792 print('wrote', name + '.html') 1793 except (ImportError, ErrorDuringImport) as value: 1794 print(value) 1795 1796def writedocs(dir, pkgpath='', done=None): 1797 """Write out HTML documentation for all modules in a directory tree.""" 1798 if done is None: done = {} 1799 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): 1800 writedoc(modname) 1801 return 1802 1803class Helper: 1804 1805 # These dictionaries map a topic name to either an alias, or a tuple 1806 # (label, seealso-items). The "label" is the label of the corresponding 1807 # section in the .rst file under Doc/ and an index into the dictionary 1808 # in pydoc_data/topics.py. 1809 # 1810 # CAUTION: if you change one of these dictionaries, be sure to adapt the 1811 # list of needed labels in Doc/tools/extensions/pyspecific.py and 1812 # regenerate the pydoc_data/topics.py file by running 1813 # make pydoc-topics 1814 # in Doc/ and copying the output file into the Lib/ directory. 1815 1816 keywords = { 1817 'False': '', 1818 'None': '', 1819 'True': '', 1820 '__peg_parser__': '', 1821 'and': 'BOOLEAN', 1822 'as': 'with', 1823 'assert': ('assert', ''), 1824 'async': ('async', ''), 1825 'await': ('await', ''), 1826 'break': ('break', 'while for'), 1827 'class': ('class', 'CLASSES SPECIALMETHODS'), 1828 'continue': ('continue', 'while for'), 1829 'def': ('function', ''), 1830 'del': ('del', 'BASICMETHODS'), 1831 'elif': 'if', 1832 'else': ('else', 'while for'), 1833 'except': 'try', 1834 'finally': 'try', 1835 'for': ('for', 'break continue while'), 1836 'from': 'import', 1837 'global': ('global', 'nonlocal NAMESPACES'), 1838 'if': ('if', 'TRUTHVALUE'), 1839 'import': ('import', 'MODULES'), 1840 'in': ('in', 'SEQUENCEMETHODS'), 1841 'is': 'COMPARISON', 1842 'lambda': ('lambda', 'FUNCTIONS'), 1843 'nonlocal': ('nonlocal', 'global NAMESPACES'), 1844 'not': 'BOOLEAN', 1845 'or': 'BOOLEAN', 1846 'pass': ('pass', ''), 1847 'raise': ('raise', 'EXCEPTIONS'), 1848 'return': ('return', 'FUNCTIONS'), 1849 'try': ('try', 'EXCEPTIONS'), 1850 'while': ('while', 'break continue if TRUTHVALUE'), 1851 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 1852 'yield': ('yield', ''), 1853 } 1854 # Either add symbols to this dictionary or to the symbols dictionary 1855 # directly: Whichever is easier. They are merged later. 1856 _strprefixes = [p + q for p in ('b', 'f', 'r', 'u') for q in ("'", '"')] 1857 _symbols_inverse = { 1858 'STRINGS' : ("'", "'''", '"', '"""', *_strprefixes), 1859 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', 1860 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), 1861 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), 1862 'UNARY' : ('-', '~'), 1863 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', 1864 '^=', '<<=', '>>=', '**=', '//='), 1865 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), 1866 'COMPLEX' : ('j', 'J') 1867 } 1868 symbols = { 1869 '%': 'OPERATORS FORMATTING', 1870 '**': 'POWER', 1871 ',': 'TUPLES LISTS FUNCTIONS', 1872 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', 1873 '...': 'ELLIPSIS', 1874 ':': 'SLICINGS DICTIONARYLITERALS', 1875 '@': 'def class', 1876 '\\': 'STRINGS', 1877 '_': 'PRIVATENAMES', 1878 '__': 'PRIVATENAMES SPECIALMETHODS', 1879 '`': 'BACKQUOTES', 1880 '(': 'TUPLES FUNCTIONS CALLS', 1881 ')': 'TUPLES FUNCTIONS CALLS', 1882 '[': 'LISTS SUBSCRIPTS SLICINGS', 1883 ']': 'LISTS SUBSCRIPTS SLICINGS' 1884 } 1885 for topic, symbols_ in _symbols_inverse.items(): 1886 for symbol in symbols_: 1887 topics = symbols.get(symbol, topic) 1888 if topic not in topics: 1889 topics = topics + ' ' + topic 1890 symbols[symbol] = topics 1891 1892 topics = { 1893 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' 1894 'FUNCTIONS CLASSES MODULES FILES inspect'), 1895 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS ' 1896 'FORMATTING TYPES'), 1897 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), 1898 'FORMATTING': ('formatstrings', 'OPERATORS'), 1899 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' 1900 'FORMATTING TYPES'), 1901 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), 1902 'INTEGER': ('integers', 'int range'), 1903 'FLOAT': ('floating', 'float math'), 1904 'COMPLEX': ('imaginary', 'complex cmath'), 1905 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING range LISTS'), 1906 'MAPPINGS': 'DICTIONARIES', 1907 'FUNCTIONS': ('typesfunctions', 'def TYPES'), 1908 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), 1909 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), 1910 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), 1911 'FRAMEOBJECTS': 'TYPES', 1912 'TRACEBACKS': 'TYPES', 1913 'NONE': ('bltin-null-object', ''), 1914 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), 1915 'SPECIALATTRIBUTES': ('specialattrs', ''), 1916 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), 1917 'MODULES': ('typesmodules', 'import'), 1918 'PACKAGES': 'import', 1919 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' 1920 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' 1921 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' 1922 'LISTS DICTIONARIES'), 1923 'OPERATORS': 'EXPRESSIONS', 1924 'PRECEDENCE': 'EXPRESSIONS', 1925 'OBJECTS': ('objects', 'TYPES'), 1926 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' 1927 'CALLABLEMETHODS SEQUENCEMETHODS MAPPINGMETHODS ' 1928 'NUMBERMETHODS CLASSES'), 1929 'BASICMETHODS': ('customization', 'hash repr str SPECIALMETHODS'), 1930 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 1931 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), 1932 'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS ' 1933 'SPECIALMETHODS'), 1934 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 1935 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' 1936 'SPECIALMETHODS'), 1937 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 1938 'NAMESPACES': ('naming', 'global nonlocal ASSIGNMENT DELETION DYNAMICFEATURES'), 1939 'DYNAMICFEATURES': ('dynamic-features', ''), 1940 'SCOPING': 'NAMESPACES', 1941 'FRAMES': 'NAMESPACES', 1942 'EXCEPTIONS': ('exceptions', 'try except finally raise'), 1943 'CONVERSIONS': ('conversions', ''), 1944 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), 1945 'SPECIALIDENTIFIERS': ('id-classes', ''), 1946 'PRIVATENAMES': ('atom-identifiers', ''), 1947 'LITERALS': ('atom-literals', 'STRINGS NUMBERS TUPLELITERALS ' 1948 'LISTLITERALS DICTIONARYLITERALS'), 1949 'TUPLES': 'SEQUENCES', 1950 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), 1951 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), 1952 'LISTLITERALS': ('lists', 'LISTS LITERALS'), 1953 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 1954 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 1955 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), 1956 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS'), 1957 'SLICINGS': ('slicings', 'SEQUENCEMETHODS'), 1958 'CALLS': ('calls', 'EXPRESSIONS'), 1959 'POWER': ('power', 'EXPRESSIONS'), 1960 'UNARY': ('unary', 'EXPRESSIONS'), 1961 'BINARY': ('binary', 'EXPRESSIONS'), 1962 'SHIFTING': ('shifting', 'EXPRESSIONS'), 1963 'BITWISE': ('bitwise', 'EXPRESSIONS'), 1964 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), 1965 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), 1966 'ASSERTION': 'assert', 1967 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), 1968 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), 1969 'DELETION': 'del', 1970 'RETURNING': 'return', 1971 'IMPORTING': 'import', 1972 'CONDITIONAL': 'if', 1973 'LOOPING': ('compound', 'for while break continue'), 1974 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), 1975 'DEBUGGING': ('debugger', 'pdb'), 1976 'CONTEXTMANAGERS': ('context-managers', 'with'), 1977 } 1978 1979 def __init__(self, input=None, output=None): 1980 self._input = input 1981 self._output = output 1982 1983 @property 1984 def input(self): 1985 return self._input or sys.stdin 1986 1987 @property 1988 def output(self): 1989 return self._output or sys.stdout 1990 1991 def __repr__(self): 1992 if inspect.stack()[1][3] == '?': 1993 self() 1994 return '' 1995 return '<%s.%s instance>' % (self.__class__.__module__, 1996 self.__class__.__qualname__) 1997 1998 _GoInteractive = object() 1999 def __call__(self, request=_GoInteractive): 2000 if request is not self._GoInteractive: 2001 self.help(request) 2002 else: 2003 self.intro() 2004 self.interact() 2005 self.output.write(''' 2006You are now leaving help and returning to the Python interpreter. 2007If you want to ask for help on a particular object directly from the 2008interpreter, you can type "help(object)". Executing "help('string')" 2009has the same effect as typing a particular string at the help> prompt. 2010''') 2011 2012 def interact(self): 2013 self.output.write('\n') 2014 while True: 2015 try: 2016 request = self.getline('help> ') 2017 if not request: break 2018 except (KeyboardInterrupt, EOFError): 2019 break 2020 request = request.strip() 2021 2022 # Make sure significant trailing quoting marks of literals don't 2023 # get deleted while cleaning input 2024 if (len(request) > 2 and request[0] == request[-1] in ("'", '"') 2025 and request[0] not in request[1:-1]): 2026 request = request[1:-1] 2027 if request.lower() in ('q', 'quit'): break 2028 if request == 'help': 2029 self.intro() 2030 else: 2031 self.help(request) 2032 2033 def getline(self, prompt): 2034 """Read one line, using input() when appropriate.""" 2035 if self.input is sys.stdin: 2036 return input(prompt) 2037 else: 2038 self.output.write(prompt) 2039 self.output.flush() 2040 return self.input.readline() 2041 2042 def help(self, request): 2043 if type(request) is type(''): 2044 request = request.strip() 2045 if request == 'keywords': self.listkeywords() 2046 elif request == 'symbols': self.listsymbols() 2047 elif request == 'topics': self.listtopics() 2048 elif request == 'modules': self.listmodules() 2049 elif request[:8] == 'modules ': 2050 self.listmodules(request.split()[1]) 2051 elif request in self.symbols: self.showsymbol(request) 2052 elif request in ['True', 'False', 'None']: 2053 # special case these keywords since they are objects too 2054 doc(eval(request), 'Help on %s:') 2055 elif request in self.keywords: self.showtopic(request) 2056 elif request in self.topics: self.showtopic(request) 2057 elif request: doc(request, 'Help on %s:', output=self._output) 2058 else: doc(str, 'Help on %s:', output=self._output) 2059 elif isinstance(request, Helper): self() 2060 else: doc(request, 'Help on %s:', output=self._output) 2061 self.output.write('\n') 2062 2063 def intro(self): 2064 self.output.write(''' 2065Welcome to Python {0}'s help utility! 2066 2067If this is your first time using Python, you should definitely check out 2068the tutorial on the Internet at https://docs.python.org/{0}/tutorial/. 2069 2070Enter the name of any module, keyword, or topic to get help on writing 2071Python programs and using Python modules. To quit this help utility and 2072return to the interpreter, just type "quit". 2073 2074To get a list of available modules, keywords, symbols, or topics, type 2075"modules", "keywords", "symbols", or "topics". Each module also comes 2076with a one-line summary of what it does; to list the modules whose name 2077or summary contain a given string such as "spam", type "modules spam". 2078'''.format('%d.%d' % sys.version_info[:2])) 2079 2080 def list(self, items, columns=4, width=80): 2081 items = list(sorted(items)) 2082 colw = width // columns 2083 rows = (len(items) + columns - 1) // columns 2084 for row in range(rows): 2085 for col in range(columns): 2086 i = col * rows + row 2087 if i < len(items): 2088 self.output.write(items[i]) 2089 if col < columns - 1: 2090 self.output.write(' ' + ' ' * (colw - 1 - len(items[i]))) 2091 self.output.write('\n') 2092 2093 def listkeywords(self): 2094 self.output.write(''' 2095Here is a list of the Python keywords. Enter any keyword to get more help. 2096 2097''') 2098 self.list(self.keywords.keys()) 2099 2100 def listsymbols(self): 2101 self.output.write(''' 2102Here is a list of the punctuation symbols which Python assigns special meaning 2103to. Enter any symbol to get more help. 2104 2105''') 2106 self.list(self.symbols.keys()) 2107 2108 def listtopics(self): 2109 self.output.write(''' 2110Here is a list of available topics. Enter any topic name to get more help. 2111 2112''') 2113 self.list(self.topics.keys()) 2114 2115 def showtopic(self, topic, more_xrefs=''): 2116 try: 2117 import pydoc_data.topics 2118 except ImportError: 2119 self.output.write(''' 2120Sorry, topic and keyword documentation is not available because the 2121module "pydoc_data.topics" could not be found. 2122''') 2123 return 2124 target = self.topics.get(topic, self.keywords.get(topic)) 2125 if not target: 2126 self.output.write('no documentation found for %s\n' % repr(topic)) 2127 return 2128 if type(target) is type(''): 2129 return self.showtopic(target, more_xrefs) 2130 2131 label, xrefs = target 2132 try: 2133 doc = pydoc_data.topics.topics[label] 2134 except KeyError: 2135 self.output.write('no documentation found for %s\n' % repr(topic)) 2136 return 2137 doc = doc.strip() + '\n' 2138 if more_xrefs: 2139 xrefs = (xrefs or '') + ' ' + more_xrefs 2140 if xrefs: 2141 import textwrap 2142 text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n' 2143 wrapped_text = textwrap.wrap(text, 72) 2144 doc += '\n%s\n' % '\n'.join(wrapped_text) 2145 pager(doc) 2146 2147 def _gettopic(self, topic, more_xrefs=''): 2148 """Return unbuffered tuple of (topic, xrefs). 2149 2150 If an error occurs here, the exception is caught and displayed by 2151 the url handler. 2152 2153 This function duplicates the showtopic method but returns its 2154 result directly so it can be formatted for display in an html page. 2155 """ 2156 try: 2157 import pydoc_data.topics 2158 except ImportError: 2159 return(''' 2160Sorry, topic and keyword documentation is not available because the 2161module "pydoc_data.topics" could not be found. 2162''' , '') 2163 target = self.topics.get(topic, self.keywords.get(topic)) 2164 if not target: 2165 raise ValueError('could not find topic') 2166 if isinstance(target, str): 2167 return self._gettopic(target, more_xrefs) 2168 label, xrefs = target 2169 doc = pydoc_data.topics.topics[label] 2170 if more_xrefs: 2171 xrefs = (xrefs or '') + ' ' + more_xrefs 2172 return doc, xrefs 2173 2174 def showsymbol(self, symbol): 2175 target = self.symbols[symbol] 2176 topic, _, xrefs = target.partition(' ') 2177 self.showtopic(topic, xrefs) 2178 2179 def listmodules(self, key=''): 2180 if key: 2181 self.output.write(''' 2182Here is a list of modules whose name or summary contains '{}'. 2183If there are any, enter a module name to get more help. 2184 2185'''.format(key)) 2186 apropos(key) 2187 else: 2188 self.output.write(''' 2189Please wait a moment while I gather a list of all available modules... 2190 2191''') 2192 modules = {} 2193 def callback(path, modname, desc, modules=modules): 2194 if modname and modname[-9:] == '.__init__': 2195 modname = modname[:-9] + ' (package)' 2196 if modname.find('.') < 0: 2197 modules[modname] = 1 2198 def onerror(modname): 2199 callback(None, modname, None) 2200 ModuleScanner().run(callback, onerror=onerror) 2201 self.list(modules.keys()) 2202 self.output.write(''' 2203Enter any module name to get more help. Or, type "modules spam" to search 2204for modules whose name or summary contain the string "spam". 2205''') 2206 2207help = Helper() 2208 2209class ModuleScanner: 2210 """An interruptible scanner that searches module synopses.""" 2211 2212 def run(self, callback, key=None, completer=None, onerror=None): 2213 if key: key = key.lower() 2214 self.quit = False 2215 seen = {} 2216 2217 for modname in sys.builtin_module_names: 2218 if modname != '__main__': 2219 seen[modname] = 1 2220 if key is None: 2221 callback(None, modname, '') 2222 else: 2223 name = __import__(modname).__doc__ or '' 2224 desc = name.split('\n')[0] 2225 name = modname + ' - ' + desc 2226 if name.lower().find(key) >= 0: 2227 callback(None, modname, desc) 2228 2229 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): 2230 if self.quit: 2231 break 2232 2233 if key is None: 2234 callback(None, modname, '') 2235 else: 2236 try: 2237 spec = pkgutil._get_spec(importer, modname) 2238 except SyntaxError: 2239 # raised by tests for bad coding cookies or BOM 2240 continue 2241 loader = spec.loader 2242 if hasattr(loader, 'get_source'): 2243 try: 2244 source = loader.get_source(modname) 2245 except Exception: 2246 if onerror: 2247 onerror(modname) 2248 continue 2249 desc = source_synopsis(io.StringIO(source)) or '' 2250 if hasattr(loader, 'get_filename'): 2251 path = loader.get_filename(modname) 2252 else: 2253 path = None 2254 else: 2255 try: 2256 module = importlib._bootstrap._load(spec) 2257 except ImportError: 2258 if onerror: 2259 onerror(modname) 2260 continue 2261 desc = module.__doc__.splitlines()[0] if module.__doc__ else '' 2262 path = getattr(module,'__file__',None) 2263 name = modname + ' - ' + desc 2264 if name.lower().find(key) >= 0: 2265 callback(path, modname, desc) 2266 2267 if completer: 2268 completer() 2269 2270def apropos(key): 2271 """Print all the one-line module summaries that contain a substring.""" 2272 def callback(path, modname, desc): 2273 if modname[-9:] == '.__init__': 2274 modname = modname[:-9] + ' (package)' 2275 print(modname, desc and '- ' + desc) 2276 def onerror(modname): 2277 pass 2278 with warnings.catch_warnings(): 2279 warnings.filterwarnings('ignore') # ignore problems during import 2280 ModuleScanner().run(callback, key, onerror=onerror) 2281 2282# --------------------------------------- enhanced Web browser interface 2283 2284def _start_server(urlhandler, hostname, port): 2285 """Start an HTTP server thread on a specific port. 2286 2287 Start an HTML/text server thread, so HTML or text documents can be 2288 browsed dynamically and interactively with a Web browser. Example use: 2289 2290 >>> import time 2291 >>> import pydoc 2292 2293 Define a URL handler. To determine what the client is asking 2294 for, check the URL and content_type. 2295 2296 Then get or generate some text or HTML code and return it. 2297 2298 >>> def my_url_handler(url, content_type): 2299 ... text = 'the URL sent was: (%s, %s)' % (url, content_type) 2300 ... return text 2301 2302 Start server thread on port 0. 2303 If you use port 0, the server will pick a random port number. 2304 You can then use serverthread.port to get the port number. 2305 2306 >>> port = 0 2307 >>> serverthread = pydoc._start_server(my_url_handler, port) 2308 2309 Check that the server is really started. If it is, open browser 2310 and get first page. Use serverthread.url as the starting page. 2311 2312 >>> if serverthread.serving: 2313 ... import webbrowser 2314 2315 The next two lines are commented out so a browser doesn't open if 2316 doctest is run on this module. 2317 2318 #... webbrowser.open(serverthread.url) 2319 #True 2320 2321 Let the server do its thing. We just need to monitor its status. 2322 Use time.sleep so the loop doesn't hog the CPU. 2323 2324 >>> starttime = time.monotonic() 2325 >>> timeout = 1 #seconds 2326 2327 This is a short timeout for testing purposes. 2328 2329 >>> while serverthread.serving: 2330 ... time.sleep(.01) 2331 ... if serverthread.serving and time.monotonic() - starttime > timeout: 2332 ... serverthread.stop() 2333 ... break 2334 2335 Print any errors that may have occurred. 2336 2337 >>> print(serverthread.error) 2338 None 2339 """ 2340 import http.server 2341 import email.message 2342 import select 2343 import threading 2344 2345 class DocHandler(http.server.BaseHTTPRequestHandler): 2346 2347 def do_GET(self): 2348 """Process a request from an HTML browser. 2349 2350 The URL received is in self.path. 2351 Get an HTML page from self.urlhandler and send it. 2352 """ 2353 if self.path.endswith('.css'): 2354 content_type = 'text/css' 2355 else: 2356 content_type = 'text/html' 2357 self.send_response(200) 2358 self.send_header('Content-Type', '%s; charset=UTF-8' % content_type) 2359 self.end_headers() 2360 self.wfile.write(self.urlhandler( 2361 self.path, content_type).encode('utf-8')) 2362 2363 def log_message(self, *args): 2364 # Don't log messages. 2365 pass 2366 2367 class DocServer(http.server.HTTPServer): 2368 2369 def __init__(self, host, port, callback): 2370 self.host = host 2371 self.address = (self.host, port) 2372 self.callback = callback 2373 self.base.__init__(self, self.address, self.handler) 2374 self.quit = False 2375 2376 def serve_until_quit(self): 2377 while not self.quit: 2378 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 2379 if rd: 2380 self.handle_request() 2381 self.server_close() 2382 2383 def server_activate(self): 2384 self.base.server_activate(self) 2385 if self.callback: 2386 self.callback(self) 2387 2388 class ServerThread(threading.Thread): 2389 2390 def __init__(self, urlhandler, host, port): 2391 self.urlhandler = urlhandler 2392 self.host = host 2393 self.port = int(port) 2394 threading.Thread.__init__(self) 2395 self.serving = False 2396 self.error = None 2397 2398 def run(self): 2399 """Start the server.""" 2400 try: 2401 DocServer.base = http.server.HTTPServer 2402 DocServer.handler = DocHandler 2403 DocHandler.MessageClass = email.message.Message 2404 DocHandler.urlhandler = staticmethod(self.urlhandler) 2405 docsvr = DocServer(self.host, self.port, self.ready) 2406 self.docserver = docsvr 2407 docsvr.serve_until_quit() 2408 except Exception as e: 2409 self.error = e 2410 2411 def ready(self, server): 2412 self.serving = True 2413 self.host = server.host 2414 self.port = server.server_port 2415 self.url = 'http://%s:%d/' % (self.host, self.port) 2416 2417 def stop(self): 2418 """Stop the server and this thread nicely""" 2419 self.docserver.quit = True 2420 self.join() 2421 # explicitly break a reference cycle: DocServer.callback 2422 # has indirectly a reference to ServerThread. 2423 self.docserver = None 2424 self.serving = False 2425 self.url = None 2426 2427 thread = ServerThread(urlhandler, hostname, port) 2428 thread.start() 2429 # Wait until thread.serving is True to make sure we are 2430 # really up before returning. 2431 while not thread.error and not thread.serving: 2432 time.sleep(.01) 2433 return thread 2434 2435 2436def _url_handler(url, content_type="text/html"): 2437 """The pydoc url handler for use with the pydoc server. 2438 2439 If the content_type is 'text/css', the _pydoc.css style 2440 sheet is read and returned if it exits. 2441 2442 If the content_type is 'text/html', then the result of 2443 get_html_page(url) is returned. 2444 """ 2445 class _HTMLDoc(HTMLDoc): 2446 2447 def page(self, title, contents): 2448 """Format an HTML page.""" 2449 css_path = "pydoc_data/_pydoc.css" 2450 css_link = ( 2451 '<link rel="stylesheet" type="text/css" href="%s">' % 2452 css_path) 2453 return '''\ 2454<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 2455<html><head><title>Pydoc: %s</title> 2456<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 2457%s</head><body bgcolor="#f0f0f8">%s<div style="clear:both;padding-top:.5em;">%s</div> 2458</body></html>''' % (title, css_link, html_navbar(), contents) 2459 2460 def filelink(self, url, path): 2461 return '<a href="getfile?key=%s">%s</a>' % (url, path) 2462 2463 2464 html = _HTMLDoc() 2465 2466 def html_navbar(): 2467 version = html.escape("%s [%s, %s]" % (platform.python_version(), 2468 platform.python_build()[0], 2469 platform.python_compiler())) 2470 return """ 2471 <div style='float:left'> 2472 Python %s<br>%s 2473 </div> 2474 <div style='float:right'> 2475 <div style='text-align:center'> 2476 <a href="index.html">Module Index</a> 2477 : <a href="topics.html">Topics</a> 2478 : <a href="keywords.html">Keywords</a> 2479 </div> 2480 <div> 2481 <form action="get" style='display:inline;'> 2482 <input type=text name=key size=15> 2483 <input type=submit value="Get"> 2484 </form> 2485 <form action="search" style='display:inline;'> 2486 <input type=text name=key size=15> 2487 <input type=submit value="Search"> 2488 </form> 2489 </div> 2490 </div> 2491 """ % (version, html.escape(platform.platform(terse=True))) 2492 2493 def html_index(): 2494 """Module Index page.""" 2495 2496 def bltinlink(name): 2497 return '<a href="%s.html">%s</a>' % (name, name) 2498 2499 heading = html.heading( 2500 '<big><big><strong>Index of Modules</strong></big></big>', 2501 '#ffffff', '#7799ee') 2502 names = [name for name in sys.builtin_module_names 2503 if name != '__main__'] 2504 contents = html.multicolumn(names, bltinlink) 2505 contents = [heading, '<p>' + html.bigsection( 2506 'Built-in Modules', '#ffffff', '#ee77aa', contents)] 2507 2508 seen = {} 2509 for dir in sys.path: 2510 contents.append(html.index(dir, seen)) 2511 2512 contents.append( 2513 '<p align=right><font color="#909090" face="helvetica,' 2514 'arial"><strong>pydoc</strong> by Ka-Ping Yee' 2515 '<ping@lfw.org></font>') 2516 return 'Index of Modules', ''.join(contents) 2517 2518 def html_search(key): 2519 """Search results page.""" 2520 # scan for modules 2521 search_result = [] 2522 2523 def callback(path, modname, desc): 2524 if modname[-9:] == '.__init__': 2525 modname = modname[:-9] + ' (package)' 2526 search_result.append((modname, desc and '- ' + desc)) 2527 2528 with warnings.catch_warnings(): 2529 warnings.filterwarnings('ignore') # ignore problems during import 2530 def onerror(modname): 2531 pass 2532 ModuleScanner().run(callback, key, onerror=onerror) 2533 2534 # format page 2535 def bltinlink(name): 2536 return '<a href="%s.html">%s</a>' % (name, name) 2537 2538 results = [] 2539 heading = html.heading( 2540 '<big><big><strong>Search Results</strong></big></big>', 2541 '#ffffff', '#7799ee') 2542 for name, desc in search_result: 2543 results.append(bltinlink(name) + desc) 2544 contents = heading + html.bigsection( 2545 'key = %s' % key, '#ffffff', '#ee77aa', '<br>'.join(results)) 2546 return 'Search Results', contents 2547 2548 def html_getfile(path): 2549 """Get and display a source file listing safely.""" 2550 path = urllib.parse.unquote(path) 2551 with tokenize.open(path) as fp: 2552 lines = html.escape(fp.read()) 2553 body = '<pre>%s</pre>' % lines 2554 heading = html.heading( 2555 '<big><big><strong>File Listing</strong></big></big>', 2556 '#ffffff', '#7799ee') 2557 contents = heading + html.bigsection( 2558 'File: %s' % path, '#ffffff', '#ee77aa', body) 2559 return 'getfile %s' % path, contents 2560 2561 def html_topics(): 2562 """Index of topic texts available.""" 2563 2564 def bltinlink(name): 2565 return '<a href="topic?key=%s">%s</a>' % (name, name) 2566 2567 heading = html.heading( 2568 '<big><big><strong>INDEX</strong></big></big>', 2569 '#ffffff', '#7799ee') 2570 names = sorted(Helper.topics.keys()) 2571 2572 contents = html.multicolumn(names, bltinlink) 2573 contents = heading + html.bigsection( 2574 'Topics', '#ffffff', '#ee77aa', contents) 2575 return 'Topics', contents 2576 2577 def html_keywords(): 2578 """Index of keywords.""" 2579 heading = html.heading( 2580 '<big><big><strong>INDEX</strong></big></big>', 2581 '#ffffff', '#7799ee') 2582 names = sorted(Helper.keywords.keys()) 2583 2584 def bltinlink(name): 2585 return '<a href="topic?key=%s">%s</a>' % (name, name) 2586 2587 contents = html.multicolumn(names, bltinlink) 2588 contents = heading + html.bigsection( 2589 'Keywords', '#ffffff', '#ee77aa', contents) 2590 return 'Keywords', contents 2591 2592 def html_topicpage(topic): 2593 """Topic or keyword help page.""" 2594 buf = io.StringIO() 2595 htmlhelp = Helper(buf, buf) 2596 contents, xrefs = htmlhelp._gettopic(topic) 2597 if topic in htmlhelp.keywords: 2598 title = 'KEYWORD' 2599 else: 2600 title = 'TOPIC' 2601 heading = html.heading( 2602 '<big><big><strong>%s</strong></big></big>' % title, 2603 '#ffffff', '#7799ee') 2604 contents = '<pre>%s</pre>' % html.markup(contents) 2605 contents = html.bigsection(topic , '#ffffff','#ee77aa', contents) 2606 if xrefs: 2607 xrefs = sorted(xrefs.split()) 2608 2609 def bltinlink(name): 2610 return '<a href="topic?key=%s">%s</a>' % (name, name) 2611 2612 xrefs = html.multicolumn(xrefs, bltinlink) 2613 xrefs = html.section('Related help topics: ', 2614 '#ffffff', '#ee77aa', xrefs) 2615 return ('%s %s' % (title, topic), 2616 ''.join((heading, contents, xrefs))) 2617 2618 def html_getobj(url): 2619 obj = locate(url, forceload=1) 2620 if obj is None and url != 'None': 2621 raise ValueError('could not find object') 2622 title = describe(obj) 2623 content = html.document(obj, url) 2624 return title, content 2625 2626 def html_error(url, exc): 2627 heading = html.heading( 2628 '<big><big><strong>Error</strong></big></big>', 2629 '#ffffff', '#7799ee') 2630 contents = '<br>'.join(html.escape(line) for line in 2631 format_exception_only(type(exc), exc)) 2632 contents = heading + html.bigsection(url, '#ffffff', '#bb0000', 2633 contents) 2634 return "Error - %s" % url, contents 2635 2636 def get_html_page(url): 2637 """Generate an HTML page for url.""" 2638 complete_url = url 2639 if url.endswith('.html'): 2640 url = url[:-5] 2641 try: 2642 if url in ("", "index"): 2643 title, content = html_index() 2644 elif url == "topics": 2645 title, content = html_topics() 2646 elif url == "keywords": 2647 title, content = html_keywords() 2648 elif '=' in url: 2649 op, _, url = url.partition('=') 2650 if op == "search?key": 2651 title, content = html_search(url) 2652 elif op == "getfile?key": 2653 title, content = html_getfile(url) 2654 elif op == "topic?key": 2655 # try topics first, then objects. 2656 try: 2657 title, content = html_topicpage(url) 2658 except ValueError: 2659 title, content = html_getobj(url) 2660 elif op == "get?key": 2661 # try objects first, then topics. 2662 if url in ("", "index"): 2663 title, content = html_index() 2664 else: 2665 try: 2666 title, content = html_getobj(url) 2667 except ValueError: 2668 title, content = html_topicpage(url) 2669 else: 2670 raise ValueError('bad pydoc url') 2671 else: 2672 title, content = html_getobj(url) 2673 except Exception as exc: 2674 # Catch any errors and display them in an error page. 2675 title, content = html_error(complete_url, exc) 2676 return html.page(title, content) 2677 2678 if url.startswith('/'): 2679 url = url[1:] 2680 if content_type == 'text/css': 2681 path_here = os.path.dirname(os.path.realpath(__file__)) 2682 css_path = os.path.join(path_here, url) 2683 with open(css_path) as fp: 2684 return ''.join(fp.readlines()) 2685 elif content_type == 'text/html': 2686 return get_html_page(url) 2687 # Errors outside the url handler are caught by the server. 2688 raise TypeError('unknown content type %r for url %s' % (content_type, url)) 2689 2690 2691def browse(port=0, *, open_browser=True, hostname='localhost'): 2692 """Start the enhanced pydoc Web server and open a Web browser. 2693 2694 Use port '0' to start the server on an arbitrary port. 2695 Set open_browser to False to suppress opening a browser. 2696 """ 2697 import webbrowser 2698 serverthread = _start_server(_url_handler, hostname, port) 2699 if serverthread.error: 2700 print(serverthread.error) 2701 return 2702 if serverthread.serving: 2703 server_help_msg = 'Server commands: [b]rowser, [q]uit' 2704 if open_browser: 2705 webbrowser.open(serverthread.url) 2706 try: 2707 print('Server ready at', serverthread.url) 2708 print(server_help_msg) 2709 while serverthread.serving: 2710 cmd = input('server> ') 2711 cmd = cmd.lower() 2712 if cmd == 'q': 2713 break 2714 elif cmd == 'b': 2715 webbrowser.open(serverthread.url) 2716 else: 2717 print(server_help_msg) 2718 except (KeyboardInterrupt, EOFError): 2719 print() 2720 finally: 2721 if serverthread.serving: 2722 serverthread.stop() 2723 print('Server stopped') 2724 2725 2726# -------------------------------------------------- command-line interface 2727 2728def ispath(x): 2729 return isinstance(x, str) and x.find(os.sep) >= 0 2730 2731def _get_revised_path(given_path, argv0): 2732 """Ensures current directory is on returned path, and argv0 directory is not 2733 2734 Exception: argv0 dir is left alone if it's also pydoc's directory. 2735 2736 Returns a new path entry list, or None if no adjustment is needed. 2737 """ 2738 # Scripts may get the current directory in their path by default if they're 2739 # run with the -m switch, or directly from the current directory. 2740 # The interactive prompt also allows imports from the current directory. 2741 2742 # Accordingly, if the current directory is already present, don't make 2743 # any changes to the given_path 2744 if '' in given_path or os.curdir in given_path or os.getcwd() in given_path: 2745 return None 2746 2747 # Otherwise, add the current directory to the given path, and remove the 2748 # script directory (as long as the latter isn't also pydoc's directory. 2749 stdlib_dir = os.path.dirname(__file__) 2750 script_dir = os.path.dirname(argv0) 2751 revised_path = given_path.copy() 2752 if script_dir in given_path and not os.path.samefile(script_dir, stdlib_dir): 2753 revised_path.remove(script_dir) 2754 revised_path.insert(0, os.getcwd()) 2755 return revised_path 2756 2757 2758# Note: the tests only cover _get_revised_path, not _adjust_cli_path itself 2759def _adjust_cli_sys_path(): 2760 """Ensures current directory is on sys.path, and __main__ directory is not. 2761 2762 Exception: __main__ dir is left alone if it's also pydoc's directory. 2763 """ 2764 revised_path = _get_revised_path(sys.path, sys.argv[0]) 2765 if revised_path is not None: 2766 sys.path[:] = revised_path 2767 2768 2769def cli(): 2770 """Command-line interface (looks at sys.argv to decide what to do).""" 2771 import getopt 2772 class BadUsage(Exception): pass 2773 2774 _adjust_cli_sys_path() 2775 2776 try: 2777 opts, args = getopt.getopt(sys.argv[1:], 'bk:n:p:w') 2778 writing = False 2779 start_server = False 2780 open_browser = False 2781 port = 0 2782 hostname = 'localhost' 2783 for opt, val in opts: 2784 if opt == '-b': 2785 start_server = True 2786 open_browser = True 2787 if opt == '-k': 2788 apropos(val) 2789 return 2790 if opt == '-p': 2791 start_server = True 2792 port = val 2793 if opt == '-w': 2794 writing = True 2795 if opt == '-n': 2796 start_server = True 2797 hostname = val 2798 2799 if start_server: 2800 browse(port, hostname=hostname, open_browser=open_browser) 2801 return 2802 2803 if not args: raise BadUsage 2804 for arg in args: 2805 if ispath(arg) and not os.path.exists(arg): 2806 print('file %r does not exist' % arg) 2807 break 2808 try: 2809 if ispath(arg) and os.path.isfile(arg): 2810 arg = importfile(arg) 2811 if writing: 2812 if ispath(arg) and os.path.isdir(arg): 2813 writedocs(arg) 2814 else: 2815 writedoc(arg) 2816 else: 2817 help.help(arg) 2818 except ErrorDuringImport as value: 2819 print(value) 2820 2821 except (getopt.error, BadUsage): 2822 cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0] 2823 print("""pydoc - the Python documentation tool 2824 2825{cmd} <name> ... 2826 Show text documentation on something. <name> may be the name of a 2827 Python keyword, topic, function, module, or package, or a dotted 2828 reference to a class or function within a module or module in a 2829 package. If <name> contains a '{sep}', it is used as the path to a 2830 Python source file to document. If name is 'keywords', 'topics', 2831 or 'modules', a listing of these things is displayed. 2832 2833{cmd} -k <keyword> 2834 Search for a keyword in the synopsis lines of all available modules. 2835 2836{cmd} -n <hostname> 2837 Start an HTTP server with the given hostname (default: localhost). 2838 2839{cmd} -p <port> 2840 Start an HTTP server on the given port on the local machine. Port 2841 number 0 can be used to get an arbitrary unused port. 2842 2843{cmd} -b 2844 Start an HTTP server on an arbitrary unused port and open a Web browser 2845 to interactively browse documentation. This option can be used in 2846 combination with -n and/or -p. 2847 2848{cmd} -w <name> ... 2849 Write out the HTML documentation for a module to a file in the current 2850 directory. If <name> contains a '{sep}', it is treated as a filename; if 2851 it names a directory, documentation is written for all the contents. 2852""".format(cmd=cmd, sep=os.sep)) 2853 2854if __name__ == '__main__': 2855 cli() 2856