omdev 0.0.0.dev215__py3-none-any.whl → 0.0.0.dev217__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omdev/.manifests.json CHANGED
@@ -195,7 +195,7 @@
195
195
  "module": ".scripts.exectime",
196
196
  "attr": "_CLI_MODULE",
197
197
  "file": "omdev/scripts/exectime.py",
198
- "line": 7,
198
+ "line": 66,
199
199
  "value": {
200
200
  "$.cli.types.CliModule": {
201
201
  "cmd_name": "py/exectime",
omdev/precheck/lite.py CHANGED
@@ -25,13 +25,46 @@ log = logging.getLogger(__name__)
25
25
  class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
26
26
  @dc.dataclass(frozen=True)
27
27
  class Config(Precheck.Config):
28
- pass
28
+ python: str = '.venvs/8/bin/python'
29
+ concurrency: int = 4
29
30
 
30
31
  def __init__(self, context: PrecheckContext, config: Config = Config()) -> None:
31
32
  super().__init__(context, config)
32
33
 
33
34
  #
34
35
 
36
+ @dc.dataclass(frozen=True)
37
+ class _Target:
38
+ path: str
39
+ kind: ta.Literal['script', 'module']
40
+
41
+ async def _collect_targets(self) -> list[_Target]:
42
+ lst = []
43
+
44
+ for fp in magic.find_magic_files(
45
+ magic.PY_MAGIC_STYLE,
46
+ self._context.src_roots,
47
+ keys=['@omlish-lite'],
48
+ ):
49
+ with open(fp) as f: # noqa # FIXME
50
+ src = f.read()
51
+
52
+ is_script = '# @omlish-script' in src.splitlines()
53
+
54
+ if is_script:
55
+ lst.append(self._Target(fp, 'script'))
56
+
57
+ elif fp.endswith('__init__.py'):
58
+ for g in glob.glob(os.path.join(os.path.dirname(fp), '**/*.py'), recursive=True):
59
+ lst.append(self._Target(g, 'module'))
60
+
61
+ else:
62
+ lst.append(self._Target(fp, 'module'))
63
+
64
+ return lst
65
+
66
+ #
67
+
35
68
  @staticmethod
36
69
  def _load_file_module(fp: str) -> None:
37
70
  import os.path # noqa
@@ -71,7 +104,7 @@ class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
71
104
 
72
105
  proc = await asyncio.create_subprocess_exec(
73
106
  *subprocess_maybe_shell_wrap_exec(
74
- '.venvs/8/bin/python',
107
+ self._config.python,
75
108
  '-c',
76
109
  self._load_file_module_payload(),
77
110
  fp,
@@ -85,7 +118,7 @@ class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
85
118
 
86
119
  return vs
87
120
 
88
- async def _run_one_module(self, fp: str) -> list[Precheck.Violation]:
121
+ async def _run_module(self, fp: str) -> list[Precheck.Violation]:
89
122
  vs: list[Precheck.Violation] = []
90
123
 
91
124
  mod = fp.rpartition('.')[0].replace(os.sep, '.')
@@ -94,7 +127,7 @@ class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
94
127
 
95
128
  proc = await asyncio.create_subprocess_exec(
96
129
  *subprocess_maybe_shell_wrap_exec(
97
- '.venvs/8/bin/python',
130
+ self._config.python,
98
131
  '-c',
99
132
  f'import {mod}',
100
133
  ),
@@ -107,34 +140,29 @@ class LitePython8Precheck(Precheck['LitePython8Precheck.Config']):
107
140
 
108
141
  return vs
109
142
 
110
- async def _run_module(self, fp: str) -> list[Precheck.Violation]:
111
- vs: list[Precheck.Violation] = []
143
+ #
112
144
 
113
- if fp.endswith('__init__.py'):
114
- pfps = glob.glob(os.path.join(os.path.dirname(fp), '**/*.py'), recursive=True)
115
- else:
116
- pfps = [fp]
145
+ async def _run_one(self, tgt: _Target) -> list[Precheck.Violation]:
146
+ if tgt.kind == 'script':
147
+ return await self._run_script(tgt.path)
117
148
 
118
- for pfp in pfps:
119
- vs.extend(await self._run_one_module(pfp))
149
+ elif tgt.kind == 'module':
150
+ return await self._run_module(tgt.path)
120
151
 
121
- return vs
152
+ else:
153
+ raise RuntimeError(f'Unknown target kind: {tgt.kind}')
122
154
 
123
155
  async def run(self) -> ta.AsyncGenerator[Precheck.Violation, None]:
124
- for fp in magic.find_magic_files(
125
- magic.PY_MAGIC_STYLE,
126
- self._context.src_roots,
127
- keys=['@omlish-lite'],
128
- ):
129
- with open(fp) as f: # noqa # FIXME
130
- src = f.read()
156
+ tgts = await self._collect_targets()
131
157
 
132
- is_script = '# @omlish-script' in src.splitlines()
158
+ sem = asyncio.Semaphore(self._config.concurrency)
133
159
 
134
- if is_script:
135
- for v in await self._run_script(fp):
136
- yield v
160
+ async def run(tgt):
161
+ async with sem:
162
+ return await self._run_one(tgt)
137
163
 
138
- else:
139
- for v in await self._run_module(fp):
140
- yield v
164
+ tasks = [asyncio.create_task(run(tgt)) for tgt in tgts]
165
+
166
+ for coro in asyncio.as_completed(tasks):
167
+ for v in await coro:
168
+ yield v
omdev/precheck/main.py CHANGED
@@ -60,6 +60,7 @@ def _check_cmd(args) -> None:
60
60
  vs: list[Precheck.Violation] = []
61
61
 
62
62
  for pc in pcs:
63
+ log.info('Running precheck: %s', type(pc).__name__)
63
64
  async for v in pc.run():
64
65
  vs.append(v)
65
66
  print('*** VIOLATION ***')
omdev/scripts/ci.py CHANGED
@@ -167,7 +167,7 @@ def asyncio_once(fn: CallableT) -> CallableT:
167
167
  return ta.cast(CallableT, inner)
168
168
 
169
169
 
170
- def drain_tasks(loop=None):
170
+ def drain_asyncio_tasks(loop=None):
171
171
  if loop is None:
172
172
  loop = asyncio.get_running_loop()
173
173
 
@@ -182,7 +182,7 @@ def draining_asyncio_tasks() -> ta.Iterator[None]:
182
182
  yield
183
183
  finally:
184
184
  if loop is not None:
185
- drain_tasks(loop) # noqa
185
+ drain_asyncio_tasks(loop) # noqa
186
186
 
187
187
 
188
188
  async def asyncio_wait_concurrent(
@@ -2563,7 +2563,7 @@ TODO:
2563
2563
 
2564
2564
  STANDARD_LOG_FORMAT_PARTS = [
2565
2565
  ('asctime', '%(asctime)-15s'),
2566
- ('process', 'pid=%(process)-6s'),
2566
+ ('process', 'pid=%(process)s'),
2567
2567
  ('thread', 'tid=%(thread)x'),
2568
2568
  ('levelname', '%(levelname)s'),
2569
2569
  ('name', '%(name)s'),
@@ -2676,6 +2676,12 @@ def configure_standard_logging(
2676
2676
  ##
2677
2677
 
2678
2678
 
2679
+ # Valid channel type kwarg values:
2680
+ # - A special flag negative int
2681
+ # - A positive fd int
2682
+ # - A file-like object
2683
+ # - None
2684
+
2679
2685
  SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
2680
2686
  'pipe': subprocess.PIPE,
2681
2687
  'stdout': subprocess.STDOUT,
@@ -2721,6 +2727,25 @@ def subprocess_close(
2721
2727
  ##
2722
2728
 
2723
2729
 
2730
+ class VerboseCalledProcessError(subprocess.CalledProcessError):
2731
+ @classmethod
2732
+ def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
2733
+ return cls(
2734
+ e.returncode,
2735
+ e.cmd,
2736
+ output=e.output,
2737
+ stderr=e.stderr,
2738
+ )
2739
+
2740
+ def __str__(self) -> str:
2741
+ msg = super().__str__()
2742
+ if self.output is not None:
2743
+ msg += f' Output: {self.output!r}'
2744
+ if self.stderr is not None:
2745
+ msg += f' Stderr: {self.stderr!r}'
2746
+ return msg
2747
+
2748
+
2724
2749
  class BaseSubprocesses(abc.ABC): # noqa
2725
2750
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
2726
2751
 
@@ -2754,16 +2779,31 @@ class BaseSubprocesses(abc.ABC): # noqa
2754
2779
  if extra_env:
2755
2780
  self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
2756
2781
 
2782
+ #
2783
+
2757
2784
  if extra_env:
2758
2785
  env = {**(env if env is not None else os.environ), **extra_env}
2759
2786
 
2787
+ #
2788
+
2760
2789
  if quiet and 'stderr' not in kwargs:
2761
2790
  if self._log and not self._log.isEnabledFor(logging.DEBUG):
2762
2791
  kwargs['stderr'] = subprocess.DEVNULL
2763
2792
 
2793
+ for chk in ('stdout', 'stderr'):
2794
+ try:
2795
+ chv = kwargs[chk]
2796
+ except KeyError:
2797
+ continue
2798
+ kwargs[chk] = SUBPROCESS_CHANNEL_OPTION_VALUES.get(chv, chv)
2799
+
2800
+ #
2801
+
2764
2802
  if not shell:
2765
2803
  cmd = subprocess_maybe_shell_wrap_exec(*cmd)
2766
2804
 
2805
+ #
2806
+
2767
2807
  return cmd, dict(
2768
2808
  env=env,
2769
2809
  shell=shell,
@@ -2771,35 +2811,57 @@ class BaseSubprocesses(abc.ABC): # noqa
2771
2811
  )
2772
2812
 
2773
2813
  @contextlib.contextmanager
2774
- def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
2814
+ def wrap_call(
2815
+ self,
2816
+ *cmd: ta.Any,
2817
+ raise_verbose: bool = False,
2818
+ **kwargs: ta.Any,
2819
+ ) -> ta.Iterator[None]:
2775
2820
  start_time = time.time()
2776
2821
  try:
2777
2822
  if self._log:
2778
2823
  self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
2824
+
2779
2825
  yield
2780
2826
 
2781
2827
  except Exception as exc: # noqa
2782
2828
  if self._log:
2783
2829
  self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
2830
+
2831
+ if (
2832
+ raise_verbose and
2833
+ isinstance(exc, subprocess.CalledProcessError) and
2834
+ not isinstance(exc, VerboseCalledProcessError) and
2835
+ (exc.output is not None or exc.stderr is not None)
2836
+ ):
2837
+ raise VerboseCalledProcessError.from_std(exc) from exc
2838
+
2784
2839
  raise
2785
2840
 
2786
2841
  finally:
2787
2842
  end_time = time.time()
2788
2843
  elapsed_s = end_time - start_time
2844
+
2789
2845
  if self._log:
2790
- self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
2846
+ self._log.debug('Subprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
2791
2847
 
2792
2848
  @contextlib.contextmanager
2793
2849
  def prepare_and_wrap(
2794
2850
  self,
2795
2851
  *cmd: ta.Any,
2852
+ raise_verbose: bool = False,
2796
2853
  **kwargs: ta.Any,
2797
2854
  ) -> ta.Iterator[ta.Tuple[
2798
2855
  ta.Tuple[ta.Any, ...],
2799
2856
  ta.Dict[str, ta.Any],
2800
2857
  ]]:
2801
2858
  cmd, kwargs = self.prepare_args(*cmd, **kwargs)
2802
- with self.wrap_call(*cmd, **kwargs):
2859
+
2860
+ with self.wrap_call(
2861
+ *cmd,
2862
+ raise_verbose=raise_verbose,
2863
+ **kwargs,
2864
+ ):
2803
2865
  yield cmd, kwargs
2804
2866
 
2805
2867
  #
omdev/scripts/exectime.py CHANGED
@@ -1,7 +1,66 @@
1
1
  #!/usr/bin/env python3
2
2
  # @omlish-script
3
- import sys
4
- import time
3
+
4
+
5
+ ##
6
+
7
+
8
+ def _run_one(src, pre=None):
9
+ import time
10
+
11
+ if pre:
12
+ exec(pre)
13
+
14
+ co = compile(src, '<string>', 'exec')
15
+ start = time.time_ns()
16
+ exec(co)
17
+ end = time.time_ns()
18
+
19
+ return end - start
20
+
21
+
22
+ ##
23
+
24
+
25
+ def _run(n, src, pre=None):
26
+ if n is None:
27
+ return _run_one(src, pre=pre)
28
+
29
+ #
30
+
31
+ import inspect
32
+
33
+ cmd = '\n'.join([
34
+ inspect.getsource(_run_one),
35
+ f'print(_run_one({src!r}, pre={pre!r}))',
36
+ ])
37
+
38
+ #
39
+
40
+ import subprocess
41
+ import sys
42
+
43
+ ts = []
44
+ for _ in range(n):
45
+ out = subprocess.check_output([sys.executable, '-c', cmd]).decode()
46
+ t = int(out.strip())
47
+ ts.append(t)
48
+
49
+ #
50
+
51
+ import statistics
52
+
53
+ o = {
54
+ # 'times': ts,
55
+ 'mean': statistics.mean(ts),
56
+ 'median': statistics.median(ts),
57
+ 'quantiles': statistics.quantiles(ts),
58
+ }
59
+
60
+ return o
61
+
62
+
63
+ ##
5
64
 
6
65
 
7
66
  # @omlish-manifest
@@ -11,23 +70,36 @@ _CLI_MODULE = {'$omdev.cli.types.CliModule': {
11
70
  }}
12
71
 
13
72
 
14
- def _main() -> None:
15
- if len(sys.argv) == 2:
16
- pre = None
17
- [src] = sys.argv[1:]
18
- elif len(sys.argv) == 3:
19
- [pre, src] = sys.argv[1:]
73
+ def _main():
74
+ import sys
75
+
76
+ args = sys.argv[1:]
77
+
78
+ n = None
79
+ if args:
80
+ try:
81
+ n = int(args[0])
82
+ except ValueError:
83
+ pass
84
+ else:
85
+ args.pop(0)
86
+
87
+ if len(args) > 1:
88
+ pre = args.pop(0)
20
89
  else:
90
+ pre = None
91
+
92
+ if len(args) != 1:
21
93
  raise Exception('Invalid arguments')
94
+ [src] = args
22
95
 
23
- if pre:
24
- exec(pre)
96
+ #
25
97
 
26
- co = compile(src, '<string>', 'exec')
27
- start = time.time_ns()
28
- exec(co)
29
- end = time.time_ns()
30
- print(end - start)
98
+ o = _run(n, src, pre=pre)
99
+
100
+ import json
101
+
102
+ print(json.dumps(o, indent=2))
31
103
 
32
104
 
33
105
  if __name__ == '__main__':
omdev/scripts/interp.py CHANGED
@@ -3500,7 +3500,7 @@ TODO:
3500
3500
 
3501
3501
  STANDARD_LOG_FORMAT_PARTS = [
3502
3502
  ('asctime', '%(asctime)-15s'),
3503
- ('process', 'pid=%(process)-6s'),
3503
+ ('process', 'pid=%(process)s'),
3504
3504
  ('thread', 'tid=%(thread)x'),
3505
3505
  ('levelname', '%(levelname)s'),
3506
3506
  ('name', '%(name)s'),
@@ -3613,6 +3613,12 @@ def configure_standard_logging(
3613
3613
  ##
3614
3614
 
3615
3615
 
3616
+ # Valid channel type kwarg values:
3617
+ # - A special flag negative int
3618
+ # - A positive fd int
3619
+ # - A file-like object
3620
+ # - None
3621
+
3616
3622
  SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
3617
3623
  'pipe': subprocess.PIPE,
3618
3624
  'stdout': subprocess.STDOUT,
@@ -3658,6 +3664,25 @@ def subprocess_close(
3658
3664
  ##
3659
3665
 
3660
3666
 
3667
+ class VerboseCalledProcessError(subprocess.CalledProcessError):
3668
+ @classmethod
3669
+ def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
3670
+ return cls(
3671
+ e.returncode,
3672
+ e.cmd,
3673
+ output=e.output,
3674
+ stderr=e.stderr,
3675
+ )
3676
+
3677
+ def __str__(self) -> str:
3678
+ msg = super().__str__()
3679
+ if self.output is not None:
3680
+ msg += f' Output: {self.output!r}'
3681
+ if self.stderr is not None:
3682
+ msg += f' Stderr: {self.stderr!r}'
3683
+ return msg
3684
+
3685
+
3661
3686
  class BaseSubprocesses(abc.ABC): # noqa
3662
3687
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
3663
3688
 
@@ -3691,16 +3716,31 @@ class BaseSubprocesses(abc.ABC): # noqa
3691
3716
  if extra_env:
3692
3717
  self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
3693
3718
 
3719
+ #
3720
+
3694
3721
  if extra_env:
3695
3722
  env = {**(env if env is not None else os.environ), **extra_env}
3696
3723
 
3724
+ #
3725
+
3697
3726
  if quiet and 'stderr' not in kwargs:
3698
3727
  if self._log and not self._log.isEnabledFor(logging.DEBUG):
3699
3728
  kwargs['stderr'] = subprocess.DEVNULL
3700
3729
 
3730
+ for chk in ('stdout', 'stderr'):
3731
+ try:
3732
+ chv = kwargs[chk]
3733
+ except KeyError:
3734
+ continue
3735
+ kwargs[chk] = SUBPROCESS_CHANNEL_OPTION_VALUES.get(chv, chv)
3736
+
3737
+ #
3738
+
3701
3739
  if not shell:
3702
3740
  cmd = subprocess_maybe_shell_wrap_exec(*cmd)
3703
3741
 
3742
+ #
3743
+
3704
3744
  return cmd, dict(
3705
3745
  env=env,
3706
3746
  shell=shell,
@@ -3708,35 +3748,57 @@ class BaseSubprocesses(abc.ABC): # noqa
3708
3748
  )
3709
3749
 
3710
3750
  @contextlib.contextmanager
3711
- def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
3751
+ def wrap_call(
3752
+ self,
3753
+ *cmd: ta.Any,
3754
+ raise_verbose: bool = False,
3755
+ **kwargs: ta.Any,
3756
+ ) -> ta.Iterator[None]:
3712
3757
  start_time = time.time()
3713
3758
  try:
3714
3759
  if self._log:
3715
3760
  self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
3761
+
3716
3762
  yield
3717
3763
 
3718
3764
  except Exception as exc: # noqa
3719
3765
  if self._log:
3720
3766
  self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
3767
+
3768
+ if (
3769
+ raise_verbose and
3770
+ isinstance(exc, subprocess.CalledProcessError) and
3771
+ not isinstance(exc, VerboseCalledProcessError) and
3772
+ (exc.output is not None or exc.stderr is not None)
3773
+ ):
3774
+ raise VerboseCalledProcessError.from_std(exc) from exc
3775
+
3721
3776
  raise
3722
3777
 
3723
3778
  finally:
3724
3779
  end_time = time.time()
3725
3780
  elapsed_s = end_time - start_time
3781
+
3726
3782
  if self._log:
3727
- self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
3783
+ self._log.debug('Subprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
3728
3784
 
3729
3785
  @contextlib.contextmanager
3730
3786
  def prepare_and_wrap(
3731
3787
  self,
3732
3788
  *cmd: ta.Any,
3789
+ raise_verbose: bool = False,
3733
3790
  **kwargs: ta.Any,
3734
3791
  ) -> ta.Iterator[ta.Tuple[
3735
3792
  ta.Tuple[ta.Any, ...],
3736
3793
  ta.Dict[str, ta.Any],
3737
3794
  ]]:
3738
3795
  cmd, kwargs = self.prepare_args(*cmd, **kwargs)
3739
- with self.wrap_call(*cmd, **kwargs):
3796
+
3797
+ with self.wrap_call(
3798
+ *cmd,
3799
+ raise_verbose=raise_verbose,
3800
+ **kwargs,
3801
+ ):
3740
3802
  yield cmd, kwargs
3741
3803
 
3742
3804
  #
@@ -5740,7 +5740,7 @@ TODO:
5740
5740
 
5741
5741
  STANDARD_LOG_FORMAT_PARTS = [
5742
5742
  ('asctime', '%(asctime)-15s'),
5743
- ('process', 'pid=%(process)-6s'),
5743
+ ('process', 'pid=%(process)s'),
5744
5744
  ('thread', 'tid=%(thread)x'),
5745
5745
  ('levelname', '%(levelname)s'),
5746
5746
  ('name', '%(name)s'),
@@ -5853,6 +5853,12 @@ def configure_standard_logging(
5853
5853
  ##
5854
5854
 
5855
5855
 
5856
+ # Valid channel type kwarg values:
5857
+ # - A special flag negative int
5858
+ # - A positive fd int
5859
+ # - A file-like object
5860
+ # - None
5861
+
5856
5862
  SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
5857
5863
  'pipe': subprocess.PIPE,
5858
5864
  'stdout': subprocess.STDOUT,
@@ -5898,6 +5904,25 @@ def subprocess_close(
5898
5904
  ##
5899
5905
 
5900
5906
 
5907
+ class VerboseCalledProcessError(subprocess.CalledProcessError):
5908
+ @classmethod
5909
+ def from_std(cls, e: subprocess.CalledProcessError) -> 'VerboseCalledProcessError':
5910
+ return cls(
5911
+ e.returncode,
5912
+ e.cmd,
5913
+ output=e.output,
5914
+ stderr=e.stderr,
5915
+ )
5916
+
5917
+ def __str__(self) -> str:
5918
+ msg = super().__str__()
5919
+ if self.output is not None:
5920
+ msg += f' Output: {self.output!r}'
5921
+ if self.stderr is not None:
5922
+ msg += f' Stderr: {self.stderr!r}'
5923
+ return msg
5924
+
5925
+
5901
5926
  class BaseSubprocesses(abc.ABC): # noqa
5902
5927
  DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
5903
5928
 
@@ -5931,16 +5956,31 @@ class BaseSubprocesses(abc.ABC): # noqa
5931
5956
  if extra_env:
5932
5957
  self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
5933
5958
 
5959
+ #
5960
+
5934
5961
  if extra_env:
5935
5962
  env = {**(env if env is not None else os.environ), **extra_env}
5936
5963
 
5964
+ #
5965
+
5937
5966
  if quiet and 'stderr' not in kwargs:
5938
5967
  if self._log and not self._log.isEnabledFor(logging.DEBUG):
5939
5968
  kwargs['stderr'] = subprocess.DEVNULL
5940
5969
 
5970
+ for chk in ('stdout', 'stderr'):
5971
+ try:
5972
+ chv = kwargs[chk]
5973
+ except KeyError:
5974
+ continue
5975
+ kwargs[chk] = SUBPROCESS_CHANNEL_OPTION_VALUES.get(chv, chv)
5976
+
5977
+ #
5978
+
5941
5979
  if not shell:
5942
5980
  cmd = subprocess_maybe_shell_wrap_exec(*cmd)
5943
5981
 
5982
+ #
5983
+
5944
5984
  return cmd, dict(
5945
5985
  env=env,
5946
5986
  shell=shell,
@@ -5948,35 +5988,57 @@ class BaseSubprocesses(abc.ABC): # noqa
5948
5988
  )
5949
5989
 
5950
5990
  @contextlib.contextmanager
5951
- def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
5991
+ def wrap_call(
5992
+ self,
5993
+ *cmd: ta.Any,
5994
+ raise_verbose: bool = False,
5995
+ **kwargs: ta.Any,
5996
+ ) -> ta.Iterator[None]:
5952
5997
  start_time = time.time()
5953
5998
  try:
5954
5999
  if self._log:
5955
6000
  self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
6001
+
5956
6002
  yield
5957
6003
 
5958
6004
  except Exception as exc: # noqa
5959
6005
  if self._log:
5960
6006
  self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
6007
+
6008
+ if (
6009
+ raise_verbose and
6010
+ isinstance(exc, subprocess.CalledProcessError) and
6011
+ not isinstance(exc, VerboseCalledProcessError) and
6012
+ (exc.output is not None or exc.stderr is not None)
6013
+ ):
6014
+ raise VerboseCalledProcessError.from_std(exc) from exc
6015
+
5961
6016
  raise
5962
6017
 
5963
6018
  finally:
5964
6019
  end_time = time.time()
5965
6020
  elapsed_s = end_time - start_time
6021
+
5966
6022
  if self._log:
5967
- self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
6023
+ self._log.debug('Subprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
5968
6024
 
5969
6025
  @contextlib.contextmanager
5970
6026
  def prepare_and_wrap(
5971
6027
  self,
5972
6028
  *cmd: ta.Any,
6029
+ raise_verbose: bool = False,
5973
6030
  **kwargs: ta.Any,
5974
6031
  ) -> ta.Iterator[ta.Tuple[
5975
6032
  ta.Tuple[ta.Any, ...],
5976
6033
  ta.Dict[str, ta.Any],
5977
6034
  ]]:
5978
6035
  cmd, kwargs = self.prepare_args(*cmd, **kwargs)
5979
- with self.wrap_call(*cmd, **kwargs):
6036
+
6037
+ with self.wrap_call(
6038
+ *cmd,
6039
+ raise_verbose=raise_verbose,
6040
+ **kwargs,
6041
+ ):
5980
6042
  yield cmd, kwargs
5981
6043
 
5982
6044
  #
@@ -18,6 +18,7 @@ if ta.TYPE_CHECKING:
18
18
  import yaml
19
19
 
20
20
  from omlish.formats import dotenv
21
+ from omlish.formats import json5
21
22
  from omlish.formats import props
22
23
  from omlish.formats import xml
23
24
 
@@ -29,6 +30,7 @@ else:
29
30
  yaml = lang.proxy_import('yaml')
30
31
 
31
32
  dotenv = lang.proxy_import('omlish.formats.dotenv')
33
+ json5 = lang.proxy_import('omlish.formats.json5')
32
34
  props = lang.proxy_import('omlish.formats.props')
33
35
  xml = lang.proxy_import('omlish.formats.xml')
34
36
 
@@ -45,6 +47,8 @@ class Format:
45
47
  class Formats(enum.Enum):
46
48
  JSON = Format(['json'], json.load)
47
49
 
50
+ JSON5 = Format(['json5'], lambda f: json5.loads(f.read()))
51
+
48
52
  YAML = Format(['yaml', 'yml'], lambda f: yaml.safe_load(f))
49
53
 
50
54
  TOML = Format(['toml'], lambda f: tomllib.loads(f.read()))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omdev
3
- Version: 0.0.0.dev215
3
+ Version: 0.0.0.dev217
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,7 @@ 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: omlish==0.0.0.dev215
15
+ Requires-Dist: omlish==0.0.0.dev217
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=24.10; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -1,4 +1,4 @@
1
- omdev/.manifests.json,sha256=VUg0T7DFMxatE-hsLqocMvGoNmELDiZZaudO1tcz-FE,9092
1
+ omdev/.manifests.json,sha256=02pFpcoefn9JQr0AIqt_6-BnWi49KF0baoGEKv8bjn0,9093
2
2
  omdev/__about__.py,sha256=j3vFclhFvyPICV6FK4aDApFzMCqJWxv9FaWwdwXrSgw,1215
3
3
  omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  omdev/bracepy.py,sha256=I8EdqtDvxzAi3I8TuMEW-RBfwXfqKbwp06CfOdj3L1o,2743
@@ -150,8 +150,8 @@ omdev/precheck/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  omdev/precheck/__main__.py,sha256=UEuS4z5-heIrwTtB-ONe1KeXJdqj8tYXMqWMpuO10so,165
151
151
  omdev/precheck/base.py,sha256=a_lGoFM-QhL8u8XDUYFhb-feEyfPbP4j8lcmNO51sHY,732
152
152
  omdev/precheck/git.py,sha256=APC5Ln7x0zDrQiGPRWPsBcVJK3vWhbU-brqR5M63JQA,849
153
- omdev/precheck/lite.py,sha256=eQjEBigjKj-y8xbDe0VnPPQYReGmxGERykyHi2cpEuM,3878
154
- omdev/precheck/main.py,sha256=QL2en3Vn8nnuRTnPeipK9ovc2KrXtv1CkT2YGPqxCio,2884
153
+ omdev/precheck/lite.py,sha256=kk41zbA2y1K0sMlbd6UyJP2mOW2I_iT6iaUW12F6mJY,4629
154
+ omdev/precheck/main.py,sha256=XkwQnC_4YH-0P9YYkVxKUHAAj0o6iXiCu6S-oU_WaQk,2948
155
155
  omdev/precheck/manifests.py,sha256=ulwuYeZ0vnRsj8uTUbQGifoBNwI82MAsJuffs3rVIak,760
156
156
  omdev/precheck/scripts.py,sha256=Xw9kkQzlDd_2V9av9qlaNpNZG9jZdy3TTo7x60MeR2I,1273
157
157
  omdev/ptk/__init__.py,sha256=QIu7cMeCKgNiXvIt7pXTESToJLuRMN0Qsxns_Z7ci0k,641
@@ -174,12 +174,12 @@ omdev/pyproject/resources/docker-dev.sh,sha256=DHkz5D18jok_oDolfg2mqrvGRWFoCe9GQ
174
174
  omdev/pyproject/resources/python.sh,sha256=jvrwddYw2KNttpuImLbdCdJK0HsUNMrHtTnmLvhxQxg,757
175
175
  omdev/scripts/__init__.py,sha256=MKCvUAEQwsIvwLixwtPlpBqmkMXLCnjjXyAXvVpDwVk,91
176
176
  omdev/scripts/bumpversion.py,sha256=Kn7fo73Hs8uJh3Hi3EIyLOlzLPWAC6dwuD_lZ3cIzuY,1064
177
- omdev/scripts/ci.py,sha256=yDx6VsztaY1VqYWF1i9Ywt2YTIseSc5wYtdvtmh5qyI,108870
177
+ omdev/scripts/ci.py,sha256=2bMl2vCMHdKQf2AmbcWV9Syv6RdizibYRDlT5Vg6q3E,110397
178
178
  omdev/scripts/execrss.py,sha256=mR0G0wERBYtQmVIn63lCIIFb5zkCM6X_XOENDFYDBKc,651
179
- omdev/scripts/exectime.py,sha256=sFb376GflU6s9gNX-2-we8hgH6w5MuQNS9g6i4SqJIo,610
179
+ omdev/scripts/exectime.py,sha256=S2O4MgtzTsFOY2IUJxsrnOIame9tEFc6aOlKP-F1JSg,1541
180
180
  omdev/scripts/importtrace.py,sha256=oa7CtcWJVMNDbyIEiRHej6ICfABfErMeo4_haIqe18Q,14041
181
- omdev/scripts/interp.py,sha256=7NrLbOkiDjBldnzpf-EpL8UTmU6U3lakvHqhSlY3X_U,141851
182
- omdev/scripts/pyproject.py,sha256=HDors8tvpSgCUzESwQ2E-Gx5jLisaYoOApOTCgmQ5P0,245595
181
+ omdev/scripts/interp.py,sha256=40KDm-mW8v7Mmhi4WByV9XzWPgwvTfKYsFKlojXR4k0,143362
182
+ omdev/scripts/pyproject.py,sha256=TG3Ne6h1W2rwT8nBG9E7QCmN_adPIO24QYaSJ7JGreU,247106
183
183
  omdev/scripts/slowcat.py,sha256=lssv4yrgJHiWfOiHkUut2p8E8Tq32zB-ujXESQxFFHY,2728
184
184
  omdev/scripts/tmpexec.py,sha256=WTYcf56Tj2qjYV14AWmV8SfT0u6Y8eIU6cKgQRvEK3c,1442
185
185
  omdev/tokens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -203,7 +203,7 @@ omdev/tools/sqlrepl.py,sha256=wAjrfXNrRV63-NJCC2HlGQnFh7lUH0bHMnOjYotQqFs,5753
203
203
  omdev/tools/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
204
204
  omdev/tools/json/__main__.py,sha256=wqpkN_NsQyNwKW4qjVj8ADJ4_C98KhrFBtE-Z1UamfU,168
205
205
  omdev/tools/json/cli.py,sha256=EubIMT-n2XsjWBZjSy2fWXqijlwrIhLsfbkg3SZzi28,9586
206
- omdev/tools/json/formats.py,sha256=S8O0-A2_b_kXUevBme4KlSG0V5Nl-WXp8emRub1zMmA,1753
206
+ omdev/tools/json/formats.py,sha256=RgtPdcs294o9n9czjafHppg1iSzD-olsIc3v8ApM9Os,1908
207
207
  omdev/tools/json/io.py,sha256=sfj2hJS9Hy3aUR8a_lLzOrYcmL9fSKyvOHiofdUASsI,1427
208
208
  omdev/tools/json/parsing.py,sha256=YOeTRY6Gd89EfcHvqXO5PRWJ3IgRCpNnI54Lb_N3v2k,2183
209
209
  omdev/tools/json/processing.py,sha256=iFm5VqaxJ97WHaun2ed7NEjMxhFeJqf28bLNfoDJft0,1209
@@ -211,9 +211,9 @@ omdev/tools/json/rendering.py,sha256=tMcjOW5edfozcMSTxxvF7WVTsbYLoe9bCKFh50qyaGw
211
211
  omdev/tools/pawk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
212
212
  omdev/tools/pawk/__main__.py,sha256=VCqeRVnqT1RPEoIrqHFSu4PXVMg4YEgF4qCQm90-eRI,66
213
213
  omdev/tools/pawk/pawk.py,sha256=zsEkfQX0jF5bn712uqPAyBSdJt2dno1LH2oeSMNfXQI,11424
214
- omdev-0.0.0.dev215.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
215
- omdev-0.0.0.dev215.dist-info/METADATA,sha256=oNR4sVjlhZ4v-B5bp8h4XPFlDnI8aniDWCUq9WLk-ZM,1638
216
- omdev-0.0.0.dev215.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
217
- omdev-0.0.0.dev215.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
218
- omdev-0.0.0.dev215.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
219
- omdev-0.0.0.dev215.dist-info/RECORD,,
214
+ omdev-0.0.0.dev217.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
215
+ omdev-0.0.0.dev217.dist-info/METADATA,sha256=lNWmIe7pfG4nlRGuyYkGYlDR5GbC5JBbwrLMiHFIfNk,1638
216
+ omdev-0.0.0.dev217.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
217
+ omdev-0.0.0.dev217.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
218
+ omdev-0.0.0.dev217.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
219
+ omdev-0.0.0.dev217.dist-info/RECORD,,