omdev 0.0.0.dev404__py3-none-any.whl → 0.0.0.dev405__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.
omdev/.manifests.json CHANGED
@@ -236,7 +236,7 @@
236
236
  "module": ".py.scripts.execstat",
237
237
  "attr": "_CLI_MODULE",
238
238
  "file": "omdev/py/scripts/execstat.py",
239
- "line": 86,
239
+ "line": 89,
240
240
  "value": {
241
241
  "$.cli.types.CliModule": {
242
242
  "cmd_name": "py/execstat",
@@ -404,7 +404,7 @@
404
404
  "module": ".tools.git.messages",
405
405
  "attr": "_TIMESTAMP_GIT_MESSAGE_GENERATOR_MANIFEST",
406
406
  "file": "omdev/tools/git/messages.py",
407
- "line": 85,
407
+ "line": 81,
408
408
  "value": {
409
409
  "$.tools.git.messages.GitMessageGeneratorManifest": {
410
410
  "mod_name": "omdev.tools.git.messages",
omdev/cli/main.py CHANGED
@@ -6,7 +6,6 @@ TODO:
6
6
  """
7
7
  import argparse
8
8
  import dataclasses as dc
9
- import os
10
9
  import runpy
11
10
  import sys
12
11
  import typing as ta
@@ -184,26 +183,24 @@ def _build_arg_parser() -> argparse.ArgumentParser:
184
183
 
185
184
 
186
185
  def _build_cmd_set(args: ta.Any) -> CliCmdSet:
187
- ldr = ManifestLoader(
188
- **ManifestLoader.kwargs_from_entry_point(
189
- globals(),
190
- **GlobalManifestLoader.default_kwargs(),
191
- ),
192
- )
186
+ ldr_cfg = GlobalManifestLoader.default_config()
193
187
 
194
- #
188
+ ldr_cfg |= ManifestLoader.config_from_entry_point(globals())
189
+
190
+ if args.cli_pkg_root:
191
+ ldr_cfg |= ManifestLoader.Config(
192
+ package_scan_root_dirs=args.cli_pkg_root,
193
+ discover_packages=False,
194
+ )
195
195
 
196
- pkgs = ldr.scan_or_discover_packages(
197
- specified_root_dirs=args.cli_pkg_root,
198
- fallback_root_dir=os.getcwd(),
199
- )
196
+ GlobalManifestLoader.initialize(ldr_cfg)
200
197
 
201
198
  #
202
199
 
203
200
  lst: list[CliCmd] = []
204
201
 
205
- for m in ldr.load(*pkgs, only=[CliModule]):
206
- lst.append(check.isinstance(m.value(), CliModule))
202
+ for mv in GlobalManifestLoader.load_values_of(CliModule):
203
+ lst.append(check.isinstance(mv, CliModule))
207
204
 
208
205
  lst.extend(_CLI_FUNCS)
209
206
 
@@ -23,6 +23,6 @@ class ManifestsPrecheck(Precheck['ManifestsPrecheck.Config']):
23
23
  async def run(self) -> ta.AsyncGenerator[Precheck.Violation]:
24
24
  for src_root in sorted(self._context.src_roots):
25
25
  try:
26
- GlobalManifestLoader.load(src_root)
26
+ GlobalManifestLoader.load(packages=[src_root])
27
27
  except Exception as e: # noqa
28
28
  yield Precheck.Violation(self, f'Error loading manifest for {src_root}: {e!r}')
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env python3
2
+ # ruff: noqa: UP045
3
+ # @omlish-lite
2
4
  # @omlish-script
3
5
  import argparse
4
6
  import inspect
@@ -8,6 +10,7 @@ import statistics
8
10
  import subprocess
9
11
  import sys
10
12
  import tempfile
13
+ import typing as ta
11
14
 
12
15
 
13
16
  ##
@@ -16,7 +19,7 @@ import tempfile
16
19
  def _run(
17
20
  src: str,
18
21
  *,
19
- setup: str | None = None,
22
+ setup: 'ta.Optional[str]' = None,
20
23
  time: bool = False,
21
24
  rss: bool = False,
22
25
  modules: bool = False,
@@ -33,8 +36,8 @@ def _run(
33
36
  if modules:
34
37
  import sys # noqa
35
38
 
36
- def get_modules() -> set[str]:
37
- return set(sys.modules)
39
+ def get_modules() -> 'ta.Sequence[str]':
40
+ return list(sys.modules)
38
41
 
39
42
  #
40
43
 
@@ -51,7 +54,7 @@ def _run(
51
54
  start_rss = get_rss() # noqa
52
55
 
53
56
  if modules:
54
- start_modules = get_modules() # noqa
57
+ start_modules = set(get_modules()) # noqa
55
58
 
56
59
  if time:
57
60
  start_time = get_time() # noqa
@@ -76,7 +79,7 @@ def _run(
76
79
  return {
77
80
  **({'time': (end_time - start_time)} if time else {}), # noqa
78
81
  **({'rss': (end_rss - start_rss)} if rss else {}), # noqa
79
- **({'modules': sorted(end_modules - start_modules)} if modules else {}), # noqa
82
+ **({'modules': [m for m in end_modules if m not in start_modules]} if modules else {}), # noqa
80
83
  }
81
84
 
82
85
 
@@ -99,6 +102,7 @@ def _main() -> None:
99
102
  parser.add_argument('-t', '--time', action='store_true')
100
103
  parser.add_argument('-r', '--rss', action='store_true')
101
104
  parser.add_argument('-m', '--modules', action='store_true')
105
+ parser.add_argument('-M', '--modules-ordered', action='store_true')
102
106
 
103
107
  parser.add_argument('-n', '--num-runs', type=int, default=1)
104
108
 
@@ -136,7 +140,7 @@ def _main() -> None:
136
140
  if i == 0:
137
141
  run_kw.update(
138
142
  rss=bool(args.rss),
139
- modules=bool(args.modules),
143
+ modules=bool(args.modules) or bool(args.modules),
140
144
  )
141
145
 
142
146
  payload = '\n'.join([
@@ -183,7 +187,11 @@ def _main() -> None:
183
187
 
184
188
  if args.modules:
185
189
  out.update({
186
- 'modules': results[0]['modules'],
190
+ 'modules': sorted(results[0]['modules']),
191
+ })
192
+ if args.modules_ordered:
193
+ out.update({
194
+ 'modules_ordered': results[0]['modules'],
187
195
  })
188
196
 
189
197
  print(json.dumps(out, indent=2))
omdev/scripts/ci.py CHANGED
@@ -99,6 +99,10 @@ CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
99
99
  CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
100
100
  CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
101
101
 
102
+ # ../../omlish/lite/contextmanagers.py
103
+ ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
104
+ AsyncExitStackedT = ta.TypeVar('AsyncExitStackedT', bound='AsyncExitStacked')
105
+
102
106
  # ../../omlish/lite/maybes.py
103
107
  U = ta.TypeVar('U')
104
108
 
@@ -121,10 +125,6 @@ AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
121
125
  # ../../omlish/http/parsing.py
122
126
  HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
123
127
 
124
- # ../../omlish/lite/contextmanagers.py
125
- ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
126
- AsyncExitStackedT = ta.TypeVar('AsyncExitStackedT', bound='AsyncExitStacked')
127
-
128
128
  # ../../omlish/lite/inject.py
129
129
  InjectorKeyCls = ta.Union[type, ta.NewType]
130
130
  InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
@@ -1026,6 +1026,201 @@ class Checks:
1026
1026
  check = Checks()
1027
1027
 
1028
1028
 
1029
+ ########################################
1030
+ # ../../../omlish/lite/contextmanagers.py
1031
+
1032
+
1033
+ ##
1034
+
1035
+
1036
+ class ExitStacked:
1037
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
1038
+ super().__init_subclass__(**kwargs)
1039
+
1040
+ for a in ('__enter__', '__exit__'):
1041
+ for b in cls.__bases__:
1042
+ if b is ExitStacked:
1043
+ continue
1044
+ try:
1045
+ fn = getattr(b, a)
1046
+ except AttributeError:
1047
+ pass
1048
+ else:
1049
+ if fn is not getattr(ExitStacked, a):
1050
+ raise TypeError(f'ExitStacked subclass {cls} must not not override {a} via {b}')
1051
+
1052
+ _exit_stack: ta.Optional[contextlib.ExitStack] = None
1053
+
1054
+ @contextlib.contextmanager
1055
+ def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
1056
+ """
1057
+ Overridable wrapper around __enter__ which deliberately does not have access to an _exit_stack yet. Intended for
1058
+ things like wrapping __enter__ in a lock.
1059
+ """
1060
+
1061
+ yield
1062
+
1063
+ @ta.final
1064
+ def __enter__(self: ExitStackedT) -> ExitStackedT:
1065
+ """
1066
+ Final because any contexts entered during this init must be exited if any exception is thrown, and user
1067
+ overriding would likely interfere with that. Override `_enter_contexts` for such init.
1068
+ """
1069
+
1070
+ with self._exit_stacked_init_wrapper():
1071
+ if self._exit_stack is not None:
1072
+ raise RuntimeError
1073
+ es = self._exit_stack = contextlib.ExitStack()
1074
+ es.__enter__()
1075
+ try:
1076
+ self._enter_contexts()
1077
+ except Exception: # noqa
1078
+ es.__exit__(*sys.exc_info())
1079
+ raise
1080
+ return self
1081
+
1082
+ @ta.final
1083
+ def __exit__(self, exc_type, exc_val, exc_tb):
1084
+ if (es := self._exit_stack) is None:
1085
+ return None
1086
+ try:
1087
+ self._exit_contexts()
1088
+ except Exception: # noqa
1089
+ es.__exit__(*sys.exc_info())
1090
+ raise
1091
+ return es.__exit__(exc_type, exc_val, exc_tb)
1092
+
1093
+ def _enter_contexts(self) -> None:
1094
+ pass
1095
+
1096
+ def _exit_contexts(self) -> None:
1097
+ pass
1098
+
1099
+ def _enter_context(self, cm: ta.ContextManager[T]) -> T:
1100
+ if (es := self._exit_stack) is None:
1101
+ raise RuntimeError
1102
+ return es.enter_context(cm)
1103
+
1104
+
1105
+ class AsyncExitStacked:
1106
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
1107
+ super().__init_subclass__(**kwargs)
1108
+
1109
+ for a in ('__aenter__', '__aexit__'):
1110
+ for b in cls.__bases__:
1111
+ if b is AsyncExitStacked:
1112
+ continue
1113
+ try:
1114
+ fn = getattr(b, a)
1115
+ except AttributeError:
1116
+ pass
1117
+ else:
1118
+ if fn is not getattr(AsyncExitStacked, a):
1119
+ raise TypeError(f'AsyncExitStacked subclass {cls} must not not override {a} via {b}')
1120
+
1121
+ _exit_stack: ta.Optional[contextlib.AsyncExitStack] = None
1122
+
1123
+ @contextlib.asynccontextmanager
1124
+ async def _async_exit_stacked_init_wrapper(self) -> ta.AsyncGenerator[None, None]:
1125
+ yield
1126
+
1127
+ @ta.final
1128
+ async def __aenter__(self: AsyncExitStackedT) -> AsyncExitStackedT:
1129
+ async with self._async_exit_stacked_init_wrapper():
1130
+ if self._exit_stack is not None:
1131
+ raise RuntimeError
1132
+ es = self._exit_stack = contextlib.AsyncExitStack()
1133
+ await es.__aenter__()
1134
+ try:
1135
+ await self._async_enter_contexts()
1136
+ except Exception: # noqa
1137
+ await es.__aexit__(*sys.exc_info())
1138
+ raise
1139
+ return self
1140
+
1141
+ @ta.final
1142
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
1143
+ if (es := self._exit_stack) is None:
1144
+ return None
1145
+ try:
1146
+ await self._async_exit_contexts()
1147
+ except Exception: # noqa
1148
+ await es.__aexit__(*sys.exc_info())
1149
+ raise
1150
+ return await es.__aexit__(exc_type, exc_val, exc_tb)
1151
+
1152
+ async def _async_enter_contexts(self) -> None:
1153
+ pass
1154
+
1155
+ async def _async_exit_contexts(self) -> None:
1156
+ pass
1157
+
1158
+ def _enter_context(self, cm: ta.ContextManager[T]) -> T:
1159
+ if (es := self._exit_stack) is None:
1160
+ raise RuntimeError
1161
+ return es.enter_context(cm)
1162
+
1163
+ async def _enter_async_context(self, cm: ta.AsyncContextManager[T]) -> T:
1164
+ if (es := self._exit_stack) is None:
1165
+ raise RuntimeError
1166
+ return await es.enter_async_context(cm)
1167
+
1168
+
1169
+ ##
1170
+
1171
+
1172
+ @contextlib.contextmanager
1173
+ def defer(fn: ta.Callable, *args: ta.Any, **kwargs: ta.Any) -> ta.Generator[ta.Callable, None, None]:
1174
+ if args or kwargs:
1175
+ fn = functools.partial(fn, *args, **kwargs)
1176
+ try:
1177
+ yield fn
1178
+ finally:
1179
+ fn()
1180
+
1181
+
1182
+ @contextlib.asynccontextmanager
1183
+ async def adefer(fn: ta.Awaitable) -> ta.AsyncGenerator[ta.Awaitable, None]:
1184
+ try:
1185
+ yield fn
1186
+ finally:
1187
+ await fn
1188
+
1189
+
1190
+ ##
1191
+
1192
+
1193
+ @contextlib.contextmanager
1194
+ def attr_setting(obj, attr, val, *, default=None): # noqa
1195
+ not_set = object()
1196
+ orig = getattr(obj, attr, not_set)
1197
+ try:
1198
+ setattr(obj, attr, val)
1199
+ if orig is not not_set:
1200
+ yield orig
1201
+ else:
1202
+ yield default
1203
+ finally:
1204
+ if orig is not_set:
1205
+ delattr(obj, attr)
1206
+ else:
1207
+ setattr(obj, attr, orig)
1208
+
1209
+
1210
+ ##
1211
+
1212
+
1213
+ class aclosing(contextlib.AbstractAsyncContextManager): # noqa
1214
+ def __init__(self, thing):
1215
+ self.thing = thing
1216
+
1217
+ async def __aenter__(self):
1218
+ return self.thing
1219
+
1220
+ async def __aexit__(self, *exc_info):
1221
+ await self.thing.aclose()
1222
+
1223
+
1029
1224
  ########################################
1030
1225
  # ../../../omlish/lite/dataclasses.py
1031
1226
 
@@ -2721,53 +2916,251 @@ class OciDataRefInfo:
2721
2916
 
2722
2917
 
2723
2918
  ########################################
2724
- # ../../../omlish/argparse/cli.py
2725
- """
2726
- FIXME:
2727
- - exit_on_error lol
2728
-
2729
- TODO:
2730
- - default command
2731
- - auto match all underscores to hyphens
2732
- - pre-run, post-run hooks
2733
- - exitstack?
2734
- - suggestion - difflib.get_close_matches
2735
- """
2919
+ # ../../oci/pack/unpacking.py
2736
2920
 
2737
2921
 
2738
2922
  ##
2739
2923
 
2740
2924
 
2741
- @dc.dataclass(eq=False)
2742
- class ArgparseArg:
2743
- args: ta.Sequence[ta.Any]
2744
- kwargs: ta.Mapping[str, ta.Any]
2745
- dest: ta.Optional[str] = None
2746
-
2747
- def __get__(self, instance, owner=None):
2748
- if instance is None:
2749
- return self
2750
- return getattr(instance.args, self.dest) # type: ignore
2751
-
2925
+ class OciLayerUnpacker(ExitStacked):
2926
+ def __init__(
2927
+ self,
2928
+ input_files: ta.Sequence[ta.Union[str, tarfile.TarFile]],
2929
+ output_file_path: str,
2930
+ ) -> None:
2931
+ super().__init__()
2752
2932
 
2753
- def argparse_arg(*args, **kwargs) -> ArgparseArg:
2754
- return ArgparseArg(args, kwargs)
2933
+ self._input_files = list(input_files)
2934
+ self._output_file_path = output_file_path
2755
2935
 
2936
+ #
2756
2937
 
2757
- def argparse_arg_(*args, **kwargs) -> ta.Any:
2758
- return argparse_arg(*args, **kwargs)
2938
+ @contextlib.contextmanager
2939
+ def _open_input_file(self, input_file: ta.Union[str, tarfile.TarFile]) -> ta.Iterator[tarfile.TarFile]:
2940
+ if isinstance(input_file, tarfile.TarFile):
2941
+ yield input_file
2759
2942
 
2943
+ elif isinstance(input_file, str):
2944
+ with tarfile.open(input_file) as tar_file:
2945
+ yield tar_file
2760
2946
 
2761
- #
2947
+ else:
2948
+ raise TypeError(input_file)
2762
2949
 
2950
+ #
2763
2951
 
2764
- @dc.dataclass(eq=False)
2765
- class ArgparseCmd:
2766
- name: str
2767
- fn: ArgparseCmdFn
2768
- args: ta.Sequence[ArgparseArg] = () # noqa
2952
+ class _Entry(ta.NamedTuple):
2953
+ file: ta.Union[str, tarfile.TarFile]
2954
+ info: tarfile.TarInfo
2769
2955
 
2770
- # _: dc.KW_ONLY
2956
+ def _build_input_file_sorted_entries(self, input_file: ta.Union[str, tarfile.TarFile]) -> ta.Sequence[_Entry]:
2957
+ dct: ta.Dict[str, OciLayerUnpacker._Entry] = {}
2958
+
2959
+ with self._open_input_file(input_file) as input_tar_file:
2960
+ for info in input_tar_file.getmembers():
2961
+ check.not_in(info.name, dct)
2962
+ dct[info.name] = self._Entry(
2963
+ file=input_file,
2964
+ info=info,
2965
+ )
2966
+
2967
+ return sorted(dct.values(), key=lambda entry: entry.info.name)
2968
+
2969
+ @cached_nullary
2970
+ def _entries_by_name(self) -> ta.Mapping[str, _Entry]:
2971
+ root: dict = {}
2972
+
2973
+ def find_dir(dir_name: str) -> dict: # noqa
2974
+ if dir_name:
2975
+ dir_parts = dir_name.split('/')
2976
+ else:
2977
+ dir_parts = []
2978
+
2979
+ cur = root # noqa
2980
+ for dir_part in dir_parts:
2981
+ cur = cur[dir_part] # noqa
2982
+
2983
+ return check.isinstance(cur, dict)
2984
+
2985
+ #
2986
+
2987
+ for input_file in self._input_files:
2988
+ sorted_entries = self._build_input_file_sorted_entries(input_file)
2989
+
2990
+ wh_names = set()
2991
+ wh_opaques = set()
2992
+
2993
+ #
2994
+
2995
+ for entry in sorted_entries:
2996
+ info = entry.info
2997
+ name = check.non_empty_str(info.name)
2998
+ base_name = os.path.basename(name)
2999
+ dir_name = os.path.dirname(name)
3000
+
3001
+ if base_name == '.wh..wh..opq':
3002
+ wh_opaques.add(dir_name)
3003
+ continue
3004
+
3005
+ if base_name.startswith('.wh.'):
3006
+ wh_base_name = os.path.basename(base_name[4:])
3007
+ wh_name = os.path.join(dir_name, wh_base_name)
3008
+ wh_names.add(wh_name)
3009
+ continue
3010
+
3011
+ cur = find_dir(dir_name)
3012
+
3013
+ if info.type == tarfile.DIRTYPE:
3014
+ try:
3015
+ ex = cur[base_name]
3016
+ except KeyError:
3017
+ cur[base_name] = {'': entry}
3018
+ else:
3019
+ ex[''] = entry
3020
+
3021
+ else:
3022
+ cur[base_name] = entry
3023
+
3024
+ #
3025
+
3026
+ for wh_name in reversed(sorted(wh_names)): # noqa
3027
+ wh_dir_name = os.path.dirname(wh_name)
3028
+ wh_base_name = os.path.basename(wh_name)
3029
+
3030
+ cur = find_dir(wh_dir_name)
3031
+ rm = cur[wh_base_name]
3032
+
3033
+ if isinstance(rm, dict):
3034
+ # Whiteouts wipe out whole directory:
3035
+ # https://github.com/containerd/containerd/blob/59c8cf6ea5f4175ad512914dd5ce554942bf144f/pkg/archive/tar_test.go#L648
3036
+ # check.equal(set(rm), '')
3037
+ del cur[wh_base_name]
3038
+
3039
+ elif isinstance(rm, self._Entry):
3040
+ del cur[wh_base_name]
3041
+
3042
+ else:
3043
+ raise TypeError(rm)
3044
+
3045
+ if wh_opaques:
3046
+ raise NotImplementedError
3047
+
3048
+ #
3049
+
3050
+ out: ta.Dict[str, OciLayerUnpacker._Entry] = {}
3051
+
3052
+ def rec(cur): # noqa
3053
+ for _, child in sorted(cur.items(), key=lambda t: t[0]):
3054
+ if isinstance(child, dict):
3055
+ rec(child)
3056
+
3057
+ elif isinstance(child, self._Entry):
3058
+ check.not_in(child.info.name, out)
3059
+ out[child.info.name] = child
3060
+
3061
+ else:
3062
+ raise TypeError(child)
3063
+
3064
+ rec(root)
3065
+
3066
+ return out
3067
+
3068
+ #
3069
+
3070
+ @cached_nullary
3071
+ def _output_tar_file(self) -> tarfile.TarFile:
3072
+ return self._enter_context(tarfile.open(self._output_file_path, 'w'))
3073
+
3074
+ #
3075
+
3076
+ def _add_unpacked_entry(
3077
+ self,
3078
+ input_tar_file: tarfile.TarFile,
3079
+ info: tarfile.TarInfo,
3080
+ ) -> None:
3081
+ base_name = os.path.basename(info.name)
3082
+ check.state(not base_name.startswith('.wh.'))
3083
+
3084
+ if info.type in tarfile.REGULAR_TYPES:
3085
+ with check.not_none(input_tar_file.extractfile(info)) as f:
3086
+ self._output_tar_file().addfile(info, f)
3087
+
3088
+ else:
3089
+ self._output_tar_file().addfile(info)
3090
+
3091
+ def _unpack_file(
3092
+ self,
3093
+ input_file: ta.Union[str, tarfile.TarFile],
3094
+ ) -> None:
3095
+ entries_by_name = self._entries_by_name()
3096
+
3097
+ with self._open_input_file(input_file) as input_tar_file:
3098
+ info: tarfile.TarInfo
3099
+ for info in input_tar_file.getmembers():
3100
+ try:
3101
+ entry = entries_by_name[info.name]
3102
+ except KeyError:
3103
+ continue
3104
+
3105
+ if entry.file != input_file:
3106
+ continue
3107
+
3108
+ self._add_unpacked_entry(input_tar_file, info)
3109
+
3110
+ @cached_nullary
3111
+ def write(self) -> None:
3112
+ for input_file in self._input_files:
3113
+ self._unpack_file(input_file)
3114
+
3115
+
3116
+ ########################################
3117
+ # ../../../omlish/argparse/cli.py
3118
+ """
3119
+ FIXME:
3120
+ - exit_on_error lol
3121
+
3122
+ TODO:
3123
+ - default command
3124
+ - auto match all underscores to hyphens
3125
+ - pre-run, post-run hooks
3126
+ - exitstack?
3127
+ - suggestion - difflib.get_close_matches
3128
+ """
3129
+
3130
+
3131
+ ##
3132
+
3133
+
3134
+ @dc.dataclass(eq=False)
3135
+ class ArgparseArg:
3136
+ args: ta.Sequence[ta.Any]
3137
+ kwargs: ta.Mapping[str, ta.Any]
3138
+ dest: ta.Optional[str] = None
3139
+
3140
+ def __get__(self, instance, owner=None):
3141
+ if instance is None:
3142
+ return self
3143
+ return getattr(instance.args, self.dest) # type: ignore
3144
+
3145
+
3146
+ def argparse_arg(*args, **kwargs) -> ArgparseArg:
3147
+ return ArgparseArg(args, kwargs)
3148
+
3149
+
3150
+ def argparse_arg_(*args, **kwargs) -> ta.Any:
3151
+ return argparse_arg(*args, **kwargs)
3152
+
3153
+
3154
+ #
3155
+
3156
+
3157
+ @dc.dataclass(eq=False)
3158
+ class ArgparseCmd:
3159
+ name: str
3160
+ fn: ArgparseCmdFn
3161
+ args: ta.Sequence[ArgparseArg] = () # noqa
3162
+
3163
+ # _: dc.KW_ONLY
2771
3164
 
2772
3165
  aliases: ta.Optional[ta.Sequence[str]] = None
2773
3166
  parent: ta.Optional['ArgparseCmd'] = None
@@ -3447,239 +3840,49 @@ class HttpRequestParser:
3447
3840
  return ParseHttpRequestError(
3448
3841
  code=http.HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
3449
3842
  message=('Line too long', str(err)),
3450
- **result_kwargs(),
3451
- )
3452
-
3453
- except http.client.HTTPException as err:
3454
- return ParseHttpRequestError(
3455
- code=http.HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
3456
- message=('Too many headers', str(err)),
3457
- **result_kwargs(),
3458
- )
3459
-
3460
- # Check for connection directive
3461
-
3462
- conn_type = headers.get('Connection', '')
3463
- if conn_type.lower() == 'close':
3464
- close_connection = True
3465
- elif (
3466
- conn_type.lower() == 'keep-alive' and
3467
- version >= HttpProtocolVersions.HTTP_1_1
3468
- ):
3469
- close_connection = False
3470
-
3471
- # Check for expect directive
3472
-
3473
- expect = headers.get('Expect', '')
3474
- if (
3475
- expect.lower() == '100-continue' and
3476
- version >= HttpProtocolVersions.HTTP_1_1
3477
- ):
3478
- expects_continue = True
3479
- else:
3480
- expects_continue = False
3481
-
3482
- # Return
3483
-
3484
- return ParsedHttpRequest(
3485
- method=method,
3486
- path=path,
3487
- expects_continue=expects_continue,
3488
- **result_kwargs(),
3489
- )
3490
-
3491
- def parse(self, read_line: ta.Callable[[int], bytes]) -> ParseHttpRequestResult:
3492
- return self._run_read_line_coro(self.coro_parse(), read_line)
3493
-
3494
-
3495
- ########################################
3496
- # ../../../omlish/lite/contextmanagers.py
3497
-
3498
-
3499
- ##
3500
-
3501
-
3502
- class ExitStacked:
3503
- def __init_subclass__(cls, **kwargs: ta.Any) -> None:
3504
- super().__init_subclass__(**kwargs)
3505
-
3506
- for a in ('__enter__', '__exit__'):
3507
- for b in cls.__bases__:
3508
- if b is ExitStacked:
3509
- continue
3510
- try:
3511
- fn = getattr(b, a)
3512
- except AttributeError:
3513
- pass
3514
- else:
3515
- if fn is not getattr(ExitStacked, a):
3516
- raise TypeError(f'ExitStacked subclass {cls} must not not override {a} via {b}')
3517
-
3518
- _exit_stack: ta.Optional[contextlib.ExitStack] = None
3519
-
3520
- @contextlib.contextmanager
3521
- def _exit_stacked_init_wrapper(self) -> ta.Iterator[None]:
3522
- """
3523
- Overridable wrapper around __enter__ which deliberately does not have access to an _exit_stack yet. Intended for
3524
- things like wrapping __enter__ in a lock.
3525
- """
3526
-
3527
- yield
3528
-
3529
- @ta.final
3530
- def __enter__(self: ExitStackedT) -> ExitStackedT:
3531
- """
3532
- Final because any contexts entered during this init must be exited if any exception is thrown, and user
3533
- overriding would likely interfere with that. Override `_enter_contexts` for such init.
3534
- """
3535
-
3536
- with self._exit_stacked_init_wrapper():
3537
- check.state(self._exit_stack is None)
3538
- es = self._exit_stack = contextlib.ExitStack()
3539
- es.__enter__()
3540
- try:
3541
- self._enter_contexts()
3542
- except Exception: # noqa
3543
- es.__exit__(*sys.exc_info())
3544
- raise
3545
- return self
3546
-
3547
- @ta.final
3548
- def __exit__(self, exc_type, exc_val, exc_tb):
3549
- if (es := self._exit_stack) is None:
3550
- return None
3551
- try:
3552
- self._exit_contexts()
3553
- except Exception: # noqa
3554
- es.__exit__(*sys.exc_info())
3555
- raise
3556
- return es.__exit__(exc_type, exc_val, exc_tb)
3557
-
3558
- def _enter_contexts(self) -> None:
3559
- pass
3560
-
3561
- def _exit_contexts(self) -> None:
3562
- pass
3563
-
3564
- def _enter_context(self, cm: ta.ContextManager[T]) -> T:
3565
- es = check.not_none(self._exit_stack)
3566
- return es.enter_context(cm)
3567
-
3568
-
3569
- class AsyncExitStacked:
3570
- def __init_subclass__(cls, **kwargs: ta.Any) -> None:
3571
- super().__init_subclass__(**kwargs)
3572
-
3573
- for a in ('__aenter__', '__aexit__'):
3574
- for b in cls.__bases__:
3575
- if b is AsyncExitStacked:
3576
- continue
3577
- try:
3578
- fn = getattr(b, a)
3579
- except AttributeError:
3580
- pass
3581
- else:
3582
- if fn is not getattr(AsyncExitStacked, a):
3583
- raise TypeError(f'AsyncExitStacked subclass {cls} must not not override {a} via {b}')
3584
-
3585
- _exit_stack: ta.Optional[contextlib.AsyncExitStack] = None
3586
-
3587
- @contextlib.asynccontextmanager
3588
- async def _async_exit_stacked_init_wrapper(self) -> ta.AsyncGenerator[None, None]:
3589
- yield
3590
-
3591
- @ta.final
3592
- async def __aenter__(self: AsyncExitStackedT) -> AsyncExitStackedT:
3593
- async with self._async_exit_stacked_init_wrapper():
3594
- check.state(self._exit_stack is None)
3595
- es = self._exit_stack = contextlib.AsyncExitStack()
3596
- await es.__aenter__()
3597
- try:
3598
- await self._async_enter_contexts()
3599
- except Exception: # noqa
3600
- await es.__aexit__(*sys.exc_info())
3601
- raise
3602
- return self
3603
-
3604
- @ta.final
3605
- async def __aexit__(self, exc_type, exc_val, exc_tb):
3606
- if (es := self._exit_stack) is None:
3607
- return None
3608
- try:
3609
- await self._async_exit_contexts()
3610
- except Exception: # noqa
3611
- await es.__aexit__(*sys.exc_info())
3612
- raise
3613
- return await es.__aexit__(exc_type, exc_val, exc_tb)
3614
-
3615
- async def _async_enter_contexts(self) -> None:
3616
- pass
3617
-
3618
- async def _async_exit_contexts(self) -> None:
3619
- pass
3620
-
3621
- def _enter_context(self, cm: ta.ContextManager[T]) -> T:
3622
- es = check.not_none(self._exit_stack)
3623
- return es.enter_context(cm)
3624
-
3625
- async def _enter_async_context(self, cm: ta.AsyncContextManager[T]) -> T:
3626
- es = check.not_none(self._exit_stack)
3627
- return await es.enter_async_context(cm)
3628
-
3629
-
3630
- ##
3631
-
3632
-
3633
- @contextlib.contextmanager
3634
- def defer(fn: ta.Callable, *args: ta.Any, **kwargs: ta.Any) -> ta.Generator[ta.Callable, None, None]:
3635
- if args or kwargs:
3636
- fn = functools.partial(fn, *args, **kwargs)
3637
- try:
3638
- yield fn
3639
- finally:
3640
- fn()
3641
-
3642
-
3643
- @contextlib.asynccontextmanager
3644
- async def adefer(fn: ta.Awaitable) -> ta.AsyncGenerator[ta.Awaitable, None]:
3645
- try:
3646
- yield fn
3647
- finally:
3648
- await fn
3649
-
3650
-
3651
- ##
3843
+ **result_kwargs(),
3844
+ )
3652
3845
 
3846
+ except http.client.HTTPException as err:
3847
+ return ParseHttpRequestError(
3848
+ code=http.HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE,
3849
+ message=('Too many headers', str(err)),
3850
+ **result_kwargs(),
3851
+ )
3653
3852
 
3654
- @contextlib.contextmanager
3655
- def attr_setting(obj, attr, val, *, default=None): # noqa
3656
- not_set = object()
3657
- orig = getattr(obj, attr, not_set)
3658
- try:
3659
- setattr(obj, attr, val)
3660
- if orig is not not_set:
3661
- yield orig
3662
- else:
3663
- yield default
3664
- finally:
3665
- if orig is not_set:
3666
- delattr(obj, attr)
3667
- else:
3668
- setattr(obj, attr, orig)
3853
+ # Check for connection directive
3669
3854
 
3855
+ conn_type = headers.get('Connection', '')
3856
+ if conn_type.lower() == 'close':
3857
+ close_connection = True
3858
+ elif (
3859
+ conn_type.lower() == 'keep-alive' and
3860
+ version >= HttpProtocolVersions.HTTP_1_1
3861
+ ):
3862
+ close_connection = False
3670
3863
 
3671
- ##
3864
+ # Check for expect directive
3672
3865
 
3866
+ expect = headers.get('Expect', '')
3867
+ if (
3868
+ expect.lower() == '100-continue' and
3869
+ version >= HttpProtocolVersions.HTTP_1_1
3870
+ ):
3871
+ expects_continue = True
3872
+ else:
3873
+ expects_continue = False
3673
3874
 
3674
- class aclosing(contextlib.AbstractAsyncContextManager): # noqa
3675
- def __init__(self, thing):
3676
- self.thing = thing
3875
+ # Return
3677
3876
 
3678
- async def __aenter__(self):
3679
- return self.thing
3877
+ return ParsedHttpRequest(
3878
+ method=method,
3879
+ path=path,
3880
+ expects_continue=expects_continue,
3881
+ **result_kwargs(),
3882
+ )
3680
3883
 
3681
- async def __aexit__(self, *exc_info):
3682
- await self.thing.aclose()
3884
+ def parse(self, read_line: ta.Callable[[int], bytes]) -> ParseHttpRequestResult:
3885
+ return self._run_read_line_coro(self.coro_parse(), read_line)
3683
3886
 
3684
3887
 
3685
3888
  ########################################
@@ -7374,204 +7577,6 @@ def get_single_oci_image_manifest(image_index: OciImageIndex) -> OciImageManifes
7374
7577
  return check.isinstance(child_index, OciImageManifest)
7375
7578
 
7376
7579
 
7377
- ########################################
7378
- # ../../oci/pack/unpacking.py
7379
-
7380
-
7381
- ##
7382
-
7383
-
7384
- class OciLayerUnpacker(ExitStacked):
7385
- def __init__(
7386
- self,
7387
- input_files: ta.Sequence[ta.Union[str, tarfile.TarFile]],
7388
- output_file_path: str,
7389
- ) -> None:
7390
- super().__init__()
7391
-
7392
- self._input_files = list(input_files)
7393
- self._output_file_path = output_file_path
7394
-
7395
- #
7396
-
7397
- @contextlib.contextmanager
7398
- def _open_input_file(self, input_file: ta.Union[str, tarfile.TarFile]) -> ta.Iterator[tarfile.TarFile]:
7399
- if isinstance(input_file, tarfile.TarFile):
7400
- yield input_file
7401
-
7402
- elif isinstance(input_file, str):
7403
- with tarfile.open(input_file) as tar_file:
7404
- yield tar_file
7405
-
7406
- else:
7407
- raise TypeError(input_file)
7408
-
7409
- #
7410
-
7411
- class _Entry(ta.NamedTuple):
7412
- file: ta.Union[str, tarfile.TarFile]
7413
- info: tarfile.TarInfo
7414
-
7415
- def _build_input_file_sorted_entries(self, input_file: ta.Union[str, tarfile.TarFile]) -> ta.Sequence[_Entry]:
7416
- dct: ta.Dict[str, OciLayerUnpacker._Entry] = {}
7417
-
7418
- with self._open_input_file(input_file) as input_tar_file:
7419
- for info in input_tar_file.getmembers():
7420
- check.not_in(info.name, dct)
7421
- dct[info.name] = self._Entry(
7422
- file=input_file,
7423
- info=info,
7424
- )
7425
-
7426
- return sorted(dct.values(), key=lambda entry: entry.info.name)
7427
-
7428
- @cached_nullary
7429
- def _entries_by_name(self) -> ta.Mapping[str, _Entry]:
7430
- root: dict = {}
7431
-
7432
- def find_dir(dir_name: str) -> dict: # noqa
7433
- if dir_name:
7434
- dir_parts = dir_name.split('/')
7435
- else:
7436
- dir_parts = []
7437
-
7438
- cur = root # noqa
7439
- for dir_part in dir_parts:
7440
- cur = cur[dir_part] # noqa
7441
-
7442
- return check.isinstance(cur, dict)
7443
-
7444
- #
7445
-
7446
- for input_file in self._input_files:
7447
- sorted_entries = self._build_input_file_sorted_entries(input_file)
7448
-
7449
- wh_names = set()
7450
- wh_opaques = set()
7451
-
7452
- #
7453
-
7454
- for entry in sorted_entries:
7455
- info = entry.info
7456
- name = check.non_empty_str(info.name)
7457
- base_name = os.path.basename(name)
7458
- dir_name = os.path.dirname(name)
7459
-
7460
- if base_name == '.wh..wh..opq':
7461
- wh_opaques.add(dir_name)
7462
- continue
7463
-
7464
- if base_name.startswith('.wh.'):
7465
- wh_base_name = os.path.basename(base_name[4:])
7466
- wh_name = os.path.join(dir_name, wh_base_name)
7467
- wh_names.add(wh_name)
7468
- continue
7469
-
7470
- cur = find_dir(dir_name)
7471
-
7472
- if info.type == tarfile.DIRTYPE:
7473
- try:
7474
- ex = cur[base_name]
7475
- except KeyError:
7476
- cur[base_name] = {'': entry}
7477
- else:
7478
- ex[''] = entry
7479
-
7480
- else:
7481
- cur[base_name] = entry
7482
-
7483
- #
7484
-
7485
- for wh_name in reversed(sorted(wh_names)): # noqa
7486
- wh_dir_name = os.path.dirname(wh_name)
7487
- wh_base_name = os.path.basename(wh_name)
7488
-
7489
- cur = find_dir(wh_dir_name)
7490
- rm = cur[wh_base_name]
7491
-
7492
- if isinstance(rm, dict):
7493
- # Whiteouts wipe out whole directory:
7494
- # https://github.com/containerd/containerd/blob/59c8cf6ea5f4175ad512914dd5ce554942bf144f/pkg/archive/tar_test.go#L648
7495
- # check.equal(set(rm), '')
7496
- del cur[wh_base_name]
7497
-
7498
- elif isinstance(rm, self._Entry):
7499
- del cur[wh_base_name]
7500
-
7501
- else:
7502
- raise TypeError(rm)
7503
-
7504
- if wh_opaques:
7505
- raise NotImplementedError
7506
-
7507
- #
7508
-
7509
- out: ta.Dict[str, OciLayerUnpacker._Entry] = {}
7510
-
7511
- def rec(cur): # noqa
7512
- for _, child in sorted(cur.items(), key=lambda t: t[0]):
7513
- if isinstance(child, dict):
7514
- rec(child)
7515
-
7516
- elif isinstance(child, self._Entry):
7517
- check.not_in(child.info.name, out)
7518
- out[child.info.name] = child
7519
-
7520
- else:
7521
- raise TypeError(child)
7522
-
7523
- rec(root)
7524
-
7525
- return out
7526
-
7527
- #
7528
-
7529
- @cached_nullary
7530
- def _output_tar_file(self) -> tarfile.TarFile:
7531
- return self._enter_context(tarfile.open(self._output_file_path, 'w'))
7532
-
7533
- #
7534
-
7535
- def _add_unpacked_entry(
7536
- self,
7537
- input_tar_file: tarfile.TarFile,
7538
- info: tarfile.TarInfo,
7539
- ) -> None:
7540
- base_name = os.path.basename(info.name)
7541
- check.state(not base_name.startswith('.wh.'))
7542
-
7543
- if info.type in tarfile.REGULAR_TYPES:
7544
- with check.not_none(input_tar_file.extractfile(info)) as f:
7545
- self._output_tar_file().addfile(info, f)
7546
-
7547
- else:
7548
- self._output_tar_file().addfile(info)
7549
-
7550
- def _unpack_file(
7551
- self,
7552
- input_file: ta.Union[str, tarfile.TarFile],
7553
- ) -> None:
7554
- entries_by_name = self._entries_by_name()
7555
-
7556
- with self._open_input_file(input_file) as input_tar_file:
7557
- info: tarfile.TarInfo
7558
- for info in input_tar_file.getmembers():
7559
- try:
7560
- entry = entries_by_name[info.name]
7561
- except KeyError:
7562
- continue
7563
-
7564
- if entry.file != input_file:
7565
- continue
7566
-
7567
- self._add_unpacked_entry(input_tar_file, info)
7568
-
7569
- @cached_nullary
7570
- def write(self) -> None:
7571
- for input_file in self._input_files:
7572
- self._unpack_file(input_file)
7573
-
7574
-
7575
7580
  ########################################
7576
7581
  # ../../oci/repositories.py
7577
7582
 
@@ -1,5 +1,4 @@
1
1
  import abc
2
- import os
3
2
  import typing as ta
4
3
 
5
4
  from omlish import cached
@@ -54,10 +53,7 @@ class StaticGitMessageGeneratorManifest(StaticModAttrManifest, GitMessageGenerat
54
53
 
55
54
  @cached.function
56
55
  def load_message_generator_manifests() -> ta.Sequence[GitMessageGeneratorManifest]:
57
- ldr = manifest_globals.GlobalManifestLoader.instance()
58
- pkgs = ldr.scan_or_discover_packages(fallback_root_dir=os.getcwd()) # FIXME
59
- mfs = ldr.load(*pkgs, only=[GitMessageGeneratorManifest])
60
- return [mf.value() for mf in mfs]
56
+ return manifest_globals.GlobalManifestLoader.load_values_of(GitMessageGeneratorManifest)
61
57
 
62
58
 
63
59
  @cached.function
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omdev
3
- Version: 0.0.0.dev404
3
+ Version: 0.0.0.dev405
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev404
17
+ Requires-Dist: omlish==0.0.0.dev405
18
18
  Provides-Extra: all
19
19
  Requires-Dist: black~=25.1; extra == "all"
20
20
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=A1ezsjeznJ_G720gDSJY8m9ZfSpaUJaVe24OmtaNS2s,12453
1
+ omdev/.manifests.json,sha256=1muOc0K_eLWBw9EE5ei6tFo4OkLFp43sSNnz9AF5m_s,12453
2
2
  omdev/__about__.py,sha256=fQNmzSa1MntcPSrzg_Vpo6JRU2RbXik2NqRz0oQCApE,1202
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/cmake.py,sha256=9rfSvFHPmKDj9ngvfDB2vK8O-xO_ZwUm7hMKLWA-yOw,4578
@@ -113,7 +113,7 @@ omdev/cli/__main__.py,sha256=mOJpgc07o0r5luQ1DlX4tk2PqZkgmbwPbdzJ3KmtjgQ,138
113
113
  omdev/cli/_pathhack.py,sha256=UshIZX3oeXq0De-9X28gy2LgKMZDf_dzabdkUhZJdNA,2124
114
114
  omdev/cli/clicli.py,sha256=TZnQYHyyh97B6N_pVYYduYgt03s8Yp5mrJ7wblXWSWY,6308
115
115
  omdev/cli/install.py,sha256=oB34AOwu07sqEztW_z5mgorAFoP_Tw556XiTPj2WSM0,4904
116
- omdev/cli/main.py,sha256=eUvc64Wi6ROcSG7V_yzrmhpY2CgR12cyz9eJGXE0xlI,6885
116
+ omdev/cli/main.py,sha256=nuRpsHB7W-Z0SU_wgEIHPFaMY2sOxiNkY9rnEek99Rs,6905
117
117
  omdev/cli/managers.py,sha256=BV98_n30Jj63OJrFgRoVZRfICxMLXEZKoEn4rMj9LV4,1160
118
118
  omdev/cli/types.py,sha256=zRSq-SVr43zhXs8dTgkpD9kot9iOFJB8KzfEUnNE7_0,500
119
119
  omdev/clipboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -218,7 +218,7 @@ omdev/precheck/git.py,sha256=O8rNQZ_vlHec0pOFbK6LOkbly5ZIUYT_HXRMqQX8GaI,774
218
218
  omdev/precheck/imports.py,sha256=3loQxHMrpI0ce4-le77NCSxutLac_5vDW4UDX7KWWg8,2565
219
219
  omdev/precheck/lite.py,sha256=qd6nXWEVut8aBSRD_NxnxXGRNa9ue8mu8ND8rGLisE4,4710
220
220
  omdev/precheck/main.py,sha256=_1A5wiu9p2th1dn_17w1ZIFtMmCIOaTFpWyvK0jopEA,4374
221
- omdev/precheck/manifests.py,sha256=5Hlo_3BF5HbcLPxn93fhDS9yy9k6iL2vFsqFNfoU9q4,799
221
+ omdev/precheck/manifests.py,sha256=dxl7GSJHKjQrR6mbwvj6j92XDGHOpxxEEQ6smJkBEe4,810
222
222
  omdev/precheck/scripts.py,sha256=6nb_lDgyX7u9kdF_BU6ubY01q_jGk96VH9q9gpOieng,1753
223
223
  omdev/precheck/unicode.py,sha256=VUNDCrlfUas_U8ugV_q0eFUXuBgKjS8YdCFm0FXREXo,2583
224
224
  omdev/ptk/__init__.py,sha256=4zhIfvhebFj4TJRRN1SQkcAe-7elizcfZLsD-fIlZsI,5198
@@ -252,7 +252,7 @@ omdev/py/docstrings/parser.py,sha256=umJEgQBkSXLwWIKZrBbFCfNrz847vpTNDqTTEwlvHpA
252
252
  omdev/py/docstrings/rest.py,sha256=c2xPYg_W01W9eYY_KLKX69E4qu4jpkgUshi5K5EyVv8,5221
253
253
  omdev/py/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
254
  omdev/py/scripts/bumpversion.py,sha256=UDMtTd_wfkqsmrtHqTKDilWVzUBigzZAOCVND7t9jyw,1124
255
- omdev/py/scripts/execstat.py,sha256=WweOgynPVGBSxGMpTZD0C9SXgiyypdtxCWDbVJo8ATo,4266
255
+ omdev/py/scripts/execstat.py,sha256=a7KTmBW3IYgvcZR3u09_5LQcte6IgJaqzUN-LnaktIA,4577
256
256
  omdev/py/scripts/importtrace.py,sha256=Plh2ixN8NxTekrNoac6l9wg9B4x1647sEOvQTSy_Hbk,14256
257
257
  omdev/py/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
258
258
  omdev/py/tools/importscan.py,sha256=4dCH0coX0OqNwesteKaTE8GxuSfLhgXYQlzNUXLiSNY,4640
@@ -270,7 +270,7 @@ omdev/pyproject/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
270
270
  omdev/pyproject/resources/docker-dev.sh,sha256=DHkz5D18jok_oDolfg2mqrvGRWFoCe9GQo04dR1czcc,838
271
271
  omdev/pyproject/resources/python.sh,sha256=rFaN4SiJ9hdLDXXsDTwugI6zsw6EPkgYMmtacZeTbvw,749
272
272
  omdev/scripts/__init__.py,sha256=MKCvUAEQwsIvwLixwtPlpBqmkMXLCnjjXyAXvVpDwVk,91
273
- omdev/scripts/ci.py,sha256=9amwcmO6M05Ek8x8hZp8SQa2Wmn7p1PTRAkkcFzeLmI,362663
273
+ omdev/scripts/ci.py,sha256=fqphFyV5xdBNC5QtxmGmCWL__na7c7E2ygWloRJBNZo,362813
274
274
  omdev/scripts/interp.py,sha256=8FR5-a95K1bL7YHNBiX1XVDPYhqw_jTWz8mVNDts6pI,158710
275
275
  omdev/scripts/pyproject.py,sha256=qgAW607OVKaeGma7XuxzyhuFyJLoKIcT9nZ52cl3KKU,270298
276
276
  omdev/scripts/slowcat.py,sha256=lssv4yrgJHiWfOiHkUut2p8E8Tq32zB-ujXESQxFFHY,2728
@@ -303,7 +303,7 @@ omdev/tools/git/__main__.py,sha256=gI87SBUgTkKUcUM-RtZWnei-UUDDqzbr5aPztb-gvbE,1
303
303
  omdev/tools/git/cli.py,sha256=I4AiCTz4OCzK6s8J2TJF1eYw9FH2JIK_CsdjQH_9UTE,16757
304
304
  omdev/tools/git/cloning.py,sha256=CNGBBMoWaTBJW4SZTf1VvhDFSm0yg7qDfNwZun_PFbU,891
305
305
  omdev/tools/git/consts.py,sha256=JuXivUNDkNhM4pe97icjRVAKM8cNRbrODquHINNKqOE,40
306
- omdev/tools/git/messages.py,sha256=vIbSGI5rJLVTOizDiYW4eOZPacWCAvsNSr9cjiGTPyI,2468
306
+ omdev/tools/git/messages.py,sha256=a3E85r3uGbT0Puo6coBwmm7sj58k8_drYyxuxeH068U,2311
307
307
  omdev/tools/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
308
308
  omdev/tools/json/__main__.py,sha256=wqpkN_NsQyNwKW4qjVj8ADJ4_C98KhrFBtE-Z1UamfU,168
309
309
  omdev/tools/json/cli.py,sha256=8aXX3ijU3lvPZamkIyUOz-vlBmyIdaX7dCQq5rN7adE,10193
@@ -321,9 +321,9 @@ omdev/tools/jsonview/resources/jsonview.js,sha256=faDvXDOXKvEvjOuIlz4D3F2ReQXb_b
321
321
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
322
322
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
323
323
  omdev/tools/pawk/pawk.py,sha256=ao5mdrpiSU4AZ8mBozoEaV3UVlmVTnRG9wD9XP70MZE,11429
324
- omdev-0.0.0.dev404.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
325
- omdev-0.0.0.dev404.dist-info/METADATA,sha256=AkucABx9r8c8wUWCpeP_dGd76XvVavPxQc5syurqhQc,5094
326
- omdev-0.0.0.dev404.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
327
- omdev-0.0.0.dev404.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
328
- omdev-0.0.0.dev404.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
329
- omdev-0.0.0.dev404.dist-info/RECORD,,
324
+ omdev-0.0.0.dev405.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
325
+ omdev-0.0.0.dev405.dist-info/METADATA,sha256=xCQhveQmUAKSQYqRtIyyyWfMmkpNxwaQkqhGVEy33L4,5094
326
+ omdev-0.0.0.dev405.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
327
+ omdev-0.0.0.dev405.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
328
+ omdev-0.0.0.dev405.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
329
+ omdev-0.0.0.dev405.dist-info/RECORD,,