omdev 0.0.0.dev40__tar.gz → 0.0.0.dev42__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 (126) hide show
  1. {omdev-0.0.0.dev40/omdev.egg-info → omdev-0.0.0.dev42}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/.manifests.json +60 -0
  3. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/cache.py +74 -4
  4. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/specs.py +5 -0
  5. omdev-0.0.0.dev42/omdev/cli/clicmds.py +81 -0
  6. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cli/main.py +5 -13
  7. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/git.py +9 -5
  8. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/manifests/load.py +30 -14
  9. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/cli.py +28 -15
  10. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/pkg.py +38 -41
  11. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/scripts/execrss.py +7 -0
  12. omdev-0.0.0.dev42/omdev/scripts/exectime.py +24 -0
  13. {omdev-0.0.0.dev40/omdev/tools → omdev-0.0.0.dev42/omdev/scripts}/importtrace.py +7 -0
  14. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/scripts/pyproject.py +74 -61
  15. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/importscan.py +6 -0
  16. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42/omdev.egg-info}/PKG-INFO +2 -2
  17. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev.egg-info/SOURCES.txt +3 -1
  18. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev.egg-info/requires.txt +1 -1
  19. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/pyproject.toml +2 -2
  20. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/LICENSE +0 -0
  21. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/MANIFEST.in +0 -0
  22. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/README.rst +0 -0
  23. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/__about__.py +0 -0
  24. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/__init__.py +0 -0
  25. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/amalg/__init__.py +0 -0
  26. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/amalg/__main__.py +0 -0
  27. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/amalg/amalg.py +0 -0
  28. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/bracepy.py +0 -0
  29. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/__init__.py +0 -0
  30. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/__init__.py +0 -0
  31. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/cache.py +0 -0
  32. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/contexts.py +0 -0
  33. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/currents.py +0 -0
  34. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/fns.py +0 -0
  35. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/resolvers.py +0 -0
  36. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/storage.py +0 -0
  37. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/compute/types.py +0 -0
  38. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/__init__.py +0 -0
  39. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/actions.py +0 -0
  40. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/consts.py +0 -0
  41. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/defaults.py +0 -0
  42. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cache/data/manifests.py +0 -0
  43. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/__init__.py +0 -0
  44. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_boilerplate.cc +0 -0
  45. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/LICENSE +0 -0
  46. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/__init__.py +0 -0
  47. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/build_ext.py +0 -0
  48. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/compilers/__init__.py +0 -0
  49. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/compilers/ccompiler.py +0 -0
  50. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/compilers/options.py +0 -0
  51. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/compilers/unixccompiler.py +0 -0
  52. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/dir_util.py +0 -0
  53. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/errors.py +0 -0
  54. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/extension.py +0 -0
  55. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/file_util.py +0 -0
  56. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/modified.py +0 -0
  57. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/spawn.py +0 -0
  58. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/sysconfig.py +0 -0
  59. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/util.py +0 -0
  60. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/_distutils/version.py +0 -0
  61. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/build.py +0 -0
  62. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/cmake.py +0 -0
  63. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/importhook.py +0 -0
  64. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/magic.py +0 -0
  65. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cexts/scan.py +0 -0
  66. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/classdot.py +0 -0
  67. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cli/__init__.py +0 -0
  68. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cli/__main__.py +0 -0
  69. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cli/install.py +0 -0
  70. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cli/types.py +0 -0
  71. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/cmake.py +0 -0
  72. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/findimports.py +0 -0
  73. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/findmagic.py +0 -0
  74. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/__init__.py +0 -0
  75. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/__main__.py +0 -0
  76. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/cli.py +0 -0
  77. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/inspect.py +0 -0
  78. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/providers.py +0 -0
  79. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/pyenv.py +0 -0
  80. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/resolvers.py +0 -0
  81. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/standalone.py +0 -0
  82. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/system.py +0 -0
  83. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/interp/types.py +0 -0
  84. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/manifests/__init__.py +0 -0
  85. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/manifests/build.py +0 -0
  86. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/manifests/types.py +0 -0
  87. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/mypy/__init__.py +0 -0
  88. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/mypy/debug.py +0 -0
  89. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/packaging/__init__.py +0 -0
  90. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/packaging/names.py +0 -0
  91. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/packaging/requires.py +0 -0
  92. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/packaging/specifiers.py +0 -0
  93. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/packaging/versions.py +0 -0
  94. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/__init__.py +0 -0
  95. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/__main__.py +0 -0
  96. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/base.py +0 -0
  97. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/git.py +0 -0
  98. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/lite.py +0 -0
  99. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/precheck.py +0 -0
  100. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/precheck/scripts.py +0 -0
  101. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/__init__.py +0 -0
  102. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/__main__.py +0 -0
  103. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/cexts.py +0 -0
  104. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/configs.py +0 -0
  105. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/pyproject/reqs.py +0 -0
  106. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/revisions.py +0 -0
  107. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/scripts/__init__.py +0 -0
  108. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/scripts/bumpversion.py +0 -0
  109. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/scripts/interp.py +0 -0
  110. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/secrets.py +0 -0
  111. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tokens.py +0 -0
  112. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/toml/__init__.py +0 -0
  113. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/toml/parser.py +0 -0
  114. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/toml/writer.py +0 -0
  115. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/__init__.py +0 -0
  116. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/dockertools.py +0 -0
  117. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/gittools.py +0 -0
  118. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/piptools.py +0 -0
  119. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/proftools.py +0 -0
  120. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/rst.py +0 -0
  121. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/tools/sqlrepl.py +0 -0
  122. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev/wheelfile.py +0 -0
  123. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev.egg-info/dependency_links.txt +0 -0
  124. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev.egg-info/entry_points.txt +0 -0
  125. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/omdev.egg-info/top_level.txt +0 -0
  126. {omdev-0.0.0.dev40 → omdev-0.0.0.dev42}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev40
