ominfra 0.0.0.dev7__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.
Files changed (44) hide show
  1. ominfra/__about__.py +27 -0
  2. ominfra/__init__.py +0 -0
  3. ominfra/bootstrap/__init__.py +0 -0
  4. ominfra/bootstrap/bootstrap.py +8 -0
  5. ominfra/cmds.py +83 -0
  6. ominfra/deploy/__init__.py +0 -0
  7. ominfra/deploy/_executor.py +1036 -0
  8. ominfra/deploy/configs.py +19 -0
  9. ominfra/deploy/executor/__init__.py +1 -0
  10. ominfra/deploy/executor/base.py +115 -0
  11. ominfra/deploy/executor/concerns/__init__.py +0 -0
  12. ominfra/deploy/executor/concerns/dirs.py +28 -0
  13. ominfra/deploy/executor/concerns/nginx.py +47 -0
  14. ominfra/deploy/executor/concerns/repo.py +17 -0
  15. ominfra/deploy/executor/concerns/supervisor.py +46 -0
  16. ominfra/deploy/executor/concerns/systemd.py +88 -0
  17. ominfra/deploy/executor/concerns/user.py +25 -0
  18. ominfra/deploy/executor/concerns/venv.py +22 -0
  19. ominfra/deploy/executor/main.py +119 -0
  20. ominfra/deploy/poly/__init__.py +1 -0
  21. ominfra/deploy/poly/_main.py +725 -0
  22. ominfra/deploy/poly/base.py +179 -0
  23. ominfra/deploy/poly/configs.py +38 -0
  24. ominfra/deploy/poly/deploy.py +25 -0
  25. ominfra/deploy/poly/main.py +18 -0
  26. ominfra/deploy/poly/nginx.py +60 -0
  27. ominfra/deploy/poly/repo.py +41 -0
  28. ominfra/deploy/poly/runtime.py +39 -0
  29. ominfra/deploy/poly/site.py +11 -0
  30. ominfra/deploy/poly/supervisor.py +64 -0
  31. ominfra/deploy/poly/venv.py +52 -0
  32. ominfra/deploy/remote.py +91 -0
  33. ominfra/pyremote/__init__.py +0 -0
  34. ominfra/pyremote/_runcommands.py +824 -0
  35. ominfra/pyremote/bootstrap.py +149 -0
  36. ominfra/pyremote/runcommands.py +56 -0
  37. ominfra/ssh.py +191 -0
  38. ominfra/tools/__init__.py +0 -0
  39. ominfra/tools/listresources.py +256 -0
  40. ominfra-0.0.0.dev7.dist-info/LICENSE +21 -0
  41. ominfra-0.0.0.dev7.dist-info/METADATA +19 -0
  42. ominfra-0.0.0.dev7.dist-info/RECORD +44 -0
  43. ominfra-0.0.0.dev7.dist-info/WHEEL +5 -0
  44. ominfra-0.0.0.dev7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,824 @@
