ominfra 0.0.0.dev174__py3-none-any.whl → 0.0.0.dev176__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.
ominfra/configs.py CHANGED
@@ -6,7 +6,8 @@ import typing as ta
6
6
 
7
7
  from omdev.toml.parser import toml_loads
8
8
  from omlish.lite.check import check
9
- from omlish.lite.marshal import unmarshal_obj
9
+ from omlish.lite.marshal import OBJ_MARSHALER_MANAGER
10
+ from omlish.lite.marshal import ObjMarshalerManager
10
11
 
11
12
 
12
13
  T = ta.TypeVar('T')
@@ -46,6 +47,7 @@ def read_config_file(
46
47
  cls: ta.Type[T],
47
48
  *,
48
49
  prepare: ta.Optional[ta.Callable[[ConfigMapping], ConfigMapping]] = None,
50
+ msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
49
51
  ) -> T:
50
52
  with open(path) as cf:
51
53
  config_dct = parse_config_file(os.path.basename(path), cf)
@@ -53,7 +55,7 @@ def read_config_file(
53
55
  if prepare is not None:
54
56
  config_dct = prepare(config_dct)
55
57
 
56
- return unmarshal_obj(config_dct, cls)
58
+ return msh.unmarshal_obj(config_dct, cls)
57
59
 
58
60
 
59
61
  def build_config_named_children(
@@ -13,7 +13,7 @@ def install_command_marshaling(
13
13
  lambda c: c,
14
14
  lambda c: c.Output,
15
15
  ]:
16
- msh.register_opj_marshaler(
16
+ msh.set_obj_marshaler(
17
17
  fn(Command),
18
18
  PolymorphicObjMarshaler.of([
19
19
  PolymorphicObjMarshaler.Impl(
@@ -86,6 +86,8 @@ class DeployAppManager(DeployPathOwner):
86
86
  os.makedirs(deploy_dir, exist_ok=True)
87
87
 
88
88
  deploying_link = os.path.join(deploy_home, 'deploys/deploying')
89
+ if os.path.exists(deploying_link):
90
+ os.unlink(deploying_link)
89
91
  relative_symlink(
90
92
  deploy_dir,
91
93
  deploying_link,
@@ -24,13 +24,12 @@ from omlish.os.paths import is_path_in_dir
24
24
  from omlish.os.paths import relative_symlink
25
25
 
26
26
  from .paths.paths import DeployPath
27
- from .specs import AllActiveDeployAppConfLink
28
- from .specs import CurrentOnlyDeployAppConfLink
29
27
  from .specs import DeployAppConfFile
30
28
  from .specs import DeployAppConfLink
31
29
  from .specs import DeployAppConfSpec
32
30
  from .tags import DEPLOY_TAG_SEPARATOR
33
31
  from .tags import DeployApp
32
+ from .tags import DeployConf
34
33
  from .tags import DeployTagMap
35
34
  from .types import DeployHome
36
35
 
@@ -63,6 +62,7 @@ class DeployConfManager:
63
62
  #
64
63
 
65
64
  class _ComputedConfLink(ta.NamedTuple):
65
+ conf: DeployConf
66
66
  is_dir: bool
67
67
  link_src: str
68
68
  link_dst: str
@@ -70,8 +70,9 @@ class DeployConfManager:
70
70
  _UNIQUE_LINK_NAME_STR = '@app--@time--@app-key'
71
71
  _UNIQUE_LINK_NAME = DeployPath.parse(_UNIQUE_LINK_NAME_STR)
72
72
 
73
+ @classmethod
73
74
  def _compute_app_conf_link_dst(
74
- self,
75
+ cls,
75
76
  link: DeployAppConfLink,
76
77
  tags: DeployTagMap,
77
78
  app_conf_dir: str,
@@ -85,6 +86,7 @@ class DeployConfManager:
85
86
  if (is_dir := link.src.endswith('/')):
86
87
  # @conf/ - links a directory in root of app conf dir to conf/@conf/@dst/
87
88
  check.arg(link.src.count('/') == 1)
89
+ conf = DeployConf(link.src.split('/')[0])
88
90
  link_dst_pfx = link.src
89
91
  link_dst_sfx = ''
90
92
 
@@ -92,6 +94,7 @@ class DeployConfManager:
92
94
  # @conf/file - links a single file in a single subdir to conf/@conf/@dst--file
93
95
  d, f = os.path.split(link.src)
94
96
  # TODO: check filename :|
97
+ conf = DeployConf(d)
95
98
  link_dst_pfx = d + '/'
96
99
  link_dst_sfx = DEPLOY_TAG_SEPARATOR + f
97
100
 
@@ -99,18 +102,20 @@ class DeployConfManager:
99
102
  # @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
100
103
  if '.' in link.src:
101
104
  l, _, r = link.src.partition('.')
105
+ conf = DeployConf(l)
102
106
  link_dst_pfx = l + '/'
103
107
  link_dst_sfx = '.' + r
104
108
  else:
109
+ conf = DeployConf(link.src)
105
110
  link_dst_pfx = link.src + '/'
106
111
  link_dst_sfx = ''
107
112
 
108
113
  #
109
114
 
110
- if isinstance(link, CurrentOnlyDeployAppConfLink):
115
+ if link.kind == 'current_only':
111
116
  link_dst_mid = str(tags[DeployApp].s)
112
- elif isinstance(link, AllActiveDeployAppConfLink):
113
- link_dst_mid = self._UNIQUE_LINK_NAME.render(tags)
117
+ elif link.kind == 'all_active':
118
+ link_dst_mid = cls._UNIQUE_LINK_NAME.render(tags)
114
119
  else:
115
120
  raise TypeError(link)
116
121
 
@@ -124,6 +129,7 @@ class DeployConfManager:
124
129
  link_dst = os.path.join(conf_link_dir, link_dst_name)
125
130
 
126
131
  return DeployConfManager._ComputedConfLink(
132
+ conf=conf,
127
133
  is_dir=is_dir,
128
134
  link_src=link_src,
129
135
  link_dst=link_dst,
@@ -170,7 +170,7 @@ class FileDeployPathPart(DeployPathPart):
170
170
  return 'file'
171
171
 
172
172
 
173
- #
173
+ ##
174
174
 
175
175
 
176
176
  @dc.dataclass(frozen=True)
@@ -197,7 +197,7 @@ class DeployPath:
197
197
  return pd
198
198
 
199
199
  @property
200
- def kind(self) -> ta.Literal['file', 'dir']:
200
+ def kind(self) -> DeployPathKind:
201
201
  return self.parts[-1].kind
202
202
 
203
203
  def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
@@ -95,7 +95,7 @@ class DeployAppConfFile:
95
95
 
96
96
 
97
97
  @dc.dataclass(frozen=True)
98
- class DeployAppConfLink(abc.ABC): # noqa
98
+ class DeployAppConfLink: # noqa
99
99
  """
100
100
  May be either:
101
101
  - @conf(.ext)* - links a single file in root of app conf dir to conf/@conf/@dst(.ext)*
@@ -105,20 +105,14 @@ class DeployAppConfLink(abc.ABC): # noqa
105
105
 
106
106
  src: str
107
107
 
108
+ kind: ta.Literal['current_only', 'all_active'] = 'current_only'
109
+
108
110
  def __post_init__(self) -> None:
109
111
  check_valid_deploy_spec_path(self.src)
110
112
  if '/' in self.src:
111
113
  check.equal(self.src.count('/'), 1)
112
114
 
113
115
 
114
- class CurrentOnlyDeployAppConfLink(DeployAppConfLink):
115
- pass
116
-
117
-
118
- class AllActiveDeployAppConfLink(DeployAppConfLink):
119
- pass
120
-
121
-
122
116
  #
123
117
 
124
118
 
@@ -4,6 +4,8 @@ import dataclasses as dc
4
4
  import typing as ta
5
5
 
6
6
  from omlish.lite.check import check
7
+ from omlish.lite.marshal import SingleFieldObjMarshaler
8
+ from omlish.lite.marshal import register_type_obj_marshaler
7
9
 
8
10
 
9
11
  ##
@@ -82,6 +84,8 @@ def _register_deploy_tag(cls):
82
84
  _DEPLOY_TAGS_BY_NAME[cls.tag_name] = cls
83
85
  _DEPLOY_TAGS_BY_KWARG[cls.tag_kwarg] = cls
84
86
 
87
+ register_type_obj_marshaler(cls, SingleFieldObjMarshaler(cls, 's'))
88
+
85
89
  return cls
86
90
 
87
91
 
@@ -6,7 +6,10 @@ TODO:
6
6
  """
7
7
  import os.path
8
8
 
9
+ from omdev.interp.resolvers import DEFAULT_INTERP_RESOLVER
10
+ from omdev.interp.types import InterpSpecifier
9
11
  from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
12
+ from omlish.lite.check import check
10
13
  from omlish.os.atomics import AtomicPathSwapping
11
14
 
12
15
  from .specs import DeployVenvSpec
@@ -28,7 +31,14 @@ class DeployVenvManager:
28
31
  git_dir: str,
29
32
  venv_dir: str,
30
33
  ) -> None:
31
- sys_exe = 'python3'
34
+ if spec.interp is not None:
35
+ i = InterpSpecifier.parse(check.not_none(spec.interp))
36
+ o = check.not_none(await DEFAULT_INTERP_RESOLVER.resolve(i))
37
+ sys_exe = o.exe
38
+ else:
39
+ sys_exe = 'python3'
40
+
41
+ #
32
42
 
33
43
  # !! NOTE: (most) venvs cannot be relocated, so an atomic swap can't be used. it's up to the path manager to
34
44
  # garbage collect orphaned dirs.
ominfra/manage/main.py CHANGED
@@ -35,6 +35,8 @@ from .targets.targets import ManageTarget
35
35
 
36
36
  @dc.dataclass(frozen=True)
37
37
  class ManageConfig:
38
+ deploy_home: ta.Optional[str] = None
39
+
38
40
  targets: ta.Optional[ta.Mapping[str, ManageTarget]] = None
39
41
 
40
42
 
@@ -69,7 +71,8 @@ class MainCli(ArgparseCli):
69
71
  argparse_arg('--deploy-home'),
70
72
 
71
73
  argparse_arg('target'),
72
- argparse_arg('command', nargs='+'),
74
+ argparse_arg('-f', '--command-file', action='append'),
75
+ argparse_arg('command', nargs='*'),
73
76
  )
74
77
  async def run(self) -> None:
75
78
  bs = MainBootstrap(
@@ -80,7 +83,7 @@ class MainCli(ArgparseCli):
80
83
  ),
81
84
 
82
85
  deploy_config=DeployConfig(
83
- deploy_home=self.args.deploy_home,
86
+ deploy_home=self.args.deploy_home or self.config().deploy_home,
84
87
  ),
85
88
 
86
89
  remote_config=RemoteConfig(
@@ -115,13 +118,19 @@ class MainCli(ArgparseCli):
115
118
  #
116
119
 
117
120
  cmds: ta.List[Command] = []
121
+
118
122
  cmd: Command
119
- for c in self.args.command:
123
+
124
+ for c in self.args.command or []:
120
125
  if not c.startswith('{'):
121
126
  c = json.dumps({c: {}})
122
127
  cmd = msh.unmarshal_obj(json.loads(c), Command)
123
128
  cmds.append(cmd)
124
129
 
130
+ for cf in self.args.command_file or []:
131
+ cmd = read_config_file(cf, Command, msh=msh)
132
+ cmds.append(cmd)
133
+
125
134
  #
126
135
 
127
136
  async with injector[ManageTargetConnector].connect(tgt) as ce:
@@ -2686,6 +2686,18 @@ class FieldsObjMarshaler(ObjMarshaler):
2686
2686
  })
2687
2687
 
2688
2688
 
2689
+ @dc.dataclass(frozen=True)
2690
+ class SingleFieldObjMarshaler(ObjMarshaler):
2691
+ ty: type
2692
+ fld: str
2693
+
2694
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
2695
+ return getattr(o, self.fld)
2696
+
2697
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
2698
+ return self.ty(**{self.fld: o})
2699
+
2700
+
2689
2701
  @dc.dataclass(frozen=True)
2690
2702
  class PolymorphicObjMarshaler(ObjMarshaler):
2691
2703
  class Impl(ta.NamedTuple):
@@ -2760,7 +2772,7 @@ _DEFAULT_OBJ_MARSHALERS: ta.Dict[ta.Any, ObjMarshaler] = {
2760
2772
  **{t: IterableObjMarshaler(t, DynamicObjMarshaler()) for t in (list, tuple, set, frozenset)},
2761
2773
  **{t: MappingObjMarshaler(t, DynamicObjMarshaler(), DynamicObjMarshaler()) for t in (dict,)},
2762
2774
 
2763
- ta.Any: DynamicObjMarshaler(),
2775
+ **{t: DynamicObjMarshaler() for t in (ta.Any, object)},
2764
2776
 
2765
2777
  **{t: DatetimeObjMarshaler(t) for t in (datetime.date, datetime.time, datetime.datetime)},
2766
2778
  decimal.Decimal: DecimalObjMarshaler(),
@@ -2785,6 +2797,16 @@ _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES: ta.Dict[ta.Any, type] = {
2785
2797
  ##
2786
2798
 
2787
2799
 
2800
+ _REGISTERED_OBJ_MARSHALERS_BY_TYPE: ta.MutableMapping[type, ObjMarshaler] = weakref.WeakKeyDictionary()
2801
+
2802
+
2803
+ def register_type_obj_marshaler(ty: type, om: ObjMarshaler) -> None:
2804
+ _REGISTERED_OBJ_MARSHALERS_BY_TYPE[ty] = om
2805
+
2806
+
2807
+ ##
2808
+
2809
+
2788
2810
  class ObjMarshalerManager:
2789
2811
  def __init__(
2790
2812
  self,
@@ -2794,6 +2816,8 @@ class ObjMarshalerManager:
2794
2816
  default_obj_marshalers: ta.Dict[ta.Any, ObjMarshaler] = _DEFAULT_OBJ_MARSHALERS, # noqa
2795
2817
  generic_mapping_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_MAPPING_TYPES, # noqa
2796
2818
  generic_iterable_types: ta.Dict[ta.Any, type] = _OBJ_MARSHALER_GENERIC_ITERABLE_TYPES, # noqa
2819
+
2820
+ registered_obj_marshalers: ta.Mapping[type, ObjMarshaler] = _REGISTERED_OBJ_MARSHALERS_BY_TYPE,
2797
2821
  ) -> None:
2798
2822
  super().__init__()
2799
2823
 
@@ -2802,6 +2826,7 @@ class ObjMarshalerManager:
2802
2826
  self._obj_marshalers = dict(default_obj_marshalers)
2803
2827
  self._generic_mapping_types = generic_mapping_types
2804
2828
  self._generic_iterable_types = generic_iterable_types
2829
+ self._registered_obj_marshalers = registered_obj_marshalers
2805
2830
 
2806
2831
  self._lock = threading.RLock()
2807
2832
  self._marshalers: ta.Dict[ta.Any, ObjMarshaler] = dict(_DEFAULT_OBJ_MARSHALERS)
@@ -2817,6 +2842,9 @@ class ObjMarshalerManager:
2817
2842
  non_strict_fields: bool = False,
2818
2843
  ) -> ObjMarshaler:
2819
2844
  if isinstance(ty, type):
2845
+ if (reg := self._registered_obj_marshalers.get(ty)) is not None:
2846
+ return reg
2847
+
2820
2848
  if abc.ABC in ty.__bases__:
2821
2849
  impls = [ity for ity in deep_subclasses(ty) if abc.ABC not in ity.__bases__] # type: ignore
2822
2850
  if all(ity.__qualname__.endswith(ty.__name__) for ity in impls):
@@ -2881,9 +2909,15 @@ class ObjMarshalerManager:
2881
2909
 
2882
2910
  #
2883
2911
 
2884
- def register_opj_marshaler(self, ty: ta.Any, m: ObjMarshaler) -> None:
2912
+ def set_obj_marshaler(
2913
+ self,
2914
+ ty: ta.Any,
2915
+ m: ObjMarshaler,
2916
+ *,
2917
+ override: bool = False,
2918
+ ) -> None:
2885
2919
  with self._lock:
2886
- if ty in self._obj_marshalers:
2920
+ if not override and ty in self._obj_marshalers:
2887
2921
  raise KeyError(ty)
2888
2922
  self._obj_marshalers[ty] = m
2889
2923
 
@@ -2974,7 +3008,7 @@ class ObjMarshalContext:
2974
3008
 
2975
3009
  OBJ_MARSHALER_MANAGER = ObjMarshalerManager()
2976
3010
 
2977
- register_opj_marshaler = OBJ_MARSHALER_MANAGER.register_opj_marshaler
3011
+ set_obj_marshaler = OBJ_MARSHALER_MANAGER.set_obj_marshaler
2978
3012
  get_obj_marshaler = OBJ_MARSHALER_MANAGER.get_obj_marshaler
2979
3013
 
2980
3014
  marshal_obj = OBJ_MARSHALER_MANAGER.marshal_obj
@@ -3259,6 +3293,7 @@ def read_config_file(
3259
3293
  cls: ta.Type[T],
3260
3294
  *,
3261
3295
  prepare: ta.Optional[ta.Callable[[ConfigMapping], ConfigMapping]] = None,
3296
+ msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
3262
3297
  ) -> T:
3263
3298
  with open(path) as cf:
3264
3299
  config_dct = parse_config_file(os.path.basename(path), cf)
@@ -3266,7 +3301,7 @@ def read_config_file(
3266
3301
  if prepare is not None:
3267
3302
  config_dct = prepare(config_dct)
3268
3303
 
3269
- return unmarshal_obj(config_dct, cls)
3304
+ return msh.unmarshal_obj(config_dct, cls)
3270
3305
 
3271
3306
 
3272
3307
  def build_config_named_children(