omlish 0.0.0.dev178__py3-none-any.whl → 0.0.0.dev179__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 CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev178'
2
- __revision__ = 'fd8b9c2c51797566d0d9a52a8b347895029c82f5'
1
+ __version__ = '0.0.0.dev179'
2
+ __revision__ = '40949c92a73910f364c60391f73bb2fcf9538caf'
3
3
 
4
4
 
5
5
  #
@@ -50,7 +50,8 @@ class HashProcessor(Processor):
50
50
  if self._info.params_extras.cache_hash:
51
51
  body = [
52
52
  f'try: return self.{self.CACHED_HASH_ATTR}',
53
- f'except AttributeError: object.__setattr__(self, {self.CACHED_HASH_ATTR!r}, h := hash({self_tuple}))',
53
+ f'except AttributeError: pass',
54
+ f'object.__setattr__(self, {self.CACHED_HASH_ATTR!r}, h := hash({self_tuple}))',
54
55
  f'return h',
55
56
  ]
56
57
  else:
omlish/inject/elements.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import abc
2
2
  import typing as ta
3
3
 
4
- from .. import check
5
4
  from .. import dataclasses as dc
6
5
  from .. import lang
7
6
  from .impl.origins import HasOriginsImpl
@@ -19,8 +18,8 @@ class ElementGenerator(lang.Abstract, lang.PackageSealed):
19
18
 
20
19
  @dc.dataclass(frozen=True)
21
20
  class Elements(lang.Final):
22
- es: frozenset[Element] | None = dc.xfield(None, coerce=check.of_isinstance((frozenset, None)))
23
- cs: frozenset['Elements'] | None = dc.xfield(None, coerce=check.of_isinstance((frozenset, None)))
21
+ es: ta.Collection[Element] | None = None
22
+ cs: ta.Collection['Elements'] | None = None
24
23
 
25
24
  def __iter__(self) -> ta.Generator[Element, None, None]:
26
25
  if self.es:
@@ -38,14 +37,14 @@ Elemental = ta.Union[ # noqa
38
37
 
39
38
 
40
39
  def as_elements(*args: Elemental) -> Elements:
41
- es: set[Element] = set()
42
- cs: set[Elements] = set()
40
+ es: list[Element] = []
41
+ cs: list[Elements] = []
43
42
 
44
43
  def rec(a):
45
44
  if isinstance(a, Element):
46
- es.add(a)
45
+ es.append(a)
47
46
  elif isinstance(a, Elements):
48
- cs.add(a)
47
+ cs.append(a)
49
48
  elif isinstance(a, ElementGenerator):
50
49
  for n in a:
51
50
  rec(n)
@@ -59,6 +58,6 @@ def as_elements(*args: Elemental) -> Elements:
59
58
  return next(iter(cs))
60
59
 
61
60
  return Elements(
62
- frozenset(es) if es else None,
63
- frozenset(cs) if cs else None,
61
+ es if es else None,
62
+ cs if cs else None,
64
63
  )
omlish/lite/inject.py CHANGED
@@ -291,30 +291,6 @@ def as_injector_bindings(*args: InjectorBindingOrBindings) -> InjectorBindings:
291
291
  ##
292
292
 
293
293
 
294
- @dc.dataclass(frozen=True)
295
- class OverridesInjectorBindings(InjectorBindings):
296
- p: InjectorBindings
297
- m: ta.Mapping[InjectorKey, InjectorBinding]
298
-
299
- def bindings(self) -> ta.Iterator[InjectorBinding]:
300
- for b in self.p.bindings():
301
- yield self.m.get(b.key, b)
302
-
303
-
304
- def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
305
- m: ta.Dict[InjectorKey, InjectorBinding] = {}
306
-
307
- for b in as_injector_bindings(*args).bindings():
308
- if b.key in m:
309
- raise DuplicateInjectorKeyError(b.key)
310
- m[b.key] = b
311
-
312
- return OverridesInjectorBindings(p, m)
313
-
314
-
315
- ##
316
-
317
-
318
294
  def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey, InjectorProvider]:
319
295
  pm: ta.Dict[InjectorKey, InjectorProvider] = {}
320
296
  am: ta.Dict[InjectorKey, ta.List[InjectorProvider]] = {}
