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 +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
|