omdev 0.0.0.dev31__py3-none-any.whl → 0.0.0.dev33__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.

@@ -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,15 @@ 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
+ prj.pop('cli_scripts', None)
305
+
306
+ ##
299
307
 
300
308
  st = dict(specs.setuptools)
301
309
  pyp_dct['tool.setuptools'] = st
@@ -379,6 +387,13 @@ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
379
387
  pkg_suffix='-cext',
380
388
  ).gen(opts)
381
389
 
390
+ if self.build_specs().pyproject.get('cli_scripts'):
391
+ _PyprojectCliPackageGenerator(
392
+ self._dir_name,
393
+ self._pkgs_root,
394
+ pkg_suffix='-cli',
395
+ ).gen(opts)
396
+
382
397
  return ret
383
398
 
384
399
 
@@ -420,7 +435,13 @@ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
420
435
  prj = specs.pyproject
421
436
  prj['dependencies'] = [f'{prj["name"]} == {prj["version"]}']
422
437
  prj['name'] += self._pkg_suffix
423
- prj.pop('optional_dependencies', None)
438
+ for k in [
439
+ 'optional_dependencies',
440
+ 'entry_points',
441
+ 'scripts',
442
+ 'cli_scripts',
443
+ ]:
444
+ prj.pop(k, None)
424
445
 
425
446
  pyp_dct['project'] = prj
426
447
 
@@ -483,3 +504,73 @@ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
483
504
 
484
505
  with open(os.path.join(self._pkg_dir(), 'setup.py'), 'w') as f:
485
506
  f.write(fc.setup_py)
507
+
508
+
509
+ ##
510
+
511
+
512
+ class _PyprojectCliPackageGenerator(BasePyprojectPackageGenerator):
513
+
514
+ #
515
+
516
+ @dc.dataclass(frozen=True)
517
+ class FileContents:
518
+ pyproject_dct: ta.Mapping[str, ta.Any]
519
+
520
+ @cached_nullary
521
+ def file_contents(self) -> FileContents:
522
+ specs = self.build_specs()
523
+
524
+ #
525
+
526
+ pyp_dct = {}
527
+
528
+ pyp_dct['build-system'] = {
529
+ 'requires': ['setuptools'],
530
+ 'build-backend': 'setuptools.build_meta',
531
+ }
532
+
533
+ prj = specs.pyproject
534
+ prj['dependencies'] = [f'{prj["name"]} == {prj["version"]}']
535
+ prj['name'] += self._pkg_suffix
536
+ for k in [
537
+ 'optional_dependencies',
538
+ 'entry_points',
539
+ 'scripts',
540
+ ]:
541
+ prj.pop(k, None)
542
+
543
+ pyp_dct['project'] = prj
544
+
545
+ if (scs := prj.pop('cli_scripts', None)):
546
+ pyp_dct['project.scripts'] = scs
547
+
548
+ #
549
+
550
+ st = dict(specs.setuptools)
551
+ pyp_dct['tool.setuptools'] = st
552
+
553
+ for k in [
554
+ 'cexts',
555
+
556
+ 'find_packages',
557
+ 'package_data',
558
+ 'manifest_in',
559
+ ]:
560
+ st.pop(k, None)
561
+
562
+ pyp_dct['tool.setuptools.packages.find'] = {
563
+ 'include': [],
564
+ }
565
+
566
+ #
567
+
568
+ return self.FileContents(
569
+ pyp_dct,
570
+ )
571
+
572
+ def _write_file_contents(self) -> None:
573
+ fc = self.file_contents()
574
+
575
+ with open(os.path.join(self._pkg_dir(), 'pyproject.toml'), 'w') as f:
576
+ TomlWriter(f).write_root(fc.pyproject_dct)
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