ominfra 0.0.0.dev134__py3-none-any.whl → 0.0.0.dev135__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,25 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import abc
3
+ import dataclasses as dc
4
+ import typing as ta
5
+
6
+
7
+ CommandInputT = ta.TypeVar('CommandInputT', bound='Command.Input')
8
+ CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
9
+
10
+
11
+ ##
12
+
13
+
14
+ class Command(abc.ABC, ta.Generic[CommandInputT, CommandOutputT]):
15
+ @dc.dataclass(frozen=True)
16
+ class Input(abc.ABC): # noqa
17
+ pass
18
+
19
+ @dc.dataclass(frozen=True)
20
+ class Output(abc.ABC): # noqa
21
+ pass
22
+
23
+ @abc.abstractmethod
24
+ def _execute(self, inp: CommandInputT) -> CommandOutputT:
25
+ raise NotImplementedError
@@ -0,0 +1,73 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
3
+ import os
4
+ import subprocess
5
+ import time
6
+ import typing as ta
7
+
8
+ from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
9
+
10
+ from .base import Command
11
+
12
+
13
+ ##
14
+
15
+
16
+ class SubprocessCommand(Command['SubprocessCommand.Input', 'SubprocessCommand.Output']):
17
+ @dc.dataclass(frozen=True)
18
+ class Input(Command.Input):
19
+ args: ta.Sequence[str]
20
+
21
+ shell: bool = False
22
+ cwd: ta.Optional[str] = None
23
+ env: ta.Optional[ta.Mapping[str, str]] = None
24
+
25
+ capture_stdout: bool = False
26
+ capture_stderr: bool = False
27
+
28
+ input: ta.Optional[bytes] = None
29
+ timeout: ta.Optional[float] = None
30
+
31
+ def __post_init__(self) -> None:
32
+ if isinstance(self.args, str):
33
+ raise TypeError(self.args)
34
+
35
+ @dc.dataclass(frozen=True)
36
+ class Output(Command.Output):
37
+ rc: int
38
+ pid: int
39
+
40
+ elapsed_s: float
41
+
42
+ stdout: ta.Optional[bytes] = None
43
+ stderr: ta.Optional[bytes] = None
44
+
45
+ def _execute(self, inp: Input) -> Output:
46
+ proc = subprocess.Popen(
47
+ subprocess_maybe_shell_wrap_exec(*inp.args),
48
+
49
+ shell=inp.shell,
50
+ cwd=inp.cwd,
51
+ env={**os.environ, **(inp.env or {})},
52
+
53
+ stdin=subprocess.PIPE if inp.input is not None else None,
54
+ stdout=subprocess.PIPE if inp.capture_stdout else None,
55
+ stderr=subprocess.PIPE if inp.capture_stderr else None,
56
+ )
57
+
58
+ start_time = time.time()
59
+ stdout, stderr = proc.communicate(
60
+ input=inp.input,
61
+ timeout=inp.timeout,
62
+ )
63
+ end_time = time.time()
64
+
65
+ return SubprocessCommand.Output(
66
+ rc=proc.returncode,
67
+ pid=proc.pid,
68
+
69
+ elapsed_s=end_time - start_time,
70
+
71
+ stdout=stdout, # noqa
72
+ stderr=stderr, # noqa
73
+ )
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ # @omlish-amalg ./_manage.py
3
+ # ruff: noqa: UP006 UP007
4
+ import inspect
5
+ import json
6
+ import os
7
+ import shlex
8
+ import struct
9
+ import subprocess
10
+ import sys
11
+ import typing as ta
12
+
13
+ from omlish.lite.check import check_not_none
14
+ from omlish.lite.json import json_dumps_compact
15
+ from omlish.lite.marshal import marshal_obj
16
+ from omlish.lite.marshal import unmarshal_obj
17
+ from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
18
+
19
+ from ...pyremote import PyremoteBootstrapDriver
20
+ from ...pyremote import pyremote_bootstrap_finalize
21
+ from ...pyremote import pyremote_build_bootstrap_cmd
22
+ from .commands.subprocess import SubprocessCommand
23
+
24
+
25
+ ##
26
+
27
+
28
+ def _send_obj(f: ta.IO, o: ta.Any) -> None:
29
+ j = json_dumps_compact(marshal_obj(o))
30
+ d = j.encode('utf-8')
31
+
32
+ f.write(struct.pack('<I', len(d)))
33
+ f.write(d)
34
+ f.flush()
35
+
36
+
37
+ def _recv_obj(f: ta.IO, ty: type) -> ta.Any:
38
+ d = f.read(4)
39
+ if not d:
40
+ return None
41
+ if len(d) != 4:
42
+ raise Exception
43
+
44
+ sz = struct.unpack('<I', d)[0]
45
+ d = f.read(sz)
46
+ if not d:
47
+ raise Exception
48
+
49
+ j = json.loads(d.decode('utf-8'))
50
+ return unmarshal_obj(j, ty)
51
+
52
+
53
+ def _remote_main() -> None:
54
+ rt = pyremote_bootstrap_finalize() # noqa
55
+
56
+ while True:
57
+ i = _recv_obj(rt.input, SubprocessCommand.Input)
58
+ if i is None:
59
+ break
60
+
61
+ o = SubprocessCommand()._execute(i) # noqa
62
+
63
+ _send_obj(sys.stdout.buffer, o)
64
+
65
+
66
+ def _main() -> None:
67
+ import argparse
68
+
69
+ parser = argparse.ArgumentParser()
70
+
71
+ parser.add_argument('--ssh')
72
+ parser.add_argument('--python', default='python3')
73
+ parser.add_argument('--_amalg-file')
74
+
75
+ args = parser.parse_args()
76
+
77
+ #
78
+
79
+ self_src = inspect.getsource(sys.modules[__name__])
80
+ self_src_lines = self_src.splitlines()
81
+ for l in self_src_lines:
82
+ if l.startswith('# @omlish-amalg-output '):
83
+ is_self_amalg = True
84
+ break
85
+ else:
86
+ is_self_amalg = False
87
+
88
+ if is_self_amalg:
89
+ amalg_src = self_src
90
+ else:
91
+ amalg_file = args._amalg_file # noqa
92
+ if amalg_file is None:
93
+ amalg_file = os.path.join(os.path.dirname(__file__), '_manage.py')
94
+ with open(amalg_file) as f:
95
+ amalg_src = f.read()
96
+
97
+ #
98
+
99
+ remote_src = '\n\n'.join([
100
+ '__name__ = "__remote__"',
101
+ amalg_src,
102
+ '_remote_main()',
103
+ ])
104
+
105
+ #
106
+
107
+ bs_src = pyremote_build_bootstrap_cmd(__package__ or 'manage')
108
+
109
+ if args.ssh is not None:
110
+ sh_src = ' '.join([args.python, '-c', shlex.quote(bs_src)])
111
+ sh_cmd = f'{args.ssh} {shlex.quote(sh_src)}'
112
+ cmd = [sh_cmd]
113
+ shell = True
114
+ else:
115
+ cmd = [args.python, '-c', bs_src]
116
+ shell = False
117
+
118
+ proc = subprocess.Popen(
119
+ subprocess_maybe_shell_wrap_exec(*cmd),
120
+ shell=shell,
121
+ stdin=subprocess.PIPE,
122
+ stdout=subprocess.PIPE,
123
+ )
124
+
125
+ stdin = check_not_none(proc.stdin)
126
+ stdout = check_not_none(proc.stdout)
127
+
128
+ res = PyremoteBootstrapDriver(remote_src).run(stdin, stdout)
129
+ print(res)
130
+
131
+ #
132
+
133
+ for ci in [
134
+ SubprocessCommand.Input(
135
+ args=['python3', '-'],
136
+ input=b'print(1)\n',
137
+ capture_stdout=True,
138
+ ),
139
+ SubprocessCommand.Input(
140
+ args=['uname'],
141
+ capture_stdout=True,
142
+ ),
143
+ ]:
144
+ _send_obj(stdin, ci)
145
+
146
+ o = _recv_obj(stdout, SubprocessCommand.Output)
147
+
148
+ print(o)
149
+
150
+ try:
151
+ stdin.close()
152
+ except BrokenPipeError:
153
+ pass
154
+
155
+ proc.wait()
156
+
157
+
158
+ if __name__ == '__main__':
159
+ _main()
ominfra/pyremote.py CHANGED
@@ -107,6 +107,9 @@ def _pyremote_bootstrap_main(context_name: str) -> None:
107
107
 
