ominfra 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.
@@ -9,6 +9,7 @@ import argparse
9
9
  import base64
10
10
  import collections
11
11
  import collections.abc
12
+ import configparser
12
13
  import contextlib
13
14
  import dataclasses as dc
14
15
  import datetime
@@ -52,7 +53,13 @@ if sys.version_info < (3, 8):
52
53
  ########################################
53
54
 
54
55
 
55
- # ../../../../omdev/toml/parser.py
56
+ # ../../../../omlish/configs/types.py
57
+ ConfigMap = ta.Mapping[str, ta.Any]
58
+
59
+ # ../../../../omlish/formats/ini/sections.py
60
+ IniSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
61
+
62
+ # ../../../../omlish/formats/toml/parser.py
56
63
  TomlParseFloat = ta.Callable[[str], ta.Any]
57
64
  TomlKey = ta.Tuple[str, ...]
58
65
  TomlPos = int # ta.TypeAlias
@@ -69,13 +76,12 @@ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
69
76
  CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
70
77
  CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
71
78
 
79
+ # ../../../../omlish/configs/formats.py
80
+ ConfigDataT = ta.TypeVar('ConfigDataT', bound='ConfigData')
81
+
72
82
  # ../../../../omlish/lite/contextmanagers.py
73
83
  ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
74
84
 
75
- # ../../../configs.py
76
- ConfigMapping = ta.Mapping[str, ta.Any]
77
- IniConfigSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
78
-
79
85
  # ../../../threadworkers.py
80
86
  ThreadWorkerT = ta.TypeVar('ThreadWorkerT', bound='ThreadWorker')
81
87
 
@@ -84,7 +90,55 @@ SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlia
84
90
 
85
91
 
86
92
  ########################################
87
- # ../../../../../omdev/toml/parser.py
93
+ # ../../../../../omlish/configs/types.py
94
+
95
+
96
+ #
97
+
98
+
99
+ ########################################
100
+ # ../../../../../omlish/formats/ini/sections.py
101
+
102
+
103
+ ##
104
+
105
+
106
+ def extract_ini_sections(cp: configparser.ConfigParser) -> IniSectionSettingsMap:
107
+ config_dct: ta.Dict[str, ta.Any] = {}
108
+ for sec in cp.sections():
109
+ cd = config_dct
110
+ for k in sec.split('.'):
111
+ cd = cd.setdefault(k, {})
112
+ cd.update(cp.items(sec))
113
+ return config_dct
114
+
115
+
116
+ ##
117
+
118
+
119
+ def render_ini_sections(
120
+ settings_by_section: IniSectionSettingsMap,
121
+ ) -> str:
122
+ out = io.StringIO()
123
+
124
+ for i, (section, settings) in enumerate(settings_by_section.items()):
125
+ if i:
126
+ out.write('\n')
127
+
128
+ out.write(f'[{section}]\n')
129
+
130
+ for k, v in settings.items():
131
+ if isinstance(v, str):
132
+ out.write(f'{k}={v}\n')
133
+ else:
134
+ for vv in v:
135
+ out.write(f'{k}={vv}\n')
136
+
137
+ return out.getvalue()
138
+
139
+
140
+ ########################################
141
+ # ../../../../../omlish/formats/toml/parser.py
88
142
  # SPDX-License-Identifier: MIT
89
143
  # SPDX-FileCopyrightText: 2021 Taneli Hukkinen
90
144
  # Licensed to PSF under a Contributor Agreement.
@@ -901,6 +955,129 @@ def toml_make_safe_parse_float(parse_float: TomlParseFloat) -> TomlParseFloat:
901
955
  return safe_parse_float
902
956
 
903
957
 
