chiark / gitweb /
documentation/python: warn when a type link should (but isn't) resolved.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 14 Jul 2019 12:54:36 +0000 (14:54 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Sun, 14 Jul 2019 17:11:09 +0000 (19:11 +0200)
Could help with figuring out what types were accidentally omitted from
the docs.

documentation/python.py
documentation/test_python/inspect_type_links/inspect_type_links.second.html
documentation/test_python/inspect_type_links/inspect_type_links/second.py
documentation/test_python/test_pybind.py

index ef94bf71162da6b63f2f7f8288ec4e73461bc75f..84df56e616b94fec5dd1338871b67fb6da7e1388 100755 (executable)
@@ -501,8 +501,19 @@ def make_name_link(state: State, referrer_path: List[str], type) -> str:
     if type is None: return None
     assert isinstance(type, str)
 
-    # Not found, return as-is
-    if not type in state.name_map: return type
+    # Not found, return as-is. However, if the prefix is one of the
+    # INPUT_MODULES, emit a warning to notify the user of a potentially missing
+    # stuff from the docs.
+    if not type in state.name_map:
+        for module in state.config['INPUT_MODULES']:
+            if isinstance(module, str):
+                module_name = module
+            else:
+                module_name = module.__name__
+            if type.startswith(module_name + '.'):
+                logging.warning("could not resolve a link to %s which is among INPUT_MODULES (referred from %s), possibly hidden/undocumented?", type, '.'.join(referrer_path))
+                break
+        return type
 
     entry = state.name_map[type]
 
index 2e2b6b2fda212f6322d766b7fd31844fd47346ca..cd07de9eedff2e0318699043162079c97e084944 100644 (file)
         <section id="functions">
           <h2><a href="#functions">Functions</a></h2>
           <dl class="m-doc">
+            <dt>
+              <span class="m-doc-wrap-bumper">def <a href="#type_cant_link" class="m-doc-self" id="type_cant_link">type_cant_link</a>(</span><span class="m-doc-wrap">a: inspect_type_links.second._Hidden)</span>
+            </dt>
+            <dd>Annotation linking to a type that&#x27;s a part of INPUT_MODULES but not known</dd>
             <dt>
               <span class="m-doc-wrap-bumper">def <a href="#type_default_values" class="m-doc-self" id="type_default_values">type_default_values</a>(</span><span class="m-doc-wrap">a: <a href="inspect_type_links.second.html#Enum" class="m-doc">Enum</a> = <a href="inspect_type_links.second.html#Enum-SECOND" class="m-doc">Enum.SECOND</a>,
               b: typing.Tuple[<a href="inspect_type_links.second.Foo.html" class="m-doc">Foo</a>] = (&lt;class &#x27;inspect_type_links.second.Foo&#x27;&gt;,),
index b77762cc2d4540c30487514d2dc3d318f737b81a..fbcad3e5e38653711966f6ca3b0714cfbf8f4f82 100644 (file)
@@ -71,6 +71,12 @@ def type_return_string_nested() -> 'Tuple[Foo, List[Enum], Any]':
 def type_return_string_invalid(a: Foo) -> 'FooBar':
     """A function with invalid return string type annotation"""
 
+class _Hidden:
+    pass
+
+def type_cant_link(a: _Hidden):
+    """Annotation linking to a type that's a part of INPUT_MODULES but not known"""
+
 def type_default_values(a: Enum = Enum.SECOND, b: Tuple[Foo] = (Foo, ), c: Foo = Foo()):
     """A function with default values, one enum, one tuple and the third nonrepresentable (yes, the tuple looks ugly)"""
 
index bb7a4da6a48949aa2ce3a50e20bbbe7756ca86f3..e27526945af96ae2060bd94a38fd62fe67c11aa5 100644 (file)
 #   DEALINGS IN THE SOFTWARE.
 #
 
+import copy
 import sys
 import unittest
 
-from python import State, parse_pybind_signature
+from python import State, parse_pybind_signature, default_config
 
 from . import BaseInspectTestCase
 
 class Signature(unittest.TestCase):
+    # make_type_link() needs state.config['INPUT_MODULES'], simply supply
+    # everything there
+    state = State(copy.deepcopy(default_config))
+
     def test(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int, a2: module.Thing) -> module.Thing3'),
             ('foo', '', [
                 ('a', 'int', 'int', None),
@@ -39,7 +44,7 @@ class Signature(unittest.TestCase):
             ], 'module.Thing3'))
 
     def test_newline(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int, a2: module.Thing) -> module.Thing3\n'),
             ('foo', '', [
                 ('a', 'int', 'int', None),
@@ -47,7 +52,7 @@ class Signature(unittest.TestCase):
             ], 'module.Thing3'))
 
     def test_docs(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int, a2: module.Thing) -> module.Thing3\n\nDocs here!!'),
             ('foo', 'Docs here!!', [
                 ('a', 'int', 'int', None),
@@ -55,19 +60,19 @@ class Signature(unittest.TestCase):
             ], 'module.Thing3'))
 
     def test_no_args(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'thingy() -> str'),
             ('thingy', '', [], 'str'))
 
     def test_no_return(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             '__init__(self: module.Thing)'),
             ('__init__', '', [
                 ('self', 'module.Thing', 'module.Thing', None),
             ], None))
 
     def test_no_arg_types(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'thingy(self, the_other_thing)'),
             ('thingy', '', [
                 ('self', None, None, None),
@@ -75,7 +80,7 @@ class Signature(unittest.TestCase):
             ], None))
 
     def test_square_brackets(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: Tuple[int, str], no_really: str) -> List[str]'),
             ('foo', '', [
                 ('a', 'Tuple[int, str]', 'Tuple[int, str]', None),
@@ -83,7 +88,7 @@ class Signature(unittest.TestCase):
             ], 'List[str]'))
 
     def test_nested_square_brackets(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: Tuple[int, List[Tuple[int, int]]], another: float) -> Union[str, Any]'),
             ('foo', '', [
                 ('a', 'Tuple[int, List[Tuple[int, int]]]', 'Tuple[int, List[Tuple[int, int]]]', None),
@@ -91,7 +96,7 @@ class Signature(unittest.TestCase):
             ], 'Union[str, Any]'))
 
     def test_callable(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: Callable[[int, Tuple[int, int]], float], another: float)'),
             ('foo', '', [
                 ('a', 'Callable[[int, Tuple[int, int]], float]', 'Callable[[int, Tuple[int, int]], float]', None),
@@ -99,7 +104,7 @@ class Signature(unittest.TestCase):
             ], None))
 
     def test_kwargs(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(*args, **kwargs)'),
             ('foo', '', [
                 ('*args', None, None, None),
@@ -109,7 +114,7 @@ class Signature(unittest.TestCase):
     # https://github.com/pybind/pybind11/commit/0826b3c10607c8d96e1d89dc819c33af3799a7b8,
     # released in 2.3.0. We want to support both, so test both.
     def test_default_values_pybind22(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: float=1.0, b: str=\'hello\')'),
             ('foo', '', [
                 ('a', 'float', 'float', '1.0'),
@@ -117,7 +122,7 @@ class Signature(unittest.TestCase):
             ], None))
 
     def test_default_values_pybind23(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: float = 1.0, b: str = \'hello\')'),
             ('foo', '', [
                 ('a', 'float', 'float', '1.0'),
@@ -125,42 +130,42 @@ class Signature(unittest.TestCase):
             ], None))
 
     def test_crazy_stuff(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int, b: Math::Vector<4, UnsignedInt>)'),
             ('foo', '', [('…', None, None, None)], None))
 
     def test_crazy_stuff_nested(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int, b: List[Math::Vector<4, UnsignedInt>])'),
             ('foo', '', [('…', None, None, None)], None))
 
     def test_crazy_stuff_docs(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int, b: Math::Vector<4, UnsignedInt>)\n\nThis is text!!'),
             ('foo', 'This is text!!', [('…', None, None, None)], None))
 
     def test_crazy_return(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int) -> Math::Vector<4, UnsignedInt>'),
             ('foo', '', [('…', None, None, None)], None))
 
     def test_crazy_return_nested(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int) -> List[Math::Vector<4, UnsignedInt>]'),
             ('foo', '', [('…', None, None, None)], None))
 
     def test_crazy_return_docs(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             'foo(a: int) -> Math::Vector<4, UnsignedInt>\n\nThis returns!'),
             ('foo', 'This returns!', [('…', None, None, None)], None))
 
     def test_no_name(self):
-        self.assertEqual(parse_pybind_signature(State({}), [],
+        self.assertEqual(parse_pybind_signature(self.state, [],
             '(arg0: MyClass) -> float'),
             ('', '', [('arg0', 'MyClass', 'MyClass', None)], 'float'))
 
     def test_module_mapping(self):
-        state = State({})
+        state = self.state
         state.module_mapping['module._module'] = 'module'
 
         self.assertEqual(parse_pybind_signature(state, [],