omdev 0.0.0.dev11__tar.gz → 0.0.0.dev12__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 (78) hide show
  1. {omdev-0.0.0.dev11/omdev.egg-info → omdev-0.0.0.dev12}/PKG-INFO +5 -2
  2. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/__about__.py +4 -0
  3. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/cmake.py +1 -2
  4. omdev-0.0.0.dev12/omdev/exts/cmake.py +342 -0
  5. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/scan.py +2 -2
  6. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/findmagic.py +5 -2
  7. omdev-0.0.0.dev12/omdev/tools/revisions.py +173 -0
  8. omdev-0.0.0.dev12/omdev/wheelfile.py +246 -0
  9. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12/omdev.egg-info}/PKG-INFO +5 -2
  10. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev.egg-info/SOURCES.txt +2 -0
  11. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev.egg-info/requires.txt +5 -1
  12. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/pyproject.toml +6 -2
  13. omdev-0.0.0.dev11/omdev/exts/cmake.py +0 -208
  14. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/LICENSE +0 -0
  15. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/MANIFEST.in +0 -0
  16. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/README.rst +0 -0
  17. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/__init__.py +0 -0
  18. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/amalg/__init__.py +0 -0
  19. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/amalg/__main__.py +0 -0
  20. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/amalg/amalg.py +0 -0
  21. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/classdot.py +0 -0
  22. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/__init__.py +0 -0
  23. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/__init__.py +0 -0
  24. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/build_ext.py +0 -0
  25. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/__init__.py +0 -0
  26. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/ccompiler.py +0 -0
  27. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/options.py +0 -0
  28. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/unixccompiler.py +0 -0
  29. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/dir_util.py +0 -0
  30. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/errors.py +0 -0
  31. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/extension.py +0 -0
  32. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/file_util.py +0 -0
  33. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/modified.py +0 -0
  34. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/spawn.py +0 -0
  35. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/sysconfig.py +0 -0
  36. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/util.py +0 -0
  37. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/_distutils/version.py +0 -0
  38. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/build.py +0 -0
  39. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/exts/importhook.py +0 -0
  40. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/__init__.py +0 -0
  41. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/__main__.py +0 -0
  42. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/cli.py +0 -0
  43. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/inspect.py +0 -0
  44. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/providers.py +0 -0
  45. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/pyenv.py +0 -0
  46. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/resolvers.py +0 -0
  47. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/standalone.py +0 -0
  48. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/system.py +0 -0
  49. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/interp/types.py +0 -0
  50. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/mypy/__init__.py +0 -0
  51. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/mypy/debug.py +0 -0
  52. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/pyproject/__init__.py +0 -0
  53. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/pyproject/__main__.py +0 -0
  54. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/pyproject/cli.py +0 -0
  55. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/pyproject/configs.py +0 -0
  56. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/pyproject/ext.py +0 -0
  57. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/pyproject/pkg.py +0 -0
  58. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/__init__.py +0 -0
  59. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/bracepy.py +0 -0
  60. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/execrss.py +0 -0
  61. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/findimports.py +0 -0
  62. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/interp.py +0 -0
  63. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/pyproject.py +0 -0
  64. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/scripts/traceimport.py +0 -0
  65. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/tokens.py +0 -0
  66. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/toml/__init__.py +0 -0
  67. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/toml/parser.py +0 -0
  68. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/toml/writer.py +0 -0
  69. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/tools/__init__.py +0 -0
  70. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/tools/dockertools.py +0 -0
  71. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/tools/gittools.py +0 -0
  72. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/tools/sqlrepl.py +0 -0
  73. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/versioning/__init__.py +0 -0
  74. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/versioning/specifiers.py +0 -0
  75. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev/versioning/versions.py +0 -0
  76. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev.egg-info/dependency_links.txt +0 -0
  77. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/omdev.egg-info/top_level.txt +0 -0
  78. {omdev-0.0.0.dev11 → omdev-0.0.0.dev12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omdev
3
- Version: 0.0.0.dev11
3
+ Version: 0.0.0.dev12
4
4
  Summary: omdev
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -12,13 +12,14 @@ 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.dev11
15
+ Requires-Dist: omlish==0.0.0.dev12
16
16
  Provides-Extra: all
17
17
  Requires-Dist: pycparser>=2.22; extra == "all"
18
18
  Requires-Dist: cffi>=1.17; extra == "all"
19
19
  Requires-Dist: pcpp>=1.30; extra == "all"
20
20
  Requires-Dist: mypy>=1.11; extra == "all"
21
21
  Requires-Dist: tokenize_rt>=6; extra == "all"
22
+ Requires-Dist: wheel>=0.44; extra == "all"
22
23
  Provides-Extra: c
23
24
  Requires-Dist: pycparser>=2.22; extra == "c"
24
25
  Requires-Dist: cffi>=1.17; extra == "c"
@@ -27,3 +28,5 @@ Provides-Extra: mypy
27
28
  Requires-Dist: mypy>=1.11; extra == "mypy"
28
29
  Provides-Extra: tokens
29
30
  Requires-Dist: tokenize_rt>=6; extra == "tokens"
31
+ Provides-Extra: wheel
32
+ Requires-Dist: wheel>=0.44; extra == "wheel"
@@ -25,6 +25,10 @@ class Project(ProjectBase):
25
25
  'tokens': [
26
26
  'tokenize_rt >= 6',
27
27
  ],
28
+
29
+ 'wheel': [
30
+ 'wheel >= 0.44',
31
+ ],
28
32
  }
