omdev 0.0.0.dev31__py3-none-any.whl → 0.0.0.dev32__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.
@@ -0,0 +1,149 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
3
+ import importlib.machinery
4
+ import importlib.metadata
5
+ import importlib.resources
6
+ import json
7
+ import typing as ta
8
+
9
+ from .types import Manifest
10
+
11
+
12
+ ##
13
+
14
+
15
+ class ManifestLoader:
16
+ def __init__(
17
+ self,
18
+ *,
19
+ module_remap: ta.Optional[ta.Mapping[str, str]] = None,
20
+ ) -> None:
21
+ super().__init__()
22
+
23
+ self._module_remap = module_remap or {}
24
+ self._module_reverse_remap = {v: k for k, v in self._module_remap.items()}
25
+
26
+ self._cls_cache: ta.Dict[str, type] = {}
27
+ self._raw_cache: ta.Dict[str, ta.Optional[ta.Sequence[Manifest]]] = {}
28
+
29
+ @classmethod
30
+ def from_entry_point(
31
+ cls,
32
+ globals: ta.Mapping[str, ta.Any], # noqa
33
+ *,
34
+ module_remap: ta.Optional[ta.Mapping[str, str]] = None,
35
+ **kwargs: ta.Any,
36
+ ) -> 'ManifestLoader':
37
+ rm: ta.Dict[str, str] = {}
38
+
39
+ if module_remap:
40
+ rm.update(module_remap)
41
+
42
+ if '__name__' in globals and '__spec__' in globals:
43
+ name: str = globals['__name__']
44
+ spec: importlib.machinery.ModuleSpec = globals['__spec__']
45
+ if '__main__' not in rm and name == '__main__':
46
+ rm[spec.name] = '__main__'
47
+
48
+ return cls(module_remap=rm, **kwargs)
49
+
50
+ def load_cls(self, key: str) -> type:
51
+ try:
52
+ return self._cls_cache[key]
53
+ except KeyError:
54
+ pass
55
+
56
+ if not key.startswith('$'):
57
+ raise Exception(f'Bad key: {key}')
58
+
59
+ parts = key[1:].split('.')
60
+ pos = next(i for i, p in enumerate(parts) if p[0].isupper())
61
+
62
+ mod_name = '.'.join(parts[:pos])
63
+ mod_name = self._module_remap.get(mod_name, mod_name)
64
+ mod = importlib.import_module(mod_name)
65
+
66
+ obj: ta.Any = mod
67
+ for ca in parts[pos:]:
68
+ obj = getattr(obj, ca)
69
+
70
+ cls = obj
71
+ if not isinstance(cls, type):
72
+ raise TypeError(cls)
73
+
74
+ self._cls_cache[key] = cls
75
+ return cls
76
+
77
+ def load_raw(self, pkg_name: str) -> ta.Optional[ta.Sequence[Manifest]]:
78
+ try:
79
+ return self._raw_cache[pkg_name]
80
+ except KeyError:
81
+ pass
82
+
83
+ t = importlib.resources.files(pkg_name).joinpath('.manifests.json')
84
+ if not t.is_file():
85
+ self._raw_cache[pkg_name] = None
86
+ return None
87
+
88
+ src = t.read_text('utf-8')
89
+ obj = json.loads(src)
90
+ if not isinstance(obj, (list, tuple)):
91
+ raise TypeError(obj)
92
+
93
+ lst: ta.List[Manifest] = []
94
+ for e in obj:
95
+ m = Manifest(**e)
96
+
97
+ m = dc.replace(m, module=pkg_name + m.module)
98
+
99
+ [(key, value_dct)] = m.value.items()
100
+ if not key.startswith('$'):
101
+ raise Exception(f'Bad key: {key}')
102
+ if key.startswith('$.'):
103
+ key = f'${pkg_name}{key[1:]}'
104
+ m = dc.replace(m, value={key: value_dct})
105
+
106
+ lst.append(m)
107
+
108
+ self._raw_cache[pkg_name] = lst
109
+ return lst
110
+
111
+ def load(
112
+ self,
113
+ *pkg_names: str,
114
+ only: ta.Optional[ta.Iterable[type]] = None,
115
+ ) -> ta.Sequence[Manifest]:
116
+ only_keys: ta.Optional[ta.Set]
117
+ if only is not None:
118
+ only_keys = set()
119
+ for cls in only:
120
+ if not (isinstance(cls, type) and dc.is_dataclass(cls)):
121
+ raise TypeError(cls)
122
+ mod_name = cls.__module__
123
+ mod_name = self._module_reverse_remap.get(mod_name, mod_name)
124
+ only_keys.add(f'${mod_name}.{cls.__qualname__}')
125
+ else:
126
+ only_keys = None
127
+
128
+ lst: ta.List[Manifest] = []
129
+ for pn in pkg_names:
130
+ for manifest in (self.load_raw(pn) or []):
131
+ [(key, value_dct)] = manifest.value.items()
132
+ if only_keys is not None and key not in only_keys:
133
+ continue
134
+
135
+ cls = self.load_cls(key)
136
+ value = cls(**value_dct)
137
+
138
+ manifest = dc.replace(manifest, value=value)
139
+ lst.append(manifest)
140
+
141
+ return lst
142
+
143
+ ENTRY_POINT_GROUP = 'omlish.manifests'
144
+
145
+ def discover(self) -> ta.Sequence[str]:
146
+ return [
147
+ ep.value
148
+ for ep in importlib.metadata.entry_points(group=self.ENTRY_POINT_GROUP)
149
+ ]
@@ -0,0 +1,16 @@
1
+ import dataclasses as dc
2
+ import typing as ta
3
+
4
+
5
+ @dc.dataclass(frozen=True)
6
+ class ManifestOrigin:
7
+ module: str
8
+ attr: str
9
+
10
+ file: str
11
+ line: int
12
+
13
+
14
+ @dc.dataclass(frozen=True)
15
+ class Manifest(ManifestOrigin):
16
+ value: ta.Any
@@ -2,6 +2,7 @@
2
2
  Tiny pre-commit
