omdev 0.0.0.dev7__py3-none-any.whl → 0.0.0.dev9__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.
Potentially problematic release.
This version of omdev might be problematic. Click here for more details.
- omdev/__about__.py +1 -1
- omdev/pyproject/cli.py +49 -0
- omdev/pyproject/configs.py +3 -2
- omdev/pyproject/pkg.py +3 -0
- omdev/scripts/interp.py +73 -4
- omdev/scripts/pyproject.py +422 -7
- {omdev-0.0.0.dev7.dist-info → omdev-0.0.0.dev9.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev7.dist-info → omdev-0.0.0.dev9.dist-info}/RECORD +11 -11
- {omdev-0.0.0.dev7.dist-info → omdev-0.0.0.dev9.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev7.dist-info → omdev-0.0.0.dev9.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev7.dist-info → omdev-0.0.0.dev9.dist-info}/top_level.txt +0 -0
omdev/__about__.py
CHANGED
omdev/pyproject/cli.py
CHANGED
|
@@ -21,7 +21,9 @@ lookit:
|
|
|
21
21
|
- https://github.com/tox-dev/tox/
|
|
22
22
|
"""
|
|
23
23
|
import argparse
|
|
24
|
+
import concurrent.futures as cf
|
|
24
25
|
import dataclasses as dc
|
|
26
|
+
import functools
|
|
25
27
|
import glob
|
|
26
28
|
import itertools
|
|
27
29
|
import os.path
|
|
@@ -44,6 +46,7 @@ from ..toml.parser import toml_loads
|
|
|
44
46
|
from .configs import PyprojectConfig
|
|
45
47
|
from .configs import PyprojectConfigPreparer
|
|
46
48
|
from .configs import VenvConfig
|
|
49
|
+
from .pkg import PyprojectPackageGenerator
|
|
47
50
|
|
|
48
51
|
|
|
49
52
|
##
|
|
@@ -287,6 +290,46 @@ def _venv_cmd(args) -> None:
|
|
|
287
290
|
##
|
|
288
291
|
|
|
289
292
|
|
|
293
|
+
def _pkg_cmd(args) -> None:
|
|
294
|
+
run = Run()
|
|
295
|
+
|
|
296
|
+
cmd = args.cmd
|
|
297
|
+
if cmd == 'gen':
|
|
298
|
+
build_root = os.path.join('.pkg')
|
|
299
|
+
|
|
300
|
+
if os.path.exists(build_root):
|
|
301
|
+
shutil.rmtree(build_root)
|
|
302
|
+
|
|
303
|
+
build_output_dir = 'dist'
|
|
304
|
+
run_build = bool(args.build)
|
|
305
|
+
|
|
306
|
+
num_threads = 8
|
|
307
|
+
|
|
308
|
+
if run_build:
|
|
309
|
+
os.makedirs(build_output_dir, exist_ok=True)
|
|
310
|
+
|
|
311
|
+
with cf.ThreadPoolExecutor(num_threads) as ex:
|
|
312
|
+
futs = [
|
|
313
|
+
ex.submit(functools.partial(
|
|
314
|
+
PyprojectPackageGenerator(
|
|
315
|
+
dir_name,
|
|
316
|
+
build_root,
|
|
317
|
+
).gen,
|
|
318
|
+
run_build=run_build,
|
|
319
|
+
build_output_dir=build_output_dir,
|
|
320
|
+
))
|
|
321
|
+
for dir_name in run.cfg().pkgs
|
|
322
|
+
]
|
|
323
|
+
for fut in futs:
|
|
324
|
+
fut.result()
|
|
325
|
+
|
|
326
|
+
else:
|
|
327
|
+
raise Exception(f'unknown subcommand: {cmd}')
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
##
|
|
331
|
+
|
|
332
|
+
|
|
290
333
|
def _build_parser() -> argparse.ArgumentParser:
|
|
291
334
|
parser = argparse.ArgumentParser()
|
|
292
335
|
parser.add_argument('--_docker_container', help=argparse.SUPPRESS)
|
|
@@ -300,6 +343,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
300
343
|
parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
|
|
301
344
|
parser_resolve.set_defaults(func=_venv_cmd)
|
|
302
345
|
|
|
346
|
+
parser_resolve = subparsers.add_parser('pkg')
|
|
347
|
+
parser_resolve.add_argument('-b', '--build', action='store_true')
|
|
348
|
+
parser_resolve.add_argument('cmd', nargs='?')
|
|
349
|
+
parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
|
|
350
|
+
parser_resolve.set_defaults(func=_pkg_cmd)
|
|
351
|
+
|
|
303
352
|
return parser
|
|
304
353
|
|
|
305
354
|
|
omdev/pyproject/configs.py
CHANGED
|
@@ -16,8 +16,9 @@ class VenvConfig:
|
|
|
16
16
|
|
|
17
17
|
@dc.dataclass(frozen=True)
|
|
18
18
|
class PyprojectConfig:
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
pkgs: ta.Sequence[str] = dc.field(default_factory=list)
|
|
20
|
+
srcs: ta.Mapping[str, ta.Sequence[str]] = dc.field(default_factory=dict)
|
|
21
|
+
venvs: ta.Mapping[str, VenvConfig] = dc.field(default_factory=dict)
|
|
21
22
|
|
|
22
23
|
venvs_dir: str = '.venvs'
|
|
23
24
|
versions_file: ta.Optional[str] = '.versions'
|
omdev/pyproject/pkg.py
CHANGED
|
@@ -27,6 +27,7 @@ import types
|
|
|
27
27
|
import typing as ta
|
|
28
28
|
|
|
29
29
|
from omlish.lite.cached import cached_nullary
|
|
30
|
+
from omlish.lite.logs import log
|
|
30
31
|
|
|
31
32
|
from ..toml.writer import TomlWriter
|
|
32
33
|
|
|
@@ -184,6 +185,8 @@ class PyprojectPackageGenerator:
|
|
|
184
185
|
run_build: bool = False,
|
|
185
186
|
build_output_dir: ta.Optional[str] = None,
|
|
186
187
|
) -> str:
|
|
188
|
+
log.info('Generating pyproject package: %s -> %s', self._dir_name, self._build_root)
|
|
189
|
+
|
|
187
190
|
self._build_dir()
|
|
188
191
|
self._write_git_ignore()
|
|
189
192
|
self._symlink_source_dir()
|
omdev/scripts/interp.py
CHANGED
|
@@ -12,6 +12,7 @@ import abc
|
|
|
12
12
|
import argparse
|
|
13
13
|
import collections
|
|
14
14
|
import dataclasses as dc
|
|
15
|
+
import datetime
|
|
15
16
|
import functools
|
|
16
17
|
import inspect
|
|
17
18
|
import itertools
|
|
@@ -24,6 +25,7 @@ import shlex
|
|
|
24
25
|
import shutil
|
|
25
26
|
import subprocess
|
|
26
27
|
import sys
|
|
28
|
+
import threading
|
|
27
29
|
import typing as ta
|
|
28
30
|
|
|
29
31
|
|
|
@@ -1134,14 +1136,28 @@ class SpecifierSet(BaseSpecifier):
|
|
|
1134
1136
|
# ../../../omlish/lite/logs.py
|
|
1135
1137
|
"""
|
|
1136
1138
|
TODO:
|
|
1139
|
+
- translate json keys
|
|
1137
1140
|
- debug
|
|
1138
1141
|
"""
|
|
1139
|
-
# ruff: noqa: UP007
|
|
1142
|
+
# ruff: noqa: UP006 UP007 N802
|
|
1140
1143
|
|
|
1141
1144
|
|
|
1142
1145
|
log = logging.getLogger(__name__)
|
|
1143
1146
|
|
|
1144
1147
|
|
|
1148
|
+
##
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
class TidLogFilter(logging.Filter):
|
|
1152
|
+
|
|
1153
|
+
def filter(self, record):
|
|
1154
|
+
record.tid = threading.get_native_id()
|
|
1155
|
+
return True
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
##
|
|
1159
|
+
|
|
1160
|
+
|
|
1145
1161
|
class JsonLogFormatter(logging.Formatter):
|
|
1146
1162
|
|
|
1147
1163
|
KEYS: ta.Mapping[str, bool] = {
|
|
@@ -1177,9 +1193,62 @@ class JsonLogFormatter(logging.Formatter):
|
|
|
1177
1193
|
return json_dumps_compact(dct)
|
|
1178
1194
|
|
|
1179
1195
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1196
|
+
##
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
STANDARD_LOG_FORMAT_PARTS = [
|
|
1200
|
+
('asctime', '%(asctime)-15s'),
|
|
1201
|
+
('process', 'pid=%(process)-6s'),
|
|
1202
|
+
('thread', 'tid=%(thread)-16s'),
|
|
1203
|
+
('levelname', '%(levelname)-8s'),
|
|
1204
|
+
('name', '%(name)s'),
|
|
1205
|
+
('separator', '::'),
|
|
1206
|
+
('message', '%(message)s'),
|
|
1207
|
+
]
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
class StandardLogFormatter(logging.Formatter):
|
|
1211
|
+
|
|
1212
|
+
@staticmethod
|
|
1213
|
+
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
|
1214
|
+
return ' '.join(v for k, v in parts)
|
|
1215
|
+
|
|
1216
|
+
converter = datetime.datetime.fromtimestamp # type: ignore
|
|
1217
|
+
|
|
1218
|
+
def formatTime(self, record, datefmt=None):
|
|
1219
|
+
ct = self.converter(record.created) # type: ignore
|
|
1220
|
+
if datefmt:
|
|
1221
|
+
return ct.strftime(datefmt) # noqa
|
|
1222
|
+
else:
|
|
1223
|
+
t = ct.strftime("%Y-%m-%d %H:%M:%S") # noqa
|
|
1224
|
+
return '%s.%03d' % (t, record.msecs)
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
##
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def configure_standard_logging(
|
|
1231
|
+
level: ta.Union[int, str] = logging.INFO,
|
|
1232
|
+
*,
|
|
1233
|
+
json: bool = False,
|
|
1234
|
+
) -> logging.Handler:
|
|
1235
|
+
handler = logging.StreamHandler()
|
|
1236
|
+
|
|
1237
|
+
formatter: logging.Formatter
|
|
1238
|
+
if json:
|
|
1239
|
+
formatter = JsonLogFormatter()
|
|
1240
|
+
else:
|
|
1241
|
+
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
|
1242
|
+
handler.setFormatter(formatter)
|
|
1243
|
+
|
|
1244
|
+
handler.addFilter(TidLogFilter())
|
|
1245
|
+
|
|
1246
|
+
logging.root.addHandler(handler)
|
|
1247
|
+
|
|
1248
|
+
if level is not None:
|
|
1249
|
+
logging.root.setLevel(level)
|
|
1250
|
+
|
|
1251
|
+
return handler
|
|
1183
1252
|
|
|
1184
1253
|
|
|
1185
1254
|
########################################
|
omdev/scripts/pyproject.py
CHANGED
|
@@ -26,6 +26,7 @@ import argparse
|
|
|
26
26
|
import base64
|
|
27
27
|
import collections
|
|
28
28
|
import collections.abc
|
|
29
|
+
import concurrent.futures as cf
|
|
29
30
|
import dataclasses as dc
|
|
30
31
|
import datetime
|
|
31
32
|
import decimal
|
|
@@ -33,6 +34,7 @@ import enum
|
|
|
33
34
|
import fractions
|
|
34
35
|
import functools
|
|
35
36
|
import glob
|
|
37
|
+
import importlib
|
|
36
38
|
import inspect
|
|
37
39
|
import itertools
|
|
38
40
|
import json
|
|
@@ -45,6 +47,7 @@ import shutil
|
|
|
45
47
|
import string
|
|
46
48
|
import subprocess
|
|
47
49
|
import sys
|
|
50
|
+
import threading
|
|
48
51
|
import types
|
|
49
52
|
import typing as ta
|
|
50
53
|
import uuid
|
|
@@ -882,6 +885,112 @@ def toml_make_safe_parse_float(parse_float: TomlParseFloat) -> TomlParseFloat:
|
|
|
882
885
|
return safe_parse_float
|
|
883
886
|
|
|
884
887
|
|
|
888
|
+
########################################
|
|
889
|
+
# ../../toml/writer.py
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
class TomlWriter:
|
|
893
|
+
def __init__(self, out: ta.TextIO) -> None:
|
|
894
|
+
super().__init__()
|
|
895
|
+
self._out = out
|
|
896
|
+
|
|
897
|
+
self._indent = 0
|
|
898
|
+
self._wrote_indent = False
|
|
899
|
+
|
|
900
|
+
#
|
|
901
|
+
|
|
902
|
+
def _w(self, s: str) -> None:
|
|
903
|
+
if not self._wrote_indent:
|
|
904
|
+
self._out.write(' ' * self._indent)
|
|
905
|
+
self._wrote_indent = True
|
|
906
|
+
self._out.write(s)
|
|
907
|
+
|
|
908
|
+
def _nl(self) -> None:
|
|
909
|
+
self._out.write('\n')
|
|
910
|
+
self._wrote_indent = False
|
|
911
|
+
|
|
912
|
+
def _needs_quote(self, s: str) -> bool:
|
|
913
|
+
return (
|
|
914
|
+
not s or
|
|
915
|
+
any(c in s for c in '\'"\n') or
|
|
916
|
+
s[0] not in string.ascii_letters
|
|
917
|
+
)
|
|
918
|
+
|
|
919
|
+
def _maybe_quote(self, s: str) -> str:
|
|
920
|
+
if self._needs_quote(s):
|
|
921
|
+
return repr(s)
|
|
922
|
+
else:
|
|
923
|
+
return s
|
|
924
|
+
|
|
925
|
+
#
|
|
926
|
+
|
|
927
|
+
def write_root(self, obj: ta.Mapping) -> None:
|
|
928
|
+
for i, (k, v) in enumerate(obj.items()):
|
|
929
|
+
if i:
|
|
930
|
+
self._nl()
|
|
931
|
+
self._w('[')
|
|
932
|
+
self._w(self._maybe_quote(k))
|
|
933
|
+
self._w(']')
|
|
934
|
+
self._nl()
|
|
935
|
+
self.write_table_contents(v)
|
|
936
|
+
|
|
937
|
+
def write_table_contents(self, obj: ta.Mapping) -> None:
|
|
938
|
+
for k, v in obj.items():
|
|
939
|
+
self.write_key(k)
|
|
940
|
+
self._w(' = ')
|
|
941
|
+
self.write_value(v)
|
|
942
|
+
self._nl()
|
|
943
|
+
|
|
944
|
+
def write_array(self, obj: ta.Sequence) -> None:
|
|
945
|
+
self._w('[')
|
|
946
|
+
self._nl()
|
|
947
|
+
self._indent += 1
|
|
948
|
+
for e in obj:
|
|
949
|
+
self.write_value(e)
|
|
950
|
+
self._w(',')
|
|
951
|
+
self._nl()
|
|
952
|
+
self._indent -= 1
|
|
953
|
+
self._w(']')
|
|
954
|
+
|
|
955
|
+
def write_inline_table(self, obj: ta.Mapping) -> None:
|
|
956
|
+
self._w('{')
|
|
957
|
+
for i, (k, v) in enumerate(obj.items()):
|
|
958
|
+
if i:
|
|
959
|
+
self._w(', ')
|
|
960
|
+
self.write_key(k)
|
|
961
|
+
self._w(' = ')
|
|
962
|
+
self.write_value(v)
|
|
963
|
+
self._w('}')
|
|
964
|
+
|
|
965
|
+
def write_inline_array(self, obj: ta.Sequence) -> None:
|
|
966
|
+
self._w('[')
|
|
967
|
+
for i, e in enumerate(obj):
|
|
968
|
+
if i:
|
|
969
|
+
self._w(', ')
|
|
970
|
+
self.write_value(e)
|
|
971
|
+
self._w(']')
|
|
972
|
+
|
|
973
|
+
def write_key(self, obj: ta.Any) -> None:
|
|
974
|
+
if isinstance(obj, str):
|
|
975
|
+
self._w(self._maybe_quote(obj.replace('_', '-')))
|
|
976
|
+
elif isinstance(obj, int):
|
|
977
|
+
self._w(repr(str(obj)))
|
|
978
|
+
else:
|
|
979
|
+
raise TypeError(obj)
|
|
980
|
+
|
|
981
|
+
def write_value(self, obj: ta.Any) -> None:
|
|
982
|
+
if isinstance(obj, bool):
|
|
983
|
+
self._w(str(obj).lower())
|
|
984
|
+
elif isinstance(obj, (str, int, float)):
|
|
985
|
+
self._w(repr(obj))
|
|
986
|
+
elif isinstance(obj, ta.Mapping):
|
|
987
|
+
self.write_inline_table(obj)
|
|
988
|
+
elif isinstance(obj, ta.Sequence):
|
|
989
|
+
self.write_array(obj)
|
|
990
|
+
else:
|
|
991
|
+
raise TypeError(obj)
|
|
992
|
+
|
|
993
|
+
|
|
885
994
|
########################################
|
|
886
995
|
# ../../versioning/versions.py
|
|
887
996
|
# Copyright (c) Donald Stufft and individual contributors.
|
|
@@ -1977,14 +2086,28 @@ class SpecifierSet(BaseSpecifier):
|
|
|
1977
2086
|
# ../../../omlish/lite/logs.py
|
|
1978
2087
|
"""
|
|
1979
2088
|
TODO:
|
|
2089
|
+
- translate json keys
|
|
1980
2090
|
- debug
|
|
1981
2091
|
"""
|
|
1982
|
-
# ruff: noqa: UP007
|
|
2092
|
+
# ruff: noqa: UP006 UP007 N802
|
|
1983
2093
|
|
|
1984
2094
|
|
|
1985
2095
|
log = logging.getLogger(__name__)
|
|
1986
2096
|
|
|
1987
2097
|
|
|
2098
|
+
##
|
|
2099
|
+
|
|
2100
|
+
|
|
2101
|
+
class TidLogFilter(logging.Filter):
|
|
2102
|
+
|
|
2103
|
+
def filter(self, record):
|
|
2104
|
+
record.tid = threading.get_native_id()
|
|
2105
|
+
return True
|
|
2106
|
+
|
|
2107
|
+
|
|
2108
|
+
##
|
|
2109
|
+
|
|
2110
|
+
|
|
1988
2111
|
class JsonLogFormatter(logging.Formatter):
|
|
1989
2112
|
|
|
1990
2113
|
KEYS: ta.Mapping[str, bool] = {
|
|
@@ -2020,9 +2143,62 @@ class JsonLogFormatter(logging.Formatter):
|
|
|
2020
2143
|
return json_dumps_compact(dct)
|
|
2021
2144
|
|
|
2022
2145
|
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2146
|
+
##
|
|
2147
|
+
|
|
2148
|
+
|
|
2149
|
+
STANDARD_LOG_FORMAT_PARTS = [
|
|
2150
|
+
('asctime', '%(asctime)-15s'),
|
|
2151
|
+
('process', 'pid=%(process)-6s'),
|
|
2152
|
+
('thread', 'tid=%(thread)-16s'),
|
|
2153
|
+
('levelname', '%(levelname)-8s'),
|
|
2154
|
+
('name', '%(name)s'),
|
|
2155
|
+
('separator', '::'),
|
|
2156
|
+
('message', '%(message)s'),
|
|
2157
|
+
]
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
class StandardLogFormatter(logging.Formatter):
|
|
2161
|
+
|
|
2162
|
+
@staticmethod
|
|
2163
|
+
def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
|
|
2164
|
+
return ' '.join(v for k, v in parts)
|
|
2165
|
+
|
|
2166
|
+
converter = datetime.datetime.fromtimestamp # type: ignore
|
|
2167
|
+
|
|
2168
|
+
def formatTime(self, record, datefmt=None):
|
|
2169
|
+
ct = self.converter(record.created) # type: ignore
|
|
2170
|
+
if datefmt:
|
|
2171
|
+
return ct.strftime(datefmt) # noqa
|
|
2172
|
+
else:
|
|
2173
|
+
t = ct.strftime("%Y-%m-%d %H:%M:%S") # noqa
|
|
2174
|
+
return '%s.%03d' % (t, record.msecs)
|
|
2175
|
+
|
|
2176
|
+
|
|
2177
|
+
##
|
|
2178
|
+
|
|
2179
|
+
|
|
2180
|
+
def configure_standard_logging(
|
|
2181
|
+
level: ta.Union[int, str] = logging.INFO,
|
|
2182
|
+
*,
|
|
2183
|
+
json: bool = False,
|
|
2184
|
+
) -> logging.Handler:
|
|
2185
|
+
handler = logging.StreamHandler()
|
|
2186
|
+
|
|
2187
|
+
formatter: logging.Formatter
|
|
2188
|
+
if json:
|
|
2189
|
+
formatter = JsonLogFormatter()
|
|
2190
|
+
else:
|
|
2191
|
+
formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
|
|
2192
|
+
handler.setFormatter(formatter)
|
|
2193
|
+
|
|
2194
|
+
handler.addFilter(TidLogFilter())
|
|
2195
|
+
|
|
2196
|
+
logging.root.addHandler(handler)
|
|
2197
|
+
|
|
2198
|
+
if level is not None:
|
|
2199
|
+
logging.root.setLevel(level)
|
|
2200
|
+
|
|
2201
|
+
return handler
|
|
2026
2202
|
|
|
2027
2203
|
|
|
2028
2204
|
########################################
|
|
@@ -2030,6 +2206,7 @@ def configure_standard_logging(level: ta.Union[int, str] = logging.INFO) -> None
|
|
|
2030
2206
|
"""
|
|
2031
2207
|
TODO:
|
|
2032
2208
|
- pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
|
|
2209
|
+
- nonstrict toggle
|
|
2033
2210
|
"""
|
|
2034
2211
|
# ruff: noqa: UP006 UP007
|
|
2035
2212
|
|
|
@@ -2151,12 +2328,13 @@ class IterableObjMarshaler(ObjMarshaler):
|
|
|
2151
2328
|
class DataclassObjMarshaler(ObjMarshaler):
|
|
2152
2329
|
ty: type
|
|
2153
2330
|
fs: ta.Mapping[str, ObjMarshaler]
|
|
2331
|
+
nonstrict: bool = False
|
|
2154
2332
|
|
|
2155
2333
|
def marshal(self, o: ta.Any) -> ta.Any:
|
|
2156
2334
|
return {k: m.marshal(getattr(o, k)) for k, m in self.fs.items()}
|
|
2157
2335
|
|
|
2158
2336
|
def unmarshal(self, o: ta.Any) -> ta.Any:
|
|
2159
|
-
return self.ty(**{k: self.fs[k].unmarshal(v) for k, v in o.items()})
|
|
2337
|
+
return self.ty(**{k: self.fs[k].unmarshal(v) for k, v in o.items() if self.nonstrict or k in self.fs})
|
|
2160
2338
|
|
|
2161
2339
|
|
|
2162
2340
|
@dc.dataclass(frozen=True)
|
|
@@ -2447,8 +2625,9 @@ class VenvConfig:
|
|
|
2447
2625
|
|
|
2448
2626
|
@dc.dataclass(frozen=True)
|
|
2449
2627
|
class PyprojectConfig:
|
|
2450
|
-
|
|
2451
|
-
|
|
2628
|
+
pkgs: ta.Sequence[str] = dc.field(default_factory=list)
|
|
2629
|
+
srcs: ta.Mapping[str, ta.Sequence[str]] = dc.field(default_factory=dict)
|
|
2630
|
+
venvs: ta.Mapping[str, VenvConfig] = dc.field(default_factory=dict)
|
|
2452
2631
|
|
|
2453
2632
|
venvs_dir: str = '.venvs'
|
|
2454
2633
|
versions_file: ta.Optional[str] = '.versions'
|
|
@@ -2528,6 +2707,196 @@ class PyprojectConfigPreparer:
|
|
|
2528
2707
|
return pcfg
|
|
2529
2708
|
|
|
2530
2709
|
|
|
2710
|
+
########################################
|
|
2711
|
+
# ../pkg.py
|
|
2712
|
+
"""
|
|
2713
|
+
TODO:
|
|
2714
|
+
- ext scanning
|
|
2715
|
+
- __revision__
|
|
2716
|
+
- entry_points
|
|
2717
|
+
|
|
2718
|
+
https://setuptools.pypa.io/en/latest/references/keywords.html
|
|
2719
|
+
https://packaging.python.org/en/latest/specifications/pyproject-toml
|
|
2720
|
+
|
|
2721
|
+
How to build a C extension in keeping with PEP 517, i.e. with pyproject.toml instead of setup.py?
|
|
2722
|
+
https://stackoverflow.com/a/66479252
|
|
2723
|
+
|
|
2724
|
+
https://github.com/pypa/sampleproject/blob/db5806e0a3204034c51b1c00dde7d5eb3fa2532e/setup.py
|
|
2725
|
+
|
|
2726
|
+
https://pip.pypa.io/en/stable/cli/pip_install/#vcs-support
|
|
2727
|
+
vcs+protocol://repo_url/#egg=pkg&subdirectory=pkg_dir
|
|
2728
|
+
'git+https://github.com/wrmsr/omlish@master#subdirectory=.pip/omlish'
|
|
2729
|
+
"""
|
|
2730
|
+
# ruff: noqa: UP006 UP007
|
|
2731
|
+
|
|
2732
|
+
|
|
2733
|
+
class PyprojectPackageGenerator:
|
|
2734
|
+
def __init__(
|
|
2735
|
+
self,
|
|
2736
|
+
dir_name: str,
|
|
2737
|
+
build_root: str,
|
|
2738
|
+
) -> None:
|
|
2739
|
+
super().__init__()
|
|
2740
|
+
self._dir_name = dir_name
|
|
2741
|
+
self._build_root = build_root
|
|
2742
|
+
|
|
2743
|
+
#
|
|
2744
|
+
|
|
2745
|
+
@cached_nullary
|
|
2746
|
+
def about(self) -> types.ModuleType:
|
|
2747
|
+
return importlib.import_module(f'{self._dir_name}.__about__')
|
|
2748
|
+
|
|
2749
|
+
@cached_nullary
|
|
2750
|
+
def project_cls(self) -> type:
|
|
2751
|
+
return self.about().Project
|
|
2752
|
+
|
|
2753
|
+
@cached_nullary
|
|
2754
|
+
def setuptools_cls(self) -> type:
|
|
2755
|
+
return self.about().Setuptools
|
|
2756
|
+
|
|
2757
|
+
#
|
|
2758
|
+
|
|
2759
|
+
@cached_nullary
|
|
2760
|
+
def _build_dir(self) -> str:
|
|
2761
|
+
build_dir: str = os.path.join(self._build_root, self._dir_name)
|
|
2762
|
+
if os.path.isdir(build_dir):
|
|
2763
|
+
shutil.rmtree(build_dir)
|
|
2764
|
+
os.makedirs(build_dir)
|
|
2765
|
+
return build_dir
|
|
2766
|
+
|
|
2767
|
+
#
|
|
2768
|
+
|
|
2769
|
+
def _write_git_ignore(self) -> None:
|
|
2770
|
+
git_ignore = [
|
|
2771
|
+
'/*.egg-info/',
|
|
2772
|
+
'/dist',
|
|
2773
|
+
]
|
|
2774
|
+
with open(os.path.join(self._build_dir(), '.gitignore'), 'w') as f:
|
|
2775
|
+
f.write('\n'.join(git_ignore))
|
|
2776
|
+
|
|
2777
|
+
#
|
|
2778
|
+
|
|
2779
|
+
def _symlink_source_dir(self) -> None:
|
|
2780
|
+
os.symlink(
|
|
2781
|
+
os.path.relpath(self._dir_name, self._build_dir()),
|
|
2782
|
+
os.path.join(self._build_dir(), self._dir_name),
|
|
2783
|
+
)
|
|
2784
|
+
|
|
2785
|
+
#
|
|
2786
|
+
|
|
2787
|
+
@dc.dataclass(frozen=True)
|
|
2788
|
+
class FileContents:
|
|
2789
|
+
pyproject_dct: ta.Mapping[str, ta.Any]
|
|
2790
|
+
manifest_in: ta.Optional[ta.Sequence[str]]
|
|
2791
|
+
|
|
2792
|
+
@staticmethod
|
|
2793
|
+
def _build_cls_dct(cls: type) -> ta.Dict[str, ta.Any]: # noqa
|
|
2794
|
+
dct = {}
|
|
2795
|
+
for b in reversed(cls.__mro__):
|
|
2796
|
+
for k, v in b.__dict__.items():
|
|
2797
|
+
if k.startswith('_'):
|
|
2798
|
+
continue
|
|
2799
|
+
dct[k] = v
|
|
2800
|
+
return dct
|
|
2801
|
+
|
|
2802
|
+
@staticmethod
|
|
2803
|
+
def _move_dict_key(
|
|
2804
|
+
sd: ta.Dict[str, ta.Any],
|
|
2805
|
+
sk: str,
|
|
2806
|
+
dd: ta.Dict[str, ta.Any],
|
|
2807
|
+
dk: str,
|
|
2808
|
+
) -> None:
|
|
2809
|
+
if sk in sd:
|
|
2810
|
+
dd[dk] = sd.pop(sk)
|
|
2811
|
+
|
|
2812
|
+
@cached_nullary
|
|
2813
|
+
def file_contents(self) -> FileContents:
|
|
2814
|
+
pyp_dct = {}
|
|
2815
|
+
|
|
2816
|
+
pyp_dct['build-system'] = {
|
|
2817
|
+
'requires': ['setuptools'],
|
|
2818
|
+
'build-backend': 'setuptools.build_meta',
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
prj = self._build_cls_dct(self.project_cls())
|
|
2822
|
+
pyp_dct['project'] = prj
|
|
2823
|
+
self._move_dict_key(prj, 'optional_dependencies', pyp_dct, 'project.optional-dependencies')
|
|
2824
|
+
|
|
2825
|
+
st = self._build_cls_dct(self.setuptools_cls())
|
|
2826
|
+
pyp_dct['tool.setuptools'] = st
|
|
2827
|
+
self._move_dict_key(st, 'find_packages', pyp_dct, 'tool.setuptools.packages.find')
|
|
2828
|
+
|
|
2829
|
+
mani_in = st.pop('manifest_in', None)
|
|
2830
|
+
|
|
2831
|
+
return self.FileContents(
|
|
2832
|
+
pyp_dct,
|
|
2833
|
+
mani_in,
|
|
2834
|
+
)
|
|
2835
|
+
|
|
2836
|
+
def _write_file_contents(self) -> None:
|
|
2837
|
+
fc = self.file_contents()
|
|
2838
|
+
|
|
2839
|
+
with open(os.path.join(self._build_dir(), 'pyproject.toml'), 'w') as f:
|
|
2840
|
+
TomlWriter(f).write_root(fc.pyproject_dct)
|
|
2841
|
+
|
|
2842
|
+
if fc.manifest_in:
|
|
2843
|
+
with open(os.path.join(self._build_dir(), 'MANIFEST.in'), 'w') as f:
|
|
2844
|
+
f.write('\n'.join(fc.manifest_in)) # noqa
|
|
2845
|
+
|
|
2846
|
+
#
|
|
2847
|
+
|
|
2848
|
+
_STANDARD_FILES: ta.Sequence[str] = [
|
|
2849
|
+
'LICENSE',
|
|
2850
|
+
'README.rst',
|
|
2851
|
+
]
|
|
2852
|
+
|
|
2853
|
+
def _symlink_standard_files(self) -> None:
|
|
2854
|
+
for fn in self._STANDARD_FILES:
|
|
2855
|
+
if os.path.exists(fn):
|
|
2856
|
+
os.symlink(os.path.relpath(fn, self._build_dir()), os.path.join(self._build_dir(), fn))
|
|
2857
|
+
|
|
2858
|
+
#
|
|
2859
|
+
|
|
2860
|
+
def _run_build(
|
|
2861
|
+
self,
|
|
2862
|
+
build_output_dir: ta.Optional[str] = None,
|
|
2863
|
+
) -> None:
|
|
2864
|
+
subprocess.check_call(
|
|
2865
|
+
[
|
|
2866
|
+
sys.executable,
|
|
2867
|
+
'-m',
|
|
2868
|
+
'build',
|
|
2869
|
+
],
|
|
2870
|
+
cwd=self._build_dir(),
|
|
2871
|
+
)
|
|
2872
|
+
|
|
2873
|
+
if build_output_dir is not None:
|
|
2874
|
+
dist_dir = os.path.join(self._build_dir(), 'dist')
|
|
2875
|
+
for fn in os.listdir(dist_dir):
|
|
2876
|
+
shutil.copyfile(os.path.join(dist_dir, fn), os.path.join(build_output_dir, fn))
|
|
2877
|
+
|
|
2878
|
+
#
|
|
2879
|
+
|
|
2880
|
+
def gen(
|
|
2881
|
+
self,
|
|
2882
|
+
*,
|
|
2883
|
+
run_build: bool = False,
|
|
2884
|
+
build_output_dir: ta.Optional[str] = None,
|
|
2885
|
+
) -> str:
|
|
2886
|
+
log.info('Generating pyproject package: %s -> %s', self._dir_name, self._build_root)
|
|
2887
|
+
|
|
2888
|
+
self._build_dir()
|
|
2889
|
+
self._write_git_ignore()
|
|
2890
|
+
self._symlink_source_dir()
|
|
2891
|
+
self._write_file_contents()
|
|
2892
|
+
self._symlink_standard_files()
|
|
2893
|
+
|
|
2894
|
+
if run_build:
|
|
2895
|
+
self._run_build(build_output_dir)
|
|
2896
|
+
|
|
2897
|
+
return self._build_dir()
|
|
2898
|
+
|
|
2899
|
+
|
|
2531
2900
|
########################################
|
|
2532
2901
|
# ../../../omlish/lite/subprocesses.py
|
|
2533
2902
|
# ruff: noqa: UP006 UP007
|
|
@@ -3552,6 +3921,46 @@ def _venv_cmd(args) -> None:
|
|
|
3552
3921
|
##
|
|
3553
3922
|
|
|
3554
3923
|
|
|
3924
|
+
def _pkg_cmd(args) -> None:
|
|
3925
|
+
run = Run()
|
|
3926
|
+
|
|
3927
|
+
cmd = args.cmd
|
|
3928
|
+
if cmd == 'gen':
|
|
3929
|
+
build_root = os.path.join('.pkg')
|
|
3930
|
+
|
|
3931
|
+
if os.path.exists(build_root):
|
|
3932
|
+
shutil.rmtree(build_root)
|
|
3933
|
+
|
|
3934
|
+
build_output_dir = 'dist'
|
|
3935
|
+
run_build = bool(args.build)
|
|
3936
|
+
|
|
3937
|
+
num_threads = 8
|
|
3938
|
+
|
|
3939
|
+
if run_build:
|
|
3940
|
+
os.makedirs(build_output_dir, exist_ok=True)
|
|
3941
|
+
|
|
3942
|
+
with cf.ThreadPoolExecutor(num_threads) as ex:
|
|
3943
|
+
futs = [
|
|
3944
|
+
ex.submit(functools.partial(
|
|
3945
|
+
PyprojectPackageGenerator(
|
|
3946
|
+
dir_name,
|
|
3947
|
+
build_root,
|
|
3948
|
+
).gen,
|
|
3949
|
+
run_build=run_build,
|
|
3950
|
+
build_output_dir=build_output_dir,
|
|
3951
|
+
))
|
|
3952
|
+
for dir_name in run.cfg().pkgs
|
|
3953
|
+
]
|
|
3954
|
+
for fut in futs:
|
|
3955
|
+
fut.result()
|
|
3956
|
+
|
|
3957
|
+
else:
|
|
3958
|
+
raise Exception(f'unknown subcommand: {cmd}')
|
|
3959
|
+
|
|
3960
|
+
|
|
3961
|
+
##
|
|
3962
|
+
|
|
3963
|
+
|
|
3555
3964
|
def _build_parser() -> argparse.ArgumentParser:
|
|
3556
3965
|
parser = argparse.ArgumentParser()
|
|
3557
3966
|
parser.add_argument('--_docker_container', help=argparse.SUPPRESS)
|
|
@@ -3565,6 +3974,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
3565
3974
|
parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
|
|
3566
3975
|
parser_resolve.set_defaults(func=_venv_cmd)
|
|
3567
3976
|
|
|
3977
|
+
parser_resolve = subparsers.add_parser('pkg')
|
|
3978
|
+
parser_resolve.add_argument('-b', '--build', action='store_true')
|
|
3979
|
+
parser_resolve.add_argument('cmd', nargs='?')
|
|
3980
|
+
parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
|
|
3981
|
+
parser_resolve.set_defaults(func=_pkg_cmd)
|
|
3982
|
+
|
|
3568
3983
|
return parser
|
|
3569
3984
|
|
|
3570
3985
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: omdev
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev9
|
|
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.dev9
|
|
16
16
|
Provides-Extra: c
|
|
17
17
|
Requires-Dist: pycparser >=2.22 ; extra == 'c'
|
|
18
18
|
Requires-Dist: cffi >=1.17 ; extra == 'c'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
omdev/__about__.py,sha256=
|
|
1
|
+
omdev/__about__.py,sha256=RXVZPsAF1ImmY7x_3cw2VrnoxdQ_bCa4unQHrnII14Y,722
|
|
2
2
|
omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
omdev/classdot.py,sha256=urN5Pzd2ooAwnfkH0z-muQxdO90IMo-sX2WB-A37lVU,1533
|
|
4
4
|
omdev/cmake.py,sha256=Sk9SL_mFCuYG0FYjG_w53wGt6mOmXfen5pZWZsqjSq4,4592
|
|
@@ -40,16 +40,16 @@ omdev/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
40
40
|
omdev/mypy/debug.py,sha256=WcZw-3Z1njg_KFGqi3DB6RuqbBa3dLArJnjVCuY1Mn0,3003
|
|
41
41
|
omdev/pyproject/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
|
42
42
|
omdev/pyproject/__main__.py,sha256=gFhR9DikwDZk0LqgdR3qq_aXQHThUOPllDmHDOfnFAU,67
|
|
43
|
-
omdev/pyproject/cli.py,sha256=
|
|
44
|
-
omdev/pyproject/configs.py,sha256=
|
|
43
|
+
omdev/pyproject/cli.py,sha256=0GM0YuA9k-Bdq2A2EKAToqoxnkGclZwpjXQAkzWLRgU,9556
|
|
44
|
+
omdev/pyproject/configs.py,sha256=MFHnmpMjlwxw74-SyX1Q1qNQ4ptwTXEzDGkeUcGY0mA,2822
|
|
45
45
|
omdev/pyproject/ext.py,sha256=x13piOOnNrYbA17qZLDVuR0p1sqhgEwpk4FtImX-klM,4281
|
|
46
|
-
omdev/pyproject/pkg.py,sha256=
|
|
46
|
+
omdev/pyproject/pkg.py,sha256=cJppGN1nWC1LJKOoI3FjyhhB40Ig6H1LQBkiKTLpBYA,5380
|
|
47
47
|
omdev/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
48
|
omdev/scripts/execrss.py,sha256=wBpdhWuQj_4EnEmvK_aLQHxaE-J52G9F6g0210fTNe8,307
|
|
49
49
|
omdev/scripts/findimports.py,sha256=d8Xf2TcE7hHh_lXUx2V1APF4Jggu2uO2K0ex6bfmqyQ,2030
|
|
50
50
|
omdev/scripts/findmagic.py,sha256=W7W07YR3zsA0hNej6-BPgQj2hWX1UydrbWnNMRdMA88,1893
|
|
51
|
-
omdev/scripts/interp.py,sha256=
|
|
52
|
-
omdev/scripts/pyproject.py,sha256=
|
|
51
|
+
omdev/scripts/interp.py,sha256=YdWMx34UTlT0QeSW0ya52cXAaqmuJZLtzyPnAe4pN3A,62828
|
|
52
|
+
omdev/scripts/pyproject.py,sha256=x2I4xX7fyo4BzvDdxVZf6dyFkrBCQsFqp2X_E0-ssiY,118628
|
|
53
53
|
omdev/scripts/traceimport.py,sha256=JmUgLEQLY7r3QNQ14Agarqh5qtMyRNenCb_OF5EIikY,13392
|
|
54
54
|
omdev/toml/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
|
55
55
|
omdev/toml/parser.py,sha256=84bn09uhYHwQGyfww6Rw6y1RxPAE_HDltODOSakcqDM,29186
|
|
@@ -60,8 +60,8 @@ omdev/tools/sqlrepl.py,sha256=v9uVQ4nvquSXcQVYIFq34ikumSILvKqzD6lUKLcncCE,5646
|
|
|
60
60
|
omdev/versioning/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
|
61
61
|
omdev/versioning/specifiers.py,sha256=6Odf9e6farwlPRsD_YqwTfYKG-BXn_dIcKtqfkhfodI,17432
|
|
62
62
|
omdev/versioning/versions.py,sha256=ei2eopEsJq3zSMJmezK1nzZgikgCdxFtnF3f69nCRZQ,12246
|
|
63
|
-
omdev-0.0.0.
|
|
64
|
-
omdev-0.0.0.
|
|
65
|
-
omdev-0.0.0.
|
|
66
|
-
omdev-0.0.0.
|
|
67
|
-
omdev-0.0.0.
|
|
63
|
+
omdev-0.0.0.dev9.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
|
64
|
+
omdev-0.0.0.dev9.dist-info/METADATA,sha256=LiR56stWGf5_3tCthvIV95RtXAZdhw4g7a41DGSFltY,757
|
|
65
|
+
omdev-0.0.0.dev9.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
|
66
|
+
omdev-0.0.0.dev9.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
|
|
67
|
+
omdev-0.0.0.dev9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|