omlish 0.0.0.dev192__py3-none-any.whl → 0.0.0.dev194__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/.manifests.json +17 -3
- omlish/__about__.py +2 -2
- omlish/codecs/base.py +2 -0
- omlish/configs/__init__.py +0 -5
- omlish/configs/formats.py +247 -0
- omlish/configs/nginx.py +72 -0
- omlish/configs/processing/__init__.py +0 -0
- omlish/configs/{flattening.py → processing/flattening.py} +31 -33
- omlish/configs/processing/inheritance.py +58 -0
- omlish/configs/processing/matching.py +53 -0
- omlish/configs/processing/names.py +50 -0
- omlish/configs/processing/rewriting.py +141 -0
- omlish/configs/processing/strings.py +45 -0
- omlish/configs/types.py +9 -0
- omlish/formats/__init__.py +4 -0
- omlish/formats/ini/__init__.py +0 -0
- omlish/formats/ini/codec.py +26 -0
- omlish/formats/ini/sections.py +45 -0
- omlish/formats/toml/__init__.py +0 -0
- omlish/formats/{toml.py → toml/codec.py} +2 -2
- omlish/formats/toml/parser.py +827 -0
- omlish/formats/toml/writer.py +124 -0
- omlish/lang/__init__.py +4 -1
- omlish/lang/iterables.py +0 -7
- omlish/lite/configs.py +38 -0
- omlish/lite/types.py +9 -0
- omlish/text/glyphsplit.py +25 -10
- {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/RECORD +33 -17
- omlish/configs/strings.py +0 -96
- {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import abc
|
4
|
+
import collections.abc
|
5
|
+
import dataclasses as dc
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
from ...lite.check import check
|
9
|
+
from ...lite.types import BUILTIN_SCALAR_ITERABLE_TYPES
|
10
|
+
|
11
|
+
|
12
|
+
T = ta.TypeVar('T')
|
13
|
+
StrT = ta.TypeVar('StrT', bound=str)
|
14
|
+
MappingT = ta.TypeVar('MappingT', bound=ta.Mapping)
|
15
|
+
IterableT = ta.TypeVar('IterableT', bound=ta.Iterable)
|
16
|
+
|
17
|
+
ConfigRewriterItem = ta.Union[int, str, None] # ta.TypeAlias
|
18
|
+
ConfigRewriterPath = ta.Tuple[ConfigRewriterItem, ...] # ta.TypeAlias
|
19
|
+
|
20
|
+
|
21
|
+
##
|
22
|
+
|
23
|
+
|
24
|
+
class RawConfigMetadata:
|
25
|
+
def __new__(cls, *args, **kwargs): # noqa
|
26
|
+
raise TypeError
|
27
|
+
|
28
|
+
|
29
|
+
class ConfigRewriter(abc.ABC): # noqa
|
30
|
+
@dc.dataclass(frozen=True)
|
31
|
+
class Context(ta.Generic[T]):
|
32
|
+
obj: T
|
33
|
+
|
34
|
+
parent: ta.Optional['ConfigRewriter.Context'] = None
|
35
|
+
item: ConfigRewriterItem = None
|
36
|
+
|
37
|
+
raw: bool = False
|
38
|
+
|
39
|
+
def __post_init__(self) -> None:
|
40
|
+
if self.parent is None:
|
41
|
+
check.none(self.item)
|
42
|
+
|
43
|
+
@property
|
44
|
+
def path(self) -> ConfigRewriterPath:
|
45
|
+
cur: ConfigRewriter.Context = self
|
46
|
+
lst: ta.List[ConfigRewriterItem] = []
|
47
|
+
while True:
|
48
|
+
if cur.parent is None:
|
49
|
+
break
|
50
|
+
lst.append(cur.item)
|
51
|
+
cur = cur.parent
|
52
|
+
return tuple(reversed(lst))
|
53
|
+
|
54
|
+
def make_child(
|
55
|
+
self,
|
56
|
+
obj: ta.Any,
|
57
|
+
item: ConfigRewriterItem,
|
58
|
+
**kwargs: ta.Any,
|
59
|
+
) -> 'ConfigRewriter.Context':
|
60
|
+
return dc.replace(
|
61
|
+
self,
|
62
|
+
obj=obj,
|
63
|
+
parent=self,
|
64
|
+
item=item,
|
65
|
+
**kwargs,
|
66
|
+
)
|
67
|
+
|
68
|
+
def rewrite_none(self, ctx: Context[None]) -> ta.Any:
|
69
|
+
return ctx.obj
|
70
|
+
|
71
|
+
def rewrite_dataclass(self, ctx: Context[T]) -> T:
|
72
|
+
kw = {}
|
73
|
+
for f in dc.fields(ctx.obj): # type: ignore
|
74
|
+
fv = getattr(ctx.obj, f.name)
|
75
|
+
nfv: ta.Any = self.rewrite(ctx.make_child(
|
76
|
+
fv,
|
77
|
+
f.name,
|
78
|
+
raw=bool(not f.metadata.get(RawConfigMetadata)),
|
79
|
+
))
|
80
|
+
if fv is not nfv:
|
81
|
+
kw[f.name] = nfv
|
82
|
+
if not kw:
|
83
|
+
return ctx.obj
|
84
|
+
return dc.replace(ctx.obj, **kw) # type: ignore
|
85
|
+
|
86
|
+
def rewrite_str(self, ctx: Context[StrT]) -> StrT:
|
87
|
+
return ctx.obj
|
88
|
+
|
89
|
+
def rewrite_builtin_scalar_iterable(self, ctx: Context[IterableT]) -> IterableT:
|
90
|
+
return ctx.obj
|
91
|
+
|
92
|
+
def rewrite_mapping(self, ctx: Context[MappingT]) -> MappingT:
|
93
|
+
nm = []
|
94
|
+
b = False
|
95
|
+
for mk, mv in ctx.obj.items():
|
96
|
+
nk: ta.Any = self.rewrite(ctx.make_child(mk, None))
|
97
|
+
nv: ta.Any = self.rewrite(ctx.make_child(mv, nk))
|
98
|
+
nm.append((nk, nv))
|
99
|
+
b |= nk is not mk or nv is not mv
|
100
|
+
if not b:
|
101
|
+
return ctx.obj
|
102
|
+
return type(ctx.obj)(nm) # type: ignore
|
103
|
+
|
104
|
+
def rewrite_iterable(self, ctx: Context[IterableT]) -> IterableT:
|
105
|
+
nl = []
|
106
|
+
b = False
|
107
|
+
for i, le in enumerate(ctx.obj):
|
108
|
+
ne: ta.Any = self.rewrite(ctx.make_child(le, i))
|
109
|
+
nl.append(ne)
|
110
|
+
b |= ne is not le
|
111
|
+
if not b:
|
112
|
+
return ctx.obj
|
113
|
+
return type(ctx.obj)(nl) # type: ignore
|
114
|
+
|
115
|
+
def rewrite_other(self, ctx: Context[T]) -> T:
|
116
|
+
return ctx.obj
|
117
|
+
|
118
|
+
def rewrite(self, ctx: Context[T]) -> T:
|
119
|
+
if ctx.obj is None:
|
120
|
+
return self.rewrite_none(ctx) # type: ignore
|
121
|
+
|
122
|
+
elif dc.is_dataclass(ctx.obj):
|
123
|
+
return self.rewrite_dataclass(ctx)
|
124
|
+
|
125
|
+
elif isinstance(ctx.obj, str):
|
126
|
+
return self.rewrite_str(ctx) # type: ignore
|
127
|
+
|
128
|
+
elif isinstance(ctx.obj, BUILTIN_SCALAR_ITERABLE_TYPES):
|
129
|
+
return self.rewrite_builtin_scalar_iterable(ctx) # type: ignore
|
130
|
+
|
131
|
+
elif isinstance(ctx.obj, collections.abc.Mapping):
|
132
|
+
return self.rewrite_mapping(ctx) # type: ignore
|
133
|
+
|
134
|
+
elif isinstance(ctx.obj, (collections.abc.Sequence, collections.abc.Set)):
|
135
|
+
return self.rewrite_iterable(ctx) # type: ignore
|
136
|
+
|
137
|
+
else:
|
138
|
+
return self.rewrite_other(ctx)
|
139
|
+
|
140
|
+
def __call__(self, v: T) -> T:
|
141
|
+
return self.rewrite(self.Context(obj=v))
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
"""
|
4
|
+
TODO:
|
5
|
+
- env vars
|
6
|
+
- coalescing - {$FOO|$BAR|baz}
|
7
|
+
- minja?
|
8
|
+
"""
|
9
|
+
import typing as ta
|
10
|
+
|
11
|
+
from .rewriting import ConfigRewriter
|
12
|
+
|
13
|
+
|
14
|
+
T = ta.TypeVar('T')
|
15
|
+
StrT = ta.TypeVar('StrT', bound=str)
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
class StringConfigRewriter(ConfigRewriter):
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
fn: ta.Optional[ta.Callable[[str], str]] = None,
|
25
|
+
) -> None:
|
26
|
+
super().__init__()
|
27
|
+
|
28
|
+
self._fn = fn
|
29
|
+
|
30
|
+
def rewrite_str(self, ctx: ConfigRewriter.Context[StrT]) -> StrT:
|
31
|
+
v = ctx.obj
|
32
|
+
if (fn := self._fn) is not None:
|
33
|
+
if not ctx.raw:
|
34
|
+
v = fn(v) # type: ignore
|
35
|
+
return type(v)(v)
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
|
40
|
+
|
41
|
+
def format_config_strings(v: T, rpl: ta.Mapping[str, str]) -> T:
|
42
|
+
def fn(v):
|
43
|
+
return v.format(**rpl)
|
44
|
+
|
45
|
+
return StringConfigRewriter(fn)(v)
|
omlish/configs/types.py
ADDED
omlish/formats/__init__.py
CHANGED
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import configparser
|
2
|
+
|
3
|
+
from ..codecs import make_object_lazy_loaded_codec
|
4
|
+
from ..codecs import make_str_object_codec
|
5
|
+
from .sections import IniSectionSettingsMap
|
6
|
+
from .sections import extract_ini_sections
|
7
|
+
from .sections import render_ini_sections
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
|
12
|
+
|
13
|
+
def loads(s: str) -> IniSectionSettingsMap:
|
14
|
+
cp = configparser.ConfigParser()
|
15
|
+
cp.read_string(s)
|
16
|
+
return extract_ini_sections(cp)
|
17
|
+
|
18
|
+
|
19
|
+
def dumps(sm: IniSectionSettingsMap) -> str:
|
20
|
+
return render_ini_sections(sm)
|
21
|
+
|
22
|
+
|
23
|
+
INI_CODEC = make_str_object_codec('ini', dumps, loads)
|
24
|
+
|
25
|
+
# @omlish-manifest
|
26
|
+
_INI_LAZY_CODEC = make_object_lazy_loaded_codec(__name__, 'INI_CODEC', INI_CODEC)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import configparser
|
4
|
+
import io
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
|
8
|
+
IniSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
|
9
|
+
|
10
|
+
|
11
|
+
##
|
12
|
+
|
13
|
+
|
14
|
+
def extract_ini_sections(cp: configparser.ConfigParser) -> IniSectionSettingsMap:
|
15
|
+
config_dct: ta.Dict[str, ta.Any] = {}
|
16
|
+
for sec in cp.sections():
|
17
|
+
cd = config_dct
|
18
|
+
for k in sec.split('.'):
|
19
|
+
cd = cd.setdefault(k, {})
|
20
|
+
cd.update(cp.items(sec))
|
21
|
+
return config_dct
|
22
|
+
|
23
|
+
|
24
|
+
##
|
25
|
+
|
26
|
+
|
27
|
+
def render_ini_sections(
|
28
|
+
settings_by_section: IniSectionSettingsMap,
|
29
|
+
) -> str:
|
30
|
+
out = io.StringIO()
|
31
|
+
|
32
|
+
for i, (section, settings) in enumerate(settings_by_section.items()):
|
33
|
+
if i:
|
34
|
+
out.write('\n')
|
35
|
+
|
36
|
+
out.write(f'[{section}]\n')
|
37
|
+
|
38
|
+
for k, v in settings.items():
|
39
|
+
if isinstance(v, str):
|
40
|
+
out.write(f'{k}={v}\n')
|
41
|
+
else:
|
42
|
+
for vv in v:
|
43
|
+
out.write(f'{k}={vv}\n')
|
44
|
+
|
45
|
+
return out.getvalue()
|
File without changes
|