3
3
 
4
4
  TODO:
5
+ - ! manifests
5
6
  - global config
6
7
  - global analyses - FilesWithShebang
7
8
  - shebang files have no relative imports
@@ -26,6 +27,7 @@ import typing as ta
26
27
 
27
28
  from omlish import logs
28
29
 
30
+ from ..cli import CliModule
29
31
  from .base import Precheck
30
32
  from .base import PrecheckContext
31
33
  from .git import GitBlacklistPrecheck
@@ -85,6 +87,10 @@ def _build_parser() -> argparse.ArgumentParser:
85
87
  return parser
86
88
 
87
89
 
90
+ # @omlish-manifest
91
+ _CLI_MODULE = CliModule('precheck', __name__)
92
+
93
+
88
94
  def _main(argv: ta.Sequence[str] | None = None) -> None:
89
95
  logs.configure_standard_logging('INFO')
90
96
 
@@ -1,3 +1,10 @@
1
+ from ..cli import CliModule
2
+
3
+
4
+ # @omlish-manifest
5
+ _CLI_MODULE = CliModule('pyproject', __name__)
6
+
7
+
1
8
  if __name__ == '__main__':
2
9
  from .cli import _main
3
10
 
omdev/pyproject/cli.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- # @omdev-amalg ../scripts/pyproject.py
2
+ # @omlish-amalg ../scripts/pyproject.py
3
3
  # ruff: noqa: UP006 UP007
