mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	Issue #3158: doctest can now find doctests in functions and methods
written in C. As a part of this, a few doctests have been added to the builtins module (on hex(), oct(), and bin()), a doctest has been fixed (hopefully on all platforms) on float, and test_builtins now runs doctests in builtins.
This commit is contained in:
		
							parent
							
								
									091167c1ca
								
							
						
					
					
						commit
						a4b7a7548c
					
				
					 7 changed files with 65 additions and 28 deletions
				
			
		|  | @ -278,6 +278,10 @@ strings are treated as if they were docstrings.  In output, a key ``K`` in | ||||||
| Any classes found are recursively searched similarly, to test docstrings in | Any classes found are recursively searched similarly, to test docstrings in | ||||||
| their contained methods and nested classes. | their contained methods and nested classes. | ||||||
| 
 | 
 | ||||||
|  | .. impl-detail:: | ||||||
|  |    Prior to version 3.4, extension modules written in C were not fully | ||||||
|  |    searched by doctest. | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| .. _doctest-finding-examples: | .. _doctest-finding-examples: | ||||||
| 
 | 
 | ||||||
|  | @ -1285,9 +1289,8 @@ DocTestFinder objects | ||||||
| 
 | 
 | ||||||
|    A processing class used to extract the :class:`DocTest`\ s that are relevant to |    A processing class used to extract the :class:`DocTest`\ s that are relevant to | ||||||
|    a given object, from its docstring and the docstrings of its contained objects. |    a given object, from its docstring and the docstrings of its contained objects. | ||||||
|    :class:`DocTest`\ s can currently be extracted from the following object types: |    :class:`DocTest`\ s can be extracted from modules, classes, functions, | ||||||
|    modules, functions, classes, methods, staticmethods, classmethods, and |    methods, staticmethods, classmethods, and properties. | ||||||
|    properties. |  | ||||||
| 
 | 
 | ||||||
|    The optional argument *verbose* can be used to display the objects searched by |    The optional argument *verbose* can be used to display the objects searched by | ||||||
|    the finder.  It defaults to ``False`` (no output). |    the finder.  It defaults to ``False`` (no output). | ||||||
|  |  | ||||||
|  | @ -918,6 +918,8 @@ def _from_module(self, module, object): | ||||||
|             return module is inspect.getmodule(object) |             return module is inspect.getmodule(object) | ||||||
|         elif inspect.isfunction(object): |         elif inspect.isfunction(object): | ||||||
|             return module.__dict__ is object.__globals__ |             return module.__dict__ is object.__globals__ | ||||||
|  |         elif inspect.ismethoddescriptor(object): | ||||||
|  |             return module.__name__ == object.__objclass__.__module__ | ||||||
|         elif inspect.isclass(object): |         elif inspect.isclass(object): | ||||||
|             return module.__name__ == object.__module__ |             return module.__name__ == object.__module__ | ||||||
|         elif hasattr(object, '__module__'): |         elif hasattr(object, '__module__'): | ||||||
|  | @ -950,7 +952,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen): | ||||||
|             for valname, val in obj.__dict__.items(): |             for valname, val in obj.__dict__.items(): | ||||||
|                 valname = '%s.%s' % (name, valname) |                 valname = '%s.%s' % (name, valname) | ||||||
|                 # Recurse to functions & classes. |                 # Recurse to functions & classes. | ||||||
|                 if ((inspect.isfunction(val) or inspect.isclass(val)) and |                 if ((inspect.isroutine(val) or inspect.isclass(val)) and | ||||||
|                     self._from_module(module, val)): |                     self._from_module(module, val)): | ||||||
|                     self._find(tests, val, valname, module, source_lines, |                     self._find(tests, val, valname, module, source_lines, | ||||||
|                                globs, seen) |                                globs, seen) | ||||||
|  | @ -962,9 +964,8 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen): | ||||||
|                     raise ValueError("DocTestFinder.find: __test__ keys " |                     raise ValueError("DocTestFinder.find: __test__ keys " | ||||||
|                                      "must be strings: %r" % |                                      "must be strings: %r" % | ||||||
|                                      (type(valname),)) |                                      (type(valname),)) | ||||||
|                 if not (inspect.isfunction(val) or inspect.isclass(val) or |                 if not (inspect.isroutine(val) or inspect.isclass(val) or | ||||||
|                         inspect.ismethod(val) or inspect.ismodule(val) or |                         inspect.ismodule(val) or isinstance(val, str)): | ||||||
|                         isinstance(val, str)): |  | ||||||
|                     raise ValueError("DocTestFinder.find: __test__ values " |                     raise ValueError("DocTestFinder.find: __test__ values " | ||||||
|                                      "must be strings, functions, methods, " |                                      "must be strings, functions, methods, " | ||||||
|                                      "classes, or modules: %r" % |                                      "classes, or modules: %r" % | ||||||
|  | @ -983,7 +984,7 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen): | ||||||
|                     val = getattr(obj, valname).__func__ |                     val = getattr(obj, valname).__func__ | ||||||
| 
 | 
 | ||||||
|                 # Recurse to methods, properties, and nested classes. |                 # Recurse to methods, properties, and nested classes. | ||||||
|                 if ((inspect.isfunction(val) or inspect.isclass(val) or |                 if ((inspect.isroutine(val) or inspect.isclass(val) or | ||||||
|                       isinstance(val, property)) and |                       isinstance(val, property)) and | ||||||
|                       self._from_module(module, val)): |                       self._from_module(module, val)): | ||||||
|                     valname = '%s.%s' % (name, valname) |                     valname = '%s.%s' % (name, valname) | ||||||
|  |  | ||||||
|  | @ -1592,21 +1592,10 @@ def test_baddecorator(self): | ||||||
|         data = 'The quick Brown fox Jumped over The lazy Dog'.split() |         data = 'The quick Brown fox Jumped over The lazy Dog'.split() | ||||||
|         self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) |         self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) | ||||||
| 
 | 
 | ||||||
| def test_main(verbose=None): | def load_tests(loader, tests, pattern): | ||||||
|     test_classes = (BuiltinTest, TestSorted) |     from doctest import DocTestSuite | ||||||
| 
 |     tests.addTest(DocTestSuite(builtins)) | ||||||
|     run_unittest(*test_classes) |     return tests | ||||||
| 
 |  | ||||||
|     # verify reference counting |  | ||||||
|     if verbose and hasattr(sys, "gettotalrefcount"): |  | ||||||
|         import gc |  | ||||||
|         counts = [None] * 5 |  | ||||||
|         for i in range(len(counts)): |  | ||||||
|             run_unittest(*test_classes) |  | ||||||
|             gc.collect() |  | ||||||
|             counts[i] = sys.gettotalrefcount() |  | ||||||
|         print(counts) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     test_main(verbose=True) |     unittest.main() | ||||||
|  |  | ||||||
|  | @ -644,6 +644,35 @@ def test_DocTestFinder(): r""" | ||||||
|     >>> test = doctest.DocTestFinder().find(f)[0] |     >>> test = doctest.DocTestFinder().find(f)[0] | ||||||
|     >>> [e.lineno for e in test.examples] |     >>> [e.lineno for e in test.examples] | ||||||
|     [1, 9, 12] |     [1, 9, 12] | ||||||
|  | 
 | ||||||
|  | Finding Doctests in Modules Not Written in Python | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  | DocTestFinder can also find doctests in most modules not written in Python. | ||||||
|  | We'll use builtins as an example, since it almost certainly isn't written in | ||||||
|  | plain ol' Python and is guaranteed to be available. | ||||||
|  | 
 | ||||||
|  |     >>> import builtins | ||||||
|  |     >>> tests = doctest.DocTestFinder().find(builtins) | ||||||
|  |     >>> len(tests) # how many objects checked for doctests | ||||||
|  |     794 | ||||||
|  |     >>> real_tests = [t for t in tests if len(t.examples) > 0] | ||||||
|  |     >>> len(real_tests) # how many objects actually have doctests | ||||||
|  |     8 | ||||||
|  |     >>> for t in real_tests: | ||||||
|  |     ...     print('{}  {}'.format(len(t.examples), t.name)) | ||||||
|  |     ... | ||||||
|  |     1  builtins.bin | ||||||
|  |     3  builtins.float.as_integer_ratio | ||||||
|  |     2  builtins.float.fromhex | ||||||
|  |     2  builtins.float.hex | ||||||
|  |     1  builtins.hex | ||||||
|  |     1  builtins.int | ||||||
|  |     2  builtins.int.bit_length | ||||||
|  |     1  builtins.oct | ||||||
|  | 
 | ||||||
|  | Note here that 'bin', 'oct', and 'hex' are functions; 'float.as_integer_ratio', | ||||||
|  | 'float.hex', and 'int.bit_length' are methods; 'float.fromhex' is a classmethod, | ||||||
|  | and 'int' is a type. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| def test_DocTestParser(): r""" | def test_DocTestParser(): r""" | ||||||
|  |  | ||||||
|  | @ -68,6 +68,9 @@ Core and Builtins | ||||||
| Library | Library | ||||||
| ------- | ------- | ||||||
| 
 | 
 | ||||||
|  | - Issue #3158: doctest can now find doctests in functions and methods | ||||||
|  |   written in C. | ||||||
|  | 
 | ||||||
| - Issue #13477: Added command line interface to the tarfile module. | - Issue #13477: Added command line interface to the tarfile module. | ||||||
|   Original patch by Berker Peksag. |   Original patch by Berker Peksag. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1417,7 +1417,7 @@ Create a floating-point number from a hexadecimal string.\n\ | ||||||
| >>> float.fromhex('0x1.ffffp10')\n\ | >>> float.fromhex('0x1.ffffp10')\n\ | ||||||
| 2047.984375\n\ | 2047.984375\n\ | ||||||
| >>> float.fromhex('-0x1p-1074')\n\ | >>> float.fromhex('-0x1p-1074')\n\ | ||||||
| -4.9406564584124654e-324"); | -5e-324"); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
|  |  | ||||||
|  | @ -350,7 +350,11 @@ builtin_bin(PyObject *self, PyObject *v) | ||||||
| PyDoc_STRVAR(bin_doc, | PyDoc_STRVAR(bin_doc, | ||||||
| "bin(number) -> string\n\
 | "bin(number) -> string\n\
 | ||||||
| \n\ | \n\ | ||||||
| Return the binary representation of an integer."); | Return the binary representation of an integer.\n\ | ||||||
|  | \n\ | ||||||
|  |    >>> bin(2796202)\n\ | ||||||
|  |    '0b1010101010101010101010'\n\ | ||||||
|  | "); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
|  | @ -1276,7 +1280,11 @@ builtin_hex(PyObject *self, PyObject *v) | ||||||
| PyDoc_STRVAR(hex_doc, | PyDoc_STRVAR(hex_doc, | ||||||
| "hex(number) -> string\n\
 | "hex(number) -> string\n\
 | ||||||
| \n\ | \n\ | ||||||
| Return the hexadecimal representation of an integer."); | Return the hexadecimal representation of an integer.\n\ | ||||||
|  | \n\ | ||||||
|  |    >>> hex(3735928559)\n\ | ||||||
|  |    '0xdeadbeef'\n\ | ||||||
|  | "); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
|  | @ -1476,7 +1484,11 @@ builtin_oct(PyObject *self, PyObject *v) | ||||||
| PyDoc_STRVAR(oct_doc, | PyDoc_STRVAR(oct_doc, | ||||||
| "oct(number) -> string\n\
 | "oct(number) -> string\n\
 | ||||||
| \n\ | \n\ | ||||||
| Return the octal representation of an integer."); | Return the octal representation of an integer.\n\ | ||||||
|  | \n\ | ||||||
|  |    >>> oct(342391)\n\ | ||||||
|  |    '0o1234567'\n\ | ||||||
|  | "); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Zachary Ware
						Zachary Ware