3
+ Version: 0.0.0.dev42
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.dev40
15
+ Requires-Dist: omlish==0.0.0.dev42
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser~=2.22; extra == "all"
18
18
  Requires-Dist: cffi~=1.17; extra == "all"
@@ -35,6 +35,18 @@
35
35
  }
36
36
  }
37
37
  },
38
+ {
39
+ "module": ".cli.clicmds",
40
+ "attr": "_CLI_MODULE",
41
+ "file": "omdev/cli/clicmds.py",
42
+ "line": 55,
43
+ "value": {
44
+ "$.cli.types.CliModule": {
45
+ "cmd_name": "cli",
46
+ "mod_name": "omdev.cli.clicmds"
47
+ }
48
+ }
49
+ },
38
50
  {
39
51
  "module": ".interp.__main__",
40
52
  "attr": "_CLI_MODULE",
@@ -71,6 +83,42 @@
71
83
  }
72
84
  }
73
85
  },
86
+ {
87
+ "module": ".scripts.execrss",
88
+ "attr": "_CLI_MODULE",
89
+ "file": "omdev/scripts/execrss.py",
90
+ "line": 11,
91
+ "value": {
92
+ "$.cli.types.CliModule": {
93
+ "cmd_name": "execrss",
94
+ "mod_name": "omdev.scripts.execrss"
95
+ }
96
+ }
97
+ },
98
+ {
99
+ "module": ".scripts.exectime",
100
+ "attr": "_CLI_MODULE",
101
+ "file": "omdev/scripts/exectime.py",
102
+ "line": 7,
103
+ "value": {
104
+ "$.cli.types.CliModule": {
105
+ "cmd_name": "exectime",
106
+ "mod_name": "omdev.scripts.exectime"
107
+ }
108
+ }
109
+ },
110
+ {
111
+ "module": ".scripts.importtrace",
112
+ "attr": "_CLI_MODULE",
113
+ "file": "omdev/scripts/importtrace.py",
114
+ "line": 481,
115
+ "value": {
116
+ "$.cli.types.CliModule": {
117
+ "cmd_name": "importtrace",
118
+ "mod_name": "omdev.scripts.importtrace"
119
+ }
120
+ }
121
+ },
74
122
  {
75
123
  "module": ".tools.dockertools",
76
124
  "attr": "_CLI_MODULE",
@@ -95,6 +143,18 @@
95
143
  }
96
144
  }
97
145
  },
