omdev 0.0.0.dev10__py3-none-any.whl → 0.0.0.dev12__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/__about__.py CHANGED
@@ -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
 
omdev/cmake.py CHANGED
@@ -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:
omdev/exts/cmake.py CHANGED
@@ -9,6 +9,7 @@ TODO:
9
9
  - json? https://github.com/nlohmann/json
10
10
  - FindPackages? FetchContent? built_ext won't have that
11
11
  - move omml git / data retriever stuff into omdev, get just the one header file from git via sha?
12
+ - support local built pys
12
13
 
13
14
  ==
14
15
 
@@ -17,178 +18,324 @@ Done:
17
18
  - aight, generate a whole cmake subdir with symlinks to src files lol
18
19
 
19
20
  """ # noqa
21
+ import argparse
22
+ import dataclasses as dc
20
23
  import io
24
+ import logging
21
25
  import os.path
22
26
  import shutil
23
27
  import sys
24
28
  import sysconfig
29
+ import typing as ta
25
30
 
26
31
  from omlish import check
32
+ from omlish import lang
33
+ from omlish import logs
27
34
 
28
35
  from .. import cmake
36
+ from ..scripts import findmagic
29
37
 
30
38
 
31
- def _main() -> None:
32
- # py_root = '$ENV{HOME}/.pyenv/versions/3.11.8/'
39
+ log = logging.getLogger(__name__)
33
40
 
34
- prj_root = os.path.abspath(os.getcwd())
35
- if not os.path.isfile(os.path.join(prj_root, 'pyproject.toml')):
36
- raise Exception('Must be run in project root')
37
41
 
38
- cmake_dir = os.path.join(prj_root, 'cmake')
39
- if os.path.exists(cmake_dir):
40
- for e in os.listdir(cmake_dir):
41
- if e == '.idea':
42
- continue
43
- ep = os.path.join(cmake_dir, e)
44
- if os.path.isfile(ep):
45
- os.unlink(ep)
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
+ )
46
154
  else:
47
- shutil.rmtree(ep)
48
- else:
49
- os.mkdir(cmake_dir)
50
-
51
- with open(os.path.join(cmake_dir, '.gitignore'), 'w') as f:
52
- f.write('\n'.join(sorted(['/cmake-*', '/build'])))
53
-
54
- if os.path.isdir(os.path.join(cmake_dir, '.idea')):
55
- with open(os.path.join(cmake_dir, '.idea', '.name'), 'w') as f:
56
- f.write('omlish')
57
-
58
- venv_exe = sys.executable
59
- venv_root = os.path.abspath(os.path.join(os.path.dirname(venv_exe), '..'))
60
- real_exe = os.path.realpath(venv_exe)
61
- py_root = os.path.abspath(os.path.join(os.path.dirname(real_exe), '..'))
62
- py_sfx = '.'.join(map(str, sys.version_info[:2]))
63
-
64
- out = io.StringIO()
65
- gen = cmake.CmakeGen(out)
66
-
67
- prj_name = 'omlish'
68
- var_prefix = prj_name.upper()
69
-
70
- gen.write(gen.preamble)
71
- gen.write('')
72
-
73
- gen.write(f'project({prj_name})')
74
- gen.write('')
75
-
76
- def sep_grps(*ls):
77
- # itertools.interleave? or smth?
78
- o = []
79
- for i, l in enumerate(ls):
80
- if not l:
81
- continue
82
- if i:
83
- o.append('')
84
- o.extend(check.not_isinstance(l, str))
85
- return o
86
-
87
- gen.write_var(cmake.Var(
88
- f'{var_prefix}_INCLUDE_DIRECTORIES',
89
- sep_grps(
90
- [f'{venv_root}/include'],
91
- [f'{py_root}/include/python{py_sfx}'],
92
- [
93
- # $ENV{HOME}/src/python/cpython
94
- # $ENV{HOME}/src/python/cpython/include
95
- ],
96
- ),
97
- ))
98
-
99
- gen.write_var(cmake.Var(
100
- f'{var_prefix}_COMPILE_OPTIONS',
101
- sep_grps(
102
- [
103
- '-Wsign-compare',
104
- '-Wunreachable-code',
105
- '-DNDEBUG',
106
- '-g',
107
- '-fwrapv',
108
- '-O3',
109
- '-Wall',
110
- ],
111
- [
112
- '-g',
113
- '-c',
114
- ],
115
- ['-std=c++20'],
116
- ),
117
- ))
118
-
119
- gen.write_var(cmake.Var(
120
- f'{var_prefix}_LINK_DIRECTORIES',
121
- sep_grps(
122
- [f'{py_root}/lib'],
123
- # ['$ENV{HOME}/src/python/cpython'],
124
- ),
125
- ))
126
-
127
- gen.write_var(cmake.Var(
128
- f'{var_prefix}_LINK_LIBRARIES',
129
- sep_grps(
130
- *([[
131
- '-bundle',
132
- '"-undefined dynamic_lookup"',
133
- ]] if sys.platform == 'darwin' else []),
134
- ),
135
- ))
136
-
137
- for ext_src in [
138
- 'omdev/exts/_boilerplate.cc',
139
- 'x/dev/c/junk.cc',
140
- 'x/dev/c/_uuid.cc',
141
- ]:
142
- ext_name = ext_src.rpartition('.')[0].replace('/', '__')
143
- so_name = ''.join([
144
- os.path.basename(ext_src).split('.')[0],
145
- '.',
146
- sysconfig.get_config_var('SOABI'),
147
- sysconfig.get_config_var('SHLIB_SUFFIX'),
148
- ])
149
-
150
- sl = os.path.join(cmake_dir, ext_src)
151
- sal = os.path.abspath(sl)
152
- sd = os.path.dirname(sal)
153
- os.makedirs(sd, exist_ok=True)
154
- rp = os.path.relpath(os.path.abspath(ext_src), sd)
155
- os.symlink(rp, sal)
156
-
157
- gen.write_target(cmake.ModuleLibrary(
158
- ext_name,
159
- src_files=[
160
- sl,
161
- ],
162
- include_dirs=[
163
- f'${{{var_prefix}_INCLUDE_DIRECTORIES}}',
164
- ],
165
- compile_opts=[
166
- f'${{{var_prefix}_COMPILE_OPTIONS}}',
167
- ],
168
- link_dirs=[
169
- f'${{{var_prefix}_LINK_DIRECTORIES}}',
170
- ],
171
- link_libs=[
172
- f'${{{var_prefix}_LINK_LIBRARIES}}',
173
- ],
174
- extra_cmds=[
175
- cmake.Command(
176
- 'add_custom_command',
177
- ['TARGET', ext_name, 'POST_BUILD'],
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
+ ],
178
261
  [
179
- ' '.join([
180
- 'COMMAND ${CMAKE_COMMAND} -E ',
181
- f'copy $<TARGET_FILE_NAME:{ext_name}> ../../{os.path.dirname(ext_src)}/{so_name}',
182
- ]),
183
- 'COMMAND_EXPAND_LISTS',
262
+ '-g',
263
+ '-c',
184
264
  ],
265
+ ['-std=c++20'],
185
266
  ),
186
- ],
187
- ))
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()
188
297
 
189
- print(out.getvalue())
190
- with open(os.path.join(cmake_dir, 'CMakeLists.txt'), 'w') as f:
191
- f.write(out.getvalue())
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)
192
339
 
193
340
 
194
341
  if __name__ == '__main__':
omdev/exts/scan.py CHANGED
@@ -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
 
omdev/interp/pyenv.py CHANGED
@@ -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():
omdev/pyproject/cli.py CHANGED
@@ -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(