omdev 0.0.0.dev13__tar.gz → 0.0.0.dev14__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.
Files changed (82) hide show
  1. {omdev-0.0.0.dev13/omdev.egg-info → omdev-0.0.0.dev14}/PKG-INFO +2 -2
  2. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/amalg/amalg.py +53 -3
  3. {omdev-0.0.0.dev13/omdev/scripts → omdev-0.0.0.dev14/omdev}/bracepy.py +14 -14
  4. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/cmake.py +25 -9
  5. omdev-0.0.0.dev14/omdev/exts/importhook.py +134 -0
  6. omdev-0.0.0.dev14/omdev/findimports.py +88 -0
  7. {omdev-0.0.0.dev13/omdev/scripts → omdev-0.0.0.dev14/omdev}/findmagic.py +24 -20
  8. omdev-0.0.0.dev14/omdev/precheck/__main__.py +4 -0
  9. omdev-0.0.0.dev14/omdev/precheck/precheck.py +316 -0
  10. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/pyproject/cli.py +3 -0
  11. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/pyproject/pkg.py +13 -2
  12. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/scripts/execrss.py +1 -0
  13. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/scripts/interp.py +2 -0
  14. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/scripts/pyproject.py +590 -184
  15. omdev-0.0.0.dev14/omdev/tools/__init__.py +0 -0
  16. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/tools/revisions.py +65 -48
  17. {omdev-0.0.0.dev13/omdev/scripts → omdev-0.0.0.dev14/omdev/tools}/traceimport.py +3 -1
  18. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14/omdev.egg-info}/PKG-INFO +2 -2
  19. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev.egg-info/SOURCES.txt +7 -4
  20. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev.egg-info/requires.txt +1 -1
  21. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/pyproject.toml +2 -2
  22. omdev-0.0.0.dev13/omdev/exts/importhook.py +0 -88
  23. omdev-0.0.0.dev13/omdev/scripts/findimports.py +0 -62
  24. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/LICENSE +0 -0
  25. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/MANIFEST.in +0 -0
  26. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/README.rst +0 -0
  27. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/__about__.py +0 -0
  28. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/__init__.py +0 -0
  29. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/amalg/__init__.py +0 -0
  30. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/amalg/__main__.py +0 -0
  31. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/classdot.py +0 -0
  32. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/cmake.py +0 -0
  33. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/__init__.py +0 -0
  34. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/__init__.py +0 -0
  35. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/build_ext.py +0 -0
  36. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/compilers/__init__.py +0 -0
  37. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/compilers/ccompiler.py +0 -0
  38. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/compilers/options.py +0 -0
  39. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/compilers/unixccompiler.py +0 -0
  40. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/dir_util.py +0 -0
  41. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/errors.py +0 -0
  42. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/extension.py +0 -0
  43. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/file_util.py +0 -0
  44. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/modified.py +0 -0
  45. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/spawn.py +0 -0
  46. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/sysconfig.py +0 -0
  47. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/util.py +0 -0
  48. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/_distutils/version.py +0 -0
  49. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/build.py +0 -0
  50. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/exts/scan.py +0 -0
  51. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/__init__.py +0 -0
  52. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/__main__.py +0 -0
  53. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/cli.py +0 -0
  54. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/inspect.py +0 -0
  55. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/providers.py +0 -0
  56. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/pyenv.py +0 -0
  57. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/resolvers.py +0 -0
  58. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/standalone.py +0 -0
  59. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/system.py +0 -0
  60. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/interp/types.py +0 -0
  61. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/mypy/__init__.py +0 -0
  62. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/mypy/debug.py +0 -0
  63. {omdev-0.0.0.dev13/omdev/scripts → omdev-0.0.0.dev14/omdev/precheck}/__init__.py +0 -0
  64. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/pyproject/__init__.py +0 -0
  65. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/pyproject/__main__.py +0 -0
  66. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/pyproject/configs.py +0 -0
  67. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/pyproject/ext.py +0 -0
  68. {omdev-0.0.0.dev13/omdev/tools → omdev-0.0.0.dev14/omdev/scripts}/__init__.py +0 -0
  69. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/tokens.py +0 -0
  70. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/toml/__init__.py +0 -0
  71. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/toml/parser.py +0 -0
  72. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/toml/writer.py +0 -0
  73. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/tools/dockertools.py +0 -0
  74. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/tools/gittools.py +0 -0
  75. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/tools/sqlrepl.py +0 -0
  76. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/versioning/__init__.py +0 -0
  77. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/versioning/specifiers.py +0 -0
  78. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/versioning/versions.py +0 -0
  79. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev/wheelfile.py +0 -0
  80. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev.egg-info/dependency_links.txt +0 -0
  81. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/omdev.egg-info/top_level.txt +0 -0
  82. {omdev-0.0.0.dev13 → omdev-0.0.0.dev14}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev13
