ominfra 0.0.0.dev159__py3-none-any.whl → 0.0.0.dev160__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.
@@ -57,15 +57,14 @@ class DeployAppManager(DeployPathOwner):
57
57
  async def prepare_app(
58
58
  self,
59
59
  spec: DeploySpec,
60
- ):
61
- app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev, spec.key()))
60
+ ) -> None:
61
+ app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.checkout.rev, spec.key()))
62
62
  app_dir = os.path.join(self._dir(), spec.app, app_tag.tag)
63
63
 
64
64
  #
65
65
 
66
66
  await self._git.checkout(
67
- spec.repo,
68
- spec.rev,
67
+ spec.checkout,
69
68
  app_dir,
70
69
  )
71
70
 
@@ -5,6 +5,8 @@ from omlish.lite.logs import log
5
5
 
6
6
  from ..commands.base import Command
7
7
  from ..commands.base import CommandExecutor
8
+ from .apps import DeployAppManager
9
+ from .specs import DeploySpec
8
10
 
9
11
 
10
12
  ##
@@ -12,13 +14,20 @@ from ..commands.base import CommandExecutor
12
14
 
13
15
  @dc.dataclass(frozen=True)
14
16
  class DeployCommand(Command['DeployCommand.Output']):
17
+ spec: DeploySpec
18
+
15
19
  @dc.dataclass(frozen=True)
16
20
  class Output(Command.Output):
17
21
  pass
18
22
 
19
23
 
24
+ @dc.dataclass(frozen=True)
20
25
  class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
26
+ _apps: DeployAppManager
27
+
21
28
  async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
22
- log.info('Deploying!')
29
+ log.info('Deploying! %r', cmd.spec)
30
+
31
+ await self._apps.prepare_app(cmd.spec)
23
32
 
24
33
  return DeployCommand.Output()
@@ -18,6 +18,7 @@ from omlish.lite.check import check
18
18
 
19
19
  from .atomics import DeployAtomicPathSwapping
20
20
  from .paths import SingleDirDeployPathOwner
21
+ from .specs import DeployGitCheckout
21
22
  from .specs import DeployGitRepo
22
23
  from .types import DeployHome
23
24
  from .types import DeployRev
@@ -69,12 +70,16 @@ class DeployGitManager(SingleDirDeployPathOwner):
69
70
  else:
70
71
  return f'https://{self._repo.host}/{self._repo.path}'
71
72
 
73
+ #
74
+
72
75
  async def _call(self, *cmd: str) -> None:
73
76
  await asyncio_subprocesses.check_call(
74
77
  *cmd,
75
78
  cwd=self._dir,
76
79
  )
77
80
 
81
+ #
82
+
78
83
  @async_cached_nullary
79
84
  async def init(self) -> None:
80
85
  os.makedirs(self._dir, exist_ok=True)
@@ -88,7 +93,9 @@ class DeployGitManager(SingleDirDeployPathOwner):
88
93
  await self.init()
89
94
  await self._call('git', 'fetch', '--depth=1', 'origin', rev)
90
95
 
91
- async def checkout(self, rev: DeployRev, dst_dir: str) -> None:
96
+ #
97
+
98
+ async def checkout(self, checkout: DeployGitCheckout, dst_dir: str) -> None:
92
99
  check.state(not os.path.exists(dst_dir))
93
100
  with self._git._atomics.begin_atomic_path_swap( # noqa
94
101
  'dir',
@@ -96,14 +103,14 @@ class DeployGitManager(SingleDirDeployPathOwner):
96
103
  auto_commit=True,
97
104
  make_dirs=True,
98
105
  ) as dst_swap:
99
- await self.fetch(rev)
106
+ await self.fetch(checkout.rev)
100
107
 
101
108
  dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_swap.tmp_path)
102
109
  await dst_call('git', 'init')
103
110
 
104
111
  await dst_call('git', 'remote', 'add', 'local', self._dir)
105
- await dst_call('git', 'fetch', '--depth=1', 'local', rev)
106
- await dst_call('git', 'checkout', rev)
112
+ await dst_call('git', 'fetch', '--depth=1', 'local', checkout.rev)
113
+ await dst_call('git', 'checkout', checkout.rev, *(checkout.subtrees or []))
107
114
 
108
115
  def get_repo_dir(self, repo: DeployGitRepo) -> RepoDir:
