Skip to content

Commit 4801ad8

Browse files
committed
update
1 parent be8d8d4 commit 4801ad8

2 files changed

Lines changed: 47 additions & 53 deletions

File tree

src/sphinxnotes/render/extractx.py

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from sphinx.util.docutils import SphinxDirective, SphinxRole
66
from sphinx.transforms import SphinxTransform
77

8-
from .render import HostWrapper
8+
from .render import HostWrapper, Phase
99
from .ctxnodes import pending_node
1010
from .utils import find_current_section, Report, Reporter
1111

@@ -19,46 +19,53 @@
1919
# ExtraContext ABC definitions
2020
# ============================
2121

22-
type ExtraContext = (
23-
ParsingPhaseExtraContext
24-
| ParsingPhaseExtraContext
25-
| ResolvingPhaseExtraContext
26-
| GlobalExtraContext
27-
)
28-
"""Type alias of all available extra contexts."""
2922

23+
class _ExtraContext(ABC):
24+
"""Base class of extra context."""
3025

31-
class ParsingPhaseExtraContext(ABC):
26+
phase: ClassVar[Phase | None] = None
27+
28+
@abstractmethod
29+
def generate(self, *args, **kwargs) -> Any: ...
30+
31+
32+
class ParsingPhaseExtraContext(_ExtraContext):
3233
"""Extra context generated during the :py:data:`~Phase.Parsing` phase.
3334
3435
The ``generate`` method receives the current directive or role being executed.
3536
"""
3637

38+
phase = Phase.Parsing
39+
3740
@abstractmethod
3841
def generate(self, directive: SphinxDirective | SphinxRole) -> Any: ...
3942

4043

41-
class ParsedPhaseExtraContext(ABC):
44+
class ParsedPhaseExtraContext(_ExtraContext):
4245
"""Extra context generated during the :py:data:`~Phase.Parsed` phase.
4346
4447
The ``generate`` method receives the current Sphinx transform.
4548
"""
4649

50+
phase = Phase.Parsed
51+
4752
@abstractmethod
4853
def generate(self, transform: SphinxTransform) -> Any: ...
4954

5055

51-
class ResolvingPhaseExtraContext(ABC):
56+
class ResolvingPhaseExtraContext(_ExtraContext):
5257
"""Extra context generated during the :py:data:`~Phase.Resolving` phase.
5358
5459
The ``generate`` method receives the current Sphinx transform.
5560
"""
5661

62+
phase = Phase.Resolving
63+
5764
@abstractmethod
5865
def generate(self, transform: SphinxTransform) -> Any: ...
5966

6067

61-
class GlobalExtraContext(ABC):
68+
class GlobalExtraContext(_ExtraContext):
6269
"""Extra context available in all phases.
6370
6471
The ``generate`` method receives the Sphinx build environment.
@@ -74,42 +81,34 @@ def generate(self, env: BuildEnvironment) -> Any: ...
7481

7582

7683
class _ExtraContextRegistry:
77-
ctxs: dict[str, ExtraContext]
84+
ctxs: dict[str, _ExtraContext]
7885

7986
def __init__(self) -> None:
8087
self.ctxs = {}
8188

82-
def is_extra_context(self, ctx: Any) -> bool:
83-
return isinstance(
84-
ctx,
85-
(
86-
ParsingPhaseExtraContext,
87-
ParsingPhaseExtraContext,
88-
ResolvingPhaseExtraContext,
89-
GlobalExtraContext,
90-
),
91-
)
92-
93-
def register(self, name: str, ctx: ExtraContext) -> None:
89+
def register(self, name: str, ctx: _ExtraContext) -> None:
9490
if name in self.ctxs:
9591
raise ValueError(f'Extra context "{name}" already registered')
96-
if not self.is_extra_context(ctx):
97-
raise TypeError(
98-
f'Invalid extra context instance "{name}", Expecting {ExtraContext}'
99-
)
10092
self.ctxs[name] = ctx
10193

102-
def get(self, name: str) -> ExtraContext | None:
94+
def get(self, name: str) -> _ExtraContext | None:
10395
if name not in self.ctxs:
10496
return None
10597
return self.ctxs[name]
10698

107-
def get_names_by_phase(self, cls: type) -> set[str]:
108-
return {name for name, ctx in self.ctxs.items() if isinstance(ctx, cls)}
109-
11099
def get_names(self) -> set[str]:
111100
return set(self.ctxs.keys())
112101

102+
def get_names_at_phase(self, phase: Phase | None) -> set[str]:
103+
return {name for name, ctx in self.ctxs.items() if ctx.phase == phase}
104+
105+
def get_names_before_phase(self, phase: Phase | None) -> set[str]:
106+
return {
107+
name
108+
for name, ctx in self.ctxs.items()
109+
if phase is None or ctx.phase is None or phase >= ctx.phase
110+
}
111+
113112

114113
# Global registry instance.
115114
_REGISTRY = _ExtraContextRegistry()
@@ -133,8 +132,8 @@ def extra_context(name: str):
133132
134133
@extra_context('doc')
135134
class DocExtraContext(ParsingPhaseExtraContext):
136-
def generate(self, directive):
137-
return proxy(HostWrapper(directive).doctree)
135+
def generate(self, ctx):
136+
return proxy(HostWrapper(ctx).doctree)
138137
139138
:param name: The context name, used in templates via ``load_extra('name')``.
140139
"""
@@ -169,13 +168,17 @@ def __init__(self, node: pending_node) -> None:
169168
Reporter(node).append(self.report)
170169

171170
# Initialize todo with requested extra contexts, validate they exist
171+
total = _REGISTRY.get_names()
172+
avail = _REGISTRY.get_names_before_phase(node.template.phase)
172173
requested = set(node.template.extra)
173-
avail = _REGISTRY.get_names()
174174
self.todo = requested & avail
175175

176176
# Report errors for non-existent contexts
177-
if nonexist := requested - avail:
178-
self.report.text(f'Extra contexts {nonexist} are not registered.')
177+
if nonexist := requested - total:
178+
self.report.text(f'Extra contexts {nonexist} are non-exist.')
179+
if nonavail := requested - nonexist:
180+
self.report.text(f'Extra contexts {nonavail} are not available '
181+
f'at pahse {node.template.phase}.')
179182

180183
def on_anytime(self, env: BuildEnvironment) -> None:
181184
self._generate(GlobalExtraContext, lambda ctx: ctx.generate(env))
@@ -189,9 +192,9 @@ def on_parsed(self, transform: SphinxTransform) -> None:
189192
def on_resolving(self, transform: SphinxTransform) -> None:
190193
self._generate(ResolvingPhaseExtraContext, lambda ctx: ctx.generate(transform))
191194

192-
def _generate(self, cls: type, gen: Callable[..., Any]) -> None:
195+
def _generate(self, cls: type[_ExtraContext], gen: Callable[..., Any]) -> None:
193196
# Get all context names available for this phase
194-
avail = _REGISTRY.get_names_by_phase(cls)
197+
avail = _REGISTRY.get_names_at_phase(cls.phase)
195198
# Find which ones are requested and not yet generated
196199
todo = avail & self.todo
197200

@@ -262,17 +265,4 @@ def generate(self, env: BuildEnvironment) -> Any:
262265
return proxy(env)
263266

264267

265-
@extra_context('docutils')
266-
class DocutilsExtraContext(GlobalExtraContext):
267-
@override
268-
def generate(self, env: BuildEnvironment) -> Any:
269-
from docutils.parsers.rst.directives import _directives
270-
from docutils.parsers.rst.roles import _roles
271-
272-
return {
273-
'directives': _directives,
274-
'roles': _roles,
275-
}
276-
277-
278268
def setup(app: Sphinx): ...

src/sphinxnotes/render/render.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class Phase(Enum):
2525
def default(cls) -> Phase:
2626
return cls.Parsing
2727

28+
def __ge__(self, other: Phase) -> bool:
29+
_ORDER = {'parsing': 1, 'parsed': 2, 'resolving': 3}
30+
return _ORDER[self.value] >= _ORDER[other.value]
31+
2832

2933
@dataclass
3034
class Template:

0 commit comments

Comments
 (0)