3
+ Version: 0.0.0.dev14
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.dev13
15
+ Requires-Dist: omlish==0.0.0.dev14
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser~=2.22; extra == "all"
18
18
  Requires-Dist: cffi~=1.17; extra == "all"
@@ -8,6 +8,7 @@ TODO:
8
8
  - more sanity checks lol
9
9
  - flake8 / ruff mgmt
10
10
  - typealias - support # noqa, other comments, and lamely support multiline by just stealing lines till it parses
11
+ - remove `if __name__ == '__main__':` blocks - thus, convention: no def _main() for these
11
12
 
12
13
  See:
13
14
  - https://github.com/xonsh/amalgamate - mine is for portability not speed, and doesn't try to work on unmodified code
@@ -27,6 +28,7 @@ import dataclasses as dc
27
28
  import io
28
29
  import logging
29
30
  import os.path
31
+ import re
30
32
  import typing as ta
31
33
 
32
34
  import tokenize_rt as trt
@@ -35,6 +37,7 @@ from omlish import check
35
37
  from omlish import collections as col
36
38
  from omlish import logs
37
39
 
40
+ from .. import findmagic
38
41
  from .. import tokens as tks
39
42
 
40
43
 
@@ -63,6 +66,47 @@ def split_header_lines(lines: ta.Iterable[Tokens]) -> tuple[list[Tokens], list[T
63
66
  return ws, nws
64
67
 
65
68
 
69
+ #
70
+
71
+
72
+ IF_MAIN_PAT = re.compile(r'if\s+__name__\s+==\s+[\'"]__main__[\'"]\s*:')
73
+
74
+
75
+ def strip_main_lines(cls: ta.Sequence[Tokens]) -> list[Tokens]:
76
+ out = []
77
+
78
+ for l in (it := iter(cls)):
79
+ if IF_MAIN_PAT.fullmatch(tks.join_toks(l).strip()):
80
+ for l in it:
81
+ if l[0].name not in ('INDENT', 'UNIMPORTANT_WS') and tks.join_toks(l).strip():
82
+ break
83
+ else:
84
+ out.append(l)
85
+
86
+ return out
87
+
88
+
89
+ #
90
+
91
+
92
+ STRIPPED_HEADER_MAGICS = [
93
+ '# @omlish-lite',
94
+ ]
95
+
96
+ STRIPPED_HEADER_PATS = [findmagic.compile_magic_pat(m) for m in STRIPPED_HEADER_MAGICS]
97
+
98
+
99
+ def strip_header_lines(hls: ta.Sequence[Tokens]) -> list[Tokens]:
100
+ if hls and tks.join_toks(hls[0]).startswith('#!'):
101
+ hls = hls[1:]
102
+ out = []
103
+ for l in hls:
104
+ ls = tks.join_toks(l)
105
+ if not any(p.fullmatch(ls) for p in STRIPPED_HEADER_PATS):
106
+ out.append(l)
107
+ return out
108
+
109
+
66
110
  ##
67
111
 
68
112
 
@@ -238,6 +282,8 @@ def make_src_file(
238
282
 
239
283
  hls, cls = split_header_lines(lines)
240
284
 
285
+ hls = strip_header_lines(hls)
286
+
241
287
  imps: list[Import] = []
242
288
  tys: list[Typing] = []
243
289
  ctls: list[Tokens] = []
@@ -328,10 +374,11 @@ def gen_amalg(
328
374
  else:
329
375
  ogf = os.path.basename(main_path)
330
376
  nhls = []
331
- if hls[0].startswith('#!'):
332
- nhls.append(hls.pop(0))
333
377
  nhls.extend([
378
+ '#!/usr/bin/env python3\n',
334
379
  '# noinspection DuplicatedCode\n',
380
+ '# @omlish-lite\n',
381
+ '# @omlish-script\n',
335
382
  f'{OUTPUT_COMMENT.strip()} {ogf}\n',
336
383
  ])
337
384
  hls = [*nhls, *hls]
@@ -398,7 +445,10 @@ def gen_amalg(
398
445
  if f is not mf and f.header_lines:
399
446
  out.write(tks.join_lines(f.header_lines))
400
447
  out.write(f'\n\n')
401
- sf_src = tks.join_lines(f.content_lines)
448
+ cls = f.content_lines
449
+ if f is not mf:
450
+ cls = strip_main_lines(cls)
451
+ sf_src = tks.join_lines(cls)
402
452
  out.write(sf_src.strip())
403
453
  if i < len(sfs) - 1:
404
454
  out.write('\n\n\n')
@@ -1,4 +1,5 @@
1
- # !/usr/bin/env python3
1
+ #!/usr/bin/env python3
2
+ # @omlish-lite
2
3
  """
3
4
  https://github.com/umlet/pwk/blob/dc23b3400108a71947a695f1fa1df0f514b42528/pwk
4
5
  """
@@ -84,22 +85,21 @@ def translate_brace_python(
84
85
  return ret.getvalue()
85
86
 
86
87
 
87
- def _main(argv=None) -> None:
88
- import argparse
89
-
90
- parser = argparse.ArgumentParser()
91
- parser.add_argument('-x', '--exec', action='store_true')
92
- parser.add_argument('cmd')
88
+ if __name__ == '__main__':
89
+ def _main(argv=None) -> None:
90
+ import argparse
93
91
 
94
- args = parser.parse_args(argv)
92
+ parser = argparse.ArgumentParser()
93
+ parser.add_argument('-x', '--exec', action='store_true')
94
+ parser.add_argument('cmd')
95
95
 
96
- src = translate_brace_python(args.cmd)
96
+ args = parser.parse_args(argv)
97
97
 
98
- if args.exec:
99
- exec(src)
100
- else:
101
- print(src)
98
+ src = translate_brace_python(args.cmd)
102
99
 
100
+ if args.exec:
101
+ exec(src)
102
+ else:
103
+ print(src)
103
104
 
104
- if __name__ == '__main__':
105
105
  _main()
@@ -1,4 +1,7 @@
1
1
  """
2
+ FIXME:
3
+ - debug tables don't handle symlinks
4
+
2
5
  TODO:
3
6
  - symlink headers, included src files (hamt_impl, ...)
4
7
  - point / copy output to dst dirs
@@ -33,7 +36,7 @@ from omlish import lang
33
36
  from omlish import logs
34
37
 
35
38
  from .. import cmake
36
- from ..scripts import findmagic
39
+ from .. import findmagic
37
40
 
38
41
 
39
42
  log = logging.getLogger(__name__)
@@ -64,10 +67,13 @@ class CmakeProjectGen:
64
67
  self,
65
68
  exts: ta.Sequence[str],
66
69
  prj_root: str | None = None,
70
+ *,
71
+ use_exe_realpath: bool = False,
67
72
  ) -> None:
68
73
  super().__init__()
69
74
  self._exts = check.not_isinstance(exts, str)
70
75
  self._prj_root = os.path.abspath(prj_root) if prj_root is not None else os.getcwd()
76
+ self._use_exe_realpath = use_exe_realpath
71
77
 
72
78
  #
73
79
 
@@ -238,11 +244,14 @@ class CmakeProjectGen:
238
244
  f'{self.var_prefix}_INCLUDE_DIRECTORIES',
239
245
  _sep_str_grps(
240
246
  [f'{self.py.venv_root}/include'],
241
- [f'{self.py.root}/include/python{self.py.suffix}'],
242
- [
243
- # $ENV{HOME}/src/python/cpython
244
- # $ENV{HOME}/src/python/cpython/include
245
- ],
247
+ (
248
+ [
249
+ (red := os.path.dirname(self.p.py_info().real_exe)),
250
+ os.path.join(red, 'include'),
251
+ ]
252
+ if self.p._use_exe_realpath else # noqa
253
+ [f'{self.py.root}/include/python{self.py.suffix}']
254
+ ),
246
255
  ),
247
256
  ))
248
257
 
@@ -269,8 +278,11 @@ class CmakeProjectGen:
269
278
  self.g.write_var(cmake.Var(
270
279
  f'{self.var_prefix}_LINK_DIRECTORIES',
271
280
  _sep_str_grps(
272
- [f'{self.py.root}/lib'],
273
- # ['$ENV{HOME}/src/python/cpython'],
281
+ (
282
+ [os.path.dirname(self.p.py_info().real_exe)]
283
+ if self.p._use_exe_realpath else # noqa
284
+ [f'{self.py.root}/lib']
285
+ ),
274
286
  ),
275
287
  ))
276
288
 
@@ -316,7 +328,10 @@ def _gen_cmd(args) -> None:
316
328
  if not args.exts:
317
329
  raise Exception('must specify exts')
318
330
 
319
- cpg = CmakeProjectGen(args.exts)
331
+ cpg = CmakeProjectGen(
332
+ args.exts,
333
+ use_exe_realpath=bool(args.realpath),
334
+ )
320
335
  cpg.run()
321
336
 
322
337
 
@@ -328,6 +343,7 @@ def _main(argv=None) -> None:
328
343
  subparsers = parser.add_subparsers()
329
344
 
330
345
  parser_gen = subparsers.add_parser('gen')
346
+ parser_gen.add_argument('--realpath', action='store_true')
331
347
  parser_gen.add_argument('exts', nargs='*')
332
348
  parser_gen.set_defaults(func=_gen_cmd)
333
349
 
@@ -0,0 +1,134 @@
1
+ """
2
+ TODO:
3
+ - whitelist packages
4
+ """
5
+ import importlib.abc
6
+ import importlib.machinery
7
+ import importlib.util
8
+ import os.path
9
+ import sys
10
+ import types
11
+ import typing as ta
12
+
13
+ from omlish import check
14
+
15
+ from . import build
16
+
17
+
18
+ ##
19
+
20
+
21
+ def load_dynamic(name: str, path: str) -> types.ModuleType:
22
+ import importlib.machinery
23
+ loader = importlib.machinery.ExtensionFileLoader(name, path)
24
+
25
+ # Issue #24748: Skip the sys.modules check in _load_module_shim; always load new extension
26
+ spec = importlib.machinery.ModuleSpec(name=name, loader=loader, origin=path)
27
+
28
+ import importlib._bootstrap # FIXME: # noqa
29
+ return importlib._bootstrap._load(spec) # noqa
30
+
31
+
32
+ ##
33
+
34
+
35
+ class CextImportLoader(importlib.machinery.ExtensionFileLoader):
36
+
37
+ def __init__(
38
+ self,
39
+ filename: str,
40
+ ) -> None:
41
+ module_name = os.path.splitext(os.path.basename(filename))[0]
42
+ super().__init__(module_name, filename)
43
+
44
+ def create_module(self, spec: importlib.machinery.ModuleSpec) -> types.ModuleType:
45
+ so_path = build.build_ext(spec.name, check.non_empty_str(spec.origin))
46
+ self.path = so_path # noqa
47
+ spec.origin = so_path
48
+ return super().create_module(spec)
49
+
50
+ def exec_module(self, module):
51
+ return super().exec_module(module)
52
+
53
+
54
+ CEXT_EXTENSIONS = ['.c', '.cc', '.cpp', '.cxx']
55
+
56
+
57
+ class CextImportMetaFinder(importlib.abc.MetaPathFinder):
58
+
59
+ def __init__(
60
+ self,
61
+ extensions: ta.AbstractSet[str] = frozenset(CEXT_EXTENSIONS),
62
+ ) -> None:
63
+ super().__init__()
64
+ self._extensions = extensions
65
+
66
+ def find_spec(
67
+ self,
68
+ fullname: str,
69
+ path: ta.Sequence[str] | None,
70
+ target: types.ModuleType | None = None,
71
+ ) -> importlib.machinery.ModuleSpec | None:
72
+ if not path:
73
+ path = [os.getcwd(), *sys.path] # top level import --
74
+ if '.' in fullname:
75
+ *parents, name = fullname.split('.')
76
+ else:
77
+ name = fullname
78
+
79
+ for entry in path:
80
+ for ext in self._extensions:
81
+ if os.path.isdir(os.path.join(entry, name)):
82
+ # this module has child modules
83
+ filename = os.path.join(entry, name, '__init__' + ext)
84
+ submodule_locations = [os.path.join(entry, name)]
85
+ else:
86
+ filename = os.path.join(entry, name + ext)
87
+ submodule_locations = None
88
+ if not os.path.exists(filename):
89
+ continue
90
+
91
+ return importlib.util.spec_from_file_location(
92
+ fullname,
93
+ filename,
94
+ loader=CextImportLoader(filename),
95
+ submodule_search_locations=submodule_locations,
96
+ )
97
+
98
+ return None # we don't know how to import this
99
+
100
+
101
+ #
102
+
103
+
104
+ def _get_installed_importers() -> list[CextImportMetaFinder]:
105
+ return [i for i in sys.meta_path if isinstance(i, CextImportMetaFinder)]
106
+
107
+
108
+ def is_installed() -> bool:
109
+ return bool(_get_installed_importers())
110
+
111
+
112
+ def install(*, flush: bool = True) -> bool:
113
+ if _get_installed_importers():
114
+ return False
115
+
116
+ sys.meta_path.append(CextImportMetaFinder())
117
+
118
+ if flush:
119
+ importlib.invalidate_caches()
120
+
121
+ return True
122
+
123
+
124
+ def uninstall(*, flush: bool = False) -> bool:
125
+ ret = False
126
+
127
+ for i in _get_installed_importers():
128
+ sys.meta_path.remove(i)
129
+ ret = True
130
+
131
+ if ret and flush:
132
+ importlib.invalidate_caches()
133
+
134
+ return ret
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env python3
2
+ # @omlish-script
3
+ """
4
+ TODO:
5
+ - multiple commands:
6
+ - dumb cmp (a = set(sys.modules); import ...; print(set(sys.modules) - a)
7
+ """
8
+ import ast
9
+ import importlib.machinery
10
+ import importlib.util
11
+ import os.path
12
+ import sys
13
+ import typing as ta
14
+
15
+
16
+ _BUILTIN_MODULE_NAMES = frozenset([*sys.builtin_module_names, *sys.stdlib_module_names])
17
+
18
+
19
+ def whichmod(i: str) -> ta.Literal['bad', 'builtin', 'dep']:
20
+ try:
21
+ l = importlib.util.find_spec(i)
22
+ except (ImportError, ValueError):
23
+ return 'bad'
24
+
25
+ if not isinstance(l, importlib.machinery.ModuleSpec) or not l.origin:
26
+ return 'bad'
27
+
28
+ # if l.origin.startswith(sys.base_prefix) or l.origin == 'frozen':
29
+ # return 'builtin'
30
+
31
+ if i in _BUILTIN_MODULE_NAMES:
32
+ return 'builtin'
33
+
34
+ return 'dep'
35
+
36
+
37
+ def yield_imports(fp: str) -> set[str]:
38
+ # if not os.path.isfile(os.path.join(os.path.dirname(fp), '__init__.py')):
39
+ # return
40
+
41
+ with open(fp) as f:
42
+ buf = f.read()
43
+
44
+ nodes: list[ast.AST] = []
45
+
46
+ def rec(n):
47
+ nodes.append(n)
48
+ for c in ast.iter_child_nodes(n):
49
+ rec(c)
50
+
51
+ rec(ast.parse(buf))
52
+
53
+ return {
54
+ *(na.name for i in nodes if isinstance(i, ast.Import) for na in i.names),
55
+ *(i.module for i in nodes if isinstance(i, ast.ImportFrom) if i.module and not i.level),
56
+ }
57
+
58
+
59
+ def find_imports(*rootps: str) -> set[str]:
60
+ imps: set[str] = set()
61
+
62
+ for rootp in rootps:
63
+ if os.path.isfile(rootp):
64
+ if rootp.endswith('.py'):
65
+ imps.update(yield_imports(os.path.join(os.path.dirname(rootp), os.path.basename(rootp))))
66
+
67
+ else:
68
+ for dp, dns, fns in os.walk(os.path.expanduser(rootp)): # noqa
69
+ for fn in fns:
70
+ if fn.endswith('.py'):
71
+ imps.update(yield_imports(os.path.join(dp, fn)))
72
+
73
+ return imps
74
+
75
+
76
+ def get_import_deps(imps: set[str]) -> set[str]:
77
+ eimps = {n for n in imps for n in [n.split('.')[0]] if n not in sys.builtin_module_names}
78
+ return {i for i in eimps if whichmod(i) != 'builtin'}
79
+
80
+
81
+ def _main() -> None:
82
+ imps = find_imports(*sys.argv[1:])
83
+ deps = get_import_deps(imps)
84
+ print(chr(10).join(sorted(deps)))
85
+
86
+
87
+ if __name__ == '__main__':
88
+ _main()
@@ -1,9 +1,15 @@
1
- import argparse
1
+ #!/usr/bin/env python3
2
+ # @omlish-lite
3
+ # @omlish-script
2
4
  import os.path
3
5
  import re
4
6
  import typing as ta
5
7
 
6
8
 
9
+ def compile_magic_pat(m: str) -> re.Pattern:
10
+ return re.compile('^' + re.escape(m) + r'($|(\s.*))')
11
+
12
+
7
13
  def find_magic(
8
14
  roots: ta.Sequence[str],
9
15
  magics: ta.Sequence[str],
@@ -16,10 +22,7 @@ def find_magic(
16
22
  if not exts:
17
23
  raise Exception('Must specify extensions')
18
24
 
19
- pats = [
20
- re.compile('^' + re.escape(m) + r'($|(\s.*))')
21
- for m in magics
22
- ]
25
+ pats = [compile_magic_pat(m) for m in magics]
23
26
 
24
27
  for root in roots:
25
28
  for dp, dns, fns in os.walk(root): # noqa
@@ -52,22 +55,23 @@ def find_magic(
52
55
  yield out
53
56
 
54
57
 
55
- def _main(argv=None) -> None:
56
- arg_parser = argparse.ArgumentParser()
57
- arg_parser.add_argument('--ext', '-x', dest='exts', action='append')
58
- arg_parser.add_argument('--magic', '-m', dest='magics', action='append')
59
- arg_parser.add_argument('--py', action='store_true')
60
- arg_parser.add_argument('roots', nargs='*')
61
- args = arg_parser.parse_args(argv)
58
+ if __name__ == '__main__':
59
+ def _main(argv=None) -> None:
60
+ import argparse
62
61
 
63
- for out in find_magic(
64
- roots=args.roots,
65
- magics=args.magics,
66
- exts=args.exts,
67
- py=args.py,
68
- ):
69
- print(out)
62
+ arg_parser = argparse.ArgumentParser()
63
+ arg_parser.add_argument('--ext', '-x', dest='exts', action='append')
64
+ arg_parser.add_argument('--magic', '-m', dest='magics', action='append')
65
+ arg_parser.add_argument('--py', action='store_true')
66
+ arg_parser.add_argument('roots', nargs='*')
67
+ args = arg_parser.parse_args(argv)
70
68
 
69
+ for out in find_magic(
70
+ roots=args.roots,
71
+ magics=args.magics,
72
+ exts=args.exts,
73
+ py=args.py,
74
+ ):
75
+ print(out)
71
76
 
72
- if __name__ == '__main__':
73
77
  _main()
@@ -0,0 +1,4 @@
1
+ if __name__ == '__main__':
2
+ from .precheck import _main
3
+
4
+ _main()