omdev 0.0.0.dev7__tar.gz → 0.0.0.dev10__tar.gz

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.

Files changed (74) hide show
  1. {omdev-0.0.0.dev7/omdev.egg-info → omdev-0.0.0.dev10}/PKG-INFO +8 -2
  2. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/__about__.py +1 -1
  3. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/pyproject/cli.py +52 -0
  4. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/pyproject/configs.py +3 -2
  5. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/pyproject/pkg.py +21 -1
  6. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/interp.py +73 -4
  7. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/pyproject.py +442 -7
  8. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10/omdev.egg-info}/PKG-INFO +8 -2
  9. omdev-0.0.0.dev10/omdev.egg-info/requires.txt +19 -0
  10. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/pyproject.toml +9 -2
  11. omdev-0.0.0.dev7/omdev.egg-info/requires.txt +0 -12
  12. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/LICENSE +0 -0
  13. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/MANIFEST.in +0 -0
  14. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/README.rst +0 -0
  15. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/__init__.py +0 -0
  16. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/amalg/__init__.py +0 -0
  17. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/amalg/__main__.py +0 -0
  18. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/amalg/amalg.py +0 -0
  19. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/classdot.py +0 -0
  20. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/cmake.py +0 -0
  21. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/__init__.py +0 -0
  22. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/__init__.py +0 -0
  23. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/build_ext.py +0 -0
  24. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/compilers/__init__.py +0 -0
  25. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/compilers/ccompiler.py +0 -0
  26. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/compilers/options.py +0 -0
  27. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/compilers/unixccompiler.py +0 -0
  28. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/dir_util.py +0 -0
  29. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/errors.py +0 -0
  30. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/extension.py +0 -0
  31. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/file_util.py +0 -0
  32. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/modified.py +0 -0
  33. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/spawn.py +0 -0
  34. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/sysconfig.py +0 -0
  35. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/util.py +0 -0
  36. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/_distutils/version.py +0 -0
  37. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/build.py +0 -0
  38. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/cmake.py +0 -0
  39. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/importhook.py +0 -0
  40. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/exts/scan.py +0 -0
  41. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/__init__.py +0 -0
  42. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/__main__.py +0 -0
  43. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/cli.py +0 -0
  44. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/inspect.py +0 -0
  45. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/providers.py +0 -0
  46. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/pyenv.py +0 -0
  47. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/resolvers.py +0 -0
  48. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/standalone.py +0 -0
  49. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/system.py +0 -0
  50. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/interp/types.py +0 -0
  51. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/mypy/__init__.py +0 -0
  52. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/mypy/debug.py +0 -0
  53. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/pyproject/__init__.py +0 -0
  54. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/pyproject/__main__.py +0 -0
  55. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/pyproject/ext.py +0 -0
  56. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/__init__.py +0 -0
  57. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/execrss.py +0 -0
  58. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/findimports.py +0 -0
  59. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/findmagic.py +0 -0
  60. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/scripts/traceimport.py +0 -0
  61. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/tokens.py +0 -0
  62. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/toml/__init__.py +0 -0
  63. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/toml/parser.py +0 -0
  64. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/toml/writer.py +0 -0
  65. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/tools/__init__.py +0 -0
  66. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/tools/dockertools.py +0 -0
  67. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/tools/sqlrepl.py +0 -0
  68. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/versioning/__init__.py +0 -0
  69. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/versioning/specifiers.py +0 -0
  70. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev/versioning/versions.py +0 -0
  71. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev.egg-info/SOURCES.txt +0 -0
  72. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev.egg-info/dependency_links.txt +0 -0
  73. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/omdev.egg-info/top_level.txt +0 -0
  74. {omdev-0.0.0.dev7 → omdev-0.0.0.dev10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev7
3
+ Version: 0.0.0.dev10
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,13 @@ 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.dev7
15
+ Requires-Dist: omlish==0.0.0.dev10
16
+ Provides-Extra: all
17
+ Requires-Dist: pycparser>=2.22; extra == "all"
18
+ Requires-Dist: cffi>=1.17; extra == "all"
19
+ Requires-Dist: pcpp>=1.30; extra == "all"
20
+ Requires-Dist: mypy>=1.11; extra == "all"
21
+ Requires-Dist: tokenize_rt>=6; extra == "all"
16
22
  Provides-Extra: c
17
23
  Requires-Dist: pycparser>=2.22; extra == "c"
18
24
  Requires-Dist: cffi>=1.17; extra == "c"
@@ -30,6 +30,6 @@ class Project(ProjectBase):
30
30
 
31
31
  class Setuptools(SetuptoolsBase):
32
32
  find_packages = {
33
- 'include': ['omdev', 'omdev.*'],
33
+ 'include': [Project.name, f'{Project.name}.*'],
34
34
  'exclude': [*SetuptoolsBase.find_packages['exclude']],
35
35
  }
@@ -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,49 @@ 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 not cmd:
298
+ raise Exception('must specify command')
299
+
300
+ elif cmd == 'gen':
301
+ build_root = os.path.join('.pkg')
302
+
303
+ if os.path.exists(build_root):
304
+ shutil.rmtree(build_root)
305
+
306
+ build_output_dir = 'dist'
307
+ run_build = bool(args.build)
308
+
309
+ num_threads = 8
310
+
311
+ if run_build:
312
+ os.makedirs(build_output_dir, exist_ok=True)
313
+
314
+ with cf.ThreadPoolExecutor(num_threads) as ex:
315
+ futs = [
316
+ ex.submit(functools.partial(
317
+ PyprojectPackageGenerator(
318
+ dir_name,
319
+ build_root,
320
+ ).gen,
321
+ run_build=run_build,
322
+ build_output_dir=build_output_dir,
323
+ ))
324
+ for dir_name in run.cfg().pkgs
325
+ ]
326
+ for fut in futs:
327
+ fut.result()
328
+
329
+ else:
330
+ raise Exception(f'unknown subcommand: {cmd}')
331
+
332
+
333
+ ##
334
+
335
+
290
336
  def _build_parser() -> argparse.ArgumentParser:
291
337
  parser = argparse.ArgumentParser()
292
338
  parser.add_argument('--_docker_container', help=argparse.SUPPRESS)
@@ -300,6 +346,12 @@ def _build_parser() -> argparse.ArgumentParser:
300
346
  parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
301
347
  parser_resolve.set_defaults(func=_venv_cmd)
302
348
 
349
+ parser_resolve = subparsers.add_parser('pkg')
350
+ parser_resolve.add_argument('-b', '--build', action='store_true')
351
+ parser_resolve.add_argument('cmd', nargs='?')
352
+ parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
353
+ parser_resolve.set_defaults(func=_pkg_cmd)
354
+
303
355
  return parser
304
356
 
305
357
 
@@ -16,8 +16,9 @@ class VenvConfig:
16
16
 
17
17
  @dc.dataclass(frozen=True)
18
18
  class PyprojectConfig:
19
- srcs: ta.Mapping[str, ta.Sequence[str]]
20
- venvs: ta.Mapping[str, VenvConfig]
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'
@@ -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
 
@@ -114,6 +115,8 @@ class PyprojectPackageGenerator:
114
115
  def file_contents(self) -> FileContents:
115
116
  pyp_dct = {}
116
117
 
118
+ #
119
+
117
120
  pyp_dct['build-system'] = {
118
121
  'requires': ['setuptools'],
119
122
  'build-backend': 'setuptools.build_meta',
@@ -121,14 +124,29 @@ class PyprojectPackageGenerator:
121
124
 
122
125
  prj = self._build_cls_dct(self.project_cls())
123
126
  pyp_dct['project'] = prj
124
- self._move_dict_key(prj, 'optional_dependencies', pyp_dct, 'project.optional-dependencies')
127
+
128
+ self._move_dict_key(prj, 'optional_dependencies', pyp_dct, extrask := 'project.optional-dependencies')
129
+ if (extras := pyp_dct.get(extrask)):
130
+ pyp_dct[extrask] = {
131
+ 'all': [
132
+ e
133
+ for lst in extras.values()
134
+ for e in lst
135
+ ],
136
+ **extras,
137
+ }
138
+
139
+ #
125
140
 
126
141
  st = self._build_cls_dct(self.setuptools_cls())
127
142
  pyp_dct['tool.setuptools'] = st
143
+
128
144
  self._move_dict_key(st, 'find_packages', pyp_dct, 'tool.setuptools.packages.find')
129
145
 
130
146
  mani_in = st.pop('manifest_in', None)
131
147
 
148
+ #
149
+
132
150
  return self.FileContents(
133
151
  pyp_dct,
134
152
  mani_in,
@@ -184,6 +202,8 @@ class PyprojectPackageGenerator:
184
202
  run_build: bool = False,
185
203
  build_output_dir: ta.Optional[str] = None,
186
204
  ) -> str:
205
+ log.info('Generating pyproject package: %s -> %s', self._dir_name, self._build_root)
206
+
187
207
  self._build_dir()
188
208
  self._write_git_ignore()
189
209
  self._symlink_source_dir()
@@ -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
- def configure_standard_logging(level: ta.Union[int, str] = logging.INFO) -> None:
1181
- logging.root.addHandler(logging.StreamHandler())
1182
- logging.root.setLevel(level)
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
  ########################################
@@ -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
- def configure_standard_logging(level: ta.Union[int, str] = logging.INFO) -> None:
2024
- logging.root.addHandler(logging.StreamHandler())
2025
- logging.root.setLevel(level)
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
- srcs: ta.Mapping[str, ta.Sequence[str]]
2451
- venvs: ta.Mapping[str, VenvConfig]
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,213 @@ 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
+ #
2817
+
2818
+ pyp_dct['build-system'] = {
2819
+ 'requires': ['setuptools'],
2820
+ 'build-backend': 'setuptools.build_meta',
2821
+ }
2822
+
2823
+ prj = self._build_cls_dct(self.project_cls())
2824
+ pyp_dct['project'] = prj
2825
+
2826
+ self._move_dict_key(prj, 'optional_dependencies', pyp_dct, extrask := 'project.optional-dependencies')
2827
+ if (extras := pyp_dct.get(extrask)):
2828
+ pyp_dct[extrask] = {
2829
+ 'all': [
2830
+ e
2831
+ for lst in extras.values()
2832
+ for e in lst
2833
+ ],
2834
+ **extras,
2835
+ }
2836
+
2837
+ #
2838
+
2839
+ st = self._build_cls_dct(self.setuptools_cls())
2840
+ pyp_dct['tool.setuptools'] = st
2841
+
2842
+ self._move_dict_key(st, 'find_packages', pyp_dct, 'tool.setuptools.packages.find')
2843
+
2844
+ mani_in = st.pop('manifest_in', None)
2845
+
2846
+ #
2847
+
2848
+ return self.FileContents(
2849
+ pyp_dct,
2850
+ mani_in,
2851
+ )
2852
+
2853
+ def _write_file_contents(self) -> None:
2854
+ fc = self.file_contents()
2855
+
2856
+ with open(os.path.join(self._build_dir(), 'pyproject.toml'), 'w') as f:
2857
+ TomlWriter(f).write_root(fc.pyproject_dct)
2858
+
2859
+ if fc.manifest_in:
2860
+ with open(os.path.join(self._build_dir(), 'MANIFEST.in'), 'w') as f:
2861
+ f.write('\n'.join(fc.manifest_in)) # noqa
2862
+
2863
+ #
2864
+
2865
+ _STANDARD_FILES: ta.Sequence[str] = [
2866
+ 'LICENSE',
2867
+ 'README.rst',
2868
+ ]
2869
+
2870
+ def _symlink_standard_files(self) -> None:
2871
+ for fn in self._STANDARD_FILES:
2872
+ if os.path.exists(fn):
2873
+ os.symlink(os.path.relpath(fn, self._build_dir()), os.path.join(self._build_dir(), fn))
2874
+
2875
+ #
2876
+
2877
+ def _run_build(
2878
+ self,
2879
+ build_output_dir: ta.Optional[str] = None,
2880
+ ) -> None:
2881
+ subprocess.check_call(
2882
+ [
2883
+ sys.executable,
2884
+ '-m',
2885
+ 'build',
2886
+ ],
2887
+ cwd=self._build_dir(),
2888
+ )
2889
+
2890
+ if build_output_dir is not None:
2891
+ dist_dir = os.path.join(self._build_dir(), 'dist')
2892
+ for fn in os.listdir(dist_dir):
2893
+ shutil.copyfile(os.path.join(dist_dir, fn), os.path.join(build_output_dir, fn))
2894
+
2895
+ #
2896
+
2897
+ def gen(
2898
+ self,
2899
+ *,
2900
+ run_build: bool = False,
2901
+ build_output_dir: ta.Optional[str] = None,
2902
+ ) -> str:
2903
+ log.info('Generating pyproject package: %s -> %s', self._dir_name, self._build_root)
2904
+
2905
+ self._build_dir()
2906
+ self._write_git_ignore()
2907
+ self._symlink_source_dir()
2908
+ self._write_file_contents()
2909
+ self._symlink_standard_files()
2910
+
2911
+ if run_build:
2912
+ self._run_build(build_output_dir)
2913
+
2914
+ return self._build_dir()
2915
+
2916
+
2531
2917
  ########################################
2532
2918
  # ../../../omlish/lite/subprocesses.py
2533
2919
  # ruff: noqa: UP006 UP007
@@ -3552,6 +3938,49 @@ def _venv_cmd(args) -> None:
3552
3938
  ##
3553
3939
 
3554
3940
 
3941
+ def _pkg_cmd(args) -> None:
3942
+ run = Run()
3943
+
3944
+ cmd = args.cmd
3945
+ if not cmd:
3946
+ raise Exception('must specify command')
3947
+
3948
+ elif cmd == 'gen':
3949
+ build_root = os.path.join('.pkg')
3950
+
3951
+ if os.path.exists(build_root):
3952
+ shutil.rmtree(build_root)
3953
+
3954
+ build_output_dir = 'dist'
3955
+ run_build = bool(args.build)
3956
+
3957
+ num_threads = 8
3958
+
3959
+ if run_build:
3960
+ os.makedirs(build_output_dir, exist_ok=True)
3961
+
3962
+ with cf.ThreadPoolExecutor(num_threads) as ex:
3963
+ futs = [
3964
+ ex.submit(functools.partial(
3965
+ PyprojectPackageGenerator(
3966
+ dir_name,
3967
+ build_root,
3968
+ ).gen,
3969
+ run_build=run_build,
3970
+ build_output_dir=build_output_dir,
3971
+ ))
3972
+ for dir_name in run.cfg().pkgs
3973
+ ]
3974
+ for fut in futs:
3975
+ fut.result()
3976
+
3977
+ else:
3978
+ raise Exception(f'unknown subcommand: {cmd}')
3979
+
3980
+
3981
+ ##
3982
+
3983
+
3555
3984
  def _build_parser() -> argparse.ArgumentParser:
3556
3985
  parser = argparse.ArgumentParser()
3557
3986
  parser.add_argument('--_docker_container', help=argparse.SUPPRESS)
@@ -3565,6 +3994,12 @@ def _build_parser() -> argparse.ArgumentParser:
3565
3994
  parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
3566
3995
  parser_resolve.set_defaults(func=_venv_cmd)
3567
3996
 
3997
+ parser_resolve = subparsers.add_parser('pkg')
3998
+ parser_resolve.add_argument('-b', '--build', action='store_true')
3999
+ parser_resolve.add_argument('cmd', nargs='?')
4000
+ parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
4001
+ parser_resolve.set_defaults(func=_pkg_cmd)
4002
+
3568
4003
  return parser
3569
4004
 
3570
4005
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev7
3
+ Version: 0.0.0.dev10
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,7 +12,13 @@ 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.dev7
15
+ Requires-Dist: omlish==0.0.0.dev10
16
+ Provides-Extra: all
17
+ Requires-Dist: pycparser>=2.22; extra == "all"
18
+ Requires-Dist: cffi>=1.17; extra == "all"
19
+ Requires-Dist: pcpp>=1.30; extra == "all"
20
+ Requires-Dist: mypy>=1.11; extra == "all"
21
+ Requires-Dist: tokenize_rt>=6; extra == "all"
16
22
  Provides-Extra: c
17
23
  Requires-Dist: pycparser>=2.22; extra == "c"
18
24
  Requires-Dist: cffi>=1.17; extra == "c"
@@ -0,0 +1,19 @@
1
+ omlish==0.0.0.dev10
2
+
3
+ [all]
4
+ pycparser>=2.22
5
+ cffi>=1.17
6
+ pcpp>=1.30
7
+ mypy>=1.11
8
+ tokenize_rt>=6
9
+
10
+ [c]
11
+ pycparser>=2.22
12
+ cffi>=1.17
13
+ pcpp>=1.30
14
+
15
+ [mypy]
16
+ mypy>=1.11
17
+
18
+ [tokens]
19
+ tokenize_rt>=6
@@ -12,7 +12,7 @@ authors = [
12
12
  urls = {source = 'https://github.com/wrmsr/omlish'}
13
13
  license = {text = 'BSD-3-Clause'}
14
14
  requires-python = '>=3.12'
15
- version = '0.0.0.dev7'
15
+ version = '0.0.0.dev10'
16
16
  classifiers = [
17
17
  'License :: OSI Approved :: BSD License',
18
18
  'Development Status :: 2 - Pre-Alpha',
@@ -22,10 +22,17 @@ classifiers = [
22
22
  ]
23
23
  description = 'omdev'
24
24
  dependencies = [
25
- 'omlish == 0.0.0.dev7',
25
+ 'omlish == 0.0.0.dev10',
26
26
  ]
27
27
 
28
28
  [project.optional-dependencies]
29
+ all = [
30
+ 'pycparser >= 2.22',
31
+ 'cffi >= 1.17',
32
+ 'pcpp >= 1.30',
33
+ 'mypy >= 1.11',
34
+ 'tokenize_rt >= 6',
35
+ ]
29
36
  c = [
30
37
  'pycparser >= 2.22',
31
38
  'cffi >= 1.17',
@@ -1,12 +0,0 @@
1
- omlish==0.0.0.dev7
2
-
3
- [c]
4
- pycparser>=2.22
5
- cffi>=1.17
6
- pcpp>=1.30
7
-
8
- [mypy]
9
- mypy>=1.11
10
-
11
- [tokens]
12
- tokenize_rt>=6
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes