]> granicus.if.org Git - python/commitdiff
Issue #3158: doctest can now find doctests in functions and methods
authorZachary Ware <zachary.ware@gmail.com>
Sun, 24 Nov 2013 07:19:09 +0000 (01:19 -0600)
committerZachary Ware <zachary.ware@gmail.com>
Sun, 24 Nov 2013 07:19:09 +0000 (01:19 -0600)
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.

Doc/library/doctest.rst
Lib/doctest.py
Lib/test/test_builtin.py
Lib/test/test_doctest.py
Misc/NEWS
Objects/floatobject.c
Python/bltinmodule.c

index 6138e300746d276bee52b06b531e96d803a97e5c..28df49c7003600734b2d22d76f15c2ae4292b0df 100644 (file)
@@ -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
 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:
 
@@ -1285,9 +1289,8 @@ DocTestFinder objects
 
    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.
-   :class:`DocTest`\ s can currently be extracted from the following object types:
-   modules, functions, classes, methods, staticmethods, classmethods, and
-   properties.
+   :class:`DocTest`\ s can be extracted from modules, classes, functions,
+   methods, staticmethods, classmethods, and properties.
 
    The optional argument *verbose* can be used to display the objects searched by
    the finder.  It defaults to ``False`` (no output).
index 7836fd2ba40e9341fa87b30fbb50cf04a1a1622a..ee4e0687844ad6e8f079d7c08a51e83b293e0db7 100644 (file)
@@ -918,6 +918,8 @@ class DocTestFinder:
             return module is inspect.getmodule(object)
         elif inspect.isfunction(object):
             return module.__dict__ is object.__globals__
+        elif inspect.ismethoddescriptor(object):
+            return module.__name__ == object.__objclass__.__module__
         elif inspect.isclass(object):
             return module.__name__ == object.__module__
         elif hasattr(object, '__module__'):
@@ -950,7 +952,7 @@ class DocTestFinder:
             for valname, val in obj.__dict__.items():
                 valname = '%s.%s' % (name, valname)
                 # 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._find(tests, val, valname, module, source_lines,
                                globs, seen)
@@ -962,9 +964,8 @@ class DocTestFinder:
                     raise ValueError("DocTestFinder.find: __test__ keys "
                                      "must be strings: %r" %
                                      (type(valname),))
-                if not (inspect.isfunction(val) or inspect.isclass(val) or
-                        inspect.ismethod(val) or inspect.ismodule(val) or
-                        isinstance(val, str)):
+                if not (inspect.isroutine(val) or inspect.isclass(val) or
+                        inspect.ismodule(val) or isinstance(val, str)):
                     raise ValueError("DocTestFinder.find: __test__ values "
                                      "must be strings, functions, methods, "
                                      "classes, or modules: %r" %
@@ -983,7 +984,7 @@ class DocTestFinder:
                     val = getattr(obj, valname).__func__
 
                 # 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
                       self._from_module(module, val)):
                     valname = '%s.%s' % (name, valname)
index 2411c9b311cb8a259933c01a0e76657aeabe6d39..68430660f016abd4fefd4ba83b8540993f00e302 100644 (file)
@@ -1592,21 +1592,10 @@ class TestSorted(unittest.TestCase):
         data = 'The quick Brown fox Jumped over The lazy Dog'.split()
         self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0)
 
-def test_main(verbose=None):
-    test_classes = (BuiltinTest, TestSorted)
-
-    run_unittest(*test_classes)
-
-    # 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)
-
+def load_tests(loader, tests, pattern):
+    from doctest import DocTestSuite
+    tests.addTest(DocTestSuite(builtins))
+    return tests
 
 if __name__ == "__main__":
-    test_main(verbose=True)
+    unittest.main()
index d4ff049fca31fe9dfbee3b2ab1db572f4d47b735..d99a16a47928941d3c79f0eb278878bdf521852a 100644 (file)
@@ -644,6 +644,35 @@ DocTestFinder finds the line number of each example:
     >>> test = doctest.DocTestFinder().find(f)[0]
     >>> [e.lineno for e in test.examples]
     [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"""
index ddd9cf6926ff39a1dae50e4c05b387f9c4dfb11d..e24ece8ea071a85814559bfed5969d05be51169f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -68,6 +68,9 @@ Core and Builtins
 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.
   Original patch by Berker Peksag.
 
index abea975c59c38c17deea50400d1e18d61e4f8209..29c3b32763c27aaf57c30e22a8f91f7c2925897a 100644 (file)
@@ -1417,7 +1417,7 @@ Create a floating-point number from a hexadecimal string.\n\
 >>> float.fromhex('0x1.ffffp10')\n\
 2047.984375\n\
 >>> float.fromhex('-0x1p-1074')\n\
--4.9406564584124654e-324");
+-5e-324");
 
 
 static PyObject *
index 96ccd6422d29e6f215faf23bff093ea050142d2f..057ab47e974a2c305a2fd3bda6d80e79a445eff7 100644 (file)
@@ -350,7 +350,11 @@ builtin_bin(PyObject *self, PyObject *v)
 PyDoc_STRVAR(bin_doc,
 "bin(number) -> string\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 *
@@ -1276,7 +1280,11 @@ builtin_hex(PyObject *self, PyObject *v)
 PyDoc_STRVAR(hex_doc,
 "hex(number) -> string\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 *
@@ -1476,7 +1484,11 @@ builtin_oct(PyObject *self, PyObject *v)
 PyDoc_STRVAR(oct_doc,
 "oct(number) -> string\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 *