Coverage for /pythoncovmergedfiles/medio/medio/usr/lib/python3.9/pydoc.py: 11%

1709 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

1#!/usr/bin/env python3 

2"""Generate Python documentation in HTML or text for interactive use. 

3 

4At the Python interactive prompt, calling help(thing) on a Python object 

5documents the object, and calling help() starts up an interactive 

6help session. 

7 

8Or, at the shell command line outside of Python: 

9 

10Run "pydoc <name>" to show documentation on something. <name> may be 

11the name of a function, module, package, or a dotted reference to a 

12class or function within a module or module in a package. If the 

13argument contains a path segment delimiter (e.g. slash on Unix, 

14backslash on Windows) it is treated as the path to a Python source file. 

15 

16Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines 

17of all available modules. 

18 

19Run "pydoc -n <hostname>" to start an HTTP server with the given 

20hostname (default: localhost) on the local machine. 

21 

22Run "pydoc -p <port>" to start an HTTP server on the given port on the 

23local machine. Port number 0 can be used to get an arbitrary unused port. 

24 

25Run "pydoc -b" to start an HTTP server on an arbitrary unused port and 

26open a Web browser to interactively browse documentation. Combine with 

27the -n and -p options to control the hostname and port used. 

28 

29Run "pydoc -w <name>" to write out the HTML documentation for a module 

30to a file named "<name>.html". 

31 

32Module docs for core modules are assumed to be in 

33 

34 /usr/share/doc/pythonX.Y/html/library 

35 

36if the pythonX.Y-doc package is installed or in 

37 

38 https://docs.python.org/X.Y/library/ 

39 

40This can be overridden by setting the PYTHONDOCS environment variable 

41to a different URL or to a local directory containing the Library 

42Reference Manual pages. 

43""" 

44__all__ = ['help'] 

45__author__ = "Ka-Ping Yee <ping@lfw.org>" 

46__date__ = "26 February 2001" 

47 

48__credits__ = """Guido van Rossum, for an excellent programming language. 

49Tommy Burnette, the original creator of manpy. 

50Paul Prescod, for all his work on onlinehelp. 

51Richard Chamberlain, for the first implementation of textdoc. 

52""" 

53 

54# Known bugs that can't be fixed here: 

55# - synopsis() cannot be prevented from clobbering existing 

56# loaded modules. 

57# - If the __file__ attribute on a module is a relative path and 

58# the current directory is changed with os.chdir(), an incorrect 

59# path will be displayed. 

60 

61import builtins 

62import importlib._bootstrap 

63import importlib._bootstrap_external 

64import importlib.machinery 

65import importlib.util 

66import inspect 

67import io 

68import os 

69import pkgutil 

70import platform 

71import re 

72import sys 

73import sysconfig 

74import time 

75import tokenize 

76import urllib.parse 

77import warnings 

78from collections import deque 

79from reprlib import Repr 

80from traceback import format_exception_only 

81 

82 

83# --------------------------------------------------------- common routines 

84 

85def pathdirs(): 

86 """Convert sys.path into a list of absolute, existing, unique paths.""" 

87 dirs = [] 

88 normdirs = [] 

89 for dir in sys.path: 

90 dir = os.path.abspath(dir or '.') 

91 normdir = os.path.normcase(dir) 

92 if normdir not in normdirs and os.path.isdir(dir): 

93 dirs.append(dir) 

94 normdirs.append(normdir) 

95 return dirs 

96 

97def _findclass(func): 

98 cls = sys.modules.get(func.__module__) 

99 if cls is None: 

100 return None 

101 for name in func.__qualname__.split('.')[:-1]: 

102 cls = getattr(cls, name) 

103 if not inspect.isclass(cls): 

104 return None 

105 return cls 

106 

107def _finddoc(obj): 

108 if inspect.ismethod(obj): 

109 name = obj.__func__.__name__ 

110 self = obj.__self__ 

111 if (inspect.isclass(self) and 

112 getattr(getattr(self, name, None), '__func__') is obj.__func__): 

113 # classmethod 

114 cls = self 

115 else: 

116 cls = self.__class__ 

117 elif inspect.isfunction(obj): 

118 name = obj.__name__ 

119 cls = _findclass(obj) 

120 if cls is None or getattr(cls, name) is not obj: 

121 return None 

122 elif inspect.isbuiltin(obj): 

123 name = obj.__name__ 

124 self = obj.__self__ 

125 if (inspect.isclass(self) and 

126 self.__qualname__ + '.' + name == obj.__qualname__): 

127 # classmethod 

128 cls = self 

129 else: 

130 cls = self.__class__ 

131 # Should be tested before isdatadescriptor(). 

132 elif isinstance(obj, property): 

133 func = obj.fget 

134 name = func.__name__ 

135 cls = _findclass(func) 

136 if cls is None or getattr(cls, name) is not obj: 

137 return None 

138 elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj): 

139 name = obj.__name__ 

140 cls = obj.__objclass__ 

141 if getattr(cls, name) is not obj: 

142 return None 

143 if inspect.ismemberdescriptor(obj): 

144 slots = getattr(cls, '__slots__', None) 

145 if isinstance(slots, dict) and name in slots: 

146 return slots[name] 

147 else: 

148 return None 

149 for base in cls.__mro__: 

150 try: 

151 doc = _getowndoc(getattr(base, name)) 

152 except AttributeError: 

153 continue 

154 if doc is not None: 

155 return doc 

156 return None 

157 

158def _getowndoc(obj): 

159 """Get the documentation string for an object if it is not 

160 inherited from its class.""" 

161 try: 

162 doc = object.__getattribute__(obj, '__doc__') 

163 if doc is None: 

164 return None 

165 if obj is not type: 

166 typedoc = type(obj).__doc__ 

167 if isinstance(typedoc, str) and typedoc == doc: 

168 return None 

169 return doc 

170 except AttributeError: 

171 return None 

172 

173def _getdoc(object): 

174 """Get the documentation string for an object. 

175 

176 All tabs are expanded to spaces. To clean up docstrings that are 

177 indented to line up with blocks of code, any whitespace than can be 

178 uniformly removed from the second line onwards is removed.""" 

179 doc = _getowndoc(object) 

180 if doc is None: 

181 try: 

182 doc = _finddoc(object) 

183 except (AttributeError, TypeError): 

184 return None 

185 if not isinstance(doc, str): 

186 return None 

187 return inspect.cleandoc(doc) 

188 

189def getdoc(object): 

190 """Get the doc string or comments for an object.""" 

191 result = _getdoc(object) or inspect.getcomments(object) 

192 return result and re.sub('^ *\n', '', result.rstrip()) or '' 

193 

194def splitdoc(doc): 

195 """Split a doc string into a synopsis line (if any) and the rest.""" 

196 lines = doc.strip().split('\n') 

197 if len(lines) == 1: 

198 return lines[0], '' 

199 elif len(lines) >= 2 and not lines[1].rstrip(): 

200 return lines[0], '\n'.join(lines[2:]) 

201 return '', '\n'.join(lines) 

202 

203def classname(object, modname): 

204 """Get a class name and qualify it with a module name if necessary.""" 

205 name = object.__name__ 

206 if object.__module__ != modname: 

207 name = object.__module__ + '.' + name 

208 return name 

209 

210def isdata(object): 

211 """Check if an object is of a type that probably means it's data.""" 

212 return not (inspect.ismodule(object) or inspect.isclass(object) or 

213 inspect.isroutine(object) or inspect.isframe(object) or 

214 inspect.istraceback(object) or inspect.iscode(object)) 

215 

216def replace(text, *pairs): 

217 """Do a series of global replacements on a string.""" 

218 while pairs: 

219 text = pairs[1].join(text.split(pairs[0])) 

220 pairs = pairs[2:] 

221 return text 

222 

223def cram(text, maxlen): 

224 """Omit part of a string if needed to make it fit in a maximum length.""" 

225 if len(text) > maxlen: 

226 pre = max(0, (maxlen-3)//2) 

227 post = max(0, maxlen-3-pre) 

228 return text[:pre] + '...' + text[len(text)-post:] 

229 return text 

230 

231_re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE) 

232def stripid(text): 

233 """Remove the hexadecimal id from a Python object representation.""" 

234 # The behaviour of %p is implementation-dependent in terms of case. 

235 return _re_stripid.sub(r'\1', text) 

236 

237def _is_bound_method(fn): 

238 """ 

239 Returns True if fn is a bound method, regardless of whether 

240 fn was implemented in Python or in C. 

241 """ 

242 if inspect.ismethod(fn): 

243 return True 

244 if inspect.isbuiltin(fn): 

245 self = getattr(fn, '__self__', None) 

246 return not (inspect.ismodule(self) or (self is None)) 

247 return False 

248 

249 

250def allmethods(cl): 

251 methods = {} 

252 for key, value in inspect.getmembers(cl, inspect.isroutine): 

253 methods[key] = 1 

254 for base in cl.__bases__: 

255 methods.update(allmethods(base)) # all your base are belong to us 

256 for key in methods.keys(): 

257 methods[key] = getattr(cl, key) 

258 return methods 

259 

260def _split_list(s, predicate): 

261 """Split sequence s via predicate, and return pair ([true], [false]). 

262 

263 The return value is a 2-tuple of lists, 

264 ([x for x in s if predicate(x)], 

265 [x for x in s if not predicate(x)]) 

266 """ 

267 

268 yes = [] 

269 no = [] 

270 for x in s: 

271 if predicate(x): 

272 yes.append(x) 

273 else: 

274 no.append(x) 

275 return yes, no 

276 

277def visiblename(name, all=None, obj=None): 

278 """Decide whether to show documentation on a variable.""" 

279 # Certain special names are redundant or internal. 

280 # XXX Remove __initializing__? 

281 if name in {'__author__', '__builtins__', '__cached__', '__credits__', 

282 '__date__', '__doc__', '__file__', '__spec__', 

283 '__loader__', '__module__', '__name__', '__package__', 

284 '__path__', '__qualname__', '__slots__', '__version__'}: 

285 return 0 

286 # Private names are hidden, but special names are displayed. 

287 if name.startswith('__') and name.endswith('__'): return 1 

288 # Namedtuples have public fields and methods with a single leading underscore 

289 if name.startswith('_') and hasattr(obj, '_fields'): 

290 return True 

291 if all is not None: 

292 # only document that which the programmer exported in __all__ 

293 return name in all 

294 else: 

295 return not name.startswith('_') 

296 

297def classify_class_attrs(object): 

298 """Wrap inspect.classify_class_attrs, with fixup for data descriptors.""" 

299 results = [] 

300 for (name, kind, cls, value) in inspect.classify_class_attrs(object): 

301 if inspect.isdatadescriptor(value): 

302 kind = 'data descriptor' 

303 if isinstance(value, property) and value.fset is None: 

304 kind = 'readonly property' 

305 results.append((name, kind, cls, value)) 

306 return results 

307 

308def sort_attributes(attrs, object): 

309 'Sort the attrs list in-place by _fields and then alphabetically by name' 

310 # This allows data descriptors to be ordered according 

311 # to a _fields attribute if present. 

312 fields = getattr(object, '_fields', []) 

313 try: 

314 field_order = {name : i-len(fields) for (i, name) in enumerate(fields)} 

315 except TypeError: 

316 field_order = {} 

317 keyfunc = lambda attr: (field_order.get(attr[0], 0), attr[0]) 

318 attrs.sort(key=keyfunc) 

319 

320# ----------------------------------------------------- module manipulation 

321 

322def ispackage(path): 

323 """Guess whether a path refers to a package directory.""" 

324 if os.path.isdir(path): 

325 for ext in ('.py', '.pyc'): 

326 if os.path.isfile(os.path.join(path, '__init__' + ext)): 

327 return True 

328 return False 

329 

330def source_synopsis(file): 

331 line = file.readline() 

332 while line[:1] == '#' or not line.strip(): 

333 line = file.readline() 

334 if not line: break 

335 line = line.strip() 

336 if line[:4] == 'r"""': line = line[1:] 

337 if line[:3] == '"""': 

338 line = line[3:] 

339 if line[-1:] == '\\': line = line[:-1] 

340 while not line.strip(): 

341 line = file.readline() 

342 if not line: break 

343 result = line.split('"""')[0].strip() 

344 else: result = None 

345 return result 

346 

347def synopsis(filename, cache={}): 

348 """Get the one-line summary out of a module file.""" 

349 mtime = os.stat(filename).st_mtime 

350 lastupdate, result = cache.get(filename, (None, None)) 

351 if lastupdate is None or lastupdate < mtime: 

352 # Look for binary suffixes first, falling back to source. 

353 if filename.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)): 

354 loader_cls = importlib.machinery.SourcelessFileLoader 

355 elif filename.endswith(tuple(importlib.machinery.EXTENSION_SUFFIXES)): 

356 loader_cls = importlib.machinery.ExtensionFileLoader 

357 else: 

358 loader_cls = None 

359 # Now handle the choice. 

360 if loader_cls is None: 

361 # Must be a source file. 

362 try: 

363 file = tokenize.open(filename) 

364 except OSError: 

365 # module can't be opened, so skip it 

366 return None 

367 # text modules can be directly examined 

368 with file: 

369 result = source_synopsis(file) 

370 else: 

371 # Must be a binary module, which has to be imported. 

372 loader = loader_cls('__temp__', filename) 

373 # XXX We probably don't need to pass in the loader here. 

374 spec = importlib.util.spec_from_file_location('__temp__', filename, 

375 loader=loader) 

376 try: 

377 module = importlib._bootstrap._load(spec) 

378 except: 

379 return None 

380 del sys.modules['__temp__'] 

