self.name_map: Dict[str, Empty] = {}
+ self.crawled: Set[object] = set()
+
def map_name_prefix(state: State, type: str) -> str:
for prefix, replace in state.module_mapping.items():
if type == prefix or type.startswith(prefix + '.'):
])
def crawl_class(state: State, path: List[str], class_):
+ assert inspect.isclass(class_)
+
+ # TODO: if this fires, it means there's a class duplicated in more than one
+ # __all__ (or it gets picked up implicitly and then in __all__) -- how to
+ # handle gracefully?
+ assert id(class_) not in state.crawled
+ state.crawled.add(id(class_))
+
class_entry = Empty()
class_entry.type = EntryType.CLASS
class_entry.object = class_
# Add itself to the name map
state.name_map['.'.join(path)] = class_entry
-def crawl_module(state: State, path: List[str], module):
+def crawl_module(state: State, path: List[str], module) -> List[Tuple[List[str], object]]:
+ assert inspect.ismodule(module)
+
+ # Assume this module is not crawled yet -- the parent crawl shouldn't even
+ # put it to members if it's crawled already. Otherwise add itself to
+ # the list of crawled objects to avoid going through it again.
+ assert id(module) not in state.crawled
+ state.crawled.add(id(module))
+
+ # This module isn't a duplicate, thus we can now safely add itself to
+ # parent's members (if there's a parent)
+ if len(path) > 1: state.name_map['.'.join(path[:-1])].members += [path[-1]]
+
module_entry = Empty()
module_entry.type = EntryType.MODULE
module_entry.object = module
module_entry.path = path
module_entry.members = []
+ # This gets returned to ensure the modules get processed in a breadth-first
+ # order
+ submodules_to_crawl: List[Tuple[List[str], object]] = []
+
# This is actually complicated -- if the module defines __all__, use that.
# The __all__ is meant to expose the public API, so we don't filter out
# underscored things.
logging.warning("unknown symbol %s in %s", name, '.'.join(path))
continue
elif type == EntryType.MODULE:
- crawl_module(state, subpath, object)
+ # TODO: this might fire if a module is in __all__ after it was
+ # picked up implicitly before -- how to handle gracefully?
+ assert id(object) not in state.crawled
+ submodules_to_crawl += [(subpath, object)]
+ # Not adding to module_entry.members, done by the nested
+ # crawl_module() itself if it is sure that it isn't a
+ # duplicated module
+ continue
elif type == EntryType.CLASS:
crawl_class(state, subpath, object)
else:
# Ignore unknown object types (with __all__ we warn instead)
continue
elif type == EntryType.MODULE:
- crawl_module(state, subpath, object)
+ submodules_to_crawl += [(subpath, object)]
+ # Not adding to module_entry.members, done by the nested
+ # crawl_module() itself if it is sure that it isn't a
+ # duplicated module
+ continue
elif type == EntryType.CLASS:
crawl_class(state, subpath, object)
else:
# Add itself to the name map
state.name_map['.'.join(path)] = module_entry
+ return submodules_to_crawl
+
_pybind_name_rx = re.compile('[a-zA-Z0-9_]*')
_pybind_arg_name_rx = re.compile('[*a-zA-Z0-9_]+')
_pybind_type_rx = re.compile('[a-zA-Z0-9_.]+')
for hook in state.hooks_pre_page: hook()
# Crawl all input modules to gather the name tree, put their names into a
- # list for the index
+ # list for the index. The crawl is done breadth-first, so the function
+ # returns a list of submodules to be crawled next.
class_index = []
+ modules_to_crawl = []
for module in config['INPUT_MODULES']:
if isinstance(module, str):
module_name = module
module = importlib.import_module(module)
else:
module_name = module.__name__
-
- crawl_module(state, [module_name], module)
+ modules_to_crawl += [([module_name], module)]
class_index += [module_name]
+ while modules_to_crawl:
+ path, object = modules_to_crawl.pop(0)
+ if id(object) in state.crawled: continue
+ modules_to_crawl += crawl_module(state, path, object)
# Add special pages to the name map. The pages are done after so they can
# override these.
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>inspect_recursive | 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>
+ inspect_recursive <span class="m-thin">module</span>
+ </h1>
+ <p>Recursive imports</p>
+ <div class="m-block m-default">
+ <h3>Contents</h3>
+ <ul>
+ <li>
+ Reference
+ <ul>
+ <li><a href="#packages">Modules</a></li>
+ <li><a href="#classes">Classes</a></li>
+ <li><a href="#functions">Functions</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <section id="namespaces">
+ <h2><a href="#namespaces">Modules</a></h2>
+ <dl class="m-doc">
+ <dt>module <a href="inspect_recursive.a.html" class="m-doc">a</a></dt>
+ <dd>Second module, imported as inspect_recursive.a, with no contents</dd>
+ <dt>module <a href="inspect_recursive.first.html" class="m-doc">first</a></dt>
+ <dd>First module, imported as inspect_recursive.first, with no contents</dd>
+ </dl>
+ </section>
+ <section id="classes">
+ <h2><a href="#classes">Classes</a></h2>
+ <dl class="m-doc">
+ <dt>class <a href="inspect_recursive.Foo.html" class="m-doc">Foo</a></dt>
+ <dd>A class</dd>
+ </dl>
+ </section>
+ <section id="functions">
+ <h2><a href="#functions">Functions</a></h2>
+ <dl class="m-doc">
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#bar" class="m-doc-self" id="bar">bar</a>(</span><span class="m-doc-wrap">) -> <a href="inspect_recursive.Foo.html" class="m-doc">Foo</a></span>
+ </dt>
+ <dd>Function that also returns Foo</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">def <a href="#foo" class="m-doc-self" id="foo">foo</a>(</span><span class="m-doc-wrap">) -> <a href="inspect_recursive.Foo.html" class="m-doc">Foo</a></span>
+ </dt>
+ <dd>Function that returns Foo</dd>
+ </dl>
+ </section>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>