sphinxnotes-data 1.0a2__tar.gz → 1.0a4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sphinxnotes_data-1.0a2/src/sphinxnotes_data.egg-info → sphinxnotes_data-1.0a4}/PKG-INFO +1 -1
- sphinxnotes_data-1.0a4/src/sphinxnotes/data/__init__.py +83 -0
- sphinxnotes_data-1.0a4/src/sphinxnotes/data/config.py +37 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/data.py +92 -74
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/preset.py +13 -2
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4/src/sphinxnotes_data.egg-info}/PKG-INFO +1 -1
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/SOURCES.txt +1 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/tests/test_data.py +38 -6
- sphinxnotes_data-1.0a2/src/sphinxnotes/data/__init__.py +0 -62
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.cruft.json +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.github/workflows/lint.yml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.github/workflows/pages.yml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.github/workflows/pypi.yml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.github/workflows/release.yml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.github/workflows/tag.yml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.github/workflows/test.yml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.gitignore +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/.pre-commit-config.yaml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/LICENSE +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/MANIFEST.in +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/Makefile +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/README.rst +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/Makefile +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/_images/.gitkeep +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/_static/.gitkeep +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/_static/sphinx-notes.png +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/changelog.rst +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/conf.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/index.rst +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/docs/make.bat +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/pyproject.toml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/ruff.toml +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/setup.cfg +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/adhoc.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/extra_contexts.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/meta.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/py.typed +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/render.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/template.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/utils/__init__.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/utils/context_proxy.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/utils/freestyle.py +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/dependency_links.txt +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/requires.txt +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/top_level.txt +0 -0
- {sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/tests/__init__.py +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sphinxnotes.data
|
|
3
|
+
~~~~~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
:copyright: Copyright 2025 by the Shengyu Zhang.
|
|
6
|
+
:license: BSD, see LICENSE for details.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from sphinx.util import logging
|
|
13
|
+
|
|
14
|
+
from . import meta
|
|
15
|
+
from .data import (
|
|
16
|
+
Registry,
|
|
17
|
+
Form,
|
|
18
|
+
BoolFlag,
|
|
19
|
+
OperFlag,
|
|
20
|
+
Value,
|
|
21
|
+
ValueWrapper,
|
|
22
|
+
RawData,
|
|
23
|
+
Data,
|
|
24
|
+
Field,
|
|
25
|
+
Schema,
|
|
26
|
+
)
|
|
27
|
+
from .template import Phase, Template
|
|
28
|
+
from .render import (
|
|
29
|
+
Caller,
|
|
30
|
+
pending_node,
|
|
31
|
+
RenderedNode,
|
|
32
|
+
rendered_node,
|
|
33
|
+
rendered_inline_node,
|
|
34
|
+
BaseDataDefiner,
|
|
35
|
+
BaseDataDefineRole,
|
|
36
|
+
BaseDataDefineDirective,
|
|
37
|
+
StrictDataDefineDirective,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from sphinx.application import Sphinx
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
"""Python API for other Sphinx extesions."""
|
|
45
|
+
__all__ = [
|
|
46
|
+
'Registry',
|
|
47
|
+
'Form',
|
|
48
|
+
'BoolFlag',
|
|
49
|
+
'OperFlag',
|
|
50
|
+
'Value',
|
|
51
|
+
'ValueWrapper',
|
|
52
|
+
'RawData',
|
|
53
|
+
'Data',
|
|
54
|
+
'Field',
|
|
55
|
+
'Schema',
|
|
56
|
+
'Phase',
|
|
57
|
+
'Template',
|
|
58
|
+
'Caller',
|
|
59
|
+
'pending_node',
|
|
60
|
+
'RenderedNode',
|
|
61
|
+
'rendered_node',
|
|
62
|
+
'rendered_inline_node',
|
|
63
|
+
'BaseDataDefiner',
|
|
64
|
+
'BaseDataDefineRole',
|
|
65
|
+
'BaseDataDefineDirective',
|
|
66
|
+
'BaseDataDefineDirective',
|
|
67
|
+
'StrictDataDefineDirective',
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
logger = logging.getLogger(__name__)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def setup(app: Sphinx):
|
|
74
|
+
meta.pre_setup(app)
|
|
75
|
+
|
|
76
|
+
from . import config, template, render, adhoc
|
|
77
|
+
|
|
78
|
+
config.setup(app)
|
|
79
|
+
template.setup(app)
|
|
80
|
+
render.setup(app)
|
|
81
|
+
adhoc.setup(app)
|
|
82
|
+
|
|
83
|
+
return meta.post_setup(app)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sphinxnotes.data.config
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
:copyright: Copyright 2025~2026 by the Shengyu Zhang.
|
|
6
|
+
:license: BSD, see LICENSE for details.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from sphinx.application import Sphinx
|
|
10
|
+
from sphinx.config import Config as SphinxConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Config:
|
|
14
|
+
"""Global config of extesion."""
|
|
15
|
+
|
|
16
|
+
template_debug: bool
|
|
17
|
+
|
|
18
|
+
date_fmt: str
|
|
19
|
+
time_fmt: str
|
|
20
|
+
datetime_fmt: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _config_inited(app: Sphinx, config: SphinxConfig) -> None:
|
|
24
|
+
Config.template_debug = config.data_template_debug
|
|
25
|
+
|
|
26
|
+
Config.date_fmt = config.data_date_fmt
|
|
27
|
+
Config.time_fmt = config.data_time_fmt
|
|
28
|
+
Config.datetime_fmt = config.data_datetime_fmt
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def setup(app: Sphinx):
|
|
32
|
+
app.add_config_value('data_template_debug', False, '', bool)
|
|
33
|
+
app.add_config_value('data_date_fmt', '%Y-%m-%d', '', str)
|
|
34
|
+
app.add_config_value('data_time_fmt', '%H:%M:%S', '', str)
|
|
35
|
+
app.add_config_value('data_datetime_fmt', '%Y-%m-%d %H:%M:%S', '', str)
|
|
36
|
+
|
|
37
|
+
app.connect('config-inited', _config_inited)
|
|
@@ -3,16 +3,18 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
import re
|
|
4
4
|
from dataclasses import dataclass, asdict, field as dataclass_field
|
|
5
5
|
from ast import literal_eval
|
|
6
|
+
from datetime import date, time, datetime
|
|
7
|
+
|
|
8
|
+
from .config import Config
|
|
6
9
|
|
|
7
10
|
if TYPE_CHECKING:
|
|
8
11
|
from typing import Any, Callable, Generator, Self, Literal
|
|
9
12
|
|
|
13
|
+
# =====================================
|
|
14
|
+
# Basic classes: Value, Form, Flag, ...
|
|
15
|
+
# =====================================
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
# Basic classes: Value, Form, Flag, ... #
|
|
13
|
-
#########################################
|
|
14
|
-
|
|
15
|
-
type PlainValue = bool | int | float | str
|
|
17
|
+
type PlainValue = bool | int | float | str | date | time | datetime
|
|
16
18
|
type Value = None | PlainValue | list[PlainValue]
|
|
17
19
|
|
|
18
20
|
|
|
@@ -24,14 +26,9 @@ class ValueWrapper:
|
|
|
24
26
|
if self.v is None:
|
|
25
27
|
return None
|
|
26
28
|
if isinstance(self.v, list):
|
|
27
|
-
if len(self.v)
|
|
28
|
-
return None
|
|
29
|
-
return self.v[0]
|
|
29
|
+
return self.v[0] if len(self.v) else None
|
|
30
30
|
return self.v
|
|
31
31
|
|
|
32
|
-
def as_str(self) -> str | None:
|
|
33
|
-
return str(self.as_plain())
|
|
34
|
-
|
|
35
32
|
def as_list(self) -> list[PlainValue]:
|
|
36
33
|
if self.v is None:
|
|
37
34
|
return []
|
|
@@ -40,13 +37,22 @@ class ValueWrapper:
|
|
|
40
37
|
else:
|
|
41
38
|
return [self.v]
|
|
42
39
|
|
|
40
|
+
def as_str(self) -> str | None:
|
|
41
|
+
v = self.as_plain()
|
|
42
|
+
return self._strify(v) if v is not None else None
|
|
43
|
+
|
|
43
44
|
def as_str_list(self) -> list[str]:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return
|
|
45
|
+
return [self._strify(x) for x in self.as_list()]
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _strify(v: PlainValue) -> str:
|
|
49
|
+
if isinstance(v, datetime):
|
|
50
|
+
return v.strftime(Config.datetime_fmt)
|
|
51
|
+
elif isinstance(v, date):
|
|
52
|
+
return v.strftime(Config.date_fmt)
|
|
53
|
+
elif isinstance(v, time):
|
|
54
|
+
return v.strftime(Config.time_fmt)
|
|
55
|
+
return str(v)
|
|
50
56
|
|
|
51
57
|
|
|
52
58
|
@dataclass(frozen=True)
|
|
@@ -77,9 +83,44 @@ class OperFlag(Flag):
|
|
|
77
83
|
store: FlagStore = 'assign'
|
|
78
84
|
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
# Registry
|
|
82
|
-
|
|
86
|
+
# ========
|
|
87
|
+
# Registry
|
|
88
|
+
# ========
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _bool_conv(v: str | None) -> bool:
|
|
92
|
+
v = v.lower().strip() if v is not None else None
|
|
93
|
+
if v in ('true', 'yes', '1', 'on', 'y', ''):
|
|
94
|
+
return True
|
|
95
|
+
if v in ('false', 'no', '0', 'off', 'n', None):
|
|
96
|
+
return False
|
|
97
|
+
# Same to :meth:`directives.flag`.
|
|
98
|
+
raise ValueError(f'no argument is allowed; "{v}" supplied')
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _str_conv(v: str) -> str:
|
|
102
|
+
try:
|
|
103
|
+
vv = literal_eval(v)
|
|
104
|
+
except (ValueError, SyntaxError):
|
|
105
|
+
return v
|
|
106
|
+
return vv if isinstance(vv, str) else v
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _date_conv(v: str) -> date:
|
|
110
|
+
return datetime.strptime(v, Config.date_fmt).date()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _time_conv(v: str) -> time:
|
|
114
|
+
return datetime.strptime(v, Config.time_fmt).time()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _datetime_conv(v: str) -> datetime:
|
|
118
|
+
return datetime.strptime(v, Config.datetime_fmt)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
_required_flag = BoolFlag('required')
|
|
122
|
+
|
|
123
|
+
_sep_flag = OperFlag('sep', etype=str)
|
|
83
124
|
|
|
84
125
|
|
|
85
126
|
class Registry:
|
|
@@ -95,33 +136,19 @@ class Registry:
|
|
|
95
136
|
'num': float,
|
|
96
137
|
'str': str,
|
|
97
138
|
'string': str,
|
|
139
|
+
'date': date,
|
|
140
|
+
'time': time,
|
|
141
|
+
'datetime': datetime,
|
|
98
142
|
}
|
|
99
143
|
|
|
100
|
-
"""Internal type converters."""
|
|
101
|
-
|
|
102
|
-
@staticmethod
|
|
103
|
-
def _bool_conv(v: str | None) -> bool:
|
|
104
|
-
v = v.lower().strip() if v is not None else None
|
|
105
|
-
if v in ('true', 'yes', '1', 'on', 'y', ''):
|
|
106
|
-
return True
|
|
107
|
-
if v in ('false', 'no', '0', 'off', 'n', None):
|
|
108
|
-
return False
|
|
109
|
-
# Same to :meth:`directives.flag`.
|
|
110
|
-
raise ValueError(f'no argument is allowed; "{v}" supplied')
|
|
111
|
-
|
|
112
|
-
@staticmethod
|
|
113
|
-
def _str_conv(v: str) -> str:
|
|
114
|
-
try:
|
|
115
|
-
vv = literal_eval(v)
|
|
116
|
-
except (ValueError, SyntaxError):
|
|
117
|
-
return v
|
|
118
|
-
return vv if isinstance(vv, str) else v
|
|
119
|
-
|
|
120
144
|
convs: dict[type, Callable[[str], Any]] = {
|
|
121
145
|
bool: _bool_conv,
|
|
122
146
|
int: int,
|
|
123
147
|
float: float,
|
|
124
148
|
str: _str_conv,
|
|
149
|
+
date: _date_conv,
|
|
150
|
+
time: _time_conv,
|
|
151
|
+
datetime: _datetime_conv,
|
|
125
152
|
}
|
|
126
153
|
|
|
127
154
|
forms: dict[str, Form] = {
|
|
@@ -132,9 +159,6 @@ class Registry:
|
|
|
132
159
|
|
|
133
160
|
"""Builtin flags."""
|
|
134
161
|
|
|
135
|
-
_required_flag = BoolFlag('required')
|
|
136
|
-
_sep_flag = OperFlag('sep', etype=str)
|
|
137
|
-
|
|
138
162
|
flags: dict[str, BoolFlag] = {
|
|
139
163
|
'required': _required_flag,
|
|
140
164
|
'require': _required_flag,
|
|
@@ -147,9 +171,9 @@ class Registry:
|
|
|
147
171
|
}
|
|
148
172
|
|
|
149
173
|
|
|
150
|
-
|
|
151
|
-
# Data, Field and Schema
|
|
152
|
-
|
|
174
|
+
# ======================
|
|
175
|
+
# Data, Field and Schema
|
|
176
|
+
# ======================
|
|
153
177
|
|
|
154
178
|
|
|
155
179
|
@dataclass
|
|
@@ -216,7 +240,7 @@ class Field:
|
|
|
216
240
|
if self.etype is bool:
|
|
217
241
|
# Special case: A single bool field is valid even when
|
|
218
242
|
# value is not supplied.
|
|
219
|
-
return
|
|
243
|
+
return _bool_conv(rawval)
|
|
220
244
|
return None
|
|
221
245
|
|
|
222
246
|
# Strip whitespace. TODO: supported unchanged?
|
|
@@ -305,7 +329,7 @@ class DSLParser:
|
|
|
305
329
|
|
|
306
330
|
self.field.etype = Registry.etypes[etype]
|
|
307
331
|
self.field.ctype = Registry.forms[form].ctype
|
|
308
|
-
self.field.flags[
|
|
332
|
+
self.field.flags[_sep_flag.name] = Registry.forms[form].sep
|
|
309
333
|
return
|
|
310
334
|
|
|
311
335
|
# Match: Type only (e.g., "int")
|
|
@@ -341,7 +365,7 @@ class DSLParser:
|
|
|
341
365
|
)
|
|
342
366
|
|
|
343
367
|
# Deal with builtin flags.
|
|
344
|
-
if flag ==
|
|
368
|
+
if flag == _sep_flag:
|
|
345
369
|
# ctype default to list if 'sep by' is used without a 'xxx of xxx'.
|
|
346
370
|
if self.field.ctype is None:
|
|
347
371
|
self.field.ctype = list
|
|
@@ -360,7 +384,7 @@ class DSLParser:
|
|
|
360
384
|
@dataclass(frozen=True)
|
|
361
385
|
class Schema(object):
|
|
362
386
|
name: Field | None
|
|
363
|
-
attrs: dict[str, Field]
|
|
387
|
+
attrs: dict[str, Field] | Field
|
|
364
388
|
content: Field | None
|
|
365
389
|
|
|
366
390
|
@classmethod
|
|
@@ -403,8 +427,8 @@ class Schema(object):
|
|
|
403
427
|
else:
|
|
404
428
|
rawattrs = data.attrs.copy()
|
|
405
429
|
for key, field in self.attrs.items():
|
|
406
|
-
rawval
|
|
407
|
-
|
|
430
|
+
if rawval := rawattrs.pop(key, None):
|
|
431
|
+
attrs[key] = self._parse_single(('attrs.' + key, field), rawval)
|
|
408
432
|
for key, rawval in rawattrs.items():
|
|
409
433
|
raise ValueError(f'unknown attr: "{key}"')
|
|
410
434
|
|
|
@@ -415,35 +439,29 @@ class Schema(object):
|
|
|
415
439
|
|
|
416
440
|
return Data(name, attrs, content)
|
|
417
441
|
|
|
418
|
-
def fields(
|
|
419
|
-
self
|
|
420
|
-
) -> Generator[tuple[str, Field]]:
|
|
421
|
-
def ok(f: Field) -> bool:
|
|
422
|
-
return not pred or pred(f)
|
|
423
|
-
|
|
424
|
-
if self.name and ok(self.name):
|
|
442
|
+
def fields(self) -> Generator[tuple[str, Field]]:
|
|
443
|
+
if self.name:
|
|
425
444
|
yield 'name', self.name
|
|
426
445
|
|
|
427
|
-
|
|
428
|
-
|
|
446
|
+
if isinstance(self.attrs, Field):
|
|
447
|
+
yield 'attrs', self.attrs
|
|
448
|
+
else:
|
|
449
|
+
for name, field in self.attrs.items():
|
|
429
450
|
yield name, field
|
|
430
451
|
|
|
431
|
-
if self.content
|
|
452
|
+
if self.content:
|
|
432
453
|
yield 'content', self.content
|
|
433
454
|
|
|
434
|
-
def items(
|
|
435
|
-
self
|
|
436
|
-
) -> Generator[tuple[str, Field, Value]]:
|
|
437
|
-
def ok(f: Field) -> bool:
|
|
438
|
-
return not pred or pred(f)
|
|
439
|
-
|
|
440
|
-
if self.name and ok(self.name):
|
|
455
|
+
def items(self, data: Data) -> Generator[tuple[str, Field, Value]]:
|
|
456
|
+
if self.name:
|
|
441
457
|
yield 'name', self.name, data.name
|
|
442
458
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
459
|
+
if isinstance(self.attrs, Field):
|
|
460
|
+
for name, val in data.attrs:
|
|
461
|
+
yield name, self.attrs, val
|
|
462
|
+
else:
|
|
463
|
+
for name, field in self.attrs.items():
|
|
464
|
+
yield name, field, data.attrs.get(name)
|
|
447
465
|
|
|
448
|
-
if self.content
|
|
466
|
+
if self.content:
|
|
449
467
|
yield 'content', self.content, data.content
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sphinxnotes.data.preset
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
Preset templates and schemas.
|
|
6
|
+
|
|
7
|
+
:copyright: Copyright 2026 by the Shengyu Zhang.
|
|
8
|
+
:license: BSD, see LICENSE for details.
|
|
9
|
+
"""
|
|
10
|
+
|
|
1
11
|
from .data import Schema, Field
|
|
2
12
|
from .template import Template, Phase
|
|
13
|
+
from .config import Config
|
|
3
14
|
|
|
4
15
|
|
|
5
16
|
class Directive:
|
|
@@ -10,7 +21,7 @@ class Directive:
|
|
|
10
21
|
@staticmethod
|
|
11
22
|
def template() -> Template:
|
|
12
23
|
return Template(
|
|
13
|
-
debug=
|
|
24
|
+
debug=Config.template_debug,
|
|
14
25
|
phase=Phase.Parsing,
|
|
15
26
|
text=""".. note::
|
|
16
27
|
|
|
@@ -36,7 +47,7 @@ class Role:
|
|
|
36
47
|
@staticmethod
|
|
37
48
|
def template() -> Template:
|
|
38
49
|
return Template(
|
|
39
|
-
debug=
|
|
50
|
+
debug=Config.template_debug,
|
|
40
51
|
phase=Phase.Parsing,
|
|
41
52
|
text="""``{{ content or 'None' }}``
|
|
42
53
|
:abbr:`ⁱⁿᶠᵒ (This is a default template for rendering the data your deinfed
|
|
@@ -23,6 +23,7 @@ docs/_static/.gitkeep
|
|
|
23
23
|
docs/_static/sphinx-notes.png
|
|
24
24
|
src/sphinxnotes/data/__init__.py
|
|
25
25
|
src/sphinxnotes/data/adhoc.py
|
|
26
|
+
src/sphinxnotes/data/config.py
|
|
26
27
|
src/sphinxnotes/data/data.py
|
|
27
28
|
src/sphinxnotes/data/extra_contexts.py
|
|
28
29
|
src/sphinxnotes/data/meta.py
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import unittest
|
|
4
|
-
from
|
|
4
|
+
from datetime import date, time, datetime
|
|
5
5
|
|
|
6
6
|
sys.path.insert(0, os.path.abspath('./src/sphinxnotes'))
|
|
7
7
|
|
|
8
8
|
from data.data import Field, Registry, BoolFlag, OperFlag
|
|
9
|
+
from data.config import Config
|
|
9
10
|
|
|
10
11
|
class TestFieldParser(unittest.TestCase):
|
|
11
12
|
|
|
12
13
|
# ==========================
|
|
13
14
|
# Basic Types
|
|
14
15
|
# ==========================
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
def test_basic_scalar(self):
|
|
17
18
|
f = Field.from_dsl('int')
|
|
18
19
|
self.assertEqual(f.parse('123'), 123)
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
f = Field.from_dsl('bool')
|
|
21
22
|
self.assertTrue(f.parse('true'))
|
|
22
23
|
self.assertTrue(f.parse('y'))
|
|
@@ -89,7 +90,7 @@ class TestFieldParser(unittest.TestCase):
|
|
|
89
90
|
self.assertTrue(f.uniq)
|
|
90
91
|
f = Field.from_dsl(r'int')
|
|
91
92
|
self.assertFalse(f.uniq)
|
|
92
|
-
|
|
93
|
+
|
|
93
94
|
# Test default value.
|
|
94
95
|
Registry.flags['ref'] = BoolFlag('ref', default=True)
|
|
95
96
|
f = Field.from_dsl(r'int, ref')
|
|
@@ -103,7 +104,7 @@ class TestFieldParser(unittest.TestCase):
|
|
|
103
104
|
self.assertEqual(f.group, 'foo')
|
|
104
105
|
f = Field.from_dsl(r'int')
|
|
105
106
|
self.assertEqual(f.group, None)
|
|
106
|
-
|
|
107
|
+
|
|
107
108
|
# Test append
|
|
108
109
|
Registry.byflags['index'] = OperFlag('index', etype=str, store='append')
|
|
109
110
|
f = Field.from_dsl(r'int, index by year')
|
|
@@ -111,6 +112,23 @@ class TestFieldParser(unittest.TestCase):
|
|
|
111
112
|
f = Field.from_dsl(r'int, index by year, index by month')
|
|
112
113
|
self.assertEqual(f.index, ['year', 'month'])
|
|
113
114
|
|
|
115
|
+
def test_datetime(self):
|
|
116
|
+
# FIXME: side effects
|
|
117
|
+
Config.date_fmt = '%Y-%m-%d'
|
|
118
|
+
Config.time_fmt = '%H:%M:%S'
|
|
119
|
+
Config.datetime_fmt = '%Y-%m-%d %H:%M:%S'
|
|
120
|
+
|
|
121
|
+
val = Field.from_dsl('date').parse('2023-10-01')
|
|
122
|
+
self.assertIsInstance(val, date)
|
|
123
|
+
self.assertEqual(val, date(2023, 10, 1))
|
|
124
|
+
|
|
125
|
+
val = Field.from_dsl('time').parse('14:30:00')
|
|
126
|
+
self.assertIsInstance(val, time)
|
|
127
|
+
self.assertEqual(val, time(14, 30, 0))
|
|
128
|
+
|
|
129
|
+
val = Field.from_dsl('datetime').parse('2023-10-01 14:30:00')
|
|
130
|
+
self.assertIsInstance(val, datetime)
|
|
131
|
+
self.assertEqual(val, datetime(2023, 10, 1, 14, 30, 0))
|
|
114
132
|
|
|
115
133
|
# ==========================
|
|
116
134
|
# Errors
|
|
@@ -119,9 +137,23 @@ class TestFieldParser(unittest.TestCase):
|
|
|
119
137
|
def test_unsupported_modifier(self):
|
|
120
138
|
with self.assertRaisesRegex(ValueError, 'unsupported type'):
|
|
121
139
|
Field.from_dsl('list of unknown')
|
|
122
|
-
|
|
140
|
+
|
|
123
141
|
with self.assertRaisesRegex(ValueError, 'unknown modifier'):
|
|
124
142
|
Field.from_dsl('int, random_mod')
|
|
125
143
|
|
|
144
|
+
|
|
145
|
+
def test_invalid_formats(self):
|
|
146
|
+
# FIXME: side effects
|
|
147
|
+
Config.date_fmt = '%Y-%m-%d'
|
|
148
|
+
Config.time_fmt = '%H:%M:%S'
|
|
149
|
+
Config.datetime_fmt = '%Y-%m-%d %H:%M:%S'
|
|
150
|
+
|
|
151
|
+
with self.assertRaisesRegex(ValueError, 'failed to parse'):
|
|
152
|
+
Field.from_dsl('date').parse('not-a-date')
|
|
153
|
+
|
|
154
|
+
with self.assertRaisesRegex(ValueError, 'failed to parse'):
|
|
155
|
+
Field.from_dsl('datetime').parse('2023/13/45')
|
|
156
|
+
|
|
157
|
+
|
|
126
158
|
if __name__ == '__main__':
|
|
127
159
|
unittest.main()
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
sphinxnotes.data
|
|
3
|
-
~~~~~~~~~~~~~~~~
|
|
4
|
-
|
|
5
|
-
:copyright: Copyright 2025 by the Shengyu Zhang.
|
|
6
|
-
:license: BSD, see LICENSE for details.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
from typing import TYPE_CHECKING
|
|
11
|
-
|
|
12
|
-
from sphinx.util import logging
|
|
13
|
-
|
|
14
|
-
from . import meta
|
|
15
|
-
from .data import (
|
|
16
|
-
Registry, Form, BoolFlag, OperFlag,
|
|
17
|
-
Value, ValueWrapper,
|
|
18
|
-
RawData, Data,
|
|
19
|
-
Field, Schema,
|
|
20
|
-
)
|
|
21
|
-
from .template import Phase, Template
|
|
22
|
-
from .render import (
|
|
23
|
-
Caller,
|
|
24
|
-
pending_node, RenderedNode, rendered_node, rendered_inline_node,
|
|
25
|
-
BaseDataDefiner, BaseDataDefineRole, BaseDataDefineDirective,
|
|
26
|
-
StrictDataDefineDirective,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
if TYPE_CHECKING:
|
|
30
|
-
from sphinx.application import Sphinx
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"""Python API for other Sphinx extesions."""
|
|
34
|
-
__all__ = [
|
|
35
|
-
'Registry', 'Form', 'BoolFlag', 'OperFlag',
|
|
36
|
-
'Value', 'ValueWrapper',
|
|
37
|
-
'RawData', 'Data',
|
|
38
|
-
'Field', 'Schema',
|
|
39
|
-
|
|
40
|
-
'Phase', 'Template',
|
|
41
|
-
|
|
42
|
-
'Caller',
|
|
43
|
-
'pending_node', 'RenderedNode', 'rendered_node', 'rendered_inline_node',
|
|
44
|
-
'BaseDataDefiner', 'BaseDataDefineRole', 'BaseDataDefineDirective',
|
|
45
|
-
'BaseDataDefineDirective', 'StrictDataDefineDirective',
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
logger = logging.getLogger(__name__)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def setup(app: Sphinx):
|
|
52
|
-
meta.pre_setup(app)
|
|
53
|
-
|
|
54
|
-
from . import template
|
|
55
|
-
from . import render
|
|
56
|
-
from . import adhoc
|
|
57
|
-
|
|
58
|
-
template.setup(app)
|
|
59
|
-
render.setup(app)
|
|
60
|
-
adhoc.setup(app)
|
|
61
|
-
|
|
62
|
-
return meta.post_setup(app)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes/data/utils/context_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
{sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/requires.txt
RENAMED
|
File without changes
|
{sphinxnotes_data-1.0a2 → sphinxnotes_data-1.0a4}/src/sphinxnotes_data.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|