omdev 0.0.0.dev151__py3-none-any.whl → 0.0.0.dev153__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 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
- 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)
omdev/imgur.py CHANGED
@@ -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
omdev/pycharm/cli.py CHANGED
@@ -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__':
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.lite.cached import async_cached_nullary
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 .reqs import RequirementsRewriter
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
- async def _venv_cmd(args) -> None:
244
- venv = Run().venvs()[args.name]
245
- if (sd := venv.cfg.docker) is not None and sd != (cd := args._docker_container): # noqa
246
- script = ' '.join([
247
- 'python3',
248
- shlex.quote(_script_rel_path()),
249
- f'--_docker_container={shlex.quote(sd)}',
250
- *map(shlex.quote, sys.argv[1:]),
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
- docker_env = {
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
- async def _pkg_cmd(args) -> None:
324
- run = Run()
215
+ elif cmd == 'srcs':
216
+ check.arg(not self.args.args)
217
+ print('\n'.join(venv.srcs()))
325
218
 
326
- cmd = args.cmd
327
- if not cmd:
328
- raise Exception('must specify command')
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
- elif cmd == 'gen':
331
- pkgs_root = os.path.join('.pkg')
223
+ else:
224
+ raise Exception(f'unknown subcommand: {cmd}')
332
225
 
333
- if os.path.exists(pkgs_root):
334
- shutil.rmtree(pkgs_root)
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
- build_output_dir = 'dist'
337
- run_build = bool(args.build)
338
- add_revision = bool(args.revision)
236
+ cmd = self.args.cmd
237
+ if not cmd:
238
+ raise Exception('must specify command')
339
239
 
340
- if run_build:
341
- os.makedirs(build_output_dir, exist_ok=True)
240
+ elif cmd == 'gen':
241
+ pkgs_root = os.path.join('.pkg')
342
242
 
343
- pgs: ta.List[BasePyprojectPackageGenerator] = [
344
- PyprojectPackageGenerator(
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
- num_threads = args.jobs or int(max(mp.cpu_count() // 1.5, 1))
353
- futs: ta.List[cf.Future]
354
- with cf.ThreadPoolExecutor(num_threads) as ex:
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
- futs = [
361
- ex.submit(functools.partial(
362
- pg.build,
363
- build_output_dir,
364
- BasePyprojectPackageGenerator.BuildOpts(
365
- add_revision=add_revision,
366
- ),
367
- ))
368
- for pg in pgs
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
- else:
374
- raise Exception(f'unknown subcommand: {cmd}')
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
- return parser
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
- parser = _build_parser()
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:
@@ -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 [])