Skip to content

Commit 8a8be35

Browse files
SilverRainZMiMoCodeDeepSeek
committed
refactor: Clean up registry APIs
- Expose ExtraContextRegistry with add() method, remove internal get/get_names - Add JinjaRegistry with add_filter() and add_extension() methods - Update Registry class with extra_context and jinja_env properties - Move default Jinja extensions to setup() for proper initialization - Update documentation and tests Co-authored-by: MiMoCode <mimo@xiaomi.com> Co-authored-by: DeepSeek <service@deepseek.com>
1 parent 3652e03 commit 8a8be35

10 files changed

Lines changed: 146 additions & 68 deletions

File tree

docs/api.rst

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Base Directive Classes
2929

3030
.. autoclass:: sphinxnotes.render.BaseContextDirective
3131
:show-inheritance:
32-
:members: process_pending_node, queue_pending_node, current_raw_data, current_context, current_template
32+
:members: process_pending_node, queue_pending_node, current_context, current_template
3333

3434
.. autoclass:: sphinxnotes.render.BaseDataDefineDirective
3535
:show-inheritance:
@@ -104,11 +104,21 @@ See :doc:`tmpl` for built-in extra-context names such as ``doc`` and
104104
:members:
105105
:undoc-members:
106106

107+
.. autoclass:: sphinxnotes.render.ExtraContextRegistry
108+
:members:
109+
:inherited-members:
110+
:undoc-members:
111+
:class-doc-from: both
112+
107113
Filters
108114
=======
109115

110116
.. autodecorator:: sphinxnotes.render.filter
111117

118+
.. autoclass:: sphinxnotes.render.JinjaRegistry
119+
:members:
120+
:undoc-members:
121+
112122
Data, Field and Schema
113123
======================
114124

@@ -131,11 +141,9 @@ Data, Field and Schema
131141
:members: name, attrs, content
132142
:undoc-members:
133143

134-
.. autoclass:: sphinxnotes.render.data.Registry
144+
.. autoclass:: sphinxnotes.render.DataRegistry
135145
:members:
136146

137-
.. autotype:: sphinxnotes.render.data.ByOptionStore
138-
139147
Registry
140148
========
141149

@@ -148,3 +156,7 @@ or add new extra context) by adding new items to
148156
.. autoclass:: sphinxnotes.render.Registry
149157

150158
.. autoproperty:: data
159+
160+
.. autoproperty:: extra_context
161+
162+
.. autoproperty:: jinja

src/sphinxnotes/render/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from . import meta
1313
from .data import (
14-
Registry as DataRegistry,
14+
DataRegistry,
1515
REGISTRY as DATA_REGISTRY,
1616
PlainValue,
1717
Value,
@@ -28,6 +28,8 @@
2828
extra_context,
2929
ExtraContext,
3030
ExtraContextRequest,
31+
ExtraContextRegistry,
32+
REGISTRY as EXTRA_CONTEXT_REGISTRY,
3133
)
3234
from .pipeline import BaseContextRole, BaseContextDirective
3335
from .sources import (
@@ -36,7 +38,7 @@
3638
BaseDataDefineDirective,
3739
StrictDataDefineDirective,
3840
)
39-
from .jinja import filter
41+
from .jinja import filter, JinjaRegistry, REGISTRY as JINJA_REGISTRY
4042

4143
if TYPE_CHECKING:
4244
from sphinx.application import Sphinx
@@ -45,6 +47,7 @@
4547
"""Python API for other Sphinx extensions."""
4648
__all__ = [
4749
'Registry',
50+
'DataRegistry',
4851
'PlainValue',
4952
'Value',
5053
'ValueWrapper',
@@ -58,6 +61,8 @@
5861
'ResolvedContext',
5962
'ExtraContext',
6063
'ExtraContextRequest',
64+
'ExtraContextRegistry',
65+
'EXTRA_CONTEXT_REGISTRY',
6166
'extra_context',
6267
'pending_node',
6368
'BaseContextRole',
@@ -67,6 +72,8 @@
6772
'BaseDataDefineDirective',
6873
'StrictDataDefineDirective',
6974
'filter',
75+
'JinjaRegistry',
76+
'JINJA_REGISTRY',
7077
]
7178

