omdev 0.0.0.dev215__py3-none-any.whl → 0.0.0.dev217__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 +1 -1
- omdev/precheck/lite.py +55 -27
- omdev/precheck/main.py +1 -0
- omdev/scripts/ci.py +68 -6
- omdev/scripts/exectime.py +87 -15
- omdev/scripts/interp.py +66 -4
- omdev/scripts/pyproject.py +66 -4
- omdev/tools/json/formats.py +4 -0
- {omdev-0.0.0.dev215.dist-info → omdev-0.0.0.dev217.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev215.dist-info → omdev-0.0.0.dev217.dist-info}/RECORD +14 -14
- {omdev-0.0.0.dev215.dist-info → omdev-0.0.0.dev217.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev215.dist-info → omdev-0.0.0.dev217.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev215.dist-info → omdev-0.0.0.dev217.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev215.dist-info → omdev-0.0.0.dev217.dist-info}/top_level.txt +0 -0
omdev/.manifests.json
CHANGED
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
111
|
-
vs: list[Precheck.Violation] = []
|
143
|
+
#
|
112
144
|
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
119
|
-
|
149
|
+
elif tgt.kind == 'module':
|
150
|
+
return await self._run_module(tgt.path)
|
120
151
|
|
121
|
-
|
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
|
-
|
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
|
-
|
158
|
+
sem = asyncio.Semaphore(self._config.concurrency)
|
133
159
|
|
134
|
-
|
135
|
-
|
136
|
-
|
160
|
+
async def run(tgt):
|
161
|
+
async with sem:
|
162
|
+
return await self._run_one(tgt)
|
137
163
|
|
138
|
-
|
139
|
-
|
140
|
-
|
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
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
|
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
|
-
|
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)
|
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(
|
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('
|
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
|
-
|
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
|
-
|
4
|
-
|
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()
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
24
|
-
exec(pre)
|
96
|
+
#
|
25
97
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
print(
|
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)
|
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(
|
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('
|
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
|
-
|
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
|
#
|
omdev/scripts/pyproject.py
CHANGED
@@ -5740,7 +5740,7 @@ TODO:
|
|
5740
5740
|
|
5741
5741
|
STANDARD_LOG_FORMAT_PARTS = [
|
5742
5742
|
('asctime', '%(asctime)-15s'),
|
5743
|
-
('process', 'pid=%(process)
|
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(
|
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('
|
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
|
-
|
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
|
#
|
omdev/tools/json/formats.py
CHANGED
@@ -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.
|
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.
|
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=
|
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=
|
154
|
-
omdev/precheck/main.py,sha256=
|
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=
|
177
|
+
omdev/scripts/ci.py,sha256=2bMl2vCMHdKQf2AmbcWV9Syv6RdizibYRDlT5Vg6q3E,110397
|
178
178
|
omdev/scripts/execrss.py,sha256=mR0G0wERBYtQmVIn63lCIIFb5zkCM6X_XOENDFYDBKc,651
|
179
|
-
omdev/scripts/exectime.py,sha256=
|
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=
|
182
|
-
omdev/scripts/pyproject.py,sha256=
|
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=
|
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.
|
215
|
-
omdev-0.0.0.
|
216
|
-
omdev-0.0.0.
|
217
|
-
omdev-0.0.0.
|
218
|
-
omdev-0.0.0.
|
219
|
-
omdev-0.0.0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|