omdev 0.0.0.dev10__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 (79) hide show
  1. {omdev-0.0.0.dev10/omdev.egg-info → omdev-0.0.0.dev12}/PKG-INFO +5 -2
  2. omdev-0.0.0.dev12/README.rst +5 -0
  3. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/__about__.py +4 -0
  4. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/cmake.py +1 -2
  5. omdev-0.0.0.dev12/omdev/exts/cmake.py +342 -0
  6. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/scan.py +2 -2
  7. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/pyenv.py +5 -1
  8. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/pyproject/cli.py +7 -5
  9. omdev-0.0.0.dev12/omdev/scripts/bracepy.py +105 -0
  10. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/findmagic.py +5 -2
  11. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/interp.py +5 -1
  12. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/pyproject.py +12 -6
  13. omdev-0.0.0.dev12/omdev/tools/gittools.py +22 -0
  14. omdev-0.0.0.dev12/omdev/tools/revisions.py +173 -0
  15. omdev-0.0.0.dev12/omdev/wheelfile.py +246 -0
  16. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12/omdev.egg-info}/PKG-INFO +5 -2
  17. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev.egg-info/SOURCES.txt +4 -0
  18. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev.egg-info/requires.txt +5 -1
  19. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/pyproject.toml +6 -2
  20. omdev-0.0.0.dev10/README.rst +0 -1
  21. omdev-0.0.0.dev10/omdev/exts/cmake.py +0 -195
  22. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/LICENSE +0 -0
  23. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/MANIFEST.in +0 -0
  24. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/__init__.py +0 -0
  25. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/amalg/__init__.py +0 -0
  26. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/amalg/__main__.py +0 -0
  27. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/amalg/amalg.py +0 -0
  28. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/classdot.py +0 -0
  29. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/__init__.py +0 -0
  30. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/__init__.py +0 -0
  31. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/build_ext.py +0 -0
  32. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/__init__.py +0 -0
  33. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/ccompiler.py +0 -0
  34. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/options.py +0 -0
  35. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/compilers/unixccompiler.py +0 -0
  36. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/dir_util.py +0 -0
  37. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/errors.py +0 -0
  38. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/extension.py +0 -0
  39. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/file_util.py +0 -0
  40. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/modified.py +0 -0
  41. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/spawn.py +0 -0
  42. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/sysconfig.py +0 -0
  43. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/util.py +0 -0
  44. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/_distutils/version.py +0 -0
  45. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/build.py +0 -0
  46. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/exts/importhook.py +0 -0
  47. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/__init__.py +0 -0
  48. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/__main__.py +0 -0
  49. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/cli.py +0 -0
  50. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/inspect.py +0 -0
  51. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/providers.py +0 -0
  52. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/resolvers.py +0 -0
  53. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/standalone.py +0 -0
  54. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/system.py +0 -0
  55. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/interp/types.py +0 -0
  56. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/mypy/__init__.py +0 -0
  57. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/mypy/debug.py +0 -0
  58. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/pyproject/__init__.py +0 -0
  59. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/pyproject/__main__.py +0 -0
  60. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/pyproject/configs.py +0 -0
  61. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/pyproject/ext.py +0 -0
  62. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/pyproject/pkg.py +0 -0
  63. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/__init__.py +0 -0
  64. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/execrss.py +0 -0
  65. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/findimports.py +0 -0
  66. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/scripts/traceimport.py +0 -0
  67. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/tokens.py +0 -0
  68. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/toml/__init__.py +0 -0
  69. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/toml/parser.py +0 -0
  70. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/toml/writer.py +0 -0
  71. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/tools/__init__.py +0 -0
  72. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/tools/dockertools.py +0 -0
  73. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/tools/sqlrepl.py +0 -0
  74. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/versioning/__init__.py +0 -0
  75. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/versioning/specifiers.py +0 -0
  76. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev/versioning/versions.py +0 -0
  77. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev.egg-info/dependency_links.txt +0 -0
  78. {omdev-0.0.0.dev10 → omdev-0.0.0.dev12}/omdev.egg-info/top_level.txt +0 -0
  79. {omdev-0.0.0.dev10 → 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.dev10
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.dev10
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"
@@ -0,0 +1,5 @@
1
+ *omlish*
2
+
3
+ Packages installable from git via:
4
+ ::
5
+ pip install 'git+https://github.com/wrmsr/omlish@master#subdirectory=.pkg/<pkg>'
@@ -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
 
@@ -67,8 +67,10 @@ class Pyenv:
67
67
  return os.path.join(check_not_none(self.root()), 'bin', 'pyenv')
68
68
 
69
69
  def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
70
+ if (root := self.root()) is None:
71
+ return []
70
72
  ret = []
71
- vp = os.path.join(self.root(), 'versions')
73
+ vp = os.path.join(root, 'versions')
72
74
  for dn in os.listdir(vp):
73
75
  ep = os.path.join(vp, dn, 'bin', 'python')
74
76
  if not os.path.isfile(ep):
@@ -77,6 +79,8 @@ class Pyenv:
77
79
  return ret
78
80
 
79
81
  def installable_versions(self) -> ta.List[str]:
82
+ if self.root() is None:
83
+ return []
80
84
  ret = []
81
85
  s = subprocess_check_output_str(self.exe(), 'install', '--list')
82
86
  for l in s.splitlines():
@@ -26,6 +26,7 @@ import dataclasses as dc
26
26
  import functools
27
27
  import glob
28
28
  import itertools
29
+ import multiprocessing as mp
29
30
  import os.path
30
31
  import shlex
31
32
  import shutil
@@ -247,13 +248,12 @@ def _venv_cmd(args) -> None:
247
248
  )
248
249
  return
249
250
 
250
- venv.create()
251
-
252
251
  cmd = args.cmd
253
252
  if not cmd:
254
- pass
253
+ venv.create()
255
254
 
256
255
  elif cmd == 'python':
256
+ venv.create()
257
257
  os.execl(
258
258
  (exe := venv.exe()),
259
259
  exe,
@@ -261,10 +261,12 @@ def _venv_cmd(args) -> None:
261
261
  )
262
262
 
263
263
  elif cmd == 'exe':
264
+ venv.create()
264
265
  check_not(args.args)
265
266
  print(venv.exe())
266
267
 
267
268
  elif cmd == 'run':
269
+ venv.create()
268
270
  sh = check_not_none(shutil.which('bash'))
269
271
  script = ' '.join(args.args)
270
272
  if not script:
@@ -281,6 +283,7 @@ def _venv_cmd(args) -> None:
281
283
  print('\n'.join(venv.srcs()))
282
284
 
283
285
  elif cmd == 'test':
286
+ venv.create()
284
287
  subprocess_check_call(venv.exe(), '-m', 'pytest', *(args.args or []), *venv.srcs())
285
288
 
286
289
  else:
@@ -306,11 +309,10 @@ def _pkg_cmd(args) -> None:
306
309
  build_output_dir = 'dist'
307
310
  run_build = bool(args.build)
308
311
 
309
- num_threads = 8
310
-
311
312
  if run_build:
312
313
  os.makedirs(build_output_dir, exist_ok=True)
313
314
 
315
+ num_threads = max(mp.cpu_count() // 2, 1)
314
316
  with cf.ThreadPoolExecutor(num_threads) as ex:
315
317
  futs = [
316
318
  ex.submit(functools.partial(
@@ -0,0 +1,105 @@
1
+ # !/usr/bin/env python3
2
+ """
3
+ https://github.com/umlet/pwk/blob/dc23b3400108a71947a695f1fa1df0f514b42528/pwk
4
+ """
5
+ import io
6
+ import tokenize
7
+
8
+
9
+ def translate_brace_python(
10
+ s: str,
11
+ *,
12
+ indent_width: int = 4,
13
+ ) -> str:
14
+ lt = tokenize.tokenize(io.BytesIO(s.encode('utf-8')).readline)
15
+
16
+ ret = io.StringIO()
17
+
18
+ indent = 0
19
+ open_braces = 0
20
+
21
+ newline = False
22
+ indent_up = False
23
+ indent_down = False
24
+ skip = False
25
+
26
+ while True:
27
+ try:
28
+ t = next(lt)
29
+
30
+ if t.type == tokenize.ENCODING:
31
+ last_t = t
32
+ continue
33
+
34
+ if t.type == tokenize.OP and t.string == ';':
35
+ newline = True
36
+
37
+ elif t.type == tokenize.OP and t.string == ':':
38
+ if open_braces == 0:
39
+ newline = True
40
+ indent_up = True
41
+
42
+ elif t.type == tokenize.OP and t.string == '{':
43
+ if last_t.type == tokenize.OP and last_t.string == ':': # noqa
44
+ skip = True
45
+ else:
46
+ open_braces += 1
47
+
48
+ elif t.type == tokenize.OP and t.string == '}':
49
+ if open_braces > 0:
50
+ open_braces -= 1
51
+ elif open_braces == 0:
52
+ if indent > 0:
53
+ newline = True
54
+ indent_down = True
55
+ skip = True
56
+ else:
57
+ raise Exception('Too many closing braces')
58
+
59
+ if indent_up:
60
+ indent += indent_width
61
+ elif indent_down:
62
+ indent -= indent_width
63
+
64
+ if newline and indent_up:
65
+ ret.write(':\n' + ' ' * indent)
66
+ elif newline:
67
+ ret.write('\n' + ' ' * indent)
68
+ elif not skip:
69
+ ret.write(t.string)
70
+ ret.write(' ')
71
+
72
+ newline = False
73
+ indent_up = False
74
+ indent_down = False
75
+ skip = False
76
+ last_t = t
77
+
78
+ except StopIteration:
79
+ break
80
+ except tokenize.TokenError:
81
+ continue
82
+
83
+ ret.write('\n')
84
+ return ret.getvalue()
85
+
86
+
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')
93
+
94
+ args = parser.parse_args(argv)
95
+
96
+ src = translate_brace_python(args.cmd)
97
+
98
+ if args.exec:
99
+ exec(src)
100
+ else:
101
+ print(src)
102
+
103
+
104
+ if __name__ == '__main__':
105
+ _main()
@@ -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)
@@ -1669,8 +1669,10 @@ class Pyenv:
1669
1669
  return os.path.join(check_not_none(self.root()), 'bin', 'pyenv')
1670
1670
 
1671
1671
  def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
1672
+ if (root := self.root()) is None:
1673
+ return []
1672
1674
  ret = []
1673
- vp = os.path.join(self.root(), 'versions')
1675
+ vp = os.path.join(root, 'versions')
1674
1676
  for dn in os.listdir(vp):
1675
1677
  ep = os.path.join(vp, dn, 'bin', 'python')
1676
1678
  if not os.path.isfile(ep):
@@ -1679,6 +1681,8 @@ class Pyenv:
1679
1681
  return ret
1680
1682
 
1681
1683
  def installable_versions(self) -> ta.List[str]:
1684
+ if self.root() is None:
1685
+ return []
1682
1686
  ret = []
1683
1687
  s = subprocess_check_output_str(self.exe(), 'install', '--list')
1684
1688
  for l in s.splitlines():