omlish 0.0.0.dev374__py3-none-any.whl → 0.0.0.dev376__py3-none-any.whl
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.
- omlish/__about__.py +2 -2
- omlish/collections/__init__.py +0 -4
- omlish/collections/mappings.py +19 -1
- omlish/collections/utils.py +2 -3
- omlish/funcs/builders.py +162 -0
- omlish/funcs/match.py +2 -0
- omlish/lang/__init__.py +4 -0
- omlish/lite/__init__.py +1 -1
- omlish/lite/json.py +2 -2
- omlish/lite/marshal.py +1 -1
- omlish/testing/unittest/loading.py +9 -9
- omlish/testing/unittest/main.py +23 -21
- omlish/testing/unittest/running.py +28 -27
- omlish/testing/unittest/types.py +1 -1
- omlish/text/minja.py +8 -28
- {omlish-0.0.0.dev374.dist-info → omlish-0.0.0.dev376.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev374.dist-info → omlish-0.0.0.dev376.dist-info}/RECORD +22 -21
- /omlish/{collections → lang}/errors.py +0 -0
- {omlish-0.0.0.dev374.dist-info → omlish-0.0.0.dev376.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev374.dist-info → omlish-0.0.0.dev376.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev374.dist-info → omlish-0.0.0.dev376.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev374.dist-info → omlish-0.0.0.dev376.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/collections/__init__.py
CHANGED
omlish/collections/mappings.py
CHANGED
@@ -66,9 +66,27 @@ class TypeMap(ta.Generic[T]):
|
|
66
66
|
def get(self, ty: type[T]) -> T | None:
|
67
67
|
return self._dct.get(ty)
|
68
68
|
|
69
|
-
def __getitem__(self, ty: type[T]) ->
|
69
|
+
def __getitem__(self, ty: type[T]) -> T:
|
70
70
|
return self._dct[ty]
|
71
71
|
|
72
|
+
_any_dct: dict[type | tuple[type, ...], tuple[T, ...]]
|
73
|
+
|
74
|
+
def get_any(self, cls: type | tuple[type, ...]) -> ta.Sequence[T]:
|
75
|
+
try:
|
76
|
+
any_dct = self._any_dct
|
77
|
+
except AttributeError:
|
78
|
+
any_dct = {}
|
79
|
+
self._any_dct = any_dct
|
80
|
+
|
81
|
+
try:
|
82
|
+
return any_dct[cls]
|
83
|
+
except KeyError:
|
84
|
+
pass
|
85
|
+
|
86
|
+
ret = tuple(tv for tv in self if isinstance(tv, cls))
|
87
|
+
any_dct[cls] = ret
|
88
|
+
return ret
|
89
|
+
|
72
90
|
|
73
91
|
class DynamicTypeMap(ta.Generic[V]):
|
74
92
|
def __init__(self, items: ta.Iterable[V] = (), *, weak: bool = False) -> None:
|
omlish/collections/utils.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import typing as ta
|
2
2
|
|
3
3
|
from .. import lang
|
4
|
-
from .errors import DuplicateKeyError
|
5
4
|
from .identity import IdentityKeyDict
|
6
5
|
from .identity import IdentitySet
|
7
6
|
|
@@ -48,7 +47,7 @@ def unique(
|
|
48
47
|
k = key(e)
|
49
48
|
if k in seen:
|
50
49
|
if strict:
|
51
|
-
raise DuplicateKeyError(k, e)
|
50
|
+
raise lang.DuplicateKeyError(k, e)
|
52
51
|
else:
|
53
52
|
seen.add(k)
|
54
53
|
ret.append(e)
|
@@ -88,7 +87,7 @@ def make_map(
|
|
88
87
|
for k, v in kvs:
|
89
88
|
if k in d:
|
90
89
|
if strict:
|
91
|
-
raise DuplicateKeyError(k)
|
90
|
+
raise lang.DuplicateKeyError(k)
|
92
91
|
else:
|
93
92
|
d[k] = v
|
94
93
|
return d
|
omlish/funcs/builders.py
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# ruff: noqa: UP006 UP045
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import io
|
5
|
+
import os.path
|
6
|
+
import sys
|
7
|
+
import typing as ta
|
8
|
+
|
9
|
+
from ..lite.cached import cached_nullary
|
10
|
+
from ..lite.check import check
|
11
|
+
from ..os.paths import is_path_in_dir
|
12
|
+
|
13
|
+
|
14
|
+
##
|
15
|
+
|
16
|
+
|
17
|
+
class FnBuilder(abc.ABC):
|
18
|
+
@abc.abstractmethod
|
19
|
+
def build_fn(
|
20
|
+
self,
|
21
|
+
name: str,
|
22
|
+
src: str,
|
23
|
+
ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
24
|
+
) -> ta.Callable:
|
25
|
+
...
|
26
|
+
|
27
|
+
|
28
|
+
#
|
29
|
+
|
30
|
+
|
31
|
+
class SimpleFnBuilder(FnBuilder):
|
32
|
+
def build_fn(
|
33
|
+
self,
|
34
|
+
name: str,
|
35
|
+
src: str,
|
36
|
+
ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
37
|
+
) -> ta.Callable:
|
38
|
+
ns = dict(ns or {})
|
39
|
+
exec(src, ns)
|
40
|
+
return ns[name]
|
41
|
+
|
42
|
+
|
43
|
+
build_fn = SimpleFnBuilder().build_fn
|
44
|
+
|
45
|
+
|
46
|
+
#
|
47
|
+
|
48
|
+
|
49
|
+
class DebugFnBuilder(FnBuilder):
|
50
|
+
def __init__(
|
51
|
+
self,
|
52
|
+
*,
|
53
|
+
mod_name_prefix: ta.Optional[str] = None,
|
54
|
+
src_dir: ta.Optional[str] = None,
|
55
|
+
) -> None:
|
56
|
+
super().__init__()
|
57
|
+
|
58
|
+
if mod_name_prefix is None:
|
59
|
+
mod_name_prefix = f'_{self.__class__.__name__}_{id(self):x}_'
|
60
|
+
self._mod_name_prefix = mod_name_prefix
|
61
|
+
|
62
|
+
self._given_src_dir = src_dir
|
63
|
+
|
64
|
+
self._num_fns = 0
|
65
|
+
self._mod_names: ta.Set[str] = set()
|
66
|
+
self._installed_sys_path = False
|
67
|
+
|
68
|
+
@cached_nullary
|
69
|
+
def _src_dir(self) -> str:
|
70
|
+
if self._given_src_dir is not None:
|
71
|
+
return self._given_src_dir
|
72
|
+
else:
|
73
|
+
return __import__('tempfile').mkdtemp(prefix=f'_{self.__class__.__name__}_{os.getpid()}__') # noqa
|
74
|
+
|
75
|
+
@cached_nullary
|
76
|
+
def _install_sys_path(self) -> None:
|
77
|
+
if (src_dir := self._src_dir()) not in sys.path:
|
78
|
+
self._installed_sys_path = True
|
79
|
+
sys.path.append(src_dir)
|
80
|
+
|
81
|
+
def uninstall_sys_path(self) -> None:
|
82
|
+
if self._installed_sys_path:
|
83
|
+
while True:
|
84
|
+
try:
|
85
|
+
sys.path.remove(self._src_dir())
|
86
|
+
except ValueError:
|
87
|
+
break
|
88
|
+
self._installed_sys_path = True
|
89
|
+
|
90
|
+
def _gen_mod_name(
|
91
|
+
self,
|
92
|
+
fn_num: int,
|
93
|
+
fn_name: str,
|
94
|
+
*,
|
95
|
+
suffix: ta.Optional[str] = None,
|
96
|
+
) -> str:
|
97
|
+
mod_name = f'{self._mod_name_prefix}{fn_num}__{fn_name}{suffix or ""}'
|
98
|
+
|
99
|
+
check.not_in(mod_name, self._mod_names)
|
100
|
+
check.not_in(mod_name, sys.modules)
|
101
|
+
|
102
|
+
self._mod_names.add(mod_name)
|
103
|
+
|
104
|
+
return mod_name
|
105
|
+
|
106
|
+
def _build_mod(
|
107
|
+
self,
|
108
|
+
mod_name: str,
|
109
|
+
mod_src: str,
|
110
|
+
) -> ta.Any:
|
111
|
+
self._install_sys_path()
|
112
|
+
|
113
|
+
src_dir = self._src_dir()
|
114
|
+
src_file = os.path.join(src_dir, f'{mod_name}.py')
|
115
|
+
check.state(is_path_in_dir(src_dir, src_file))
|
116
|
+
|
117
|
+
with open(src_file, 'w') as f:
|
118
|
+
f.write(mod_src)
|
119
|
+
|
120
|
+
mod = __import__(mod_name)
|
121
|
+
|
122
|
+
check.equal(mod.__file__, src_file)
|
123
|
+
|
124
|
+
return mod
|
125
|
+
|
126
|
+
def build_fn(
|
127
|
+
self,
|
128
|
+
name: str,
|
129
|
+
src: str,
|
130
|
+
ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
131
|
+
) -> ta.Callable:
|
132
|
+
fn_num = self._num_fns
|
133
|
+
self._num_fns += 1
|
134
|
+
|
135
|
+
mod_name = self._gen_mod_name(fn_num, name)
|
136
|
+
|
137
|
+
src_preamble: ta.List[str] = []
|
138
|
+
|
139
|
+
if ns:
|
140
|
+
ns_mod_name = self._gen_mod_name(fn_num, name, suffix='__ns')
|
141
|
+
ns_mod = self._build_mod(ns_mod_name, '')
|
142
|
+
|
143
|
+
for k, v in ns.items():
|
144
|
+
setattr(ns_mod, k, v)
|
145
|
+
|
146
|
+
src_preamble.append(f'from {ns_mod_name} import (')
|
147
|
+
for k in sorted(ns):
|
148
|
+
src_preamble.append(f' {k},')
|
149
|
+
src_preamble.append(')')
|
150
|
+
|
151
|
+
src_sb = io.StringIO()
|
152
|
+
if src_preamble:
|
153
|
+
src_sb.write('\n'.join(src_preamble))
|
154
|
+
src_sb.write('\n\n')
|
155
|
+
src_sb.write(src)
|
156
|
+
if not src.endswith('\n'):
|
157
|
+
src_sb.write('\n')
|
158
|
+
|
159
|
+
mod = self._build_mod(mod_name, src_sb.getvalue())
|
160
|
+
|
161
|
+
fn = mod.__dict__[name]
|
162
|
+
return fn
|
omlish/funcs/match.py
CHANGED
omlish/lang/__init__.py
CHANGED
omlish/lite/__init__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# @omlish-lite
|
2
2
|
"""
|
3
|
-
These are the 'core' lite modules. These generally have a 'full' equivalent, in which case
|
3
|
+
These are the 'core' lite modules. These generally have a 'full' equivalent, in which case standard code should prefer
|
4
4
|
that.
|
5
5
|
"""
|
omlish/lite/json.py
CHANGED
@@ -12,7 +12,7 @@ JSON_PRETTY_KWARGS: ta.Mapping[str, ta.Any] = dict(
|
|
12
12
|
indent=JSON_PRETTY_INDENT,
|
13
13
|
)
|
14
14
|
|
15
|
-
json_dump_pretty: ta.Callable[...,
|
15
|
+
json_dump_pretty: ta.Callable[..., None] = functools.partial(json.dump, **JSON_PRETTY_KWARGS)
|
16
16
|
json_dumps_pretty: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_PRETTY_KWARGS)
|
17
17
|
|
18
18
|
|
@@ -26,5 +26,5 @@ JSON_COMPACT_KWARGS: ta.Mapping[str, ta.Any] = dict(
|
|
26
26
|
separators=JSON_COMPACT_SEPARATORS,
|
27
27
|
)
|
28
28
|
|
29
|
-
json_dump_compact: ta.Callable[...,
|
29
|
+
json_dump_compact: ta.Callable[..., None] = functools.partial(json.dump, **JSON_COMPACT_KWARGS)
|
30
30
|
json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
|
omlish/lite/marshal.py
CHANGED
@@ -43,13 +43,13 @@ import types
|
|
43
43
|
import typing as ta
|
44
44
|
import unittest
|
45
45
|
|
46
|
-
from .types import
|
46
|
+
from .types import UnittestTest
|
47
47
|
|
48
48
|
|
49
49
|
##
|
50
50
|
|
51
51
|
|
52
|
-
class
|
52
|
+
class UnittestTargetLoader:
|
53
53
|
def __init__(
|
54
54
|
self,
|
55
55
|
*,
|
@@ -81,7 +81,7 @@ class TestTargetLoader:
|
|
81
81
|
pattern: ta.Optional[str] = None
|
82
82
|
top: ta.Optional[str] = None
|
83
83
|
|
84
|
-
def load(self, target: Target) ->
|
84
|
+
def load(self, target: Target) -> UnittestTest:
|
85
85
|
loader = self._loader
|
86
86
|
if loader is None:
|
87
87
|
loader = unittest.loader.TestLoader()
|
@@ -89,8 +89,8 @@ class TestTargetLoader:
|
|
89
89
|
if self._test_name_patterns:
|
90
90
|
loader.testNamePatterns = self._test_name_patterns # type: ignore[assignment]
|
91
91
|
|
92
|
-
if isinstance(target,
|
93
|
-
return ta.cast(
|
92
|
+
if isinstance(target, UnittestTargetLoader.DiscoveryTarget):
|
93
|
+
return ta.cast(UnittestTest, loader.discover(
|
94
94
|
target.start, # type: ignore[arg-type]
|
95
95
|
target.pattern, # type: ignore[arg-type]
|
96
96
|
target.top,
|
@@ -102,11 +102,11 @@ class TestTargetLoader:
|
|
102
102
|
for part in module.split('.')[1:]:
|
103
103
|
module = getattr(module, part)
|
104
104
|
|
105
|
-
if isinstance(target,
|
106
|
-
return ta.cast(
|
105
|
+
if isinstance(target, UnittestTargetLoader.ModuleTarget):
|
106
|
+
return ta.cast(UnittestTest, loader.loadTestsFromModule(module))
|
107
107
|
|
108
|
-
elif isinstance(target,
|
109
|
-
return ta.cast(
|
108
|
+
elif isinstance(target, UnittestTargetLoader.NamesTarget):
|
109
|
+
return ta.cast(UnittestTest, loader.loadTestsFromNames(
|
110
110
|
target.test_names, # type: ignore[arg-type]
|
111
111
|
module,
|
112
112
|
))
|
omlish/testing/unittest/main.py
CHANGED
@@ -49,25 +49,14 @@ import sys
|
|
49
49
|
import types
|
50
50
|
import typing as ta
|
51
51
|
|
52
|
-
from .loading import
|
53
|
-
from .running import
|
52
|
+
from .loading import UnittestTargetLoader
|
53
|
+
from .running import UnittestTestRunner
|
54
54
|
|
55
55
|
|
56
56
|
##
|
57
57
|
|
58
58
|
|
59
|
-
|
60
|
-
if obj is None:
|
61
|
-
return {}
|
62
|
-
|
63
|
-
return {
|
64
|
-
a: v
|
65
|
-
for a in attrs
|
66
|
-
if (v := getattr(obj, a, None)) is not None
|
67
|
-
}
|
68
|
-
|
69
|
-
|
70
|
-
class TestRunCli:
|
59
|
+
class UnittestRunCli:
|
71
60
|
def __init__(self) -> None:
|
72
61
|
super().__init__()
|
73
62
|
|
@@ -195,9 +184,22 @@ class TestRunCli:
|
|
195
184
|
|
196
185
|
#
|
197
186
|
|
187
|
+
@staticmethod
|
188
|
+
def _get_attr_dict(obj: ta.Optional[ta.Any], *attrs: str) -> ta.Dict[str, ta.Any]:
|
189
|
+
if obj is None:
|
190
|
+
return {}
|
191
|
+
|
192
|
+
return {
|
193
|
+
a: v
|
194
|
+
for a in attrs
|
195
|
+
if (v := getattr(obj, a, None)) is not None
|
196
|
+
}
|
197
|
+
|
198
|
+
#
|
199
|
+
|
198
200
|
IMPORT_PATH_PAT = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*')
|
199
201
|
|
200
|
-
def _build_target(self, name: str, args: ParsedArgs) ->
|
202
|
+
def _build_target(self, name: str, args: ParsedArgs) -> UnittestTargetLoader.Target:
|
201
203
|
is_discovery = False
|
202
204
|
if os.path.isdir(name):
|
203
205
|
is_discovery = True
|
@@ -207,12 +209,12 @@ class TestRunCli:
|
|
207
209
|
is_discovery = True
|
208
210
|
|
209
211
|
if not is_discovery:
|
210
|
-
return
|
212
|
+
return UnittestTargetLoader.NamesTarget([name])
|
211
213
|
|
212
214
|
else:
|
213
|
-
return
|
215
|
+
return UnittestTargetLoader.DiscoveryTarget(
|
214
216
|
start=name,
|
215
|
-
**_get_attr_dict(
|
217
|
+
**self._get_attr_dict(
|
216
218
|
args.args,
|
217
219
|
'pattern',
|
218
220
|
'top',
|
@@ -229,7 +231,7 @@ class TestRunCli:
|
|
229
231
|
*,
|
230
232
|
exit: bool = False, # noqa
|
231
233
|
) -> None:
|
232
|
-
loader =
|
234
|
+
loader = UnittestTargetLoader(**self._get_attr_dict(
|
233
235
|
args.args,
|
234
236
|
'test_name_patterns',
|
235
237
|
))
|
@@ -239,7 +241,7 @@ class TestRunCli:
|
|
239
241
|
for target_arg in (args.args.target if args.args is not None else None) or [] # noqa
|
240
242
|
]
|
241
243
|
|
242
|
-
runner =
|
244
|
+
runner = UnittestTestRunner(UnittestTestRunner.Args(**self._get_attr_dict(
|
243
245
|
args.args,
|
244
246
|
'verbosity',
|
245
247
|
'failfast',
|
@@ -266,7 +268,7 @@ class TestRunCli:
|
|
266
268
|
|
267
269
|
|
268
270
|
def _main() -> None:
|
269
|
-
cli =
|
271
|
+
cli = UnittestRunCli()
|
270
272
|
args = cli.parse_args(sys.argv[1:])
|
271
273
|
cli.run(args, exit=True)
|
272
274
|
|
@@ -45,30 +45,13 @@ import typing as ta
|
|
45
45
|
import unittest
|
46
46
|
import warnings
|
47
47
|
|
48
|
-
from .types import
|
48
|
+
from .types import UnittestTest
|
49
49
|
|
50
50
|
|
51
51
|
##
|
52
52
|
|
53
53
|
|
54
|
-
class
|
55
|
-
def __init__(self, stream):
|
56
|
-
super().__init__()
|
57
|
-
|
58
|
-
self.stream = stream
|
59
|
-
|
60
|
-
def __getattr__(self, attr):
|
61
|
-
if attr in ('stream', '__getstate__'):
|
62
|
-
raise AttributeError(attr)
|
63
|
-
return getattr(self.stream, attr)
|
64
|
-
|
65
|
-
def writeln(self, arg=None):
|
66
|
-
if arg:
|
67
|
-
self.write(arg)
|
68
|
-
self.write('\n') # text-mode streams translate to \r\n if needed
|
69
|
-
|
70
|
-
|
71
|
-
class TestRunner:
|
54
|
+
class UnittestTestRunner:
|
72
55
|
"""
|
73
56
|
A test runner class that displays results in textual form.
|
74
57
|
|
@@ -98,7 +81,25 @@ class TestRunner:
|
|
98
81
|
|
99
82
|
if stream is None:
|
100
83
|
stream = sys.stderr
|
101
|
-
self._stream = _WritelnDecorator(stream)
|
84
|
+
self._stream = UnittestTestRunner._WritelnDecorator(stream)
|
85
|
+
|
86
|
+
#
|
87
|
+
|
88
|
+
class _WritelnDecorator:
|
89
|
+
def __init__(self, stream):
|
90
|
+
super().__init__()
|
91
|
+
|
92
|
+
self.stream = stream
|
93
|
+
|
94
|
+
def __getattr__(self, attr):
|
95
|
+
if attr in ('stream', '__getstate__'):
|
96
|
+
raise AttributeError(attr)
|
97
|
+
return getattr(self.stream, attr)
|
98
|
+
|
99
|
+
def writeln(self, arg=None):
|
100
|
+
if arg:
|
101
|
+
self.write(arg)
|
102
|
+
self.write('\n') # text-mode streams translate to \r\n if needed
|
102
103
|
|
103
104
|
#
|
104
105
|
|
@@ -179,7 +180,7 @@ class TestRunner:
|
|
179
180
|
|
180
181
|
time_taken = stop_time - start_time
|
181
182
|
|
182
|
-
return
|
183
|
+
return UnittestTestRunner._InternalRunTestResult(
|
183
184
|
result,
|
184
185
|
time_taken,
|
185
186
|
)
|
@@ -206,7 +207,7 @@ class TestRunner:
|
|
206
207
|
unexpected_successes: ta.Sequence[str]
|
207
208
|
|
208
209
|
@classmethod
|
209
|
-
def merge(cls, results: ta.Iterable['
|
210
|
+
def merge(cls, results: ta.Iterable['UnittestTestRunner.RunResult']) -> 'UnittestTestRunner.RunResult':
|
210
211
|
def reduce_attr(fn, a):
|
211
212
|
return fn(getattr(r, a) for r in results)
|
212
213
|
|
@@ -233,11 +234,11 @@ class TestRunner:
|
|
233
234
|
|
234
235
|
def as_test_and_reasons(l):
|
235
236
|
return [
|
236
|
-
|
237
|
+
UnittestTestRunner.RunResult.TestAndReason(result.getDescription(t), r)
|
237
238
|
for t, r in l
|
238
239
|
]
|
239
240
|
|
240
|
-
return
|
241
|
+
return UnittestTestRunner.RunResult(
|
241
242
|
raw_results=[result],
|
242
243
|
time_taken=internal_result.time_taken,
|
243
244
|
|
@@ -254,11 +255,11 @@ class TestRunner:
|
|
254
255
|
|
255
256
|
#
|
256
257
|
|
257
|
-
def run(self, test:
|
258
|
+
def run(self, test: UnittestTest) -> RunResult:
|
258
259
|
return self._build_run_result(self._internal_run_test(test))
|
259
260
|
|
260
|
-
def run_many(self, tests: ta.Iterable[
|
261
|
-
return
|
261
|
+
def run_many(self, tests: ta.Iterable[UnittestTest]) -> RunResult:
|
262
|
+
return UnittestTestRunner.RunResult.merge([self.run(t) for t in tests])
|
262
263
|
|
263
264
|
#
|
264
265
|
|
omlish/testing/unittest/types.py
CHANGED
omlish/text/minja.py
CHANGED
@@ -10,6 +10,8 @@ import io
|
|
10
10
|
import re
|
11
11
|
import typing as ta
|
12
12
|
|
13
|
+
from ..funcs.builders import FnBuilder
|
14
|
+
from ..funcs.builders import SimpleFnBuilder
|
13
15
|
from ..lite.cached import cached_nullary
|
14
16
|
from ..lite.check import check
|
15
17
|
from ..lite.maybes import Maybe
|
@@ -76,19 +78,9 @@ class MinjaTemplate:
|
|
76
78
|
##
|
77
79
|
|
78
80
|
|
79
|
-
class MinjaFunctionBuilder(ta.Protocol):
|
80
|
-
def __call__(
|
81
|
-
self,
|
82
|
-
name: str,
|
83
|
-
src: str,
|
84
|
-
ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
85
|
-
) -> ta.Callable:
|
86
|
-
...
|
87
|
-
|
88
|
-
|
89
81
|
class MinjaTemplateCompiler:
|
90
82
|
"""
|
91
|
-
Compiles a template string into a Python
|
83
|
+
Compiles a template string into a Python fn. The returned fn takes a dictionary 'context' and returns
|
92
84
|
the rendered string.
|
93
85
|
|
94
86
|
Supported syntax:
|
@@ -111,7 +103,7 @@ class MinjaTemplateCompiler:
|
|
111
103
|
fragment_processor: ta.Optional[ta.Callable[[MinjaTemplateFragmentKind, str], str]] = None,
|
112
104
|
strict_strings: bool = False,
|
113
105
|
stringifier: ta.Optional[ta.Callable[[ta.Any], str]] = None,
|
114
|
-
|
106
|
+
fn_builder: ta.Optional[FnBuilder] = None,
|
115
107
|
) -> None:
|
116
108
|
super().__init__()
|
117
109
|
|
@@ -136,9 +128,9 @@ class MinjaTemplateCompiler:
|
|
136
128
|
stringifier = lambda o: str(o)
|
137
129
|
self._stringifier = stringifier
|
138
130
|
|
139
|
-
if
|
140
|
-
|
141
|
-
self.
|
131
|
+
if fn_builder is None:
|
132
|
+
fn_builder = SimpleFnBuilder()
|
133
|
+
self._fn_builder = fn_builder
|
142
134
|
|
143
135
|
self._stack: ta.List[ta.Literal['for', 'if']] = []
|
144
136
|
|
@@ -148,18 +140,6 @@ class MinjaTemplateCompiler:
|
|
148
140
|
raise TypeError(o)
|
149
141
|
return o
|
150
142
|
|
151
|
-
@staticmethod
|
152
|
-
def _build_function(
|
153
|
-
name: str,
|
154
|
-
src: str,
|
155
|
-
ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
|
156
|
-
) -> ta.Callable:
|
157
|
-
glo: dict = {}
|
158
|
-
if ns:
|
159
|
-
glo.update(ns)
|
160
|
-
exec(src, glo)
|
161
|
-
return glo[name]
|
162
|
-
|
163
143
|
#
|
164
144
|
|
165
145
|
_TAG_PAT = re.compile(
|
@@ -315,7 +295,7 @@ class MinjaTemplateCompiler:
|
|
315
295
|
raise KeyError(k)
|
316
296
|
ns[k] = v
|
317
297
|
|
318
|
-
render_fn = self.
|
298
|
+
render_fn = self._fn_builder.build_fn(
|
319
299
|
self._RENDER_FN_NAME,
|
320
300
|
rendered.src,
|
321
301
|
ns,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
omlish/.manifests.json,sha256=aT8yZ-Zh-9wfHl5Ym5ouiWC1i0cy7Q7RlhzavB6VLPI,8587
|
2
|
-
omlish/__about__.py,sha256=
|
2
|
+
omlish/__about__.py,sha256=lX9Qff_gTIwCTfkKiodK4oXEtPuWYPT7T-APjawfFVY,3478
|
3
3
|
omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
|
4
4
|
omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
|
5
5
|
omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
|
@@ -76,20 +76,19 @@ omlish/codecs/funcs.py,sha256=or0Jogczuzk7csDTRl-HURMEjl8LXXqxxXYK45xcM5w,855
|
|
76
76
|
omlish/codecs/registry.py,sha256=2FnO5YP7ui1LzkguwESY0MP3WIdwgPTIJTM_4RyTOEg,3896
|
77
77
|
omlish/codecs/standard.py,sha256=eiZ4u9ep0XrA4Z_D1zJI0vmWyuN8HLrX4Se_r_Cq_ZM,60
|
78
78
|
omlish/codecs/text.py,sha256=MgAzXapiHie-hhLBmcho67WXfWbmhlHz4tNPcHXnWUk,5711
|
79
|
-
omlish/collections/__init__.py,sha256
|
79
|
+
omlish/collections/__init__.py,sha256=-CcIq7e8OkQakexxpJOXf2q6LdVqzo7B-quPtwur_BQ,2634
|
80
80
|
omlish/collections/abc.py,sha256=p9zhL5oNV5WPyWmMn34fWfkuxPQAjOtL7WQA-Xsyhwk,2628
|
81
81
|
omlish/collections/bimap.py,sha256=3szDCscPJlFRtkpyVQNWneg4s50mr6Rd0jdTzVEIcnE,1661
|
82
82
|
omlish/collections/coerce.py,sha256=tAls15v_7p5bUN33R7Zbko87KW5toWHl9fRialCqyNY,7030
|
83
|
-
omlish/collections/errors.py,sha256=shcS-NCnEUudF8qC_SmO2TQyjivKlS4TDjaz_faqQ0c,44
|
84
83
|
omlish/collections/frozen.py,sha256=LMbAHYDENIQk1hvjCTvpnx66m1TalrHa4CSn8n_tsXQ,4142
|
85
84
|
omlish/collections/hasheq.py,sha256=uHypfZlHhicQPvx9OOlpT9MSLwfc_mFil-WaxF9dTOo,3732
|
86
85
|
omlish/collections/identity.py,sha256=S_W508EkU-AW0TZ7cv1wWUc6EG54vww4XbcfGjDFTgg,4793
|
87
|
-
omlish/collections/mappings.py,sha256=
|
86
|
+
omlish/collections/mappings.py,sha256=lAzKP9RONLjFH7emIDoY-6jukuD-Sa7fdxzP7u6qrqU,3261
|
88
87
|
omlish/collections/multimaps.py,sha256=8Sqi1iVxUObnL47s8b1esa2TERoiEyyY4RBWF1VjDiw,3820
|
89
88
|
omlish/collections/ordered.py,sha256=XV_ufr3QycjQ3nIy9L3BufdBosWD-3wb5xVZEHpZocQ,2467
|
90
89
|
omlish/collections/ranked.py,sha256=McB8C2UQfUvrbmxGTpBz1-EZuyCLkBFtktzncMdt8_Y,2287
|
91
90
|
omlish/collections/unmodifiable.py,sha256=0ZkA_0paWlkgaU6OupFdSV0yFP--7XoI3lqhs3Ci6ME,4951
|
92
|
-
omlish/collections/utils.py,sha256=
|
91
|
+
omlish/collections/utils.py,sha256=JYBYoConWxApqJLOjXR6GuQI76G0KQDjg6kkXrTofUw,4063
|
93
92
|
omlish/collections/cache/__init__.py,sha256=D1gO71VcwxFTZP9gAc9isHfg_TEdalwhsJcgGLvS9hg,233
|
94
93
|
omlish/collections/cache/descriptor.py,sha256=5SOsKNxnhisJY22l7tujMOI6MGGr6TERzgsfjvGXOyA,5013
|
95
94
|
omlish/collections/cache/impl.py,sha256=Y18OcAsNL6dIWuk89UlZBpqq0iBU-P4VDio6eis43Us,14760
|
@@ -305,8 +304,9 @@ omlish/formats/toml/codec.py,sha256=5HFGWEPd9IFxPlRMRheX8FEDlRIzLe1moHEOj2_PFKU,
|
|
305
304
|
omlish/formats/toml/parser.py,sha256=O2M0penQV3t8NAsq_conJjvTsXI8iivUFuBg2a5J3dU,30643
|
306
305
|
omlish/formats/toml/writer.py,sha256=kLLQNEA_Kzd3ue7UXPQ_torOKoaLT82W16Bt99sID-w,3231
|
307
306
|
omlish/funcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
307
|
+
omlish/funcs/builders.py,sha256=mJkgvJxM98x5pbMQzRY95tRUsbAfy_ivHtt4fDKfzsM,3984
|
308
308
|
omlish/funcs/genmachine.py,sha256=D9dChaliNBIjYE6lJP5ctcVQUCffNBhceyaaLvBJ7ns,2578
|
309
|
-
omlish/funcs/match.py,sha256=
|
309
|
+
omlish/funcs/match.py,sha256=EPeKojvecnJuDEWaXEYuef0Sx1J6Y0uTL01h4Z8ldxc,6199
|
310
310
|
omlish/funcs/pairs.py,sha256=VCkZjDmJGtR76BsejsHNfb4TcpHCtkkmak-zWDFchAo,3904
|
311
311
|
omlish/funcs/pipes.py,sha256=E7Sz8Aj8ke_vCs5AMNwg1I36kRdHVGTnzxVQaDyn43U,2490
|
312
312
|
omlish/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -419,7 +419,7 @@ omlish/iterators/iterators.py,sha256=RxW35yQ5ed8vBQ22IqpDXFx-i5JiLQdp7-pkMZXhJJ8
|
|
419
419
|
omlish/iterators/recipes.py,sha256=wOwOZg-zWG9Zc3wcAxJFSe2rtavVBYwZOfG09qYEx_4,472
|
420
420
|
omlish/iterators/tools.py,sha256=M16LXrJhMdsz5ea2qH0vws30ZvhQuQSCVFSLpRf_gTg,2096
|
421
421
|
omlish/iterators/unique.py,sha256=Nw0pSaNEcHAkve0ugfLPvJcirDOn9ECyC5wIL8JlJKI,1395
|
422
|
-
omlish/lang/__init__.py,sha256=
|
422
|
+
omlish/lang/__init__.py,sha256=1XbAE88POrWtIHreNk47HAcl6Ch15uTH4cJFo2L7wFM,6619
|
423
423
|
omlish/lang/attrs.py,sha256=zFiVuGVOq88x45464T_LxDa-ZEq_RD9zJLq2zeVEBDc,5105
|
424
424
|
omlish/lang/casing.py,sha256=cFUlbDdXLhwnWwcYx4qnM5c4zGX7hIRUfcjiZbxUD28,4636
|
425
425
|
omlish/lang/clsdct.py,sha256=HAGIvBSbCefzRjXriwYSBLO7QHKRv2UsE78jixOb-fA,1828
|
@@ -429,6 +429,7 @@ omlish/lang/contextmanagers.py,sha256=7mbTG7yIwM2whQBuhja5Tt0C4kfefBUW4Y_AIn5j8i
|
|
429
429
|
omlish/lang/datetimes.py,sha256=01tg21QOx-PWDlm-CSFTalym3vpqF0EKzeinmtcVNoU,379
|
430
430
|
omlish/lang/descriptors.py,sha256=zBtgO9LjdSTGHNUgiIqswh78WOVoGH6KzS0NbgB1Wls,6572
|
431
431
|
omlish/lang/enums.py,sha256=F9tflHfaAoV2MpyuhZzpfX9-H55M3zNa9hCszsngEo8,111
|
432
|
+
omlish/lang/errors.py,sha256=shcS-NCnEUudF8qC_SmO2TQyjivKlS4TDjaz_faqQ0c,44
|
432
433
|
omlish/lang/functions.py,sha256=aLdxhmqG0Pj9tBgsKdoCu_q15r82WIkNqDDSPQU19L8,5689
|
433
434
|
omlish/lang/generators.py,sha256=a4D5HU_mySs2T2z3xCmE_s3t4QJkj0YRrK4-hhpGd0A,5197
|
434
435
|
omlish/lang/imports.py,sha256=y9W9Y-d_cQ35QCLuSIPoa6vnEqSErFCz8b-34IH128U,10552
|
@@ -462,7 +463,7 @@ omlish/lifecycles/controller.py,sha256=U_4mfp3n0zxH3RgFrcAi6yODuLIR5RF-uwB2tEBdX
|
|
462
463
|
omlish/lifecycles/manager.py,sha256=92s1IH_gDP25PM5tFuPMP2hD_6s5fPi_VzZiDS5549g,5449
|
463
464
|
omlish/lifecycles/states.py,sha256=6gTdY3hn7-1sJ60lA3GeMx5RVKvXtFBBXE4KEjoO1Hs,1297
|
464
465
|
omlish/lifecycles/transitions.py,sha256=3IFdWGtAeoy3XRlIyW7yCKV4e4Iof9ytkqklGMRFYQs,1944
|
465
|
-
omlish/lite/__init__.py,sha256=
|
466
|
+
omlish/lite/__init__.py,sha256=cyZEpGob7XjU8DohyNxVe5CQRk4CQ5vrHL42OdhQb8w,148
|
466
467
|
omlish/lite/args.py,sha256=_py7azSCqPJwA2P1qY0B91sSiORsvYIK7IBO6hPDYK4,739
|
467
468
|
omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
|
468
469
|
omlish/lite/check.py,sha256=ytCkwZoKfOlJqylL-AGm8C2WfsWJd2q3kFbnZCzX3_M,13844
|
@@ -471,9 +472,9 @@ omlish/lite/contextmanagers.py,sha256=jpMxp5xwooRQJxsQ6J2ll4AJP9O7a5_YrLCGgwUFfD
|
|
471
472
|
omlish/lite/dataclasses.py,sha256=aRSCZz1jN_UI-CWJhN0SJeKxa-79vXNUZ6YOMgG31SE,3610
|
472
473
|
omlish/lite/imports.py,sha256=JDYRFxu-ofHEBfd5VV3b27oKOLhtTpuzte1_Nt7yLgw,1352
|
473
474
|
omlish/lite/inject.py,sha256=xvmLmtD3_2INnkurJQv76_Rkh9usbApEQrXJ4cvuVAk,29019
|
474
|
-
omlish/lite/json.py,sha256=
|
475
|
+
omlish/lite/json.py,sha256=m0Ce9eqUZG23-H7-oOp8n1sf4fzno5vtK4AK_4Vc-Mg,706
|
475
476
|
omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
|
476
|
-
omlish/lite/marshal.py,sha256=
|
477
|
+
omlish/lite/marshal.py,sha256=JD_8ox5-yeIo7MZ6iipCdiVxx33So52M02AtvFlRGC8,20392
|
477
478
|
omlish/lite/maybes.py,sha256=0p_fzb6yiOjEpvMKaQ53Q6CH1VPW1or7v7Lt1JIKcgM,4359
|
478
479
|
omlish/lite/pycharm.py,sha256=FRHGcCDo42UzZXqNwW_DkhI-6kb_CmJKPiQ8F6mYkLA,1174
|
479
480
|
omlish/lite/reflect.py,sha256=pzOY2PPuHH0omdtglkN6DheXDrGopdL3PtTJnejyLFU,2189
|
@@ -792,10 +793,10 @@ omlish/testing/pytest/plugins/switches/plugin.py,sha256=RBxjefl9RDJuSmT_W0lTSd9D
|
|
792
793
|
omlish/testing/pytest/plugins/switches/switches.py,sha256=lj8S9RMwUAW7a93ZqqTjoD4dRVkeGts2sl8Cn-H17hc,1890
|
793
794
|
omlish/testing/unittest/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
794
795
|
omlish/testing/unittest/__main__.py,sha256=d23loR_cKfTYZwYiqpt_CmKI7dd5WcYFgIYzqMep75E,68
|
795
|
-
omlish/testing/unittest/loading.py,sha256=
|
796
|
-
omlish/testing/unittest/main.py,sha256=
|
797
|
-
omlish/testing/unittest/running.py,sha256=
|
798
|
-
omlish/testing/unittest/types.py,sha256=
|
796
|
+
omlish/testing/unittest/loading.py,sha256=JBiCO0iv0moal2fXILic6iojLxsahMNqinIU-jMWbXg,4820
|
797
|
+
omlish/testing/unittest/main.py,sha256=R6Kevx8LyUjI2DSaE0IdhPwrO5AOXRNCJ8mYBNXJiUI,8574
|
798
|
+
omlish/testing/unittest/running.py,sha256=QBaj1ZNpMPMbPeFDmqRwuA9TMKE6O3b8Ck9QbUfuKWI,11974
|
799
|
+
omlish/testing/unittest/types.py,sha256=56ACNtKt-w_cx7Olsy-m1FYvcUHWsjou9ftXB6CWzeY,110
|
799
800
|
omlish/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
800
801
|
omlish/text/asdl.py,sha256=AS3irh-sag5pqyH3beJif78PjCbOaFso1NeKq-HXuTs,16867
|
801
802
|
omlish/text/decoding.py,sha256=sQWGckWzRslRHYKpj1SBeoo6AVqXm5HFlWFRARN1QpM,1286
|
@@ -805,7 +806,7 @@ omlish/text/glyphsplit.py,sha256=ZX9mhwTtmUE8rJpuD1jBO1CTc6xzmBCtbfiHmp5vMM8,381
|
|
805
806
|
omlish/text/indent.py,sha256=BWVVaHs_B1ppwHJJIxKCDG3iCutkYy5e1qr59Z_Suzg,1524
|
806
807
|
omlish/text/linecache.py,sha256=hRYlEhD63ZfA6_ZOTkQIcnON-3W56QMAhcG3vEJqj9M,1858
|
807
808
|
omlish/text/mangle.py,sha256=k7mYavVgxJ2ENV2wfjw3c9u3hqH5NeVpjoxYbyaYC0Y,2796
|
808
|
-
omlish/text/minja.py,sha256=
|
809
|
+
omlish/text/minja.py,sha256=7UKNalkWpTG_364OIo7p5ym--uiNPR2RFBW_W8rrO4I,9194
|
809
810
|
omlish/text/parts.py,sha256=MpiCUyfpcL4PLb2Etj8V7Yj4qofhy0xVwBrIL6RfNdg,6646
|
810
811
|
omlish/text/random.py,sha256=8feS5JE_tSjYlMl-lp0j93kCfzBae9AM2cXlRLebXMA,199
|
811
812
|
omlish/text/templating.py,sha256=Nf5BVDgrYBf0WmYwV6SnHPDvl9XVMu8v7MX78pInLTw,3325
|
@@ -889,9 +890,9 @@ omlish/typedvalues/marshal.py,sha256=AtBz7Jq-BfW8vwM7HSxSpR85JAXmxK2T0xDblmm1HI0
|
|
889
890
|
omlish/typedvalues/of_.py,sha256=UXkxSj504WI2UrFlqdZJbu2hyDwBhL7XVrc2qdR02GQ,1309
|
890
891
|
omlish/typedvalues/reflect.py,sha256=PAvKW6T4cW7u--iX80w3HWwZUS3SmIZ2_lQjT65uAyk,1026
|
891
892
|
omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
|
892
|
-
omlish-0.0.0.
|
893
|
-
omlish-0.0.0.
|
894
|
-
omlish-0.0.0.
|
895
|
-
omlish-0.0.0.
|
896
|
-
omlish-0.0.0.
|
897
|
-
omlish-0.0.0.
|
893
|
+
omlish-0.0.0.dev376.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
894
|
+
omlish-0.0.0.dev376.dist-info/METADATA,sha256=Z-x_WlkLgQst0V6us3ONJzE-m2dvOa2-aaMmnizL-JY,4416
|
895
|
+
omlish-0.0.0.dev376.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
896
|
+
omlish-0.0.0.dev376.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
|
897
|
+
omlish-0.0.0.dev376.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
|
898
|
+
omlish-0.0.0.dev376.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|