109
116
  try:
@@ -112,5 +119,5 @@ class DeployGitManager(SingleDirDeployPathOwner):
112
119
  repo_dir = self._repo_dirs[repo] = DeployGitManager.RepoDir(self, repo)
113
120
  return repo_dir
114
121
 
115
- async def checkout(self, repo: DeployGitRepo, rev: DeployRev, dst_dir: str) -> None:
116
- await self.get_repo_dir(repo).checkout(rev, dst_dir)
122
+ async def checkout(self, checkout: DeployGitCheckout, dst_dir: str) -> None:
123
+ await self.get_repo_dir(checkout.repo).checkout(checkout, dst_dir)
@@ -1,40 +1,11 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  """
3
- ~deploy
4
- deploy.pid (flock)
5
- /app
6
- /<appplaceholder> - shallow clone
7
- /conf
8
- /env
9
- <appplaceholder>.env
10
- /nginx
11
- <appplaceholder>.conf
12
- /supervisor
13
- <appplaceholder>.conf
14
- /venv
15
- /<appplaceholder>
16
-
17
- /tmp
18
-
19
- ?
20
- /logs
21
- /wrmsr--omlish--<placeholder>
22
-
23
- placeholder = <name>--<rev>--<when>
24
-
25
- ==
26
-
27
- for dn in [
28
- 'app',
29
- 'conf',
30
- 'conf/env',
31
- 'conf/nginx',
32
- 'conf/supervisor',
33
- 'venv',
34
- ]:
35
-
36
- ==
37
-
3
+ TODO:
4
+ - run/pidfile
5
+ - logs/...
6
+ - current symlink
7
+ - conf/{nginx,supervisor}
8
+ - env/?
38
9
  """
39
10
  import abc
40
11
  import dataclasses as dc
@@ -59,7 +30,7 @@ DEPLOY_PATH_PLACEHOLDER_SEPARATORS = '-.'
59
30
 
60
31
  DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
61
32
  'app',
62
- 'tag', # <rev>-<dt>
33
+ 'tag',
63
34
  ])
64
35
 
65
36
 
@@ -25,14 +25,28 @@ class DeployGitRepo:
25
25
  check.not_in('.', check.non_empty_str(self.path))
26
26
 
27
27
 
28
+ @dc.dataclass(frozen=True)
29
+ class DeployGitCheckout:
30
+ repo: DeployGitRepo
31
+ rev: DeployRev
32
+
33
+ subtrees: ta.Optional[ta.Sequence[str]] = None
34
+
35
+ def __post_init__(self) -> None:
36
+ hash(self)
37
+ check.non_empty_str(self.rev)
38
+ if self.subtrees is not None:
39
+ for st in self.subtrees:
40
+ check.non_empty_str(st)
41
+
42
+
28
43
  ##
29
44
 
30
45
 
31
46
  @dc.dataclass(frozen=True)
32
47
  class DeploySpec:
33
48
  app: DeployApp
34
- repo: DeployGitRepo
35
- rev: DeployRev
49
+ checkout: DeployGitCheckout
36
50
 
37
51
  def __post_init__(self) -> None:
38
52
  hash(self)
@@ -1487,6 +1487,10 @@ def is_new_type(spec: ta.Any) -> bool:
1487
1487
  return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
1488
1488
 
1489
1489
 
1490
+ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
1491
+ return spec.__supertype__
1492
+
1493
+
1490
1494
  def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
1491
1495
  seen = set()
1492
1496
  todo = list(reversed(cls.__subclasses__()))
@@ -2450,9 +2454,7 @@ class aclosing(contextlib.AbstractAsyncContextManager): # noqa
2450
2454
  """
2451
2455
  TODO:
2452
2456
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
2453
- - namedtuple
2454
2457
  - literals
2455
- - newtypes?
2456
2458
  """
2457
2459
 
2458
2460
 
@@ -2462,7 +2464,7 @@ TODO:
2462
2464
  @dc.dataclass(frozen=True)
2463
2465
  class ObjMarshalOptions:
2464
2466
  raw_bytes: bool = False
2465
- nonstrict_dataclasses: bool = False
2467
+ non_strict_fields: bool = False
2466
2468
 
2467
2469
 
2468
2470
  class ObjMarshaler(abc.ABC):
@@ -2591,10 +2593,10 @@ class IterableObjMarshaler(ObjMarshaler):
2591
2593
 
