ominfra 0.0.0.dev159__py3-none-any.whl → 0.0.0.dev160__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,