29
33
 
30
34
 
@@ -1,5 +1,4 @@
1
1
  import abc
2
- import io
3
2
  import typing as ta
4
3
 
5
4
  from omlish import dataclasses as dc
@@ -80,7 +79,7 @@ class CmakeGen:
80
79
 
81
80
  def __init__(
82
81
  self,
83
- out: io.TextIOBase,
82
+ out: ta.TextIO,
84
83
  *,
85
84
  indent: int = 4,
86
85
  ) -> None:
@@ -0,0 +1,342 @@
1
+ """
2
+ TODO:
3
+ - symlink headers, included src files (hamt_impl, ...)
4
+ - point / copy output to dst dirs
5
+ - libs
6
+ - ..
7
+ - pybind
8
+ - catch2?
9
+ - json? https://github.com/nlohmann/json
10
+ - FindPackages? FetchContent? built_ext won't have that
11
+ - move omml git / data retriever stuff into omdev, get just the one header file from git via sha?
12
+ - support local built pys
13
+
14
+ ==
15
+
16
+ Done:
17
+ - https://intellij-support.jetbrains.com/hc/en-us/community/posts/206608485-Multiple-Jetbrain-IDE-sharing-the-same-project-directory really?
18
+ - aight, generate a whole cmake subdir with symlinks to src files lol
19
+
20
+ """ # noqa
21
+ import argparse
22
+ import dataclasses as dc
23
+ import io
24
+ import logging
25
+ import os.path
26
+ import shutil
27
+ import sys
28
+ import sysconfig
29
+ import typing as ta
30
+
31
+ from omlish import check
32
+ from omlish import lang
33
+ from omlish import logs
34
+
35
+ from .. import cmake
36
+ from ..scripts import findmagic
37
+
38
+
39
+ log = logging.getLogger(__name__)
40
+
41
+
42
+ ##
43
+
44
+
45
+ MAGIC = '@omdev-ext'
46
+ MAGIC_COMMENT = f'// {MAGIC}'
47
+
48
+ FILE_EXTENSIONS = ('c', 'cc', 'cpp')
49
+
50
+
51
+ def _sep_str_grps(*ls: ta.Sequence[str]) -> list[str]:
52
+ o = []
53
+ for i, l in enumerate(ls):
54
+ if not l:
55
+ continue
56
+ if i:
57
+ o.append('')
58
+ o.extend(check.not_isinstance(l, str))
59
+ return o
60
+
61
+
62
+ class CmakeProjectGen:
63
+ def __init__(
64
+ self,
65
+ exts: ta.Sequence[str],
66
+ prj_root: str | None = None,
67
+ ) -> None:
68
+ super().__init__()
69
+ self._exts = check.not_isinstance(exts, str)
70
+ self._prj_root = os.path.abspath(prj_root) if prj_root is not None else os.getcwd()
71
+
72
+ #
73
+
74
+ @property
75
+ def prj_root(self) -> str:
76
+ return self._prj_root
77
+
78
+ @lang.cached_function
79
+ def prj_name(self) -> str:
80
+ return os.path.basename(os.path.dirname(self.prj_root))
81
+
82
+ @lang.cached_function
83
+ def cmake_dir(self) -> str:
84
+ cmake_dir = os.path.join(self.prj_root, 'cmake')
85
+ if os.path.exists(cmake_dir):
86
+ for e in os.listdir(cmake_dir):
87
+ if e == '.idea':
88
+ continue
89
+ ep = os.path.join(cmake_dir, e)
90
+ if os.path.isfile(ep):
91
+ os.unlink(ep)
92
+ else:
93
+ shutil.rmtree(ep)
94
+ else:
95
+ os.mkdir(cmake_dir)
96
+ return cmake_dir
97
+
98
+ #
99
+
100
+ def write_git_ignore(self) -> None:
101
+ with open(os.path.join(self.cmake_dir(), '.gitignore'), 'w') as f:
102
+ f.write('\n'.join(sorted(['/cmake-*', '/build'])))
103
+
104
+ #
105
+
106
+ def write_idea_name(self) -> None:
107
+ idea_dir = os.path.join(self.cmake_dir(), '.idea')
108
+ if not os.path.isdir(idea_dir):
109
+ os.mkdir(idea_dir)
110
+ idea_name_file = os.path.join(idea_dir, '.name')
111
+ if not os.path.isfile(idea_name_file):
112
+ with open(idea_name_file, 'w') as f:
113
+ f.write(self.prj_name())
114
+
115
+ #
116
+
117
+ @dc.dataclass(frozen=True, kw_only=True)
118
+ class PyInfo:
119
+ venv_exe: str
120
+ venv_root: str
121
+ real_exe: str
122
+ root: str
123
+ suffix: str
124
+
125
+ @lang.cached_function
126
+ def py_info(self) -> PyInfo:
127
+ venv_exe = sys.executable
128
+ real_exe = os.path.realpath(venv_exe)
129
+ return self.PyInfo(
130
+ venv_exe=venv_exe,
131
+ venv_root=os.path.abspath(os.path.join(os.path.dirname(venv_exe), '..')),
132
+ real_exe=real_exe,
133
+ root=os.path.abspath(os.path.join(os.path.dirname(real_exe), '..')),
134
+ suffix='.'.join(map(str, sys.version_info[:2])),
135
+ )
136
+
137
+ #
138
+
139
+ @lang.cached_function
140
+ def ext_files(self) -> ta.Sequence[str]:
141
+ out = []
142
+ for e in self._exts:
143
+ e = os.path.abspath(e)
144
+ if os.path.isfile(e):
145
+ out.append(e)
146
+ elif os.path.isdir(e):
147
+ out.extend(
148
+ findmagic.find_magic(
149
+ [e],
150
+ [MAGIC_COMMENT],
151
+ FILE_EXTENSIONS,
152
+ ),
153
+ )
154
+ else:
155
+ raise KeyError(e)
156
+ return out
157
+
158
+ #
159
+
160
+ class _CmakeListsGen:
161
+ def __init__(
162
+ self,
163
+ p: 'CmakeProjectGen',
164
+ out: ta.TextIO,
165
+ ) -> None:
166
+ super().__init__()
167
+ self.p = p
168
+ self.g = cmake.CmakeGen(out)
169
+
170
+ @lang.cached_property
171
+ def var_prefix(self) -> str:
172
+ return self.p.prj_name().upper()
173
+
174
+ @lang.cached_property
175
+ def py(self) -> 'CmakeProjectGen.PyInfo':
176
+ return self.p.py_info()
177
+
178
+ def _add_ext(self, ext_src: str) -> None:
179
+ ext_name = ext_src.rpartition('.')[0].replace('/', '__')
180
+
181
+ log.info('Adding cmake c extension: %s -> %s', ext_src, ext_name)
182
+
183
+ so_name = ''.join([
184
+ os.path.basename(ext_src).split('.')[0],
185
+ '.',
186
+ sysconfig.get_config_var('SOABI'),
187
+ sysconfig.get_config_var('SHLIB_SUFFIX'),
188
+ ])
189
+
190
+ sl = os.path.join(self.p.cmake_dir(), ext_src)
191
+ sal = os.path.abspath(sl)
192
+ sd = os.path.dirname(sal)
193
+ os.makedirs(sd, exist_ok=True)
194
+ rp = os.path.relpath(os.path.abspath(ext_src), sd)
195
+ os.symlink(rp, sal)
196
+
197
+ ml = cmake.ModuleLibrary(
198
+ ext_name,
199
+ src_files=[
200
+ sl,
201
+ ],
202
+ include_dirs=[
203
+ f'${{{self.var_prefix}_INCLUDE_DIRECTORIES}}',
204
+ ],
205
+ compile_opts=[
206
+ f'${{{self.var_prefix}_COMPILE_OPTIONS}}',
207
+ ],
208
+ link_dirs=[
209
+ f'${{{self.var_prefix}_LINK_DIRECTORIES}}',
210
+ ],
211
+ link_libs=[
212
+ f'${{{self.var_prefix}_LINK_LIBRARIES}}',
213
+ ],
214
+ extra_cmds=[
215
+ cmake.Command(
216
+ 'add_custom_command',
217
+ ['TARGET', ext_name, 'POST_BUILD'],
218
+ [
219
+ ' '.join([
220
+ 'COMMAND ${CMAKE_COMMAND} -E ',
221
+ f'copy $<TARGET_FILE_NAME:{ext_name}> ../../{os.path.dirname(ext_src)}/{so_name}',
222
+ ]),
223
+ 'COMMAND_EXPAND_LISTS',
224
+ ],
225
+ ),
226
+ ],
227
+ )
228
+ self.g.write_target(ml)
229
+
230
+ def run(self) -> None:
231
+ self.g.write(self.g.preamble)
232
+ self.g.write('')
233
+
234
+ self.g.write(f'project({self.p.prj_name()})')
235
+ self.g.write('')
236
+
237
+ self.g.write_var(cmake.Var(
238
+ f'{self.var_prefix}_INCLUDE_DIRECTORIES',
239
+ _sep_str_grps(
240
+ [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
+ ],
246
+ ),
247
+ ))
248
+
249
+ self.g.write_var(cmake.Var(
250
+ f'{self.var_prefix}_COMPILE_OPTIONS',
251
+ _sep_str_grps(
252
+ [
253
+ '-Wsign-compare',
254
+ '-Wunreachable-code',
255
+ '-DNDEBUG',
256
+ '-g',
257
+ '-fwrapv',
258
+ '-O3',
259
+ '-Wall',
260
+ ],
261
+ [
262
+ '-g',
263
+ '-c',
264
+ ],
265
+ ['-std=c++20'],
266
+ ),
267
+ ))
268
+
269
+ self.g.write_var(cmake.Var(
270
+ f'{self.var_prefix}_LINK_DIRECTORIES',
271
+ _sep_str_grps(
272
+ [f'{self.py.root}/lib'],
273
+ # ['$ENV{HOME}/src/python/cpython'],
274
+ ),
275
+ ))
276
+
277
+ self.g.write_var(cmake.Var(
278
+ f'{self.var_prefix}_LINK_LIBRARIES',
279
+ _sep_str_grps(
280
+ *([[
281
+ '-bundle',
282
+ '"-undefined dynamic_lookup"',
283
+ ]] if sys.platform == 'darwin' else []),
284
+ ),
285
+ ))
286
+
287
+ for ext_src in self.p.ext_files():
288
+ self._add_ext(os.path.relpath(ext_src, self.p.prj_root))
289
+
290
+ #
291
+
292
+ def run(self) -> None:
293
+ if not os.path.isfile(os.path.join(self._prj_root, 'pyproject.toml')):
294
+ raise Exception('Must be run in project root')
295
+
296
+ self.ext_files()
297
+
298
+ log.info('Generating cmake project %s', self.prj_name())
299
+
300
+ self.cmake_dir()
301
+ self.write_git_ignore()
302
+ self.write_idea_name()
303
+
304
+ out = io.StringIO()
305
+ clg = self._CmakeListsGen(self, out)
306
+ clg.run()
307
+
308
+ with open(os.path.join(self.cmake_dir(), 'CMakeLists.txt'), 'w') as f:
309
+ f.write(out.getvalue())
310
+
311
+
312
+ ##
313
+
314
+
315
+ def _gen_cmd(args) -> None:
316
+ if not args.exts:
317
+ raise Exception('must specify exts')
318
+
319
+ cpg = CmakeProjectGen(args.exts)
320
+ cpg.run()
321
+
322
+
323
+ def _main(argv=None) -> None:
324
+ logs.configure_standard_logging('INFO')
325
+
326
+ parser = argparse.ArgumentParser()
327
+
328
+ subparsers = parser.add_subparsers()
329
+
330
+ parser_gen = subparsers.add_parser('gen')
331
+ parser_gen.add_argument('exts', nargs='*')
332
+ parser_gen.set_defaults(func=_gen_cmd)
333
+
334
+ args = parser.parse_args(argv)
335
+ if not getattr(args, 'func', None):
336
+ parser.print_help()
337
+ else:
338
+ args.func(args)
339
+
340
+
341
+ if __name__ == '__main__':
342
+ _main()
@@ -12,7 +12,7 @@ log = logging.getLogger(__name__)
12
12
  SCAN_COMMENT = '// @omdev-ext'