381 result = module.__doc__.splitlines()[0] if module.__doc__ else None 

382 # Cache the result. 

383 cache[filename] = (mtime, result) 

384 return result 

385 

386class ErrorDuringImport(Exception): 

387 """Errors that occurred while trying to import something to document it.""" 

388 def __init__(self, filename, exc_info): 

389 self.filename = filename 

390 self.exc, self.value, self.tb = exc_info 

391 

392 def __str__(self): 

393 exc = self.exc.__name__ 

394 return 'problem in %s - %s: %s' % (self.filename, exc, self.value) 

395 

396def importfile(path): 

397 """Import a Python source file or compiled file given its path.""" 

398 magic = importlib.util.MAGIC_NUMBER 

399 with open(path, 'rb') as file: 

400 is_bytecode = magic == file.read(len(magic)) 

401 filename = os.path.basename(path) 

402 name, ext = os.path.splitext(filename) 

403 if is_bytecode: 

404 loader = importlib._bootstrap_external.SourcelessFileLoader(name, path) 

405 else: 

406 loader = importlib._bootstrap_external.SourceFileLoader(name, path) 

407 # XXX We probably don't need to pass in the loader here. 

408 spec = importlib.util.spec_from_file_location(name, path, loader=loader) 

409 try: 

410 return importlib._bootstrap._load(spec) 

411 except: 

412 raise ErrorDuringImport(path, sys.exc_info()) 

413 

414def safeimport(path, forceload=0, cache={}): 

415 """Import a module; handle errors; return None if the module isn't found. 

416 

417 If the module *is* found but an exception occurs, it's wrapped in an 

418 ErrorDuringImport exception and reraised. Unlike __import__, if a 

419 package path is specified, the module at the end of the path is returned, 

420 not the package at the beginning. If the optional 'forceload' argument 

421 is 1, we reload the module from disk (unless it's a dynamic extension).""" 

422 try: 

423 # If forceload is 1 and the module has been previously loaded from 

424 # disk, we always have to reload the module. Checking the file's 

425 # mtime isn't good enough (e.g. the module could contain a class 

426 # that inherits from another module that has changed). 

427 if forceload and path in sys.modules: 

428 if path not in sys.builtin_module_names: 

429 # Remove the module from sys.modules and re-import to try 

430 # and avoid problems with partially loaded modules. 

431 # Also remove any submodules because they won't appear 

432 # in the newly loaded module's namespace if they're already 

433 # in sys.modules. 

434 subs = [m for m in sys.modules if m.startswith(path + '.')] 

435 for key in [path] + subs: 

436 # Prevent garbage collection. 

437 cache[key] = sys.modules[key] 

438 del sys.modules[key] 

439 module = __import__(path) 

440 except: 

441 # Did the error occur before or after the module was found? 

442 (exc, value, tb) = info = sys.exc_info() 

443 if path in sys.modules: 

444 # An error occurred while executing the imported module. 

445 raise ErrorDuringImport(sys.modules[path].__file__, info) 

446 elif exc is SyntaxError: 

447 # A SyntaxError occurred before we could execute the module. 

448 raise ErrorDuringImport(value.filename, info) 

449 elif issubclass(exc, ImportError) and value.name == path: 

450 # No such module in the path. 

451 return None 

452 else: 

453 # Some other error occurred during the importing process. 

454 raise ErrorDuringImport(path, sys.exc_info()) 

455 for part in path.split('.')[1:]: 

456 try: module = getattr(module, part) 

457 except AttributeError: return None 

458 return module 

459 

460# ---------------------------------------------------- formatter base class 

461 

462class Doc: 

463 

464 PYTHONDOCS = os.environ.get("PYTHONDOCS", 

465 "https://docs.python.org/%d.%d/library" 

466 % sys.version_info[:2]) 

467 

468 def document(self, object, name=None, *args): 

469 """Generate documentation for an object.""" 

470 args = (object, name) + args 

471 # 'try' clause is to attempt to handle the possibility that inspect 

472 # identifies something in a way that pydoc itself has issues handling; 

473 # think 'super' and how it is a descriptor (which raises the exception 

474 # by lacking a __name__ attribute) and an instance. 

475 try: 

476 if inspect.ismodule(object): return self.docmodule(*args) 

477 if inspect.isclass(object): return self.docclass(*args) 

478 if inspect.isroutine(object): return self.docroutine(*args) 

479 except AttributeError: 

480 pass 

481 if inspect.isdatadescriptor(object): return self.docdata(*args) 

482 return self.docother(*args) 

483 

484 def fail(self, object, name=None, *args): 

485 """Raise an exception for unimplemented types.""" 

486 message = "don't know how to document object%s of type %s" % ( 

487 name and ' ' + repr(name), type(object).__name__) 

488 raise TypeError(message) 

489 

490 docmodule = docclass = docroutine = docother = docproperty = docdata = fail 

491 

492 def getdocloc(self, object, basedir=sysconfig.get_path('stdlib')): 

493 """Return the location of module docs or None""" 

494 

495 try: 

496 file = inspect.getabsfile(object) 

497 except TypeError: 

498 file = '(built-in)' 

499 

500 docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS) 

501 

502 basedir = os.path.normcase(basedir) 

503 if (isinstance(object, type(os)) and 

504 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp', 

505 'marshal', 'posix', 'signal', 'sys', 

506 '_thread', 'zipimport') or 

507 (file.startswith(basedir) and 

508 not file.startswith(os.path.join(basedir, 'dist-packages')) and 

509 not file.startswith(os.path.join(basedir, 'site-packages')))) and 

510 object.__name__ not in ('xml.etree', 'test.pydoc_mod')): 

511 if docloc.startswith(("http://", "https://")): 

512 docloc = "%s/%s" % (docloc.rstrip("/"), object.__name__.lower()) 

513 else: 

514 docloc = os.path.join(docloc, object.__name__.lower() + ".html") 

515 else: 

516 docloc = None 

517 return docloc 

518 

519# -------------------------------------------- HTML documentation generator 

520 

521class HTMLRepr(Repr): 

522 """Class for safely making an HTML representation of a Python object.""" 

523 def __init__(self): 

524 Repr.__init__(self) 

525 self.maxlist = self.maxtuple = 20 

526 self.maxdict = 10 

527 self.maxstring = self.maxother = 100 

528 

529 def escape(self, text): 

530 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;') 

531 

532 def repr(self, object): 

533 return Repr.repr(self, object) 

534 

535 def repr1(self, x, level): 

536 if hasattr(type(x), '__name__'): 

537 methodname = 'repr_' + '_'.join(type(x).__name__.split()) 

538 if hasattr(self, methodname): 

539 return getattr(self, methodname)(x, level) 

540 return self.escape(cram(stripid(repr(x)), self.maxother)) 

541 

542 def repr_string(self, x, level): 

543 test = cram(x, self.maxstring) 

544 testrepr = repr(test) 

545 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): 

546 # Backslashes are only literal in the string and are never 

547 # needed to make any special characters, so show a raw string. 

548 return 'r' + testrepr[0] + self.escape(test) + testrepr[0] 

549 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)', 

550 r'<font color="#c040c0">\1</font>', 

551 self.escape(testrepr)) 

552 

553 repr_str = repr_string 

554 

555 def repr_instance(self, x, level): 

556 try: 

557 return self.escape(cram(stripid(repr(x)), self.maxstring)) 

558 except: 

559 return self.escape('<%s instance>' % x.__class__.__name__) 

560 

561 repr_unicode = repr_string 

562 

563class HTMLDoc(Doc): 

564 """Formatter class for HTML documentation.""" 

565 

566 # ------------------------------------------- HTML formatting utilities 

567 

568 _repr_instance = HTMLRepr() 

569 repr = _repr_instance.repr 

570 escape = _repr_instance.escape 

571 

572 def page(self, title, contents): 

573 """Format an HTML page.""" 

574 return '''\ 

575<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 

576<html><head><title>Python: %s</title> 

577<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 

578</head><body bgcolor="#f0f0f8"> 

579%s 

580</body></html>''' % (title, contents) 

581 

582 def heading(self, title, fgcol, bgcol, extras=''): 

583 """Format a page heading.""" 

584 return ''' 

585<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> 

586<tr bgcolor="%s"> 

587<td valign=bottom>&nbsp;<br> 

588<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td 

589><td align=right valign=bottom 

590><font color="%s" face="helvetica, arial">%s</font></td></tr></table> 

591 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;') 

592 

593 def section(self, title, fgcol, bgcol, contents, width=6, 

594 prelude='', marginalia=None, gap='&nbsp;'): 

595 """Format a section with a heading.""" 

596 if marginalia is None: 

597 marginalia = '<tt>' + '&nbsp;' * width + '</tt>' 

598 result = '''<p> 

599<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 

600<tr bgcolor="%s"> 

601<td colspan=3 valign=bottom>&nbsp;<br> 

602<font color="%s" face="helvetica, arial">%s</font></td></tr> 

603 ''' % (bgcol, fgcol, title) 

604 if prelude: 

605 result = result + ''' 

606<tr bgcolor="%s"><td rowspan=2>%s</td> 

607<td colspan=2>%s</td></tr> 

608<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap) 

609 else: 

