omdev 0.0.0.dev25__py3-none-any.whl → 0.0.0.dev27__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/manifests.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- !!! manifests! get-manifest, _manifest.py
2
+ !!! manifests! get-manifest, .manifest.json
3
3
  - dumb dicts, root keys are 'types'
4
4
  - get put in _manifest.py, root level dict or smth
5
5
  - IMPORT files w comment
@@ -10,7 +10,7 @@
10
10
  - # @omlish-manifest \n _CACHE_MANIFEST = {'cache': {'name': 'llm', …
11
11
  - also can do prechecks!
12
12
  """
13
- # ruff: noqa: UP006
13
+ # ruff: noqa: UP006 UP007
14
14
  # @omlish-lite
15
15
  import argparse
16
16
  import collections
@@ -22,10 +22,13 @@ import re
22
22
  import shlex
23
23
  import subprocess
24
24
  import sys
25
+ import time
25
26
  import typing as ta
26
27
 
27
28
  from omlish.lite.cached import cached_nullary
28
29
  from omlish.lite.json import json_dumps_pretty
30
+ from omlish.lite.logs import configure_standard_logging
31
+ from omlish.lite.logs import log
29
32
 
30
33
  from . import findmagic
31
34
 
@@ -84,13 +87,17 @@ def build_module_manifests(
84
87
  base: str,
85
88
  *,
86
89
  shell_wrap: bool = True,
90
+ warn_threshold_s: ta.Optional[float] = 1.,
87
91
  ) -> ta.Sequence[Manifest]:
88
- print((file, base))
92
+ log.info('Extracting manifests from file %s', file)
89
93
 
90
94
  if not file.endswith('.py'):
91
95
  raise Exception(file)
92
96
 
93
97
  mod_name = file.rpartition('.')[0].replace(os.sep, '.')
98
+ mod_base = mod_name.split('.')[0]
99
+ if mod_base != (first_dir := file.split(os.path.sep)[0]):
100
+ raise Exception(f'Unexpected module base: {mod_base=} != {first_dir=}')
94
101
 
95
102
  with open(os.path.join(base, file)) as f:
96
103
  src = f.read()
@@ -103,10 +110,10 @@ def build_module_manifests(
103
110
  raise Exception(nl)
104
111
 
105
112
  origins.append(ManifestOrigin(
106
- module=mod_name,
113
+ module='.'.join(['', *mod_name.split('.')[1:]]),
107
114
  attr=m.groupdict()['name'],
108
115
 
109
- file=file,
116
+ file=os.path.join(*os.path.split(file)[1:]), # noqa
110
117
  line=i + 1,
111
118
  ))
112
119
 
@@ -132,8 +139,15 @@ def build_module_manifests(
132
139
  if shell_wrap:
133
140
  args = ['sh', '-c', ' '.join(map(shlex.quote, args))]
134
141
 
142
+ start_time = time.time()
143
+
135
144
  subproc_out = subprocess.check_output(args)
136
145
 
146
+ end_time = time.time()
147
+
148
+ if warn_threshold_s is not None and (elapsed_time := (end_time - start_time)) >= warn_threshold_s:
149
+ log.warning('Manifest extraction took a long time: %s, %.2f s', file, elapsed_time)
150
+
137
151
  sp_lines = subproc_out.decode().strip().splitlines()
138
152
  if len(sp_lines) != 1:
139
153
  raise Exception('Unexpected subprocess output')
@@ -145,11 +159,17 @@ def build_module_manifests(
145
159
  out: ta.List[Manifest] = []
146
160
 
147
161
  for o in origins:
148
- manifest = dct[o.attr]
162
+ value = dct[o.attr]
163
+
164
+ if not (
165
+ isinstance(value, ta.Mapping) and
166
+ all(isinstance(k, str) and k.startswith('$') and len(k) > 1 for k in value)
167
+ ):
168
+ raise TypeError(f'Manifests must be mapping of strings starting with $: {value!r}')
149
169
 
150
170
  out.append(Manifest(
151
171
  **dc.asdict(o),
152
- value=manifest,
172
+ value=value,
153
173
  ))
154
174
 
155
175
  return out
@@ -175,7 +195,7 @@ def build_package_manifests(
175
195
  manifests.extend(build_module_manifests(os.path.relpath(file, base), base))
176
196
 
177
197
  if write:
178
- with open(os.path.join(pkg_dir, '_manifests.json'), 'w') as f:
198
+ with open(os.path.join(pkg_dir, '.manifests.json'), 'w') as f:
179
199
  f.write(json_dumps_pretty([dc.asdict(m) for m in manifests]))
180
200
  f.write('\n')
181
201
 
@@ -202,9 +222,11 @@ if __name__ == '__main__':
202
222
  write=args.write or False,
203
223
  )
204
224
  if not args.quiet:
205
- print(json_dumps_pretty(ms))
225
+ print(json_dumps_pretty([dc.asdict(m) for m in ms]))
206
226
 
207
227
  def _main(argv=None) -> None:
228
+ configure_standard_logging('INFO')
229
+
208
230
  parser = argparse.ArgumentParser()
209
231
  subparsers = parser.add_subparsers()
210
232
 
omdev/pyproject/pkg.py CHANGED
@@ -30,13 +30,14 @@ import dataclasses as dc
30
30
  import importlib
31
31
  import os.path
32
32
  import shutil
33
- import subprocess
34
33
  import sys
34
+ import tempfile
35
35
  import types
36
36
  import typing as ta
37
37
 
38
38
  from omlish.lite.cached import cached_nullary
39
39
  from omlish.lite.logs import log
40
+ from omlish.lite.subprocesses import subprocess_check_call
40
41
 
41
42
  from ..cexts.magic import CextMagic
42
43
  from ..findmagic import find_magic
@@ -138,6 +139,35 @@ class BasePyprojectPackageGenerator(abc.ABC):
138
139
 
139
140
  #
140
141
 
142
+ class _PkgData(ta.NamedTuple):
143
+ inc: ta.List[str]
144
+ exc: ta.List[str]
145
+
146
+ @cached_nullary
147
+ def _collect_pkg_data(self) -> _PkgData:
148
+ inc: ta.List[str] = []
149
+ exc: ta.List[str] = []
150
+
151
+ for p, ds, fs in os.walk(self._dir_name): # noqa
152
+ for f in fs:
153
+ if f != '.pkgdata':
154
+ continue
155
+ rp = os.path.relpath(p, self._dir_name)
156
+ log.info('Found pkgdata %s for pkg %s', rp, self._dir_name)
157
+ with open(os.path.join(p, f)) as fo:
158
+ src = fo.read()
159
+ for l in src.splitlines():
160
+ if not (l := l.strip()):
161
+ continue
162
+ if l.startswith('!'):
163
+ exc.append(os.path.join(rp, l[1:]))
164
+ else:
165
+ inc.append(os.path.join(rp, l))
166
+
167
+ return self._PkgData(inc, exc)
168
+
169
+ #
170
+
141
171
  @abc.abstractmethod
142
172
  def _write_file_contents(self) -> None:
143
173
  raise NotImplementedError
@@ -161,13 +191,12 @@ class BasePyprojectPackageGenerator(abc.ABC):
161
191
  build_output_dir: ta.Optional[str] = None,
162
192
  *,
163
193
  add_revision: bool = False,
194
+ test: bool = False,
164
195
  ) -> None:
165
- subprocess.check_call(
166
- [
167
- sys.executable,
168
- '-m',
169
- 'build',
170
- ],
196
+ subprocess_check_call(
197
+ sys.executable,
198
+ '-m',
199
+ 'build',
171
200
  cwd=self._pkg_dir(),
172
201
  )
173
202
 
@@ -176,6 +205,25 @@ class BasePyprojectPackageGenerator(abc.ABC):
176
205
  if add_revision:
177
206
  GitRevisionAdder().add_to(dist_dir)
178
207
 
208
+ if test:
209
+ for fn in os.listdir(dist_dir):
210
+ tmp_dir = tempfile.mkdtemp()
211
+
212
+ subprocess_check_call(
213
+ sys.executable,
214
+ '-m', 'venv',
215
+ 'test-install',
216
+ cwd=tmp_dir,
217
+ )
218
+
219
+ subprocess_check_call(
220
+ os.path.join(tmp_dir, 'test-install', 'bin', 'python3'),
221
+ '-m', 'pip',
222
+ 'install',
223
+ os.path.abspath(os.path.join(dist_dir, fn)),
224
+ cwd=tmp_dir,
225
+ )
226
+
179
227
  if build_output_dir is not None:
180
228
  for fn in os.listdir(dist_dir):
181
229
  shutil.copyfile(os.path.join(dist_dir, fn), os.path.join(build_output_dir, fn))
@@ -249,13 +297,56 @@ class PyprojectPackageGenerator(BasePyprojectPackageGenerator):
249
297
 
250
298
  #
251
299
 
252
- st = specs.setuptools
300
+ st = dict(specs.setuptools)
253
301
  pyp_dct['tool.setuptools'] = st
254
302
 
255
303
  st.pop('cexts', None)
256
304
 
257
- self._move_dict_key(st, 'find_packages', pyp_dct, 'tool.setuptools.packages.find')
258
- self._move_dict_key(st, 'package_data', pyp_dct, 'tool.setuptools.package-data')
305
+ #
306
+
307
+ # TODO: default
308
+ # find_packages = {
309
+ # 'include': [Project.name, f'{Project.name}.*'],
310
+ # 'exclude': [*SetuptoolsBase.find_packages['exclude']],
311
+ # }
312
+
313
+ fp = dict(st.pop('find_packages', {}))
314
+
315
+ pyp_dct['tool.setuptools.packages.find'] = fp
316
+
317
+ #
318
+
319
+ # TODO: default
320
+ # package_data = {
321
+ # '*': [
322
+ # '*.c',
323
+ # '*.cc',
324
+ # '*.h',
325
+ # '.manifests.json',
326
+ # 'LICENSE',
327
+ # ],
328
+ # }
329
+
330
+ pd = dict(st.pop('package_data', {}))
331
+ epd = dict(st.pop('exclude_package_data', {}))
332
+
333
+ cpd = self._collect_pkg_data()
334
+ if cpd.inc:
335
+ pd['*'] = [*pd.get('*', []), *sorted(set(cpd.inc))]
336
+ if cpd.exc:
337
+ epd['*'] = [*epd.get('*', []), *sorted(set(cpd.exc))]
338
+
339
+ if pd:
340
+ pyp_dct['tool.setuptools.package-data'] = pd
341
+ if epd:
342
+ pyp_dct['tool.setuptools.exclude-package-data'] = epd
343
+
344
+ #
345
+
346
+ # TODO: default
347
+ # manifest_in = [
348
+ # 'global-exclude **/conftest.py',
349
+ # ]
259
350
 
260
351
  mani_in = st.pop('manifest_in', None)
261
352
 
@@ -335,7 +426,7 @@ class _PyprojectCextPackageGenerator(BasePyprojectPackageGenerator):
335
426
 
336
427
  #
337
428
 
338
- st = specs.setuptools
429
+ st = dict(specs.setuptools)
339
430
  pyp_dct['tool.setuptools'] = st
340
431
 
341
432
  for k in [
omdev/revisions.py CHANGED
@@ -9,45 +9,22 @@ TODO:
9
9
  import argparse
10
10
  import io
11
11
  import os.path
12
- import subprocess
13
12
  import tarfile
14
13
  import typing as ta
15
14
  import zipfile
16
15
 
17
16
  from omlish.lite.cached import cached_nullary
17
+ from omlish.lite.check import check_non_empty_str
18
18
  from omlish.lite.logs import configure_standard_logging
19
19
  from omlish.lite.logs import log
20
20
 
21
+ from .git import get_git_revision
21
22
  from .wheelfile import WheelFile
22
23
 
23
24
 
24
25
  ##
25
26
 
26
27
 
27
- def get_git_revision() -> str:
28
- has_untracked = bool(subprocess.check_output([
29
- 'git',
30
- 'ls-files',
31
- '.',
32
- '--exclude-standard',
33
- '--others',
34
- ]).decode().strip())
35
-
36
- dirty_rev = subprocess.check_output([
37
- 'git',
38
- 'describe',
39
- '--match=NeVeRmAtCh',
40
- '--always',
41
- '--abbrev=40',
42
- '--dirty',
43
- ]).decode().strip()
44
-
45
- return dirty_rev + ('-untracked' if has_untracked else '')
46
-
47
-
48
- ##
49
-
50
-
51
28
  class GitRevisionAdder:
52
29
  def __init__(
53
30
  self,
@@ -62,7 +39,7 @@ class GitRevisionAdder:
62
39
  def revision(self) -> str:
63
40
  if self._given_revision is not None:
64
41
  return self._given_revision
65
- return get_git_revision()
42
+ return check_non_empty_str(get_git_revision())
66
43
 
67
44
  REVISION_ATTR = '__revision__'
68
45
 
omdev/scripts/interp.py CHANGED
@@ -3,7 +3,7 @@
3
3
  # @omlish-lite
4
4
  # @omlish-script
5
5
  # @omdev-amalg-output ../interp/cli.py
6
- # ruff: noqa: UP007
6
+ # ruff: noqa: N802 UP006 UP007 UP036
7
7
  """