@@ -338,6 +314,31 @@ def build_injector_provider_map(bs: InjectorBindings) -> ta.Mapping[InjectorKey,
338
314
  return pm
339
315
 
340
316
 
317
+ ###
318
+ # overrides
319
+
320
+
321
+ @dc.dataclass(frozen=True)
322
+ class OverridesInjectorBindings(InjectorBindings):
323
+ p: InjectorBindings
324
+ m: ta.Mapping[InjectorKey, InjectorBinding]
325
+
326
+ def bindings(self) -> ta.Iterator[InjectorBinding]:
327
+ for b in self.p.bindings():
328
+ yield self.m.get(b.key, b)
329
+
330
+
331
+ def injector_override(p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
332
+ m: ta.Dict[InjectorKey, InjectorBinding] = {}
333
+
334
+ for b in as_injector_bindings(*args).bindings():
335
+ if b.key in m:
336
+ raise DuplicateInjectorKeyError(b.key)
337
+ m[b.key] = b
338
+
339
+ return OverridesInjectorBindings(p, m)
340
+
341
+
341
342
  ###
342
343
  # scopes
343
344
 
@@ -362,7 +363,7 @@ class InjectorScope(abc.ABC): # noqa
362
363
  @dc.dataclass(frozen=True)
363
364
  class State:
364
365
  seeds: ta.Dict[InjectorKey, ta.Any]
365
- prvs: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
366
+ provisions: ta.Dict[InjectorKey, ta.Any] = dc.field(default_factory=dict)
366
367
 
367
368
  def new_state(self, vs: ta.Mapping[InjectorKey, ta.Any]) -> State:
368
369
  vs = dict(vs)
@@ -442,11 +443,11 @@ class ScopedInjectorProvider(InjectorProvider):
442
443
  def pfn(i: Injector) -> ta.Any:
443
444
  st = i[self.sc].state()
444
445
  try:
445
- return st.prvs[self.k]
446
+ return st.provisions[self.k]
446
447
  except KeyError:
447
448
  pass
448
449
  v = ufn(i)
449
- st.prvs[self.k] = v
450
+ st.provisions[self.k] = v
450
451
  return v
451
452
 
452
453
  ufn = self.p.provider_fn()
@@ -470,9 +471,7 @@ class _ScopeSeedInjectorProvider(InjectorProvider):
470
471
 
471
472
 
472
473
  def bind_injector_scope(sc: ta.Type[InjectorScope]) -> InjectorBindingOrBindings:
473
- return as_injector_bindings(
474
- InjectorBinder.bind(sc, singleton=True),
475
- )
474
+ return InjectorBinder.bind(sc, singleton=True)
476
475
 
477
476
 
478
477
  #
@@ -1012,6 +1011,8 @@ class InjectionApi:
1012
1011
  def as_bindings(self, *args: InjectorBindingOrBindings) -> InjectorBindings:
1013
1012
  return as_injector_bindings(*args)
1014
1013
 
1014
+ # overrides
1015
+
1015
1016
  def override(self, p: InjectorBindings, *args: InjectorBindingOrBindings) -> InjectorBindings:
1016
1017
  return injector_override(p, *args)
1017
1018
 
omlish/text/minja.py ADDED
@@ -0,0 +1,223 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - raw
6
+ - blocks / inheritance
7
+ """
8
+ import io
9
+ import re
10
+ import typing as ta
11
+
12
+ from omlish.lite.cached import cached_nullary
13
+ from omlish.lite.check import check
14
+
15
+
16
+ ##
17
+
18
+
19
+ class MinjaTemplate:
20
+ def __init__(self, fn: ta.Callable) -> None:
21
+ super().__init__()
22
+
23
+ self._fn = fn
24
+
25
+ def __call__(self, **kwargs: ta.Any) -> str:
26
+ return self._fn(**kwargs)
27
+
28
+
29
+ ##
30
+
31
+
32
+ class MinjaTemplateCompiler:
33
+ """
34
+ Compiles a template string into a Python function. The returned function takes a dictionary 'context' and returns
35
+ the rendered string.
36
+
37
+ Supported syntax:
38
+ - Literal text remains literal.
39
+ - {{ expr }}: Evaluates 'expr' in the given context and writes its str() to output.
40
+ - {% code %}: Executes the given Python code (e.g. 'for x in items:'), must be terminated appropriately.
41
+ - {% endfor %} to close for loops.
42
+ - {% endif %} to close if blocks.
43
+ - {# comment #}: Ignored completely.
44
+ """
45
+
46
+ DEFAULT_INDENT: str = ' '
47
+
48
+ def __init__(
49
+ self,
50
+ src: str,
51
+ args: ta.Sequence[str],
52
+ *,
53
+ indent: str = DEFAULT_INDENT,
54
+ ) -> None:
55
+ super().__init__()
56
+
57
+ self._src = check.isinstance(src, str)
58
+ self._args = check.not_isinstance(args, str)
59
+ self._indent_str: str = check.non_empty_str(indent)
60
+
61
+ self._stack: ta.List[ta.Literal['for', 'if']] = []
62
+ self._lines: ta.List[str] = []
63
+
64
+ #
65
+
66
+ _TAG_PAT = re.compile(
67
+ r'({{.*?}}|{%.*?%}|{#.*?#})',
68
+ flags=re.DOTALL,
69
+ )
70
+
71
+ @classmethod
72
+ def _split_tags(cls, src: str) -> ta.List[ta.Tuple[str, str]]:
73
+ raw = cls._TAG_PAT.split(src)
74
+
75
+ #
76
+
77
+ parts: ta.List[ta.Tuple[str, str]] = []
78
+ for s in raw:
79
+ if not s:
80
+ continue
81
+
82
+ for g, l, r in [
83
+ ('{', '{{', '}}'),
84
+ ('%', '{%', '%}'),
85
+ ('#', '{#', '#}'),
86
+ ]:
87
+ if s.startswith(l) and s.endswith(r):
88
+ parts.append((g, s[len(l):-len(r)]))
89
+ break
90
+ else:
91
+ parts.append(('', s))
92
+
93
+ #
94
+
95
+ for i, (g, s) in enumerate(parts):
96
+ if s.startswith('-'):
97
+ if i > 0:
98
+ lg, ls = parts[i - 1]
99
+ parts[i - 1] = (lg, ls.rstrip())
100
+ s = s[1:].lstrip()
101
+
102
+ if s.endswith('-'):
103
+ if i < len(parts) - 1:
104
+ rg, rs = parts[i + 1]
105
+ parts[i + 1] = (rg, rs.lstrip())
106
+ s = s[:-1].rstrip()
107
+
108
+ parts[i] = (g, s)
109
+
110
+ #
111
+
112
+ parts = [(g, s) for g, s in parts if g or s]
113
+
114
+ #
115
+
116
+ return parts
117
+
118
+ #
119
+
120
+ def _indent(self, line: str, ofs: int = 0) -> str:
121
+ return self._indent_str * (len(self._stack) + 1 + ofs) + line
122
+
123
+ #
124
+
125
+ _RENDER_FN_NAME = '__render'
126
+
127
+ @cached_nullary
128
+ def render(self) -> ta.Tuple[str, ta.Mapping[str, ta.Any]]:
129
+ parts = self._split_tags(self._src)
130
+
131
+ self._lines.append(f'def {self._RENDER_FN_NAME}({", ".join(self._args)}):')
132
+ self._lines.append(self._indent('__output = __StringIO()'))
133
+
134
+ for g, s in parts:
135
+ if g == '{':
136
+ expr = s.strip()
137
+ self._lines.append(self._indent(f'__output.write(str({expr}))'))
138
+
139
+ elif g == '%':
140
+ stmt = s.strip()
141
+
142
+ if stmt.startswith('for '):
143
+ self._lines.append(self._indent(stmt + ':'))
144
+ self._stack.append('for')
145
+ elif stmt.startswith('endfor'):
146
+ check.equal(self._stack.pop(), 'for')
147
+
148
+ elif stmt.startswith('if '):
149
+ self._lines.append(self._indent(stmt + ':'))
150
+ self._stack.append('if')
151
+ elif stmt.startswith('elif '):
152
+ check.equal(self._stack[-1], 'if')
153
+ self._lines.append(self._indent(stmt + ':', -1))
154
+ elif stmt.strip() == 'else':
155
+ check.equal(self._stack[-1], 'if')
156
+ self._lines.append(self._indent('else:', -1))
157
+ elif stmt.startswith('endif'):
158
+ check.equal(self._stack.pop(), 'if')
159
+
160
+ else:
161
+ self._lines.append(self._indent(stmt))
162
+
163
+ elif g == '#':
164
+ pass
165
+
166
+ elif not g:
167
+ if s:
168
+ safe_text = s.replace('"""', '\\"""')
169
+ self._lines.append(self._indent(f'__output.write("""{safe_text}""")'))
170
+
171
+ else:
172
+ raise KeyError(g)
173
+
174
+ check.empty(self._stack)
175
+
176
+ self._lines.append(self._indent('return __output.getvalue()'))
177
+
178
+ ns = {
179
+ '__StringIO': io.StringIO,
180
+ }
181
+
182
+ return ('\n'.join(self._lines), ns)
183
+
184
+ #
185
+
186
+ @classmethod
187
+ def _make_fn(
188
+ cls,
189
+ name: str,
190
+ src: str,
191
+ ns: ta.Optional[ta.Mapping[str, ta.Any]] = None,
192
+ ) -> ta.Callable:
193
+ glo: dict = {}
194
+ if ns:
195
+ glo.update(ns)
196
+ exec(src, glo)
197
+ return glo[name]
198
+
199
+ #
200
+
201
+ @cached_nullary
202
+ def compile(self) -> MinjaTemplate:
203
+ render_src, render_ns = self.render()
204
+
205
+ render_fn = self._make_fn(
206
+ self._RENDER_FN_NAME,
207
+ render_src,
208
+ render_ns,
209
+ )
210
+
211
+ return MinjaTemplate(render_fn)
212
+
213
+
214
+ ##
215
+
216
+
217
+ def compile_minja_template(src: str, args: ta.Sequence[str] = ()) -> MinjaTemplate:
218
+ return MinjaTemplateCompiler(src, args).compile()
219
+
220
+
221
+ def render_minja_template(src: str, **kwargs: ta.Any) -> str:
222
+ tmpl = compile_minja_template(src, list(kwargs))
223
+ return tmpl(**kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omlish
3
- Version: 0.0.0.dev178
3
+ Version: 0.0.0.dev179
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=lRkBDFxlAbf6lN5upo3WSf-owW8YG1T21dfpbQL-XHM,7598
2
- omlish/__about__.py,sha256=d7Vqq3YraWwcnd0sR_ExiQePePaN2h_hwFfJ-RBD7IQ,3409
2
+ omlish/__about__.py,sha256=hsTRZl82-_TWSF4oD3MRaC6rua1ked88afN1hbpqoUc,3409
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
5
5
  omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
@@ -164,7 +164,7 @@ omlish/dataclasses/impl/descriptors.py,sha256=rEYE1Len99agTQCC25hSPMnM19BgPr0ZCh
164
164
  omlish/dataclasses/impl/exceptions.py,sha256=-vqxZmfXVflymVuiM553XTlJProse5HEMktTpfdPCIY,1275
165
165
  omlish/dataclasses/impl/fields.py,sha256=4_5qMz9LlOS6U67cDKQ-oorTtSGGxR77I4hw88soM44,6878
166
166
  omlish/dataclasses/impl/frozen.py,sha256=x87DSM8FIMZ3c_BIUE8NooCkExFjPsabeqIueEP5qKs,2988
167
- omlish/dataclasses/impl/hashing.py,sha256=FKnHuXCg9ylrzK2TLGqO5yfRN4HX3F415CSLlVYXtYE,3190
167
+ omlish/dataclasses/impl/hashing.py,sha256=0Gr6XIRkKy4pr-mdHblIlQCy3mBxycjMqJk3oZDw43s,3215
168
168
  omlish/dataclasses/impl/init.py,sha256=5kYcMDlI6EVeLQ6RCTk1bvYjb-cwg0AYfVE9FPZJlYI,6365
169
169
  omlish/dataclasses/impl/internals.py,sha256=UvZYjrLT1S8ntyxJ_vRPIkPOF00K8HatGAygErgoXTU,2990
170
170
  omlish/dataclasses/impl/main.py,sha256=Ti0PKbFKraKvfmoPuR-G7nLVNzRC8mvEuXhCuC-M2kc,2574
@@ -282,7 +282,7 @@ omlish/inject/__init__.py,sha256=n0RC9UDGsBQQ39cST39-XJqJPq2M0tnnh9yJubW9azo,189
282
282
  omlish/inject/binder.py,sha256=DAbc8TZi5w8Mna0TUtq0mT4jeDVA7i7SlBtOFrh2swc,4185
283
283
  omlish/inject/bindings.py,sha256=pLXn2U3kvmAS-68IOG-tr77DbiI-wp9hGyy4lhG6_H8,525
284
284
  omlish/inject/eagers.py,sha256=5AkGYuwijG0ihsH9NSaZotggalJ5_xWXhHE9mkn6IBA,329
285
- omlish/inject/elements.py,sha256=BzTnkNS-3iAMI47LMC2543u6A8Tfk3aJXn3CO191ez4,1547
285
+ omlish/inject/elements.py,sha256=npppGgYBQja0oPHkdBDmRhpm3md1mZT1FVcIQ--W2qI,1398
286
286
  omlish/inject/exceptions.py,sha256=_wkN2tF55gQzmMOMKJC_9jYHBZzaBiCDcyqI9Sf2UZs,626
287
287
  omlish/inject/injector.py,sha256=CoCUeMm1Oot4sG4Ti1sKCWrhlvtJ5QAeAI22AFWu2RQ,1066
288
288
  omlish/inject/inspect.py,sha256=tw49r1RJVHrEHmT8WWA3_Bl9Z0L3lEGRqlLhbM5OmAM,592
@@ -379,7 +379,7 @@ omlish/lite/cached.py,sha256=O7ozcoDNFm1Hg2wtpHEqYSp_i_nCLNOP6Ueq_Uk-7mU,1300
379
379
  omlish/lite/check.py,sha256=0PD-GKtaDqDX6jU5KbzbMvH-vl6jH82xgYfplmfTQkg,12941
380
380
  omlish/lite/contextmanagers.py,sha256=m9JO--p7L7mSl4cycXysH-1AO27weDKjP3DZG61cwwM,1683
381
381
  omlish/lite/dataclasses.py,sha256=M6UD4VwGo0Ky7RNzKWbO0IOy7iBZVCIbTiC6EYbFnX8,1035
382
- omlish/lite/inject.py,sha256=yolrXQPZ903FcZwbSrUXsXNCylZ31W1vesjLelRWGgo,28758
382
+ omlish/lite/inject.py,sha256=qBUftFeXMiRgANYbNS2e7TePMYyFAcuLgsJiLyMTW5o,28769
383
383
  omlish/lite/json.py,sha256=7-02Ny4fq-6YAu5ynvqoijhuYXWpLmfCI19GUeZnb1c,740
384
384
  omlish/lite/logs.py,sha256=CWFG0NKGhqNeEgryF5atN2gkPYbUdTINEw_s1phbINM,51
385
385
  omlish/lite/marshal.py,sha256=O_v_slgjAAiSGWComdRhqLXdumuIlBDc3SvoG_p00QM,15863
@@ -568,11 +568,12 @@ omlish/text/asdl.py,sha256=AS3irh-sag5pqyH3beJif78PjCbOaFso1NeKq-HXuTs,16867
568
568
  omlish/text/delimit.py,sha256=ubPXcXQmtbOVrUsNh5gH1mDq5H-n1y2R4cPL5_DQf68,4928
569
569
  omlish/text/glyphsplit.py,sha256=Ug-dPRO7x-OrNNr8g1y6DotSZ2KH0S-VcOmUobwa4B0,3296
570
570
  omlish/text/indent.py,sha256=6Jj6TFY9unaPa4xPzrnZemJ-fHsV53IamP93XGjSUHs,1274
571
+ omlish/text/minja.py,sha256=KAmZ2POcLcxwF4DPKxdWa16uWxXmVz1UnJXLSwt4oZo,5761
571
572
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
572
573
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
573
- omlish-0.0.0.dev178.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
574
- omlish-0.0.0.dev178.dist-info/METADATA,sha256=Zltjh6LQhgQnlgh8RPDJgnQHdnTVpzHEP3rMc3OcjJU,4264
575
- omlish-0.0.0.dev178.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
576
- omlish-0.0.0.dev178.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
577
- omlish-0.0.0.dev178.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
578
- omlish-0.0.0.dev178.dist-info/RECORD,,
574
+ omlish-0.0.0.dev179.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
575
+ omlish-0.0.0.dev179.dist-info/METADATA,sha256=IYVOAZRpPvFaMBtdeTokZT1AlSGB41FLnxa00c4kz54,4264
576
+ omlish-0.0.0.dev179.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
577
+ omlish-0.0.0.dev179.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
578
+ omlish-0.0.0.dev179.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
579
+ omlish-0.0.0.dev179.dist-info/RECORD,,