|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +import inspect |
| 4 | +from docutils import nodes |
| 5 | +from sphinx.util import logging |
| 6 | +from sphinx.util.docutils import SphinxDirective # <--- New Base Class |
| 7 | + |
| 8 | +logger = logging.getLogger(__name__) |
| 9 | + |
| 10 | +from mrinufft._utils import MethodRegister |
| 11 | + |
| 12 | +import inspect |
| 13 | +import re |
| 14 | + |
| 15 | + |
| 16 | +def get_signature(func): |
| 17 | + """Safely extracts a clean function signature, without type annotations.""" |
| 18 | + sig = str(inspect.signature(func)) |
| 19 | + sig = sig.split("->")[0].strip() |
| 20 | + # iterative removal of bracketed expressions: |
| 21 | + while "[" in sig: |
| 22 | + sig = re.sub(r"\[[^\[\]]*\]", "", sig) |
| 23 | + sig = re.sub(r"\[.*?\]", "", sig) # remove complex type annotation with brackets |
| 24 | + sig = re.sub(r":\s*[^,=\)]+", " ", sig) # remove type annotations |
| 25 | + sig = re.sub(r"\s{2,}", " ", sig) # collapse multiple spaces |
| 26 | + sig = re.sub(r"\s,", ",", sig) # collapse multiple spaces |
| 27 | + sig = re.sub(r"\s=\s", "=", sig) # collapse multiple spaces |
| 28 | + return sig |
| 29 | + |
| 30 | + |
| 31 | +class AutoregistryDirective(SphinxDirective): # <--- Inherit from SphinxDirective |
| 32 | + """Directive to list all entries in a specified sub-registry key.""" |
| 33 | + |
| 34 | + required_arguments = 1 |
| 35 | + has_content = False |
| 36 | + |
| 37 | + # We now have access to self.env, self.app, and self.parse_text_to_nodes |
| 38 | + |
| 39 | + def run(self): |
| 40 | + registry_key = self.arguments[0] |
| 41 | + |
| 42 | + try: |
| 43 | + # 1. Build rendering context |
| 44 | + registry_dict = MethodRegister.registry[registry_key] |
| 45 | + items_context = [ |
| 46 | + { |
| 47 | + "name": name, |
| 48 | + "truename": ( |
| 49 | + func.__name__ if hasattr(func, "__name__") else str(func) |
| 50 | + ), |
| 51 | + "path": ( |
| 52 | + (func.__module__ + "." + func.__name__) |
| 53 | + if hasattr(func, "__module__") |
| 54 | + else str(func) |
| 55 | + ), |
| 56 | + "sig": get_signature(func), |
| 57 | + # ... (rest of your context building logic) ... |
| 58 | + } |
| 59 | + for name, func in registry_dict.items() |
| 60 | + ] |
| 61 | + |
| 62 | + context = { |
| 63 | + "registry_key": registry_key, |
| 64 | + "items": items_context, |
| 65 | + } |
| 66 | + |
| 67 | + # 2. Render template to RST string |
| 68 | + # self.app is available because we inherit from SphinxDirective |
| 69 | + rst_content = self.env.app.builder.templates.render( |
| 70 | + "autoregistry.rst", context |
| 71 | + ) |
| 72 | + |
| 73 | + # 3. THE FIX: Use the built-in utility |
| 74 | + # self.parse_text_to_nodes is a simple wrapper for nested_parse_to_nodes |
| 75 | + # that correctly passes the required RSTState (self.state). |
| 76 | + result_nodes = self.parse_text_to_nodes(rst_content) |
| 77 | + |
| 78 | + # The nodes are now fully parsed and contain pending_xref nodes |
| 79 | + # which Sphinx will automatically resolve later in the build process. |
| 80 | + # No manual env.resolve_references call is needed! |
| 81 | + return result_nodes |
| 82 | + |
| 83 | + except Exception as e: |
| 84 | + error_message = ( |
| 85 | + f"Error resolving autoregistry for key '{registry_key}': {e}" |
| 86 | + ) |
| 87 | + logger.error(error_message) |
| 88 | + return [nodes.literal_block(error_message, error_message)] |
| 89 | + |
| 90 | + |
| 91 | +# Delete resolve_autoregistry function |
| 92 | +def setup(app): |
| 93 | + """Main Sphinx extension entry point.""" |
| 94 | + app.add_directive("autoregistry", AutoregistryDirective) |
| 95 | + |
| 96 | + return { |
| 97 | + "version": "0.4", |
| 98 | + "parallel_read_safe": True, |
| 99 | + "parallel_write_safe": True, |
| 100 | + } |
0 commit comments