omdev 0.0.0.dev151__tar.gz → 0.0.0.dev153__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 (168) hide show
  1. {omdev-0.0.0.dev151/omdev.egg-info → omdev-0.0.0.dev153}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/cache.py +2 -2
  3. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/imgur.py +1 -1
  4. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pycharm/cli.py +1 -1
  5. omdev-0.0.0.dev153/omdev/pyproject/cli.py +302 -0
  6. omdev-0.0.0.dev153/omdev/pyproject/venvs.py +114 -0
  7. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/interp.py +18 -57
  8. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/pyproject.py +472 -255
  9. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/doc.py +1 -1
  10. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/docker.py +1 -1
  11. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153/omdev.egg-info}/PKG-INFO +2 -2
  12. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev.egg-info/SOURCES.txt +1 -0
  13. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev.egg-info/requires.txt +1 -1
  14. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/pyproject.toml +2 -2
  15. omdev-0.0.0.dev151/omdev/pyproject/cli.py +0 -427
  16. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/LICENSE +0 -0
  17. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/MANIFEST.in +0 -0
  18. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/README.rst +0 -0
  19. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/.manifests.json +0 -0
  20. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/__about__.py +0 -0
  21. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/__init__.py +0 -0
  22. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/amalg/__init__.py +0 -0
  23. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/amalg/__main__.py +0 -0
  24. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/amalg/amalg.py +0 -0
  25. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/antlr/__init__.py +0 -0
  26. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/antlr/consts.py +0 -0
  27. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/antlr/gen.py +0 -0
  28. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/bracepy.py +0 -0
  29. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/__init__.py +0 -0
  30. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/__init__.py +0 -0
  31. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/cache.py +0 -0
  32. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/contexts.py +0 -0
  33. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/currents.py +0 -0
  34. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/fns.py +0 -0
  35. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/resolvers.py +0 -0
  36. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/storage.py +0 -0
  37. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/compute/types.py +0 -0
  38. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/__init__.py +0 -0
  39. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/actions.py +0 -0
  40. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/consts.py +0 -0
  41. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/defaults.py +0 -0
  42. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/manifests.py +0 -0
  43. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cache/data/specs.py +0 -0
  44. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/__init__.py +0 -0
  45. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_boilerplate.cc +0 -0
  46. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/LICENSE +0 -0
  47. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/__init__.py +0 -0
  48. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/build_ext.py +0 -0
  49. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
  50. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
  51. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/compilers/options.py +0 -0
  52. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
  53. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/dir_util.py +0 -0
  54. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/errors.py +0 -0
  55. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/extension.py +0 -0
  56. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/file_util.py +0 -0
  57. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/modified.py +0 -0
  58. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/spawn.py +0 -0
  59. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/sysconfig.py +0 -0
  60. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/util.py +0 -0
  61. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/_distutils/version.py +0 -0
  62. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/build.py +0 -0
  63. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/cmake.py +0 -0
  64. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/importhook.py +0 -0
  65. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/magic.py +0 -0
  66. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cexts/scan.py +0 -0
  67. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/classdot.py +0 -0
  68. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/__init__.py +0 -0
  69. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/__main__.py +0 -0
  70. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/_pathhack.py +0 -0
  71. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/clicli.py +0 -0
  72. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/install.py +0 -0
  73. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/main.py +0 -0
  74. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/managers.py +0 -0
  75. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cli/types.py +0 -0
  76. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/clipboard/__init__.py +0 -0
  77. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/clipboard/clipboard.py +0 -0
  78. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/clipboard/darwin_cf.py +0 -0
  79. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/clipboard/linux_x11.py +0 -0
  80. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/cmake.py +0 -0
  81. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/findimports.py +0 -0
  82. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/git.py +0 -0
  83. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/__init__.py +0 -0
  84. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/__main__.py +0 -0
  85. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/cli.py +0 -0
  86. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/inspect.py +0 -0
  87. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/providers.py +0 -0
  88. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/pyenv.py +0 -0
  89. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/resolvers.py +0 -0
  90. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/standalone.py +0 -0
  91. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/system.py +0 -0
  92. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/interp/types.py +0 -0
  93. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/magic/__init__.py +0 -0
  94. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/magic/find.py +0 -0
  95. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/magic/magic.py +0 -0
  96. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/magic/prepare.py +0 -0
  97. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/magic/styles.py +0 -0
  98. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/manifests/__init__.py +0 -0
  99. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/manifests/__main__.py +0 -0
  100. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/manifests/build.py +0 -0
  101. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/manifests/main.py +0 -0
  102. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/mypy/__init__.py +0 -0
  103. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/mypy/debug.py +0 -0
  104. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/packaging/__init__.py +0 -0
  105. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/packaging/marshal.py +0 -0
  106. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/packaging/names.py +0 -0
  107. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/packaging/requires.py +0 -0
  108. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/packaging/specifiers.py +0 -0
  109. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/packaging/versions.py +0 -0
  110. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pip.py +0 -0
  111. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/__init__.py +0 -0
  112. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/__main__.py +0 -0
  113. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/base.py +0 -0
  114. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/git.py +0 -0
  115. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/lite.py +0 -0
  116. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/main.py +0 -0
  117. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/manifests.py +0 -0
  118. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/precheck/scripts.py +0 -0
  119. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/ptk/__init__.py +0 -0
  120. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/ptk/apps/__init__.py +0 -0
  121. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/ptk/apps/ncdu.py +0 -0
  122. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pycharm/__init__.py +0 -0
  123. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pycharm/__main__.py +0 -0
  124. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pyproject/__init__.py +0 -0
  125. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pyproject/__main__.py +0 -0
  126. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pyproject/cexts.py +0 -0
  127. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pyproject/configs.py +0 -0
  128. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pyproject/pkg.py +0 -0
  129. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/pyproject/reqs.py +0 -0
  130. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/revisions.py +0 -0
  131. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/__init__.py +0 -0
  132. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/bumpversion.py +0 -0
  133. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/execrss.py +0 -0
  134. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/exectime.py +0 -0
  135. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/importtrace.py +0 -0
  136. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/slowcat.py +0 -0
  137. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/scripts/tmpexec.py +0 -0
  138. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/secrets.py +0 -0
  139. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tokens.py +0 -0
  140. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/toml/__init__.py +0 -0
  141. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/toml/parser.py +0 -0
  142. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/toml/writer.py +0 -0
  143. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/__init__.py +0 -0
  144. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/cloc.py +0 -0
  145. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/git.py +0 -0
  146. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/importscan.py +0 -0
  147. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/__init__.py +0 -0
  148. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/__main__.py +0 -0
  149. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/cli.py +0 -0
  150. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/formats.py +0 -0
  151. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/io.py +0 -0
  152. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/parsing.py +0 -0
  153. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/processing.py +0 -0
  154. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/json/rendering.py +0 -0
  155. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/mkrelimp.py +0 -0
  156. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/notebook.py +0 -0
  157. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/pawk/__init__.py +0 -0
  158. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/pawk/__main__.py +0 -0
  159. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/pawk/pawk.py +0 -0
  160. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/pip.py +0 -0
  161. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/prof.py +0 -0
  162. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/qr.py +0 -0
  163. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/tools/sqlrepl.py +0 -0
  164. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev/wheelfile.py +0 -0
  165. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev.egg-info/dependency_links.txt +0 -0
  166. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev.egg-info/entry_points.txt +0 -0
  167. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/omdev.egg-info/top_level.txt +0 -0
  168. {omdev-0.0.0.dev151 → omdev-0.0.0.dev153}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev151
3
+ Version: 0.0.0.dev153
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.dev151
15
+ Requires-Dist: omlish==0.0.0.dev153
16
16
  Provides-Extra: all
17
17
  Requires-Dist: black~=24.10; extra == "all"
18
18
  Requires-Dist: pycparser~=2.22; extra == "all"
@@ -24,8 +24,8 @@ import urllib.request
24
24
  from omlish import check
25
25
  from omlish import lang
26
26
  from omlish import marshal as msh
27
- from omlish import os as osu
28
27
  from omlish.formats import json
28
+ from omlish.os.files import touch
29
29
 
30
30
  from ... import git
31
31
  from .actions import Action
@@ -262,7 +262,7 @@ class Cache:
262
262
  if not os.path.isdir(item_dir):
263
263
  self._fetch_item(spec, item_dir)
264
264
 
265
- osu.touch(os.path.join(item_dir, 'accessed'))
265
+ touch(os.path.join(item_dir, 'accessed'))
266
266
 
267
267
  data_dir = os.path.join(item_dir, 'data')
268
268
  return self._return_val(spec, data_dir)
@@ -124,7 +124,7 @@ def _main() -> None:
124
124
 
125
125
  print(resp.data.link)
126
126
 
127
- Cli().call_and_exit()
127
+ Cli()(exit=True)
128
128
 