2592
2594
 
2593
2595
  @dc.dataclass(frozen=True)
2594
- class DataclassObjMarshaler(ObjMarshaler):
2596
+ class FieldsObjMarshaler(ObjMarshaler):
2595
2597
  ty: type
2596
2598
  fs: ta.Mapping[str, ObjMarshaler]
2597
- nonstrict: bool = False
2599
+ non_strict: bool = False
2598
2600
 
2599
2601
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
2600
2602
  return {
@@ -2606,7 +2608,7 @@ class DataclassObjMarshaler(ObjMarshaler):
2606
2608
  return self.ty(**{
2607
2609
  k: self.fs[k].unmarshal(v, ctx)
2608
2610
  for k, v in o.items()
2609
- if not (self.nonstrict or ctx.options.nonstrict_dataclasses) or k in self.fs
2611
+ if not (self.non_strict or ctx.options.non_strict_fields) or k in self.fs
2610
2612
  })
2611
2613
 
2612
2614
 
@@ -2738,7 +2740,7 @@ class ObjMarshalerManager:
2738
2740
  ty: ta.Any,
2739
2741
  rec: ta.Callable[[ta.Any], ObjMarshaler],
2740
2742
  *,
2741
- nonstrict_dataclasses: bool = False,
2743
+ non_strict_fields: bool = False,
2742
2744
  ) -> ObjMarshaler:
2743
2745
  if isinstance(ty, type):
2744
2746
  if abc.ABC in ty.__bases__:
@@ -2760,12 +2762,22 @@ class ObjMarshalerManager:
2760
2762
  return EnumObjMarshaler(ty)
2761
2763
 
2762
2764
  if dc.is_dataclass(ty):