610 result = result + ''' 

611<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap) 

612 

613 return result + '\n<td width="100%%">%s</td></tr></table>' % contents 

614 

615 def bigsection(self, title, *args): 

616 """Format a section with a big heading.""" 

617 title = '<big><strong>%s</strong></big>' % title 

618 return self.section(title, *args) 

619 

620 def preformat(self, text): 

621 """Format literal preformatted text.""" 

622 text = self.escape(text.expandtabs()) 

623 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n', 

624 ' ', '&nbsp;', '\n', '<br>\n') 

625 

626 def multicolumn(self, list, format, cols=4): 

627 """Format a list of items into a multi-column list.""" 

628 result = '' 

629 rows = (len(list)+cols-1)//cols 

630 for col in range(cols): 

631 result = result + '<td width="%d%%" valign=top>' % (100//cols) 

632 for i in range(rows*col, rows*col+rows): 

633 if i < len(list): 

634 result = result + format(list[i]) + '<br>\n' 

635 result = result + '</td>' 

636 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result 

637 

638 def grey(self, text): return '<font color="#909090">%s</font>' % text 

639 

640 def namelink(self, name, *dicts): 

641 """Make a link for an identifier, given name-to-URL mappings.""" 

642 for dict in dicts: 

643 if name in dict: 

644 return '<a href="%s">%s</a>' % (dict[name], name) 

645 return name 

646 

647 def classlink(self, object, modname): 

648 """Make a link for a class.""" 

649 name, module = object.__name__, sys.modules.get(object.__module__) 

650 if hasattr(module, name) and getattr(module, name) is object: 

651 return '<a href="%s.html#%s">%s</a>' % ( 

652 module.__name__, name, classname(object, modname)) 

653 return classname(object, modname) 

654 

655 def modulelink(self, object): 

656 """Make a link for a module.""" 

657 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__) 

658 

659 def modpkglink(self, modpkginfo): 

660 """Make a link for a module or package to display in an index.""" 

661 name, path, ispackage, shadowed = modpkginfo 

662 if shadowed: 

663 return self.grey(name) 

664 if path: 

665 url = '%s.%s.html' % (path, name) 

666 else: 

667 url = '%s.html' % name 

668 if ispackage: 

669 text = '<strong>%s</strong>&nbsp;(package)' % name 

670 else: 

671 text = name 

672 return '<a href="%s">%s</a>' % (url, text) 

673 

674 def filelink(self, url, path): 

675 """Make a link to source file.""" 

676 return '<a href="file:%s">%s</a>' % (url, path) 

677 

678 def markup(self, text, escape=None, funcs={}, classes={}, methods={}): 

679 """Mark up some plain text, given a context of symbols to look for. 

680 Each context dictionary maps object names to anchor names.""" 

681 escape = escape or self.escape 

682 results = [] 

683 here = 0 

684 pattern = re.compile(r'\b((http|https|ftp)://\S+[\w/]|' 

685 r'RFC[- ]?(\d+)|' 

686 r'PEP[- ]?(\d+)|' 

687 r'(self\.)?(\w+))') 

688 while True: 

689 match = pattern.search(text, here) 

690 if not match: break 

691 start, end = match.span() 

692 results.append(escape(text[here:start])) 

693 

694 all, scheme, rfc, pep, selfdot, name = match.groups() 

695 if scheme: 

696 url = escape(all).replace('"', '&quot;') 

697 results.append('<a href="%s">%s</a>' % (url, url)) 

698 elif rfc: 

699 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) 

700 results.append('<a href="%s">%s</a>' % (url, escape(all))) 

701 elif pep: 

702 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) 

703 results.append('<a href="%s">%s</a>' % (url, escape(all))) 

704 elif selfdot: 

705 # Create a link for methods like 'self.method(...)' 

706 # and use <strong> for attributes like 'self.attr' 

707 if text[end:end+1] == '(': 

708 results.append('self.' + self.namelink(name, methods)) 

709 else: 

710 results.append('self.<strong>%s</strong>' % name) 

711 elif text[end:end+1] == '(': 

712 results.append(self.namelink(name, methods, funcs, classes)) 

713 else: 

714 results.append(self.namelink(name, classes)) 

715 here = end 

716 results.append(escape(text[here:])) 

717 return ''.join(results) 

718 

719 # ---------------------------------------------- type-specific routines 

720 

721 def formattree(self, tree, modname, parent=None): 

722 """Produce HTML for a class tree as given by inspect.getclasstree().""" 

723 result = '' 

724 for entry in tree: 

725 if type(entry) is type(()): 

726 c, bases = entry 

727 result = result + '<dt><font face="helvetica, arial">' 

728 result = result + self.classlink(c, modname) 

729 if bases and bases != (parent,): 

730 parents = [] 

731 for base in bases: 

732 parents.append(self.classlink(base, modname)) 

733 result = result + '(' + ', '.join(parents) + ')' 

734 result = result + '\n</font></dt>' 

735 elif type(entry) is type([]): 

736 result = result + '<dd>\n%s</dd>\n' % self.formattree( 

737 entry, modname, c) 

738 return '<dl>\n%s</dl>\n' % result 

739 

740 def docmodule(self, object, name=None, mod=None, *ignored): 

741 """Produce HTML documentation for a module object.""" 

742 name = object.__name__ # ignore the passed-in name 

743 try: 

744 all = object.__all__ 

745 except AttributeError: 

746 all = None 

747 parts = name.split('.') 

748 links = [] 

749 for i in range(len(parts)-1): 

750 links.append( 

751 '<a href="%s.html"><font color="#ffffff">%s</font></a>' % 

752 ('.'.join(parts[:i+1]), parts[i])) 

753 linkedname = '.'.join(links + parts[-1:]) 

754 head = '<big><big><strong>%s</strong></big></big>' % linkedname 

755 try: 

756 path = inspect.getabsfile(object) 

757 url = urllib.parse.quote(path) 

758 filelink = self.filelink(url, path) 

759 except TypeError: 

760 filelink = '(built-in)' 

761 info = [] 

762 if hasattr(object, '__version__'): 

763 version = str(object.__version__) 

764 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': 

765 version = version[11:-1].strip() 

766 info.append('version %s' % self.escape(version)) 

767 if hasattr(object, '__date__'): 

768 info.append(self.escape(str(object.__date__))) 

769 if info: 

770 head = head + ' (%s)' % ', '.join(info) 

771 docloc = self.getdocloc(object) 

772 if docloc is not None: 

773 docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals() 

774 else: 

775 docloc = '' 

776 result = self.heading( 

777 head, '#ffffff', '#7799ee', 

778 '<a href=".">index</a><br>' + filelink + docloc) 

779 

780 modules = inspect.getmembers(object, inspect.ismodule) 

781 

782 classes, cdict = [], {} 

783 for key, value in inspect.getmembers(object, inspect.isclass): 

784 # if __all__ exists, believe it. Otherwise use old heuristic. 

785 if (all is not None or 

786 (inspect.getmodule(value) or object) is object): 

787 if visiblename(key, all, object): 

788 classes.append((key, value)) 

789 cdict[key] = cdict[value] = '#' + key 

790 for key, value in classes: 

791 for base in value.__bases__: 

792 key, modname = base.__name__, base.__module__ 

793 module = sys.modules.get(modname) 

794 if modname != name and module and hasattr(module, key): 

795 if getattr(module, key) is base: 

796 if not key in cdict: 

797 cdict[key] = cdict[base] = modname + '.html#' + key 

798 funcs, fdict = [], {} 

799 for key, value in inspect.getmembers(object, inspect.isroutine): 

800 # if __all__ exists, believe it. Otherwise use old heuristic. 

801 if (all is not None or 

802 inspect.isbuiltin(value) or inspect.getmodule(value) is object): 

803 if visiblename(key, all, object): 

804 funcs.append((key, value)) 

805 fdict[key] = '#-' + key 

806 if inspect.isfunction(value): fdict[value] = fdict[key] 

807 data = [] 

808 for key, value in inspect.getmembers(object, isdata): 

809 if visiblename(key, all, object): 

810 data.append((key, value)) 

811 

812 doc = self.markup(getdoc(object), self.preformat, fdict, cdict) 

813 doc = doc and '<tt>%s</tt>' % doc 

814 result = result + '<p>%s</p>\n' % doc 

815 

816 if hasattr(object, '__path__'): 

817 modpkgs = [] 

818 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): 

819 modpkgs.append((modname, name, ispkg, 0)) 

820 modpkgs.sort() 

821 contents = self.multicolumn(modpkgs, self.modpkglink) 

822 result = result + self.bigsection( 

823 'Package Contents', '#ffffff', '#aa55cc', contents) 

824 elif modules: 

825 contents = self.multicolumn( 

826 modules, lambda t: self.modulelink(t[1])) 

827 result = result + self.bigsection( 

828 'Modules', '#ffffff', '#aa55cc', contents) 

829 

830 if classes: 

831 classlist = [value for (key, value) in classes] 

832 contents = [ 

833 self.formattree(inspect.getclasstree(classlist, 1), name)] 

834 for key, value in classes: 

835 contents.append(self.document(value, key, name, fdict, cdict)) 

836 result = result + self.bigsection( 

837 'Classes', '#ffffff', '#ee77aa', ' '.join(contents)) 

838 if funcs: 

839 contents = [] 

840 for key, value in funcs: 

841 contents.append(self.document(value, key, name, fdict, cdict)) 

842 result = result + self.bigsection( 

843 'Functions', '#ffffff', '#eeaa77', ' '.join(contents)) 

844 if data: 

845 contents = [] 

846 for key, value in data: 

847 contents.append(self.document(value, key)) 

848 result = result + self.bigsection( 

849 'Data', '#ffffff', '#55aa55', '<br>\n'.join(contents)) 

850 if hasattr(object, '__author__'): 

851 contents = self.markup(str(object.__author__), self.preformat) 

852 result = result + self.bigsection( 

853 'Author', '#ffffff', '#7799ee', contents) 

854 if hasattr(object, '__credits__'): 

855 contents = self.markup(str(object.__credits__), self.preformat) 

856 result = result + self.bigsection( 

857 'Credits', '#ffffff', '#7799ee', contents) 

858 

859 return result 

860 

861 def docclass(self, object, name=None, mod=None, funcs={}, classes={}, 

862 *ignored): 

863 """Produce HTML documentation for a class object.""" 

864 realname = object.__name__ 

865 name = name or realname 

866 bases = object.__bases__ 

867 

868 contents = [] 

869 push = contents.append 

870 

871 # Cute little class to pump out a horizontal rule between sections. 

872 class HorizontalRule: 

873 def __init__(self): 

874 self.needone = 0 

875 def maybe(self): 

876 if self.needone: 

877 push('<hr>\n') 

878 self.needone = 1 

879 hr = HorizontalRule() 

880 

881 # List the mro, if non-trivial. 

882 mro = deque(inspect.getmro(object)) 

883 if len(mro) > 2: 

884 hr.maybe() 

885 push('<dl><dt>Method resolution order:</dt>\n') 

886 for base in mro: 

887 push('<dd>%s</dd>\n' % self.classlink(base, 

888 object.__module__)) 

889 push('</dl>\n') 

890 

891 def spill(msg, attrs, predicate): 

892 ok, attrs = _split_list(attrs, predicate) 

893 if ok: 

894 hr.maybe() 

895 push(msg) 

896 for name, kind, homecls, value in ok: 

897 try: 

898 value = getattr(object, name) 

899 except Exception: 

900 # Some descriptors may meet a failure in their __get__. 

901 # (bug #1785) 

902 push(self.docdata(value, name, mod)) 

903 else: 

904 push(self.document(value, name, mod, 

905 funcs, classes, mdict, object)) 

906 push('\n') 

907 return attrs 

908 

909 def spilldescriptors(msg, attrs, predicate): 

910 ok, attrs = _split_list(attrs, predicate) 

911 if ok: 

912 hr.maybe() 

913 push(msg) 

914 for name, kind, homecls, value in ok: 

915 push(self.docdata(value, name, mod)) 

916 return attrs 

917 

918 def spilldata(msg, attrs, predicate): 

919 ok, attrs = _split_list(attrs, predicate) 

920 if ok: 

921 hr.maybe() 

922 push(msg) 

923 for name, kind, homecls, value in ok: 

924 base = self.docother(getattr(object, name), name, mod) 

925 doc = getdoc(value) 

926 if not doc: 

927 push('<dl><dt>%s</dl>\n' % base) 

928 else: 

929 doc = self.markup(getdoc(value), self.preformat, 

930 funcs, classes, mdict) 

931 doc = '<dd><tt>%s</tt>' % doc 

932 push('<dl><dt>%s%s</dl>\n' % (base, doc)) 

933 push('\n') 

934 return attrs 

935 

936 attrs = [(name, kind, cls, value) 

937 for name, kind, cls, value in classify_class_attrs(object) 

938 if visiblename(name, obj=object)] 

939 

940 mdict = {} 

941 for key, kind, homecls, value in attrs: 

942 mdict[key] = anchor = '#' + name + '-' + key 

943 try: 

944 value = getattr(object, name) 

945 except Exception: 

946 # Some descriptors may meet a failure in their __get__. 

947 # (bug #1785) 

948 pass 

949 try: 

950 # The value may not be hashable (e.g., a data attr with 

951 # a dict or list value). 

952 mdict[value] = anchor 

953 except TypeError: 

954 pass 

955 

956 while attrs: 

957 if mro: 

958 thisclass = mro.popleft() 

959 else: 

960 thisclass = attrs[0][2] 

961 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 

962 

963 if object is not builtins.object and thisclass is builtins.object: 

964 attrs = inherited 

965 continue 

966 elif thisclass is object: 

967 tag = 'defined here' 

968 else: 

969 tag = 'inherited from %s' % self.classlink(thisclass, 

970 object.__module__) 

971 tag += ':<br>\n' 

972 

973 sort_attributes(attrs, object) 

974 

975 # Pump out the attrs, segregated by kind. 

976 attrs = spill('Methods %s' % tag, attrs, 

977 lambda t: t[1] == 'method') 

978 attrs = spill('Class methods %s' % tag, attrs, 

979 lambda t: t[1] == 'class method') 

980 attrs = spill('Static methods %s' % tag, attrs, 

981 lambda t: t[1] == 'static method') 

982 attrs = spilldescriptors("Readonly properties %s" % tag, attrs, 

983 lambda t: t[1] == 'readonly property') 

984 attrs = spilldescriptors('Data descriptors %s' % tag, attrs, 

985 lambda t: t[1] == 'data descriptor') 

986 attrs = spilldata('Data and other attributes %s' % tag, attrs, 

987 lambda t: t[1] == 'data') 

988 assert attrs == [] 

989 attrs = inherited 

990 

991 contents = ''.join(contents) 

992 

993 if name == realname: 

994 title = '<a name="%s">class <strong>%s</strong></a>' % ( 

995 name, realname) 

996 else: 

997 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % ( 

998 name, name, realname) 

999 if bases: 

1000 parents = [] 

1001 for base in bases: 

1002 parents.append(self.classlink(base, object.__module__)) 

1003 title = title + '(%s)' % ', '.join(parents) 

1004 

1005 decl = '' 

1006 try: 

1007 signature = inspect.signature(object) 

1008 except (ValueError, TypeError): 

1009 signature = None 

1010 if signature: 

1011 argspec = str(signature) 

1012 if argspec and argspec != '()': 

1013 decl = name + self.escape(argspec) + '\n\n' 

1014 

1015 doc = getdoc(object) 

1016 if decl: 

1017 doc = decl + (doc or '') 

1018 doc = self.markup(doc, self.preformat, funcs, classes, mdict) 

1019 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc 

1020 

1021 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc) 

1022 

1023 def formatvalue(self, object): 

1024 """Format an argument default value as text.""" 

1025 return self.grey('=' + self.repr(object)) 

1026 

1027 def docroutine(self, object, name=None, mod=None, 

1028 funcs={}, classes={}, methods={}, cl=None): 

1029 """Produce HTML documentation for a function or method object.""" 

1030 realname = object.__name__ 

1031 name = name or realname 

1032 anchor = (cl and cl.__name__ or '') + '-' + name 

1033 note = '' 

1034 skipdocs = 0 

1035 if _is_bound_method(object): 

1036 imclass = object.__self__.__class__ 

1037 if cl: 

1038 if imclass is not cl: 

1039 note = ' from ' + self.classlink(imclass, mod) 

1040 else: 

1041 if object.__self__ is not None: 

1042 note = ' method of %s instance' % self.classlink( 

1043 object.__self__.__class__, mod) 

1044 else: 

1045 note = ' unbound %s method' % self.classlink(imclass,mod) 

1046 

1047 if (inspect.iscoroutinefunction(object) or 

1048 inspect.isasyncgenfunction(object)): 

1049 asyncqualifier = 'async ' 

1050 else: 

1051 asyncqualifier = '' 

1052 

1053 if name == realname: 

1054 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname) 

1055 else: 

1056 if cl and inspect.getattr_static(cl, realname, []) is object: 

1057 reallink = '<a href="#%s">%s</a>' % ( 

1058 cl.__name__ + '-' + realname, realname) 

1059 skipdocs = 1 

1060 else: 

1061 reallink = realname 

1062 title = '<a name="%s"><strong>%s</strong></a> = %s' % ( 

1063 anchor, name, reallink) 

1064 argspec = None 

1065 if inspect.isroutine(object): 

1066 try: 

1067 signature = inspect.signature(object) 

1068 except (ValueError, TypeError): 

1069 signature = None 

1070 if signature: 

1071 argspec = str(signature) 

1072 if realname == '<lambda>': 

1073 title = '<strong>%s</strong> <em>lambda</em> ' % name 

1074 # XXX lambda's won't usually have func_annotations['return'] 

1075 # since the syntax doesn't support but it is possible. 

1076 # So removing parentheses isn't truly safe. 

1077 argspec = argspec[1:-1] # remove parentheses 

1078 if not argspec: 

1079 argspec = '(...)' 

1080 

1081 decl = asyncqualifier + title + self.escape(argspec) + (note and 

1082 self.grey('<font face="helvetica, arial">%s</font>' % note)) 

1083 

1084 if skipdocs: 

1085 return '<dl><dt>%s</dt></dl>\n' % decl 

1086 else: 

1087 doc = self.markup( 

1088 getdoc(object), self.preformat, funcs, classes, methods) 

1089 doc = doc and '<dd><tt>%s</tt></dd>' % doc 

1090 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) 

1091 

1092 def docdata(self, object, name=None, mod=None, cl=None): 

1093 """Produce html documentation for a data descriptor.""" 

1094 results = [] 

1095 push = results.append 

1096 

1097 if name: 

1098 push('<dl><dt><strong>%s</strong></dt>\n' % name) 

1099 doc = self.markup(getdoc(object), self.preformat) 

1100 if doc: 

1101 push('<dd><tt>%s</tt></dd>\n' % doc) 

1102 push('</dl>\n') 

1103 

1104 return ''.join(results) 

1105 

1106 docproperty = docdata 

1107 

1108 def docother(self, object, name=None, mod=None, *ignored): 

1109 """Produce HTML documentation for a data object.""" 

1110 lhs = name and '<strong>%s</strong> = ' % name or '' 

1111 return lhs + self.repr(object) 

1112 

1113 def index(self, dir, shadowed=None): 

1114 """Generate an HTML index for a directory of modules.""" 

1115 modpkgs = [] 

1116 if shadowed is None: shadowed = {} 

1117 for importer, name, ispkg in pkgutil.iter_modules([dir]): 

1118 if any((0xD800 <= ord(ch) <= 0xDFFF) for ch in name): 

1119 # ignore a module if its name contains a surrogate character 

1120 continue 

1121 modpkgs.append((name, '', ispkg, name in shadowed)) 

1122 shadowed[name] = 1 

1123 

1124 modpkgs.sort() 

1125 contents = self.multicolumn(modpkgs, self.modpkglink) 

1126 return self.bigsection(dir, '#ffffff', '#ee77aa', contents) 

1127 

1128# -------------------------------------------- text documentation generator 

1129 

1130class TextRepr(Repr): 

1131 """Class for safely making a text representation of a Python object.""" 

1132 def __init__(self): 

1133 Repr.__init__(self) 

1134 self.maxlist = self.maxtuple = 20 

1135 self.maxdict = 10 

1136 self.maxstring = self.maxother = 100 

1137 

1138 def repr1(self, x, level): 

1139 if hasattr(type(x), '__name__'): 

1140 methodname = 'repr_' + '_'.join(type(x).__name__.split()) 

1141 if hasattr(self, methodname): 

1142 return getattr(self, methodname)(x, level) 

1143 return cram(stripid(repr(x)), self.maxother) 

1144 

1145 def repr_string(self, x, level): 

1146 test = cram(x, self.maxstring) 

1147 testrepr = repr(test) 

1148 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): 

1149 # Backslashes are only literal in the string and are never 

1150 # needed to make any special characters, so show a raw string. 

1151 return 'r' + testrepr[0] + test + testrepr[0] 

1152 return testrepr 

1153 

1154 repr_str = repr_string 

1155 

1156 def repr_instance(self, x, level): 

1157 try: 

1158 return cram(stripid(repr(x)), self.maxstring) 

1159 except: 

1160 return '<%s instance>' % x.__class__.__name__ 

1161 

1162class TextDoc(Doc): 

1163 """Formatter class for text documentation.""" 

1164 

1165 # ------------------------------------------- text formatting utilities 

1166 

1167 _repr_instance = TextRepr() 

1168 repr = _repr_instance.repr 

1169 

1170 def bold(self, text): 

1171 """Format a string in bold by overstriking.""" 

1172 return ''.join(ch + '\b' + ch for ch in text) 

1173 

1174 def indent(self, text, prefix=' '): 

1175 """Indent text by prepending a given prefix to each line.""" 

1176 if not text: return '' 

1177 lines = [prefix + line for line in text.split('\n')] 

1178 if lines: lines[-1] = lines[-1].rstrip() 

1179 return '\n'.join(lines) 

1180 

1181 def section(self, title, contents): 

1182 """Format a section with a given heading.""" 

1183 clean_contents = self.indent(contents).rstrip() 

1184 return self.bold(title) + '\n' + clean_contents + '\n\n' 

1185 

1186 # ---------------------------------------------- type-specific routines 

1187 

1188 def formattree(self, tree, modname, parent=None, prefix=''): 

1189 """Render in text a class tree as returned by inspect.getclasstree().""" 

1190 result = '' 

1191 for entry in tree: 

1192 if type(entry) is type(()): 

1193 c, bases = entry 

1194 result = result + prefix + classname(c, modname) 

1195 if bases and bases != (parent,): 

1196 parents = (classname(c, modname) for c in bases) 

1197 result = result + '(%s)' % ', '.join(parents) 

1198 result = result + '\n' 

1199 elif type(entry) is type([]): 

1200 result = result + self.formattree( 

1201 entry, modname, c, prefix + ' ') 

1202 return result 

1203 

1204 def docmodule(self, object, name=None, mod=None): 

1205 """Produce text documentation for a given module object.""" 

1206 name = object.__name__ # ignore the passed-in name 

1207 synop, desc = splitdoc(getdoc(object)) 

1208 result = self.section('NAME', name + (synop and ' - ' + synop)) 

1209 all = getattr(object, '__all__', None) 

1210 docloc = self.getdocloc(object) 

1211 if docloc is not None: 

1212 result = result + self.section('MODULE REFERENCE', docloc + """ 

1213 

1214The following documentation is automatically generated from the Python 

1215source files. It may be incomplete, incorrect or include features that 

1216are considered implementation detail and may vary between Python 

1217implementations. When in doubt, consult the module reference at the 

1218location listed above. 

1219""") 

1220 

1221 if desc: 

1222 result = result + self.section('DESCRIPTION', desc) 

1223 

1224 classes = [] 

1225 for key, value in inspect.getmembers(object, inspect.isclass): 

1226 # if __all__ exists, believe it. Otherwise use old heuristic. 

1227 if (all is not None 

1228 or (inspect.getmodule(value) or object) is object): 

1229 if visiblename(key, all, object): 

1230 classes.append((key, value)) 

1231 funcs = [] 

1232 for key, value in inspect.getmembers(object, inspect.isroutine): 

1233 # if __all__ exists, believe it. Otherwise use old heuristic. 

1234 if (all is not None or 

1235 inspect.isbuiltin(value) or inspect.getmodule(value) is object): 

1236 if visiblename(key, all, object): 

1237 funcs.append((key, value)) 

1238 data = [] 

1239 for key, value in inspect.getmembers(object, isdata): 

1240 if visiblename(key, all, object): 

1241 data.append((key, value)) 

1242 

1243 modpkgs = [] 

1244 modpkgs_names = set() 

1245 if hasattr(object, '__path__'): 

1246 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): 

1247 modpkgs_names.add(modname) 

1248 if ispkg: 

1249 modpkgs.append(modname + ' (package)') 

1250 else: 

1251 modpkgs.append(modname) 

1252 

1253 modpkgs.sort() 

1254 result = result + self.section( 

1255 'PACKAGE CONTENTS', '\n'.join(modpkgs)) 

1256 

1257 # Detect submodules as sometimes created by C extensions 

1258 submodules = [] 

1259 for key, value in inspect.getmembers(object, inspect.ismodule): 

1260 if value.__name__.startswith(name + '.') and key not in modpkgs_names: 

1261 submodules.append(key) 

1262 if submodules: 

1263 submodules.sort() 

1264 result = result + self.section( 

1265 'SUBMODULES', '\n'.join(submodules)) 

1266 

1267 if classes: 

1268 classlist = [value for key, value in classes] 

1269 contents = [self.formattree( 

1270 inspect.getclasstree(classlist, 1), name)] 

1271 for key, value in classes: 

1272 contents.append(self.document(value, key, name)) 

1273 result = result + self.section('CLASSES', '\n'.join(contents)) 

1274 

1275 if funcs: 

1276 contents = [] 

1277 for key, value in funcs: 

1278 contents.append(self.document(value, key, name)) 

1279 result = result + self.section('FUNCTIONS', '\n'.join(contents)) 

1280 

1281 if data: 

1282 contents = [] 

1283 for key, value in data: 

1284 contents.append(self.docother(value, key, name, maxlen=70)) 

1285 result = result + self.section('DATA', '\n'.join(contents)) 

1286 

1287 if hasattr(object, '__version__'): 

1288 version = str(object.__version__) 

1289 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': 

1290 version = version[11:-1].strip() 

1291 result = result + self.section('VERSION', version) 

1292 if hasattr(object, '__date__'): 

1293 result = result + self.section('DATE', str(object.__date__)) 

1294 if hasattr(object, '__author__'): 

1295 result = result + self.section('AUTHOR', str(object.__author__)) 

1296 if hasattr(object, '__credits__'): 

1297 result = result + self.section('CREDITS', str(object.__credits__)) 

1298 try: 

1299 file = inspect.getabsfile(object) 

1300 except TypeError: 

1301 file = '(built-in)' 

1302 result = result + self.section('FILE', file) 

1303 return result 

1304 

1305 def docclass(self, object, name=None, mod=None, *ignored): 

1306 """Produce text documentation for a given class object.""" 

1307 realname = object.__name__ 

1308 name = name or realname 

1309 bases = object.__bases__ 

1310 

1311 def makename(c, m=object.__module__): 

1312 return classname(c, m) 

1313 

1314 if name == realname: 

1315 title = 'class ' + self.bold(realname) 

1316 else: 

1317 title = self.bold(name) + ' = class ' + realname 

1318 if bases: 

1319 parents = map(makename, bases) 

1320 title = title + '(%s)' % ', '.join(parents) 

1321 

1322 contents = [] 

1323 push = contents.append 

1324 

1325 try: 

1326 signature = inspect.signature(object) 

1327 except (ValueError, TypeError): 

1328 signature = None 

1329 if signature: 

1330 argspec = str(signature) 

1331 if argspec and argspec != '()': 

1332 push(name + argspec + '\n') 

1333 

1334 doc = getdoc(object) 

1335 if doc: 

1336 push(doc + '\n') 

1337 

1338 # List the mro, if non-trivial. 

1339 mro = deque(inspect.getmro(object)) 

1340 if len(mro) > 2: 

1341 push("Method resolution order:") 

1342 for base in mro: 

1343 push(' ' + makename(base)) 

1344 push('') 

1345 

1346 # List the built-in subclasses, if any: 

1347 subclasses = sorted( 

1348 (str(cls.__name__) for cls in type.__subclasses__(object) 

1349 if not cls.__name__.startswith("_") and cls.__module__ == "builtins"), 

1350 key=str.lower 

1351 ) 

1352 no_of_subclasses = len(subclasses) 

1353 MAX_SUBCLASSES_TO_DISPLAY = 4 

1354 if subclasses: 

1355 push("Built-in subclasses:") 

1356 for subclassname in subclasses[:MAX_SUBCLASSES_TO_DISPLAY]: 

1357 push(' ' + subclassname) 

1358 if no_of_subclasses > MAX_SUBCLASSES_TO_DISPLAY: 

1359 push(' ... and ' + 

1360 str(no_of_subclasses - MAX_SUBCLASSES_TO_DISPLAY) + 

1361 ' other subclasses') 

1362 push('') 

1363 

1364 # Cute little class to pump out a horizontal rule between sections. 

1365 class HorizontalRule: 

1366 def __init__(self): 

1367 self.needone = 0 

1368 def maybe(self): 

1369 if self.needone: 

1370 push('-' * 70) 

1371 self.needone = 1 

1372 hr = HorizontalRule() 

1373 

1374 def spill(msg, attrs, predicate): 

1375 ok, attrs = _split_list(attrs, predicate) 

1376 if ok: 

1377 hr.maybe() 

1378 push(msg) 

1379 for name, kind, homecls, value in ok: 

1380 try: 

1381 value = getattr(object, name) 

1382 except Exception: 

1383 # Some descriptors may meet a failure in their __get__. 

1384 # (bug #1785) 

1385 push(self.docdata(value, name, mod)) 

1386 else: 

1387 push(self.document(value, 

1388 name, mod, object)) 

1389 return attrs 

1390 

1391 def spilldescriptors(msg, attrs, predicate): 

1392 ok, attrs = _split_list(attrs, predicate) 

1393 if ok: 

1394 hr.maybe() 

1395 push(msg) 

1396 for name, kind, homecls, value in ok: 

1397 push(self.docdata(value, name, mod)) 

1398 return attrs 

1399 

1400 def spilldata(msg, attrs, predicate): 

1401 ok, attrs = _split_list(attrs, predicate) 

1402 if ok: 

1403 hr.maybe() 

1404 push(msg) 

1405 for name, kind, homecls, value in ok: 

1406 doc = getdoc(value) 

1407 try: 

1408 obj = getattr(object, name) 

1409 except AttributeError: 

1410 obj = homecls.__dict__[name] 

1411 push(self.docother(obj, name, mod, maxlen=70, doc=doc) + 

1412 '\n') 

1413 return attrs 

1414 

1415 attrs = [(name, kind, cls, value) 

1416 for name, kind, cls, value in classify_class_attrs(object) 

1417 if visiblename(name, obj=object)] 

1418 

1419 while attrs: 

1420 if mro: 

1421 thisclass = mro.popleft() 

1422 else: 

1423 thisclass = attrs[0][2] 

1424 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 

1425 

1426 if object is not builtins.object and thisclass is builtins.object: 

1427 attrs = inherited 

1428 continue 

1429 elif thisclass is object: 

1430 tag = "defined here" 

1431 else: 

1432 tag = "inherited from %s" % classname(thisclass, 

1433 object.__module__) 

1434 

1435 sort_attributes(attrs, object) 

1436 

1437 # Pump out the attrs, segregated by kind. 

1438 attrs = spill("Methods %s:\n" % tag, attrs, 

1439 lambda t: t[1] == 'method') 

1440 attrs = spill("Class methods %s:\n" % tag, attrs, 

1441 lambda t: t[1] == 'class method') 

1442 attrs = spill("Static methods %s:\n" % tag, attrs, 

1443 lambda t: t[1] == 'static method') 

1444 attrs = spilldescriptors("Readonly properties %s:\n" % tag, attrs, 

1445 lambda t: t[1] == 'readonly property') 

1446 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs, 

1447 lambda t: t[1] == 'data descriptor') 

1448 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs, 

1449 lambda t: t[1] == 'data') 

1450 

1451 assert attrs == [] 

1452 attrs = inherited 

1453 

1454 contents = '\n'.join(contents) 

1455 if not contents: 

1456 return title + '\n' 

1457 return title + '\n' + self.indent(contents.rstrip(), ' | ') + '\n' 

1458 

1459 def formatvalue(self, object): 

1460 """Format an argument default value as text.""" 

1461 return '=' + self.repr(object) 

1462 

1463 def docroutine(self, object, name=None, mod=None, cl=None): 

1464 """Produce text documentation for a function or method object.""" 

1465 realname = object.__name__ 

1466 name = name or realname 

1467 note = '' 

1468 skipdocs = 0 

1469 if _is_bound_method(object): 

1470 imclass = object.__self__.__class__ 

1471 if cl: 

1472 if imclass is not cl: 

1473 note = ' from ' + classname(imclass, mod) 

1474 else: 

1475 if object.__self__ is not None: 

1476 note = ' method of %s instance' % classname( 

1477 object.__self__.__class__, mod) 

1478 else: 

1479 note = ' unbound %s method' % classname(imclass,mod) 

1480 

1481 if (inspect.iscoroutinefunction(object) or 

1482 inspect.isasyncgenfunction(object)): 

1483 asyncqualifier = 'async ' 

1484 else: 

1485 asyncqualifier = '' 

1486 

1487 if name == realname: 

1488 title = self.bold(realname) 

1489 else: 

1490 if cl and inspect.getattr_static(cl, realname, []) is object: 

1491 skipdocs = 1 

1492 title = self.bold(name) + ' = ' + realname 

1493 argspec = None 

1494 

1495 if inspect.isroutine(object): 

1496 try: 

1497 signature = inspect.signature(object) 

1498 except (ValueError, TypeError): 

1499 signature = None 

1500 if signature: 

1501 argspec = str(signature) 

1502 if realname == '<lambda>': 

1503 title = self.bold(name) + ' lambda ' 

1504 # XXX lambda's won't usually have func_annotations['return'] 

1505 # since the syntax doesn't support but it is possible. 

1506 # So removing parentheses isn't truly safe. 

1507 argspec = argspec[1:-1] # remove parentheses 

1508 if not argspec: 

1509 argspec = '(...)' 

1510 decl = asyncqualifier + title + argspec + note 

1511 

1512 if skipdocs: 

1513 return decl + '\n' 

1514 else: 

1515 doc = getdoc(object) or '' 

1516 return decl + '\n' + (doc and self.indent(doc).rstrip() + '\n') 

1517 

1518 def docdata(self, object, name=None, mod=None, cl=None): 

1519 """Produce text documentation for a data descriptor.""" 

1520 results = [] 

1521 push = results.append 

1522 

1523 if name: 

1524 push(self.bold(name)) 

1525 push('\n') 

1526 doc = getdoc(object) or '' 

1527 if doc: 

1528 push(self.indent(doc)) 

1529 push('\n') 

1530 return ''.join(results) 

1531 

1532 docproperty = docdata 

1533 

1534 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None): 

1535 """Produce text documentation for a data object.""" 

1536 repr = self.repr(object) 

1537 if maxlen: 

1538 line = (name and name + ' = ' or '') + repr 

1539 chop = maxlen - len(line) 

1540 if chop < 0: repr = repr[:chop] + '...' 

1541 line = (name and self.bold(name) + ' = ' or '') + repr 

1542 if not doc: 

1543 doc = getdoc(object) 

1544 if doc: 

1545 line += '\n' + self.indent(str(doc)) + '\n' 

1546 return line 

1547 

1548class _PlainTextDoc(TextDoc): 

1549 """Subclass of TextDoc which overrides string styling""" 

1550 def bold(self, text): 

1551 return text 

1552 

1553# --------------------------------------------------------- user interfaces 

1554 

1555def pager(text): 

1556 """The first time this is called, determine what kind of pager to use.""" 

1557 global pager 

1558 pager = getpager() 

1559 pager(text) 

1560 

1561def getpager(): 

1562 """Decide what method to use for paging through text.""" 

1563 if not hasattr(sys.stdin, "isatty"): 

1564 return plainpager 

1565 if not hasattr(sys.stdout, "isatty"): 

1566 return plainpager 

1567 if not sys.stdin.isatty() or not sys.stdout.isatty(): 

1568 return plainpager 

1569 use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER') 

1570 if use_pager: 

1571 if sys.platform == 'win32': # pipes completely broken in Windows 

1572 return lambda text: tempfilepager(plain(text), use_pager) 

1573 elif os.environ.get('TERM') in ('dumb', 'emacs'): 

1574 return lambda text: pipepager(plain(text), use_pager) 

1575 else: 

1576 return lambda text: pipepager(text, use_pager) 

1577 if os.environ.get('TERM') in ('dumb', 'emacs'): 

1578 return plainpager 

1579 if sys.platform == 'win32': 

1580 return lambda text: tempfilepager(plain(text), 'more <') 

1581 if hasattr(os, 'system') and os.system('(pager) 2>/dev/null') == 0: 

1582 return lambda text: pipepager(text, 'pager') 

1583 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: 

1584 return lambda text: pipepager(text, 'less') 

1585 

1586 import tempfile 

1587 (fd, filename) = tempfile.mkstemp() 

1588 os.close(fd) 

1589 try: 

1590 if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: 

1591 return lambda text: pipepager(text, 'more') 

1592 else: 

1593 return ttypager 

1594 finally: 

1595 os.unlink(filename) 

1596 

1597def plain(text): 

1598 """Remove boldface formatting from text.""" 

1599 return re.sub('.\b', '', text) 

1600 

1601def pipepager(text, cmd): 

1602 """Page through text by feeding it to another program.""" 

1603 import subprocess 

1604 proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE) 

1605 try: 

1606 with io.TextIOWrapper(proc.stdin, errors='backslashreplace') as pipe: 

1607 try: 

1608 pipe.write(text) 

1609 except KeyboardInterrupt: 

1610 # We've hereby abandoned whatever text hasn't been written, 

1611 # but the pager is still in control of the terminal. 

1612 pass 

1613 except OSError: 

1614 pass # Ignore broken pipes caused by quitting the pager program. 

1615 while True: 

1616 try: 

1617 proc.wait() 

1618 break 

1619 except KeyboardInterrupt: 

1620 # Ignore ctl-c like the pager itself does. Otherwise the pager is 

1621 # left running and the terminal is in raw mode and unusable. 

1622 pass 

1623 

1624def tempfilepager(text, cmd): 

1625 """Page through text by invoking a program on a temporary file.""" 

1626 import tempfile 

1627 filename = tempfile.mktemp() 

1628 with open(filename, 'w', errors='backslashreplace') as file: 

1629 file.write(text) 

1630 try: 

1631 os.system(cmd + ' "' + filename + '"') 

1632 finally: 

1633 os.unlink(filename) 

1634 

1635def _escape_stdout(text): 

1636 # Escape non-encodable characters to avoid encoding errors later 

1637 encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8' 

1638 return text.encode(encoding, 'backslashreplace').decode(encoding) 

1639 

1640def ttypager(text): 

1641 """Page through text on a text terminal.""" 

1642 lines = plain(_escape_stdout(text)).split('\n') 

1643 try: 

1644 import tty 

1645 fd = sys.stdin.fileno() 

1646 old = tty.tcgetattr(fd) 

1647 tty.setcbreak(fd) 

1648 getchar = lambda: sys.stdin.read(1) 

1649 except (ImportError, AttributeError, io.UnsupportedOperation): 

1650 tty = None 

1651 getchar = lambda: sys.stdin.readline()[:-1][:1] 

1652 

1653 try: 

1654 try: 

1655 h = int(os.environ.get('LINES', 0)) 

1656 except ValueError: 

1657 h = 0 

1658 if h <= 1: 

1659 h = 25 

1660 r = inc = h - 1 

1661 sys.stdout.write('\n'.join(lines[:inc]) + '\n') 

1662 while lines[r:]: 

1663 sys.stdout.write('-- more --') 

1664 sys.stdout.flush() 

1665 c = getchar() 

1666 

1667 if c in ('q', 'Q'): 

1668 sys.stdout.write('\r \r') 

1669 break 

1670 elif c in ('\r', '\n'): 

1671 sys.stdout.write('\r \r' + lines[r] + '\n') 

1672 r = r + 1 

1673 continue 

1674 if c in ('b', 'B', '\x1b'): 

1675 r = r - inc - inc 

1676 if r < 0: r = 0 

1677 sys.stdout.write('\n' + '\n'.join(lines[r:r+inc]) + '\n') 

1678 r = r + inc 

1679 

1680 finally: 

1681 if tty: 

1682 tty.tcsetattr(fd, tty.TCSAFLUSH, old) 

1683 

1684def plainpager(text): 

1685 """Simply print unformatted text. This is the ultimate fallback.""" 

1686 sys.stdout.write(plain(_escape_stdout(text))) 

1687 

1688def describe(thing): 

1689 """Produce a short description of the given thing.""" 

1690 if inspect.ismodule(thing): 

1691 if thing.__name__ in sys.builtin_module_names: 

1692 return 'built-in module ' + thing.__name__ 

1693 if hasattr(thing, '__path__'): 

1694 return 'package ' + thing.__name__ 

1695 else: 

1696 return 'module ' + thing.__name__ 

1697 if inspect.isbuiltin(thing): 

1698 return 'built-in function ' + thing.__name__ 

1699 if inspect.isgetsetdescriptor(thing): 

1700 return 'getset descriptor %s.%s.%s' % ( 

1701 thing.__objclass__.__module__, thing.__objclass__.__name__, 

1702 thing.__name__) 

1703 if inspect.ismemberdescriptor(thing): 

1704 return 'member descriptor %s.%s.%s' % ( 

1705 thing.__objclass__.__module__, thing.__objclass__.__name__, 

1706 thing.__name__) 

1707 if inspect.isclass(thing): 

1708 return 'class ' + thing.__name__ 

1709 if inspect.isfunction(thing): 

1710 return 'function ' + thing.__name__ 

1711 if inspect.ismethod(thing): 

1712 return 'method ' + thing.__name__ 

1713 return type(thing).__name__ 

1714 

1715def locate(path, forceload=0): 

1716 """Locate an object by name or dotted path, importing as necessary.""" 

1717 parts = [part for part in path.split('.') if part] 

1718 module, n = None, 0 

1719 while n < len(parts): 

1720 nextmodule = safeimport('.'.join(parts[:n+1]), forceload) 

1721 if nextmodule: module, n = nextmodule, n + 1 

1722 else: break 

1723 if module: 

1724 object = module 

1725 else: 

1726 object = builtins 

1727 for part in parts[n:]: 

1728 try: 

1729 object = getattr(object, part) 

1730 except AttributeError: 

1731 return None 

1732 return object 

1733 

1734# --------------------------------------- interactive interpreter interface 

1735 

1736text = TextDoc() 

1737plaintext = _PlainTextDoc() 

1738html = HTMLDoc() 

1739 

1740def resolve(thing, forceload=0): 

1741 """Given an object or a path to an object, get the object and its name.""" 

1742 if isinstance(thing, str): 

1743 object = locate(thing, forceload) 

1744 if object is None: 

1745 raise ImportError('''\ 

