From 5b1795d1b61f8785f31c0ebbff22d2c0d635598c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 21 Apr 2019 00:07:07 +0200 Subject: [PATCH] documentation/python: support pybind11-style submodules. The usual detection fails there. Oh well. --- documentation/python.py | 17 ++++--- documentation/test_python/CMakeLists.txt | 6 +-- .../pybind_submodules/pybind_submodules.cpp | 8 +++ .../pybind_submodules/pybind_submodules.html | 51 +++++++++++++++++++ documentation/test_python/test_pybind.py | 10 ++++ 5 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 documentation/test_python/pybind_submodules/pybind_submodules.cpp create mode 100644 documentation/test_python/pybind_submodules/pybind_submodules.html diff --git a/documentation/python.py b/documentation/python.py index 872367d8..1e62a227 100755 --- a/documentation/python.py +++ b/documentation/python.py @@ -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)] diff --git a/documentation/test_python/CMakeLists.txt b/documentation/test_python/CMakeLists.txt index ac0550bd..c37d827d 100644 --- a/documentation/test_python/CMakeLists.txt +++ b/documentation/test_python/CMakeLists.txt @@ -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 index 00000000..9d01d2e0 --- /dev/null +++ b/documentation/test_python/pybind_submodules/pybind_submodules.cpp @@ -0,0 +1,8 @@ +#include + +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 index 00000000..11d4e98d --- /dev/null +++ b/documentation/test_python/pybind_submodules/pybind_submodules.html @@ -0,0 +1,51 @@ + + + + + pybind_submodules | My Python Project + + + + + +
+
+
+
+
+

+ pybind_submodules module +

+

pybind11 submodules

+
+

Contents

+ +
+
+

Modules

+
+
module another
+
Yay another
+
module sub
+
Yay a submodule
+
+
+
+
+
+
+ + diff --git a/documentation/test_python/test_pybind.py b/documentation/test_python/test_pybind.py index ec47be11..01caab8c 100644 --- a/documentation/test_python/test_pybind.py +++ b/documentation/test_python/test_pybind.py @@ -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')) -- 2.30.2