2763
- return DataclassObjMarshaler(
2765
+ return FieldsObjMarshaler(
2764
2766
  ty,
2765
2767
  {f.name: rec(f.type) for f in dc.fields(ty)},
2766
- nonstrict=nonstrict_dataclasses,
2768
+ non_strict=non_strict_fields,
2769
+ )
2770
+
2771
+ if issubclass(ty, tuple) and hasattr(ty, '_fields'):
2772
+ return FieldsObjMarshaler(
2773
+ ty,
2774
+ {p.name: rec(p.annotation) for p in inspect.signature(ty).parameters.values()},
2775
+ non_strict=non_strict_fields,
2767
2776
  )
2768
2777
 
2778
+ if is_new_type(ty):
2779
+ return rec(get_new_type_supertype(ty))
2780
+
2769
2781
  if is_generic_alias(ty):
2770
2782
  try:
2771
2783
  mt = self._generic_mapping_types[ta.get_origin(ty)]
ominfra/scripts/manage.py CHANGED
@@ -2692,6 +2692,10 @@ def is_new_type(spec: ta.Any) -> bool:
2692
2692
  return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
2693
2693
 
2694
2694
 
2695
+ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
2696
+ return spec.__supertype__
2697
+
2698
+
2695
2699
  def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
2696
2700
  seen = set()
2697
2701
  todo = list(reversed(cls.__subclasses__()))
@@ -4261,41 +4265,12 @@ class TempDirDeployAtomicPathSwapping(DeployAtomicPathSwapping):
4261
4265
  ########################################
4262
4266
  # ../deploy/paths.py
4263
4267
  """
4264
- ~deploy
4265
- deploy.pid (flock)
4266
- /app
4267
- /<appplaceholder> - shallow clone
4268
- /conf
4269
- /env
4270
- <appplaceholder>.env
4271
- /nginx
4272
- <appplaceholder>.conf
4273
- /supervisor
4274
- <appplaceholder>.conf
4275
- /venv
4276
- /<appplaceholder>
4277
-
4278
- /tmp
4279
-
4280
- ?
4281
- /logs
4282
- /wrmsr--omlish--<placeholder>
4283
-
4284
- placeholder = <name>--<rev>--<when>
4285
-
4286
- ==
4287
-
4288
- for dn in [
4289
- 'app',
4290
- 'conf',
4291
- 'conf/env',
4292
- 'conf/nginx',
4293
- 'conf/supervisor',
4294
- 'venv',
4295
- ]:
4296
-
4297
- ==
4298
-
4268
+ TODO:
4269
+ - run/pidfile
4270
+ - logs/...
4271
+ - current symlink
4272
+ - conf/{nginx,supervisor}
4273
+ - env/?
4299
4274
  """
4300
4275
 
4301
4276
 
@@ -4307,7 +4282,7 @@ DEPLOY_PATH_PLACEHOLDER_SEPARATORS = '-.'
4307
4282
 
4308
4283
  DEPLOY_PATH_PLACEHOLDERS: ta.FrozenSet[str] = frozenset([
4309
4284
  'app',
4310
- 'tag', # <rev>-<dt>
4285
+ 'tag',
4311
4286
  ])
4312
4287
 
4313
4288
 
@@ -4534,14 +4509,28 @@ class DeployGitRepo:
4534
4509
  check.not_in('.', check.non_empty_str(self.path))
4535
4510
 
4536
4511
 
4512
+ @dc.dataclass(frozen=True)
4513
+ class DeployGitCheckout:
4514
+ repo: DeployGitRepo
4515
+ rev: DeployRev
4516
+
4517
+ subtrees: ta.Optional[ta.Sequence[str]] = None
4518
+
4519
+ def __post_init__(self) -> None:
4520
+ hash(self)
4521
+ check.non_empty_str(self.rev)
4522
+ if self.subtrees is not None:
4523
+ for st in self.subtrees:
4524
+ check.non_empty_str(st)
4525
+
4526
+
4537
4527
  ##
4538
4528
 
4539
4529
 
4540
4530
  @dc.dataclass(frozen=True)
4541
4531
  class DeploySpec:
4542
4532
  app: DeployApp
4543
- repo: DeployGitRepo
4544
- rev: DeployRev
4533
+ checkout: DeployGitCheckout
4545
4534
 
4546
4535
  def __post_init__(self) -> None:
4547
4536
  hash(self)
@@ -6008,9 +5997,7 @@ inj = Injection
6008
5997
  """
6009
5998
  TODO:
6010
5999
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
6011
- - namedtuple
6012
6000
  - literals
6013
- - newtypes?
6014
6001
  """
6015
6002
 
6016
6003
 
@@ -6020,7 +6007,7 @@ TODO:
6020
6007
  @dc.dataclass(frozen=True)
6021
6008
  class ObjMarshalOptions:
6022
6009
  raw_bytes: bool = False
6023
- nonstrict_dataclasses: bool = False
6010
+ non_strict_fields: bool = False
6024
6011
 
6025
6012
 
6026
6013
  class ObjMarshaler(abc.ABC):
@@ -6149,10 +6136,10 @@ class IterableObjMarshaler(ObjMarshaler):
6149
6136
 
6150
6137
 
6151
6138
  @dc.dataclass(frozen=True)
6152
- class DataclassObjMarshaler(ObjMarshaler):
6139
+ class FieldsObjMarshaler(ObjMarshaler):
6153
6140
  ty: type
6154
6141
  fs: ta.Mapping[str, ObjMarshaler]
6155
- nonstrict: bool = False
6142
+ non_strict: bool = False
6156
6143
 
6157
6144
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
6158
6145
  return {
@@ -6164,7 +6151,7 @@ class DataclassObjMarshaler(ObjMarshaler):
6164
6151
  return self.ty(**{
6165
6152
  k: self.fs[k].unmarshal(v, ctx)
6166
6153
  for k, v in o.items()
6167
- if not (self.nonstrict or ctx.options.nonstrict_dataclasses) or k in self.fs
6154
+ if not (self.non_strict or ctx.options.non_strict_fields) or k in self.fs
6168
6155
  })
6169
6156
 
6170
6157
 
@@ -6296,7 +6283,7 @@ class ObjMarshalerManager:
6296
6283
  ty: ta.Any,
6297
6284
  rec: ta.Callable[[ta.Any], ObjMarshaler],
6298
6285
  *,
6299
- nonstrict_dataclasses: bool = False,
6286
+ non_strict_fields: bool = False,
6300
6287
  ) -> ObjMarshaler:
6301
6288
  if isinstance(ty, type):
6302
6289
  if abc.ABC in ty.__bases__:
@@ -6318,12 +6305,22 @@ class ObjMarshalerManager:
6318
6305
  return EnumObjMarshaler(ty)
6319
6306
 
6320
6307
  if dc.is_dataclass(ty):
6321
- return DataclassObjMarshaler(
6308
+ return FieldsObjMarshaler(
6322
6309
  ty,
6323
6310
  {f.name: rec(f.type) for f in dc.fields(ty)},
6324
- nonstrict=nonstrict_dataclasses,
6311
+ non_strict=non_strict_fields,
6312
+ )
6313
+
6314
+ if issubclass(ty, tuple) and hasattr(ty, '_fields'):
6315
+ return FieldsObjMarshaler(
6316
+ ty,
6317
+ {p.name: rec(p.annotation) for p in inspect.signature(ty).parameters.values()},
6318
+ non_strict=non_strict_fields,
6325
6319
  )
6326
6320
 
6321
+ if is_new_type(ty):
6322
+ return rec(get_new_type_supertype(ty))
6323
+
6327
6324
  if is_generic_alias(ty):
6328
6325
  try:
6329
6326
  mt = self._generic_mapping_types[ta.get_origin(ty)]
@@ -6749,27 +6746,6 @@ class PingCommandExecutor(CommandExecutor[PingCommand, PingCommand.Output]):
6749
6746
  CommandExecutorMap = ta.NewType('CommandExecutorMap', ta.Mapping[ta.Type[Command], CommandExecutor])
6750
6747
 
6751
6748
 
6752
- ########################################
6753
- # ../deploy/commands.py
6754
-
6755
-
6756
- ##
6757
-
6758
-
6759
- @dc.dataclass(frozen=True)
6760
- class DeployCommand(Command['DeployCommand.Output']):
6761
- @dc.dataclass(frozen=True)
6762
- class Output(Command.Output):
6763
- pass
6764
-
6765
-
6766
- class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
6767
- async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
6768
- log.info('Deploying!')
6769
-
6770
- return DeployCommand.Output()
6771
-
6772
-
6773
6749
  ########################################
6774
6750
  # ../deploy/tmp.py
6775
6751
 
@@ -8237,12 +8213,16 @@ class DeployGitManager(SingleDirDeployPathOwner):
8237
8213
  else:
8238
8214
  return f'https://{self._repo.host}/{self._repo.path}'
8239
8215
 
8216
+ #
8217
+
8240
8218
  async def _call(self, *cmd: str) -> None:
8241
8219
  await asyncio_subprocesses.check_call(
8242
8220
  *cmd,
8243
8221
  cwd=self._dir,
8244
8222
  )
8245
8223
 
8224
+ #
8225
+
8246
8226
  @async_cached_nullary
8247
8227
  async def init(self) -> None:
8248
8228
  os.makedirs(self._dir, exist_ok=True)
@@ -8256,7 +8236,9 @@ class DeployGitManager(SingleDirDeployPathOwner):
8256
8236
  await self.init()
8257
8237
  await self._call('git', 'fetch', '--depth=1', 'origin', rev)
8258
8238
 
8259
- async def checkout(self, rev: DeployRev, dst_dir: str) -> None:
8239
+ #
8240
+
8241
+ async def checkout(self, checkout: DeployGitCheckout, dst_dir: str) -> None:
8260
8242
  check.state(not os.path.exists(dst_dir))
8261
8243
  with self._git._atomics.begin_atomic_path_swap( # noqa
8262
8244
  'dir',
@@ -8264,14 +8246,14 @@ class DeployGitManager(SingleDirDeployPathOwner):
8264
8246
  auto_commit=True,
8265
8247
  make_dirs=True,
8266
8248
  ) as dst_swap:
8267
- await self.fetch(rev)
8249
+ await self.fetch(checkout.rev)
8268
8250
 
8269
8251
  dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_swap.tmp_path)
8270
8252
  await dst_call('git', 'init')
8271
8253
 
8272
8254
  await dst_call('git', 'remote', 'add', 'local', self._dir)
8273
- await dst_call('git', 'fetch', '--depth=1', 'local', rev)
8274
- await dst_call('git', 'checkout', rev)
8255
+ await dst_call('git', 'fetch', '--depth=1', 'local', checkout.rev)
8256
+ await dst_call('git', 'checkout', checkout.rev, *(checkout.subtrees or []))
8275
8257
 
8276
8258
  def get_repo_dir(self, repo: DeployGitRepo) -> RepoDir:
8277
8259
  try:
@@ -8280,8 +8262,8 @@ class DeployGitManager(SingleDirDeployPathOwner):
8280
8262
  repo_dir = self._repo_dirs[repo] = DeployGitManager.RepoDir(self, repo)
8281
8263
  return repo_dir
8282
8264
 
8283
- async def checkout(self, repo: DeployGitRepo, rev: DeployRev, dst_dir: str) -> None:
8284
- await self.get_repo_dir(repo).checkout(rev, dst_dir)
8265
+ async def checkout(self, checkout: DeployGitCheckout, dst_dir: str) -> None:
8266
+ await self.get_repo_dir(checkout.repo).checkout(checkout, dst_dir)
8285
8267
 
8286
8268
 
8287
8269
  ########################################
@@ -8919,15 +8901,14 @@ class DeployAppManager(DeployPathOwner):
8919
8901
  async def prepare_app(
8920
8902
  self,
8921
8903
  spec: DeploySpec,
8922
- ):
8923
- app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev, spec.key()))
8904
+ ) -> None:
8905
+ app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.checkout.rev, spec.key()))
8924
8906
  app_dir = os.path.join(self._dir(), spec.app, app_tag.tag)
