ominfra 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.
@@ -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