108
108
 
109
109
  def pyremote_build_bootstrap_cmd(context_name: str) -> str:
110
+ if any(c in context_name for c in '\'"'):
111
+ raise NameError(context_name)
112
+
110
113
  bs_src = textwrap.dedent(inspect.getsource(_pyremote_bootstrap_main))
111
114
 
112
115
  for gl in [
@@ -133,6 +136,9 @@ def pyremote_build_bootstrap_cmd(context_name: str) -> str:
133
136
  bs_z = zlib.compress(bs_src.encode('utf-8'))
134
137
  bs_z64 = base64.encodebytes(bs_z).replace(b'\n', b'')
135
138
 
139
+ def dq_repr(o: ta.Any) -> str:
140
+ return '"' + repr(o)[1:-1] + '"'
141
+
136
142
  stmts = [
137
143
  f'import {", ".join(_PYREMOTE_BOOTSTRAP_IMPORTS)}',
138
144
  f'exec(zlib.decompress(base64.decodebytes({bs_z64!r})))',
@@ -326,7 +332,7 @@ class PyremoteBootstrapDriver:
326
332
  def run(self, stdin: ta.IO, stdout: ta.IO) -> Result:
327
333
  gen = self.gen()
328
334
 
329
- gi: bytes | None = None
335
+ gi: ta.Optional[bytes] = None
330
336
  while True:
331
337
  try:
332
338
  if gi is not None:
@@ -2289,6 +2289,13 @@ class PolymorphicObjMarshaler(ObjMarshaler):
2289
2289
  impls_by_ty: ta.Mapping[type, Impl]
2290
2290
  impls_by_tag: ta.Mapping[str, Impl]
2291
2291
 
2292
+ @classmethod
2293
+ def of(cls, impls: ta.Iterable[Impl]) -> 'PolymorphicObjMarshaler':
2294
+ return cls(
2295
+ {i.ty: i for i in impls},
2296
+ {i.tag: i for i in impls},
2297
+ )
2298
+
2292
2299
  def marshal(self, o: ta.Any) -> ta.Any:
2293
2300
  impl = self.impls_by_ty[type(o)]
2294
2301
  return {impl.tag: impl.m.marshal(o)}
@@ -2376,7 +2383,7 @@ def _make_obj_marshaler(
2376
2383
  ) -> ObjMarshaler:
2377
2384
  if isinstance(ty, type):
2378
2385
  if abc.ABC in ty.__bases__:
2379
- impls = [ # type: ignore
2386
+ return PolymorphicObjMarshaler.of([ # type: ignore
2380
2387
  PolymorphicObjMarshaler.Impl(
2381
2388
  ity,
2382
2389
  ity.__qualname__,
@@ -2384,11 +2391,7 @@ def _make_obj_marshaler(
2384
2391
  )
2385
2392
  for ity in deep_subclasses(ty)
2386
2393
  if abc.ABC not in ity.__bases__
2387
- ]
2388
- return PolymorphicObjMarshaler(
2389
- {i.ty: i for i in impls},
2390
- {i.tag: i for i in impls},
2391
- )
2394
+ ])
2392
2395
 
2393
2396
  if issubclass(ty, enum.Enum):
2394
2397
  return EnumObjMarshaler(ty)
@@ -4861,6 +4861,13 @@ class PolymorphicObjMarshaler(ObjMarshaler):
4861
4861
  impls_by_ty: ta.Mapping[type, Impl]
4862
4862
  impls_by_tag: ta.Mapping[str, Impl]
4863
4863
 
4864
+ @classmethod
4865
+ def of(cls, impls: ta.Iterable[Impl]) -> 'PolymorphicObjMarshaler':
4866
+ return cls(
4867
+ {i.ty: i for i in impls},
4868
+ {i.tag: i for i in impls},
4869
+ )
4870
+
4864
4871
  def marshal(self, o: ta.Any) -> ta.Any:
4865
4872
  impl = self.impls_by_ty[type(o)]
4866
4873
  return {impl.tag: impl.m.marshal(o)}
@@ -4948,7 +4955,7 @@ def _make_obj_marshaler(
4948
4955
  ) -> ObjMarshaler:
4949
4956
  if isinstance(ty, type):
4950
4957
  if abc.ABC in ty.__bases__:
4951
- impls = [ # type: ignore
4958
+ return PolymorphicObjMarshaler.of([ # type: ignore
4952
4959
  PolymorphicObjMarshaler.Impl(
4953
4960
  ity,
4954
4961
  ity.__qualname__,
@@ -4956,11 +4963,7 @@ def _make_obj_marshaler(
4956
4963
  )
4957
4964
  for ity in deep_subclasses(ty)
4958
4965
  if abc.ABC not in ity.__bases__
4959
- ]
4960
- return PolymorphicObjMarshaler(
4961
- {i.ty: i for i in impls},
4962
- {i.tag: i for i in impls},
4963
- )
4966
+ ])
4964
4967
 
4965
4968
  if issubclass(ty, enum.Enum):
4966
4969
  return EnumObjMarshaler(ty)
@@ -6182,6 +6185,7 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
6182
6185
  *,
6183
6186
  read_size: int = 0x10000,
6184
6187
  write_size: int = 0x10000,
6188
+ log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
6185
6189
  ) -> None:
6186
6190
  check_state(not sock.getblocking())
6187
6191
 
@@ -6190,6 +6194,7 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
6190
6194
  self._handler = handler
6191
6195
  self._read_size = read_size
6192
6196
  self._write_size = write_size
6197
+ self._log_handler = log_handler
6193
6198
 
6194
6199
  self._read_buf = ReadableListBuffer()
6195
6200
  self._write_buf: IncrementalWriteBuffer | None = None
@@ -6224,7 +6229,8 @@ class CoroHttpServerConnectionFdIoHandler(SocketFdIoHandler):
6224
6229
  break
6225
6230
 
6226
6231
  if isinstance(o, CoroHttpServer.AnyLogIo):
6227
- print(o)
6232
+ if self._log_handler is not None:
6233
+ self._log_handler(self._coro_srv, o)
6228
6234
  o = None
6229
6235
 
6230
6236
  elif isinstance(o, CoroHttpServer.ReadIo):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev134
3
+ Version: 0.0.0.dev135
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
13
  Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev==0.0.0.dev134
16
- Requires-Dist: omlish==0.0.0.dev134
15
+ Requires-Dist: omdev==0.0.0.dev135
16
+ Requires-Dist: omlish==0.0.0.dev135
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -3,7 +3,7 @@ ominfra/__about__.py,sha256=6i1AoruFYQCd-PyhhbDQDWY2d1tiQu9nkwWr-fXAqfY,705
3
3
  ominfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ominfra/cmds.py,sha256=E0AfnvEmnKntXWvmLW5L05_NeDpBET1VBXn7vV6EwBQ,2083
5
5
  ominfra/configs.py,sha256=8aU1Qmbr-qjaE2iP3gAbA2SWJYMPZ-uGK007L01PoOI,1727
6
- ominfra/pyremote.py,sha256=nqzmMKZvUMiw2y65v8VVjx832sKfSB7jBOkP8SQBmGI,10121
6
+ ominfra/pyremote.py,sha256=z3D9nj26omHgl3_q_h7uSHRtgYu-Sb06fMQCqS69_QI,10289
7
7
  ominfra/ssh.py,sha256=jQpc4WvkMckIfk4vILda8zFaeharRqc_6wxW50b0OjQ,5431
8
8
  ominfra/threadworkers.py,sha256=oX4ubZn7h932saXpRIJu2MNhBExgGGMuGhdXarZxLJw,4948
9
9
  ominfra/clouds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -30,7 +30,7 @@ ominfra/journald/tailer.py,sha256=5abcFMfgi7fnY9ZEQe2ZVobaJxjQkeu6d9Kagw33a1w,33
30
30
  ominfra/manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  ominfra/manage/manage.py,sha256=BttL8LFEknHZE_h2Pt5dAqbfUkv6qy43WI0raXBZ1a8,151
32
32
  ominfra/manage/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- ominfra/manage/deploy/_executor.py,sha256=lk1pXkwqijudeQ9Ti8oxP4O-qUFDFzcXI8H-Ox5K-SA,35385
33
+ ominfra/manage/deploy/_executor.py,sha256=amAnQWG5PBrsYVqd7K6xILyq88mhTUZucTWtSenceTE,35466
34
34
  ominfra/manage/deploy/configs.py,sha256=qi0kwT7G2NH7dXLOQic-u6R3yeadup_QtvrjwWIggbM,435
35
35
  ominfra/manage/deploy/remote.py,sha256=WHHElLmYk6wEDC4R8s_jHi1z71CCuV3soqF3elzIWEM,2145
36
36
  ominfra/manage/deploy/executor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
@@ -56,9 +56,15 @@ ominfra/manage/deploy/poly/runtime.py,sha256=G66AI-a27Wi6QYpG3M0ryqI7c5MnJkXt-_j
56
56
  ominfra/manage/deploy/poly/site.py,sha256=QJwDDJoVm2-kxi4bxIrp-mn4y2qDLuW3CAUax3W8gv8,239
57
57
  ominfra/manage/deploy/poly/supervisor.py,sha256=zkl6VQBcAZaMAhyR9DbbbqULcgFCDZoe9S_vP-mMFQ8,2289
58
58
  ominfra/manage/deploy/poly/venv.py,sha256=BoipDEa4NTeodjf3L57KJfq9eGKLagFNKwD8pS4yrzA,1552
59
+ ominfra/manage/new/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
60
+ ominfra/manage/new/_manage.py,sha256=ycfNgyFofYs6f5jYV2SrAprGChQd-uVhWc79Tvd9iB0,40217
61
+ ominfra/manage/new/main.py,sha256=WeiBAM7HQosnDbKahRmDWNvNYUZaKH2d3ooaqk9kQ04,3558
62
+ ominfra/manage/new/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
+ ominfra/manage/new/commands/base.py,sha256=TTrHL213jf-ClVqToiNHuxQey1Yf6052E8u3E9hAf7Q,574
64
+ ominfra/manage/new/commands/subprocess.py,sha256=GpbD-cTorgCRg203lCl81HAh-NBYA6ObKa5ZP2ss9rg,1884
59
65
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
- ominfra/scripts/journald2aws.py,sha256=o6WgI9R3MXdH4-BKUYAK2pcu3E5T0GAl5pggzR4Lhrk,131537
61
- ominfra/scripts/supervisor.py,sha256=gy2P8K8aATDQDvSFqgnvIun2qlZrWg2F066kzcoQRdc,245528
66
+ ominfra/scripts/journald2aws.py,sha256=kCyeyCo53GRoCKAdVAe0aGkgmINxi8IBcUfRXphACek,131618
67
+ ominfra/scripts/supervisor.py,sha256=GO_e3e6WWqdBE-WejtaTrzU8yjtZBiadD-o2Nb-aJ6o,245838
62
68
  ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
63
69
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
64
70
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -100,9 +106,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
100
106
  ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
101
107
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
108
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
103
- ominfra-0.0.0.dev134.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
104
- ominfra-0.0.0.dev134.dist-info/METADATA,sha256=pJ8EYFED29tv1T8wzhlFFm2rKeNXXS2c4Iw8hjFEvSA,731
105
- ominfra-0.0.0.dev134.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
106
- ominfra-0.0.0.dev134.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
107
- ominfra-0.0.0.dev134.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
108
- ominfra-0.0.0.dev134.dist-info/RECORD,,
109
+ ominfra-0.0.0.dev135.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
110
+ ominfra-0.0.0.dev135.dist-info/METADATA,sha256=l8zW4aJP_w8h0b-wfdGnlXqPlofGjiw0HLQAfdFfJK4,731
111
+ ominfra-0.0.0.dev135.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
112
+ ominfra-0.0.0.dev135.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
113
+ ominfra-0.0.0.dev135.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
114
+ ominfra-0.0.0.dev135.dist-info/RECORD,,