omdev 0.0.0.dev23__tar.gz → 0.0.0.dev25__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 (90) hide show
  1. {omdev-0.0.0.dev23/omdev.egg-info → omdev-0.0.0.dev25}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/README.rst +3 -0
  3. omdev-0.0.0.dev25/omdev/_manifests.json +1 -0
  4. omdev-0.0.0.dev25/omdev/cexts/_boilerplate.cc +82 -0
  5. omdev-0.0.0.dev25/omdev/cexts/_distutils/LICENSE +12 -0
  6. omdev-0.0.0.dev25/omdev/cexts/build.py +87 -0
  7. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/cmake.py +1 -1
  8. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/importhook.py +1 -1
  9. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/findmagic.py +7 -0
  10. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/pyenv.py +26 -1
  11. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/resolvers.py +1 -1
  12. omdev-0.0.0.dev25/omdev/manifests.py +225 -0
  13. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/cli.py +14 -2
  14. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/pkg.py +9 -3
  15. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/scripts/interp.py +160 -6
  16. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/scripts/pyproject.py +190 -11
  17. omdev-0.0.0.dev25/omdev/tools/importscan.py +187 -0
  18. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25/omdev.egg-info}/PKG-INFO +2 -2
  19. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev.egg-info/SOURCES.txt +6 -1
  20. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev.egg-info/requires.txt +1 -1
  21. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/pyproject.toml +12 -3
  22. omdev-0.0.0.dev23/omdev/cexts/build.py +0 -43
  23. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/LICENSE +0 -0
  24. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/MANIFEST.in +0 -0
  25. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/__about__.py +0 -0
  26. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/__init__.py +0 -0
  27. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/amalg/__init__.py +0 -0
  28. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/amalg/__main__.py +0 -0
  29. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/amalg/amalg.py +0 -0
  30. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/bracepy.py +0 -0
  31. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/__init__.py +0 -0
  32. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/__init__.py +0 -0
  33. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/build_ext.py +0 -0
  34. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
  35. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
  36. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/compilers/options.py +0 -0
  37. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
  38. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/dir_util.py +0 -0
  39. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/errors.py +0 -0
  40. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/extension.py +0 -0
  41. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/file_util.py +0 -0
  42. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/modified.py +0 -0
  43. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/spawn.py +0 -0
  44. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/sysconfig.py +0 -0
  45. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/util.py +0 -0
  46. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/_distutils/version.py +0 -0
  47. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/magic.py +0 -0
  48. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cexts/scan.py +0 -0
  49. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/classdot.py +0 -0
  50. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/cmake.py +0 -0
  51. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/findimports.py +0 -0
  52. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/__init__.py +0 -0
  53. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/__main__.py +0 -0
  54. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/cli.py +0 -0
  55. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/inspect.py +0 -0
  56. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/providers.py +0 -0
  57. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/standalone.py +0 -0
  58. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/system.py +0 -0
  59. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/interp/types.py +0 -0
  60. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/mypy/__init__.py +0 -0
  61. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/mypy/debug.py +0 -0
  62. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/precheck/__init__.py +0 -0
  63. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/precheck/__main__.py +0 -0
  64. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/precheck/precheck.py +0 -0
  65. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/__init__.py +0 -0
  66. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/__main__.py +0 -0
  67. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/cexts.py +0 -0
  68. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/configs.py +0 -0
  69. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/pyproject/reqs.py +0 -0
  70. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/revisions.py +0 -0
  71. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/scripts/__init__.py +0 -0
  72. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/scripts/bumpversion.py +0 -0
  73. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/scripts/execrss.py +0 -0
  74. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/tokens.py +0 -0
  75. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/toml/__init__.py +0 -0
  76. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/toml/parser.py +0 -0
  77. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/toml/writer.py +0 -0
  78. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/tools/__init__.py +0 -0
  79. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/tools/dockertools.py +0 -0
  80. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/tools/gittools.py +0 -0
  81. /omdev-0.0.0.dev23/omdev/tools/traceimport.py → /omdev-0.0.0.dev25/omdev/tools/importtrace.py +0 -0
  82. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/tools/rst.py +0 -0
  83. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/tools/sqlrepl.py +0 -0
  84. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/versioning/__init__.py +0 -0
  85. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/versioning/specifiers.py +0 -0
  86. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/versioning/versions.py +0 -0
  87. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev/wheelfile.py +0 -0
  88. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev.egg-info/dependency_links.txt +0 -0
  89. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/omdev.egg-info/top_level.txt +0 -0
  90. {omdev-0.0.0.dev23 → omdev-0.0.0.dev25}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev23