146
+ {
147
+ "module": ".tools.importscan",
148
+ "attr": "_CLI_MODULE",
149
+ "file": "omdev/tools/importscan.py",
150
+ "line": 165,
151
+ "value": {
152
+ "$.cli.types.CliModule": {
153
+ "cmd_name": "importscan",
154
+ "mod_name": "omdev.tools.importscan"
155
+ }
156
+ }
157
+ },
98
158
  {
99
159
  "module": ".tools.piptools",
100
160
  "attr": "_CLI_MODULE",
@@ -10,11 +10,14 @@ TODO:
10
10
  - chaining? or is this compcache..
11
11
  - download resume ala hf_hub
12
12
  """
13
+ import contextlib
13
14
  import logging
14
15
  import os.path
15
16
  import shutil
16
17
  import subprocess
17
18
  import tempfile
19
+ import typing as ta
20
+ import urllib.error
18
21
  import urllib.parse
19
22
  import urllib.request
20
23
 
@@ -40,6 +43,57 @@ log = logging.getLogger(__name__)
40
43
  ##
41
44
 
42
45
 
46
+ def _url_retrieve(
47
+ req: urllib.request.Request,
48
+ out_file: str | None = None,
49
+ ) -> tuple[str, ta.Any]:
50
+ p = urllib.parse.urlparse(req.full_url)
51
+
52
+ with contextlib.ExitStack() as es:
53
+ fp = es.enter_context(contextlib.closing(urllib.request.urlopen(req))) # noqa
54
+
55
+ headers = fp.info()
56
+
57
+ # Just return the local path and the "headers" for file:// URLs. No sense in performing a copy unless requested.
58
+ if p.scheme == 'file' and not out_file:
59
+ return os.path.normpath(p.path), headers
60
+
61
+ success = False
62
+
63
+ tfp: ta.Any
64
+ if out_file:
65
+ tfp = es.enter_context(open(out_file, 'wb'))
66
+
67
+ else:
68
+ tfp = es.enter_context(tempfile.NamedTemporaryFile(delete=False))
69
+ out_file = tfp.name
70
+
71
+ def _cleanup():
72
+ if not success and out_file:
73
+ os.unlink(out_file)
74
+
75
+ es.enter_context(lang.defer(_cleanup)) # noqa
76
+
77
+ result = out_file, headers
78
+ size = -1
79
+ read = 0
80
+ if 'content-length' in headers:
81
+ size = int(headers['Content-Length'])
82
+
83
+ while block := fp.read(1024 * 8):
84
+ read += len(block)
85
+ tfp.write(block)
86
+
87
+ if size >= 0 and read < size:
88
+ raise urllib.error.ContentTooShortError(
89
+ f'retrieval incomplete: got only {read} out of {size} bytes',
90
+ result, # type: ignore
91
+ )
92
+
93
+ success = True
94
+ return result # type: ignore
95
+
96
+
43
97
  class Cache:
44
98
  def __init__(self, base_dir: str) -> None:
45
99
  super().__init__()
@@ -49,16 +103,32 @@ class Cache:
49
103
 
50
104
  #
51
105
 
52
- def _fetch_url(self, url: str, out_file: str) -> None:
106
+ def _fetch_url(
107
+ self,
108
+ url: str,
109
+ out_file: str,
110
+ *,
111
+ headers: ta.Mapping[str, str] | None = None,
112
+ ) -> None:
53
113
  log.info('Fetching url: %s -> %s', url, out_file)
54
114
 
55
- urllib.request.urlretrieve(url, out_file) # noqa
115
+ _url_retrieve(
116
+ urllib.request.Request( # noqa
117
+ url,
118
+ **(dict(headers=headers) if headers is not None else {}), # type: ignore
119
+ ),
120
+ out_file,
121
+ )
56
122
 
57
123
  def _fetch_into(self, spec: Spec, data_dir: str) -> None:
58
124
  log.info('Fetching spec: %s %r -> %s', spec.digest, spec, data_dir)
59
125
 
60
126
  if isinstance(spec, UrlSpec):
61
- self._fetch_url(spec.url, os.path.join(data_dir, spec.file_name_or_default))
127
+ self._fetch_url(
128
+ spec.url,
129
+ os.path.join(data_dir, spec.file_name_or_default),
130
+ headers=spec.headers,
131
+ )
62
132
 
63
133
  elif isinstance(spec, GithubContentSpec):
64
134
  for repo_file in spec.files:
@@ -104,7 +174,7 @@ class Cache:
104
174
 
105
175
  def _perform_action(self, action: Action, data_dir: str) -> None:
106
176
  if isinstance(action, ExtractAction):
107
- for f in action.files:
177
+ for f in [action.files] if isinstance(action.files, str) else action.files:
108
178
  file = os.path.join(data_dir, f)
109
179
  if not os.path.isfile(file):
110
180
  raise Exception(f'Not file: {file}')
@@ -1,4 +1,5 @@
1
1
  import hashlib
2
+ import operator
2
3
  import typing as ta
3
4
  import urllib.parse
4
5
  import urllib.request
@@ -59,6 +60,10 @@ class UrlSpec(Spec):
59
60
  url: str = dc.xfield(validate=lambda u: bool(urllib.parse.urlparse(u)))
60
61
  file_name: str | None = None
61
62
 
63
+ _: dc.KW_ONLY
64
+
65
+ headers: ta.Mapping[str, str] | None = dc.field(default=None) | msh.update_field_metadata(omit_if=operator.not_)
66
+
62
67
  @cached.property
63
68
  def file_name_or_default(self) -> str:
64
69
  if self.file_name is not None:
@@ -0,0 +1,81 @@
1
+ import argparse
2
+ import inspect
3
+ import os
4
+ import subprocess
5
+ import sys
6
+
7
+ from omlish import __about__
8
+
9
+ from . import install
10
+ from .types import CliModule
11
+
12
+
13
+ def _print_version(args) -> None:
14
+ print(__about__.__version__)
15
+
16
+
17
+ def _print_revision(args) -> None:
18
+ print(__about__.__revision__)
19
+
20
+
21
+ def _reinstall(args) -> None:
22
+ mod_name = globals()['__spec__'].name
23
+ tool_name = '.'.join([mod_name.partition('.')[0], 'tools', 'piptools'])
24
+
25
+ out = subprocess.check_output([
26
+ sys.executable,
27
+ '-m',
28
+ tool_name,
29
+ 'list-root-dists',
30
+ ]).decode()
31
+
32
+ deps = sorted(
33
+ ({s for l in out.splitlines() if (s := l.strip())} | set(args.extra_deps or []))
34
+ - {install.DEFAULT_CLI_PKG} # noqa
35
+ )
36
+
37
+ if deps:
38
+ print('Reinstalling with following additional dependencies:')
39
+ print('\n'.join(' ' + d for d in deps))
40
+ else:
41
+ print('No additional dependencies detected.')
42
+ print()
43
+ print('Continue with reinstall? (ctrl-c to cancel)')
44
+ input()
45
+
46
+ install_src = inspect.getsource(install)
47
+
48
+ os.execl(
49
+ sys.executable,
50
+ '-c',
51
+ install_src,
52
+ )
53
+
54
+
55
+ # @omlish-manifest
56
+ _CLI_MODULE = CliModule('cli', __name__)
57
+
58
+
59
+ def _main(argv=None) -> None:
60
+ parser = argparse.ArgumentParser()
61
+ subparsers = parser.add_subparsers()
62
+
63
+ parser_version = subparsers.add_parser('version')
64
+ parser_version.set_defaults(func=_print_version)
65
+
66
+ parser_revision = subparsers.add_parser('revision')
67
+ parser_revision.set_defaults(func=_print_revision)
68
+
69
+ parser_reinstall = subparsers.add_parser('reinstall')
70
+ parser_reinstall.add_argument('extra_deps', nargs='*')
71
+ parser_reinstall.set_defaults(func=_reinstall)
72
+
73
+ args = parser.parse_args(argv)
74
+ if not getattr(args, 'func', None):
75
+ parser.print_help()
76
+ else:
77
+ args.func(args)
78
+
79
+
80
+ if __name__ == '__main__':
81
+ _main()
@@ -1,5 +1,7 @@
1
1
  """
2
2
  TODO:
3
+ - cache ldr.discover() somehow if in uvx/pipx - very slow
4
+ - <venv-root>/.omdev-cli-manifest-cache.json - {pkg_name: manifests_json}
3
5
  - allow manually specifying manifest packages
4
6
  - https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts
5
7
  - https://packaging.python.org/en/latest/specifications/entry-points/#entry-points
@@ -8,8 +10,8 @@ import argparse
8
10
  import os
9
11
  import runpy
10
12
  import sys
13
+ import typing as ta
11
14
 
12
- from omlish import __about__
13
15
  from omlish import check
14
16
 
15
17
  from ..manifests.load import ManifestLoader
@@ -21,18 +23,7 @@ from .types import CliModule
21
23
  ##
22
24
 
23
25
 
24
- def _print_version() -> None:
25
- print(__about__.__version__)
26
-
27
-
28
- def _print_revision() -> None:
29
- print(__about__.__revision__)
30
-
31
-
32
- _CLI_FUNCS = [
33
- CliFunc('version', _print_version),
34
- CliFunc('revision', _print_revision),
35
- ]
26
+ _CLI_FUNCS: ta.Sequence[CliFunc] = []
36
27
 
37
28
 
38
29
  ##
@@ -46,6 +37,7 @@ def _main() -> None:
46
37
  ldr = ManifestLoader.from_entry_point(globals())
47
38
 
48
39
  pkgs = ldr.discover()
40
+
49
41
  if not pkgs:
50
42
  pkgs = []
51
43
  for n in os.listdir(os.getcwd()):
@@ -73,11 +73,15 @@ def get_git_revision(
73
73
  if cwd is None:
74
74
  cwd = os.getcwd()
75
75
 
76
- if subprocess.run([ # noqa
77
- 'git',
78
- 'rev-parse',
79
- '--is-inside-work-tree',
80
- ], stderr=subprocess.PIPE).returncode:
76
+ if subprocess.run( # noqa
77
+ [
78
+ 'git',
79
+ 'rev-parse',
80
+ '--is-inside-work-tree',
81
+ ],
82
+ stdout=subprocess.PIPE,
83
+ stderr=subprocess.PIPE,
84
+ ).returncode:
81
85
  return None
82
86
 
83
87
  has_untracked = bool(subprocess.check_output([
@@ -1,7 +1,12 @@
1
+ """
2
+ Should be kept somewhat lightweight - used in cli entrypoints.
3
+
4
+ TODO:
5
+ - persisted caching support - {pkg_name: manifests}
6
+ """
1
7
  # ruff: noqa: UP006 UP007
2
8
  import dataclasses as dc
3
9
  import importlib.machinery
4
- import importlib.metadata
5
10
  import importlib.resources
6
11
  import json
7
12
  import typing as ta
@@ -74,19 +79,7 @@ class ManifestLoader:
74
79
  self._cls_cache[key] = cls
75
80
  return cls
76
81
 
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)
82
+ def load_contents(self, obj: ta.Any, pkg_name: str) -> ta.Sequence[Manifest]:
90
83
  if not isinstance(obj, (list, tuple)):
91
84
  raise TypeError(obj)
92
85
 
@@ -105,6 +98,26 @@ class ManifestLoader:
105
98
 
106
99
  lst.append(m)
107
100
 
101
+ return lst
102
+
103
+ def load_raw(self, pkg_name: str) -> ta.Optional[ta.Sequence[Manifest]]:
104
+ try:
105
+ return self._raw_cache[pkg_name]
106
+ except KeyError:
107
+ pass
108
+
109
+ t = importlib.resources.files(pkg_name).joinpath('.manifests.json')
110
+ if not t.is_file():
111
+ self._raw_cache[pkg_name] = None
112
+ return None
113
+
114
+ src = t.read_text('utf-8')
115
+ obj = json.loads(src)
116
+ if not isinstance(obj, (list, tuple)):
117
+ raise TypeError(obj)
118
+
119
+ lst = self.load_contents(obj, pkg_name)
120
+
108
121
  self._raw_cache[pkg_name] = lst
109
122
  return lst
110
123
 
@@ -143,6 +156,9 @@ class ManifestLoader:
143
156
  ENTRY_POINT_GROUP = 'omlish.manifests'
144
157
 
145
158
  def discover(self) -> ta.Sequence[str]:
159
+ # This is a fat dep so do it late.
160
+ import importlib.metadata
161
+
146
162
  return [
147
163
  ep.value
148
164
  for ep in importlib.metadata.entry_points(group=self.ENTRY_POINT_GROUP)
@@ -47,6 +47,7 @@ from ..toml.parser import toml_loads
47
47
  from .configs import PyprojectConfig
48
48
  from .configs import PyprojectConfigPreparer
49
49
  from .configs import VenvConfig
50
+ from .pkg import BasePyprojectPackageGenerator
50
51
  from .pkg import PyprojectPackageGenerator
51
52
  from .reqs import RequirementsRewriter
52
53
 
@@ -338,25 +339,36 @@ def _pkg_cmd(args) -> None:
338
339
  if run_build:
339
340
  os.makedirs(build_output_dir, exist_ok=True)
340
341
 
341
- num_threads = max(mp.cpu_count() // 2, 1)
342
+ pgs: ta.List[BasePyprojectPackageGenerator] = [
343
+ PyprojectPackageGenerator(
344
+ dir_name,
345
+ pkgs_root,
346
+ )
347
+ for dir_name in run.cfg().pkgs
348
+ ]
349
+ pgs = list(itertools.chain.from_iterable([pg, *pg.children()] for pg in pgs))
350
+
351
+ num_threads = args.jobs or max(mp.cpu_count() // 2, 1)
352
+ futs: ta.List[cf.Future]
342
353
  with cf.ThreadPoolExecutor(num_threads) as ex:
343
- futs = [
344
- ex.submit(functools.partial(
345
- PyprojectPackageGenerator(
346
- dir_name,
347
- pkgs_root,
348
- ).gen,
349
- PyprojectPackageGenerator.GenOpts(
350
- run_build=run_build,
351
- build_output_dir=build_output_dir,
352
- add_revision=add_revision,
353
- ),
354
- ))
355
- for dir_name in run.cfg().pkgs
356
- ]
354
+ futs = [ex.submit(pg.gen) for pg in pgs]
357
355
  for fut in futs:
358
356
  fut.result()
359
357
 
358
+ if run_build:
359
+ futs = [
360
+ ex.submit(functools.partial(
361
+ pg.build,
362
+ build_output_dir,
363
+ BasePyprojectPackageGenerator.BuildOpts(
364
+ add_revision=add_revision,
365
+ ),
366
+ ))
367
+ for pg in pgs
368
+ ]
369
+ for fut in futs:
370
+ fut.result()
371
+
360
372
  else:
361
373
  raise Exception(f'unknown subcommand: {cmd}')
362
374
 
@@ -380,6 +392,7 @@ def _build_parser() -> argparse.ArgumentParser:
380
392
  parser_resolve = subparsers.add_parser('pkg')
381
393
  parser_resolve.add_argument('-b', '--build', action='store_true')
382
394
  parser_resolve.add_argument('-r', '--revision', action='store_true')
395
+ parser_resolve.add_argument('-j', '--jobs', type=int)
383
396
  parser_resolve.add_argument('cmd', nargs='?')
384
397
  parser_resolve.add_argument('args', nargs=argparse.REMAINDER)
385
398
  parser_resolve.set_defaults(func=_pkg_cmd)
@@ -186,12 +186,33 @@ class BasePyprojectPackageGenerator(abc.ABC):
186
186
 
187
187
  #
188
188
 
189
- def _run_build(
189
+ def children(self) -> ta.Sequence['BasePyprojectPackageGenerator']:
190
+ return []
191
+
192
+ #
193
+
194
+ def gen(self) -> str:
195
+ log.info('Generating pyproject package: %s -> %s (%s)', self._dir_name, self._pkgs_root, self._pkg_suffix)
196
+
197
+ self._pkg_dir()
198
+ self._write_git_ignore()
199
+ self._symlink_source_dir()
200
+ self._write_file_contents()
201
+ self._symlink_standard_files()
202
+
203
+ return self._pkg_dir()
204
+
205
+ #
206
+
207
+ @dc.dataclass(frozen=True)
208
+ class BuildOpts:
209
+ add_revision: bool = False
210
+ test: bool = False
211
+
212
+ def build(
190
213
  self,
191
- build_output_dir: ta.Optional[str] = None,
192
- *,
193
- add_revision: bool = False,
194
- test: bool = False,
214
+ output_dir: ta.Optional[str] = None,
215
+ opts: BuildOpts = BuildOpts(),
195
216
  ) -> None:
196
217
  subprocess_check_call(
197
218
  sys.executable,
@@ -202,10 +223,10 @@ class BasePyprojectPackageGenerator(abc.ABC):
202
223
 
203
224
  dist_dir = os.path.join(self._pkg_dir(), 'dist')
204
225
 
205
- if add_revision:
226
+ if opts.add_revision:
206
227
  GitRevisionAdder().add_to(dist_dir)
207
228
 
208
- if test:
229
+ if opts.test:
209
230
  for fn in os.listdir(dist_dir):
210
231
  tmp_dir = tempfile.mkdtemp()
211
232
 
@@ -224,34 +245,9 @@ class BasePyprojectPackageGenerator(abc.ABC):
224
245
  cwd=tmp_dir,
225
246
  )
226
247
 
227
- if build_output_dir is not None:
248
+ if output_dir is not None:
228
249
  for fn in os.listdir(dist_dir):
229
- shutil.copyfile(os.path.join(dist_dir, fn), os.path.join(build_output_dir, fn))
230
-
231
- #
232
-
233
- @dc.dataclass(frozen=True)
234
- class GenOpts:
235
- run_build: bool = False
236
- build_output_dir: ta.Optional[str] = None
237
- add_revision: bool = False
238
-
239
- def gen(self, opts: GenOpts = GenOpts()) -> str:
240
- log.info('Generating pyproject package: %s -> %s (%s)', self._dir_name, self._pkgs_root, self._pkg_suffix)
241
-
242
- self._pkg_dir()
243
- self._write_git_ignore()
244
- self._symlink_source_dir()
245
- self._write_file_contents()
246
- self._symlink_standard_files()
247
-
248
- if opts.run_build:
249
- self._run_build(
250
- opts.build_output_dir,
251
- add_revision=opts.add_revision,
252
- )
253
-
254
- return self._pkg_dir()
250
+ shutil.copyfile(os.path.join(dist_dir, fn), os.path.join(output_dir, fn))
255
251
 
256
252
 
257
253
  #
@@ -377,24 +373,25 @@ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
377
373
 
378
374
  #
379
375
 
380
- def gen(self, opts: BasePyprojectPackageGenerator.GenOpts = BasePyprojectPackageGenerator.GenOpts()) -> str:
381
- ret = super().gen(opts)
376
+ @cached_nullary
377
+ def children(self) -> ta.Sequence[BasePyprojectPackageGenerator]:
378
+ out: ta.List[BasePyprojectPackageGenerator] = []
382
379
 
383
380
  if self.build_specs().setuptools.get('cexts'):
384
- _PyprojectCextPackageGenerator(
381
+ out.append(_PyprojectCextPackageGenerator(
385
382
  self._dir_name,
386
383
  self._pkgs_root,
387
384
  pkg_suffix='-cext',
388
- ).gen(opts)
385
+ ))
389
386
 
390
387
  if self.build_specs().pyproject.get('cli_scripts'):
391
- _PyprojectCliPackageGenerator(
388
+ out.append(_PyprojectCliPackageGenerator(
392
389
  self._dir_name,
393
390
  self._pkgs_root,
394
391
  pkg_suffix='-cli',
395
- ).gen(opts)
392
+ ))
396
393
 
397
- return ret
394
+ return out
398
395
 
399
396
 
400
397
  #
@@ -8,6 +8,13 @@ def _get_rss() -> int:
8
8
  return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
9
9
 
10
10
 
11
+ # @omlish-manifest
12
+ _CLI_MODULE = {'$omdev.cli.types.CliModule': {
13
+ 'cmd_name': 'execrss',
14
+ 'mod_name': __name__,
15
+ }}
16
+
17
+
11
18
  def _main() -> None:
12
19
  [src] = sys.argv[1:]
13
20
  start = _get_rss()
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env python3
2
+ # @omlish-script
3
+ import sys
4
+ import time
5
+
6
+
7
+ # @omlish-manifest
8
+ _CLI_MODULE = {'$omdev.cli.types.CliModule': {
9
+ 'cmd_name': 'exectime',
10
+ 'mod_name': __name__,
11
+ }}
12
+
13
+
14
+ def _main() -> None:
15
+ [src] = sys.argv[1:]
16
+ co = compile(src, '<string>', 'exec')
17
+ start = time.time_ns()
18
+ exec(co)
19
+ end = time.time_ns()
20
+ print(end - start)
21
+
22
+
23
+ if __name__ == '__main__':
24
+ _main()
@@ -478,6 +478,13 @@ class SqliteWriter:
478
478
  ##
479
479
 
480
480
 
481
+ # @omlish-manifest
482
+ _CLI_MODULE = {'$omdev.cli.types.CliModule': {
483
+ 'cmd_name': 'importtrace',
484
+ 'mod_name': __name__,
485
+ }}
486
+
487
+
481
488
  def _main() -> None:
482
489
  if sys.version_info < REQUIRED_PYTHON_VERSION:
483
490
  raise EnvironmentError(f'Requires python {REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa