chiark / gitweb /
documentation/python: adapt to slightly different internals in 3.6.
authorVladimír Vondruš <mosra@centrum.cz>
Sat, 20 Apr 2019 14:47:55 +0000 (16:47 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Mon, 22 Apr 2019 15:53:36 +0000 (17:53 +0200)
documentation/python.py
documentation/test_python/inspect_annotations/math36.html [new file with mode: 0644]
documentation/test_python/test_inspect.py

index cd38f8ab4e1ec068f4cdfb71e7e44dfdeaba2cb3..5901270405893fbe61bf18c16958bd11de54976b 100755 (executable)
@@ -41,6 +41,7 @@ from types import SimpleNamespace as Empty
 from importlib.machinery import SourceFileLoader
 from typing import Tuple, Dict, Any, List
 from urllib.parse import urljoin
+from distutils.version import LooseVersion
 
 import jinja2
 
@@ -147,11 +148,14 @@ def extract_annotation(annotation) -> str:
     # TODO: why this is not None directly?
     if annotation is inspect.Signature.empty: return None
 
-    # To avoid getting <class 'foo.bar'> for classes (and getting foo.bar
+    # To avoid getting <class 'foo.bar'> for types (and getting foo.bar
     # instead) but getting the actual type for types annotated with e.g.
-    # List[int], we need to branch on isclass()
-    if inspect.isclass(annotation): return extract_type(annotation)
-    return str(annotation)
+    # List[int], we need to check if the annotation is actually from the
+    # typing module or it's directly a type. In Python 3.7 this worked with
+    # inspect.isclass(annotation), but on 3.6 that gives True for annotations
+    # as well and then we would get just List instead of List[int].
+    if annotation.__module__ == 'typing': return str(annotation)
+    return extract_type(annotation)
 
 def render(config, template: str, page, env: jinja2.Environment):
     template = env.get_template(template)
@@ -404,9 +408,7 @@ def render_module(config, path, module, env):
 # to have in the docs, so filter them out. Uh... kinda ugly.
 _filtered_builtin_functions = set([
     ('__delattr__', "Implement delattr(self, name)."),
-    ('__dir__', "Default dir() implementation."),
     ('__eq__', "Return self==value."),
-    ('__format__', "Default object formatter."),
     ('__ge__', "Return self>=value."),
     ('__getattribute__', "Return getattr(self, name)."),
     ('__gt__', "Return self>value."),
@@ -421,11 +423,8 @@ _filtered_builtin_functions = set([
     ('__ne__', "Return self!=value."),
     ('__new__',
         "Create and return a new object.  See help(type) for accurate signature."),
-    ('__reduce__', "Helper for pickle."),
-    ('__reduce_ex__', "Helper for pickle."),
     ('__repr__', "Return repr(self)."),
     ('__setattr__', "Implement setattr(self, name, value)."),
-    ('__sizeof__', "Size of object in memory, in bytes."),
     ('__str__', "Return str(self)."),
     ('__subclasshook__',
         "Abstract classes can override this to customize issubclass().\n\n"
@@ -435,6 +434,24 @@ _filtered_builtin_functions = set([
         "overrides the normal algorithm (and the outcome is cached).\n")
 ])
 
+# Python 3.6 has slightly different docstrings than 3.7
+if LooseVersion(sys.version) >= LooseVersion("3.7"):
+    _filtered_builtin_functions.update({
+        ('__dir__', "Default dir() implementation."),
+        ('__format__', "Default object formatter."),
+        ('__reduce__', "Helper for pickle."),
+        ('__reduce_ex__', "Helper for pickle."),
+        ('__sizeof__', "Size of object in memory, in bytes."),
+    })
+else:
+    _filtered_builtin_functions.update({
+        ('__dir__', "__dir__() -> list\ndefault dir() implementation"),
+        ('__format__', "default object formatter"),
+        ('__reduce__', "helper for pickle"),
+        ('__reduce_ex__', "helper for pickle"),
+        ('__sizeof__', "__sizeof__() -> int\nsize of object in memory, in bytes")
+    })
+
 _filtered_builtin_properties = set([
     ('__weakref__', "list of weak references to the object (if defined)")
 ])
diff --git a/documentation/test_python/inspect_annotations/math36.html b/documentation/test_python/inspect_annotations/math36.html
new file mode 100644 (file)
index 0000000..f8cf25d
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>math | My Python Project</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+  <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+    </div>
+  </div>
+</nav></header>
+<main><article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>
+          math <span class="m-thin">module</span>
+        </h1>
+        <p>This module is always available.  It provides access to the
+mathematical functions defined by the C standard.</p>
+        <div class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#functions">Functions</a></li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+        <section id="functions">
+          <h2><a href="#functions">Functions</a></h2>
+          <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="" class="m-doc-self">log</a>(</span><span class="m-doc-wrap">...)</span>
+            </dt>
+            <dd>log(x[, base])</dd>
+          </dl>
+        </section>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
index 09a34a30e6c301291fcc277be1af133019bd40de..e483e76f563d72bfcd1a8994f865d4d80758501f 100644 (file)
 #   DEALINGS IN THE SOFTWARE.
 #
 
+import math
 import os
 import sys
-import math
+import unittest
+
+from distutils.version import LooseVersion
 
 from . import BaseTestCase
 
@@ -75,6 +78,8 @@ class Annotations(BaseTestCase):
         self.assertEqual(*self.actual_expected_contents('inspect_annotations.html'))
         self.assertEqual(*self.actual_expected_contents('inspect_annotations.Foo.html'))
 
+    @unittest.skipUnless(LooseVersion(sys.version) >= LooseVersion('3.7'),
+        "signature with / for pow() is not present in 3.6")
     def test_math(self):
         # From math export only pow() so we have the verification easier, and
         # in addition log() because it doesn't provide any signature metadata
@@ -89,3 +94,20 @@ class Annotations(BaseTestCase):
         assert not hasattr(math, '__all__')
 
         self.assertEqual(*self.actual_expected_contents('math.html'))
+
+    @unittest.skipUnless(LooseVersion(sys.version) < LooseVersion('3.7'),
+        "docstring for log() is different in 3.7")
+    def test_math36(self):
+        # From math export only pow() so we have the verification easier, and
+        # in addition log() because it doesn't provide any signature metadata
+        assert not hasattr(math, '__all__')
+        math.__all__ = ['log']
+
+        self.run_python({
+            'INPUT_MODULES': [math]
+        })
+
+        del math.__all__
+        assert not hasattr(math, '__all__')
+
+        self.assertEqual(*self.actual_expected_contents('math.html', 'math36.html'))