958
+ ########################################
959
+ # ../../../../../omlish/formats/toml/writer.py
960
+
961
+
962
+ class TomlWriter:
963
+ @dc.dataclass(frozen=True)
964
+ class Literal:
965
+ s: str
966
+
967
+ def __init__(self, out: ta.TextIO) -> None:
968
+ super().__init__()
969
+ self._out = out
970
+
971
+ self._indent = 0
972
+ self._wrote_indent = False
973
+
974
+ #
975
+
976
+ def _w(self, s: str) -> None:
977
+ if not self._wrote_indent:
978
+ self._out.write(' ' * self._indent)
979
+ self._wrote_indent = True
980
+ self._out.write(s)
981
+
982
+ def _nl(self) -> None:
983
+ self._out.write('\n')
984
+ self._wrote_indent = False
985
+
986
+ def _needs_quote(self, s: str) -> bool:
987
+ return (
988
+ not s or
989
+ any(c in s for c in '\'"\n') or
990
+ s[0] not in string.ascii_letters
991
+ )
992
+
993
+ def _maybe_quote(self, s: str) -> str:
994
+ if self._needs_quote(s):
995
+ return repr(s)
996
+ else:
997
+ return s
998
+
999
+ #
1000
+
1001
+ def write_root(self, obj: ta.Mapping) -> None:
1002
+ for i, (k, v) in enumerate(obj.items()):
1003
+ if i:
1004
+ self._nl()
1005
+ self._w('[')
1006
+ self._w(self._maybe_quote(k))
1007
+ self._w(']')
1008
+ self._nl()
1009
+ self.write_table_contents(v)
1010
+
1011
+ def write_table_contents(self, obj: ta.Mapping) -> None:
1012
+ for k, v in obj.items():
1013
+ self.write_key(k)
1014
+ self._w(' = ')
1015
+ self.write_value(v)
1016
+ self._nl()
1017
+
1018
+ def write_array(self, obj: ta.Sequence) -> None:
1019
+ self._w('[')
1020
+ self._nl()
1021
+ self._indent += 1
1022
+ for e in obj:
1023
+ self.write_value(e)
1024
+ self._w(',')
1025
+ self._nl()
1026
+ self._indent -= 1
1027
+ self._w(']')
1028
+
1029
+ def write_inline_table(self, obj: ta.Mapping) -> None:
1030
+ self._w('{')
1031
+ for i, (k, v) in enumerate(obj.items()):
1032
+ if i:
1033
+ self._w(', ')
1034
+ self.write_key(k)
1035
+ self._w(' = ')
1036
+ self.write_value(v)
1037
+ self._w('}')
1038
+
1039
+ def write_inline_array(self, obj: ta.Sequence) -> None:
1040
+ self._w('[')
1041
+ for i, e in enumerate(obj):
1042
+ if i:
1043
+ self._w(', ')
1044
+ self.write_value(e)
1045
+ self._w(']')
1046
+
1047
+ def write_key(self, obj: ta.Any) -> None:
1048
+ if isinstance(obj, TomlWriter.Literal):
1049
+ self._w(obj.s)
1050
+ elif isinstance(obj, str):
1051
+ self._w(self._maybe_quote(obj.replace('_', '-')))
1052
+ elif isinstance(obj, int):
1053
+ self._w(repr(str(obj)))
1054
+ else:
1055
+ raise TypeError(obj)
1056
+
1057
+ def write_value(self, obj: ta.Any) -> None:
1058
+ if isinstance(obj, bool):
1059
+ self._w(str(obj).lower())
1060
+ elif isinstance(obj, (str, int, float)):
1061
+ self._w(repr(obj))
1062
+ elif isinstance(obj, ta.Mapping):
1063
+ self.write_inline_table(obj)
1064
+ elif isinstance(obj, ta.Sequence):
1065
+ if not obj:
1066
+ self.write_inline_array(obj)
1067
+ else:
1068
+ self.write_array(obj)
1069
+ else:
1070
+ raise TypeError(obj)
1071
+
1072
+ #
1073
+
1074
+ @classmethod
1075
+ def write_str(cls, obj: ta.Any) -> str:
1076
+ out = io.StringIO()
1077
+ cls(out).write_value(obj)
1078
+ return out.getvalue()
1079
+
1080
+
904
1081
  ########################################
905
1082
  # ../../../../../omlish/lite/cached.py
906
1083
 
@@ -2218,6 +2395,235 @@ class JournalctlToAwsCursor:
2218
2395
  os.rename(ncf, cf)
2219
2396
 
2220
2397
 