1746No Python documentation found for %r. 

1747Use help() to get the interactive help utility. 

1748Use help(str) for help on the str class.''' % thing) 

1749 return object, thing 

1750 else: 

1751 name = getattr(thing, '__name__', None) 

1752 return thing, name if isinstance(name, str) else None 

1753 

1754def render_doc(thing, title='Python Library Documentation: %s', forceload=0, 

1755 renderer=None): 

1756 """Render text documentation, given an object or a path to an object.""" 

1757 if renderer is None: 

1758 renderer = text 

1759 object, name = resolve(thing, forceload) 

1760 desc = describe(object) 

1761 module = inspect.getmodule(object) 

1762 if name and '.' in name: 

1763 desc += ' in ' + name[:name.rfind('.')] 

1764 elif module and module is not object: 

1765 desc += ' in module ' + module.__name__ 

1766 

1767 if not (inspect.ismodule(object) or 

1768 inspect.isclass(object) or 

1769 inspect.isroutine(object) or 

1770 inspect.isdatadescriptor(object) or 

1771 _getdoc(object)): 

1772 # If the passed object is a piece of data or an instance, 

1773 # document its available methods instead of its value. 

1774 if hasattr(object, '__origin__'): 

1775 object = object.__origin__ 

1776 else: 

1777 object = type(object) 

1778 desc += ' object' 

1779 return title % desc + '\n\n' + renderer.document(object, name) 

1780 

1781def doc(thing, title='Python Library Documentation: %s', forceload=0, 

1782 output=None): 

1783 """Display text documentation, given an object or a path to an object.""" 

1784 try: 

1785 if output is None: 

1786 pager(render_doc(thing, title, forceload)) 

1787 else: 

1788 output.write(render_doc(thing, title, forceload, plaintext)) 

1789 except (ImportError, ErrorDuringImport) as value: 

1790 print(value) 

1791 

1792def writedoc(thing, forceload=0): 

1793 """Write HTML documentation to a file in the current directory.""" 

1794 try: 

1795 object, name = resolve(thing, forceload) 

1796 page = html.page(describe(object), html.document(object, name)) 

1797 with open(name + '.html', 'w', encoding='utf-8') as file: 

1798 file.write(page) 

1799 print('wrote', name + '.html') 

1800 except (ImportError, ErrorDuringImport) as value: 

1801 print(value) 

1802 

1803def writedocs(dir, pkgpath='', done=None): 

1804 """Write out HTML documentation for all modules in a directory tree.""" 

1805 if done is None: done = {} 

1806 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): 

1807 writedoc(modname) 

1808 return 

1809 

1810class Helper: 

1811 

1812 # These dictionaries map a topic name to either an alias, or a tuple 

1813 # (label, seealso-items). The "label" is the label of the corresponding 

1814 # section in the .rst file under Doc/ and an index into the dictionary 

1815 # in pydoc_data/topics.py. 

1816 # 

1817 # CAUTION: if you change one of these dictionaries, be sure to adapt the 

1818 # list of needed labels in Doc/tools/extensions/pyspecific.py and 

1819 # regenerate the pydoc_data/topics.py file by running 

1820 # make pydoc-topics 

1821 # in Doc/ and copying the output file into the Lib/ directory. 

1822 

1823 keywords = { 

1824 'False': '', 

1825 'None': '', 

1826 'True': '', 

1827 '__peg_parser__': '', 

1828 'and': 'BOOLEAN', 

1829 'as': 'with', 

1830 'assert': ('assert', ''), 

1831 'async': ('async', ''), 

1832 'await': ('await', ''), 

1833 'break': ('break', 'while for'), 

1834 'class': ('class', 'CLASSES SPECIALMETHODS'), 

1835 'continue': ('continue', 'while for'), 

1836 'def': ('function', ''), 

1837 'del': ('del', 'BASICMETHODS'), 

1838 'elif': 'if', 

1839 'else': ('else', 'while for'), 

1840 'except': 'try', 

1841 'finally': 'try', 

1842 'for': ('for', 'break continue while'), 

1843 'from': 'import', 

1844 'global': ('global', 'nonlocal NAMESPACES'), 

1845 'if': ('if', 'TRUTHVALUE'), 

1846 'import': ('import', 'MODULES'), 

1847 'in': ('in', 'SEQUENCEMETHODS'), 

1848 'is': 'COMPARISON', 

1849 'lambda': ('lambda', 'FUNCTIONS'), 

1850 'nonlocal': ('nonlocal', 'global NAMESPACES'), 

1851 'not': 'BOOLEAN', 

1852 'or': 'BOOLEAN', 

1853 'pass': ('pass', ''), 

1854 'raise': ('raise', 'EXCEPTIONS'), 

1855 'return': ('return', 'FUNCTIONS'), 

1856 'try': ('try', 'EXCEPTIONS'), 

1857 'while': ('while', 'break continue if TRUTHVALUE'), 

1858 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 

1859 'yield': ('yield', ''), 

1860 } 

1861 # Either add symbols to this dictionary or to the symbols dictionary 

1862 # directly: Whichever is easier. They are merged later. 

1863 _strprefixes = [p + q for p in ('b', 'f', 'r', 'u') for q in ("'", '"')] 

1864 _symbols_inverse = { 

1865 'STRINGS' : ("'", "'''", '"', '"""', *_strprefixes), 

1866 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', 

1867 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), 

1868 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), 

1869 'UNARY' : ('-', '~'), 

1870 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', 

1871 '^=', '<<=', '>>=', '**=', '//='), 

1872 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), 

1873 'COMPLEX' : ('j', 'J') 

1874 } 

1875 symbols = { 

1876 '%': 'OPERATORS FORMATTING', 

1877 '**': 'POWER', 

1878 ',': 'TUPLES LISTS FUNCTIONS', 

1879 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', 

1880 '...': 'ELLIPSIS', 

1881 ':': 'SLICINGS DICTIONARYLITERALS', 

1882 '@': 'def class', 

1883 '\\': 'STRINGS', 

1884 '_': 'PRIVATENAMES', 

1885 '__': 'PRIVATENAMES SPECIALMETHODS', 

1886 '`': 'BACKQUOTES', 

1887 '(': 'TUPLES FUNCTIONS CALLS', 

1888 ')': 'TUPLES FUNCTIONS CALLS', 

1889 '[': 'LISTS SUBSCRIPTS SLICINGS', 

1890 ']': 'LISTS SUBSCRIPTS SLICINGS' 

1891 } 

1892 for topic, symbols_ in _symbols_inverse.items(): 

1893 for symbol in symbols_: 

1894 topics = symbols.get(symbol, topic) 

1895 if topic not in topics: 

1896 topics = topics + ' ' + topic 

1897 symbols[symbol] = topics 

1898 

1899 topics = { 

1900 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' 

1901 'FUNCTIONS CLASSES MODULES FILES inspect'), 

1902 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS ' 

1903 'FORMATTING TYPES'), 

1904 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), 

1905 'FORMATTING': ('formatstrings', 'OPERATORS'), 

1906 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' 

1907 'FORMATTING TYPES'), 

1908 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), 

1909 'INTEGER': ('integers', 'int range'), 

1910 'FLOAT': ('floating', 'float math'), 

1911 'COMPLEX': ('imaginary', 'complex cmath'), 

1912 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING range LISTS'), 

1913 'MAPPINGS': 'DICTIONARIES', 

1914 'FUNCTIONS': ('typesfunctions', 'def TYPES'), 

1915 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), 

1916 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), 

1917 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), 

1918 'FRAMEOBJECTS': 'TYPES', 

1919 'TRACEBACKS': 'TYPES', 

1920 'NONE': ('bltin-null-object', ''), 

1921 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), 

1922 'SPECIALATTRIBUTES': ('specialattrs', ''), 

1923 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), 

1924 'MODULES': ('typesmodules', 'import'), 

1925 'PACKAGES': 'import', 

1926 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' 

1927 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' 

1928 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' 

1929 'LISTS DICTIONARIES'), 

1930 'OPERATORS': 'EXPRESSIONS', 

1931 'PRECEDENCE': 'EXPRESSIONS', 

1932 'OBJECTS': ('objects', 'TYPES'), 

1933 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' 

1934 'CALLABLEMETHODS SEQUENCEMETHODS MAPPINGMETHODS ' 

1935 'NUMBERMETHODS CLASSES'), 

1936 'BASICMETHODS': ('customization', 'hash repr str SPECIALMETHODS'), 

1937 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 

1938 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), 

1939 'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS ' 

1940 'SPECIALMETHODS'), 

1941 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 

1942 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' 

1943 'SPECIALMETHODS'), 

1944 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 

1945 'NAMESPACES': ('naming', 'global nonlocal ASSIGNMENT DELETION DYNAMICFEATURES'), 

1946 'DYNAMICFEATURES': ('dynamic-features', ''), 

1947 'SCOPING': 'NAMESPACES', 

1948 'FRAMES': 'NAMESPACES', 

1949 'EXCEPTIONS': ('exceptions', 'try except finally raise'), 

1950 'CONVERSIONS': ('conversions', ''), 

1951 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), 

1952 'SPECIALIDENTIFIERS': ('id-classes', ''), 

1953 'PRIVATENAMES': ('atom-identifiers', ''), 

1954 'LITERALS': ('atom-literals', 'STRINGS NUMBERS TUPLELITERALS ' 

1955 'LISTLITERALS DICTIONARYLITERALS'), 

1956 'TUPLES': 'SEQUENCES', 

1957 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), 

1958 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), 

1959 'LISTLITERALS': ('lists', 'LISTS LITERALS'), 

1960 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 

1961 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 

1962 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), 

1963 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS'), 

1964 'SLICINGS': ('slicings', 'SEQUENCEMETHODS'), 

1965 'CALLS': ('calls', 'EXPRESSIONS'), 

1966 'POWER': ('power', 'EXPRESSIONS'), 

1967 'UNARY': ('unary', 'EXPRESSIONS'), 

1968 'BINARY': ('binary', 'EXPRESSIONS'), 

1969 'SHIFTING': ('shifting', 'EXPRESSIONS'), 

1970 'BITWISE': ('bitwise', 'EXPRESSIONS'), 

1971 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), 

1972 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), 

1973 'ASSERTION': 'assert', 

1974 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), 

1975 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), 

1976 'DELETION': 'del', 

1977 'RETURNING': 'return', 

1978 'IMPORTING': 'import', 

1979 'CONDITIONAL': 'if', 

1980 'LOOPING': ('compound', 'for while break continue'), 

1981 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), 

1982 'DEBUGGING': ('debugger', 'pdb'), 

1983 'CONTEXTMANAGERS': ('context-managers', 'with'), 

1984 } 

1985 

1986 def __init__(self, input=None, output=None): 

1987 self._input = input 

1988 self._output = output 

1989 

1990 @property 

1991 def input(self): 

1992 return self._input or sys.stdin 

1993 

1994 @property 

1995 def output(self): 

1996 return self._output or sys.stdout 

1997 

1998 def __repr__(self): 

1999 if inspect.stack()[1][3] == '?': 

2000 self() 

2001 return '' 

2002 return '<%s.%s instance>' % (self.__class__.__module__, 

2003 self.__class__.__qualname__) 

2004 

2005 _GoInteractive = object() 

2006 def __call__(self, request=_GoInteractive): 

2007 if request is not self._GoInteractive: 

2008 self.help(request) 

2009 else: 

2010 self.intro() 

2011 self.interact() 

2012 self.output.write(''' 

2013You are now leaving help and returning to the Python interpreter. 

2014If you want to ask for help on a particular object directly from the 

2015interpreter, you can type "help(object)". Executing "help('string')" 

2016has the same effect as typing a particular string at the help> prompt. 

2017''') 

2018 

2019 def interact(self): 

2020 self.output.write('\n') 

2021 while True: 

2022 try: 

2023 request = self.getline('help> ') 

2024 if not request: break 

2025 except (KeyboardInterrupt, EOFError): 

2026 break 

2027 request = request.strip() 

2028 

2029 # Make sure significant trailing quoting marks of literals don't 

2030 # get deleted while cleaning input 

2031 if (len(request) > 2 and request[0] == request[-1] in ("'", '"') 

2032 and request[0] not in request[1:-1]): 

2033 request = request[1:-1] 

2034 if request.lower() in ('q', 'quit'): break 

2035 if request == 'help': 

2036 self.intro() 

2037 else: 

2038 self.help(request) 

2039 

2040 def getline(self, prompt): 

2041 """Read one line, using input() when appropriate.""" 

2042 if self.input is sys.stdin: 

2043 return input(prompt) 

2044 else: 

2045 self.output.write(prompt) 

2046 self.output.flush() 

2047 return self.input.readline() 

2048 

2049 def help(self, request): 

2050 if type(request) is type(''): 

2051 request = request.strip() 

2052 if request == 'keywords': self.listkeywords() 

2053 elif request == 'symbols': self.listsymbols() 

2054 elif request == 'topics': self.listtopics() 

2055 elif request == 'modules': self.listmodules() 

2056 elif request[:8] == 'modules ': 

2057 self.listmodules(request.split()[1]) 

2058 elif request in self.symbols: self.showsymbol(request) 

2059 elif request in ['True', 'False', 'None']: 

2060 # special case these keywords since they are objects too 

2061 doc(eval(request), 'Help on %s:') 

2062 elif request in self.keywords: self.showtopic(request) 

2063 elif request in self.topics: self.showtopic(request) 

2064 elif request: doc(request, 'Help on %s:', output=self._output) 

2065 else: doc(str, 'Help on %s:', output=self._output) 

2066 elif isinstance(request, Helper): self() 

2067 else: doc(request, 'Help on %s:', output=self._output) 

2068 self.output.write('\n') 

2069 

2070 def intro(self): 

2071 self.output.write(''' 

2072Welcome to Python {0}'s help utility! 

2073 

2074If this is your first time using Python, you should definitely check out 

2075the tutorial on the Internet at https://docs.python.org/{0}/tutorial/. 

2076 

2077Enter the name of any module, keyword, or topic to get help on writing 

2078Python programs and using Python modules. To quit this help utility and 

2079return to the interpreter, just type "quit". 

2080 

2081To get a list of available modules, keywords, symbols, or topics, type 

2082"modules", "keywords", "symbols", or "topics". Each module also comes 

2083with a one-line summary of what it does; to list the modules whose name 

2084or summary contain a given string such as "spam", type "modules spam". 

2085'''.format('%d.%d' % sys.version_info[:2])) 

2086 

2087 def list(self, items, columns=4, width=80): 

2088 items = list(sorted(items)) 

2089 colw = width // columns 

2090 rows = (len(items) + columns - 1) // columns 

2091 for row in range(rows): 

2092 for col in range(columns): 

2093 i = col * rows + row 

2094 if i < len(items): 

2095 self.output.write(items[i]) 

2096 if col < columns - 1: 

2097 self.output.write(' ' + ' ' * (colw - 1 - len(items[i]))) 

2098 self.output.write('\n') 

2099 

2100 def listkeywords(self): 

2101 self.output.write(''' 

2102Here is a list of the Python keywords. Enter any keyword to get more help. 

2103 

2104''') 

2105 self.list(self.keywords.keys()) 

2106 

2107 def listsymbols(self): 

2108 self.output.write(''' 

2109Here is a list of the punctuation symbols which Python assigns special meaning 

2110to. Enter any symbol to get more help. 

2111 

2112''') 

2113 self.list(self.symbols.keys()) 

2114 

2115 def listtopics(self): 

2116 self.output.write(''' 

2117Here is a list of available topics. Enter any topic name to get more help. 

2118 

2119''') 

2120 self.list(self.topics.keys()) 

2121 

2122 def showtopic(self, topic, more_xrefs=''): 

2123 try: 

2124 import pydoc_data.topics 

2125 except ImportError: 

2126 self.output.write(''' 

2127Sorry, topic and keyword documentation is not available because the 

2128module "pydoc_data.topics" could not be found. 

2129''') 

2130 return 

2131 target = self.topics.get(topic, self.keywords.get(topic)) 

2132 if not target: 

2133 self.output.write('no documentation found for %s\n' % repr(topic)) 

2134 return 

2135 if type(target) is type(''): 

2136 return self.showtopic(target, more_xrefs) 

2137 

2138 label, xrefs = target 

2139 try: 

2140 doc = pydoc_data.topics.topics[label] 

2141 except KeyError: 

2142 self.output.write('no documentation found for %s\n' % repr(topic)) 

2143 return 

2144 doc = doc.strip() + '\n' 

2145 if more_xrefs: 

2146 xrefs = (xrefs or '') + ' ' + more_xrefs 

2147 if xrefs: 

2148 import textwrap 

2149 text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n' 

2150 wrapped_text = textwrap.wrap(text, 72) 

2151 doc += '\n%s\n' % '\n'.join(wrapped_text) 

2152 pager(doc) 

2153 

2154 def _gettopic(self, topic, more_xrefs=''): 

2155 """Return unbuffered tuple of (topic, xrefs). 