8925
8907
 
8926
8908
  #
8927
8909
 
8928
8910
  await self._git.checkout(
8929
- spec.repo,
8930
- spec.rev,
8911
+ spec.checkout,
8931
8912
  app_dir,
8932
8913
  )
8933
8914
 
@@ -9650,6 +9631,34 @@ class SystemInterpProvider(InterpProvider):
9650
9631
  raise KeyError(version)
9651
9632
 
9652
9633
 
9634
+ ########################################
9635
+ # ../deploy/commands.py
9636
+
9637
+
9638
+ ##
9639
+
9640
+
9641
+ @dc.dataclass(frozen=True)
9642
+ class DeployCommand(Command['DeployCommand.Output']):
9643
+ spec: DeploySpec
9644
+
9645
+ @dc.dataclass(frozen=True)
9646
+ class Output(Command.Output):
9647
+ pass
9648
+
9649
+
9650
+ @dc.dataclass(frozen=True)
9651
+ class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]):
9652
+ _apps: DeployAppManager
9653
+
9654
+ async def execute(self, cmd: DeployCommand) -> DeployCommand.Output:
9655
+ log.info('Deploying! %r', cmd.spec)
9656
+
9657
+ await self._apps.prepare_app(cmd.spec)
9658
+
9659
+ return DeployCommand.Output()
9660
+
9661
+
9653
9662
  ########################################
9654
9663
  # ../remote/inject.py
9655
9664
 
@@ -2296,6 +2296,10 @@ def is_new_type(spec: ta.Any) -> bool:
2296
2296
  return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
2297
2297
 
2298
2298
 
2299
+ def get_new_type_supertype(spec: ta.Any) -> ta.Any:
2300
+ return spec.__supertype__
2301
+
2302
+
2299
2303
  def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
2300
2304
  seen = set()
2301
2305
  todo = list(reversed(cls.__subclasses__()))
@@ -4854,9 +4858,7 @@ inj = Injection
4854
4858
  """
4855
4859
  TODO:
4856
4860
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
4857
- - namedtuple
4858
4861
  - literals
4859
- - newtypes?
4860
4862
  """
4861
4863
 
4862
4864
 
@@ -4866,7 +4868,7 @@ TODO:
4866
4868
  @dc.dataclass(frozen=True)
4867
4869
  class ObjMarshalOptions:
4868
4870
  raw_bytes: bool = False
4869
- nonstrict_dataclasses: bool = False
4871
+ non_strict_fields: bool = False
4870
4872
 
4871
4873
 
4872
4874
  class ObjMarshaler(abc.ABC):
@@ -4995,10 +4997,10 @@ class IterableObjMarshaler(ObjMarshaler):
4995
4997
 
4996
4998
 
4997
4999
  @dc.dataclass(frozen=True)
4998
- class DataclassObjMarshaler(ObjMarshaler):
5000
+ class FieldsObjMarshaler(ObjMarshaler):
4999
5001
  ty: type
5000
5002
  fs: ta.Mapping[str, ObjMarshaler]
5001
- nonstrict: bool = False
5003
+ non_strict: bool = False
5002
5004
 
5003
5005
  def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
5004
5006
  return {
@@ -5010,7 +5012,7 @@ class DataclassObjMarshaler(ObjMarshaler):
5010
5012
  return self.ty(**{
5011
5013
  k: self.fs[k].unmarshal(v, ctx)
5012
5014
  for k, v in o.items()
5013
- if not (self.nonstrict or ctx.options.nonstrict_dataclasses) or k in self.fs
5015
+ if not (self.non_strict or ctx.options.non_strict_fields) or k in self.fs
5014
5016
  })
5015
5017
 
5016
5018
 
@@ -5142,7 +5144,7 @@ class ObjMarshalerManager:
5142
5144
  ty: ta.Any,
5143
5145
  rec: ta.Callable[[ta.Any], ObjMarshaler],
5144
5146
  *,
5145
- nonstrict_dataclasses: bool = False,
5147
+ non_strict_fields: bool = False,
5146
5148
  ) -> ObjMarshaler:
5147
5149
  if isinstance(ty, type):
5148
5150
  if abc.ABC in ty.__bases__:
@@ -5164,12 +5166,22 @@ class ObjMarshalerManager:
5164
5166
  return EnumObjMarshaler(ty)
5165
5167
 
5166
5168
  if dc.is_dataclass(ty):
5167
- return DataclassObjMarshaler(
5169
+ return FieldsObjMarshaler(
5168
5170
  ty,
5169
5171
  {f.name: rec(f.type) for f in dc.fields(ty)},
5170
- nonstrict=nonstrict_dataclasses,
5172
+ non_strict=non_strict_fields,
5173
+ )
5174
+
5175
+ if issubclass(ty, tuple) and hasattr(ty, '_fields'):
5176
+ return FieldsObjMarshaler(
5177
+ ty,
5178
+ {p.name: rec(p.annotation) for p in inspect.signature(ty).parameters.values()},
5179
+ non_strict=non_strict_fields,
5171
5180
  )
5172
5181
 
5182
+ if is_new_type(ty):
5183
+ return rec(get_new_type_supertype(ty))
5184
+
5173
5185
  if is_generic_alias(ty):
5174
5186
  try:
5175
5187
  mt = self._generic_mapping_types[ta.get_origin(ty)]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev159
3
+ Version: 0.0.0.dev160
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.dev159
16
- Requires-Dist: omlish==0.0.0.dev159
15
+ Requires-Dist: omdev==0.0.0.dev160
16
+ Requires-Dist: omlish==0.0.0.dev160
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -44,15 +44,15 @@ ominfra/manage/commands/ping.py,sha256=DVZFzL1Z_f-Bq53vxMrL3xOi0iK_nMonJE4KvQf9w
44
44
  ominfra/manage/commands/subprocess.py,sha256=yHGMbAI-xKe_9BUs5IZ3Yav8qRE-I9aGnBtTwW15Pnw,2440
45
45
  ominfra/manage/commands/types.py,sha256=XFZPeqeIBAaIIQF3pdPbGxLlb-LCrz6WtlDWO2q_vz0,210
46
46
  ominfra/manage/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- ominfra/manage/deploy/apps.py,sha256=zXoIiY2FARQ5Bwpqqh69nQt5hon9nliYRIarSNZuST4,1888
47
+ ominfra/manage/deploy/apps.py,sha256=lXcbyX8_wrvvwKtIMM9P_Mh7xL8yj6z9_PFTl_0u-0U,1887
48
48
  ominfra/manage/deploy/atomics.py,sha256=j9_L2LXls2dR1I4rQw3msIa2D90JwEO9Mb8KBGOKmyU,5180
49
- ominfra/manage/deploy/commands.py,sha256=YZGdpiaqj_-iMp2SMdz4hB6bTKk3UQxnHeTblGyxuuk,560
49
+ ominfra/manage/deploy/commands.py,sha256=N9qVntnRgJ_IneI7rEQB2Za0oU7gouPfm-sl2MCwW1E,764
50
50
  ominfra/manage/deploy/config.py,sha256=aR6ubMEWqkTI55XtcG1Cczn6YhCVN6eSL8DT5EHQJN0,166
51
- ominfra/manage/deploy/git.py,sha256=t_GQBElH5dL56ww-OLoPKu__Qdtbz9VvR_DQExwtS8M,3606
51
+ ominfra/manage/deploy/git.py,sha256=T-ad5HtavIsZG3Y1lNimYXvrvklWfm2kZBcV1DLlI-A,3750
52
52
  ominfra/manage/deploy/inject.py,sha256=JBc96rxOL7Q6P78yZP4WHp08i2AHvV0JHbRpUzbFblw,1444
53
53
  ominfra/manage/deploy/interp.py,sha256=OKkenH8YKEW_mEDR6X7_ZLxK9a1Ox6KHSwFPTHT6OzA,1029
54
- ominfra/manage/deploy/paths.py,sha256=Wda2kvn-AjIBTnfk5XwC9Dpj89Px6CaocHvPNy5uRNA,7203
55
- ominfra/manage/deploy/specs.py,sha256=q71CORthSCAtJaR-dNRFVFF3zQvM58Xuo3tE-urNZWU,882
54
+ ominfra/manage/deploy/paths.py,sha256=tK8zZFWOHDRdTN5AlTe-3MpgZqovhWrljGosQmeEYvo,6839
55
+ ominfra/manage/deploy/specs.py,sha256=Yq3WiLNJcodUBEsJfP18gPGB3X2ABI1g8YLlsUvJOXg,1230
56
56
  ominfra/manage/deploy/tmp.py,sha256=Wg29UMsWL_A8anFsE-XyvkTNsMfH26Nr8BvJxgKNxMo,1248
57
57
  ominfra/manage/deploy/types.py,sha256=o95wqvTGNRq8Cxx7VpqeX-9x1tI8k8BpqPFvJZkJYBA,305
58
58
  ominfra/manage/deploy/venvs.py,sha256=A1nqFo1Zhxg-Sw3Uyxe6hck4ZEh3bBq8GIjnJPvNLd8,2232
@@ -76,9 +76,9 @@ ominfra/manage/targets/connection.py,sha256=j2QrVS-QFOZJ47TqwaMt8MSPg0whokysGePa
76
76
  ominfra/manage/targets/inject.py,sha256=P4597xWM-V3I_gCt2O71OLhYQkkXtuJvkYRsIbhhMcE,1561
77
77
  ominfra/manage/targets/targets.py,sha256=CFl8Uirgn3gfowO1Fn-LBK-6qYqEMFJ9snPUl0gCRuM,1753
78
78
  ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- ominfra/scripts/journald2aws.py,sha256=ptC5QEa-AQo1XQQnrEdroExdUfX47hnegrIF8-cWYtY,154666
80
- ominfra/scripts/manage.py,sha256=uxnQ19q9uIbspC_vIN6SM0IikyW5zC16kibqtgjBnTM,293288
81
- ominfra/scripts/supervisor.py,sha256=5qEwZY701LZVZgN3zfesv2eo-zI4NieJyQW1ZzuxPa4,273568
79
+ ominfra/scripts/journald2aws.py,sha256=EC8tSKW3hztBV_Kr_ykK72AmcvnWivUxcz6Sfg3M_hI,155085
80
+ ominfra/scripts/manage.py,sha256=mrjtcbAYVv3RkhEV_tcdPra5lZTJG27k0qgAZsye9zk,293936
81
+ ominfra/scripts/supervisor.py,sha256=npGYEWSZfY7E24mdkJ3HrL_ax6AcqjHfqh-7nZ_sX0U,273987
82
82
  ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
83
83
  ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
84
84
  ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
@@ -120,9 +120,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
120
120
  ominfra/tailscale/cli.py,sha256=h6akQJMl0KuWLHS7Ur6WcBZ2JwF0DJQhsPTnFBdGyNk,3571
121
121
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
122
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
123
- ominfra-0.0.0.dev159.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
124
- ominfra-0.0.0.dev159.dist-info/METADATA,sha256=7V3dKKJZtJN-ecCQ8V4428AnWq-mr7W7HR64ez-s5mg,731
125
- ominfra-0.0.0.dev159.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
126
- ominfra-0.0.0.dev159.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
127
- ominfra-0.0.0.dev159.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
128
- ominfra-0.0.0.dev159.dist-info/RECORD,,
123
+ ominfra-0.0.0.dev160.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
124
+ ominfra-0.0.0.dev160.dist-info/METADATA,sha256=eS2Inhifb3u6Dusi7KHOATGGlcALPwhc363rDTOzVz0,731
125
+ ominfra-0.0.0.dev160.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
126
+ ominfra-0.0.0.dev160.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
127
+ ominfra-0.0.0.dev160.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
128
+ ominfra-0.0.0.dev160.dist-info/RECORD,,