2398
+ ########################################
2399
+ # ../../../../../omlish/configs/formats.py
2400
+ """
2401
+ Notes:
2402
+ - necessarily string-oriented
2403
+ - single file, as this is intended to be amalg'd and thus all included anyway
2404
+
2405
+ TODO:
2406
+ - ConfigDataMapper? to_map -> ConfigMap?
2407
+ - nginx ?
2408
+ - raw ?
2409
+ """
2410
+
2411
+
2412
+ ##
2413
+
2414
+
2415
+ @dc.dataclass(frozen=True)
2416
+ class ConfigData(abc.ABC): # noqa
2417
+ @abc.abstractmethod
2418
+ def as_map(self) -> ConfigMap:
2419
+ raise NotImplementedError
2420
+
2421
+
2422
+ #
2423
+
2424
+
2425
+ class ConfigLoader(abc.ABC, ta.Generic[ConfigDataT]):
2426
+ @property
2427
+ def file_exts(self) -> ta.Sequence[str]:
2428
+ return ()
2429
+
2430
+ def match_file(self, n: str) -> bool:
2431
+ return '.' in n and n.split('.')[-1] in check.not_isinstance(self.file_exts, str)
2432
+
2433
+ #
2434
+
2435
+ def load_file(self, p: str) -> ConfigDataT:
2436
+ with open(p) as f:
2437
+ return self.load_str(f.read())
2438
+
2439
+ @abc.abstractmethod
2440
+ def load_str(self, s: str) -> ConfigDataT:
2441
+ raise NotImplementedError
2442
+
2443
+
2444
+ #
2445
+
2446
+
2447
+ class ConfigRenderer(abc.ABC, ta.Generic[ConfigDataT]):
2448
+ @property
2449
+ @abc.abstractmethod
2450
+ def data_cls(self) -> ta.Type[ConfigDataT]:
2451
+ raise NotImplementedError
2452
+
2453
+ def match_data(self, d: ConfigDataT) -> bool:
2454
+ return isinstance(d, self.data_cls)
2455
+
2456
+ #
2457
+
2458
+ @abc.abstractmethod
2459
+ def render(self, d: ConfigDataT) -> str:
2460
+ raise NotImplementedError
2461
+
2462
+
2463
+ ##
2464
+
2465
+
2466
+ @dc.dataclass(frozen=True)
2467
+ class ObjConfigData(ConfigData, abc.ABC):
2468
+ obj: ta.Any
2469
+
2470
+ def as_map(self) -> ConfigMap:
2471
+ return check.isinstance(self.obj, collections.abc.Mapping)
2472
+
2473
+
2474
+ ##
2475
+
2476
+
2477
+ @dc.dataclass(frozen=True)
2478
+ class JsonConfigData(ObjConfigData):
2479
+ pass
2480
+
2481
+
2482
+ class JsonConfigLoader(ConfigLoader[JsonConfigData]):
2483
+ file_exts = ('json',)
2484
+
2485
+ def load_str(self, s: str) -> JsonConfigData:
2486
+ return JsonConfigData(json.loads(s))
2487
+
2488
+
2489
+ class JsonConfigRenderer(ConfigRenderer[JsonConfigData]):
2490
+ data_cls = JsonConfigData
2491
+
2492
+ def render(self, d: JsonConfigData) -> str:
2493
+ return json_dumps_pretty(d.obj)
2494
+
2495
+
2496
+ ##
2497
+
2498
+
2499
+ @dc.dataclass(frozen=True)
2500
+ class TomlConfigData(ObjConfigData):
2501
+ pass
2502
+
2503
+
2504
+ class TomlConfigLoader(ConfigLoader[TomlConfigData]):
2505
+ file_exts = ('toml',)
2506
+
2507
+ def load_str(self, s: str) -> TomlConfigData:
2508
+ return TomlConfigData(toml_loads(s))
2509
+
2510
+
2511
+ class TomlConfigRenderer(ConfigRenderer[TomlConfigData]):
2512
+ data_cls = TomlConfigData
2513
+
2514
+ def render(self, d: TomlConfigData) -> str:
2515
+ return TomlWriter.write_str(d.obj)
2516
+
2517
+
2518
+ ##
2519
+
2520
+
2521
+ @dc.dataclass(frozen=True)
2522
+ class YamlConfigData(ObjConfigData):
2523
+ pass
2524
+
2525
+
2526
+ class YamlConfigLoader(ConfigLoader[YamlConfigData]):
2527
+ file_exts = ('yaml', 'yml')
2528
+
2529
+ def load_str(self, s: str) -> YamlConfigData:
2530
+ return YamlConfigData(__import__('yaml').safe_load(s))
2531
+
2532
+
2533
+ class YamlConfigRenderer(ConfigRenderer[YamlConfigData]):
2534
+ data_cls = YamlConfigData
2535
+
2536
+ def render(self, d: YamlConfigData) -> str:
2537
+ return __import__('yaml').safe_dump(d.obj)
2538
+
2539
+
2540
+ ##
2541
+
2542
+
2543
+ @dc.dataclass(frozen=True)
2544
+ class IniConfigData(ConfigData):
2545
+ sections: IniSectionSettingsMap
2546
+
2547
+ def as_map(self) -> ConfigMap:
2548
+ return self.sections
2549
+
2550
+
2551
+ class IniConfigLoader(ConfigLoader[IniConfigData]):
2552
+ file_exts = ('ini',)
2553
+
2554
+ def load_str(self, s: str) -> IniConfigData:
2555
+ cp = configparser.ConfigParser()
2556
+ cp.read_string(s)
2557
+ return IniConfigData(extract_ini_sections(cp))
2558
+
2559
+
2560
+ class IniConfigRenderer(ConfigRenderer[IniConfigData]):
2561
+ data_cls = IniConfigData
2562
+
2563
+ def render(self, d: IniConfigData) -> str:
2564
+ return render_ini_sections(d.sections)
2565
+
2566
+
2567
+ ##
2568
+
2569
+
2570
+ @dc.dataclass(frozen=True)
2571
+ class SwitchedConfigFileLoader:
2572
+ loaders: ta.Sequence[ConfigLoader]
2573
+ default: ta.Optional[ConfigLoader] = None
2574
+
2575
+ def load_file(self, p: str) -> ConfigData:
2576
+ n = os.path.basename(p)
2577
+
2578
+ for l in self.loaders:
2579
+ if l.match_file(n):
2580
+ return l.load_file(p)
2581
+
2582
+ if (d := self.default) is not None:
2583
+ return d.load_file(p)
2584
+
2585
+ raise NameError(n)
2586
+
2587
+
2588
+ DEFAULT_CONFIG_LOADERS: ta.Sequence[ConfigLoader] = [
2589
+ JsonConfigLoader(),
2590
+ TomlConfigLoader(),
2591
+ YamlConfigLoader(),
2592
+ IniConfigLoader(),
2593
+ ]
2594
+
2595
+ DEFAULT_CONFIG_LOADER: ConfigLoader = JsonConfigLoader()
2596
+
2597
+ DEFAULT_CONFIG_FILE_LOADER = SwitchedConfigFileLoader(
2598
+ loaders=DEFAULT_CONFIG_LOADERS,
2599
+ default=DEFAULT_CONFIG_LOADER,
2600
+ )
2601
+
2602
+
2603
+ ##
2604
+
2605
+
2606
+ @dc.dataclass(frozen=True)
2607
+ class SwitchedConfigRenderer:
2608
+ renderers: ta.Sequence[ConfigRenderer]
2609
+
2610
+ def render(self, d: ConfigData) -> str:
2611
+ for r in self.renderers:
2612
+ if r.match_data(d):
2613
+ return r.render(d)
2614
+ raise TypeError(d)
2615
+
2616
+
2617
+ DEFAULT_CONFIG_RENDERERS: ta.Sequence[ConfigRenderer] = [
2618
+ JsonConfigRenderer(),
2619
+ TomlConfigRenderer(),
2620
+ YamlConfigRenderer(),
2621
+ IniConfigRenderer(),
2622
+ ]
2623
+
2624
+ DEFAULT_CONFIG_RENDERER = SwitchedConfigRenderer(DEFAULT_CONFIG_RENDERERS)
2625
+
2626
+
2221
2627
  ########################################
2222
2628
  # ../../../../../omlish/io/buffers.py
2223
2629
 
@@ -3280,121 +3686,6 @@ class AwsLogMessageBuilder:
3280
3686
  return [post]
3281
3687
 
3282
3688
 
3283
- ########################################
3284
- # ../../../../configs.py
3285
-
3286
-
3287
- ##
3288
-
3289
-
3290
- def parse_config_file(
3291
- name: str,
3292
- f: ta.TextIO,
3293
- ) -> ConfigMapping:
3294
- if name.endswith('.toml'):
3295
- return toml_loads(f.read())
3296
-
3297
- elif any(name.endswith(e) for e in ('.yml', '.yaml')):
3298
- yaml = __import__('yaml')
3299
- return yaml.safe_load(f)
3300
-
3301
- elif name.endswith('.ini'):
3302
- import configparser
3303
- cp = configparser.ConfigParser()
3304
- cp.read_file(f)
3305
- config_dct: ta.Dict[str, ta.Any] = {}
3306
- for sec in cp.sections():
3307
- cd = config_dct
3308
- for k in sec.split('.'):
3309
- cd = cd.setdefault(k, {})
3310
- cd.update(cp.items(sec))
3311
- return config_dct
3312
-
3313
- else:
3314
- return json.loads(f.read())
3315
-
3316
-
3317
- def read_config_file(
3318
- path: str,
3319
- cls: ta.Type[T],
3320
- *,
3321
- prepare: ta.Optional[ta.Callable[[ConfigMapping], ConfigMapping]] = None,
3322
- msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
3323
- ) -> T:
3324
- with open(path) as cf:
3325
- config_dct = parse_config_file(os.path.basename(path), cf)
3326
-
3327
- if prepare is not None:
3328
- config_dct = prepare(config_dct)
3329
-
3330
- return msh.unmarshal_obj(config_dct, cls)
3331
-
3332
-
3333
- ##
3334
-
3335
-
3336
- def build_config_named_children(
3337
- o: ta.Union[
3338
- ta.Sequence[ConfigMapping],
3339
- ta.Mapping[str, ConfigMapping],
3340
- None,
3341
- ],
3342
- *,
3343
- name_key: str = 'name',
3344
- ) -> ta.Optional[ta.Sequence[ConfigMapping]]:
3345
- if o is None:
3346
- return None
3347
-
3348
- lst: ta.List[ConfigMapping] = []
3349
- if isinstance(o, ta.Mapping):
3350
- for k, v in o.items():
3351
- check.isinstance(v, ta.Mapping)
3352
- if name_key in v:
3353
- n = v[name_key]
3354
- if k != n:
3355
- raise KeyError(f'Given names do not match: {n} != {k}')
3356
- lst.append(v)
3357
- else:
3358
- lst.append({name_key: k, **v})
3359
-
3360
- else:
3361
- check.not_isinstance(o, str)
3362
- lst.extend(o)
3363
-
3364
- seen = set()
3365
- for d in lst:
3366
- n = d['name']
3367
- if n in d:
3368
- raise KeyError(f'Duplicate name: {n}')
3369
- seen.add(n)
3370
-
3371
- return lst
3372
-
3373
-
3374
- ##
3375
-
3376
-
3377
- def render_ini_config(
3378
- settings_by_section: IniConfigSectionSettingsMap,
3379
- ) -> str:
3380
- out = io.StringIO()
3381
-
3382
- for i, (section, settings) in enumerate(settings_by_section.items()):
3383
- if i:
3384
- out.write('\n')
3385
-
3386
- out.write(f'[{section}]\n')
3387
-
3388
- for k, v in settings.items():
3389
- if isinstance(v, str):
3390
- out.write(f'{k}={v}\n')
3391
- else:
3392
- for vv in v:
3393
- out.write(f'{k}={vv}\n')
3394
-
3395
- return out.getvalue()
3396
-
3397
-
3398
3689
  ########################################
3399
3690
  # ../../../../journald/messages.py
3400
3691
 
@@ -3654,6 +3945,38 @@ class ThreadWorkerGroup:
3654
3945
  return dct
3655
3946
 
3656
3947
 
3948
+ ########################################
3949
+ # ../../../../../omlish/lite/configs.py
3950
+
3951
+
3952
+ ##
3953
+
3954
+
3955
+ def load_config_file_obj(
3956
+ f: str,
3957
+ cls: ta.Type[T],
3958
+ *,
3959
+ prepare: ta.Union[
3960
+ ta.Callable[[ConfigMap], ConfigMap],
3961
+ ta.Iterable[ta.Callable[[ConfigMap], ConfigMap]],
3962
+ ] = (),
3963
+ msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
3964
+ ) -> T:
3965
+ config_data = DEFAULT_CONFIG_FILE_LOADER.load_file(f)
3966
+
3967
+ config_dct = config_data.as_map()
3968
+
3969
+ if prepare is not None:
3970
+ if isinstance(prepare, ta.Iterable):
3971
+ pfs = list(prepare)
3972
+ else:
3973
+ pfs = [prepare]
3974
+ for pf in pfs:
3975
+ config_dct = pf(config_dct)
3976
+
3977
+ return msh.unmarshal_obj(config_dct, cls)
3978
+
3979
+
3657
3980
  ########################################
3658
3981
  # ../../../../../omlish/logs/standard.py
3659
3982
  """
@@ -4934,7 +5257,10 @@ def _main() -> None:
4934
5257
 
4935
5258
  config: JournalctlToAwsDriver.Config
4936
5259
  if args.config_file:
4937
- config = read_config_file(os.path.expanduser(args.config_file), JournalctlToAwsDriver.Config)
5260
+ config = load_config_file_obj(
5261
+ os.path.expanduser(args.config_file),
5262
+ JournalctlToAwsDriver.Config,
5263
+ )
4938
5264
  else:
4939
5265
  config = JournalctlToAwsDriver.Config()
4940
5266