2156 

2157 If an error occurs here, the exception is caught and displayed by 

2158 the url handler. 

2159 

2160 This function duplicates the showtopic method but returns its 

2161 result directly so it can be formatted for display in an html page. 

2162 """ 

2163 try: 

2164 import pydoc_data.topics 

2165 except ImportError: 

2166 return(''' 

2167Sorry, topic and keyword documentation is not available because the 

2168module "pydoc_data.topics" could not be found. 

2169''' , '') 

2170 target = self.topics.get(topic, self.keywords.get(topic)) 

2171 if not target: 

2172 raise ValueError('could not find topic') 

2173 if isinstance(target, str): 

2174 return self._gettopic(target, more_xrefs) 

2175 label, xrefs = target 

2176 doc = pydoc_data.topics.topics[label] 

2177 if more_xrefs: 

2178 xrefs = (xrefs or '') + ' ' + more_xrefs 

2179 return doc, xrefs 

2180 

2181 def showsymbol(self, symbol): 

2182 target = self.symbols[symbol] 

2183 topic, _, xrefs = target.partition(' ') 

2184 self.showtopic(topic, xrefs) 

2185 

2186 def listmodules(self, key=''): 

2187 if key: 

2188 self.output.write(''' 

2189Here is a list of modules whose name or summary contains '{}'. 

2190If there are any, enter a module name to get more help. 

2191 

2192'''.format(key)) 

2193 apropos(key) 

2194 else: 

2195 self.output.write(''' 

2196Please wait a moment while I gather a list of all available modules... 

2197 

2198''') 

2199 modules = {} 

2200 def callback(path, modname, desc, modules=modules): 

2201 if modname and modname[-9:] == '.__init__': 

2202 modname = modname[:-9] + ' (package)' 

2203 if modname.find('.') < 0: 

2204 modules[modname] = 1 

2205 def onerror(modname): 

2206 callback(None, modname, None) 

2207 ModuleScanner().run(callback, onerror=onerror) 

2208 self.list(modules.keys()) 

2209 self.output.write(''' 

2210Enter any module name to get more help. Or, type "modules spam" to search 

2211for modules whose name or summary contain the string "spam". 

2212''') 

2213 

2214help = Helper() 

2215 

2216class ModuleScanner: 

2217 """An interruptible scanner that searches module synopses.""" 

2218 

2219 def run(self, callback, key=None, completer=None, onerror=None): 

2220 if key: key = key.lower() 

2221 self.quit = False 

2222 seen = {} 

2223 

2224 for modname in sys.builtin_module_names: 

2225 if modname != '__main__': 

2226 seen[modname] = 1 

2227 if key is None: 

2228 callback(None, modname, '') 

2229 else: 

2230 name = __import__(modname).__doc__ or '' 

2231 desc = name.split('\n')[0] 

2232 name = modname + ' - ' + desc 

2233 if name.lower().find(key) >= 0: 

2234 callback(None, modname, desc) 

2235 

2236 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): 

2237 if self.quit: 

2238 break 

2239 

2240 if key is None: 

2241 callback(None, modname, '') 

2242 else: 

2243 try: 

2244 spec = pkgutil._get_spec(importer, modname) 

2245 except SyntaxError: 

2246 # raised by tests for bad coding cookies or BOM 

2247 continue 

2248 loader = spec.loader 

2249 if hasattr(loader, 'get_source'): 

2250 try: 

2251 source = loader.get_source(modname) 

2252 except Exception: 

2253 if onerror: 

2254 onerror(modname) 

2255 continue 

2256 desc = source_synopsis(io.StringIO(source)) or '' 

2257 if hasattr(loader, 'get_filename'): 

2258 path = loader.get_filename(modname) 

2259 else: 

2260 path = None 

2261 else: 

2262 try: 

2263 module = importlib._bootstrap._load(spec) 

2264 except ImportError: 

2265 if onerror: 

2266 onerror(modname) 

2267 continue 

2268 desc = module.__doc__.splitlines()[0] if module.__doc__ else '' 

2269 path = getattr(module,'__file__',None) 

2270 name = modname + ' - ' + desc 

2271 if name.lower().find(key) >= 0: 

2272 callback(path, modname, desc) 

2273 

2274 if completer: 

2275 completer() 

2276 

2277def apropos(key): 

2278 """Print all the one-line module summaries that contain a substring.""" 

2279 def callback(path, modname, desc): 

2280 if modname[-9:] == '.__init__': 

2281 modname = modname[:-9] + ' (package)' 

2282 print(modname, desc and '- ' + desc) 

2283 def onerror(modname): 

2284 pass 

2285 with warnings.catch_warnings(): 

2286 warnings.filterwarnings('ignore') # ignore problems during import 

2287 ModuleScanner().run(callback, key, onerror=onerror) 

2288 

2289# --------------------------------------- enhanced Web browser interface 

2290 

2291def _start_server(urlhandler, hostname, port): 

2292 """Start an HTTP server thread on a specific port. 

2293 

2294 Start an HTML/text server thread, so HTML or text documents can be 

2295 browsed dynamically and interactively with a Web browser. Example use: 

2296 

2297 >>> import time 

2298 >>> import pydoc 

2299 

2300 Define a URL handler. To determine what the client is asking 

2301 for, check the URL and content_type. 

2302 

2303 Then get or generate some text or HTML code and return it. 

2304 

2305 >>> def my_url_handler(url, content_type): 

2306 ... text = 'the URL sent was: (%s, %s)' % (url, content_type) 

2307 ... return text 

2308 

2309 Start server thread on port 0. 

2310 If you use port 0, the server will pick a random port number. 

2311 You can then use serverthread.port to get the port number. 

2312 

2313 >>> port = 0 

2314 >>> serverthread = pydoc._start_server(my_url_handler, port) 

2315 

2316 Check that the server is really started. If it is, open browser 

2317 and get first page. Use serverthread.url as the starting page. 

2318 

2319 >>> if serverthread.serving: 

2320 ... import webbrowser 

2321 

2322 The next two lines are commented out so a browser doesn't open if 

2323 doctest is run on this module. 

2324 

2325 #... webbrowser.open(serverthread.url) 

2326 #True 

2327 

2328 Let the server do its thing. We just need to monitor its status. 

