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