From 2dc3d9d3690b0e36fadea9d3b49eace28e8d3256 Mon Sep 17 00:00:00 2001 From: Perceval Wajsburt Date: Fri, 6 Jun 2025 09:18:28 +0200 Subject: [PATCH 1/3] feat: add fallback to support namespace packages --- .../python/_internal/handler.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/mkdocstrings_handlers/python/_internal/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py index 158d7dd..b7442e9 100644 --- a/src/mkdocstrings_handlers/python/_internal/handler.py +++ b/src/mkdocstrings_handlers/python/_internal/handler.py @@ -263,6 +263,9 @@ 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: + self.try_to_import(identifier, loader) + try: doc_object = self._modules_collection[identifier] except KeyError as error: @@ -278,6 +281,47 @@ 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 = () + doc_object = None + for part in identifier.split("."): + parts = parts + (part,) + sub_identifier = ".".join(parts) + spec = importlib.util.find_spec(".".join(parts)) + if spec 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") + origin = spec.origin + if origin is None: + # Pick the first loc that is accessible from here (ie is a children of cwd) + origin = next( + ( + loc + for loc in spec.submodule_search_locations + if loc.startswith(os.getcwd()) + ), + None, + ) + + 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. From 52abc9e3df1787a335069660a588a069976c6575 Mon Sep 17 00:00:00 2001 From: Perceval Wajsburt Date: Fri, 6 Jun 2025 11:42:07 +0200 Subject: [PATCH 2/3] fix: use fallback even when the top module has been imported --- .../python/_internal/handler.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/mkdocstrings_handlers/python/_internal/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py index b7442e9..0e051fc 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,8 +267,10 @@ 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: - self.try_to_import(identifier, loader) + 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] @@ -286,7 +292,6 @@ def try_to_import(self, identifier: str, loader): import importlib.util from pathlib import Path - parts = () doc_object = None for part in identifier.split("."): From b590263596bf50d62ccb3f0ce4cc2852317ffef6 Mon Sep 17 00:00:00 2001 From: Perceval Wajsburt Date: Tue, 10 Jun 2025 17:24:32 +0200 Subject: [PATCH 3/3] fix: improve fallback for references to modules installed in site-package --- .../python/_internal/handler.py | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/mkdocstrings_handlers/python/_internal/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py index 0e051fc..3246919 100644 --- a/src/mkdocstrings_handlers/python/_internal/handler.py +++ b/src/mkdocstrings_handlers/python/_internal/handler.py @@ -292,29 +292,41 @@ def try_to_import(self, identifier: str, loader): import importlib.util from pathlib import Path - parts = () - doc_object = None - for part in identifier.split("."): - parts = parts + (part,) - sub_identifier = ".".join(parts) - spec = importlib.util.find_spec(".".join(parts)) + 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: - 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") + continue origin = spec.origin - if origin is None: - # Pick the first loc that is accessible from here (ie is a children of cwd) + 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 loc.startswith(os.getcwd()) + 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, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy