diff --git a/src/mkdocstrings_handlers/python/_internal/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py index 158d7dd..3246919 100644 --- a/src/mkdocstrings_handlers/python/_internal/handler.py +++ b/src/mkdocstrings_handlers/python/_internal/handler.py @@ -228,9 +228,9 @@ def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: parser = parser_name and Parser(parser_name) parser_options = options.docstring_options and asdict(options.docstring_options) - if unknown_module: + def make_loader(): extensions = self.normalize_extension_paths(options.extensions) - loader = GriffeLoader( + return GriffeLoader( extensions=load_extensions(*extensions), search_paths=self._paths, docstring_parser=parser, @@ -240,6 +240,10 @@ def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: allow_inspection=options.allow_inspection, force_inspection=options.force_inspection, ) + + loader = None + if unknown_module: + loader = make_loader() try: for pre_loaded_module in options.preload_modules: if pre_loaded_module not in self._modules_collection: @@ -263,6 +267,11 @@ def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: _logger.debug(f"{len(unresolved)} aliases were still unresolved after {iterations} iterations") _logger.debug(f"Unresolved aliases: {', '.join(sorted(unresolved))}") + if identifier not in self._modules_collection: + if loader is None: + loader = make_loader() + self.try_to_import(identifier, loader) + try: doc_object = self._modules_collection[identifier] except KeyError as error: @@ -278,6 +287,58 @@ def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: return doc_object + + def try_to_import(self, identifier: str, loader): + import importlib.util + from pathlib import Path + + parts = identifier.split(".") + origins = [None] * len(parts) + child_origin = None + for i in range(len(parts) - 1, -1, -1): + sub_identifier = ".".join(parts[:i + 1]) + spec = importlib.util.find_spec(sub_identifier) + if spec is None: + continue + origin = spec.origin + if origin is None and child_origin is not None: + # Pick the first loc that is a parent of child_origin + origin = next( + ( + loc + for loc in spec.submodule_search_locations + if child_origin.startswith(loc) + ), + None, + ) + origins[i] = origin + if origin is not None: + child_origin = origin + + parts = () + doc_object = None + for i, part in enumerate(identifier.split(".")): + parts = parts + (part,) + sub_identifier = ".".join(parts) + origin = origins[i] + if origin is None: + if doc_object is not None and part in doc_object.members: + # If we already have a doc_object, we can just use it + doc_object = doc_object.get_member(part) + continue + raise CollectionError(f"Module {identifier} could not be found") + + doc_object = loader._load_module( + part, + Path(origin), + parent=doc_object, + ) + try: + self._modules_collection.set_member(sub_identifier, doc_object) + except: + pass + return doc_object + def render(self, data: CollectorItem, options: PythonOptions) -> str: """Render the collected data.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: