Skip to content

Commit 3d69eeb

Browse files
authored
refactor: Load extra context on demand (#12)
* refactor: Load extra context on demand * refactor: Inject template globals explicitly * chore * refactor: Replace extra context loader with closure * docs: Clarify ExtraContextRequest.host * update * refactor: Shorten extra context request args * chore: make fmt * refactor: Restrict section extra context phases * docs: Minor docs changes
1 parent a0f898f commit 3d69eeb

21 files changed

Lines changed: 199 additions & 309 deletions

File tree

docs/api.rst

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,16 @@ Extra Context
9292
=============
9393

9494
See :doc:`tmpl` for built-in extra-context names such as ``doc`` and
95-
``sphinx``, plus usage examples.
95+
``env``, plus usage examples.
9696

9797
.. autodecorator:: sphinxnotes.render.extra_context
9898

99-
.. autoclass:: sphinxnotes.render.ParsingPhaseExtraContext
100-
:members: phase, generate
99+
.. autoclass:: sphinxnotes.render.ExtraContext
100+
:members: generate
101101
:undoc-members:
102102

103-
.. autoclass:: sphinxnotes.render.ParsedPhaseExtraContext
104-
:members: phase, generate
105-
:undoc-members:
106-
107-
.. autoclass:: sphinxnotes.render.ResolvingPhaseExtraContext
108-
:members: phase, generate
109-
:undoc-members:
110-
111-
.. autoclass:: sphinxnotes.render.GlobalExtraContext
112-
:members: phase, generate
103+
.. autoclass:: sphinxnotes.render.ExtraContextRequest
104+
:members:
113105
:undoc-members:
114106

115107
Filters

docs/conf.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,5 @@ The extension provides the following configuration:
2525
- ``text`` (str): the Jinja2 template text.
2626
- ``on`` (str, optional): same as :rst:dir:`data.template:on`
2727
- ``debug`` (bool, optional): same as :rst:dir:`data.template:debug`
28-
- ``extra`` (list[str], optional): same as :rst:dir:`data.template:extra`
2928

3029
See :ref:`usage-custom-directive` for example.
31-

docs/ext.rst

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,8 @@ Extending Extra Contexts
6969
Extra contexts are registered by a
7070
:py:deco:`sphinxnotes.render.extra_context` class decorator.
7171

72-
The decorated class must be one of the following classes:
73-
:py:class:`~sphinxnotes.render.ParsingPhaseExtraContext`,
74-
:py:class:`~sphinxnotes.render.ParsedPhaseExtraContext`,
75-
:py:class:`~sphinxnotes.render.ResolvingPhaseExtraContext`,
76-
:py:class:`~sphinxnotes.render.GlobalExtraContext`.
72+
The decorated class must be a subclass of
73+
:py:class:`~sphinxnotes.render.ExtraContext`.
7774

7875
.. literalinclude:: ../tests/roots/test-extra-context/conf.py
7976
:language: python
@@ -88,7 +85,6 @@ The decorated class must be one of the following classes:
8885
:style: grid
8986

9087
.. data.render::
91-
:extra: cat
9288
9389
{{ load_extra('cat').name }}
9490

docs/tmpl.rst

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,13 @@ sources (such as Sphinx application, JSON file, and etc.). Unlike main context
158158
which comes from the directive/role itself, extra context lets you fetch data
159159
that was prepared beforehand.
160160

161-
Extra contexts are typically generated on demand at different construction stages,
162-
so you need to declare them in advance, and load it in the template using the
163-
``load_extra()`` function:
164-
165-
The way of declaring extra context is vary depending on the extension you use.
166-
For ``sphinxnotes.render.ext`` extension, :rst:dir:`data.template:extra`,
167-
:rst:dir:`data.render:extra` and the ``templat.extra`` field of
168-
:confval:`render_ext_data_define_directives` are for this.
161+
Extra contexts are generated on demand. Load them in the template using the
162+
``load_extra()`` function.
169163

170164
.. example::
171165
:style: grid
172166

173167
.. data.render::
174-
:extra: doc
175168
176169
{% set doc = load_extra('doc') %}
177170
@@ -191,7 +184,6 @@ The following extra contexts are available:
191184
:style: grid
192185

193186
.. data.render::
194-
:extra: app
195187
196188
{% set app = load_extra('app') %}
197189
@@ -207,15 +199,14 @@ The following extra contexts are available:
207199
:style: grid
208200

209201
.. data.render::
210-
:extra: env
211202
212203
{% set env = load_extra('env') %}
213204
214205
**{{ env.all_docs | length }}**
215206
documents found.
216207
217208
``markup``
218-
:Phase: parsing and later
209+
:Phase: :term:`parsing`
219210

220211
Information about the current directive or role invocation, such as its
221212
type, name, source text, and line number.
@@ -224,7 +215,6 @@ The following extra contexts are available:
224215
:style: grid
225216

226217
.. data.render::
227-
:extra: markup
228218
229219
{%
230220
set m = load_extra('markup')
@@ -238,30 +228,29 @@ The following extra contexts are available:
238228
{% endfor %}
239229
240230
``section``
241-
:Phase: parsing and later
231+
:Phase: :term:`parsed` and :term:`resolving`
242232

243233
A proxy to the current :py:class:`docutils.nodes.section` node, when one
244-
exists.
234+
exists. This extra context is not available during the parsing phase.
245235

246236
.. example::
247237
:style: grid
248238

249239
.. data.render::
250-
:extra: section
240+
:on: parsed
251241
252-
Section Title:
242+
Section Title:
253243
"{{ load_extra('section').title }}"
254244
255245
``doc``
256-
:Phase: parsing and later
246+
:Phase: all
257247

258248
A proxy to the current :py:class:`docutils.notes.document` node.
259249

260250
.. example::
261251
:style: grid
262252

263253
.. data.render::
264-
:extra: doc
265254
266255
Document title:
267256
"{{ load_extra('doc').title }}".
@@ -331,7 +320,6 @@ Each template has a render phase that determines when it is processed:
331320

332321
.. data.render::
333322
:on: parsing
334-
:extra: doc env
335323
336324
{% set doc = load_extra('doc') %}
337325
{% set env = load_extra('env') %}
@@ -354,7 +342,6 @@ Each template has a render phase that determines when it is processed:
354342

355343
.. data.render::
356344
:on: parsed
357-
:extra: doc env
358345
359346
{% set doc = load_extra('doc') %}
360347
{% set env = load_extra('env') %}
@@ -378,7 +365,6 @@ Each template has a render phase that determines when it is processed:
378365

379366
.. data.render::
380367
:on: resolving
381-
:extra: doc env
382368
383369
{% set doc = load_extra('doc') %}
384370
{% set env = load_extra('env') %}

docs/usage.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ Directives
5757
5858
Enable :ref:`debug report <debug>` for template rendering.
5959
60-
.. rst:directive:option:: extra
61-
:type: space separted list
62-
63-
List of :ref:`extra-context` to be used in the template.
64-
6560
The content of the directive should be Jinja2 Template, please refer to
6661
::doc:`tmpl`.
6762

@@ -116,7 +111,6 @@ Directives
116111

117112
.. rst:directive:option:: on
118113
.. rst:directive:option:: debug
119-
.. rst:directive:option:: extra
120114
121115
The options of this directive are same to :rst:dir:`data.template`.
122116

src/sphinxnotes/render/__init__.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@
2626
from .ctxnodes import pending_node
2727
from .extractx import (
2828
extra_context,
29-
ParsingPhaseExtraContext,
30-
ParsedPhaseExtraContext,
31-
ResolvingPhaseExtraContext,
32-
GlobalExtraContext,
29+
ExtraContext,
30+
ExtraContextRequest,
3331
)
3432
from .pipeline import BaseContextRole, BaseContextDirective
3533
from .sources import (
@@ -58,10 +56,8 @@
5856
'Template',
5957
'UnresolvedContext',
6058
'ResolvedContext',
61-
'ParsingPhaseExtraContext',
62-
'ParsedPhaseExtraContext',
63-
'ResolvingPhaseExtraContext',
64-
'GlobalExtraContext',
59+
'ExtraContext',
60+
'ExtraContextRequest',
6561
'extra_context',
6662
'pending_node',
6763
'BaseContextRole',

src/sphinxnotes/render/ctxnodes.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
UnresolvedContext,
1313
ResolvedContext,
1414
)
15+
from .extractx import ExtraContextRequest, extra_context_loader, extra_context_names
1516
from .markup import MarkupRenderer
1617
from .jinja import TemplateRenderer
1718
from .utils import (
@@ -21,7 +22,7 @@
2122
)
2223

2324
if TYPE_CHECKING:
24-
from typing import Any, Callable
25+
from typing import Callable, Any
2526
from .markup import Host
2627
from .ctx import ResolvedContext
2728

@@ -31,8 +32,6 @@ class pending_node(nodes.Element):
3132

3233
# The context to be rendered by Jinja template.
3334
ctx: UnresolvedContext | ResolvedContext
34-
# The extra context as supplement to ctx.
35-
extra: dict[str, Any]
3635
#: Jinja template for rendering the context.
3736
template: Template
3837
#: Whether rendering to inline nodes.
@@ -59,14 +58,13 @@ def __init__(
5958
except Exception as exc:
6059
self._ctx_pickle_error = exc
6160
self.ctx = ctx
62-
self.extra = {}
6361
self.template = tmpl
6462
self.inline = inline
6563
self.rendered = False
6664

6765
# Init hook lists.
6866
self._unresolved_context_hooks = []
69-
self._resolved_data_hooks = []
67+
self._resolved_context_hooks = []
7068
self._markup_text_hooks = []
7169
self._rendered_nodes_hooks = []
7270

@@ -126,19 +124,25 @@ def err_report() -> Report:
126124
else:
127125
ctx = self.ctx
128126

129-
for hook in self._resolved_data_hooks:
127+
for hook in self._resolved_context_hooks:
130128
hook(self, ctx)
131129

132130
report.text(f'Resolved context (type: {type(ctx)}):')
133131
report.code(pformat(ctx), lang='python')
134-
report.text('Extra context (only keys):')
135-
report.code(pformat(list(self.extra.keys())), lang='python')
136132
report.text(f'Template (phase: {self.template.phase}):')
137133
report.code(self.template.text, lang='jinja')
138134

135+
extractx_req = ExtraContextRequest(self.template.phase, self, host.env, host)
136+
report.text('Available extra context names:')
137+
report.code(pformat(sorted(extra_context_names())), lang='python')
138+
139139
# 2. Render the template and context to markup text.
140140
try:
141-
markup = TemplateRenderer(self.template.text).render(ctx, extra=self.extra)
141+
markup = TemplateRenderer(self.template.text).render(
142+
ctx,
143+
globals={'load_extra': extra_context_loader(extractx_req)},
144+
debug=self.template.debug,
145+
)
142146
except Exception as e:
143147
report = err_report()
144148
report.text('Failed to render Jinja template:')
@@ -227,15 +231,15 @@ def unwrap_and_replace_self_inline(self, inliner: Report.Inliner) -> None:
227231
type RenderedNodesHook = Callable[[pending_node, list[nodes.Node]], None]
228232

229233
_unresolved_context_hooks: list[UnresolvedContextHook]
230-
_resolved_data_hooks: list[ResolvedContextHook]
234+
_resolved_context_hooks: list[ResolvedContextHook]
231235
_markup_text_hooks: list[MarkupTextHook]
232236
_rendered_nodes_hooks: list[RenderedNodesHook]
233237

234238
def hook_unresolved_context(self, hook: UnresolvedContextHook) -> None:
235239
self._unresolved_context_hooks.append(hook)
236240

237241
def hook_resolved_context(self, hook: ResolvedContextHook) -> None:
238-
self._resolved_data_hooks.append(hook)
242+
self._resolved_context_hooks.append(hook)
239243

240244
def hook_markup_text(self, hook: MarkupTextHook) -> None:
241245
self._markup_text_hooks.append(hook)

src/sphinxnotes/render/ext/adhoc.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,15 @@ class TemplateDefineDirective(SphinxDirective):
5050
option_spec = {
5151
'on': phase_option_spec,
5252
'debug': directives.flag,
53-
'extra': directives.unchanged,
5453
}
5554
has_content = True
5655

5756
@override
5857
def run(self) -> list[nodes.Node]:
59-
extra = self.options.get('extra', '')
60-
6158
self.env.temp_data[TEMPLATE_KEY] = Template(
6259
'\n'.join(self.content),
6360
phase=self.options.get('on', Phase.default()),
6461
debug='debug' in self.options,
65-
extra=extra.split() if extra else [],
6662
)
6763

6864
return []
@@ -135,7 +131,6 @@ class DataRenderDirective(BaseContextDirective):
135131
option_spec = {
136132
'on': phase_option_spec,
137133
'debug': directives.flag,
138-
'extra': directives.unchanged,
139134
}
140135
has_content = True
141136

@@ -145,14 +140,10 @@ def current_context(self) -> UnresolvedContext | ResolvedContext:
145140

146141
@override
147142
def current_template(self) -> Template:
148-
extra_str = self.options.get('extra', '')
149-
extra_list = extra_str.split() if extra_str else []
150-
151143
return Template(
152144
'\n'.join(self.content),
153145
phase=self.options.get('on', Phase.default()),
154146
debug='debug' in self.options,
155-
extra=extra_list,
156147
)
157148

158149

src/sphinxnotes/render/ext/derive.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
Optional('on', default='parsing'): Or('parsing', 'parsed', 'resolving'),
3232
'text': str,
3333
Optional('debug', default=False): bool,
34-
Optional('extra', default=[]): list,
3534
},
3635
}
3736
)
@@ -50,7 +49,6 @@ def _validate_directive_define(d: dict, config: Config) -> tuple[Schema, Templat
5049
text=tmpldef['text'],
5150
phase=Phase[tmpldef['on'].title()],
5251
debug=tmpldef['debug'],
53-
extra=tmpldef['extra'],
5452
)
5553

5654
return schema, template

0 commit comments

Comments
 (0)