-
-
Notifications
You must be signed in to change notification settings - Fork 146
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
base: master
Are you sure you want to change the base?
Conversation
The original method of finding submodules by recursing through the directory structure didn't work with extension modules (aka native "C" extensions) because there is no directory structure to recurse through. This commit adds code to recurse through submodules by inspecting `obj.__all__`, i.e., using the same mechanism that is already used for finding variables, functions, and classes. This is a minimalistic first sketch of a solution. It works on a native extension that I tried it on but it introduces some duplication since there are now two strategies to search for submodules. For a native module, the first strategy (searching the directory structure) will fail but the second (new) strategy (using `obj.__all__`) will work. For regular modules, both strategies will likely work and so the second strategy will just overwrite the results from the first strategy with identical objects. This should technically work but it seems unnecessary. I just don't feel confident enough to remove the first strategy without further guidance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you construct a minimal, self-contained test with a module that has no path, but contains a reference to another module, and assert both modules are discovered? Like:
Line 42 in aef1917
EMPTY_MODULE = ModuleType('empty') |
For regular modules, the second strategy will just overwrite the results
The procedure actually skips modules that have the same name as one that was already defined:
Lines 734 to 737 in aef1917
for root in iter_modules(self.obj.__path__): | |
# Ignore if this module was already doc'd. | |
if root in self.doc: | |
continue |
Though this method seems unreliable.
Further, if I:
import .foo as bar
module foo
will now be be documented as foo
and as bar
... 🤔
- The submodule detection for extension modules (aka native "C" extensions) now runs only if the regular submodule detection didn't run. - Further, the submodule detection for extension modules runs only if submodules are explicitly exposed via the "__all__" attribute. This is is because it would otherwise be difficult or impossible to reliably detect cycles and/or reexports of external modules.
For portability reasons, this unit test only simulates how an extension model would look like. It doesn't actually package an extension module, because then we'd have to provide a separte extension module for each platform, os, and python version.
Thanks for the quick feedback! I've moved the new code into the Studying the existing code for "normal" submodule discovery, I realized I should be more careful to avoid capturing cycles and reexports (not sure if they're possible in native extension modules though). So I made sure the new code only runs if the parent module explicitly exposes public members through the
Is that possible with native extension modules? I know too little about Python's module system to tell. |
One problem I encountered: the HTML documentation for a native extension module now places all files in the top level directory. The contents of the files shows the correct nested hierarchy of submodules but the paths for the corresponding HTML files doesn't reflect this, so paths may clash if, e.g., a submodule has the same name as its parent module. Any idea how to fix this? |
456869b
to
94d26e8
Compare
Fixes #319
The original method of finding submodules by recursing through the
directory structure didn't work with extension modules (aka native "C"
extensions) because there is no directory structure to recurse through.
This commit adds code to recurse through submodules by inspecting
obj.__all__
, i.e., using the same mechanism that is already used forfinding variables, functions, and classes.
This is a minimalistic first sketch of a solution. It works on a native
extension that I tried it on but it introduces some duplication since
there are now two strategies to search for submodules. For a native
module, the first strategy (searching the directory structure) will fail
but the second (new) strategy (using
obj.__all__
) will work. Forregular modules, both strategies will likely work and so the second
strategy will just overwrite the results from the first strategy with
identical objects. This should technically work but it seems
unnecessary. I just don't feel confident enough to remove the first
strategy without further guidance.