13
13
 
14
14
 
15
- def _scan_one(
15
+ def scan_one(
16
16
  input_path: str,
17
17
  **kwargs: ta.Any,
18
18
  ) -> None:
@@ -42,7 +42,7 @@ def _scan_cmd(args) -> None:
42
42
  log.info('Scanning %s', i)
43
43
  for we_dirpath, we_dirnames, we_filenames in os.walk(i): # noqa
44
44
  for fname in we_filenames:
45
- _scan_one(
45
+ scan_one(
46
46
  os.path.abspath(os.path.join(we_dirpath, fname)),
47
47
  )
48
48
 
@@ -28,8 +28,11 @@ def find_magic(
28
28
  continue
29
29
 
30
30
  fp = os.path.join(dp, fn)
31
- with open(fp) as f:
32
- src = f.read()
31
+ try:
32
+ with open(fp) as f:
33
+ src = f.read()
34
+ except UnicodeDecodeError:
35
+ continue
33
36
 
34
37
  if not any(
35
38
  any(pat.fullmatch(l) for pat in pats)
@@ -0,0 +1,173 @@
1
+ """
2
+ TODO:
3
+ - omlish-lite, move to pyproject/
4
+ - vendor-lite wheel.wheelfile
5
+ """
6
+ # ruff: noqa: TCH003 UP006 UP007
7
+ # @omlish-lite
8
+ import argparse
9
+ import io
10
+ import os.path
11
+ import subprocess
12
+ import tarfile
13
+ import typing as ta
14
+ import zipfile
15
+
16
+ from omlish.lite.logs import configure_standard_logging
17
+ from omlish.lite.logs import log
18
+
19
+ from ..wheelfile import WheelFile
20
+
21
+
22
+ class RevisionAdder:
23
+ def __init__(
24
+ self,
25
+ revision: str,
26
+ output_suffix: ta.Optional[str] = None,
27
+ ) -> None:
28
+ super().__init__()
29
+ self._revision = revision
30
+ self._output_suffix = output_suffix
31
+
32
+ REVISION_ATTR = '__revision__'
33
+
34
+ def add_to_contents(self, dct: ta.Dict[str, bytes]) -> bool:
35
+ changed = False
36
+ for n in dct:
37
+ if not n.endswith('__about__.py'):
38
+ continue
39
+ src = dct[n].decode('utf-8')
40
+ lines = src.splitlines(keepends=True)
41
+ for i, l in enumerate(lines):
42
+ if l != f'{self.REVISION_ATTR} = None\n':
43
+ continue
44
+ lines[i] = f"{self.REVISION_ATTR} = '{self._revision}'\n"
45
+ changed = True
46
+ dct[n] = ''.join(lines).encode('utf-8')
47
+ return changed
48
+
49
+ def add_to_wheel(self, f: str) -> None:
50
+ if not f.endswith('.whl'):
51
+ raise Exception(f)
52
+ log.info('Scanning wheel %s', f)
53
+
54
+ zis: ta.Dict[str, zipfile.ZipInfo] = {}
55
+ dct: ta.Dict[str, bytes] = {}
56
+ with WheelFile(f) as wf:
57
+ for zi in wf.filelist:
58
+ if zi.filename == wf.record_path:
59
+ continue
60
+ zis[zi.filename] = zi
61
+ dct[zi.filename] = wf.read(zi.filename)
62
+
63
+ if self.add_to_contents(dct):
64
+ of = f[:-4] + (self._output_suffix or '') + '.whl'
65
+ log.info('Repacking wheel %s', of)
66
+ with WheelFile(of, 'w') as wf:
67
+ for n, d in dct.items():
68
+ log.info('Adding zipinfo %s', n)
69
+ wf.writestr(zis[n], d)
70
+
71
+ def add_to_tgz(self, f: str) -> None:
72
+ if not f.endswith('.tar.gz'):
73
+ raise Exception(f)
74
+ log.info('Scanning tgz %s', f)
75
+
76
+ tis: ta.Dict[str, tarfile.TarInfo] = {}
77
+ dct: ta.Dict[str, bytes] = {}
78
+ with tarfile.open(f, 'r:gz') as tf:
79
+ for ti in tf:
80
+ tis[ti.name] = ti
81
+ if ti.type == tarfile.REGTYPE:
82
+ with tf.extractfile(ti.name) as tif: # type: ignore
83
+ dct[ti.name] = tif.read()
84
+
85
+ if self.add_to_contents(dct):
86
+ of = f[:-7] + (self._output_suffix or '') + '.tar.gz'
87
+ log.info('Repacking tgz %s', of)
88
+ with tarfile.open(of, 'w:gz') as tf:
89
+ for n, ti in tis.items():
90
+ log.info('Adding tarinfo %s', n)
91
+ if n in dct:
92
+ data = dct[n]
93
+ ti.size = len(data)
94
+ fo = io.BytesIO(data)
95
+ else:
96
+ fo = None
97
+ tf.addfile(ti, fileobj=fo)
98
+
99
+ EXTS = ('.tar.gz', '.whl')
100
+
101
+ def add_to_file(self, f: str) -> None:
102
+ if f.endswith('.whl'):
103
+ self.add_to_wheel(f)
104
+
105
+ elif f.endswith('.tar.gz'):
106
+ self.add_to_tgz(f)
107
+
108
+ def add_to(self, tgt: str) -> None:
109
+ if os.path.isfile(tgt):
110
+ self.add_to_file(tgt)
111
+
112
+ elif os.path.isdir(tgt):
113
+ for dp, dns, fns in os.walk(tgt): # noqa
114
+ for f in fns:
115
+ if any(f.endswith(ext) for ext in self.EXTS):
116
+ self.add_to_file(os.path.join(dp, f))
117
+
118
+
119
+ #
120
+
121
+
122
+ def get_revision() -> str:
123
+ return subprocess.check_output([
124
+ 'git',
125
+ 'describe',
126
+ '--match=NeVeRmAtCh',
127
+ '--always',
128
+ '--abbrev=40',
129
+ '--dirty',
130
+ ]).decode().strip()
131
+
132
+
133
+ #
134
+
135
+
136
+ def _add_cmd(args) -> None:
137
+ if (revision := args.revision) is None:
138
+ revision = get_revision()
139
+ log.info('Using revision %s', revision)
140
+
141
+ if not args.targets:
142
+ raise Exception('must specify targets')
143
+
144
+ ra = RevisionAdder(
145
+ revision,
146
+ output_suffix=args.suffix,
147
+ )
148
+ for tgt in args.targets:
149
+ ra.add_to(tgt)
150
+
151
+
152
+ def _main(argv=None) -> None:
153
+ configure_standard_logging('INFO')
154
+
155
+ parser = argparse.ArgumentParser()
156
+
157
+ subparsers = parser.add_subparsers()
158
+
159
+ parser_add = subparsers.add_parser('add')
160
+ parser_add.add_argument('-r', '--revision')
161
+ parser_add.add_argument('-s', '--suffix')
162
+ parser_add.add_argument('targets', nargs='*')
163
+ parser_add.set_defaults(func=_add_cmd)
164
+
165
+ args = parser.parse_args(argv)
166
+ if not getattr(args, 'func', None):
167
+ parser.print_help()
168
+ else:
169
+ args.func(args)
170
+
171
+
172
+ if __name__ == '__main__':
173
+ _main()