Skip to content

Commit 1585447

Browse files
committed
save
1 parent b029337 commit 1585447

8 files changed

Lines changed: 274 additions & 236 deletions

File tree

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Introduction
4646
:{{ k }}: {{ v }}
4747
{%- endfor %}
4848

49-
This document "_doc.title" has {{ _doc.sections | length }} section(s).
49+
This document "{{ _doc.title }}" has {{ _doc.sections | length }} section(s).
5050

5151
{{ content }}
5252

src/sphinxnotes/data/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
BaseDataDefiner,
3434
BaseDataDefineRole,
3535
BaseDataDefineDirective,
36-
StrictDataDefineDirective,
3736
ExtraContextRegistry,
3837
EXTRACTX_REGISTRY,
3938
)
39+
from .derive import StrictDataDefineDirective
4040
from .config import Config
4141
from . import extractx
4242

src/sphinxnotes/data/data.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import re
1414
from dataclasses import dataclass, asdict, field as dataclass_field
1515
from ast import literal_eval
16-
from abc import ABC, abstractmethod
16+
17+
from .utils import Unpicklable
1718

1819
if TYPE_CHECKING:
1920
from typing import Any, Callable, Generator, Self, Literal
@@ -209,18 +210,19 @@ def add_by_option(
209210
# Data, Field and Schema
210211
# ======================
211212

212-
213-
class Data(ABC):
214-
@abstractmethod
215-
def asdict(self) -> dict[str, Any]: ...
216-
217-
218213
@dataclass
219214
class RawData:
220215
name: str | None
221216
attrs: dict[str, str]
222217
content: str | None
223218

219+
@dataclass
220+
class PendingData(Unpicklable):
221+
raw: RawData
222+
schema: Schema
223+
224+
def parse(self) -> ParsedData:
225+
return self.schema.parse(self.raw)
224226

225227
@dataclass
226228
class ParsedData:
@@ -247,19 +249,6 @@ def asdict(self) -> dict[str, Any]:
247249
return ctx
248250

249251

250-
@dataclass
251-
class PendingData(Data):
252-
raw: RawData
253-
schema: Schema
254-
255-
def parse(self) -> ParsedData:
256-
return self.schema.parse(self.raw)
257-
258-
@override
259-
def asdict(self) -> dict[str, Any]:
260-
return self.parse().asdict()
261-
262-
263252
@dataclass
264253
class Field:
265254
#: Type of element.

src/sphinxnotes/data/derive.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
sphinxnotes.data.derive
3+
~~~~~~~~~~~~~~~~~~~~~~~
4+
5+
Module for rendering data to doctree nodes.
6+
7+
:copyright: Copyright 2025 by the Shengyu Zhang.
8+
:license: BSD, see LICENSE for details.
9+
"""
10+
11+
from __future__ import annotations
12+
from typing import TYPE_CHECKING, override
13+
14+
from docutils.parsers.rst import directives
15+
16+
from .data import Field, Schema
17+
from .template import Template
18+
from .render import BaseDataDefineDirective
19+
20+
class StrictDataDefineDirective(BaseDataDefineDirective):
21+
final_argument_whitespace = True
22+
23+
schema: Schema
24+
template: Template
25+
26+
@override
27+
def current_template(self) -> Template:
28+
return self.template
29+
30+
@override
31+
def current_schema(self) -> Schema:
32+
return self.schema
33+
34+
@classmethod
35+
def derive(
36+
cls, name: str, schema: Schema, tmpl: Template
37+
) -> type[StrictDataDefineDirective]:
38+
"""Generate an AnyDirective child class for describing object."""
39+
if not schema.name:
40+
required_arguments = 0
41+
optional_arguments = 0
42+
elif schema.name.required:
43+
required_arguments = 1
44+
optional_arguments = 0
45+
else:
46+
required_arguments = 0
47+
optional_arguments = 1
48+
49+
assert not isinstance(schema.attrs, Field)
50+
option_spec = {}
51+
for name, field in schema.attrs.items():
52+
if field.required:
53+
option_spec[name] = directives.unchanged_required
54+
else:
55+
option_spec[name] = directives.unchanged
56+
57+
has_content = schema.content is not None
58+
59+
# Generate directive class
60+
return type(
61+
'%sStrictDataDefineDirective' % name.title(),
62+
(cls,),
63+
{
64+
'schema': schema,
65+
'template': tmpl,
66+
'has_content': has_content,
67+
'required_arguments': required_arguments,
68+
'optional_arguments': optional_arguments,
69+
'option_spec': option_spec,
70+
},
71+
)

src/sphinxnotes/data/extractx.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import annotations
2-
from typing import override
2+
from typing import TYPE_CHECKING, override
33

44
from sphinx.util.docutils import SphinxDirective, SphinxRole
55
from sphinx.transforms import SphinxTransform
@@ -9,17 +9,17 @@
99
from .render import (
1010
EXTRACTX_REGISTRY,
1111
ParsePhaseContextGenerator,
12-
FullPhaseContextGenerator,
12+
RenderPhaseContextGenerator,
1313
ParseCaller,
1414
Caller,
15-
pending_node,
1615
)
17-
from .template import Context
1816

17+
if TYPE_CHECKING:
18+
from typing import Any
1919

2020
class MarkupContextGenerator(ParsePhaseContextGenerator):
2121
@override
22-
def generate(self, caller: ParseCaller, n: pending_node) -> Context:
22+
def generate(self, caller: ParseCaller) -> Any:
2323
isdir = isinstance(caller, SphinxDirective)
2424
return {
2525
'type': 'directive' if isdir else 'role',
@@ -29,9 +29,9 @@ def generate(self, caller: ParseCaller, n: pending_node) -> Context:
2929
}
3030

3131

32-
class DocContextGenerator(FullPhaseContextGenerator):
32+
class DocContextGenerator(RenderPhaseContextGenerator):
3333
@override
34-
def generate(self, caller: Caller, n: pending_node) -> Context:
34+
def generate(self, caller: Caller) -> Any:
3535
if isinstance(caller, SphinxDirective):
3636
return proxy(caller.state.document)
3737
elif isinstance(caller, SphinxRole):
@@ -42,33 +42,31 @@ def generate(self, caller: Caller, n: pending_node) -> Context:
4242
assert False
4343

4444

45-
class SectionContextGenerator(FullPhaseContextGenerator):
45+
class SectionContextGenerator(ParsePhaseContextGenerator):
4646
@override
47-
def generate(self, caller: Caller, n: pending_node) -> Context:
48-
if n.parent:
49-
return proxy(find_current_section(n.parent))
50-
elif isinstance(caller, SphinxDirective):
47+
def generate(self, caller: ParseCaller) -> Any:
48+
if isinstance(caller, SphinxDirective):
5149
return proxy(find_current_section(caller.state.parent))
5250
elif isinstance(caller, SphinxRole):
5351
return proxy(caller.inliner.parent)
5452
else:
5553
assert False
5654

5755

58-
class SphinxEnvContextGenerator(FullPhaseContextGenerator):
56+
class SphinxEnvContextGenerator(RenderPhaseContextGenerator):
5957
@override
60-
def generate(self, caller: Caller, n: pending_node) -> Context:
58+
def generate(self, caller: Caller) -> Any:
6159
return proxy(caller.env)
6260

6361

64-
class SphinxConfigContextGenerator(FullPhaseContextGenerator):
62+
class SphinxConfigContextGenerator(RenderPhaseContextGenerator):
6563
@override
66-
def generate(self, caller: Caller, n: pending_node) -> Context:
64+
def generate(self, caller: Caller) -> Any:
6765
return proxy(caller.config)
6866

6967

70-
EXTRACTX_REGISTRY.add_parsing_phase_context('markup', MarkupContextGenerator())
71-
EXTRACTX_REGISTRY.add_full_phase_context('doc', DocContextGenerator())
72-
EXTRACTX_REGISTRY.add_full_phase_context('section', SectionContextGenerator())
73-
EXTRACTX_REGISTRY.add_full_phase_context('env', SphinxEnvContextGenerator())
74-
EXTRACTX_REGISTRY.add_full_phase_context('config', SphinxConfigContextGenerator())
68+
EXTRACTX_REGISTRY.add_parsing('markup', MarkupContextGenerator())
69+
EXTRACTX_REGISTRY.add_parsing('section', SectionContextGenerator())
70+
EXTRACTX_REGISTRY.add_render('doc', DocContextGenerator())
71+
EXTRACTX_REGISTRY.add_render('env', SphinxEnvContextGenerator())
72+
EXTRACTX_REGISTRY.add_render('config', SphinxConfigContextGenerator())

0 commit comments

Comments
 (0)