8
8
  TODO:
9
9
  - partial best-matches - '3.12'
@@ -31,6 +31,17 @@ import threading
31
31
  import typing as ta
32
32
 
33
33
 
34
+ ########################################
35
+
36
+
37
+ if sys.version_info < (3, 8):
38
+ raise OSError(
39
+ f'Requires python (3, 8), got {sys.version_info} from {sys.executable}') # noqa
40
+
41
+
42
+ ########################################
43
+
44
+
34
45
  # ../../versioning/versions.py
35
46
  VersionLocalType = ta.Tuple[ta.Union[int, str], ...]
36
47
  VersionCmpPrePostDevType = ta.Union['InfinityVersionType', 'NegativeInfinityVersionType', ta.Tuple[str, int]]
@@ -72,7 +83,6 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
72
83
  # Apache License, Version 2.0, and the BSD License. See the LICENSE file in the root of this repository for complete
73
84
  # details.
74
85
  # https://github.com/pypa/packaging/blob/2c885fe91a54559e2382902dce28428ad2887be5/src/packaging/version.py
75
- # ruff: noqa: UP006 UP007
76
86
 
77
87
 
78
88
  ##
@@ -479,7 +489,6 @@ class cached_nullary: # noqa
479
489
 
480
490
  ########################################
481
491
  # ../../../omlish/lite/check.py