129
129
 
130
130
  # @omlish-manifest
@@ -141,7 +141,7 @@ class Cli(ap.Cli):
141
141
 
142
142
 
143
143
  def _main() -> None:
144
- Cli().call_and_exit()
144
+ Cli()(exit=True)
145
145
 
146
146
 
147
147
  if __name__ == '__main__':
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env python3
2
+ # @omlish-amalg ../scripts/pyproject.py
3
+ # ruff: noqa: UP006 UP007
4
+ """
5
+ TODO:
6
+ - check / tests, src dir sets
7
+ - ci
8
+ - build / package / publish / version roll
9
+ - {pkg_name: [src_dirs]}, default excludes, generate MANIFST.in, ...
10
+ - env vars - PYTHONPATH
11
+
12
+ See:
13
+ - https://pdm-project.org/en/latest/
14
+ - https://rye.astral.sh/philosophy/
15
+ - https://github.com/indygreg/python-build-standalone/blob/main/pythonbuild/cpython.py
16
+ - https://astral.sh/blog/uv
17
+ - https://github.com/jazzband/pip-tools
18
+ - https://github.com/Osiris-Team/1JPM
19
+ - https://github.com/brettcannon/microvenv
20
+ - https://github.com/pypa/pipx
21
+ - https://github.com/tox-dev/tox/
22
+ """
23
+ import argparse
24
+ import asyncio
25
+ import concurrent.futures as cf
26
+ import dataclasses as dc
27
+ import functools
28
+ import itertools
29
+ import multiprocessing as mp
30
+ import os.path
31
+ import shlex
32
+ import shutil
33
+ import sys
34
+ import typing as ta
35
+
36
+ from omlish.argparse.cli import ArgparseCli
37
+ from omlish.argparse.cli import argparse_arg
38
+ from omlish.argparse.cli import argparse_command
39
+ from omlish.lite.asyncio.subprocesses import asyncio_subprocess_check_call
40
+ from omlish.lite.cached import cached_nullary
41
+ from omlish.lite.check import check
42
+ from omlish.lite.logs import configure_standard_logging
43
+ from omlish.lite.runtime import check_runtime_version
44
+
45
+ from ..toml.parser import toml_loads
46
+ from .configs import PyprojectConfig
47
+ from .configs import PyprojectConfigPreparer
48
+ from .pkg import BasePyprojectPackageGenerator
49
+ from .pkg import PyprojectPackageGenerator
50
+ from .venvs import Venv
51
+
52
+
53
+ ##
54
+
55
+
56
+ @dc.dataclass(frozen=True)
57
+ class VersionsFile:
58
+ name: ta.Optional[str] = '.versions'
59
+
60
+ @staticmethod
61
+ def parse(s: str) -> ta.Mapping[str, str]:
62
+ return {
63
+ k: v
64
+ for l in s.splitlines()
65
+ if (sl := l.split('#')[0].strip())
66
+ for k, _, v in (sl.partition('='),)
67
+ }
68
+
69
+ @cached_nullary
70
+ def contents(self) -> ta.Mapping[str, str]:
71
+ if not self.name or not os.path.exists(self.name):
72
+ return {}
73
+ with open(self.name) as f:
74
+ s = f.read()
75
+ return self.parse(s)
76
+
77
+ @staticmethod
78
+ def get_pythons(d: ta.Mapping[str, str]) -> ta.Mapping[str, str]:
79
+ pfx = 'PYTHON_'
80
+ return {k[len(pfx):].lower(): v for k, v in d.items() if k.startswith(pfx)}
81
+
82
+ @cached_nullary
83
+ def pythons(self) -> ta.Mapping[str, str]:
84
+ return self.get_pythons(self.contents())
85
+
86
+
87
+ ##
88
+
89
+
90
+ @cached_nullary
91
+ def _script_rel_path() -> str:
92
+ cwd = os.getcwd()
93
+ if not (f := __file__).startswith(cwd):
94
+ raise OSError(f'file {f} not in {cwd}')
95
+ return f[len(cwd):].lstrip(os.sep)
96
+
97
+
98
+ ##
99
+
100
+
101
+ class Run:
102
+ def __init__(
103
+ self,
104
+ *,
105
+ raw_cfg: ta.Union[ta.Mapping[str, ta.Any], str, None] = None,
106
+ ) -> None:
107
+ super().__init__()
108
+
109
+ self._raw_cfg = raw_cfg
110
+
111
+ @cached_nullary
112
+ def raw_cfg(self) -> ta.Mapping[str, ta.Any]:
113
+ if self._raw_cfg is None:
114
+ with open('pyproject.toml') as f:
115
+ buf = f.read()
116
+ elif isinstance(self._raw_cfg, str):
117
+ buf = self._raw_cfg
118
+ else:
119
+ return self._raw_cfg
120
+ return toml_loads(buf)
121
+
122
+ @cached_nullary
123
+ def cfg(self) -> PyprojectConfig:
124
+ dct = self.raw_cfg()['tool']['omlish']['pyproject']
125
+ return PyprojectConfigPreparer(
126
+ python_versions=VersionsFile().pythons(),
127
+ ).prepare_config(dct)
128
+
129
+ @cached_nullary
130
+ def venvs(self) -> ta.Mapping[str, Venv]:
131
+ return {
132
+ n: Venv(n, c)
133
+ for n, c in self.cfg().venvs.items()
134
+ if not n.startswith('_')
135
+ }
136
+
137
+
138
+ ##
139
+
140
+
141
+ class PyprojectCli(ArgparseCli):
142
+ _docker_container = argparse_arg('--_docker_container', help=argparse.SUPPRESS)
143
+
144
+ @argparse_command(
145
+ argparse_arg('name'),
146
+ argparse_arg('-e', '--docker-env', action='append'),
147
+ argparse_arg('cmd', nargs='?'),
148
+ argparse_arg('args', nargs=argparse.REMAINDER),
149
+ )
150
+ async def venv(self) -> None:
151
+ venv = Run().venvs()[self.args.name]
152
+ if (sd := venv.cfg.docker) is not None and sd != (cd := self.args._docker_container): # noqa
153
+ script = ' '.join([
154
+ 'python3',
155
+ shlex.quote(_script_rel_path()),
156
+ f'--_docker_container={shlex.quote(sd)}',
157
+ *map(shlex.quote, sys.argv[1:]),
158
+ ])
159
+
160
+ docker_env = {
161
+ 'DOCKER_HOST_PLATFORM': os.environ.get('DOCKER_HOST_PLATFORM', sys.platform),
162
+ }
163
+ for e in self.args.docker_env or []:
164
+ if '=' in e:
165
+ k, _, v = e.split('=')
166
+ docker_env[k] = v
167
+ else:
168
+ docker_env[e] = os.environ.get(e, '')
169
+
170
+ await asyncio_subprocess_check_call(
171
+ 'docker',
172
+ 'compose',
173
+ '-f', 'docker/compose.yml',
174
+ 'exec',
175
+ *itertools.chain.from_iterable(
176
+ ('-e', f'{k}={v}')
177
+ for k, v in docker_env.items()
178
+ ),
179
+ '-it', sd,
180
+ 'bash', '--login', '-c', script,
181
+ )
182
+
183
+ return
184
+
185
+ cmd = self.args.cmd
186
+ if not cmd:
187
+ await venv.create()
188
+
189
+ elif cmd == 'python':
190
+ await venv.create()
191
+ os.execl(
192
+ (exe := venv.exe()),
193
+ exe,
194
+ *self.args.args,
195
+ )
196
+
197
+ elif cmd == 'exe':
198
+ await venv.create()
199
+ check.arg(not self.args.args)
200
+ print(venv.exe())
201
+
202
+ elif cmd == 'run':
203
+ await venv.create()
204
+ sh = check.not_none(shutil.which('bash'))
205
+ script = ' '.join(self.args.args)
206
+ if not script:
207
+ script = sh
208
+ os.execl(
209
+ (bash := check.not_none(sh)),
210
+ bash,
211
+ '-c',
212
+ f'. {venv.dir_name}/bin/activate && ' + script,
213
+ )
214
+
215
+ elif cmd == 'srcs':
216
+ check.arg(not self.args.args)
217
+ print('\n'.join(venv.srcs()))
218
+
219
+ elif cmd == 'test':
220
+ await venv.create()
221
+ await asyncio_subprocess_check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
222
+
223
+ else:
224
+ raise Exception(f'unknown subcommand: {cmd}')
225
+
226
+ @argparse_command(
227
+ argparse_arg('-b', '--build', action='store_true'),
228
+ argparse_arg('-r', '--revision', action='store_true'),
229
+ argparse_arg('-j', '--jobs', type=int),
230
+ argparse_arg('cmd', nargs='?'),
231
+ argparse_arg('args', nargs=argparse.REMAINDER),
232
+ )
233
+ async def pkg(self) -> None:
234
+ run = Run()
235
+
236
+ cmd = self.args.cmd
237
+ if not cmd:
238
+ raise Exception('must specify command')
239
+
240
+ elif cmd == 'gen':
241
+ pkgs_root = os.path.join('.pkg')
242
+
243
+ if os.path.exists(pkgs_root):
244
+ shutil.rmtree(pkgs_root)
245
+
246
+ build_output_dir = 'dist'
247
+ run_build = bool(self.args.build)
248
+ add_revision = bool(self.args.revision)
249
+
250
+ if run_build:
251
+ os.makedirs(build_output_dir, exist_ok=True)
252
+
253
+ pgs: ta.List[BasePyprojectPackageGenerator] = [
254
+ PyprojectPackageGenerator(
255
+ dir_name,
256
+ pkgs_root,
257
+ )
258
+ for dir_name in run.cfg().pkgs
259
+ ]
260
+ pgs = list(itertools.chain.from_iterable([pg, *pg.children()] for pg in pgs))
261
+
262
+ num_threads = self.args.jobs or int(max(mp.cpu_count() // 1.5, 1))
263
+ futs: ta.List[cf.Future]
264
+ with cf.ThreadPoolExecutor(num_threads) as ex:
265
+ futs = [ex.submit(pg.gen) for pg in pgs]
266
+ for fut in futs:
267
+ fut.result()
268
+
269
+ if run_build:
270
+ futs = [
271
+ ex.submit(functools.partial(
272
+ pg.build,
273
+ build_output_dir,
274
+ BasePyprojectPackageGenerator.BuildOpts(
275
+ add_revision=add_revision,
276
+ ),
277
+ ))
278
+ for pg in pgs
279
+ ]
280
+ for fut in futs:
281
+ fut.result()
282
+
283
+ else:
284
+ raise Exception(f'unknown subcommand: {cmd}')
285
+
286
+
287
+ ##
288
+
289
+
290
+ async def _async_main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
291
+ check_runtime_version()
292
+ configure_standard_logging()
293
+
294
+ await PyprojectCli(argv).async_cli_run()
295
+
296
+
297
+ def _main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
298
+ asyncio.run(_async_main(argv))
299
+
300
+
301
+ if __name__ == '__main__':
302
+ _main()
@@ -0,0 +1,114 @@
1
+ # ruff: noqa: UP006 UP007
2
+ import glob
3
+ import os.path
4
+ import typing as ta
5
+
6
+ from omlish.lite.asyncio.subprocesses import asyncio_subprocess_check_call
7
+ from omlish.lite.cached import async_cached_nullary
8
+ from omlish.lite.cached import cached_nullary
9
+ from omlish.lite.check import check
10
+ from omlish.lite.logs import log
11
+
12
+ from ..interp.resolvers import DEFAULT_INTERP_RESOLVER
13
+ from ..interp.types import InterpSpecifier
14
+ from .configs import VenvConfig
15
+ from .reqs import RequirementsRewriter
16
+
17
+
18
+ ##
19
+
20
+
21
+ class Venv:
22
+ def __init__(
23
+ self,
24
+ name: str,
25
+ cfg: VenvConfig,
26
+ ) -> None:
27
+ super().__init__()
28
+ self._name = name
29
+ self._cfg = cfg
30
+
31
+ @property
32
+ def cfg(self) -> VenvConfig:
33
+ return self._cfg
34
+
35
+ DIR_NAME = '.venvs'
36
+
37
+ @property
38
+ def dir_name(self) -> str:
39
+ return os.path.join(self.DIR_NAME, self._name)
40
+
41
+ @async_cached_nullary
42
+ async def interp_exe(self) -> str:
43
+ i = InterpSpecifier.parse(check.not_none(self._cfg.interp))
44
+ return check.not_none(await DEFAULT_INTERP_RESOLVER.resolve(i, install=True)).exe
45
+
46
+ @cached_nullary
47
+ def exe(self) -> str:
48
+ ve = os.path.join(self.dir_name, 'bin/python')
49
+ if not os.path.isfile(ve):
50
+ raise Exception(f'venv exe {ve} does not exist or is not a file!')
51
+ return ve
52
+
53
+ @async_cached_nullary
54
+ async def create(self) -> bool:
55
+ if os.path.exists(dn := self.dir_name):
56
+ if not os.path.isdir(dn):
57
+ raise Exception(f'{dn} exists but is not a directory!')
58
+ return False
59
+
60
+ log.info('Using interpreter %s', (ie := await self.interp_exe()))
61
+ await asyncio_subprocess_check_call(ie, '-m', 'venv', dn)
62
+
63
+ ve = self.exe()
64
+ uv = self._cfg.use_uv
65
+
66
+ await asyncio_subprocess_check_call(
67
+ ve,
68
+ '-m', 'pip',
69
+ 'install', '-v', '--upgrade',
70
+ 'pip',
71
+ 'setuptools',
72
+ 'wheel',
73
+ *(['uv'] if uv else []),
74
+ )
75
+
76
+ if sr := self._cfg.requires:
77
+ rr = RequirementsRewriter(self._name)
78
+ reqs = [rr.rewrite(req) for req in sr]
79
+
80
+ # TODO: automatically try slower uv download when it fails? lol
81
+ # Caused by: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: 30s). # noqa
82
+ # UV_CONCURRENT_DOWNLOADS=4 UV_HTTP_TIMEOUT=3600
83
+
84
+ await asyncio_subprocess_check_call(
85
+ ve,
86
+ '-m',
87
+ *(['uv'] if uv else []),
88
+ 'pip',
89
+ 'install',
90
+ *([] if uv else ['-v']),
91
+ *reqs,
92
+ )
93
+
94
+ return True
95
+
96
+ @staticmethod
97
+ def _resolve_srcs(raw: ta.List[str]) -> ta.List[str]:
98
+ out: list[str] = []
99
+ seen: ta.Set[str] = set()
100
+ for r in raw:
101
+ es: list[str]
102
+ if any(c in r for c in '*?'):
103
+ es = list(glob.glob(r, recursive=True))
104
+ else:
105
+ es = [r]
106
+ for e in es:
107
+ if e not in seen:
108
+ seen.add(e)
109
+ out.append(e)
110
+ return out
111
+
112
+ @cached_nullary
113
+ def srcs(self) -> ta.Sequence[str]:
114
+ return self._resolve_srcs(self._cfg.srcs or [])
@@ -55,7 +55,7 @@ VersionCmpLocalType = ta.Union['NegativeInfinityVersionType', _VersionCmpLocalTy
55
55
  VersionCmpKey = ta.Tuple[int, ta.Tuple[int, ...], VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpPrePostDevType, VersionCmpLocalType] # noqa
56
56
  VersionComparisonMethod = ta.Callable[[VersionCmpKey, VersionCmpKey], bool]
57
57
 
58
- # ../../omlish/lite/asyncio/asyncio.py
58
+ # ../../omlish/asyncs/asyncio/timeouts.py
59
59
  AwaitableT = ta.TypeVar('AwaitableT', bound=ta.Awaitable)
60
60
 
61
61
  # ../../omlish/lite/cached.py
@@ -76,7 +76,7 @@ UnparsedVersionVar = ta.TypeVar('UnparsedVersionVar', bound=UnparsedVersion)
76
76
  CallableVersionOperator = ta.Callable[['Version', str], bool]
77
77
 
78
78
  # ../../omlish/lite/subprocesses.py
79
- SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull']
79
+ SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
80
80
 
81
81
 
82
82
  ########################################
@@ -487,54 +487,7 @@ def canonicalize_version(
487
487
 
488
488
 
489
489
  ########################################
490
- # ../../../omlish/lite/asyncio/asyncio.py
491
-
492
-
493
- ##
494
-
495
-
496
- ASYNCIO_DEFAULT_BUFFER_LIMIT = 2 ** 16
497
-
498
-
499
- async def asyncio_open_stream_reader(
500
- f: ta.IO,
501
- loop: ta.Any = None,
502
- *,
503
- limit: int = ASYNCIO_DEFAULT_BUFFER_LIMIT,
504
- ) -> asyncio.StreamReader:
505
- if loop is None:
506
- loop = asyncio.get_running_loop()
507
-
508
- reader = asyncio.StreamReader(limit=limit, loop=loop)
509
- await loop.connect_read_pipe(
510
- lambda: asyncio.StreamReaderProtocol(reader, loop=loop),
511
- f,
512
- )
513
-
514
- return reader
515
-
516
-
517
- async def asyncio_open_stream_writer(
518
- f: ta.IO,
519
- loop: ta.Any = None,
520
- ) -> asyncio.StreamWriter:
521
- if loop is None:
522
- loop = asyncio.get_running_loop()
523
-
524
- writer_transport, writer_protocol = await loop.connect_write_pipe(
525
- lambda: asyncio.streams.FlowControlMixin(loop=loop),
526
- f,
527
- )
528
-
529
- return asyncio.streams.StreamWriter(
530
- writer_transport,
531
- writer_protocol,
532
- None,
533
- loop,
534
- )
535
-
536
-
537
- ##
490
+ # ../../../omlish/asyncs/asyncio/timeouts.py
538
491
 
539
492
 
540
493
  def asyncio_maybe_timeout(
@@ -2455,22 +2408,25 @@ async def asyncio_subprocess_communicate(
2455
2408
  return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
2456
2409
 
2457
2410
 
2458
- ##
2459
-
2460
-
2461
- async def _asyncio_subprocess_check_run(
2411
+ async def asyncio_subprocess_run(
2462
2412
  *args: str,
2463
2413
  input: ta.Any = None, # noqa
2464
2414
  timeout: ta.Optional[float] = None,
2415
+ check: bool = False, # noqa
2416
+ capture_output: ta.Optional[bool] = None,
2465
2417
  **kwargs: ta.Any,
2466
2418
  ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
2419
+ if capture_output:
2420
+ kwargs.setdefault('stdout', subprocess.PIPE)
2421
+ kwargs.setdefault('stderr', subprocess.PIPE)
2422
+
2467
2423
  args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
2468
2424
 
2469
2425
  proc: asyncio.subprocess.Process
2470
2426
  async with asyncio_subprocess_popen(*args, **kwargs) as proc:
2471
2427
  stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
2472
2428
 
2473
- if proc.returncode:
2429
+ if check and proc.returncode:
2474
2430
  raise subprocess.CalledProcessError(
2475
2431
  proc.returncode,
2476
2432
  args,
@@ -2481,6 +2437,9 @@ async def _asyncio_subprocess_check_run(
2481
2437
  return stdout, stderr
2482
2438
 
2483
2439
 
2440
+ ##
2441
+
2442
+
2484
2443
  async def asyncio_subprocess_check_call(
2485
2444
  *args: str,
2486
2445
  stdout: ta.Any = sys.stderr,
@@ -2488,11 +2447,12 @@ async def asyncio_subprocess_check_call(
2488
2447
  timeout: ta.Optional[float] = None,
2489
2448
  **kwargs: ta.Any,
2490
2449
  ) -> None:
2491
- _, _ = await _asyncio_subprocess_check_run(
2450
+ _, _ = await asyncio_subprocess_run(
2492
2451
  *args,
2493
2452
  stdout=stdout,
2494
2453
  input=input,
2495
2454
  timeout=timeout,
2455
+ check=True,
2496
2456
  **kwargs,
2497
2457
  )
2498
2458
 
@@ -2503,11 +2463,12 @@ async def asyncio_subprocess_check_output(
2503
2463
  timeout: ta.Optional[float] = None,
2504
2464
  **kwargs: ta.Any,
2505
2465
  ) -> bytes:
2506
- stdout, stderr = await _asyncio_subprocess_check_run(
2466
+ stdout, stderr = await asyncio_subprocess_run(
2507
2467
  *args,
2508
2468
  stdout=asyncio.subprocess.PIPE,
2509
2469
  input=input,
2510
2470
  timeout=timeout,
2471
+ check=True,
2511
2472
  **kwargs,
2512
2473
  )
2513
2474