omdev 0.0.0.dev150__py3-none-any.whl → 0.0.0.dev152__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.
- omdev/cache/data/cache.py +2 -2
- omdev/imgur.py +1 -1
- omdev/pycharm/cli.py +1 -1
- omdev/pyproject/cli.py +133 -258
- omdev/pyproject/venvs.py +114 -0
- omdev/scripts/interp.py +2 -2
- omdev/scripts/pyproject.py +456 -200
- omdev/tools/doc.py +1 -1
- omdev/tools/docker.py +2 -2
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/RECORD +15 -14
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/LICENSE +0 -0
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev150.dist-info → omdev-0.0.0.dev152.dist-info}/top_level.txt +0 -0
omdev/cache/data/cache.py
CHANGED
|
@@ -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
|
-
|
|
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)
|
omdev/imgur.py
CHANGED
omdev/pycharm/cli.py
CHANGED
omdev/pyproject/cli.py
CHANGED
|
@@ -25,7 +25,6 @@ import asyncio
|
|
|
25
25
|
import concurrent.futures as cf
|
|
26
26
|
import dataclasses as dc
|
|
27
27
|
import functools
|
|
28
|
-
import glob
|
|
29
28
|
import itertools
|
|
30
29
|
import multiprocessing as mp
|
|
31
30
|
import os.path
|
|
@@ -34,23 +33,21 @@ import shutil
|
|
|
34
33
|
import sys
|
|
35
34
|
import typing as ta
|
|
36
35
|
|
|
37
|
-
from omlish.
|
|
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
|
|
38
40
|
from omlish.lite.cached import cached_nullary
|
|
39
41
|
from omlish.lite.check import check
|
|
40
42
|
from omlish.lite.logs import configure_standard_logging
|
|
41
|
-
from omlish.lite.logs import log
|
|
42
43
|
from omlish.lite.runtime import check_runtime_version
|
|
43
|
-
from omlish.lite.subprocesses import subprocess_check_call
|
|
44
44
|
|
|
45
|
-
from ..interp.resolvers import DEFAULT_INTERP_RESOLVER
|
|
46
|
-
from ..interp.types import InterpSpecifier
|
|
47
45
|
from ..toml.parser import toml_loads
|
|
48
46
|
from .configs import PyprojectConfig
|
|
49
47
|
from .configs import PyprojectConfigPreparer
|
|
50
|
-
from .configs import VenvConfig
|
|
51
48
|
from .pkg import BasePyprojectPackageGenerator
|
|
52
49
|
from .pkg import PyprojectPackageGenerator
|
|
53
|
-
from .
|
|
50
|
+
from .venvs import Venv
|
|
54
51
|
|
|
55
52
|
|
|
56
53
|
##
|
|
@@ -101,105 +98,6 @@ def _script_rel_path() -> str:
|
|
|
101
98
|
##
|
|
102
99
|
|
|
103
100
|
|
|
104
|
-
class Venv:
|
|
105
|
-
def __init__(
|
|
106
|
-
self,
|
|
107
|
-
name: str,
|
|
108
|
-
cfg: VenvConfig,
|
|
109
|
-
) -> None:
|
|
110
|
-
super().__init__()
|
|
111
|
-
self._name = name
|
|
112
|
-
self._cfg = cfg
|
|
113
|
-
|
|
114
|
-
@property
|
|
115
|
-
def cfg(self) -> VenvConfig:
|
|
116
|
-
return self._cfg
|
|
117
|
-
|
|
118
|
-
DIR_NAME = '.venvs'
|
|
119
|
-
|
|
120
|
-
@property
|
|
121
|
-
def dir_name(self) -> str:
|
|
122
|
-
return os.path.join(self.DIR_NAME, self._name)
|
|
123
|
-
|
|
124
|
-
@async_cached_nullary
|
|
125
|
-
async def interp_exe(self) -> str:
|
|
126
|
-
i = InterpSpecifier.parse(check.not_none(self._cfg.interp))
|
|
127
|
-
return check.not_none(await DEFAULT_INTERP_RESOLVER.resolve(i, install=True)).exe
|
|
128
|
-
|
|
129
|
-
@cached_nullary
|
|
130
|
-
def exe(self) -> str:
|
|
131
|
-
ve = os.path.join(self.dir_name, 'bin/python')
|
|
132
|
-
if not os.path.isfile(ve):
|
|
133
|
-
raise Exception(f'venv exe {ve} does not exist or is not a file!')
|
|
134
|
-
return ve
|
|
135
|
-
|
|
136
|
-
@async_cached_nullary
|
|
137
|
-
async def create(self) -> bool:
|
|
138
|
-
if os.path.exists(dn := self.dir_name):
|
|
139
|
-
if not os.path.isdir(dn):
|
|
140
|
-
raise Exception(f'{dn} exists but is not a directory!')
|
|
141
|
-
return False
|
|
142
|
-
|
|
143
|
-
log.info('Using interpreter %s', (ie := await self.interp_exe()))
|
|
144
|
-
subprocess_check_call(ie, '-m', 'venv', dn)
|
|
145
|
-
|
|
146
|
-
ve = self.exe()
|
|
147
|
-
uv = self._cfg.use_uv
|
|
148
|
-
|
|
149
|
-
subprocess_check_call(
|
|
150
|
-
ve,
|
|
151
|
-
'-m', 'pip',
|
|
152
|
-
'install', '-v', '--upgrade',
|
|
153
|
-
'pip',
|
|
154
|
-
'setuptools',
|
|
155
|
-
'wheel',
|
|
156
|
-
*(['uv'] if uv else []),
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
if sr := self._cfg.requires:
|
|
160
|
-
rr = RequirementsRewriter(self._name)
|
|
161
|
-
reqs = [rr.rewrite(req) for req in sr]
|
|
162
|
-
|
|
163
|
-
# TODO: automatically try slower uv download when it fails? lol
|
|
164
|
-
# Caused by: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: 30s). # noqa
|
|
165
|
-
# UV_CONCURRENT_DOWNLOADS=4 UV_HTTP_TIMEOUT=3600
|
|
166
|
-
|
|
167
|
-
subprocess_check_call(
|
|
168
|
-
ve,
|
|
169
|
-
'-m',
|
|
170
|
-
*(['uv'] if uv else []),
|
|
171
|
-
'pip',
|
|
172
|
-
'install',
|
|
173
|
-
*([] if uv else ['-v']),
|
|
174
|
-
*reqs,
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
return True
|
|
178
|
-
|
|
179
|
-
@staticmethod
|
|
180
|
-
def _resolve_srcs(raw: ta.List[str]) -> ta.List[str]:
|
|
181
|
-
out: list[str] = []
|
|
182
|
-
seen: ta.Set[str] = set()
|
|
183
|
-
for r in raw:
|
|
184
|
-
es: list[str]
|
|
185
|
-
if any(c in r for c in '*?'):
|
|
186
|
-
es = list(glob.glob(r, recursive=True))
|
|
187
|
-
else:
|
|
188
|
-
es = [r]
|
|
189
|
-
for e in es:
|
|
190
|
-
if e not in seen:
|
|
191
|
-
seen.add(e)
|
|
192
|
-
out.append(e)
|
|
193
|
-
return out
|
|
194
|
-
|
|
195
|
-
@cached_nullary
|
|
196
|
-
def srcs(self) -> ta.Sequence[str]:
|
|
197
|
-
return self._resolve_srcs(self._cfg.srcs or [])
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
##
|
|
201
|
-
|
|
202
|
-
|
|
203
101
|
class Run:
|
|
204
102
|
def __init__(
|
|
205
103
|
self,
|
|
@@ -240,183 +138,160 @@ class Run:
|
|
|
240
138
|
##
|
|
241
139
|
|
|
242
140
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
+
)
|
|
252
182
|
|
|
253
|
-
|
|
254
|
-
'DOCKER_HOST_PLATFORM': os.environ.get('DOCKER_HOST_PLATFORM', sys.platform),
|
|
255
|
-
}
|
|
256
|
-
for e in args.docker_env or []:
|
|
257
|
-
if '=' in e:
|
|
258
|
-
k, _, v = e.split('=')
|
|
259
|
-
docker_env[k] = v
|
|
260
|
-
else:
|
|
261
|
-
docker_env[e] = os.environ.get(e, '')
|
|
262
|
-
|
|
263
|
-
subprocess_check_call(
|
|
264
|
-
'docker',
|
|
265
|
-
'compose',
|
|
266
|
-
'-f', 'docker/compose.yml',
|
|
267
|
-
'exec',
|
|
268
|
-
*itertools.chain.from_iterable(
|
|
269
|
-
('-e', f'{k}={v}')
|
|
270
|
-
for k, v in docker_env.items()
|
|
271
|
-
),
|
|
272
|
-
'-it', sd,
|
|
273
|
-
'bash', '--login', '-c', script,
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
return
|
|
277
|
-
|
|
278
|
-
cmd = args.cmd
|
|
279
|
-
if not cmd:
|
|
280
|
-
await venv.create()
|
|
281
|
-
|
|
282
|
-
elif cmd == 'python':
|
|
283
|
-
await venv.create()
|
|
284
|
-
os.execl(
|
|
285
|
-
(exe := venv.exe()),
|
|
286
|
-
exe,
|
|
287
|
-
*args.args,
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
elif cmd == 'exe':
|
|
291
|
-
await venv.create()
|
|
292
|
-
check.arg(not args.args)
|
|
293
|
-
print(venv.exe())
|
|
294
|
-
|
|
295
|
-
elif cmd == 'run':
|
|
296
|
-
await venv.create()
|
|
297
|
-
sh = check.not_none(shutil.which('bash'))
|
|
298
|
-
script = ' '.join(args.args)
|
|
299
|
-
if not script:
|
|
300
|
-
script = sh
|
|
301
|
-
os.execl(
|
|
302
|
-
(bash := check.not_none(sh)),
|
|
303
|
-
bash,
|
|
304
|
-
'-c',
|
|
305
|
-
f'. {venv.dir_name}/bin/activate && ' + script,
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
elif cmd == 'srcs':
|
|
309
|
-
check.arg(not args.args)
|
|
310
|
-
print('\n'.join(venv.srcs()))
|
|
311
|
-
|
|
312
|
-
elif cmd == 'test':
|
|
313
|
-
await venv.create()
|
|
314
|
-
subprocess_check_call(venv.exe(), '-m', 'pytest', *(args.args or []), *venv.srcs())
|
|
315
|
-
|
|
316
|
-
else:
|
|
317
|
-
raise Exception(f'unknown subcommand: {cmd}')
|
|
183
|
+
return
|
|
318
184
|
|
|
185
|
+
cmd = self.args.cmd
|
|
186
|
+
if not cmd:
|
|
187
|
+
await venv.create()
|
|
319
188
|
|
|
320
|
-
|
|
189
|
+
elif cmd == 'python':
|
|
190
|
+
await venv.create()
|
|
191
|
+
os.execl(
|
|
192
|
+
(exe := venv.exe()),
|
|
193
|
+
exe,
|
|
194
|
+
*self.args.args,
|
|
195
|
+
)
|
|
321
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
|
+
)
|
|
322
214
|
|
|
323
|
-
|
|
324
|
-
|
|
215
|
+
elif cmd == 'srcs':
|
|
216
|
+
check.arg(not self.args.args)
|
|
217
|
+
print('\n'.join(venv.srcs()))
|
|
325
218
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
219
|
+
elif cmd == 'test':
|
|
220
|
+
await venv.create()
|
|
221
|
+
await asyncio_subprocess_check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
|
|
329
222
|
|
|
330
|
-
|
|
331
|
-
|
|
223
|
+
else:
|
|
224
|
+
raise Exception(f'unknown subcommand: {cmd}')
|
|
332
225
|
|
|
333
|
-
|
|
334
|
-
|
|
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()
|
|
335
235
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
236
|
+
cmd = self.args.cmd
|
|
237
|
+
if not cmd:
|
|
238
|
+
raise Exception('must specify command')
|
|
339
239
|
|
|
340
|
-
|
|
341
|
-
os.
|
|
240
|
+
elif cmd == 'gen':
|
|
241
|
+
pkgs_root = os.path.join('.pkg')
|
|
342
242
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
dir_name,
|
|
346
|
-
pkgs_root,
|
|
347
|
-
)
|
|
348
|
-
for dir_name in run.cfg().pkgs
|
|
349
|
-
]
|
|
350
|
-
pgs = list(itertools.chain.from_iterable([pg, *pg.children()] for pg in pgs))
|
|
243
|
+
if os.path.exists(pkgs_root):
|
|
244
|
+
shutil.rmtree(pkgs_root)
|
|
351
245
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
futs = [ex.submit(pg.gen) for pg in pgs]
|
|
356
|
-
for fut in futs:
|
|
357
|
-
fut.result()
|
|
246
|
+
build_output_dir = 'dist'
|
|
247
|
+
run_build = bool(self.args.build)
|
|
248
|
+
add_revision = bool(self.args.revision)
|
|
358
249
|
|
|
359
250
|
if run_build:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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]
|
|
370
266
|
for fut in futs:
|
|
371
267
|
fut.result()
|
|
372
268
|
|
|
373
|
-
|
|
374
|
-
|
|
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()
|
|
375
282
|
|
|
283
|
+
else:
|
|
284
|
+
raise Exception(f'unknown subcommand: {cmd}')
|
|
376
285
|
|
|
377
|
-
##
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
def _build_parser() -> argparse.ArgumentParser:
|
|
381
|
-
parser = argparse.ArgumentParser()
|
|
382
|
-
parser.add_argument('--_docker_container', help=argparse.SUPPRESS)
|
|
383
|
-
|
|
384
|
-
subparsers = parser.add_subparsers()
|
|
385
|
-
|
|
386
|
-
#
|
|
387
|
-
|
|
388
|
-
parser_venv = subparsers.add_parser('venv')
|
|
389
|
-
parser_venv.add_argument('name')
|
|
390
|
-
parser_venv.add_argument('-e', '--docker-env', action='append')
|
|
391
|
-
parser_venv.add_argument('cmd', nargs='?')
|
|
392
|
-
parser_venv.add_argument('args', nargs=argparse.REMAINDER)
|
|
393
|
-
parser_venv.set_defaults(func=_venv_cmd)
|
|
394
|
-
|
|
395
|
-
#
|
|
396
|
-
|
|
397
|
-
parser_pkg = subparsers.add_parser('pkg')
|
|
398
|
-
parser_pkg.add_argument('-b', '--build', action='store_true')
|
|
399
|
-
parser_pkg.add_argument('-r', '--revision', action='store_true')
|
|
400
|
-
parser_pkg.add_argument('-j', '--jobs', type=int)
|
|
401
|
-
parser_pkg.add_argument('cmd', nargs='?')
|
|
402
|
-
parser_pkg.add_argument('args', nargs=argparse.REMAINDER)
|
|
403
|
-
parser_pkg.set_defaults(func=_pkg_cmd)
|
|
404
|
-
|
|
405
|
-
#
|
|
406
286
|
|
|
407
|
-
|
|
287
|
+
##
|
|
408
288
|
|
|
409
289
|
|
|
410
290
|
async def _async_main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
|
|
411
291
|
check_runtime_version()
|
|
412
292
|
configure_standard_logging()
|
|
413
293
|
|
|
414
|
-
|
|
415
|
-
args = parser.parse_args(argv)
|
|
416
|
-
if not getattr(args, 'func', None):
|
|
417
|
-
parser.print_help()
|
|
418
|
-
else:
|
|
419
|
-
await args.func(args)
|
|
294
|
+
await PyprojectCli(argv).async_cli_run()
|
|
420
295
|
|
|
421
296
|
|
|
422
297
|
def _main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
|
omdev/pyproject/venvs.py
ADDED
|
@@ -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 [])
|
omdev/scripts/interp.py
CHANGED
|
@@ -65,7 +65,7 @@ CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
|
|
65
65
|
# ../../omlish/lite/check.py
|
|
66
66
|
SizedT = ta.TypeVar('SizedT', bound=ta.Sized)
|
|
67
67
|
CheckMessage = ta.Union[str, ta.Callable[..., ta.Optional[str]], None] # ta.TypeAlias
|
|
68
|
-
CheckLateConfigureFn = ta.Callable[['Checks'], None]
|
|
68
|
+
CheckLateConfigureFn = ta.Callable[['Checks'], None] # ta.TypeAlias
|
|
69
69
|
CheckOnRaiseFn = ta.Callable[[Exception], None] # ta.TypeAlias
|
|
70
70
|
CheckExceptionFactory = ta.Callable[..., Exception] # ta.TypeAlias
|
|
71
71
|
CheckArgsRenderer = ta.Callable[..., ta.Optional[str]] # ta.TypeAlias
|
|
@@ -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
|
########################################
|