chiark / gitweb /
documentation/python: support pybind11-style submodules.
authorVladimír Vondruš <mosra@centrum.cz>
Sat, 20 Apr 2019 22:07:07 +0000 (00:07 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Mon, 22 Apr 2019 15:53:36 +0000 (17:53 +0200)
The usual detection fails there. Oh well.

documentation/python.py
documentation/test_python/CMakeLists.txt
documentation/test_python/pybind_submodules/pybind_submodules.cpp [new file with mode: 0644]
documentation/test_python/pybind_submodules/pybind_submodules.html [new file with mode: 0644]
documentation/test_python/test_pybind.py

index 872367d8377da12748cd8f588ee3dfa98ca375ce..1e62a227f7949268141b8a9d030d2af1447bc83c 100755 (executable)
@@ -113,7 +113,7 @@ def is_internal_function_name(name: str) -> bool:
     """
     return name.startswith('_') and not (name.startswith('__') and name.endswith('__'))
 
-def is_internal_or_imported_module_member(parent, path: str, name: str, object) -> bool:
+def is_internal_or_imported_module_member(state: State, parent, path: str, name: str, object) -> bool:
     """If the module member is internal or imported."""
 
     if name.startswith('_'): return True
@@ -135,6 +135,11 @@ def is_internal_or_imported_module_member(parent, path: str, name: str, object)
     # handle modules and packages differently. See also for more info:
     # https://stackoverflow.com/a/7948672
     else:
+        # pybind11 submodules have __package__ set to None for nested modules,
+        # the top-level __package__ is '' though. Allow these if parent
+        # __package__ is empty (either '' or None).
+        if state.config['PYBIND11_COMPATIBILITY'] and object.__package__ is None and not parent.__package__: return False
+
         # The parent is a single-file module (not a package), these don't have
         # submodules so this is most definitely an imported module. Source:
         # https://docs.python.org/3/reference/import.html#packages
@@ -404,7 +409,7 @@ def render_module(state: State, path, module, env):
     else:
         # Get (and render) inner modules
         for name, object in inspect.getmembers(module, inspect.ismodule):
-            if is_internal_or_imported_module_member(module, path, name, object): continue
+            if is_internal_or_imported_module_member(state, module, path, name, object): continue
 
             subpath = path + [name]
             page.modules += [extract_module_doc(subpath, object)]
@@ -412,7 +417,7 @@ def render_module(state: State, path, module, env):
 
         # Get (and render) inner classes
         for name, object in inspect.getmembers(module, lambda o: inspect.isclass(o) and not is_enum(state, o)):
-            if is_internal_or_imported_module_member(module, path, name, object): continue
+            if is_internal_or_imported_module_member(state, module, path, name, object): continue
 
             subpath = path + [name]
             if not object.__doc__: logging.warning("%s is undocumented", '.'.join(subpath))
@@ -422,7 +427,7 @@ def render_module(state: State, path, module, env):
 
         # Get enums
         for name, object in inspect.getmembers(module, lambda o: is_enum(state, o)):
-            if is_internal_or_imported_module_member(module, path, name, object): continue
+            if is_internal_or_imported_module_member(state, module, path, name, object): continue
 
             subpath = path + [name]
             if not object.__doc__: logging.warning("%s is undocumented", '.'.join(subpath))
@@ -433,7 +438,7 @@ def render_module(state: State, path, module, env):
 
         # Get inner functions
         for name, object in inspect.getmembers(module, lambda o: inspect.isfunction(o) or inspect.isbuiltin(o)):
-            if is_internal_or_imported_module_member(module, path, name, object): continue
+            if is_internal_or_imported_module_member(state, module, path, name, object): continue
 
             subpath = path + [name]
             if not object.__doc__: logging.warning("%s() is undocumented", '.'.join(subpath))
@@ -443,7 +448,7 @@ def render_module(state: State, path, module, env):
         # Get data
         # TODO: unify this query
         for name, object in inspect.getmembers(module, lambda o: not inspect.ismodule(o) and not inspect.isclass(o) and not inspect.isroutine(o) and not inspect.isframe(o) and not inspect.istraceback(o) and not inspect.iscode(o)):
-            if is_internal_or_imported_module_member(module, path, name, object): continue
+            if is_internal_or_imported_module_member(state, module, path, name, object): continue
 
             page.data += [extract_data_doc(module, path + [name], object)]
 
index ac0550bd9e5b8b99d7de1441101808f412167eb6..c37d827d3939bafe400acee66a7bf9e81588adf5 100644 (file)
@@ -27,7 +27,7 @@ project(McssDocumentationPybindTests)
 
 find_package(pybind11 CONFIG REQUIRED)
 
-foreach(target pybind_enums)
-    pybind11_add_module(${target} ${target}/${target}.cpp)
-    set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${target})
+foreach(target enums submodules)
+    pybind11_add_module(pybind_${target} pybind_${target}/pybind_${target}.cpp)
+    set_target_properties(pybind_${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/pybind_${target})
 endforeach()
diff --git a/documentation/test_python/pybind_submodules/pybind_submodules.cpp b/documentation/test_python/pybind_submodules/pybind_submodules.cpp
new file mode 100644 (file)
index 0000000..9d01d2e
--- /dev/null
@@ -0,0 +1,8 @@
+#include <pybind11/pybind11.h>
+
+PYBIND11_MODULE(pybind_submodules, m) {
+    m.doc() = "pybind11 submodules";
+
+    m.def_submodule("sub", "Yay a submodule");
+    m.def_submodule("another", "Yay another");
+}
diff --git a/documentation/test_python/pybind_submodules/pybind_submodules.html b/documentation/test_python/pybind_submodules/pybind_submodules.html
new file mode 100644 (file)
index 0000000..11d4e98
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>pybind_submodules | 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>
+          pybind_submodules <span class="m-thin">module</span>
+        </h1>
+        <p>pybind11 submodules</p>
+        <div class="m-block m-default">
+          <h3>Contents</h3>
+          <ul>
+            <li>
+              Reference
+              <ul>
+                <li><a href="#packages">Modules</a></li>
+              </ul>
+            </li>
+          </ul>
+        </div>
+        <section id="namespaces">
+          <h2><a href="#namespaces">Modules</a></h2>
+          <dl class="m-doc">
+            <dt>module <a href="pybind_submodules.another.html" class="m-doc">another</a></dt>
+            <dd>Yay another</dd>
+            <dt>module <a href="pybind_submodules.sub.html" class="m-doc">sub</a></dt>
+            <dd>Yay a submodule</dd>
+          </dl>
+        </section>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
index ec47be119caae668071035b974f1df059ba436ae..01caab8c92c2e5c19c99f60414111c9ed4a041e6 100644 (file)
@@ -33,3 +33,13 @@ class Enums(BaseTestCase):
             'PYBIND11_COMPATIBILITY': True
         })
         self.assertEqual(*self.actual_expected_contents('pybind_enums.html'))
+
+class Submodules(BaseTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, 'submodules', *args, **kwargs)
+
+    def test(self):
+        self.run_python({
+            'PYBIND11_COMPATIBILITY': True
+        })
+        self.assertEqual(*self.actual_expected_contents('pybind_submodules.html'))