3
+ Version: 0.0.0.dev25
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.dev23
15
+ Requires-Dist: omlish==0.0.0.dev25
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser~=2.22; extra == "all"
18
18
  Requires-Dist: cffi~=1.17; extra == "all"
@@ -8,3 +8,6 @@ Core packages installable from git via:
8
8
  .. code-block::
9
9
 
10
10
  pip install 'git+https://github.com/wrmsr/omlish@master#subdirectory=.pkg/<pkg>'
11
+
12
+ Core packages have no required dependencies, but numerous optional ones - see their respective ``pyproject.toml`` files
13
+ for details.
@@ -0,0 +1 @@
1
+ []
@@ -0,0 +1,82 @@
1
+ // @omdev-cext
2
+ #define PY_SSIZE_T_CLEAN
3
+ #include "Python.h"
4
+ #include "structmember.h"
5
+
6
+ #include <unistd.h>
7
+
8
+ //
9
+
10
+ #define _MODULE_NAME "_boilerplate"
11
+ #define _PACKAGE_NAME "omdev.cexts"
12
+ #define _MODULE_FULL_NAME _PACKAGE_NAME "." _MODULE_NAME
13
+
14
+ typedef struct boilerplate_state {
15
+ } boilerplate_state;
16
+
17
+ static inline boilerplate_state * get_boilerplate_state(PyObject *module)
18
+ {
19
+ void *state = PyModule_GetState(module);
20
+ assert(state != NULL);
21
+ return (boilerplate_state *)state;
22
+ }
23
+
24
+ //
25
+
26
+ PyDoc_STRVAR(boilerplate_doc, "boilerplate");
27
+
28
+ static int boilerplate_exec(PyObject *module)
29
+ {
30
+ get_boilerplate_state(module);
31
+ return 0;
32
+ }
33
+
34
+ static int boilerplate_traverse(PyObject *module, visitproc visit, void *arg)
35
+ {
36
+ get_boilerplate_state(module);
37
+ return 0;
38
+ }
39
+
40
+ static int boilerplate_clear(PyObject *module)
41
+ {
42
+ get_boilerplate_state(module);
43
+ return 0;
44
+ }
45
+
46
+ static void boilerplate_free(void *module)
47
+ {
48
+ boilerplate_clear((PyObject *)module);
49
+ }
50
+
51
+ static PyMethodDef boilerplate_methods[] = {
52
+ {NULL, NULL, 0, NULL}
53
+ };
54
+
55
+ static struct PyModuleDef_Slot boilerplate_slots[] = {
56
+ {Py_mod_exec, (void *) boilerplate_exec},
57
+ // #if PY_VERSION_HEX >= 0x030D0000
58
+ // {Py_mod_gil, Py_MOD_GIL_NOT_USED},
59
+ // #endif
60
+ {0, NULL}
61
+ };
62
+
63
+ static struct PyModuleDef boilerplate_module = {
64
+ .m_base = PyModuleDef_HEAD_INIT,
65
+ .m_name = _MODULE_NAME,
66
+ .m_doc = boilerplate_doc,
67
+ .m_size = sizeof(boilerplate_state),
68
+ .m_methods = boilerplate_methods,
69
+ .m_slots = boilerplate_slots,
70
+ .m_traverse = boilerplate_traverse,
71
+ .m_clear = boilerplate_clear,
72
+ .m_free = boilerplate_free,
73
+ };
74
+
75
+ extern "C" {
76
+
77
+ PyMODINIT_FUNC PyInit__boilerplate(void)
78
+ {
79
+ return PyModuleDef_Init(&boilerplate_module);
80
+ }
81
+
82
+ }
@@ -0,0 +1,12 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
2
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
3
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
4
+ persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
7
+ Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
10
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
11
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
12
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,87 @@
1
+ import dataclasses as dc
2
+ import os.path
3
+ import sys
4
+ import sysconfig
5
+ import typing as ta
6
+
7
+ from omlish import check
8
+ from omlish import lang
9
+
10
+ from . import _distutils as du
11
+
12
+
13
+ CPP_STD = 'c++20'
14
+
15
+
16
+ @dc.dataclass(frozen=True)
17
+ class BuildExt:
18
+ full_name: str
19
+ src_file: str
20
+
21
+ inplace: bool = dc.field(default=True, kw_only=True)
22
+ debug: bool = dc.field(default=True, kw_only=True)
23
+ force: bool = dc.field(default=False, kw_only=True)
24
+
25
+ dry_run: bool = dc.field(default=False, kw_only=True)
26
+ verbose: bool = dc.field(default=False, kw_only=True)
27
+
28
+ extra_src_files: lang.SequenceNotStr[str] | None = dc.field(default=None, kw_only=True)
29
+ include_dirs: lang.SequenceNotStr[str] | None = dc.field(default=None, kw_only=True)
30
+ compile_args: lang.SequenceNotStr[str] | None = dc.field(default=None, kw_only=True)
31
+ link_args: lang.SequenceNotStr[str] | None = dc.field(default=None, kw_only=True)
32
+ define_macros: ta.Sequence[tuple[str, str]] | None = dc.field(default=None, kw_only=True)
33
+ undef_macros: lang.SequenceNotStr[str] | None = dc.field(default=None, kw_only=True)
34
+
35
+ def __post_init__(self) -> None:
36
+ check.not_isinstance(self.compile_args, str)
37
+ check.not_isinstance(self.link_args, str)
38
+
39
+
40
+ def build_ext(ext: BuildExt) -> str:
41
+ extra_link_args: list[str] = []
42
+ if sys.platform == 'darwin':
43
+ extra_link_args.append('-Wl,-no_fixup_chains')
44
+
45
+ du_ext = du.Extension(
46
+ ext.full_name,
47
+ sources=[
48
+ ext.src_file,
49
+ *(ext.extra_src_files or []),
50
+ ],
51
+ include_dirs=[
52
+ os.path.dirname(ext.src_file),
53
+ *(ext.include_dirs or []),
54
+ ],
55
+ extra_compile_args=[
56
+ *([f'-std={CPP_STD}'] if any(ext.src_file.endswith(sf) for sf in ('cc', 'cpp')) else []),
57
+ *(ext.compile_args or []),
58
+ ],
59
+ extra_link_args=[
60
+ *extra_link_args,
61
+ *(ext.link_args or []),
62
+ ],
63
+ define_macros=(list(ext.define_macros) if ext.define_macros is not None else None),
64
+ undef_macros=(list(ext.undef_macros) if ext.undef_macros is not None else None),
65
+ )
66
+
67
+ cmd_obj = du.BuildExt(du.BuildExt.Options(
68
+ inplace=ext.inplace,
69
+ debug=ext.debug,
70
+ force=ext.force,
71
+
72
+ dry_run=ext.dry_run,
73
+ verbose=ext.verbose,
74
+ ))
75
+ cmd_obj.build_extension(du_ext)
76
+
77
+ so_file = os.path.join(
78
+ os.path.dirname(ext.src_file),
79
+ ''.join([
80
+ ext.full_name.rpartition('.')[2],
81
+ '.',
82
+ sysconfig.get_config_var('SOABI'),
83
+ sysconfig.get_config_var('SHLIB_SUFFIX'),
84
+ ]),
85
+ )
86
+
87
+ return so_file
@@ -302,7 +302,7 @@ class CmakeProjectGen:
302
302
  clg.run()
303
303
 
304
304
  with open(os.path.join(self.cmake_dir(), 'CMakeLists.txt'), 'w') as f:
305
- f.write(out.getvalue())
305
+ f.write(out.getvalue().strip() + '\n')
306
306
 
307
307
 
308
308
  ##
@@ -42,7 +42,7 @@ class CextImportLoader(importlib.machinery.ExtensionFileLoader):
42
42
  super().__init__(module_name, filename)
43
43
 
44
44
  def create_module(self, spec: importlib.machinery.ModuleSpec) -> types.ModuleType:
45
- so_path = build.build_ext(spec.name, check.non_empty_str(spec.origin))
45
+ so_path = build.build_ext(build.BuildExt(spec.name, check.non_empty_str(spec.origin)))
46
46
  self.path = so_path # noqa
47
47
  spec.origin = so_path
48
48
  return super().create_module(spec)
@@ -17,6 +17,13 @@ def find_magic(
17
17
  *,
18
18
  py: bool = False,
19
19
  ) -> ta.Iterator[str]:
20
+ if isinstance(roots, str):
21
+ raise TypeError(roots)
22
+ if isinstance(magics, str):
23
+ raise TypeError(magics)
24
+ if isinstance(exts, str):
25
+ raise TypeError(exts)
26
+
20
27
  if not magics:
21
28
  raise Exception('Must specify magics')
22
29
  if not exts:
@@ -92,6 +92,14 @@ class Pyenv:
92
92
  ret.append(l)
93
93
  return ret
94
94
 
95
+ def update(self) -> bool:
96
+ if (root := self.root()) is None:
97
+ return False
98
+ if not os.path.isdir(os.path.join(root, '.git')):
99
+ return False
100
+ subprocess_check_call('git', 'pull', cwd=root)
101
+ return True
102
+
95
103
 
96
104
  ##
97
105
 
@@ -292,6 +300,10 @@ class PyenvInterpProvider(InterpProvider):
292
300
 
293
301
  inspect: bool = False,
294
302
  inspector: InterpInspector = INTERP_INSPECTOR,
303
+
304
+ *,
305
+
306
+ try_update: bool = False,
295
307
  ) -> None:
296
308
  super().__init__()
297
309
 
@@ -300,6 +312,8 @@ class PyenvInterpProvider(InterpProvider):
300
312
  self._inspect = inspect
301
313
  self._inspector = inspector
302
314
 
315
+ self._try_update = try_update
316
+
303
317
  #
304
318
 
305
319
  @staticmethod
@@ -364,8 +378,9 @@ class PyenvInterpProvider(InterpProvider):
364
378
 
365
379
  #
366
380
 
367
- def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
381
+ def _get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
368
382
  lst = []
383
+
369
384
  for vs in self._pyenv.installable_versions():
370
385
  if (iv := self.guess_version(vs)) is None:
371
386
  continue
@@ -373,6 +388,16 @@ class PyenvInterpProvider(InterpProvider):
373
388
  raise Exception('Pyenv installable versions not expected to have debug suffix')
374
389
  for d in [False, True]:
375
390
  lst.append(dc.replace(iv, opts=dc.replace(iv.opts, debug=d)))
391
+
392
+ return lst
393
+
394
+ def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
395
+ lst = self._get_installable_versions(spec)
396
+
397
+ if self._try_update and not any(v in spec for v in lst):
398
+ if self._pyenv.update():
399
+ lst = self._get_installable_versions(spec)
400
+
376
401
  return lst
377
402
 
378
403
  def install_version(self, version: InterpVersion) -> Interp:
@@ -99,7 +99,7 @@ class InterpResolver:
99
99
 
100
100
  DEFAULT_INTERP_RESOLVER = InterpResolver([(p.name, p) for p in [
101
101
  # pyenv is preferred to system interpreters as it tends to have more support for things like tkinter
102
- PyenvInterpProvider(),
102
+ PyenvInterpProvider(try_update=True),
103
103
 
104
104
  RunningInterpProvider(),
105
105
 
@@ -0,0 +1,225 @@
1
+ """
2
+ !!! manifests! get-manifest, _manifest.py
3
+ - dumb dicts, root keys are 'types'
4
+ - get put in _manifest.py, root level dict or smth
5
+ - IMPORT files w comment
6
+ - comment must immediately precede a global val setter
7
+ - val is grabbed from imported module dict by name
8
+ - value is repr'd somehow (roundtrip checked) (naw, json lol)
9
+ - dumped in _manifest.py
10
+ - # @omlish-manifest \n _CACHE_MANIFEST = {'cache': {'name': 'llm', …
11
+ - also can do prechecks!
12
+ """
13
+ # ruff: noqa: UP006
14
+ # @omlish-lite
15
+ import argparse
16
+ import collections
17
+ import dataclasses as dc
18
+ import inspect
19
+ import json
20
+ import os.path
21
+ import re
22
+ import shlex
23
+ import subprocess
24
+ import sys
25
+ import typing as ta
26
+
27
+ from omlish.lite.cached import cached_nullary
28
+ from omlish.lite.json import json_dumps_pretty
29
+
30
+ from . import findmagic
31
+
32
+
33
+ ##
34
+
35
+
36
+ @dc.dataclass(frozen=True)
37
+ class ManifestOrigin:
38
+ module: str
39
+ attr: str
40
+
41
+ file: str
42
+ line: int
43
+
44
+
45
+ @dc.dataclass(frozen=True)
46
+ class Manifest(ManifestOrigin):
47
+ value: ta.Any
48
+
49
+
50
+ MANIFEST_MAGIC = '# @omlish-manifest'
51
+
52
+ _MANIFEST_GLOBAL_PAT = re.compile(r'^(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*=.*')
53
+
54
+
55
+ def _dump_module_manifests(spec: str, *attrs: str) -> None:
56
+ import importlib
57
+ import json
58
+
59
+ mod = importlib.import_module(spec)
60
+
61
+ out = {}
62
+ for attr in attrs:
63
+ manifest = getattr(mod, attr)
64
+
65
+ manifest_json = json.dumps(manifest)
66
+ rt_manifest = json.loads(manifest_json)
67
+
68
+ if rt_manifest != manifest:
69
+ raise Exception(f'Manifest failed to roundtrip: {manifest} != {rt_manifest}')
70
+
71
+ out[attr] = rt_manifest
72
+
73
+ out_json = json.dumps(out, indent=None, separators=(',', ':'))
74
+ print(out_json)
75
+
76
+
77
+ @cached_nullary
78
+ def _payload_src() -> str:
79
+ return inspect.getsource(_dump_module_manifests)
80
+
81
+
82
+ def build_module_manifests(
83
+ file: str,
84
+ base: str,
85
+ *,
86
+ shell_wrap: bool = True,
87
+ ) -> ta.Sequence[Manifest]:
88
+ print((file, base))
89
+
90
+ if not file.endswith('.py'):
91
+ raise Exception(file)
92
+
93
+ mod_name = file.rpartition('.')[0].replace(os.sep, '.')
94
+
95
+ with open(os.path.join(base, file)) as f:
96
+ src = f.read()
97
+
98
+ origins: ta.List[ManifestOrigin] = []
99
+ lines = src.splitlines(keepends=True)
100
+ for i, l in enumerate(lines):
101
+ if l.startswith(MANIFEST_MAGIC):
102
+ if (m := _MANIFEST_GLOBAL_PAT.match(nl := lines[i + 1])) is None:
103
+ raise Exception(nl)
104
+
105
+ origins.append(ManifestOrigin(
106
+ module=mod_name,
107
+ attr=m.groupdict()['name'],
108
+
109
+ file=file,
110
+ line=i + 1,
111
+ ))
112
+
113
+ if not origins:
114
+ raise Exception('no manifests found')
115
+
116
+ if (dups := [k for k, v in collections.Counter(o.attr for o in origins).items() if v > 1]):
117
+ raise Exception(f'Duplicate attrs: {dups}')
118
+
119
+ attrs = [o.attr for o in origins]
120
+
121
+ subproc_src = '\n\n'.join([
122
+ _payload_src(),
123
+ f'_dump_module_manifests({mod_name!r}, {", ".join(repr(a) for a in attrs)})\n',
124
+ ])
125
+
126
+ args = [
127
+ sys.executable,
128
+ '-c',
129
+ subproc_src,
130
+ ]
131
+
132
+ if shell_wrap:
133
+ args = ['sh', '-c', ' '.join(map(shlex.quote, args))]
134
+
135
+ subproc_out = subprocess.check_output(args)
136
+
137
+ sp_lines = subproc_out.decode().strip().splitlines()
138
+ if len(sp_lines) != 1:
139
+ raise Exception('Unexpected subprocess output')
140
+
141
+ dct = json.loads(sp_lines[0])
142
+ if set(dct) != set(attrs):
143
+ raise Exception('Unexpected subprocess output keys')
144
+
145
+ out: ta.List[Manifest] = []
146
+
147
+ for o in origins:
148
+ manifest = dct[o.attr]
149
+
150
+ out.append(Manifest(
151
+ **dc.asdict(o),
152
+ value=manifest,
153
+ ))
154
+
155
+ return out
156
+
157
+
158
+ def build_package_manifests(
159
+ name: str,
160
+ base: str,
161
+ *,
162
+ write: bool = False,
163
+ ) -> ta.List[Manifest]:
164
+ pkg_dir = os.path.join(base, name)
165
+ if not os.path.isdir(pkg_dir) or not os.path.isfile(os.path.join(pkg_dir, '__init__.py')):
166
+ raise Exception(pkg_dir)
167
+
168
+ manifests: ta.List[Manifest] = []
169
+
170
+ for file in findmagic.find_magic(
171
+ [pkg_dir],
172
+ [MANIFEST_MAGIC],
173
+ ['py'],
174
+ ):
175
+ manifests.extend(build_module_manifests(os.path.relpath(file, base), base))
176
+
177
+ if write:
178
+ with open(os.path.join(pkg_dir, '_manifests.json'), 'w') as f:
179
+ f.write(json_dumps_pretty([dc.asdict(m) for m in manifests]))
180
+ f.write('\n')
181
+
182
+ return manifests
183
+
184
+
185
+ ##
186
+
187
+
188
+ if __name__ == '__main__':
189
+ def _gen_cmd(args) -> None:
190
+ if args.base is not None:
191
+ base = args.base
192
+ else:
193
+ base = os.getcwd()
194
+ base = os.path.abspath(base)
195
+ if not os.path.isdir(base):
196
+ raise RuntimeError(base)
197
+
198
+ for pkg in args.package:
199
+ ms = build_package_manifests(
200
+ pkg,
201
+ base,
202
+ write=args.write or False,
203
+ )
204
+ if not args.quiet:
205
+ print(json_dumps_pretty(ms))
206
+
207
+ def _main(argv=None) -> None:
208
+ parser = argparse.ArgumentParser()
209
+ subparsers = parser.add_subparsers()
210
+
211
+ parser_gen = subparsers.add_parser('gen')
212
+ parser_gen.add_argument('-b', '--base')
213
+ parser_gen.add_argument('-w', '--write', action='store_true')
214
+ parser_gen.add_argument('-q', '--quiet', action='store_true')
215
+ parser_gen.add_argument('package', nargs='*')
216
+
217
+ parser_gen.set_defaults(func=_gen_cmd)
218
+
219
+ args = parser.parse_args(argv)
220
+ if not getattr(args, 'func', None):
221
+ parser.print_help()
222
+ else:
223
+ args.func(args)
224
+
225
+ _main()
@@ -243,18 +243,30 @@ def _venv_cmd(args) -> None:
243
243
  f'--_docker_container={shlex.quote(sd)}',
244
244
  *map(shlex.quote, sys.argv[1:]),
245
245
  ])
246
+
247
+ docker_env = {
248
+ 'DOCKER_HOST_PLATFORM': os.environ.get('DOCKER_HOST_PLATFORM', sys.platform),
249
+ }
250
+ for e in args.docker_env or []:
251
+ if '=' in e:
252
+ k, _, v = e.split('=')
253
+ docker_env[k] = v
254
+ else:
255
+ docker_env[e] = os.environ.get(e, '')
256
+
246
257
  subprocess_check_call(
247
258
  'docker',
248
259
  'compose',
249
260
  '-f', 'docker/compose.yml',
250
261
  'exec',
251
262
  *itertools.chain.from_iterable(
252
- ('-e', f'{e}={os.environ.get(e, "")}' if '=' not in e else e)
253
- for e in (args.docker_env or [])
263
+ ('-e', f'{k}={v}')
264
+ for k, v in docker_env.items()
254
265
  ),
255
266
  '-it', sd,
256
267
  'bash', '--login', '-c', script,
257
268
  )
269
+
258
270
  return
259
271
 
260
272
  cmd = args.cmd
@@ -255,6 +255,7 @@ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
255
255
  st.pop('cexts', None)
256
256
 
257
257
  self._move_dict_key(st, 'find_packages', pyp_dct, 'tool.setuptools.packages.find')
258
+ self._move_dict_key(st, 'package_data', pyp_dct, 'tool.setuptools.package-data')
258
259
 
259
260
  mani_in = st.pop('manifest_in', None)
260
261
 
@@ -337,9 +338,14 @@ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
337
338
  st = specs.setuptools
338
339
  pyp_dct['tool.setuptools'] = st
339
340
 
340
- st.pop('cexts', None)
341
- st.pop('find_packages', None)
342
- st.pop('manifest_in', None)
341
+ for k in [
342
+ 'cexts',
343
+
344
+ 'find_packages',
345
+ 'package_data',
346
+ 'manifest_in',
347
+ ]:
348
+ st.pop(k, None)
343
349
 
344
350
  pyp_dct['tool.setuptools.packages.find'] = {
345
351
  'include': [],