4
4
  """
5
5
  TODO:
omdev/pyproject/pkg.py CHANGED
@@ -295,7 +295,13 @@ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
295
295
  **extras,
296
296
  }
297
297
 
298
- #
298
+ if (eps := prj.pop('entry_points', None)):
299
+ pyp_dct['project.entry-points'] = {TomlWriter.Literal(f"'{k}'"): v for k, v in eps.items()} # type: ignore # noqa
300
+
301
+ if (scs := prj.pop('scripts', None)):
302
+ pyp_dct['project.scripts'] = scs
303
+
304
+ ##
299
305
 
300
306
  st = dict(specs.setuptools)
301
307
  pyp_dct['tool.setuptools'] = st
@@ -420,7 +426,12 @@ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
420
426
  prj = specs.pyproject
421
427
  prj['dependencies'] = [f'{prj["name"]} == {prj["version"]}']
422
428
  prj['name'] += self._pkg_suffix
423
- prj.pop('optional_dependencies', None)
429
+ for k in [
430
+ 'optional_dependencies',
431
+ 'entry_points',
432
+ 'scripts',
433
+ ]:
434
+ prj.pop(k, None)
424
435
 
425
436
  pyp_dct['project'] = prj
426
437
 
omdev/pyproject/reqs.py CHANGED
@@ -22,7 +22,7 @@ class RequirementsRewriter:
22
22
  def _tmp_dir(self) -> str:
23
23
  return tempfile.mkdtemp('-omlish-reqs')
24
24
 
25
- VENV_MAGIC = '# @omdev-venv'
25
+ VENV_MAGIC = '# @omlish-venv'
26
26
 
27
27
  def rewrite_file(self, in_file: str) -> str:
28
28
  with open(in_file) as f:
omdev/scripts/interp.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # noinspection DuplicatedCode
3
3
  # @omlish-lite
4
4
  # @omlish-script
5
- # @omdev-amalg-output ../interp/cli.py
5
+ # @omlish-amalg-output ../interp/cli.py
6
6
  # ruff: noqa: N802 UP006 UP007 UP036
7
7
  """
8
8
  TODO:
@@ -1796,16 +1796,14 @@ class RunningInterpProvider(InterpProvider):
1796
1796
  """
1797
1797
  TODO:
1798
1798
  - custom tags
1799
+ - 'aliases'
1800
+ - https://github.com/pyenv/pyenv/pull/2966
1801
+ - https://github.com/pyenv/pyenv/issues/218 (lol)
1802
+ - probably need custom (temp?) definition file
1803
+ - *or* python-build directly just into the versions dir?
1799
1804
  - optionally install / upgrade pyenv itself
1800
1805
  - new vers dont need these custom mac opts, only run on old vers
1801
-
1802
- TODO opts:
1803
- - --enable-loadable-sqlite-extensions LDFLAGS="-L/opt/homebrew/opt/sqlite/lib" CPPFLAGS="-I/opt/homebrew/opt/sqlite/include"
1804
- - --enable-shared
1805
- - --enable-optimizations
1806
- - --enable-profiling ?
1807
- - --enable-ipv6 ?
1808
- """ # noqa
1806
+ """
1809
1807
 
1810
1808
 
1811
1809
  ##
@@ -1899,8 +1897,33 @@ class PyenvInstallOpts:
1899
1897
  )
1900
1898
 
1901
1899
 
1902
- DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-s', '-v'])
1900
+ # TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
1901
+ DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(
1902
+ opts=[
1903
+ '-s',
1904
+ '-v',
1905
+ '-k',
1906
+ ],
1907
+ conf_opts=[
1908
+ '--enable-loadable-sqlite-extensions',
1909
+
1910
+ # '--enable-shared',
1911
+
1912
+ '--enable-optimizations',
1913
+ '--with-lto',
1914
+
1915
+ # '--enable-profiling', # ?
1916
+
1917
+ # '--enable-ipv6', # ?
1918
+ ],
1919
+ cflags=[
1920
+ # '-march=native',
1921
+ # '-mtune=native',
1922
+ ],
1923
+ )
1924
+
1903
1925
  DEBUG_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-g'])
1926
+
1904
1927
  THREADED_PYENV_INSTALL_OPTS = PyenvInstallOpts(conf_opts=['--disable-gil'])
1905
1928
 
1906
1929
 
@@ -1999,6 +2022,7 @@ class PyenvVersionInstaller:
1999
2022
  opts: ta.Optional[PyenvInstallOpts] = None,
2000
2023
  interp_opts: InterpOpts = InterpOpts(),
2001
2024
  *,
2025
+ install_name: ta.Optional[str] = None,
2002
2026
  no_default_opts: bool = False,
2003
2027
  pyenv: Pyenv = Pyenv(),
2004
2028
  ) -> None:
@@ -2019,6 +2043,7 @@ class PyenvVersionInstaller:
2019
2043
  self._version = version
2020
2044
  self._opts = opts
2021
2045
  self._interp_opts = interp_opts
2046
+ self._given_install_name = install_name
2022
2047
 
2023
2048
  self._no_default_opts = no_default_opts
2024
2049
  self._pyenv = pyenv
@@ -2033,6 +2058,8 @@ class PyenvVersionInstaller:
2033
2058
 
2034
2059
  @cached_nullary
2035
2060
  def install_name(self) -> str:
2061
+ if self._given_install_name is not None:
2062
+ return self._given_install_name
2036
2063
  return self._version + ('-debug' if self._interp_opts.debug else '')
2037
2064
 
2038
2065
  @cached_nullary
@@ -2052,11 +2079,26 @@ class PyenvVersionInstaller:
2052
2079
  v += ' ' + os.environ[k]
2053
2080
  env[k] = v
2054
2081
 
2055
- subprocess_check_call(
2056
- self._pyenv.exe(),
2057
- 'install',
2082
+ conf_args = [
2058
2083
  *self._opts.opts,
2059
2084
  self._version,
2085
+ ]
2086
+
2087
+ if self._given_install_name is not None:
2088
+ full_args = [
2089
+ os.path.join(check_not_none(self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'),
2090
+ *conf_args,
2091
+ self.install_dir(),
2092
+ ]
2093
+ else:
2094
+ full_args = [
2095
+ self._pyenv.exe(),
2096
+ 'install',
2097
+ *conf_args,
2098
+ ]
2099
+
2100
+ subprocess_check_call(
2101
+ *full_args,
2060
2102
  env=env,
2061
2103
  )
2062
2104
 
@@ -2414,9 +2456,13 @@ def _list_cmd(args) -> None:
2414
2456
 
2415
2457
 
2416
2458
  def _resolve_cmd(args) -> None:
2417
- r = DEFAULT_INTERP_RESOLVER
2459
+ if args.provider:
2460
+ p = INTERP_PROVIDER_TYPES_BY_NAME[args.provider]()
2461
+ r = InterpResolver([(p.name, p)])
2462
+ else:
2463
+ r = DEFAULT_INTERP_RESOLVER
2418
2464
  s = InterpSpecifier.parse(args.version)
2419
- print(check_not_none(r.resolve(s)).exe)
2465
+ print(check_not_none(r.resolve(s, install=bool(args.install))).exe)
2420
2466
 
2421
2467
 
2422
2468
  def _build_parser() -> argparse.ArgumentParser:
@@ -2426,12 +2472,14 @@ def _build_parser() -> argparse.ArgumentParser:
2426
2472
 
2427
2473
  parser_list = subparsers.add_parser('list')
2428
2474
  parser_list.add_argument('version')
2429
- parser_list.add_argument('--debug', action='store_true')
2475
+ parser_list.add_argument('-d', '--debug', action='store_true')
2430
2476
  parser_list.set_defaults(func=_list_cmd)
2431
2477
 
2432
2478
  parser_resolve = subparsers.add_parser('resolve')
2433
2479
  parser_resolve.add_argument('version')
2434
- parser_resolve.add_argument('--debug', action='store_true')
2480
+ parser_resolve.add_argument('-p', '--provider')
2481
+ parser_resolve.add_argument('-d', '--debug', action='store_true')
2482
+ parser_resolve.add_argument('-i', '--install', action='store_true')
2435
2483
  parser_resolve.set_defaults(func=_resolve_cmd)
2436
2484
 
2437
2485
  return parser
@@ -2,7 +2,7 @@
2
2
  # noinspection DuplicatedCode
3
3
  # @omlish-lite
4
4
  # @omlish-script
5
- # @omdev-amalg-output ../pyproject/cli.py
5
+ # @omlish-amalg-output ../pyproject/cli.py
6
6
  # ruff: noqa: N802 TCH003 UP006 UP007 UP036
7
7
  """
8
8
  TODO:
@@ -104,7 +104,7 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
104
104
 
105
105
 
106
106
  class CextMagic:
107
- MAGIC = '@omdev-cext'
107
+ MAGIC = '@omlish-cext'
108
108
  MAGIC_COMMENT = f'// {MAGIC}'
109
109
 
110
110
  FILE_EXTENSIONS = ('c', 'cc', 'cpp')
@@ -1089,6 +1089,10 @@ def toml_make_safe_parse_float(parse_float: TomlParseFloat) -> TomlParseFloat:
1089
1089
 
1090
1090
 
1091
1091
  class TomlWriter:
1092
+ @dc.dataclass(frozen=True)
1093
+ class Literal:
1094
+ s: str
1095
+
1092
1096
  def __init__(self, out: ta.TextIO) -> None:
1093
1097
  super().__init__()
1094
1098
  self._out = out
@@ -1170,7 +1174,9 @@ class TomlWriter:
1170
1174
  self._w(']')
1171
1175
 
1172
1176
  def write_key(self, obj: ta.Any) -> None:
1173
- if isinstance(obj, str):
1177
+ if isinstance(obj, TomlWriter.Literal):
1178
+ self._w(obj.s)
1179
+ elif isinstance(obj, str):
1174
1180
  self._w(self._maybe_quote(obj.replace('_', '-')))
1175
1181
  elif isinstance(obj, int):
1176
1182
  self._w(repr(str(obj)))
@@ -2022,7 +2028,7 @@ class RequirementsRewriter:
2022
2028
  def _tmp_dir(self) -> str:
2023
2029
  return tempfile.mkdtemp('-omlish-reqs')
2024
2030
 
2025
- VENV_MAGIC = '# @omdev-venv'
2031
+ VENV_MAGIC = '# @omlish-venv'
2026
2032
 
2027
2033
  def rewrite_file(self, in_file: str) -> str:
2028
2034
  with open(in_file) as f:
@@ -3977,7 +3983,13 @@ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
3977
3983
  **extras,
3978
3984
  }
3979
3985
 
3980
- #
3986
+ if (eps := prj.pop('entry_points', None)):
3987
+ pyp_dct['project.entry-points'] = {TomlWriter.Literal(f"'{k}'"): v for k, v in eps.items()} # type: ignore # noqa
3988
+
3989
+ if (scs := prj.pop('scripts', None)):
3990
+ pyp_dct['project.scripts'] = scs
3991
+
3992
+ ##
3981
3993
 
3982
3994
  st = dict(specs.setuptools)
3983
3995
  pyp_dct['tool.setuptools'] = st
@@ -4102,7 +4114,12 @@ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
4102
4114
  prj = specs.pyproject
4103
4115
  prj['dependencies'] = [f'{prj["name"]} == {prj["version"]}']
4104
4116
  prj['name'] += self._pkg_suffix
4105
- prj.pop('optional_dependencies', None)
4117
+ for k in [
4118
+ 'optional_dependencies',
4119
+ 'entry_points',
4120
+ 'scripts',
4121
+ ]:
4122
+ prj.pop(k, None)
4106
4123
 
4107
4124
  pyp_dct['project'] = prj
4108
4125
 