2329 Use time.sleep so the loop doesn't hog the CPU. 

2330 

2331 >>> starttime = time.monotonic() 

2332 >>> timeout = 1 #seconds 

2333 

2334 This is a short timeout for testing purposes. 

2335 

2336 >>> while serverthread.serving: 

2337 ... time.sleep(.01) 

2338 ... if serverthread.serving and time.monotonic() - starttime > timeout: 

2339 ... serverthread.stop() 

2340 ... break 

2341 

2342 Print any errors that may have occurred. 

2343 

2344 >>> print(serverthread.error) 

2345 None 

2346 """ 

2347 import http.server 

2348 import email.message 

2349 import select 

2350 import threading 

2351 

2352 class DocHandler(http.server.BaseHTTPRequestHandler): 

2353 

2354 def do_GET(self): 

2355 """Process a request from an HTML browser. 

2356 

2357 The URL received is in self.path. 

2358 Get an HTML page from self.urlhandler and send it. 

2359 """ 

2360 if self.path.endswith('.css'): 

2361 content_type = 'text/css' 

2362 else: 

2363 content_type = 'text/html' 

2364 self.send_response(200) 

2365 self.send_header('Content-Type', '%s; charset=UTF-8' % content_type) 

2366 self.end_headers() 

2367 self.wfile.write(self.urlhandler( 

2368 self.path, content_type).encode('utf-8')) 

2369 

2370 def log_message(self, *args): 

2371 # Don't log messages. 

2372 pass 

2373 

2374 class DocServer(http.server.HTTPServer): 

2375 

2376 def __init__(self, host, port, callback): 

2377 self.host = host 

2378 self.address = (self.host, port) 

2379 self.callback = callback 

2380 self.base.__init__(self, self.address, self.handler) 

2381 self.quit = False 

2382 

2383 def serve_until_quit(self): 

2384 while not self.quit: 

2385 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 

2386 if rd: 

2387 self.handle_request() 

2388 self.server_close() 

2389 

2390 def server_activate(self): 

2391 self.base.server_activate(self) 

2392 if self.callback: 

2393 self.callback(self) 

2394 

2395 class ServerThread(threading.Thread): 

2396 

2397 def __init__(self, urlhandler, host, port): 

2398 self.urlhandler = urlhandler 

2399 self.host = host 

2400 self.port = int(port) 

2401 threading.Thread.__init__(self) 

2402 self.serving = False 

2403 self.error = None 

2404 

2405 def run(self): 

2406 """Start the server.""" 

2407 try: 

2408 DocServer.base = http.server.HTTPServer 

2409 DocServer.handler = DocHandler 

2410 DocHandler.MessageClass = email.message.Message 

2411 DocHandler.urlhandler = staticmethod(self.urlhandler) 

2412 docsvr = DocServer(self.host, self.port, self.ready) 

2413 self.docserver = docsvr 

2414 docsvr.serve_until_quit() 

2415 except Exception as e: 

2416 self.error = e 

2417 

2418 def ready(self, server): 

2419 self.serving = True 

2420 self.host = server.host 

2421 self.port = server.server_port 

2422 self.url = 'http://%s:%d/' % (self.host, self.port) 

2423 

2424 def stop(self): 

2425 """Stop the server and this thread nicely""" 

2426 self.docserver.quit = True 

2427 self.join() 

2428 # explicitly break a reference cycle: DocServer.callback 

2429 # has indirectly a reference to ServerThread. 

2430 self.docserver = None 

2431 self.serving = False 

2432 self.url = None 

2433 

2434 thread = ServerThread(urlhandler, hostname, port) 

2435 thread.start() 

2436 # Wait until thread.serving is True to make sure we are 

2437 # really up before returning. 

2438 while not thread.error and not thread.serving: 

2439 time.sleep(.01) 

2440 return thread 

2441 

2442 

2443def _url_handler(url, content_type="text/html"): 

2444 """The pydoc url handler for use with the pydoc server. 

2445 

2446 If the content_type is 'text/css', the _pydoc.css style 

2447 sheet is read and returned if it exits. 

2448 

2449 If the content_type is 'text/html', then the result of 

2450 get_html_page(url) is returned. 

2451 """ 

2452 class _HTMLDoc(HTMLDoc): 

2453 

2454 def page(self, title, contents): 

2455 """Format an HTML page.""" 

2456 css_path = "pydoc_data/_pydoc.css" 

2457 css_link = ( 

2458 '<link rel="stylesheet" type="text/css" href="%s">' % 

2459 css_path) 

2460 return '''\ 

2461<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 

2462<html><head><title>Pydoc: %s</title> 

2463<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 

2464%s</head><body bgcolor="#f0f0f8">%s<div style="clear:both;padding-top:.5em;">%s</div> 

2465</body></html>''' % (title, css_link, html_navbar(), contents) 

2466 

2467 

2468 html = _HTMLDoc() 

2469 

2470 def html_navbar(): 

2471 version = html.escape("%s [%s, %s]" % (platform.python_version(), 

2472 platform.python_build()[0], 

2473 platform.python_compiler())) 

2474 return """ 

2475 <div style='float:left'> 

2476 Python %s<br>%s 

2477 </div> 

2478 <div style='float:right'> 

2479 <div style='text-align:center'> 

2480 <a href="index.html">Module Index</a> 

2481 : <a href="topics.html">Topics</a> 

2482 : <a href="keywords.html">Keywords</a> 

2483 </div> 

2484 <div> 

2485 <form action="get" style='display:inline;'> 

2486 <input type=text name=key size=15> 

2487 <input type=submit value="Get"> 

2488 </form>&nbsp; 

2489 <form action="search" style='display:inline;'> 

2490 <input type=text name=key size=15> 