482
- # ruff: noqa: UP006 UP007
483
492
 
484
493
 
485
494
  def check_isinstance(v: T, spec: ta.Union[ta.Type[T], tuple]) -> T:
@@ -506,6 +515,12 @@ def check_not(v: ta.Any) -> None:
506
515
  return v
507
516
 
508
517
 
518
+ def check_non_empty_str(v: ta.Optional[str]) -> str:
519
+ if not v:
520
+ raise ValueError
521
+ return v
522
+
523
+
509
524
  ########################################
510
525
  # ../../../omlish/lite/json.py
511
526
 
@@ -539,7 +554,6 @@ json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON
539
554
 
540
555
  ########################################
541
556
  # ../../../omlish/lite/reflect.py
542
- # ruff: noqa: UP006
543
557
 
544
558
 
545
559
  _GENERIC_ALIAS_TYPES = (
@@ -640,7 +654,6 @@ def is_sunder(name: str) -> bool:
640
654
  # Apache License, Version 2.0, and the BSD License. See the LICENSE file in the root of this repository for complete
641
655
  # details.
642
656
  # https://github.com/pypa/packaging/blob/2c885fe91a54559e2382902dce28428ad2887be5/src/packaging/specifiers.py
643
- # ruff: noqa: UP006 UP007
644
657
 
645
658
 
646
659
  ##
@@ -1146,7 +1159,6 @@ TODO:
1146
1159
  - translate json keys
1147
1160
  - debug
1148
1161
  """
1149
- # ruff: noqa: UP006 UP007 N802
1150
1162
 
1151
1163
 
1152
1164
  log = logging.getLogger(__name__)
@@ -1345,46 +1357,51 @@ def configure_standard_logging(
1345
1357
  *,
1346
1358
  json: bool = False,
1347
1359
  target: ta.Optional[logging.Logger] = None,
1348
- no_check: bool = False,
1360
+ force: bool = False,
1349
1361
  ) -> ta.Optional[StandardLogHandler]:
1350
- if target is None:
1351
- target = logging.root
1362
+ logging._acquireLock() # type: ignore # noqa
1363
+ try:
1364
+ if target is None:
1365
+ target = logging.root
1352
1366
 
1353
- #
1367
+ #
1354
1368
 
1355
- if not no_check:
1356
- if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
1357
- return None
1369
+ if not force:
1370
+ if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
1371
+ return None
1358
1372
 
1359
- #
1373
+ #
1360
1374
 
1361
- handler = logging.StreamHandler()
1375
+ handler = logging.StreamHandler()
1362
1376
 
1363
- #
1377
+ #
1364
1378
 
1365
- formatter: logging.Formatter
1366
- if json:
1367
- formatter = JsonLogFormatter()
1368
- else:
1369
- formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
1370
- handler.setFormatter(formatter)
1379
+ formatter: logging.Formatter
1380
+ if json:
1381
+ formatter = JsonLogFormatter()
1382
+ else:
1383
+ formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
1384
+ handler.setFormatter(formatter)
1371
1385
 
1372
- #
1386
+ #
1373
1387
 
1374
- handler.addFilter(TidLogFilter())
1388
+ handler.addFilter(TidLogFilter())
1375
1389
 
1376
- #
1390
+ #
1377
1391
 
1378
- target.addHandler(handler)
1392
+ target.addHandler(handler)
1379
1393
 
1380
- #
1394
+ #
1381
1395
 
1382
- if level is not None:
1383
- target.setLevel(level)
1396
+ if level is not None:
1397
+ target.setLevel(level)
1384
1398
 
1385
- #
1399
+ #
1400
+
1401
+ return StandardLogHandler(handler)
1386
1402
 
1387
- return StandardLogHandler(handler)
1403
+ finally:
1404
+ logging._releaseLock() # type: ignore # noqa
1388
1405
 
1389
1406
 
1390
1407
  ########################################
@@ -1407,7 +1424,6 @@ def check_runtime_version() -> None:
1407
1424
 
1408
1425
  ########################################
1409
1426
  # ../types.py
1410
- # ruff: noqa: UP006
1411
1427
 
1412
1428
 
1413
1429
  # See https://peps.python.org/pep-3149/
@@ -1499,7 +1515,6 @@ class Interp:
1499
1515
 
1500
1516
  ########################################
1501
1517
  # ../../../omlish/lite/subprocesses.py
1502
- # ruff: noqa: UP006 UP007
1503
1518
 
1504
1519
 
1505
1520
  ##
@@ -1606,7 +1621,6 @@ def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
1606
1621
 
1607
1622
  ########################################
1608
1623
  # ../inspect.py
1609
- # ruff: noqa: UP006 UP007
1610
1624
 
1611
1625
 
1612
1626
  @dc.dataclass(frozen=True)
@@ -1770,7 +1784,6 @@ TODO:
1770
1784
  - optionally install / upgrade pyenv itself
1771
1785
  - new vers dont need these custom mac opts, only run on old vers
1772
1786
  """
1773
- # ruff: noqa: UP006 UP007
1774
1787
 
1775
1788
 
1776
1789
  ##
@@ -2165,7 +2178,6 @@ TODO:
2165
2178
  - python, python3, python3.12, ...
2166
2179
  - check if path py's are venvs: sys.prefix != sys.base_prefix
2167
2180
  """
2168
- # ruff: noqa: UP006 UP007
2169
2181
 
2170
2182
 
2171
2183
  ##
@@ -2274,7 +2286,6 @@ class SystemInterpProvider(InterpProvider):
2274
2286
 
2275
2287
  ########################################
2276
2288
  # ../resolvers.py
2277
- # ruff: noqa: UP006 UP007
2278
2289
 
2279
2290
 
2280
2291
  INTERP_PROVIDER_TYPES_BY_NAME: ta.Mapping[str, ta.Type[InterpProvider]] = {