Skip to content

Commit e70a718

Browse files
committed
feat: Provide a global Sphinx extra context
1 parent d6213c7 commit e70a718

5 files changed

Lines changed: 52 additions & 62 deletions

File tree

src/sphinxnotes/data/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
class Config:
1818
"""Global config of extension."""
1919

20-
template_debug: bool
20+
render_debug: bool
2121

2222

2323
def _config_inited(app: Sphinx, config: SphinxConfig) -> None:
24-
Config.template_debug = config.data_template_debug
24+
Config.render_debug = config.data_render_debug
2525

2626

2727
def setup(app: Sphinx):
28-
app.add_config_value('data_template_debug', False, '', bool)
28+
app.add_config_value('data_render_debug', False, '', bool)
2929

3030
app.connect('config-inited', _config_inited)

src/sphinxnotes/data/render/__init__.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def render(host: Host, pending: pending_data) -> rendered_data:
4242
rendered.update_all_atts(pending)
4343
# Copy source and line (which are not included in update_all_atts).
4444
rendered.source, rendered.line = pending.source, pending.line
45+
# Copy the pending's children to the rendered.
46+
rendered[:0] = pending.children # TODO: do not modify?
4547

4648
report = Report(
4749
'Render Debug Report', 'DEBUG', source=pending.source, line=pending.line
@@ -121,13 +123,6 @@ def replace(host: Host, pending: pending_data, rendered: rendered_data) -> None:
121123

122124
assert pending.parent
123125

124-
# Clear all empty reports.
125-
Reporter(pending).clear_empty()
126-
# Adopt the pending's children to the rendered.
127-
rendered[:0] = pending.children
128-
# Clear all children.
129-
pending.clear()
130-
131126
if pending.inline:
132127
doc = HostWrapper(host).doctree
133128
# Report(nodes.system_message subclass) is not inline node,
@@ -222,7 +217,7 @@ def render_pending_node(self, pending: pending_data) -> rendered_data:
222217
host = cast(Host, self)
223218

224219
# Generate and save parsing phase extra context for later use.
225-
ExtraContextGenerator(pending).on_rendering(host)
220+
ExtraContextGenerator(pending).on_anytime()
226221

227222
rendered = render(host, pending)
228223

@@ -318,10 +313,10 @@ def fix_lineno(level, message, *children, **kwargs):
318313
self.state_machine.reporter.system_message = fix_lineno
319314

320315
# Generate and save render phase extra contexts for later use.
321-
ExtraContextGenerator(pending).on_rendering(self)
316+
ExtraContextGenerator(pending).on_anytime()
322317

323318
rendered = render(self, pending)
324-
replace(pending, rendered)
319+
replace(self, pending, rendered)
325320

326321
# Restore system_message method.
327322
self.state_machine.reporter.system_message = orig_sysmsg
@@ -409,7 +404,7 @@ def apply(self, **kwargs):
409404
continue
410405

411406
# Generate and save render phase extra contexts for later use.
412-
ExtraContextGenerator(pending).on_rendering(self)
407+
ExtraContextGenerator(pending).on_anytime()
413408

414409
rendered = render(self, pending)
415410
replace(self, pending, rendered)
@@ -422,3 +417,7 @@ def setup(app: Sphinx) -> None:
422417

423418
# Hook for Phase.Resolving.
424419
app.add_post_transform(_ResolvingHook)
420+
421+
from . import extractx
422+
423+
extractx.setup(app)

src/sphinxnotes/data/render/extractx.py

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66

77
from ..utils import find_current_section
88
from ..utils.ctxproxy import proxy
9-
from .renderer import Host, ParseHost
109

1110
if TYPE_CHECKING:
1211
from typing import Any, Callable, ClassVar
12+
from sphinx.application import Sphinx
13+
from .renderer import ParseHost
1314

1415

1516
from ..utils import Report
@@ -18,23 +19,17 @@
1819
from .reporter import Reporter
1920

2021

21-
class ExtraContxt(ABC):
22-
"""Base class for extra context generator in different render phase."""
23-
24-
...
25-
26-
27-
class RenderPhaseExtraContext(ExtraContxt):
22+
class GlobalExtraContxt(ABC):
2823
@abstractmethod
29-
def generate(self, host: Host) -> Any: ...
24+
def generate(self) -> Any: ...
3025

3126

32-
class ParsePhaseExtraContext(ExtraContxt):
27+
class ParsePhaseExtraContext(ABC):
3328
@abstractmethod
3429
def generate(self, host: ParseHost) -> Any: ...
3530

3631

37-
class TransformPhaseExtraContext(ExtraContxt):
32+
class TransformPhaseExtraContext(ABC):
3833
@abstractmethod
3934
def generate(self, host: TransformHost) -> Any: ...
4035

@@ -49,50 +44,47 @@ class ExtraContextRegistry:
4944
parsing: dict[str, ParsePhaseExtraContext]
5045
parsed: dict[str, ParsePhaseExtraContext]
5146
post_transform: dict[str, TransformPhaseExtraContext]
52-
render: dict[str, RenderPhaseExtraContext]
47+
global_: dict[str, GlobalExtraContxt]
5348

5449
def __init__(self) -> None:
5550
self.names = set()
5651
self.parsing = {}
5752
self.parsed = {}
5853
self.post_transform = {}
59-
self.render = {}
54+
self.global_ = {}
6055

61-
self.add_parsing_phase_extra_context('markup', _MarkupExtraContext())
62-
self.add_parsing_phase_extra_context('section', _SectionExtraContext())
63-
self.add_render_phase_extra_context('doc', _DocExtraContext())
64-
self.add_render_phase_extra_context('env', _SphinxEnvExtraContext())
65-
self.add_render_phase_extra_context('config', _SphinxConfigExtraContext())
56+
self.add_global_context('sphinx', _SphinxExtraContext())
57+
self.add_parsing_phase_context('markup', _MarkupExtraContext())
58+
self.add_parsing_phase_context('section', _SectionExtraContext())
59+
self.add_parsing_phase_context('doc', _DocExtraContext())
6660

6761
def _name_dedup(self, name: str) -> None:
6862
# TODO: allow dup
6963
if name in self.names:
7064
raise ValueError(f'Context generator {name} already exists')
7165
self.names.add(name)
7266

73-
def add_parsing_phase_extra_context(
67+
def add_parsing_phase_context(
7468
self, name: str, ctxgen: ParsePhaseExtraContext
7569
) -> None:
7670
self._name_dedup(name)
7771
self.parsing['_' + name] = ctxgen
7872

79-
def add_parsed_phase_extra_context(
73+
def add_parsed_phase_context(
8074
self, name: str, ctxgen: ParsePhaseExtraContext
8175
) -> None:
8276
self._name_dedup(name)
8377
self.parsed['_' + name] = ctxgen
8478

85-
def add_post_transform_phase_extra_context(
79+
def add_post_transform_phase_context(
8680
self, name: str, ctxgen: TransformPhaseExtraContext
8781
) -> None:
8882
self._name_dedup(name)
8983
self.post_transform['_' + name] = ctxgen
9084

91-
def add_render_phase_extra_context(
92-
self, name: str, ctxgen: RenderPhaseExtraContext
93-
):
85+
def add_global_context(self, name: str, ctxgen: GlobalExtraContxt):
9486
self._name_dedup(name)
95-
self.render['_' + name] = ctxgen
87+
self.global_['_' + name] = ctxgen
9688

9789

9890
# ===================================
@@ -112,9 +104,9 @@ def generate(self, host: ParseHost) -> Any:
112104
}
113105

114106

115-
class _DocExtraContext(RenderPhaseExtraContext):
107+
class _DocExtraContext(ParsePhaseExtraContext):
116108
@override
117-
def generate(self, host: Host) -> Any:
109+
def generate(self, host: ParseHost) -> Any:
118110
return proxy(HostWrapper(host).doctree)
119111

120112

@@ -125,16 +117,12 @@ def generate(self, host: ParseHost) -> Any:
125117
return proxy(find_current_section(parent))
126118

127119

128-
class _SphinxEnvExtraContext(RenderPhaseExtraContext):
129-
@override
130-
def generate(self, host: Host) -> Any:
131-
return proxy(host.env)
120+
class _SphinxExtraContext(GlobalExtraContxt):
121+
app: ClassVar[Sphinx]
132122

133-
134-
class _SphinxConfigExtraContext(RenderPhaseExtraContext):
135123
@override
136-
def generate(self, host: Host) -> Any:
137-
return proxy(host.config)
124+
def generate(self) -> Any:
125+
return proxy(self.app)
138126

139127

140128
# ========================
@@ -150,12 +138,17 @@ class ExtraContextGenerator:
150138

151139
def __init__(self, node: pending_data) -> None:
152140
self.node = node
153-
self.report = Report('Extra Context Generation Report', 'ERROR')
141+
self.report = Report(
142+
'Extra Context Generation Report',
143+
'ERROR',
144+
source=node.source,
145+
line=node.line,
146+
)
154147
Reporter(node).append(self.report)
155148

156-
def on_rendering(self, host: Host) -> None:
157-
for name, ctxgen in self.registry.render.items():
158-
self._safegen(name, lambda: ctxgen.generate(host))
149+
def on_anytime(self) -> None:
150+
for name, ctxgen in self.registry.global_.items():
151+
self._safegen(name, lambda: ctxgen.generate())
159152

160153
def on_parsing(self, host: ParseHost) -> None:
161154
for name, ctxgen in self.registry.parsing.items():
@@ -174,5 +167,9 @@ def _safegen(self, name: str, gen: Callable[[], Any]):
174167
# ctxgen.generate can be user-defined code, exception of any kind are possible.
175168
self.node.extra[name] = gen()
176169
except Exception:
177-
self.report.text(f'Failed to generate extra context {name}:')
170+
self.report.text(f'Failed to generate extra context "{name}":')
178171
self.report.excption()
172+
173+
174+
def setup(app: Sphinx):
175+
_SphinxExtraContext.app = app

src/sphinxnotes/data/render/reporter.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from dataclasses import dataclass
33
from typing import Callable
44

5-
from docutils.nodes import system_message
65

76
from ..utils import Report
87
from .nodes import pending_data, rendered_data
@@ -33,7 +32,3 @@ def clear(self, pred: Callable[[Report], bool] | None = None) -> list[Report]:
3332

3433
def clear_empty(self) -> None:
3534
self.clear(lambda x: x.empty())
36-
37-
@staticmethod
38-
def reports_to_system_messages(reports: list[Report]) -> list[system_message]:
39-
return [x for x in reports]

src/sphinxnotes/data/utils/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ def __init__(
104104
self, title: str, level: Level = 'DEBUG', *children, **attributes
105105
) -> None:
106106
super().__init__(title + ':', type=level, level=2, *children, **attributes)
107-
self.log(title)
108107

109108
def empty(self) -> bool:
110109
# title is the only children
@@ -142,8 +141,8 @@ def list(self, lines: Iterable[str]) -> None:
142141
self.node(bullet_list)
143142

144143
def excption(self) -> None:
145-
self.text('Exception:')
146-
self.code(traceback.format_exc())
144+
# https://pygments.org/docs/lexers/#pygments.lexers.python.PythonTracebackLexer
145+
self.code(traceback.format_exc(), lang='pytb')
147146

148147
def is_error(self) -> bool:
149148
return self.level == 'ERROR'

0 commit comments

Comments
 (0)