2491 <input type=submit value="Search"> 

2492 </form> 

2493 </div> 

2494 </div> 

2495 """ % (version, html.escape(platform.platform(terse=True))) 

2496 

2497 def html_index(): 

2498 """Module Index page.""" 

2499 

2500 def bltinlink(name): 

2501 return '<a href="%s.html">%s</a>' % (name, name) 

2502 

2503 heading = html.heading( 

2504 '<big><big><strong>Index of Modules</strong></big></big>', 

2505 '#ffffff', '#7799ee') 

2506 names = [name for name in sys.builtin_module_names 

2507 if name != '__main__'] 

2508 contents = html.multicolumn(names, bltinlink) 

2509 contents = [heading, '<p>' + html.bigsection( 

2510 'Built-in Modules', '#ffffff', '#ee77aa', contents)] 

2511 

2512 seen = {} 

2513 for dir in sys.path: 

2514 contents.append(html.index(dir, seen)) 

2515 

2516 contents.append( 

2517 '<p align=right><font color="#909090" face="helvetica,' 

2518 'arial"><strong>pydoc</strong> by Ka-Ping Yee' 

2519 '&lt;ping@lfw.org&gt;</font>') 

2520 return 'Index of Modules', ''.join(contents) 

2521 

2522 def html_search(key): 

2523 """Search results page.""" 

2524 # scan for modules 

2525 search_result = [] 

2526 

2527 def callback(path, modname, desc): 

2528 if modname[-9:] == '.__init__': 

2529 modname = modname[:-9] + ' (package)' 

2530 search_result.append((modname, desc and '- ' + desc)) 

2531 

2532 with warnings.catch_warnings(): 

2533 warnings.filterwarnings('ignore') # ignore problems during import 

2534 def onerror(modname): 

2535 pass 

2536 ModuleScanner().run(callback, key, onerror=onerror) 

2537 

2538 # format page 

2539 def bltinlink(name): 

2540 return '<a href="%s.html">%s</a>' % (name, name) 

2541 

2542 results = [] 

2543 heading = html.heading( 

2544 '<big><big><strong>Search Results</strong></big></big>', 

2545 '#ffffff', '#7799ee') 

2546 for name, desc in search_result: 

2547 results.append(bltinlink(name) + desc) 

2548 contents = heading + html.bigsection( 

2549 'key = %s' % key, '#ffffff', '#ee77aa', '<br>'.join(results)) 

2550 return 'Search Results', contents 

2551 

2552 def html_topics(): 

2553 """Index of topic texts available.""" 

2554 

2555 def bltinlink(name): 

2556 return '<a href="topic?key=%s">%s</a>' % (name, name) 

2557 

2558 heading = html.heading( 

2559 '<big><big><strong>INDEX</strong></big></big>', 

2560 '#ffffff', '#7799ee') 

2561 names = sorted(Helper.topics.keys()) 

2562 

2563 contents = html.multicolumn(names, bltinlink) 

2564 contents = heading + html.bigsection( 

2565 'Topics', '#ffffff', '#ee77aa', contents) 

2566 return 'Topics', contents 

2567 

2568 def html_keywords(): 

2569 """Index of keywords.""" 

2570 heading = html.heading( 

2571 '<big><big><strong>INDEX</strong></big></big>', 

2572 '#ffffff', '#7799ee') 

2573 names = sorted(Helper.keywords.keys()) 

2574 

2575 def bltinlink(name): 

2576 return '<a href="topic?key=%s">%s</a>' % (name, name) 

2577 

2578 contents = html.multicolumn(names, bltinlink) 

2579 contents = heading + html.bigsection( 

2580 'Keywords', '#ffffff', '#ee77aa', contents) 

2581 return 'Keywords', contents 

2582 

2583 def html_topicpage(topic): 

2584 """Topic or keyword help page.""" 

2585 buf = io.StringIO() 

2586 htmlhelp = Helper(buf, buf) 

2587 contents, xrefs = htmlhelp._gettopic(topic) 

2588 if topic in htmlhelp.keywords: 

2589 title = 'KEYWORD' 

2590 else: 

2591 title = 'TOPIC' 

2592 heading = html.heading( 

2593 '<big><big><strong>%s</strong></big></big>' % title, 

2594 '#ffffff', '#7799ee') 

2595 contents = '<pre>%s</pre>' % html.markup(contents) 

2596 contents = html.bigsection(topic , '#ffffff','#ee77aa', contents) 

2597 if xrefs: 

2598 xrefs = sorted(xrefs.split()) 

2599 

2600 def bltinlink(name): 

2601 return '<a href="topic?key=%s">%s</a>' % (name, name) 

2602 

2603 xrefs = html.multicolumn(xrefs, bltinlink) 

2604 xrefs = html.section('Related help topics: ', 

2605 '#ffffff', '#ee77aa', xrefs) 

2606 return ('%s %s' % (title, topic), 

2607 ''.join((heading, contents, xrefs))) 

2608 

2609 def html_getobj(url): 

2610 obj = locate(url, forceload=1) 

2611 if obj is None and url != 'None': 

2612 raise ValueError('could not find object') 

2613 title = describe(obj) 

2614 content = html.document(obj, url) 

2615 return title, content 

2616 

2617 def html_error(url, exc): 

2618 heading = html.heading( 

2619 '<big><big><strong>Error</strong></big></big>', 

2620 '#ffffff', '#7799ee') 

2621 contents = '<br>'.join(html.escape(line) for line in 

2622 format_exception_only(type(exc), exc)) 

2623 contents = heading + html.bigsection(url, '#ffffff', '#bb0000', 

2624 contents) 

2625 return "Error - %s" % url, contents 

2626 

2627 def get_html_page(url): 

2628 """Generate an HTML page for url.""" 

2629 complete_url = url 

2630 if url.endswith('.html'): 

2631 url = url[:-5] 

2632 try: 

2633 if url in ("", "index"): 

2634 title, content = html_index() 

2635 elif url == "topics": 

2636 title, content = html_topics() 

2637 elif url == "keywords": 

2638 title, content = html_keywords() 

2639 elif '=' in url: 

2640 op, _, url = url.partition('=') 

2641 if op == "search?key": 

2642 title, content = html_search(url) 

2643 elif op == "topic?key": 

2644 # try topics first, then objects. 

2645 try: 

2646 title, content = html_topicpage(url) 

2647 except ValueError: 

2648 title, content = html_getobj(url) 

2649 elif op == "get?key": 

2650 # try objects first, then topics. 

2651 if url in ("", "index"): 

2652 title, content = html_index() 

2653 else: 

2654 try: 

2655 title, content = html_getobj(url) 

2656 except ValueError: 

2657 title, content = html_topicpage(url) 

2658 else: 

2659 raise ValueError('bad pydoc url') 

2660 else: 

2661 title, content = html_getobj(url) 

2662 except Exception as exc: 

2663 # Catch any errors and display them in an error page. 

2664 title, content = html_error(complete_url, exc) 

2665 return html.page(title, content) 

2666 

2667 if url.startswith('/'): 

2668 url = url[1:] 

2669 if content_type == 'text/css': 

2670 path_here = os.path.dirname(os.path.realpath(__file__)) 

2671 css_path = os.path.join(path_here, url) 

2672 with open(css_path) as fp: 

2673 return ''.join(fp.readlines()) 

2674 elif content_type == 'text/html': 

2675 return get_html_page(url) 

2676 # Errors outside the url handler are caught by the server. 

2677 raise TypeError('unknown content type %r for url %s' % (content_type, url)) 

2678 

2679 

2680def browse(port=0, *, open_browser=True, hostname='localhost'): 

2681 """Start the enhanced pydoc Web server and open a Web browser. 

2682 

2683 Use port '0' to start the server on an arbitrary port. 

2684 Set open_browser to False to suppress opening a browser. 

2685 """ 

2686 import webbrowser 

2687 serverthread = _start_server(_url_handler, hostname, port) 

2688 if serverthread.error: 

2689 print(serverthread.error) 

2690 return 

2691 if serverthread.serving: 

2692 server_help_msg = 'Server commands: [b]rowser, [q]uit' 

2693 if open_browser: 

2694 webbrowser.open(serverthread.url) 

2695 try: 

2696 print('Server ready at', serverthread.url) 

2697 print(server_help_msg) 

2698 while serverthread.serving: 

2699 cmd = input('server> ') 

2700 cmd = cmd.lower() 

2701 if cmd == 'q': 

2702 break 

2703 elif cmd == 'b': 

2704 webbrowser.open(serverthread.url) 

2705 else: 

2706 print(server_help_msg) 

2707 except (KeyboardInterrupt, EOFError): 

2708 print() 

2709 finally: 

2710 if serverthread.serving: 

2711 serverthread.stop() 

2712 print('Server stopped') 

2713 

2714 

2715# -------------------------------------------------- command-line interface 

2716 

2717def ispath(x): 

2718 return isinstance(x, str) and x.find(os.sep) >= 0 

2719 

2720def _get_revised_path(given_path, argv0): 

2721 """Ensures current directory is on returned path, and argv0 directory is not 

2722 

2723 Exception: argv0 dir is left alone if it's also pydoc's directory. 

2724 

2725 Returns a new path entry list, or None if no adjustment is needed. 

2726 """ 

2727 # Scripts may get the current directory in their path by default if they're 

2728 # run with the -m switch, or directly from the current directory. 

2729 # The interactive prompt also allows imports from the current directory. 

2730 

2731 # Accordingly, if the current directory is already present, don't make 

2732 # any changes to the given_path 

2733 if '' in given_path or os.curdir in given_path or os.getcwd() in given_path: 

2734 return None 

2735 

2736 # Otherwise, add the current directory to the given path, and remove the 

2737 # script directory (as long as the latter isn't also pydoc's directory. 

2738 stdlib_dir = os.path.dirname(__file__) 

2739 script_dir = os.path.dirname(argv0) 

2740 revised_path = given_path.copy() 

2741 if script_dir in given_path and not os.path.samefile(script_dir, stdlib_dir): 

2742 revised_path.remove(script_dir) 

2743 revised_path.insert(0, os.getcwd()) 

2744 return revised_path 

2745 

2746 

2747# Note: the tests only cover _get_revised_path, not _adjust_cli_path itself 

2748def _adjust_cli_sys_path(): 

2749 """Ensures current directory is on sys.path, and __main__ directory is not. 

2750 

2751 Exception: __main__ dir is left alone if it's also pydoc's directory. 

2752 """ 

2753 revised_path = _get_revised_path(sys.path, sys.argv[0]) 

2754 if revised_path is not None: 

2755 sys.path[:] = revised_path 

2756 

2757 

2758def cli(): 

2759 """Command-line interface (looks at sys.argv to decide what to do).""" 

2760 import getopt 

2761 class BadUsage(Exception): pass 

2762 

2763 _adjust_cli_sys_path() 

2764 

2765 try: 

2766 opts, args = getopt.getopt(sys.argv[1:], 'bk:n:p:w') 

2767 writing = False 

2768 start_server = False 

2769 open_browser = False 

2770 port = 0 

2771 hostname = 'localhost' 

2772 for opt, val in opts: 

2773 if opt == '-b': 

2774 start_server = True 

2775 open_browser = True 

2776 if opt == '-k': 

2777 apropos(val) 

2778 return 

2779 if opt == '-p': 

2780 start_server = True 

2781 port = val 

2782 if opt == '-w': 

2783 writing = True 

2784 if opt == '-n': 

2785 start_server = True 

2786 hostname = val 

2787 

2788 if start_server: 

2789 browse(port, hostname=hostname, open_browser=open_browser) 

2790 return 

2791 

2792 if not args: raise BadUsage 

2793 for arg in args: 

2794 if ispath(arg) and not os.path.exists(arg): 

2795 print('file %r does not exist' % arg) 

2796 break 

2797 try: 

2798 if ispath(arg) and os.path.isfile(arg): 

2799 arg = importfile(arg) 

2800 if writing: 

2801 if ispath(arg) and os.path.isdir(arg): 

2802 writedocs(arg) 

2803 else: 

2804 writedoc(arg) 

2805 else: 

2806 help.help(arg) 

2807 except ErrorDuringImport as value: 

2808 print(value) 

2809 

2810 except (getopt.error, BadUsage): 

2811 cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0] 

2812 print("""pydoc - the Python documentation tool 

2813 

2814{cmd} <name> ... 

2815 Show text documentation on something. <name> may be the name of a 

2816 Python keyword, topic, function, module, or package, or a dotted 

2817 reference to a class or function within a module or module in a 

2818 package. If <name> contains a '{sep}', it is used as the path to a 

2819 Python source file to document. If name is 'keywords', 'topics', 

2820 or 'modules', a listing of these things is displayed. 

2821 

2822{cmd} -k <keyword> 

2823 Search for a keyword in the synopsis lines of all available modules. 

2824 

2825{cmd} -n <hostname> 

2826 Start an HTTP server with the given hostname (default: localhost). 

2827 

2828{cmd} -p <port> 

2829 Start an HTTP server on the given port on the local machine. Port 

2830 number 0 can be used to get an arbitrary unused port. 

2831 

2832{cmd} -b 

2833 Start an HTTP server on an arbitrary unused port and open a Web browser 

2834 to interactively browse documentation. This option can be used in 

2835 combination with -n and/or -p. 

2836 

2837{cmd} -w <name> ... 

2838 Write out the HTML documentation for a module to a file in the current 

2839 directory. If <name> contains a '{sep}', it is treated as a filename; if 

2840 it names a directory, documentation is written for all the contents. 

2841""".format(cmd=cmd, sep=os.sep)) 

2842 

2843if __name__ == '__main__': 

2844 cli()