7279

@@ -77,6 +84,14 @@ class Registry:
7784
def data(self) -> DataRegistry:
7885
return DATA_REGISTRY
7986

87+
@property
88+
def extra_context(self) -> ExtraContextRegistry:
89+
return EXTRA_CONTEXT_REGISTRY
90+
91+
@property
92+
def jinja(self) -> JinjaRegistry:
93+
return JINJA_REGISTRY
94+
8095

8196
REGISTRY = Registry()
8297

src/sphinxnotes/render/ctxnodes.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,30 @@
2929
class pending_node(nodes.Element):
3030
"""A docutils node to be rendered."""
3131

32-
# The context to be rendered by Jinja template.
32+
#: The context to be rendered by Jinja template.
3333
ctx: UnresolvedContext | ResolvedContext
3434
#: Jinja template for rendering the context.
3535
template: Template
3636
#: Whether rendering to inline nodes.
3737
inline: bool
3838
#: Whether the rendering pipeline is finished (failed is also finished).
3939
rendered: bool
40-
#: Stored pickling error for later-phase unresolved context.
40+
41+
# Stored pickling error for later-phase unresolved context.
4142
_ctx_pickle_error: Exception | None
4243

44+
# Types for hook functions.
45+
type UnresolvedContextHook = Callable[[pending_node, UnresolvedContext], None]
46+
type ResolvedContextHook = Callable[[pending_node, ResolvedContext], None]
47+
type MarkupTextHook = Callable[[pending_node, str], str]
48+
type RenderedNodesHook = Callable[[pending_node, list[nodes.Node]], None]
49+
50+
# Hooks for processing render intermediate products.
51+
_unresolved_context_hooks: list[UnresolvedContextHook]
52+
_resolved_context_hooks: list[ResolvedContextHook]
53+
_markup_text_hooks: list[MarkupTextHook]
54+
_rendered_nodes_hooks: list[RenderedNodesHook]
55+
4356
def __init__(
4457
self,
4558
ctx: UnresolvedContext | ResolvedContext,
@@ -50,16 +63,18 @@ def __init__(
5063
**attributes,
5164
) -> None:
5265
super().__init__(rawsource, *children, **attributes)
66+
self.ctx = ctx
67+
self.template = tmpl
68+
self.inline = inline
69+
self.rendered = False
70+
71+
# Test whehter ctx pickle-able.
5372
self._ctx_pickle_error = None
5473
if isinstance(ctx, UnresolvedContext) and tmpl.phase != Phase.Parsing:
5574
try:
5675
pickle.dumps(ctx)
5776
except Exception as exc:
5877
self._ctx_pickle_error = exc
59-
self.ctx = ctx
60-
self.template = tmpl
61-
self.inline = inline
62-
self.rendered = False
6378

6479
# Init hook lists.
6580
self._unresolved_context_hooks = []
@@ -239,18 +254,6 @@ def unwrap_and_replace_self_inline(self, inliner: Report.Inliner) -> None:
239254
# Replace self with inline nodes.
240255
self.replace_self(ns)
241256

242-
"""Hooks for processing render intermediate products."""
243-
244-
type UnresolvedContextHook = Callable[[pending_node, UnresolvedContext], None]
245-
type ResolvedContextHook = Callable[[pending_node, ResolvedContext], None]
246-
type MarkupTextHook = Callable[[pending_node, str], str]
247-
type RenderedNodesHook = Callable[[pending_node, list[nodes.Node]], None]
248-
249-
_unresolved_context_hooks: list[UnresolvedContextHook]
250-
_resolved_context_hooks: list[ResolvedContextHook]
251-
_markup_text_hooks: list[MarkupTextHook]
252-
_rendered_nodes_hooks: list[RenderedNodesHook]
253-
254257
def hook_unresolved_context(self, hook: UnresolvedContextHook) -> None:
255258
self._unresolved_context_hooks.append(hook)
256259

src/sphinxnotes/render/data.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def _str_conv(v: str) -> str:
106106
return vv if isinstance(vv, str) else v
107107

108108

109-
class Registry:
109+
class DataRegistry:
110110
"""Stores supported element types and element forms (containers)."""
111111

112112
etypes: dict[str, type]
@@ -227,7 +227,7 @@ def add_by_option(
227227
in the DSL
228228
:param etype: The value type for this option
229229
:param default: The default value for this option
230-
:param store: How to store multiple values
230+
:param store: How to store multiple values, can be ``'assign'`` or ``'append'``
231231
:param aliases: Alternative names for this option
232232
233233
.. seealso:: :ref:`add-custom-by-options`
@@ -239,7 +239,7 @@ def add_by_option(
239239
self.byopts[alias] = opt
240240

241241

242-
REGISTRY = Registry()
242+
REGISTRY = DataRegistry()
243243

244244
# ======================
245245
# Data, Field and Schema

src/sphinxnotes/render/extractx.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,34 @@ def generate(self, req: ExtraContextRequest, *args, **kwargs) -> Any: ...
4141
# ==========================
4242

4343

44-
class _ExtraContextRegistry:
45-
ctxs: dict[str, ExtraContext]
44+
class ExtraContextRegistry:
45+
"""Registry for extra contexts."""
46+
47+
_ctxs: dict[str, ExtraContext]
4648

4749
def __init__(self) -> None:
48-
self.ctxs = {}
50+
self._ctxs = {}
4951

50-
def register(self, name: str, ctx: ExtraContext) -> None:
51-
if name in self.ctxs:
52-
raise ValueError(f'Extra context "{name}" already registered')
53-
self.ctxs[name] = ctx
52+
def add(self, name: str, ctx: ExtraContext) -> None:
53+
"""Register an extra context.
5454
55-
def get(self, name: str) -> ExtraContext | None:
56-
if name not in self.ctxs:
57-
return None
58-
return self.ctxs[name]
55+
:param name: The context name, used in templates via ``load_extra('name')``
56+
:param ctx: An :py:class:`ExtraContext` instance
57+
58+
.. note:: Using the :py:deco:`extra_context` decorator is recommended for most cases.
59+
"""
60+
if name in self._ctxs:
61+
raise ValueError(f'Extra context "{name}" already registered')
62+
self._ctxs[name] = ctx
5963

60-
def get_names(self) -> set[str]:
61-
return set(self.ctxs.keys())
6264

65+
REGISTRY = ExtraContextRegistry()
66+
"""The global registry for extra contexts.
6367
64-
_REGISTRY = _ExtraContextRegistry()
68+
This is the underlying registry used by the :py:func:`extra_context` decorator.
69+
Using the decorator is recommended for most cases, but you can also register
70+
extra contexts directly via this registry.
71+
"""
6572

6673

6774
def extra_context(name: str):
@@ -71,19 +78,19 @@ def extra_context(name: str):
7178
"""
7279

7380
def decorator(cls):
74-
_REGISTRY.register(name, cls())
81+
REGISTRY.add(name, cls())
7582
return cls
7683

7784
return decorator
7885

7986

8087
def extra_context_names() -> set[str]:
81-
return _REGISTRY.get_names()
88+
return set(REGISTRY._ctxs.keys())
8289

8390

8491
def extra_context_loader(request: ExtraContextRequest):
8592
def load_extra(name: str, *args, **kwargs) -> Any:
86-
ctx = _REGISTRY.get(name)
93+
ctx = REGISTRY._ctxs.get(name)
8794
if ctx is None:
8895
raise ValueError(
8996
f'Extra context "{name}" is not registered. '

0 commit comments

Comments
 (0)