1
+ #!/usr/bin/env python3
2
+ # noinspection DuplicatedCode
3
+ # @omdev-amalg-output runcommands.py
4
+ # ruff: noqa: UP006 UP007
5
+ import abc
6
+ import base64
7
+ import collections.abc
8
+ import dataclasses as dc
9
+ import datetime
10
+ import decimal
11
+ import enum
12
+ import fractions
13
+ import functools
14
+ import inspect
15
+ import io
16
+ import json
17
+ import logging
18
+ import os
19
+ import shlex
20
+ import subprocess
21
+ import sys
22
+ import textwrap
23
+ import typing as ta
24
+ import uuid
25
+ import weakref # noqa
26
+ import zlib
27
+
28
+
29
+ T = ta.TypeVar('T')
30
+
31
+
32
+ ########################################
33
+ # ../bootstrap.py
34
+ """
35
+ Basically this: https://mitogen.networkgenomics.com/howitworks.html
36
+ """
37
+
38
+
39
+ ##
40
+
41
+
42
+ _BOOTSTRAP_COMM_FD = 100
43
+ _BOOTSTRAP_SRC_FD = 101
44
+
45
+ _BOOTSTRAP_CHILD_PID_VAR = '_PYR_CPID'
46
+ _BOOTSTRAP_ARGV0_VAR = '_PYR_ARGV0'
47
+
48
+ BOOTSTRAP_ACK0 = b'OPYR000\n'
49
+ BOOTSTRAP_ACK1 = b'OPYR001\n'
50
+
51
+ _BOOTSTRAP_PROC_TITLE_FMT = '(pyremote:%s)'
52
+
53
+ _BOOTSTRAP_IMPORTS = [
54
+ 'base64',
55
+ 'os',
56
+ 'sys',
57
+ 'zlib',
58
+ ]
59
+
60
+
61
+ def _bootstrap_main(context_name: str, main_z_len: int) -> None:
62
+ # Two copies of main src to be sent to parent
63
+ r0, w0 = os.pipe()
64
+ r1, w1 = os.pipe()
65
+
66
+ if (cp := os.fork()):
67
+ # Parent process
68
+
69
+ # Dup original stdin to comm_fd for use as comm channel
70
+ os.dup2(0, _BOOTSTRAP_COMM_FD)
71
+
72
+ # Overwrite stdin (fed to python repl) with first copy of src
73
+ os.dup2(r0, 0)
74
+
75
+ # Dup second copy of src to src_fd to recover after launch
76
+ os.dup2(r1, _BOOTSTRAP_SRC_FD)
77
+
78
+ # Close remaining fd's
79
+ for f in [r0, w0, r1, w1]:
80
+ os.close(f)
81
+
82
+ # Save child pid to close after relaunch
83
+ os.environ[_BOOTSTRAP_CHILD_PID_VAR] = str(cp)
84
+
85
+ # Save original argv0
86
+ os.environ[_BOOTSTRAP_ARGV0_VAR] = sys.executable
87
+
88
+ # Start repl reading stdin from r0
89
+ os.execl(sys.executable, sys.executable + (_BOOTSTRAP_PROC_TITLE_FMT % (context_name,)))
90
+
91
+ else:
92
+ # Child process
93
+
94
+ # Write first ack
95
+ os.write(1, BOOTSTRAP_ACK0)
96
+
97
+ # Read main src from stdin
98
+ main_src = zlib.decompress(os.fdopen(0, 'rb').read(main_z_len))
99
+
100
+ # Write both copies of main src
101
+ for w in [w0, w1]:
102
+ fp = os.fdopen(w, 'wb', 0)
103
+ fp.write(main_src)
104
+ fp.close()
105
+
106
+ # Write second ack
107
+ os.write(1, BOOTSTRAP_ACK1)
108
+
109
+ sys.exit(0)
110
+
111
+
112
+ #
113
+
114
+
115
+ def bootstrap_payload(context_name: str, main_z_len: int) -> str:
116
+ bs_src = textwrap.dedent(inspect.getsource(_bootstrap_main))
117
+
118
+ for gl in [
119
+ '_BOOTSTRAP_COMM_FD',
120
+ '_BOOTSTRAP_SRC_FD',
121
+
122
+ '_BOOTSTRAP_CHILD_PID_VAR',
123
+ '_BOOTSTRAP_ARGV0_VAR',
124
+
125
+ 'BOOTSTRAP_ACK0',
126
+ 'BOOTSTRAP_ACK1',
127
+
128
+ '_BOOTSTRAP_PROC_TITLE_FMT',
129
+ ]:
130
+ bs_src = bs_src.replace(gl, repr(globals()[gl]))
131
+
132
+ bs_src = '\n'.join(
133
+ cl
134
+ for l in bs_src.splitlines()
135
+ if (cl := (l.split('#')[0]).rstrip())
136
+ if cl.strip()
137
+ )
138
+
139
+ bs_z = zlib.compress(bs_src.encode('utf-8'))
140
+ bs_z64 = base64.encodebytes(bs_z).replace(b'\n', b'')
141
+
142
+ stmts = [
143
+ f'import {", ".join(_BOOTSTRAP_IMPORTS)}',
144
+ f'exec(zlib.decompress(base64.decodebytes({bs_z64!r})))',
145
+ f'_bootstrap_main({context_name!r}, {main_z_len})',
146
+ ]
147
+
148
+ cmd = '; '.join(stmts)
149
+ return cmd
150
+
151
+
152
+ #
153
+
154
+
155
+ class PostBoostrap(ta.NamedTuple):
156
+ input: ta.BinaryIO
157
+ main_src: str
158
+
159
+
160
+ def post_boostrap() -> PostBoostrap:
161
+ # Restore original argv0
162
+ sys.executable = os.environ.pop(_BOOTSTRAP_ARGV0_VAR)
163
+
164
+ # Reap boostrap child
165
+ os.waitpid(int(os.environ.pop(_BOOTSTRAP_CHILD_PID_VAR)), 0)
166
+
167
+ # Read second copy of main src
168
+ r1 = os.fdopen(_BOOTSTRAP_SRC_FD, 'rb', 0)
169
+ main_src = r1.read().decode('utf-8')
170
+ r1.close()
171
+
172
+ return PostBoostrap(
173
+ input=os.fdopen(_BOOTSTRAP_COMM_FD, 'rb', 0),
174
+ main_src=main_src,
175
+ )
176
+
177
+
178
+ ########################################
179
+ # ../../../omlish/lite/cached.py
180
+
181
+
182
+ class cached_nullary: # noqa
183
+ def __init__(self, fn):
184
+ super().__init__()
185
+ self._fn = fn
186
+ self._value = self._missing = object()
187
+ functools.update_wrapper(self, fn)
188
+
189
+ def __call__(self, *args, **kwargs): # noqa
190
+ if self._value is self._missing:
191
+ self._value = self._fn()
192
+ return self._value
193
+
194
+ def __get__(self, instance, owner): # noqa
195
+ bound = instance.__dict__[self._fn.__name__] = self.__class__(self._fn.__get__(instance, owner))
196
+ return bound
197
+
198
+
199
+ ########################################
200
+ # ../../../omlish/lite/check.py
201
+ # ruff: noqa: UP006 UP007
202
+
203
+
204
+ def check_isinstance(v: T, spec: ta.Union[ta.Type[T], tuple]) -> T:
205
+ if not isinstance(v, spec):
206
+ raise TypeError(v)
207
+ return v
208
+
209
+
210
+ def check_not_isinstance(v: T, spec: ta.Union[type, tuple]) -> T:
211
+ if isinstance(v, spec):
212
+ raise TypeError(v)
213
+ return v
214
+
215
+
216
+ def check_not_none(v: ta.Optional[T]) -> T:
217
+ if v is None:
218
+ raise ValueError
219
+ return v
220
+
221
+
222
+ def check_not(v: ta.Any) -> None:
223
+ if v:
224
+ raise ValueError(v)
225
+ return v
226
+
227
+
228
+ ########################################
229
+ # ../../../omlish/lite/json.py
230
+
231
+
232
+ ##
233
+
234
+
235
+ JSON_PRETTY_INDENT = 2
236
+
237
+ JSON_PRETTY_KWARGS: ta.Mapping[str, ta.Any] = dict(
238
+ indent=JSON_PRETTY_INDENT,
239
+ )
240
+
241
+ json_dump_pretty: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON_PRETTY_KWARGS) # type: ignore
242
+ json_dumps_pretty: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_PRETTY_KWARGS)
243
+
244
+
245
+ ##
246
+
247
+
248
+ JSON_COMPACT_SEPARATORS = (',', ':')
249
+
250
+ JSON_COMPACT_KWARGS: ta.Mapping[str, ta.Any] = dict(
251
+ indent=None,
252
+ separators=JSON_COMPACT_SEPARATORS,
253
+ )
254
+
255
+ json_dump_compact: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON_COMPACT_KWARGS) # type: ignore
256
+ json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
257
+
258
+
259
+ ########################################
260
+ # ../../../omlish/lite/reflect.py
261
+ # ruff: noqa: UP006
262
+
263
+
264
+ _GENERIC_ALIAS_TYPES = (
265
+ ta._GenericAlias, # type: ignore # noqa
266
+ *([ta._SpecialGenericAlias] if hasattr(ta, '_SpecialGenericAlias') else []), # noqa
267
+ )
268
+
269
+
270
+ def is_generic_alias(obj, *, origin: ta.Any = None) -> bool:
271
+ return (
272
+ isinstance(obj, _GENERIC_ALIAS_TYPES) and
273
+ (origin is None or ta.get_origin(obj) is origin)
274
+ )
275
+
276
+
277
+ is_union_alias = functools.partial(is_generic_alias, origin=ta.Union)
278
+ is_callable_alias = functools.partial(is_generic_alias, origin=ta.Callable)
279
+
280
+
281
+ def is_optional_alias(spec: ta.Any) -> bool:
282
+ return (
283
+ isinstance(spec, _GENERIC_ALIAS_TYPES) and # noqa
284
+ ta.get_origin(spec) is ta.Union and
285
+ len(ta.get_args(spec)) == 2 and
286
+ any(a in (None, type(None)) for a in ta.get_args(spec))
287
+ )
288
+
289
+
290
+ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
291
+ [it] = [it for it in ta.get_args(spec) if it not in (None, type(None))]
292
+ return it
293
+
294
+
295
+ def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
296
+ seen = set()
297
+ todo = list(reversed(cls.__subclasses__()))
298
+ while todo:
299
+ cur = todo.pop()
300
+ if cur in seen:
301
+ continue
302
+ seen.add(cur)
303
+ yield cur
304
+ todo.extend(reversed(cur.__subclasses__()))
305
+
306
+
307
+ ########################################
308
+ # ../../../omlish/lite/logs.py
309
+ """
310
+ TODO:
311
+ - debug
312
+ """
313
+ # ruff: noqa: UP007
314
+
315
+
316
+ log = logging.getLogger(__name__)
317
+
318
+
319
+ class JsonLogFormatter(logging.Formatter):
320
+
321
+ KEYS: ta.Mapping[str, bool] = {
322
+ 'name': False,
323
+ 'msg': False,
324
+ 'args': False,
325
+ 'levelname': False,
326
+ 'levelno': False,
327
+ 'pathname': False,
328
+ 'filename': False,
329
+ 'module': False,
330
+ 'exc_info': True,
331
+ 'exc_text': True,
332
+ 'stack_info': True,
333
+ 'lineno': False,
334
+ 'funcName': False,
335
+ 'created': False,
336
+ 'msecs': False,
337
+ 'relativeCreated': False,
338
+ 'thread': False,
339
+ 'threadName': False,
340
+ 'processName': False,
341
+ 'process': False,
342
+ }
343
+
344
+ def format(self, record: logging.LogRecord) -> str:
345
+ dct = {
346
+ k: v
347
+ for k, o in self.KEYS.items()
348
+ for v in [getattr(record, k)]
349
+ if not (o and v is None)
350
+ }
351
+ return json_dumps_compact(dct)
352
+
353
+
354
+ def configure_standard_logging(level: ta.Union[int, str] = logging.INFO) -> None:
355
+ logging.root.addHandler(logging.StreamHandler())
356
+ logging.root.setLevel(level)
357
+
358
+
359
+ ########################################
360
+ # ../../../omlish/lite/marshal.py
361
+ """
362
+ TODO:
363
+ - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
364
+ """
365
+ # ruff: noqa: UP006 UP007
366
+
367
+
368
+ ##
369
+
370
+
371
+ class ObjMarshaler(abc.ABC):
372
+ @abc.abstractmethod
373
+ def marshal(self, o: ta.Any) -> ta.Any:
374
+ raise NotImplementedError
375
+
376
+ @abc.abstractmethod
377
+ def unmarshal(self, o: ta.Any) -> ta.Any:
378
+ raise NotImplementedError
379
+
380
+
381
+ class NopObjMarshaler(ObjMarshaler):
382
+ def marshal(self, o: ta.Any) -> ta.Any:
383
+ return o
384
+
385
+ def unmarshal(self, o: ta.Any) -> ta.Any:
386
+ return o
387
+
388
+
389
+ @dc.dataclass()
390
+ class ProxyObjMarshaler(ObjMarshaler):
391
+ m: ta.Optional[ObjMarshaler] = None
392
+
393
+ def marshal(self, o: ta.Any) -> ta.Any:
394
+ return check_not_none(self.m).marshal(o)
395
+
396
+ def unmarshal(self, o: ta.Any) -> ta.Any:
397
+ return check_not_none(self.m).unmarshal(o)
398
+
399
+
400
+ @dc.dataclass(frozen=True)
401
+ class CastObjMarshaler(ObjMarshaler):
402
+ ty: type
403
+
404
+ def marshal(self, o: ta.Any) -> ta.Any:
405
+ return o
406
+
407
+ def unmarshal(self, o: ta.Any) -> ta.Any:
408
+ return self.ty(o)
409
+
410
+
411
+ class DynamicObjMarshaler(ObjMarshaler):
412
+ def marshal(self, o: ta.Any) -> ta.Any:
413
+ return marshal_obj(o)
414
+
415
+ def unmarshal(self, o: ta.Any) -> ta.Any:
416
+ return o
417
+
418
+
419
+ @dc.dataclass(frozen=True)
420
+ class Base64ObjMarshaler(ObjMarshaler):
421
+ ty: type
422
+
423
+ def marshal(self, o: ta.Any) -> ta.Any:
424
+ return base64.b64encode(o).decode('ascii')
425
+
426
+ def unmarshal(self, o: ta.Any) -> ta.Any:
427
+ return self.ty(base64.b64decode(o))
428
+
429
+
430
+ @dc.dataclass(frozen=True)
431
+ class EnumObjMarshaler(ObjMarshaler):
432
+ ty: type
433
+
434
+ def marshal(self, o: ta.Any) -> ta.Any:
435
+ return o.name
436
+
437
+ def unmarshal(self, o: ta.Any) -> ta.Any:
438
+ return self.ty.__members__[o] # type: ignore
439
+
440
+
441
+ @dc.dataclass(frozen=True)
442
+ class OptionalObjMarshaler(ObjMarshaler):
443
+ item: ObjMarshaler
444
+
445
+ def marshal(self, o: ta.Any) -> ta.Any:
446
+ if o is None:
447
+ return None
448
+ return self.item.marshal(o)
449
+
450
+ def unmarshal(self, o: ta.Any) -> ta.Any:
451
+ if o is None:
452
+ return None
453
+ return self.item.unmarshal(o)
454
+
455
+
456
+ @dc.dataclass(frozen=True)
457
+ class MappingObjMarshaler(ObjMarshaler):
458
+ ty: type
459
+ km: ObjMarshaler
460
+ vm: ObjMarshaler
461
+
462
+ def marshal(self, o: ta.Any) -> ta.Any:
463
+ return {self.km.marshal(k): self.vm.marshal(v) for k, v in o.items()}
464
+
465
+ def unmarshal(self, o: ta.Any) -> ta.Any:
466
+ return self.ty((self.km.unmarshal(k), self.vm.unmarshal(v)) for k, v in o.items())
467
+
468
+
469
+ @dc.dataclass(frozen=True)
470
+ class IterableObjMarshaler(ObjMarshaler):
471
+ ty: type
472
+ item: ObjMarshaler
473
+
474
+ def marshal(self, o: ta.Any) -> ta.Any:
475
+ return [self.item.marshal(e) for e in o]
476
+
477
+ def unmarshal(self, o: ta.Any) -> ta.Any:
478
+ return self.ty(self.item.unmarshal(e) for e in o)
479
+
480
+
481
+ @dc.dataclass(frozen=True)
482
+ class DataclassObjMarshaler(ObjMarshaler):
483
+ ty: type
484
+ fs: ta.Mapping[str, ObjMarshaler]
485
+
486
+ def marshal(self, o: ta.Any) -> ta.Any:
487
+ return {k: m.marshal(getattr(o, k)) for k, m in self.fs.items()}
488
+
489
+ def unmarshal(self, o: ta.Any) -> ta.Any:
490
+ return self.ty(**{k: self.fs[k].unmarshal(v) for k, v in o.items()})
491
+
492
+
493
+ @dc.dataclass(frozen=True)
494
+ class PolymorphicObjMarshaler(ObjMarshaler):
495
+ class Impl(ta.NamedTuple):
496
+ ty: type
497
+ tag: str
498
+ m: ObjMarshaler
499
+
500
+ impls_by_ty: ta.Mapping[type, Impl]
501
+ impls_by_tag: ta.Mapping[str, Impl]
502
+
503
+ def marshal(self, o: ta.Any) -> ta.Any:
504
+ impl = self.impls_by_ty[type(o)]
505
+ return {impl.tag: impl.m.marshal(o)}
506
+
507
+ def unmarshal(self, o: ta.Any) -> ta.Any:
508
+ [(t, v)] = o.items()
509
+ impl = self.impls_by_tag[t]
510
+ return impl.m.unmarshal(v)
511
+
512
+
513
+ @dc.dataclass(frozen=True)
514
+ class DatetimeObjMarshaler(ObjMarshaler):
515
+ ty: type
516
+
517
+ def marshal(self, o: ta.Any) -> ta.Any:
518
+ return o.isoformat()
519
+
520
+ def unmarshal(self, o: ta.Any) -> ta.Any:
521
+ return self.ty.fromisoformat(o) # type: ignore
522
+
523
+
524
+ class DecimalObjMarshaler(ObjMarshaler):
525
+ def marshal(self, o: ta.Any) -> ta.Any:
526
+ return str(check_isinstance(o, decimal.Decimal))
527
+
528
+ def unmarshal(self, v: ta.Any) -> ta.Any:
529
+ return decimal.Decimal(check_isinstance(v, str))
530
+
531
+
532
+ class FractionObjMarshaler(ObjMarshaler):
533
+ def marshal(self, o: ta.Any) -> ta.Any:
534
+ fr = check_isinstance(o, fractions.Fraction)
535
+ return [fr.numerator, fr.denominator]
536
+
537
+ def unmarshal(self, v: ta.Any) -> ta.Any:
538
+ num, denom = check_isinstance(v, list)
539
+ return fractions.Fraction(num, denom)
540
+
541
+
542
+ class UuidObjMarshaler(ObjMarshaler):
543
+ def marshal(self, o: ta.Any) -> ta.Any:
544
+ return str(o)
545
+
546
+ def unmarshal(self, o: ta.Any) -> ta.Any:
547
+ return uuid.UUID(o)
548
+
549
+
550
+ _OBJ_MARSHALERS: ta.Dict[ta.Any, ObjMarshaler] = {
551
+ **{t: NopObjMarshaler() for t in (type(None),)},
552
+ **{t: CastObjMarshaler(t) for t in (int, float, str, bool)},
553
+ **{t: Base64ObjMarshaler(t) for t in (bytes, bytearray)},
554
+ **{t: IterableObjMarshaler(t, DynamicObjMarshaler()) for t in (list, tuple, set, frozenset)},
555
+ **{t: MappingObjMarshaler(t, DynamicObjMarshaler(), DynamicObjMarshaler()) for t in (dict,)},
556
+
557
+ ta.Any: DynamicObjMarshaler(),
558
+
559
+ **{t: DatetimeObjMarshaler(t) for t in (datetime.date, datetime.time, datetime.datetime)},
560
+ decimal.Decimal: DecimalObjMarshaler(),
561
+ fractions.Fraction: FractionObjMarshaler(),
562
+ uuid.UUID: UuidObjMarshaler(),
563
+ }
564
+
565
+ _OBJ_MARSHALER_GENERIC_MAPPING_TYPES: ta.Dict[ta.Any, type] = {
566
+ **{t: t for t in (dict,)},
567
+ **{t: dict for t in (collections.abc.Mapping, collections.abc.MutableMapping)},
568
+ }
569
+
570
+ _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
571
+ **{t: t for t in (list, tuple, set, frozenset)},
572
+ **{t: frozenset for t in (collections.abc.Set, collections.abc.MutableSet)},
573
+ **{t: tuple for t in (collections.abc.Sequence, collections.abc.MutableSequence)},
574
+ }
575
+
576
+
577
+ def register_opj_marshaler(ty: ta.Any, m: ObjMarshaler) -> None:
578
+ if ty in _OBJ_MARSHALERS:
579
+ raise KeyError(ty)
580
+ _OBJ_MARSHALERS[ty] = m
581
+
582
+
583
+ def _make_obj_marshaler(ty: ta.Any) -> ObjMarshaler:
584
+ if isinstance(ty, type) and abc.ABC in ty.__bases__:
585
+ impls = [ # type: ignore
586
+ PolymorphicObjMarshaler.Impl(
587
+ ity,
588
+ ity.__qualname__,
589
+ get_obj_marshaler(ity),
590
+ )
591
+ for ity in deep_subclasses(ty)
592
+ if abc.ABC not in ity.__bases__
593
+ ]
594
+ return PolymorphicObjMarshaler(
595
+ {i.ty: i for i in impls},
596
+ {i.tag: i for i in impls},
597
+ )
598
+
599
+ if isinstance(ty, type) and issubclass(ty, enum.Enum):
600
+ return EnumObjMarshaler(ty)
601
+
602
+ if dc.is_dataclass(ty):
603
+ return DataclassObjMarshaler(
604
+ ty,
605
+ {f.name: get_obj_marshaler(f.type) for f in dc.fields(ty)},
606
+ )
607
+
608
+ if is_generic_alias(ty):
609
+ try:
610
+ mt = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES[ta.get_origin(ty)]
611
+ except KeyError:
612
+ pass
613
+ else:
614
+ k, v = ta.get_args(ty)
615
+ return MappingObjMarshaler(mt, get_obj_marshaler(k), get_obj_marshaler(v))
616
+
617
+ try:
618
+ st = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES[ta.get_origin(ty)]
619
+ except KeyError:
620
+ pass
621
+ else:
622
+ [e] = ta.get_args(ty)
623
+ return IterableObjMarshaler(st, get_obj_marshaler(e))
624
+
625
+ if is_union_alias(ty):
626
+ return OptionalObjMarshaler(get_obj_marshaler(get_optional_alias_arg(ty)))
627
+
628
+ raise TypeError(ty)
629
+
630
+
631
+ def get_obj_marshaler(ty: ta.Any) -> ObjMarshaler:
632
+ try:
633
+ return _OBJ_MARSHALERS[ty]
634
+ except KeyError:
635
+ pass
636
+
637
+ p = ProxyObjMarshaler()
638
+ _OBJ_MARSHALERS[ty] = p
639
+ try:
640
+ m = _make_obj_marshaler(ty)
641
+ except Exception:
642
+ del _OBJ_MARSHALERS[ty]
643
+ raise
644
+ else:
645
+ p.m = m
646
+ _OBJ_MARSHALERS[ty] = m
647
+ return m
648
+
649
+
650
+ def marshal_obj(o: ta.Any, ty: ta.Any = None) -> ta.Any:
651
+ return get_obj_marshaler(ty if ty is not None else type(o)).marshal(o)
652
+
653
+
654
+ def unmarshal_obj(o: ta.Any, ty: ta.Union[ta.Type[T], ta.Any]) -> T:
655
+ return get_obj_marshaler(ty).unmarshal(o)
656
+
657
+
658
+ ########################################
659
+ # ../../../omlish/lite/runtime.py
660
+
661
+
662
+ @cached_nullary
663
+ def is_debugger_attached() -> bool:
664
+ return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
665
+
666
+
667
+ REQUIRED_PYTHON_VERSION = (3, 8)
668
+
669
+
670
+ def check_runtime_version() -> None:
671
+ if sys.version_info < REQUIRED_PYTHON_VERSION:
672
+ raise OSError(
673
+ f'Requires python {REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
674
+
675
+
676
+ ########################################
677
+ # ../../../omlish/lite/subprocesses.py
678
+ # ruff: noqa: UP006 UP007
679
+
680
+
681
+ ##
682
+
683
+
684
+ _SUBPROCESS_SHELL_WRAP_EXECS = False
685
+
686
+
687
+ def subprocess_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
688
+ return ('sh', '-c', ' '.join(map(shlex.quote, args)))
689
+
690
+
691
+ def subprocess_maybe_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
692
+ if _SUBPROCESS_SHELL_WRAP_EXECS or is_debugger_attached():
693
+ return subprocess_shell_wrap_exec(*args)
694
+ else:
695
+ return args
696
+
697
+
698
+ def _prepare_subprocess_invocation(
699
+ *args: str,
700
+ env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
701
+ extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
702
+ quiet: bool = False,
703
+ shell: bool = False,
704
+ **kwargs: ta.Any,
705
+ ) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
706
+ log.debug(args)
707
+ if extra_env:
708
+ log.debug(extra_env)
709
+
710
+ if extra_env:
711
+ env = {**(env if env is not None else os.environ), **extra_env}
712
+
713
+ if quiet and 'stderr' not in kwargs:
714
+ if not log.isEnabledFor(logging.DEBUG):
715
+ kwargs['stderr'] = subprocess.DEVNULL
716
+
717
+ if not shell:
718
+ args = subprocess_maybe_shell_wrap_exec(*args)
719
+
720
+ return args, dict(
721
+ env=env,
722
+ shell=shell,
723
+ **kwargs,
724
+ )
725
+
726
+
727
+ def subprocess_check_call(*args: str, stdout=sys.stderr, **kwargs: ta.Any) -> None:
728
+ args, kwargs = _prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
729
+ return subprocess.check_call(args, **kwargs) # type: ignore
730
+
731
+
732
+ def subprocess_check_output(*args: str, **kwargs: ta.Any) -> bytes:
733
+ args, kwargs = _prepare_subprocess_invocation(*args, **kwargs)
734
+ return subprocess.check_output(args, **kwargs)
735
+
736
+
737
+ def subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
738
+ return subprocess_check_output(*args, **kwargs).decode().strip()
739
+
740
+
741
+ ##
742
+
743
+
744
+ DEFAULT_SUBPROCESS_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
745
+ FileNotFoundError,
746
+ subprocess.CalledProcessError,
747
+ )
748
+
749
+
750
+ def subprocess_try_call(
751
+ *args: str,
752
+ try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
753
+ **kwargs: ta.Any,
754
+ ) -> bool:
755
+ try:
756
+ subprocess_check_call(*args, **kwargs)
757
+ except try_exceptions as e: # noqa
758
+ if log.isEnabledFor(logging.DEBUG):
759
+ log.exception('command failed')
760
+ return False
761
+ else:
762
+ return True
763
+
764
+
765
+ def subprocess_try_output(
766
+ *args: str,
767
+ try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
768
+ **kwargs: ta.Any,
769
+ ) -> ta.Optional[bytes]:
770
+ try:
771
+ return subprocess_check_output(*args, **kwargs)
772
+ except try_exceptions as e: # noqa
773
+ if log.isEnabledFor(logging.DEBUG):
774
+ log.exception('command failed')
775
+ return None
776
+
777
+
778
+ def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
779
+ out = subprocess_try_output(*args, **kwargs)
780
+ return out.decode().strip() if out is not None else None
781
+
782
+
783
+ ########################################
784
+ # runcommands.py
785
+
786
+
787
+ @dc.dataclass(frozen=True)
788
+ class CommandRequest:
789
+ cmd: ta.Sequence[str]
790
+ in_: ta.Optional[bytes] = None
791
+
792
+
793
+ @dc.dataclass(frozen=True)
794
+ class CommandResponse:
795
+ req: CommandRequest
796
+ rc: int
797
+ out: bytes
798
+ err: bytes
799
+
800
+
801
+ def _run_commands_loop(input: ta.BinaryIO, output: ta.BinaryIO = sys.stdout.buffer) -> None: # noqa
802
+ while (l := input.readline().decode('utf-8').strip()):
803
+ req: CommandRequest = unmarshal_obj(json.loads(l), CommandRequest)
804
+ proc = subprocess.Popen( # type: ignore
805
+ subprocess_maybe_shell_wrap_exec(*req.cmd),
806
+ **(dict(stdin=io.BytesIO(req.in_)) if req.in_ is not None else {}),
807
+ stdout=subprocess.PIPE,
808
+ stderr=subprocess.PIPE,
809
+ )
810
+ out, err = proc.communicate()
811
+ resp = CommandResponse(
812
+ req=req,
813
+ rc=proc.returncode,
814
+ out=out, # noqa
815
+ err=err, # noqa
816
+ )
817
+ output.write(json_dumps_compact(marshal_obj(resp)).encode('utf-8'))
818
+ output.write(b'\n')
819
+ output.flush()
820
+
821
+
822
+ def run_commands_main() -> None:
823
+ bs = post_boostrap()
824
+ _run_commands_loop(bs.input)