omlish 0.0.0.dev192__py3-none-any.whl → 0.0.0.dev194__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. omlish/.manifests.json +17 -3
  2. omlish/__about__.py +2 -2
  3. omlish/codecs/base.py +2 -0
  4. omlish/configs/__init__.py +0 -5
  5. omlish/configs/formats.py +247 -0
  6. omlish/configs/nginx.py +72 -0
  7. omlish/configs/processing/__init__.py +0 -0
  8. omlish/configs/{flattening.py → processing/flattening.py} +31 -33
  9. omlish/configs/processing/inheritance.py +58 -0
  10. omlish/configs/processing/matching.py +53 -0
  11. omlish/configs/processing/names.py +50 -0
  12. omlish/configs/processing/rewriting.py +141 -0
  13. omlish/configs/processing/strings.py +45 -0
  14. omlish/configs/types.py +9 -0
  15. omlish/formats/__init__.py +4 -0
  16. omlish/formats/ini/__init__.py +0 -0
  17. omlish/formats/ini/codec.py +26 -0
  18. omlish/formats/ini/sections.py +45 -0
  19. omlish/formats/toml/__init__.py +0 -0
  20. omlish/formats/{toml.py → toml/codec.py} +2 -2
  21. omlish/formats/toml/parser.py +827 -0
  22. omlish/formats/toml/writer.py +124 -0
  23. omlish/lang/__init__.py +4 -1
  24. omlish/lang/iterables.py +0 -7
  25. omlish/lite/configs.py +38 -0
  26. omlish/lite/types.py +9 -0
  27. omlish/text/glyphsplit.py +25 -10
  28. {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/METADATA +1 -1
  29. {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/RECORD +33 -17
  30. omlish/configs/strings.py +0 -96
  31. {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/LICENSE +0 -0
  32. {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/WHEEL +0 -0
  33. {omlish-0.0.0.dev192.dist-info → omlish-0.0.0.dev194.dist-info}/entry_points.txt +0 -0
  34. {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)
@@ -0,0 +1,9 @@
1
+ # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
3
+ import typing as ta
4
+
5
+
6
+ ConfigMap = ta.Mapping[str, ta.Any]
7
+
8
+
9
+ #
@@ -0,0 +1,4 @@
1
+ """
2
+ TODO:
3
+ - csv
4
+ """
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
@@ -1,7 +1,7 @@
1
1
  import tomllib
2
2
 
3
- from .codecs import make_object_lazy_loaded_codec
4
- from .codecs import make_str_object_codec
3
+ from ..codecs import make_object_lazy_loaded_codec
4
+ from ..codecs import make_str_object_codec
5
5
 
6
6
 
7
7
  ##