Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recurse through submodules in extension modules #318

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions pdoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,24 @@ def iter_modules(paths):
if m.is_namespace and not m.doc:
del self.doc[root]
self._context.pop(m.refname)
elif hasattr(self.obj, '__all__'):
# Python extension modules don't get recognized by `is_package` because they have no
# "__path__" attribute. We treat them here separately. We support submodules of
# extension modules only if they are explicitly exposed via the "__all__" attribute
# because otherwise it's hard to distinguish proper submodules from re-exports (i.e.,
# the function `is_from_this_module` doesn't work on submodules).
for name, obj in public_objs:
if inspect.ismodule(obj) and not hasattr(obj, '__file__') and name not in self.doc:
try:
m = Module(
obj, docfilter=docfilter, supermodule=self,
context=self._context, skip_errors=skip_errors)
except Exception as ex:
if skip_errors:
warn(str(ex), Module.ImportWarning)
continue
raise
self.doc[name] = m

# Apply docfilter
if docfilter:
Expand Down
29 changes: 29 additions & 0 deletions pdoc/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,35 @@ def test_module(self):
self.assertEqual(sorted(m.name for m in m.submodules()),
[EXAMPLE_MODULE + '.' + m for m in submodules])

@ignore_warnings
def test_module_without_path(self):
# GH-319: https://github.com/pdoc3/pdoc/issues/319
parent_module = ModuleType('parent_module')
child_module1 = ModuleType('child_module1')
child_module2 = ModuleType('child_module2')
grandchild_module = ModuleType('grandchild_module')

child_module1.grandchild_module = grandchild_module
child_module1.__all__ = ['grandchild_module']

parent_module.child_module1 = child_module1
parent_module.child_module2 = child_module2
parent_module.__all__ = ['child_module1', 'child_module2']

assert not hasattr(parent_module, '__path__')
assert not hasattr(child_module1, '__path__')
assert not hasattr(child_module2, '__path__')
assert not hasattr(grandchild_module, '__path__')

parent_module_pdoc = pdoc.Module(parent_module)

children_modules_pdoc = sorted(parent_module_pdoc.submodules(), key=lambda m: m.name)
self.assertEqual(
[m.name for m in children_modules_pdoc], ['child_module1', 'child_module2'])
self.assertEqual(
[m.name for m in children_modules_pdoc[0].submodules()], ['grandchild_module'])
self.assertEqual(children_modules_pdoc[1].submodules(), [])

def test_Module_find_class(self):
class A:
pass
Expand Down