@@ -4233,16 +4250,14 @@ class RunningInterpProvider(InterpProvider):
4233
4250
  """
4234
4251
  TODO:
4235
4252
  - custom tags
4253
+ - 'aliases'
4254
+ - https://github.com/pyenv/pyenv/pull/2966
4255
+ - https://github.com/pyenv/pyenv/issues/218 (lol)
4256
+ - probably need custom (temp?) definition file
4257
+ - *or* python-build directly just into the versions dir?
4236
4258
  - optionally install / upgrade pyenv itself
4237
4259
  - new vers dont need these custom mac opts, only run on old vers
4238
-
4239
- TODO opts:
4240
- - --enable-loadable-sqlite-extensions LDFLAGS="-L/opt/homebrew/opt/sqlite/lib" CPPFLAGS="-I/opt/homebrew/opt/sqlite/include"
4241
- - --enable-shared
4242
- - --enable-optimizations
4243
- - --enable-profiling ?
4244
- - --enable-ipv6 ?
4245
- """ # noqa
4260
+ """
4246
4261
 
4247
4262
 
4248
4263
  ##
@@ -4336,8 +4351,33 @@ class PyenvInstallOpts:
4336
4351
  )
4337
4352
 
4338
4353
 
4339
- DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-s', '-v'])
4354
+ # TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
4355
+ DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(
4356
+ opts=[
4357
+ '-s',
4358
+ '-v',
4359
+ '-k',
4360
+ ],
4361
+ conf_opts=[
4362
+ '--enable-loadable-sqlite-extensions',
4363
+
4364
+ # '--enable-shared',
4365
+
4366
+ '--enable-optimizations',
4367
+ '--with-lto',
4368
+
4369
+ # '--enable-profiling', # ?
4370
+
4371
+ # '--enable-ipv6', # ?
4372
+ ],
4373
+ cflags=[
4374
+ # '-march=native',
4375
+ # '-mtune=native',
4376
+ ],
4377
+ )
4378
+
4340
4379
  DEBUG_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-g'])
4380
+
4341
4381
  THREADED_PYENV_INSTALL_OPTS = PyenvInstallOpts(conf_opts=['--disable-gil'])
4342
4382
 
4343
4383
 
@@ -4436,6 +4476,7 @@ class PyenvVersionInstaller:
4436
4476
  opts: ta.Optional[PyenvInstallOpts] = None,
4437
4477
  interp_opts: InterpOpts = InterpOpts(),
4438
4478
  *,
4479
+ install_name: ta.Optional[str] = None,
4439
4480
  no_default_opts: bool = False,
4440
4481
  pyenv: Pyenv = Pyenv(),
4441
4482
  ) -> None:
@@ -4456,6 +4497,7 @@ class PyenvVersionInstaller:
4456
4497
  self._version = version
4457
4498
  self._opts = opts
4458
4499
  self._interp_opts = interp_opts
4500
+ self._given_install_name = install_name
4459
4501
 
4460
4502
  self._no_default_opts = no_default_opts
4461
4503
  self._pyenv = pyenv
@@ -4470,6 +4512,8 @@ class PyenvVersionInstaller:
4470
4512
 
4471
4513
  @cached_nullary
4472
4514
  def install_name(self) -> str:
4515
+ if self._given_install_name is not None:
4516
+ return self._given_install_name
4473
4517
  return self._version + ('-debug' if self._interp_opts.debug else '')
4474
4518
 
4475
4519
  @cached_nullary
@@ -4489,11 +4533,26 @@ class PyenvVersionInstaller:
4489
4533
  v += ' ' + os.environ[k]
4490
4534
  env[k] = v
4491
4535
 
4492
- subprocess_check_call(
4493
- self._pyenv.exe(),
4494
- 'install',
4536
+ conf_args = [
4495
4537
  *self._opts.opts,
4496
4538
  self._version,
4539
+ ]
4540
+
4541
+ if self._given_install_name is not None:
4542
+ full_args = [
4543
+ os.path.join(check_not_none(self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'),
4544
+ *conf_args,
4545
+ self.install_dir(),
4546
+ ]
4547
+ else:
4548
+ full_args = [
4549
+ self._pyenv.exe(),
4550
+ 'install',
4551
+ *conf_args,
4552
+ ]
4553
+
4554
+ subprocess_check_call(
4555
+ *full_args,
4497
4556
  env=env,
4498
4557
  )
4499
4558
 
omdev/toml/writer.py CHANGED
@@ -1,8 +1,13 @@
1
+ import dataclasses as dc
1
2
  import string
2
3
  import typing as ta
3
4
 
4
5
 
5
6
  class TomlWriter:
7
+ @dc.dataclass(frozen=True)
8
+ class Literal:
9
+ s: str
10
+
6
11
  def __init__(self, out: ta.TextIO) -> None:
7
12
  super().__init__()
8
13
  self._out = out
@@ -84,7 +89,9 @@ class TomlWriter:
84
89
  self._w(']')
85
90
 
86
91
  def write_key(self, obj: ta.Any) -> None:
87
- if isinstance(obj, str):
92
+ if isinstance(obj, TomlWriter.Literal):
93
+ self._w(obj.s)
94
+ elif isinstance(obj, str):
88
95
  self._w(self._maybe_quote(obj.replace('_', '-')))
89
96
  elif isinstance(obj, int):
90
97
  self._w(repr(str(obj)))
@@ -14,6 +14,8 @@ from omlish import lang
14
14
  from omlish import logs
15
15
  from omlish.formats import yaml
16
16
 
17
+ from ..cli import CliModule
18
+
17
19
 
18
20
  @lang.cached_function
19
21
  def docker_exe() -> str:
@@ -178,6 +180,10 @@ class Cli(ap.Cli):
178
180
  f.write(new_src)
179
181
 
180
182
 
183
+ # @omlish-manifest
184
+ _CLI_MODULE = CliModule('docker', __name__)
185
+
186
+
181
187
  if __name__ == '__main__':
182
188
  logs.configure_standard_logging('INFO')
183
189
  Cli()()
omdev/tools/gittools.py CHANGED
@@ -3,6 +3,8 @@ import subprocess
3
3
  from omlish import argparse as ap
4
4
  from omlish import logs
5
5
 
6
+ from ..cli import CliModule
7
+
6
8
 
7
9
  class Cli(ap.Cli):
8
10
  @ap.command()
@@ -17,6 +19,10 @@ class Cli(ap.Cli):
17
19
  )
18
20
 
19
21
 
22
+ # @omlish-manifest
23
+ _CLI_MODULE = CliModule('git', __name__)
24
+
25
+
20
26
  if __name__ == '__main__':
21
27
  logs.configure_standard_logging('INFO')
22
28
  Cli()()
omdev/tools/piptools.py CHANGED
@@ -5,6 +5,8 @@ import xml.etree.ElementTree as ET # noqa
5
5
  from omlish import argparse as ap
6
6
  from omlish import check
7
7
 
8
+ from ..cli import CliModule
9
+
8
10
 
9
11
  PYPI_URL = 'https://pypi.org/'
10
12
 
@@ -46,5 +48,9 @@ class Cli(ap.Cli):
46
48
  f.write(new_src)
47
49
 
48
50
 
51
+ # @omlish-manifest
52
+ _CLI_MODULE = CliModule('pip', __name__)
53
+
54
+
49
55
  if __name__ == '__main__':
50
56
  Cli()()
omdev/tools/sqlrepl.py CHANGED
@@ -13,6 +13,8 @@ from omlish import check
13
13
  from omlish import lang
14
14
  from omlish import logs
15
15
 
16
+ from ..cli import CliModule
17
+
16
18
 
17
19
  @dc.dataclass(frozen=True)
18
20
  class ServerSpec:
@@ -188,6 +190,10 @@ class Cli(ap.Cli):
188
190
  raise Exception(f'unhandled dialect: {dialect=}')
189
191
 
190
192
 
193
+ # @omlish-manifest
194
+ _CLI_MODULE = CliModule('sqlrepl', __name__)
195
+
196
+
191
197
  if __name__ == '__main__':
192
198
  logs.configure_standard_logging('INFO')
193
199
  Cli()()