1import os 2import sys 3import contextlib 4import importlib.util 5import inspect 6import pydoc 7import py_compile 8import keyword 9import _pickle 10import pkgutil 11import re 12import stat 13import string 14import tempfile 15import test.support 16import time 17import types 18import typing 19import unittest 20import urllib.parse 21import xml.etree 22import xml.etree.ElementTree 23import textwrap 24from io import StringIO 25from collections import namedtuple 26from test.support import os_helper 27from test.support.script_helper import assert_python_ok 28from test.support import threading_helper 29from test.support import (reap_children, captured_output, captured_stdout, 30 captured_stderr, requires_docstrings) 31from test.support.os_helper import (TESTFN, rmtree, unlink) 32from test import pydoc_mod 33 34 35class nonascii: 36 'Це не латиниця' 37 pass 38 39if test.support.HAVE_DOCSTRINGS: 40 expected_data_docstrings = ( 41 'dictionary for instance variables (if defined)', 42 'list of weak references to the object (if defined)', 43 ) * 2 44else: 45 expected_data_docstrings = ('', '', '', '') 46 47expected_text_pattern = """ 48NAME 49 test.pydoc_mod - This is a test module for test_pydoc 50%s 51CLASSES 52 builtins.object 53 A 54 B 55 C 56\x20\x20\x20\x20 57 class A(builtins.object) 58 | Hello and goodbye 59 |\x20\x20 60 | Methods defined here: 61 |\x20\x20 62 | __init__() 63 | Wow, I have no function! 64 |\x20\x20 65 | ---------------------------------------------------------------------- 66 | Data descriptors defined here: 67 |\x20\x20 68 | __dict__%s 69 |\x20\x20 70 | __weakref__%s 71\x20\x20\x20\x20 72 class B(builtins.object) 73 | Data descriptors defined here: 74 |\x20\x20 75 | __dict__%s 76 |\x20\x20 77 | __weakref__%s 78 |\x20\x20 79 | ---------------------------------------------------------------------- 80 | Data and other attributes defined here: 81 |\x20\x20 82 | NO_MEANING = 'eggs' 83 |\x20\x20 84 | __annotations__ = {'NO_MEANING': <class 'str'>} 85\x20\x20\x20\x20 86 class C(builtins.object) 87 | Methods defined here: 88 |\x20\x20 89 | get_answer(self) 90 | Return say_no() 91 |\x20\x20 92 | is_it_true(self) 93 | Return self.get_answer() 94 |\x20\x20 95 | say_no(self) 96 |\x20\x20 97 | ---------------------------------------------------------------------- 98 | Data descriptors defined here: 99 |\x20\x20 100 | __dict__ 101 | dictionary for instance variables (if defined) 102 |\x20\x20 103 | __weakref__ 104 | list of weak references to the object (if defined) 105 106FUNCTIONS 107 doc_func() 108 This function solves all of the world's problems: 109 hunger 110 lack of Python 111 war 112\x20\x20\x20\x20 113 nodoc_func() 114 115DATA 116 __xyz__ = 'X, Y and Z' 117 118VERSION 119 1.2.3.4 120 121AUTHOR 122 Benjamin Peterson 123 124CREDITS 125 Nobody 126 127FILE 128 %s 129""".strip() 130 131expected_text_data_docstrings = tuple('\n | ' + s if s else '' 132 for s in expected_data_docstrings) 133 134expected_html_pattern = """ 135<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> 136<tr bgcolor="#7799ee"> 137<td valign=bottom> <br> 138<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong><a href="test.html"><font color="#ffffff">test</font></a>.pydoc_mod</strong></big></big> (version 1.2.3.4)</font></td 139><td align=right valign=bottom 140><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:%s">%s</a>%s</font></td></tr></table> 141 <p><tt>This is a test module for test_pydoc</tt></p> 142<p> 143<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 144<tr bgcolor="#ee77aa"> 145<td colspan=3 valign=bottom> <br> 146<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr> 147\x20\x20\x20\x20 148<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td> 149<td width="100%%"><dl> 150<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a> 151</font></dt><dd> 152<dl> 153<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a> 154</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a> 155</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#C">C</a> 156</font></dt></dl> 157</dd> 158</dl> 159 <p> 160<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 161<tr bgcolor="#ffc8d8"> 162<td colspan=3 valign=bottom> <br> 163<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr> 164\x20\x20\x20\x20 165<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> 166<td colspan=2><tt>Hello and goodbye<br> </tt></td></tr> 167<tr><td> </td> 168<td width="100%%">Methods defined here:<br> 169<dl><dt><a name="A-__init__"><strong>__init__</strong></a>()</dt><dd><tt>Wow, I have no function!</tt></dd></dl> 170 171<hr> 172Data descriptors defined here:<br> 173<dl><dt><strong>__dict__</strong></dt> 174<dd><tt>%s</tt></dd> 175</dl> 176<dl><dt><strong>__weakref__</strong></dt> 177<dd><tt>%s</tt></dd> 178</dl> 179</td></tr></table> <p> 180<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 181<tr bgcolor="#ffc8d8"> 182<td colspan=3 valign=bottom> <br> 183<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr> 184\x20\x20\x20\x20 185<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> 186<td width="100%%">Data descriptors defined here:<br> 187<dl><dt><strong>__dict__</strong></dt> 188<dd><tt>%s</tt></dd> 189</dl> 190<dl><dt><strong>__weakref__</strong></dt> 191<dd><tt>%s</tt></dd> 192</dl> 193<hr> 194Data and other attributes defined here:<br> 195<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl> 196 197<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': <class 'str'>}</dl> 198 199</td></tr></table> <p> 200<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 201<tr bgcolor="#ffc8d8"> 202<td colspan=3 valign=bottom> <br> 203<font color="#000000" face="helvetica, arial"><a name="C">class <strong>C</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr> 204\x20\x20\x20\x20 205<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> 206<td width="100%%">Methods defined here:<br> 207<dl><dt><a name="C-get_answer"><strong>get_answer</strong></a>(self)</dt><dd><tt>Return <a href="#C-say_no">say_no</a>()</tt></dd></dl> 208 209<dl><dt><a name="C-is_it_true"><strong>is_it_true</strong></a>(self)</dt><dd><tt>Return self.<a href="#C-get_answer">get_answer</a>()</tt></dd></dl> 210 211<dl><dt><a name="C-say_no"><strong>say_no</strong></a>(self)</dt></dl> 212 213<hr> 214Data descriptors defined here:<br> 215<dl><dt><strong>__dict__</strong></dt> 216<dd><tt>dictionary for instance variables (if defined)</tt></dd> 217</dl> 218<dl><dt><strong>__weakref__</strong></dt> 219<dd><tt>list of weak references to the object (if defined)</tt></dd> 220</dl> 221</td></tr></table></td></tr></table><p> 222<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 223<tr bgcolor="#eeaa77"> 224<td colspan=3 valign=bottom> <br> 225<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr> 226\x20\x20\x20\x20 227<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td> 228<td width="100%%"><dl><dt><a name="-doc_func"><strong>doc_func</strong></a>()</dt><dd><tt>This function solves all of the world's problems:<br> 229hunger<br> 230lack of Python<br> 231war</tt></dd></dl> 232 <dl><dt><a name="-nodoc_func"><strong>nodoc_func</strong></a>()</dt></dl> 233</td></tr></table><p> 234<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 235<tr bgcolor="#55aa55"> 236<td colspan=3 valign=bottom> <br> 237<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr> 238\x20\x20\x20\x20 239<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td> 240<td width="100%%"><strong>__xyz__</strong> = 'X, Y and Z'</td></tr></table><p> 241<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 242<tr bgcolor="#7799ee"> 243<td colspan=3 valign=bottom> <br> 244<font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr> 245\x20\x20\x20\x20 246<tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td> 247<td width="100%%">Benjamin Peterson</td></tr></table><p> 248<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 249<tr bgcolor="#7799ee"> 250<td colspan=3 valign=bottom> <br> 251<font color="#ffffff" face="helvetica, arial"><big><strong>Credits</strong></big></font></td></tr> 252\x20\x20\x20\x20 253<tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td> 254<td width="100%%">Nobody</td></tr></table> 255""".strip() # ' <- emacs turd 256 257expected_html_data_docstrings = tuple(s.replace(' ', ' ') 258 for s in expected_data_docstrings) 259 260# output pattern for missing module 261missing_pattern = '''\ 262No Python documentation found for %r. 263Use help() to get the interactive help utility. 264Use help(str) for help on the str class.'''.replace('\n', os.linesep) 265 266# output pattern for module with bad imports 267badimport_pattern = "problem in %s - ModuleNotFoundError: No module named %r" 268 269expected_dynamicattribute_pattern = """ 270Help on class DA in module %s: 271 272class DA(builtins.object) 273 | Data descriptors defined here: 274 |\x20\x20 275 | __dict__%s 276 |\x20\x20 277 | __weakref__%s 278 |\x20\x20 279 | ham 280 |\x20\x20 281 | ---------------------------------------------------------------------- 282 | Data and other attributes inherited from Meta: 283 |\x20\x20 284 | ham = 'spam' 285""".strip() 286 287expected_virtualattribute_pattern1 = """ 288Help on class Class in module %s: 289 290class Class(builtins.object) 291 | Data and other attributes inherited from Meta: 292 |\x20\x20 293 | LIFE = 42 294""".strip() 295 296expected_virtualattribute_pattern2 = """ 297Help on class Class1 in module %s: 298 299class Class1(builtins.object) 300 | Data and other attributes inherited from Meta1: 301 |\x20\x20 302 | one = 1 303""".strip() 304 305expected_virtualattribute_pattern3 = """ 306Help on class Class2 in module %s: 307 308class Class2(Class1) 309 | Method resolution order: 310 | Class2 311 | Class1 312 | builtins.object 313 |\x20\x20 314 | Data and other attributes inherited from Meta1: 315 |\x20\x20 316 | one = 1 317 |\x20\x20 318 | ---------------------------------------------------------------------- 319 | Data and other attributes inherited from Meta3: 320 |\x20\x20 321 | three = 3 322 |\x20\x20 323 | ---------------------------------------------------------------------- 324 | Data and other attributes inherited from Meta2: 325 |\x20\x20 326 | two = 2 327""".strip() 328 329expected_missingattribute_pattern = """ 330Help on class C in module %s: 331 332class C(builtins.object) 333 | Data and other attributes defined here: 334 |\x20\x20 335 | here = 'present!' 336""".strip() 337 338def run_pydoc(module_name, *args, **env): 339 """ 340 Runs pydoc on the specified module. Returns the stripped 341 output of pydoc. 342 """ 343 args = args + (module_name,) 344 # do not write bytecode files to avoid caching errors 345 rc, out, err = assert_python_ok('-B', pydoc.__file__, *args, **env) 346 return out.strip() 347 348def get_pydoc_html(module): 349 "Returns pydoc generated output as html" 350 doc = pydoc.HTMLDoc() 351 output = doc.docmodule(module) 352 loc = doc.getdocloc(pydoc_mod) or "" 353 if loc: 354 loc = "<br><a href=\"" + loc + "\">Module Docs</a>" 355 return output.strip(), loc 356 357def get_pydoc_link(module): 358 "Returns a documentation web link of a module" 359 abspath = os.path.abspath 360 dirname = os.path.dirname 361 basedir = dirname(dirname(abspath(__file__))) 362 doc = pydoc.TextDoc() 363 loc = doc.getdocloc(module, basedir=basedir) 364 return loc 365 366def get_pydoc_text(module): 367 "Returns pydoc generated output as text" 368 doc = pydoc.TextDoc() 369 loc = doc.getdocloc(pydoc_mod) or "" 370 if loc: 371 loc = "\nMODULE DOCS\n " + loc + "\n" 372 373 output = doc.docmodule(module) 374 375 # clean up the extra text formatting that pydoc performs 376 patt = re.compile('\b.') 377 output = patt.sub('', output) 378 return output.strip(), loc 379 380def get_html_title(text): 381 # Bit of hack, but good enough for test purposes 382 header, _, _ = text.partition("</head>") 383 _, _, title = header.partition("<title>") 384 title, _, _ = title.partition("</title>") 385 return title 386 387 388class PydocBaseTest(unittest.TestCase): 389 390 def _restricted_walk_packages(self, walk_packages, path=None): 391 """ 392 A version of pkgutil.walk_packages() that will restrict itself to 393 a given path. 394 """ 395 default_path = path or [os.path.dirname(__file__)] 396 def wrapper(path=None, prefix='', onerror=None): 397 return walk_packages(path or default_path, prefix, onerror) 398 return wrapper 399 400 @contextlib.contextmanager 401 def restrict_walk_packages(self, path=None): 402 walk_packages = pkgutil.walk_packages 403 pkgutil.walk_packages = self._restricted_walk_packages(walk_packages, 404 path) 405 try: 406 yield 407 finally: 408 pkgutil.walk_packages = walk_packages 409 410 def call_url_handler(self, url, expected_title): 411 text = pydoc._url_handler(url, "text/html") 412 result = get_html_title(text) 413 # Check the title to ensure an unexpected error page was not returned 414 self.assertEqual(result, expected_title, text) 415 return text 416 417 418class PydocDocTest(unittest.TestCase): 419 maxDiff = None 420 421 @unittest.skipIf(sys.flags.optimize >= 2, 422 "Docstrings are omitted with -O2 and above") 423 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 424 'trace function introduces __locals__ unexpectedly') 425 @requires_docstrings 426 def test_html_doc(self): 427 result, doc_loc = get_pydoc_html(pydoc_mod) 428 mod_file = inspect.getabsfile(pydoc_mod) 429 mod_url = urllib.parse.quote(mod_file) 430 expected_html = expected_html_pattern % ( 431 (mod_url, mod_file, doc_loc) + 432 expected_html_data_docstrings) 433 self.assertEqual(result, expected_html) 434 435 @unittest.skipIf(sys.flags.optimize >= 2, 436 "Docstrings are omitted with -O2 and above") 437 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 438 'trace function introduces __locals__ unexpectedly') 439 @requires_docstrings 440 def test_text_doc(self): 441 result, doc_loc = get_pydoc_text(pydoc_mod) 442 expected_text = expected_text_pattern % ( 443 (doc_loc,) + 444 expected_text_data_docstrings + 445 (inspect.getabsfile(pydoc_mod),)) 446 self.assertEqual(expected_text, result) 447 448 def test_text_enum_member_with_value_zero(self): 449 # Test issue #20654 to ensure enum member with value 0 can be 450 # displayed. It used to throw KeyError: 'zero'. 451 import enum 452 class BinaryInteger(enum.IntEnum): 453 zero = 0 454 one = 1 455 doc = pydoc.render_doc(BinaryInteger) 456 self.assertIn('<BinaryInteger.zero: 0>', doc) 457 458 def test_mixed_case_module_names_are_lower_cased(self): 459 # issue16484 460 doc_link = get_pydoc_link(xml.etree.ElementTree) 461 self.assertIn('xml.etree.elementtree', doc_link) 462 463 def test_issue8225(self): 464 # Test issue8225 to ensure no doc link appears for xml.etree 465 result, doc_loc = get_pydoc_text(xml.etree) 466 self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link") 467 468 def test_getpager_with_stdin_none(self): 469 previous_stdin = sys.stdin 470 try: 471 sys.stdin = None 472 pydoc.getpager() # Shouldn't fail. 473 finally: 474 sys.stdin = previous_stdin 475 476 def test_non_str_name(self): 477 # issue14638 478 # Treat illegal (non-str) name like no name 479 480 class A: 481 __name__ = 42 482 class B: 483 pass 484 adoc = pydoc.render_doc(A()) 485 bdoc = pydoc.render_doc(B()) 486 self.assertEqual(adoc.replace("A", "B"), bdoc) 487 488 def test_not_here(self): 489 missing_module = "test.i_am_not_here" 490 result = str(run_pydoc(missing_module), 'ascii') 491 expected = missing_pattern % missing_module 492 self.assertEqual(expected, result, 493 "documentation for missing module found") 494 495 @unittest.skipIf(sys.flags.optimize >= 2, 496 'Docstrings are omitted with -OO and above') 497 def test_not_ascii(self): 498 result = run_pydoc('test.test_pydoc.nonascii', PYTHONIOENCODING='ascii') 499 encoded = nonascii.__doc__.encode('ascii', 'backslashreplace') 500 self.assertIn(encoded, result) 501 502 def test_input_strip(self): 503 missing_module = " test.i_am_not_here " 504 result = str(run_pydoc(missing_module), 'ascii') 505 expected = missing_pattern % missing_module.strip() 506 self.assertEqual(expected, result) 507 508 def test_stripid(self): 509 # test with strings, other implementations might have different repr() 510 stripid = pydoc.stripid 511 # strip the id 512 self.assertEqual(stripid('<function stripid at 0x88dcee4>'), 513 '<function stripid>') 514 self.assertEqual(stripid('<function stripid at 0x01F65390>'), 515 '<function stripid>') 516 # nothing to strip, return the same text 517 self.assertEqual(stripid('42'), '42') 518 self.assertEqual(stripid("<type 'exceptions.Exception'>"), 519 "<type 'exceptions.Exception'>") 520 521 def test_builtin_with_more_than_four_children(self): 522 """Tests help on builtin object which have more than four child classes. 523 524 When running help() on a builtin class which has child classes, it 525 should contain a "Built-in subclasses" section and only 4 classes 526 should be displayed with a hint on how many more subclasses are present. 527 For example: 528 529 >>> help(object) 530 Help on class object in module builtins: 531 532 class object 533 | The most base type 534 | 535 | Built-in subclasses: 536 | async_generator 537 | BaseException 538 | builtin_function_or_method 539 | bytearray 540 | ... and 82 other subclasses 541 """ 542 doc = pydoc.TextDoc() 543 text = doc.docclass(object) 544 snip = (" | Built-in subclasses:\n" 545 " | async_generator\n" 546 " | BaseException\n" 547 " | builtin_function_or_method\n" 548 " | bytearray\n" 549 " | ... and \\d+ other subclasses") 550 self.assertRegex(text, snip) 551 552 def test_builtin_with_child(self): 553 """Tests help on builtin object which have only child classes. 554 555 When running help() on a builtin class which has child classes, it 556 should contain a "Built-in subclasses" section. For example: 557 558 >>> help(ArithmeticError) 559 Help on class ArithmeticError in module builtins: 560 561 class ArithmeticError(Exception) 562 | Base class for arithmetic errors. 563 | 564 ... 565 | 566 | Built-in subclasses: 567 | FloatingPointError 568 | OverflowError 569 | ZeroDivisionError 570 """ 571 doc = pydoc.TextDoc() 572 text = doc.docclass(ArithmeticError) 573 snip = (" | Built-in subclasses:\n" 574 " | FloatingPointError\n" 575 " | OverflowError\n" 576 " | ZeroDivisionError") 577 self.assertIn(snip, text) 578 579 def test_builtin_with_grandchild(self): 580 """Tests help on builtin classes which have grandchild classes. 581 582 When running help() on a builtin class which has child classes, it 583 should contain a "Built-in subclasses" section. However, if it also has 584 grandchildren, these should not show up on the subclasses section. 585 For example: 586 587 >>> help(Exception) 588 Help on class Exception in module builtins: 589 590 class Exception(BaseException) 591 | Common base class for all non-exit exceptions. 592 | 593 ... 594 | 595 | Built-in subclasses: 596 | ArithmeticError 597 | AssertionError 598 | AttributeError 599 ... 600 """ 601 doc = pydoc.TextDoc() 602 text = doc.docclass(Exception) 603 snip = (" | Built-in subclasses:\n" 604 " | ArithmeticError\n" 605 " | AssertionError\n" 606 " | AttributeError") 607 self.assertIn(snip, text) 608 # Testing that the grandchild ZeroDivisionError does not show up 609 self.assertNotIn('ZeroDivisionError', text) 610 611 def test_builtin_no_child(self): 612 """Tests help on builtin object which have no child classes. 613 614 When running help() on a builtin class which has no child classes, it 615 should not contain any "Built-in subclasses" section. For example: 616 617 >>> help(ZeroDivisionError) 618 619 Help on class ZeroDivisionError in module builtins: 620 621 class ZeroDivisionError(ArithmeticError) 622 | Second argument to a division or modulo operation was zero. 623 | 624 | Method resolution order: 625 | ZeroDivisionError 626 | ArithmeticError 627 | Exception 628 | BaseException 629 | object 630 | 631 | Methods defined here: 632 ... 633 """ 634 doc = pydoc.TextDoc() 635 text = doc.docclass(ZeroDivisionError) 636 # Testing that the subclasses section does not appear 637 self.assertNotIn('Built-in subclasses', text) 638 639 def test_builtin_on_metaclasses(self): 640 """Tests help on metaclasses. 641 642 When running help() on a metaclasses such as type, it 643 should not contain any "Built-in subclasses" section. 644 """ 645 doc = pydoc.TextDoc() 646 text = doc.docclass(type) 647 # Testing that the subclasses section does not appear 648 self.assertNotIn('Built-in subclasses', text) 649 650 @unittest.skipIf(sys.flags.optimize >= 2, 651 'Docstrings are omitted with -O2 and above') 652 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 653 'trace function introduces __locals__ unexpectedly') 654 @requires_docstrings 655 def test_help_output_redirect(self): 656 # issue 940286, if output is set in Helper, then all output from 657 # Helper.help should be redirected 658 old_pattern = expected_text_pattern 659 getpager_old = pydoc.getpager 660 getpager_new = lambda: (lambda x: x) 661 self.maxDiff = None 662 663 buf = StringIO() 664 helper = pydoc.Helper(output=buf) 665 unused, doc_loc = get_pydoc_text(pydoc_mod) 666 module = "test.pydoc_mod" 667 help_header = """ 668 Help on module test.pydoc_mod in test: 669 670 """.lstrip() 671 help_header = textwrap.dedent(help_header) 672 expected_help_pattern = help_header + expected_text_pattern 673 674 pydoc.getpager = getpager_new 675 try: 676 with captured_output('stdout') as output, \ 677 captured_output('stderr') as err: 678 helper.help(module) 679 result = buf.getvalue().strip() 680 expected_text = expected_help_pattern % ( 681 (doc_loc,) + 682 expected_text_data_docstrings + 683 (inspect.getabsfile(pydoc_mod),)) 684 self.assertEqual('', output.getvalue()) 685 self.assertEqual('', err.getvalue()) 686 self.assertEqual(expected_text, result) 687 finally: 688 pydoc.getpager = getpager_old 689 690 def test_namedtuple_fields(self): 691 Person = namedtuple('Person', ['nickname', 'firstname']) 692 with captured_stdout() as help_io: 693 pydoc.help(Person) 694 helptext = help_io.getvalue() 695 self.assertIn("nickname", helptext) 696 self.assertIn("firstname", helptext) 697 self.assertIn("Alias for field number 0", helptext) 698 self.assertIn("Alias for field number 1", helptext) 699 700 def test_namedtuple_public_underscore(self): 701 NT = namedtuple('NT', ['abc', 'def'], rename=True) 702 with captured_stdout() as help_io: 703 pydoc.help(NT) 704 helptext = help_io.getvalue() 705 self.assertIn('_1', helptext) 706 self.assertIn('_replace', helptext) 707 self.assertIn('_asdict', helptext) 708 709 def test_synopsis(self): 710 self.addCleanup(unlink, TESTFN) 711 for encoding in ('ISO-8859-1', 'UTF-8'): 712 with open(TESTFN, 'w', encoding=encoding) as script: 713 if encoding != 'UTF-8': 714 print('#coding: {}'.format(encoding), file=script) 715 print('"""line 1: h\xe9', file=script) 716 print('line 2: hi"""', file=script) 717 synopsis = pydoc.synopsis(TESTFN, {}) 718 self.assertEqual(synopsis, 'line 1: h\xe9') 719 720 @unittest.skipIf(sys.flags.optimize >= 2, 721 'Docstrings are omitted with -OO and above') 722 def test_synopsis_sourceless(self): 723 expected = os.__doc__.splitlines()[0] 724 filename = os.__cached__ 725 synopsis = pydoc.synopsis(filename) 726 727 self.assertEqual(synopsis, expected) 728 729 def test_synopsis_sourceless_empty_doc(self): 730 with os_helper.temp_cwd() as test_dir: 731 init_path = os.path.join(test_dir, 'foomod42.py') 732 cached_path = importlib.util.cache_from_source(init_path) 733 with open(init_path, 'w') as fobj: 734 fobj.write("foo = 1") 735 py_compile.compile(init_path) 736 synopsis = pydoc.synopsis(init_path, {}) 737 self.assertIsNone(synopsis) 738 synopsis_cached = pydoc.synopsis(cached_path, {}) 739 self.assertIsNone(synopsis_cached) 740 741 def test_splitdoc_with_description(self): 742 example_string = "I Am A Doc\n\n\nHere is my description" 743 self.assertEqual(pydoc.splitdoc(example_string), 744 ('I Am A Doc', '\nHere is my description')) 745 746 def test_is_package_when_not_package(self): 747 with os_helper.temp_cwd() as test_dir: 748 self.assertFalse(pydoc.ispackage(test_dir)) 749 750 def test_is_package_when_is_package(self): 751 with os_helper.temp_cwd() as test_dir: 752 init_path = os.path.join(test_dir, '__init__.py') 753 open(init_path, 'w').close() 754 self.assertTrue(pydoc.ispackage(test_dir)) 755 os.remove(init_path) 756 757 def test_allmethods(self): 758 # issue 17476: allmethods was no longer returning unbound methods. 759 # This test is a bit fragile in the face of changes to object and type, 760 # but I can't think of a better way to do it without duplicating the 761 # logic of the function under test. 762 763 class TestClass(object): 764 def method_returning_true(self): 765 return True 766 767 # What we expect to get back: everything on object... 768 expected = dict(vars(object)) 769 # ...plus our unbound method... 770 expected['method_returning_true'] = TestClass.method_returning_true 771 # ...but not the non-methods on object. 772 del expected['__doc__'] 773 del expected['__class__'] 774 # inspect resolves descriptors on type into methods, but vars doesn't, 775 # so we need to update __subclasshook__ and __init_subclass__. 776 expected['__subclasshook__'] = TestClass.__subclasshook__ 777 expected['__init_subclass__'] = TestClass.__init_subclass__ 778 779 methods = pydoc.allmethods(TestClass) 780 self.assertDictEqual(methods, expected) 781 782 def test_method_aliases(self): 783 class A: 784 def tkraise(self, aboveThis=None): 785 """Raise this widget in the stacking order.""" 786 lift = tkraise 787 def a_size(self): 788 """Return size""" 789 class B(A): 790 def itemconfigure(self, tagOrId, cnf=None, **kw): 791 """Configure resources of an item TAGORID.""" 792 itemconfig = itemconfigure 793 b_size = A.a_size 794 795 doc = pydoc.render_doc(B) 796 # clean up the extra text formatting that pydoc performs 797 doc = re.sub('\b.', '', doc) 798 self.assertEqual(doc, '''\ 799Python Library Documentation: class B in module %s 800 801class B(A) 802 | Method resolution order: 803 | B 804 | A 805 | builtins.object 806 |\x20\x20 807 | Methods defined here: 808 |\x20\x20 809 | b_size = a_size(self) 810 |\x20\x20 811 | itemconfig = itemconfigure(self, tagOrId, cnf=None, **kw) 812 |\x20\x20 813 | itemconfigure(self, tagOrId, cnf=None, **kw) 814 | Configure resources of an item TAGORID. 815 |\x20\x20 816 | ---------------------------------------------------------------------- 817 | Methods inherited from A: 818 |\x20\x20 819 | a_size(self) 820 | Return size 821 |\x20\x20 822 | lift = tkraise(self, aboveThis=None) 823 |\x20\x20 824 | tkraise(self, aboveThis=None) 825 | Raise this widget in the stacking order. 826 |\x20\x20 827 | ---------------------------------------------------------------------- 828 | Data descriptors inherited from A: 829 |\x20\x20 830 | __dict__ 831 | dictionary for instance variables (if defined) 832 |\x20\x20 833 | __weakref__ 834 | list of weak references to the object (if defined) 835''' % __name__) 836 837 doc = pydoc.render_doc(B, renderer=pydoc.HTMLDoc()) 838 self.assertEqual(doc, '''\ 839Python Library Documentation: class B in module %s 840 841<p> 842<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 843<tr bgcolor="#ffc8d8"> 844<td colspan=3 valign=bottom> <br> 845<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(A)</font></td></tr> 846\x20\x20\x20\x20 847<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> 848<td width="100%%"><dl><dt>Method resolution order:</dt> 849<dd>B</dd> 850<dd>A</dd> 851<dd><a href="builtins.html#object">builtins.object</a></dd> 852</dl> 853<hr> 854Methods defined here:<br> 855<dl><dt><a name="B-b_size"><strong>b_size</strong></a> = <a href="#B-a_size">a_size</a>(self)</dt></dl> 856 857<dl><dt><a name="B-itemconfig"><strong>itemconfig</strong></a> = <a href="#B-itemconfigure">itemconfigure</a>(self, tagOrId, cnf=None, **kw)</dt></dl> 858 859<dl><dt><a name="B-itemconfigure"><strong>itemconfigure</strong></a>(self, tagOrId, cnf=None, **kw)</dt><dd><tt>Configure resources of an item TAGORID.</tt></dd></dl> 860 861<hr> 862Methods inherited from A:<br> 863<dl><dt><a name="B-a_size"><strong>a_size</strong></a>(self)</dt><dd><tt>Return size</tt></dd></dl> 864 865<dl><dt><a name="B-lift"><strong>lift</strong></a> = <a href="#B-tkraise">tkraise</a>(self, aboveThis=None)</dt></dl> 866 867<dl><dt><a name="B-tkraise"><strong>tkraise</strong></a>(self, aboveThis=None)</dt><dd><tt>Raise this widget in the stacking order.</tt></dd></dl> 868 869<hr> 870Data descriptors inherited from A:<br> 871<dl><dt><strong>__dict__</strong></dt> 872<dd><tt>dictionary for instance variables (if defined)</tt></dd> 873</dl> 874<dl><dt><strong>__weakref__</strong></dt> 875<dd><tt>list of weak references to the object (if defined)</tt></dd> 876</dl> 877</td></tr></table>\ 878''' % __name__) 879 880 881class PydocImportTest(PydocBaseTest): 882 883 def setUp(self): 884 self.test_dir = os.mkdir(TESTFN) 885 self.addCleanup(rmtree, TESTFN) 886 importlib.invalidate_caches() 887 888 def test_badimport(self): 889 # This tests the fix for issue 5230, where if pydoc found the module 890 # but the module had an internal import error pydoc would report no doc 891 # found. 892 modname = 'testmod_xyzzy' 893 testpairs = ( 894 ('i_am_not_here', 'i_am_not_here'), 895 ('test.i_am_not_here_either', 'test.i_am_not_here_either'), 896 ('test.i_am_not_here.neither_am_i', 'test.i_am_not_here'), 897 ('i_am_not_here.{}'.format(modname), 'i_am_not_here'), 898 ('test.{}'.format(modname), 'test.{}'.format(modname)), 899 ) 900 901 sourcefn = os.path.join(TESTFN, modname) + os.extsep + "py" 902 for importstring, expectedinmsg in testpairs: 903 with open(sourcefn, 'w') as f: 904 f.write("import {}\n".format(importstring)) 905 result = run_pydoc(modname, PYTHONPATH=TESTFN).decode("ascii") 906 expected = badimport_pattern % (modname, expectedinmsg) 907 self.assertEqual(expected, result) 908 909 def test_apropos_with_bad_package(self): 910 # Issue 7425 - pydoc -k failed when bad package on path 911 pkgdir = os.path.join(TESTFN, "syntaxerr") 912 os.mkdir(pkgdir) 913 badsyntax = os.path.join(pkgdir, "__init__") + os.extsep + "py" 914 with open(badsyntax, 'w') as f: 915 f.write("invalid python syntax = $1\n") 916 with self.restrict_walk_packages(path=[TESTFN]): 917 with captured_stdout() as out: 918 with captured_stderr() as err: 919 pydoc.apropos('xyzzy') 920 # No result, no error 921 self.assertEqual(out.getvalue(), '') 922 self.assertEqual(err.getvalue(), '') 923 # The package name is still matched 924 with captured_stdout() as out: 925 with captured_stderr() as err: 926 pydoc.apropos('syntaxerr') 927 self.assertEqual(out.getvalue().strip(), 'syntaxerr') 928 self.assertEqual(err.getvalue(), '') 929 930 def test_apropos_with_unreadable_dir(self): 931 # Issue 7367 - pydoc -k failed when unreadable dir on path 932 self.unreadable_dir = os.path.join(TESTFN, "unreadable") 933 os.mkdir(self.unreadable_dir, 0) 934 self.addCleanup(os.rmdir, self.unreadable_dir) 935 # Note, on Windows the directory appears to be still 936 # readable so this is not really testing the issue there 937 with self.restrict_walk_packages(path=[TESTFN]): 938 with captured_stdout() as out: 939 with captured_stderr() as err: 940 pydoc.apropos('SOMEKEY') 941 # No result, no error 942 self.assertEqual(out.getvalue(), '') 943 self.assertEqual(err.getvalue(), '') 944 945 def test_apropos_empty_doc(self): 946 pkgdir = os.path.join(TESTFN, 'walkpkg') 947 os.mkdir(pkgdir) 948 self.addCleanup(rmtree, pkgdir) 949 init_path = os.path.join(pkgdir, '__init__.py') 950 with open(init_path, 'w') as fobj: 951 fobj.write("foo = 1") 952 current_mode = stat.S_IMODE(os.stat(pkgdir).st_mode) 953 try: 954 os.chmod(pkgdir, current_mode & ~stat.S_IEXEC) 955 with self.restrict_walk_packages(path=[TESTFN]), captured_stdout() as stdout: 956 pydoc.apropos('') 957 self.assertIn('walkpkg', stdout.getvalue()) 958 finally: 959 os.chmod(pkgdir, current_mode) 960 961 def test_url_search_package_error(self): 962 # URL handler search should cope with packages that raise exceptions 963 pkgdir = os.path.join(TESTFN, "test_error_package") 964 os.mkdir(pkgdir) 965 init = os.path.join(pkgdir, "__init__.py") 966 with open(init, "wt", encoding="ascii") as f: 967 f.write("""raise ValueError("ouch")\n""") 968 with self.restrict_walk_packages(path=[TESTFN]): 969 # Package has to be importable for the error to have any effect 970 saved_paths = tuple(sys.path) 971 sys.path.insert(0, TESTFN) 972 try: 973 with self.assertRaisesRegex(ValueError, "ouch"): 974 import test_error_package # Sanity check 975 976 text = self.call_url_handler("search?key=test_error_package", 977 "Pydoc: Search Results") 978 found = ('<a href="test_error_package.html">' 979 'test_error_package</a>') 980 self.assertIn(found, text) 981 finally: 982 sys.path[:] = saved_paths 983 984 @unittest.skip('causes undesirable side-effects (#20128)') 985 def test_modules(self): 986 # See Helper.listmodules(). 987 num_header_lines = 2 988 num_module_lines_min = 5 # Playing it safe. 989 num_footer_lines = 3 990 expected = num_header_lines + num_module_lines_min + num_footer_lines 991 992 output = StringIO() 993 helper = pydoc.Helper(output=output) 994 helper('modules') 995 result = output.getvalue().strip() 996 num_lines = len(result.splitlines()) 997 998 self.assertGreaterEqual(num_lines, expected) 999 1000 @unittest.skip('causes undesirable side-effects (#20128)') 1001 def test_modules_search(self): 1002 # See Helper.listmodules(). 1003 expected = 'pydoc - ' 1004 1005 output = StringIO() 1006 helper = pydoc.Helper(output=output) 1007 with captured_stdout() as help_io: 1008 helper('modules pydoc') 1009 result = help_io.getvalue() 1010 1011 self.assertIn(expected, result) 1012 1013 @unittest.skip('some buildbots are not cooperating (#20128)') 1014 def test_modules_search_builtin(self): 1015 expected = 'gc - ' 1016 1017 output = StringIO() 1018 helper = pydoc.Helper(output=output) 1019 with captured_stdout() as help_io: 1020 helper('modules garbage') 1021 result = help_io.getvalue() 1022 1023 self.assertTrue(result.startswith(expected)) 1024 1025 def test_importfile(self): 1026 loaded_pydoc = pydoc.importfile(pydoc.__file__) 1027 1028 self.assertIsNot(loaded_pydoc, pydoc) 1029 self.assertEqual(loaded_pydoc.__name__, 'pydoc') 1030 self.assertEqual(loaded_pydoc.__file__, pydoc.__file__) 1031 self.assertEqual(loaded_pydoc.__spec__, pydoc.__spec__) 1032 1033 1034class TestDescriptions(unittest.TestCase): 1035 1036 def test_module(self): 1037 # Check that pydocfodder module can be described 1038 from test import pydocfodder 1039 doc = pydoc.render_doc(pydocfodder) 1040 self.assertIn("pydocfodder", doc) 1041 1042 def test_class(self): 1043 class C: "New-style class" 1044 c = C() 1045 1046 self.assertEqual(pydoc.describe(C), 'class C') 1047 self.assertEqual(pydoc.describe(c), 'C') 1048 expected = 'C in module %s object' % __name__ 1049 self.assertIn(expected, pydoc.render_doc(c)) 1050 1051 def test_typing_pydoc(self): 1052 def foo(data: typing.List[typing.Any], 1053 x: int) -> typing.Iterator[typing.Tuple[int, typing.Any]]: 1054 ... 1055 T = typing.TypeVar('T') 1056 class C(typing.Generic[T], typing.Mapping[int, str]): ... 1057 self.assertEqual(pydoc.render_doc(foo).splitlines()[-1], 1058 'f\x08fo\x08oo\x08o(data: List[Any], x: int)' 1059 ' -> Iterator[Tuple[int, Any]]') 1060 self.assertEqual(pydoc.render_doc(C).splitlines()[2], 1061 'class C\x08C(collections.abc.Mapping, typing.Generic)') 1062 1063 def test_builtin(self): 1064 for name in ('str', 'str.translate', 'builtins.str', 1065 'builtins.str.translate'): 1066 # test low-level function 1067 self.assertIsNotNone(pydoc.locate(name)) 1068 # test high-level function 1069 try: 1070 pydoc.render_doc(name) 1071 except ImportError: 1072 self.fail('finding the doc of {!r} failed'.format(name)) 1073 1074 for name in ('notbuiltins', 'strrr', 'strr.translate', 1075 'str.trrrranslate', 'builtins.strrr', 1076 'builtins.str.trrranslate'): 1077 self.assertIsNone(pydoc.locate(name)) 1078 self.assertRaises(ImportError, pydoc.render_doc, name) 1079 1080 @staticmethod 1081 def _get_summary_line(o): 1082 text = pydoc.plain(pydoc.render_doc(o)) 1083 lines = text.split('\n') 1084 assert len(lines) >= 2 1085 return lines[2] 1086 1087 @staticmethod 1088 def _get_summary_lines(o): 1089 text = pydoc.plain(pydoc.render_doc(o)) 1090 lines = text.split('\n') 1091 return '\n'.join(lines[2:]) 1092 1093 # these should include "self" 1094 def test_unbound_python_method(self): 1095 self.assertEqual(self._get_summary_line(textwrap.TextWrapper.wrap), 1096 "wrap(self, text)") 1097 1098 @requires_docstrings 1099 def test_unbound_builtin_method(self): 1100 self.assertEqual(self._get_summary_line(_pickle.Pickler.dump), 1101 "dump(self, obj, /)") 1102 1103 # these no longer include "self" 1104 def test_bound_python_method(self): 1105 t = textwrap.TextWrapper() 1106 self.assertEqual(self._get_summary_line(t.wrap), 1107 "wrap(text) method of textwrap.TextWrapper instance") 1108 def test_field_order_for_named_tuples(self): 1109 Person = namedtuple('Person', ['nickname', 'firstname', 'agegroup']) 1110 s = pydoc.render_doc(Person) 1111 self.assertLess(s.index('nickname'), s.index('firstname')) 1112 self.assertLess(s.index('firstname'), s.index('agegroup')) 1113 1114 class NonIterableFields: 1115 _fields = None 1116 1117 class NonHashableFields: 1118 _fields = [[]] 1119 1120 # Make sure these doesn't fail 1121 pydoc.render_doc(NonIterableFields) 1122 pydoc.render_doc(NonHashableFields) 1123 1124 @requires_docstrings 1125 def test_bound_builtin_method(self): 1126 s = StringIO() 1127 p = _pickle.Pickler(s) 1128 self.assertEqual(self._get_summary_line(p.dump), 1129 "dump(obj, /) method of _pickle.Pickler instance") 1130 1131 # this should *never* include self! 1132 @requires_docstrings 1133 def test_module_level_callable(self): 1134 self.assertEqual(self._get_summary_line(os.stat), 1135 "stat(path, *, dir_fd=None, follow_symlinks=True)") 1136 1137 @requires_docstrings 1138 def test_staticmethod(self): 1139 class X: 1140 @staticmethod 1141 def sm(x, y): 1142 '''A static method''' 1143 ... 1144 self.assertEqual(self._get_summary_lines(X.__dict__['sm']), 1145 'sm(x, y)\n' 1146 ' A static method\n') 1147 self.assertEqual(self._get_summary_lines(X.sm), """\ 1148sm(x, y) 1149 A static method 1150""") 1151 self.assertIn(""" 1152 | Static methods defined here: 1153 |\x20\x20 1154 | sm(x, y) 1155 | A static method 1156""", pydoc.plain(pydoc.render_doc(X))) 1157 1158 @requires_docstrings 1159 def test_classmethod(self): 1160 class X: 1161 @classmethod 1162 def cm(cls, x): 1163 '''A class method''' 1164 ... 1165 self.assertEqual(self._get_summary_lines(X.__dict__['cm']), 1166 'cm(...)\n' 1167 ' A class method\n') 1168 self.assertEqual(self._get_summary_lines(X.cm), """\ 1169cm(x) method of builtins.type instance 1170 A class method 1171""") 1172 self.assertIn(""" 1173 | Class methods defined here: 1174 |\x20\x20 1175 | cm(x) from builtins.type 1176 | A class method 1177""", pydoc.plain(pydoc.render_doc(X))) 1178 1179 @requires_docstrings 1180 def test_getset_descriptor(self): 1181 # Currently these attributes are implemented as getset descriptors 1182 # in CPython. 1183 self.assertEqual(self._get_summary_line(int.numerator), "numerator") 1184 self.assertEqual(self._get_summary_line(float.real), "real") 1185 self.assertEqual(self._get_summary_line(Exception.args), "args") 1186 self.assertEqual(self._get_summary_line(memoryview.obj), "obj") 1187 1188 @requires_docstrings 1189 def test_member_descriptor(self): 1190 # Currently these attributes are implemented as member descriptors 1191 # in CPython. 1192 self.assertEqual(self._get_summary_line(complex.real), "real") 1193 self.assertEqual(self._get_summary_line(range.start), "start") 1194 self.assertEqual(self._get_summary_line(slice.start), "start") 1195 self.assertEqual(self._get_summary_line(property.fget), "fget") 1196 self.assertEqual(self._get_summary_line(StopIteration.value), "value") 1197 1198 @requires_docstrings 1199 def test_slot_descriptor(self): 1200 class Point: 1201 __slots__ = 'x', 'y' 1202 self.assertEqual(self._get_summary_line(Point.x), "x") 1203 1204 @requires_docstrings 1205 def test_dict_attr_descriptor(self): 1206 class NS: 1207 pass 1208 self.assertEqual(self._get_summary_line(NS.__dict__['__dict__']), 1209 "__dict__") 1210 1211 @requires_docstrings 1212 def test_structseq_member_descriptor(self): 1213 self.assertEqual(self._get_summary_line(type(sys.hash_info).width), 1214 "width") 1215 self.assertEqual(self._get_summary_line(type(sys.flags).debug), 1216 "debug") 1217 self.assertEqual(self._get_summary_line(type(sys.version_info).major), 1218 "major") 1219 self.assertEqual(self._get_summary_line(type(sys.float_info).max), 1220 "max") 1221 1222 @requires_docstrings 1223 def test_namedtuple_field_descriptor(self): 1224 Box = namedtuple('Box', ('width', 'height')) 1225 self.assertEqual(self._get_summary_lines(Box.width), """\ 1226 Alias for field number 0 1227""") 1228 1229 @requires_docstrings 1230 def test_property(self): 1231 class Rect: 1232 @property 1233 def area(self): 1234 '''Area of the rect''' 1235 return self.w * self.h 1236 1237 self.assertEqual(self._get_summary_lines(Rect.area), """\ 1238 Area of the rect 1239""") 1240 self.assertIn(""" 1241 | area 1242 | Area of the rect 1243""", pydoc.plain(pydoc.render_doc(Rect))) 1244 1245 @requires_docstrings 1246 def test_custom_non_data_descriptor(self): 1247 class Descr: 1248 def __get__(self, obj, cls): 1249 if obj is None: 1250 return self 1251 return 42 1252 class X: 1253 attr = Descr() 1254 1255 self.assertEqual(self._get_summary_lines(X.attr), """\ 1256<test.test_pydoc.TestDescriptions.test_custom_non_data_descriptor.<locals>.Descr object>""") 1257 1258 X.attr.__doc__ = 'Custom descriptor' 1259 self.assertEqual(self._get_summary_lines(X.attr), """\ 1260<test.test_pydoc.TestDescriptions.test_custom_non_data_descriptor.<locals>.Descr object> 1261 Custom descriptor 1262""") 1263 1264 X.attr.__name__ = 'foo' 1265 self.assertEqual(self._get_summary_lines(X.attr), """\ 1266foo(...) 1267 Custom descriptor 1268""") 1269 1270 @requires_docstrings 1271 def test_custom_data_descriptor(self): 1272 class Descr: 1273 def __get__(self, obj, cls): 1274 if obj is None: 1275 return self 1276 return 42 1277 def __set__(self, obj, cls): 1278 1/0 1279 class X: 1280 attr = Descr() 1281 1282 self.assertEqual(self._get_summary_lines(X.attr), "") 1283 1284 X.attr.__doc__ = 'Custom descriptor' 1285 self.assertEqual(self._get_summary_lines(X.attr), """\ 1286 Custom descriptor 1287""") 1288 1289 X.attr.__name__ = 'foo' 1290 self.assertEqual(self._get_summary_lines(X.attr), """\ 1291foo 1292 Custom descriptor 1293""") 1294 1295 def test_async_annotation(self): 1296 async def coro_function(ign) -> int: 1297 return 1 1298 1299 text = pydoc.plain(pydoc.plaintext.document(coro_function)) 1300 self.assertIn('async coro_function', text) 1301 1302 html = pydoc.HTMLDoc().document(coro_function) 1303 self.assertIn( 1304 'async <a name="-coro_function"><strong>coro_function', 1305 html) 1306 1307 def test_async_generator_annotation(self): 1308 async def an_async_generator(): 1309 yield 1 1310 1311 text = pydoc.plain(pydoc.plaintext.document(an_async_generator)) 1312 self.assertIn('async an_async_generator', text) 1313 1314 html = pydoc.HTMLDoc().document(an_async_generator) 1315 self.assertIn( 1316 'async <a name="-an_async_generator"><strong>an_async_generator', 1317 html) 1318 1319 def test_html_for_https_links(self): 1320 def a_fn_with_https_link(): 1321 """a link https://localhost/""" 1322 pass 1323 1324 html = pydoc.HTMLDoc().document(a_fn_with_https_link) 1325 self.assertIn( 1326 '<a href="https://localhost/">https://localhost/</a>', 1327 html 1328 ) 1329 1330class PydocServerTest(unittest.TestCase): 1331 """Tests for pydoc._start_server""" 1332 1333 def test_server(self): 1334 1335 # Minimal test that starts the server, then stops it. 1336 def my_url_handler(url, content_type): 1337 text = 'the URL sent was: (%s, %s)' % (url, content_type) 1338 return text 1339 1340 serverthread = pydoc._start_server(my_url_handler, hostname='0.0.0.0', port=0) 1341 self.assertIn('0.0.0.0', serverthread.docserver.address) 1342 1343 starttime = time.monotonic() 1344 timeout = test.support.SHORT_TIMEOUT 1345 1346 while serverthread.serving: 1347 time.sleep(.01) 1348 if serverthread.serving and time.monotonic() - starttime > timeout: 1349 serverthread.stop() 1350 break 1351 1352 self.assertEqual(serverthread.error, None) 1353 1354 1355class PydocUrlHandlerTest(PydocBaseTest): 1356 """Tests for pydoc._url_handler""" 1357 1358 def test_content_type_err(self): 1359 f = pydoc._url_handler 1360 self.assertRaises(TypeError, f, 'A', '') 1361 self.assertRaises(TypeError, f, 'B', 'foobar') 1362 1363 def test_url_requests(self): 1364 # Test for the correct title in the html pages returned. 1365 # This tests the different parts of the URL handler without 1366 # getting too picky about the exact html. 1367 requests = [ 1368 ("", "Pydoc: Index of Modules"), 1369 ("get?key=", "Pydoc: Index of Modules"), 1370 ("index", "Pydoc: Index of Modules"), 1371 ("topics", "Pydoc: Topics"), 1372 ("keywords", "Pydoc: Keywords"), 1373 ("pydoc", "Pydoc: module pydoc"), 1374 ("get?key=pydoc", "Pydoc: module pydoc"), 1375 ("search?key=pydoc", "Pydoc: Search Results"), 1376 ("topic?key=def", "Pydoc: KEYWORD def"), 1377 ("topic?key=STRINGS", "Pydoc: TOPIC STRINGS"), 1378 ("foobar", "Pydoc: Error - foobar"), 1379 ] 1380 1381 with self.restrict_walk_packages(): 1382 for url, title in requests: 1383 self.call_url_handler(url, title) 1384 1385 1386class TestHelper(unittest.TestCase): 1387 def test_keywords(self): 1388 self.assertEqual(sorted(pydoc.Helper.keywords), 1389 sorted(keyword.kwlist)) 1390 1391class PydocWithMetaClasses(unittest.TestCase): 1392 @unittest.skipIf(sys.flags.optimize >= 2, 1393 "Docstrings are omitted with -O2 and above") 1394 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 1395 'trace function introduces __locals__ unexpectedly') 1396 def test_DynamicClassAttribute(self): 1397 class Meta(type): 1398 def __getattr__(self, name): 1399 if name == 'ham': 1400 return 'spam' 1401 return super().__getattr__(name) 1402 class DA(metaclass=Meta): 1403 @types.DynamicClassAttribute 1404 def ham(self): 1405 return 'eggs' 1406 expected_text_data_docstrings = tuple('\n | ' + s if s else '' 1407 for s in expected_data_docstrings) 1408 output = StringIO() 1409 helper = pydoc.Helper(output=output) 1410 helper(DA) 1411 expected_text = expected_dynamicattribute_pattern % ( 1412 (__name__,) + expected_text_data_docstrings[:2]) 1413 result = output.getvalue().strip() 1414 self.assertEqual(expected_text, result) 1415 1416 @unittest.skipIf(sys.flags.optimize >= 2, 1417 "Docstrings are omitted with -O2 and above") 1418 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 1419 'trace function introduces __locals__ unexpectedly') 1420 def test_virtualClassAttributeWithOneMeta(self): 1421 class Meta(type): 1422 def __dir__(cls): 1423 return ['__class__', '__module__', '__name__', 'LIFE'] 1424 def __getattr__(self, name): 1425 if name =='LIFE': 1426 return 42 1427 return super().__getattr(name) 1428 class Class(metaclass=Meta): 1429 pass 1430 output = StringIO() 1431 helper = pydoc.Helper(output=output) 1432 helper(Class) 1433 expected_text = expected_virtualattribute_pattern1 % __name__ 1434 result = output.getvalue().strip() 1435 self.assertEqual(expected_text, result) 1436 1437 @unittest.skipIf(sys.flags.optimize >= 2, 1438 "Docstrings are omitted with -O2 and above") 1439 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 1440 'trace function introduces __locals__ unexpectedly') 1441 def test_virtualClassAttributeWithTwoMeta(self): 1442 class Meta1(type): 1443 def __dir__(cls): 1444 return ['__class__', '__module__', '__name__', 'one'] 1445 def __getattr__(self, name): 1446 if name =='one': 1447 return 1 1448 return super().__getattr__(name) 1449 class Meta2(type): 1450 def __dir__(cls): 1451 return ['__class__', '__module__', '__name__', 'two'] 1452 def __getattr__(self, name): 1453 if name =='two': 1454 return 2 1455 return super().__getattr__(name) 1456 class Meta3(Meta1, Meta2): 1457 def __dir__(cls): 1458 return list(sorted(set( 1459 ['__class__', '__module__', '__name__', 'three'] + 1460 Meta1.__dir__(cls) + Meta2.__dir__(cls)))) 1461 def __getattr__(self, name): 1462 if name =='three': 1463 return 3 1464 return super().__getattr__(name) 1465 class Class1(metaclass=Meta1): 1466 pass 1467 class Class2(Class1, metaclass=Meta3): 1468 pass 1469 fail1 = fail2 = False 1470 output = StringIO() 1471 helper = pydoc.Helper(output=output) 1472 helper(Class1) 1473 expected_text1 = expected_virtualattribute_pattern2 % __name__ 1474 result1 = output.getvalue().strip() 1475 self.assertEqual(expected_text1, result1) 1476 output = StringIO() 1477 helper = pydoc.Helper(output=output) 1478 helper(Class2) 1479 expected_text2 = expected_virtualattribute_pattern3 % __name__ 1480 result2 = output.getvalue().strip() 1481 self.assertEqual(expected_text2, result2) 1482 1483 @unittest.skipIf(sys.flags.optimize >= 2, 1484 "Docstrings are omitted with -O2 and above") 1485 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 1486 'trace function introduces __locals__ unexpectedly') 1487 def test_buggy_dir(self): 1488 class M(type): 1489 def __dir__(cls): 1490 return ['__class__', '__name__', 'missing', 'here'] 1491 class C(metaclass=M): 1492 here = 'present!' 1493 output = StringIO() 1494 helper = pydoc.Helper(output=output) 1495 helper(C) 1496 expected_text = expected_missingattribute_pattern % __name__ 1497 result = output.getvalue().strip() 1498 self.assertEqual(expected_text, result) 1499 1500 def test_resolve_false(self): 1501 # Issue #23008: pydoc enum.{,Int}Enum failed 1502 # because bool(enum.Enum) is False. 1503 with captured_stdout() as help_io: 1504 pydoc.help('enum.Enum') 1505 helptext = help_io.getvalue() 1506 self.assertIn('class Enum', helptext) 1507 1508 1509class TestInternalUtilities(unittest.TestCase): 1510 1511 def setUp(self): 1512 tmpdir = tempfile.TemporaryDirectory() 1513 self.argv0dir = tmpdir.name 1514 self.argv0 = os.path.join(tmpdir.name, "nonexistent") 1515 self.addCleanup(tmpdir.cleanup) 1516 self.abs_curdir = abs_curdir = os.getcwd() 1517 self.curdir_spellings = ["", os.curdir, abs_curdir] 1518 1519 def _get_revised_path(self, given_path, argv0=None): 1520 # Checking that pydoc.cli() actually calls pydoc._get_revised_path() 1521 # is handled via code review (at least for now). 1522 if argv0 is None: 1523 argv0 = self.argv0 1524 return pydoc._get_revised_path(given_path, argv0) 1525 1526 def _get_starting_path(self): 1527 # Get a copy of sys.path without the current directory. 1528 clean_path = sys.path.copy() 1529 for spelling in self.curdir_spellings: 1530 for __ in range(clean_path.count(spelling)): 1531 clean_path.remove(spelling) 1532 return clean_path 1533 1534 def test_sys_path_adjustment_adds_missing_curdir(self): 1535 clean_path = self._get_starting_path() 1536 expected_path = [self.abs_curdir] + clean_path 1537 self.assertEqual(self._get_revised_path(clean_path), expected_path) 1538 1539 def test_sys_path_adjustment_removes_argv0_dir(self): 1540 clean_path = self._get_starting_path() 1541 expected_path = [self.abs_curdir] + clean_path 1542 leading_argv0dir = [self.argv0dir] + clean_path 1543 self.assertEqual(self._get_revised_path(leading_argv0dir), expected_path) 1544 trailing_argv0dir = clean_path + [self.argv0dir] 1545 self.assertEqual(self._get_revised_path(trailing_argv0dir), expected_path) 1546 1547 1548 def test_sys_path_adjustment_protects_pydoc_dir(self): 1549 def _get_revised_path(given_path): 1550 return self._get_revised_path(given_path, argv0=pydoc.__file__) 1551 clean_path = self._get_starting_path() 1552 leading_argv0dir = [self.argv0dir] + clean_path 1553 expected_path = [self.abs_curdir] + leading_argv0dir 1554 self.assertEqual(_get_revised_path(leading_argv0dir), expected_path) 1555 trailing_argv0dir = clean_path + [self.argv0dir] 1556 expected_path = [self.abs_curdir] + trailing_argv0dir 1557 self.assertEqual(_get_revised_path(trailing_argv0dir), expected_path) 1558 1559 def test_sys_path_adjustment_when_curdir_already_included(self): 1560 clean_path = self._get_starting_path() 1561 for spelling in self.curdir_spellings: 1562 with self.subTest(curdir_spelling=spelling): 1563 # If curdir is already present, no alterations are made at all 1564 leading_curdir = [spelling] + clean_path 1565 self.assertIsNone(self._get_revised_path(leading_curdir)) 1566 trailing_curdir = clean_path + [spelling] 1567 self.assertIsNone(self._get_revised_path(trailing_curdir)) 1568 leading_argv0dir = [self.argv0dir] + leading_curdir 1569 self.assertIsNone(self._get_revised_path(leading_argv0dir)) 1570 trailing_argv0dir = trailing_curdir + [self.argv0dir] 1571 self.assertIsNone(self._get_revised_path(trailing_argv0dir)) 1572 1573 1574def setUpModule(): 1575 thread_info = threading_helper.threading_setup() 1576 unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info) 1577 unittest.addModuleCleanup(reap_children) 1578 1579 1580if __name__ == "__main__": 1581 unittest.main() 1582