omdev 0.0.0.dev181__py3-none-any.whl → 0.0.0.dev183__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.
@@ -10,32 +10,14 @@ TODO:
10
10
  - optionally install / upgrade pyenv itself
11
11
  - new vers dont need these custom mac opts, only run on old vers
12
12
  """
13
- import abc
14
- import dataclasses as dc
15
- import itertools
16
- import logging
17
13
  import os.path
18
14
  import shutil
19
- import sys
20
15
  import typing as ta
21
16
 
22
17
  from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
23
18
  from omlish.lite.cached import async_cached_nullary
24
- from omlish.lite.cached import cached_nullary
25
19
  from omlish.lite.check import check
26
20
 
27
- from ...packaging.versions import InvalidVersion
28
- from ...packaging.versions import Version
29
- from ..inspect import InterpInspector
30
- from ..providers.base import InterpProvider
31
- from ..types import Interp
32
- from ..types import InterpOpts
33
- from ..types import InterpSpecifier
34
- from ..types import InterpVersion
35
-
36
-
37
- ##
38
-
39
21
 
40
22
  class Pyenv:
41
23
  def __init__(
@@ -102,368 +84,3 @@ class Pyenv:
102
84
  return False
103
85
  await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
104
86
  return True
105
-
106
-
107
- ##
108
-
109
-
110
- @dc.dataclass(frozen=True)
111
- class PyenvInstallOpts:
112
- opts: ta.Sequence[str] = ()
113
- conf_opts: ta.Sequence[str] = ()
114
- cflags: ta.Sequence[str] = ()
115
- ldflags: ta.Sequence[str] = ()
116
- env: ta.Mapping[str, str] = dc.field(default_factory=dict)
117
-
118
- def merge(self, *others: 'PyenvInstallOpts') -> 'PyenvInstallOpts':
119
- return PyenvInstallOpts(
120
- opts=list(itertools.chain.from_iterable(o.opts for o in [self, *others])),
121
- conf_opts=list(itertools.chain.from_iterable(o.conf_opts for o in [self, *others])),
122
- cflags=list(itertools.chain.from_iterable(o.cflags for o in [self, *others])),
123
- ldflags=list(itertools.chain.from_iterable(o.ldflags for o in [self, *others])),
124
- env=dict(itertools.chain.from_iterable(o.env.items() for o in [self, *others])),
125
- )
126
-
127
-
128
- # TODO: https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance
129
- DEFAULT_PYENV_INSTALL_OPTS = PyenvInstallOpts(
130
- opts=[
131
- '-s',
132
- '-v',
133
- '-k',
134
- ],
135
- conf_opts=[
136
- # FIXME: breaks on mac for older py's
137
- '--enable-loadable-sqlite-extensions',
138
-
139
- # '--enable-shared',
140
-
141
- '--enable-optimizations',
142
- '--with-lto',
143
-
144
- # '--enable-profiling', # ?
145
-
146
- # '--enable-ipv6', # ?
147
- ],
148
- cflags=[
149
- # '-march=native',
150
- # '-mtune=native',
151
- ],
152
- )
153
-
154
- DEBUG_PYENV_INSTALL_OPTS = PyenvInstallOpts(opts=['-g'])
155
-
156
- THREADED_PYENV_INSTALL_OPTS = PyenvInstallOpts(conf_opts=['--disable-gil'])
157
-
158
-
159
- #
160
-
161
-
162
- class PyenvInstallOptsProvider(abc.ABC):
163
- @abc.abstractmethod
164
- def opts(self) -> ta.Awaitable[PyenvInstallOpts]:
165
- raise NotImplementedError
166
-
167
-
168
- class LinuxPyenvInstallOpts(PyenvInstallOptsProvider):
169
- async def opts(self) -> PyenvInstallOpts:
170
- return PyenvInstallOpts()
171
-
172
-
173
- class DarwinPyenvInstallOpts(PyenvInstallOptsProvider):
174
- @cached_nullary
175
- def framework_opts(self) -> PyenvInstallOpts:
176
- return PyenvInstallOpts(conf_opts=['--enable-framework'])
177
-
178
- @cached_nullary
179
- def has_brew(self) -> bool:
180
- return shutil.which('brew') is not None
181
-
182
- BREW_DEPS: ta.Sequence[str] = [
183
- 'openssl',
184
- 'readline',
185
- 'sqlite3',
186
- 'zlib',
187
- ]
188
-
189
- @async_cached_nullary
190
- async def brew_deps_opts(self) -> PyenvInstallOpts:
191
- cflags = []
192
- ldflags = []
193
- for dep in self.BREW_DEPS:
194
- dep_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', dep)
195
- cflags.append(f'-I{dep_prefix}/include')
196
- ldflags.append(f'-L{dep_prefix}/lib')
197
- return PyenvInstallOpts(
198
- cflags=cflags,
199
- ldflags=ldflags,
200
- )
201
-
202
- @async_cached_nullary
203
- async def brew_tcl_opts(self) -> PyenvInstallOpts:
204
- if await asyncio_subprocesses.try_output('brew', '--prefix', 'tcl-tk') is None:
205
- return PyenvInstallOpts()
206
-
207
- tcl_tk_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', 'tcl-tk')
208
- tcl_tk_ver_str = await asyncio_subprocesses.check_output_str('brew', 'ls', '--versions', 'tcl-tk')
209
- tcl_tk_ver = '.'.join(tcl_tk_ver_str.split()[1].split('.')[:2])
210
-
211
- return PyenvInstallOpts(conf_opts=[
212
- f"--with-tcltk-includes='-I{tcl_tk_prefix}/include'",
213
- f"--with-tcltk-libs='-L{tcl_tk_prefix}/lib -ltcl{tcl_tk_ver} -ltk{tcl_tk_ver}'",
214
- ])
215
-
216
- # @cached_nullary
217
- # def brew_ssl_opts(self) -> PyenvInstallOpts:
218
- # pkg_config_path = subprocess_check_output_str('brew', '--prefix', 'openssl')
219
- # if 'PKG_CONFIG_PATH' in os.environ:
220
- # pkg_config_path += ':' + os.environ['PKG_CONFIG_PATH']
221
- # return PyenvInstallOpts(env={'PKG_CONFIG_PATH': pkg_config_path})
222
-
223
- async def opts(self) -> PyenvInstallOpts:
224
- return PyenvInstallOpts().merge(
225
- self.framework_opts(),
226
- await self.brew_deps_opts(),
227
- await self.brew_tcl_opts(),
228
- # self.brew_ssl_opts(),
229
- )
230
-
231
-
232
- PLATFORM_PYENV_INSTALL_OPTS: ta.Dict[str, PyenvInstallOptsProvider] = {
233
- 'darwin': DarwinPyenvInstallOpts(),
234
- 'linux': LinuxPyenvInstallOpts(),
235
- }
236
-
237
-
238
- ##
239
-
240
-
241
- class PyenvVersionInstaller:
242
- """
243
- Messy: can install freethreaded build with a 't' suffixed version str _or_ by THREADED_PYENV_INSTALL_OPTS - need
244
- latter to build custom interp with ft, need former to use canned / blessed interps. Muh.
245
- """
246
-
247
- def __init__(
248
- self,
249
- version: str,
250
- opts: ta.Optional[PyenvInstallOpts] = None,
251
- interp_opts: InterpOpts = InterpOpts(),
252
- *,
253
- pyenv: Pyenv,
254
-
255
- install_name: ta.Optional[str] = None,
256
- no_default_opts: bool = False,
257
- ) -> None:
258
- super().__init__()
259
-
260
- self._version = version
261
- self._given_opts = opts
262
- self._interp_opts = interp_opts
263
- self._given_install_name = install_name
264
-
265
- self._no_default_opts = no_default_opts
266
- self._pyenv = pyenv
267
-
268
- @property
269
- def version(self) -> str:
270
- return self._version
271
-
272
- @async_cached_nullary
273
- async def opts(self) -> PyenvInstallOpts:
274
- opts = self._given_opts
275
- if self._no_default_opts:
276
- if opts is None:
277
- opts = PyenvInstallOpts()
278
- else:
279
- lst = [self._given_opts if self._given_opts is not None else DEFAULT_PYENV_INSTALL_OPTS]
280
- if self._interp_opts.debug:
281
- lst.append(DEBUG_PYENV_INSTALL_OPTS)
282
- if self._interp_opts.threaded:
283
- lst.append(THREADED_PYENV_INSTALL_OPTS)
284
- lst.append(await PLATFORM_PYENV_INSTALL_OPTS[sys.platform].opts())
285
- opts = PyenvInstallOpts().merge(*lst)
286
- return opts
287
-
288
- @cached_nullary
289
- def install_name(self) -> str:
290
- if self._given_install_name is not None:
291
- return self._given_install_name
292
- return self._version + ('-debug' if self._interp_opts.debug else '')
293
-
294
- @async_cached_nullary
295
- async def install_dir(self) -> str:
296
- return str(os.path.join(check.not_none(await self._pyenv.root()), 'versions', self.install_name()))
297
-
298
- @async_cached_nullary
299
- async def install(self) -> str:
300
- opts = await self.opts()
301
- env = {**os.environ, **opts.env}
302
- for k, l in [
303
- ('CFLAGS', opts.cflags),
304
- ('LDFLAGS', opts.ldflags),
305
- ('PYTHON_CONFIGURE_OPTS', opts.conf_opts),
306
- ]:
307
- v = ' '.join(l)
308
- if k in os.environ:
309
- v += ' ' + os.environ[k]
310
- env[k] = v
311
-
312
- conf_args = [
313
- *opts.opts,
314
- self._version,
315
- ]
316
-
317
- full_args: ta.List[str]
318
- if self._given_install_name is not None:
319
- full_args = [
320
- os.path.join(check.not_none(await self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'), # noqa
321
- *conf_args,
322
- await self.install_dir(),
323
- ]
324
- else:
325
- full_args = [
326
- await self._pyenv.exe(),
327
- 'install',
328
- *conf_args,
329
- ]
330
-
331
- await asyncio_subprocesses.check_call(
332
- *full_args,
333
- env=env,
334
- )
335
-
336
- exe = os.path.join(await self.install_dir(), 'bin', 'python')
337
- if not os.path.isfile(exe):
338
- raise RuntimeError(f'Interpreter not found: {exe}')
339
- return exe
340
-
341
-
342
- ##
343
-
344
-
345
- class PyenvInterpProvider(InterpProvider):
346
- @dc.dataclass(frozen=True)
347
- class Options:
348
- inspect: bool = False
349
-
350
- try_update: bool = False
351
-
352
- def __init__(
353
- self,
354
- options: Options = Options(),
355
- *,
356
- pyenv: Pyenv,
357
- inspector: InterpInspector,
358
- log: ta.Optional[logging.Logger] = None,
359
- ) -> None:
360
- super().__init__()
361
-
362
- self._options = options
363
-
364
- self._pyenv = pyenv
365
- self._inspector = inspector
366
- self._log = log
367
-
368
- #
369
-
370
- @staticmethod
371
- def guess_version(s: str) -> ta.Optional[InterpVersion]:
372
- def strip_sfx(s: str, sfx: str) -> ta.Tuple[str, bool]:
373
- if s.endswith(sfx):
374
- return s[:-len(sfx)], True
375
- return s, False
376
- ok = {}
377
- s, ok['debug'] = strip_sfx(s, '-debug')
378
- s, ok['threaded'] = strip_sfx(s, 't')
379
- try:
380
- v = Version(s)
381
- except InvalidVersion:
382
- return None
383
- return InterpVersion(v, InterpOpts(**ok))
384
-
385
- class Installed(ta.NamedTuple):
386
- name: str
387
- exe: str
388
- version: InterpVersion
389
-
390
- async def _make_installed(self, vn: str, ep: str) -> ta.Optional[Installed]:
391
- iv: ta.Optional[InterpVersion]
392
- if self._options.inspect:
393
- try:
394
- iv = check.not_none(await self._inspector.inspect(ep)).iv
395
- except Exception as e: # noqa
396
- return None
397
- else:
398
- iv = self.guess_version(vn)
399
- if iv is None:
400
- return None
401
- return PyenvInterpProvider.Installed(
402
- name=vn,
403
- exe=ep,
404
- version=iv,
405
- )
406
-
407
- async def installed(self) -> ta.Sequence[Installed]:
408
- ret: ta.List[PyenvInterpProvider.Installed] = []
409
- for vn, ep in await self._pyenv.version_exes():
410
- if (i := await self._make_installed(vn, ep)) is None:
411
- if self._log is not None:
412
- self._log.debug('Invalid pyenv version: %s', vn)
413
- continue
414
- ret.append(i)
415
- return ret
416
-
417
- #
418
-
419
- async def get_installed_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
420
- return [i.version for i in await self.installed()]
421
-
422
- async def get_installed_version(self, version: InterpVersion) -> Interp:
423
- for i in await self.installed():
424
- if i.version == version:
425
- return Interp(
426
- exe=i.exe,
427
- version=i.version,
428
- )
429
- raise KeyError(version)
430
-
431
- #
432
-
433
- async def _get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
434
- lst = []
435
-
436
- for vs in await self._pyenv.installable_versions():
437
- if (iv := self.guess_version(vs)) is None:
438
- continue
439
- if iv.opts.debug:
440
- raise Exception('Pyenv installable versions not expected to have debug suffix')
441
- for d in [False, True]:
442
- lst.append(dc.replace(iv, opts=dc.replace(iv.opts, debug=d)))
443
-
444
- return lst
445
-
446
- async def get_installable_versions(self, spec: InterpSpecifier) -> ta.Sequence[InterpVersion]:
447
- lst = await self._get_installable_versions(spec)
448
-
449
- if self._options.try_update and not any(v in spec for v in lst):
450
- if self._pyenv.update():
451
- lst = await self._get_installable_versions(spec)
452
-
453
- return lst
454
-
455
- async def install_version(self, version: InterpVersion) -> Interp:
456
- inst_version = str(version.version)
457
- inst_opts = version.opts
458
- if inst_opts.threaded:
459
- inst_version += 't'
460
- inst_opts = dc.replace(inst_opts, threaded=False)
461
-
462
- installer = PyenvVersionInstaller(
463
- inst_version,
464
- interp_opts=inst_opts,
465
- pyenv=self._pyenv,
466
- )
467
-
468
- exe = await installer.install()
469
- return Interp(exe, version)
omdev/scripts/interp.py CHANGED
@@ -4262,6 +4262,88 @@ class InterpInspector:
4262
4262
  return ret
4263
4263
 
4264
4264
 
4265
+ ########################################
4266
+ # ../pyenv/pyenv.py
4267
+ """
4268
+ TODO:
4269
+ - custom tags
4270
+ - 'aliases'
4271
+ - https://github.com/pyenv/pyenv/pull/2966
4272
+ - https://github.com/pyenv/pyenv/issues/218 (lol)
4273
+ - probably need custom (temp?) definition file
4274
+ - *or* python-build directly just into the versions dir?
4275
+ - optionally install / upgrade pyenv itself
4276
+ - new vers dont need these custom mac opts, only run on old vers
4277
+ """
4278
+
4279
+
4280
+ class Pyenv:
4281
+ def __init__(
4282
+ self,
4283
+ *,
4284
+ root: ta.Optional[str] = None,
4285
+ ) -> None:
4286
+ if root is not None and not (isinstance(root, str) and root):
4287
+ raise ValueError(f'pyenv_root: {root!r}')
4288
+
4289
+ super().__init__()
4290
+
4291
+ self._root_kw = root
4292
+
4293
+ @async_cached_nullary
4294
+ async def root(self) -> ta.Optional[str]:
4295
+ if self._root_kw is not None:
4296
+ return self._root_kw
4297
+
4298
+ if shutil.which('pyenv'):
4299
+ return await asyncio_subprocesses.check_output_str('pyenv', 'root')
4300
+
4301
+ d = os.path.expanduser('~/.pyenv')
4302
+ if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
4303
+ return d
4304
+
4305
+ return None
4306
+
4307
+ @async_cached_nullary
4308
+ async def exe(self) -> str:
4309
+ return os.path.join(check.not_none(await self.root()), 'bin', 'pyenv')
4310
+
4311
+ async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
4312
+ if (root := await self.root()) is None:
4313
+ return []
4314
+ ret = []
4315
+ vp = os.path.join(root, 'versions')
4316
+ if os.path.isdir(vp):
4317
+ for dn in os.listdir(vp):
4318
+ ep = os.path.join(vp, dn, 'bin', 'python')
4319
+ if not os.path.isfile(ep):
4320
+ continue
4321
+ ret.append((dn, ep))
4322
+ return ret
4323
+
4324
+ async def installable_versions(self) -> ta.List[str]:
4325
+ if await self.root() is None:
4326
+ return []
4327
+ ret = []
4328
+ s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
4329
+ for l in s.splitlines():
4330
+ if not l.startswith(' '):
4331
+ continue
4332
+ l = l.strip()
4333
+ if not l:
4334
+ continue
4335
+ ret.append(l)
4336
+ return ret
4337
+
4338
+ async def update(self) -> bool:
4339
+ if (root := await self.root()) is None:
4340
+ return False
4341
+ if not os.path.isdir(os.path.join(root, '.git')):
4342
+ return False
4343
+ await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
4344
+ return True
4345
+
4346
+
4265
4347
  ########################################
4266
4348
  # ../resolvers.py
4267
4349
 
@@ -4500,88 +4582,7 @@ class SystemInterpProvider(InterpProvider):
4500
4582
 
4501
4583
 
4502
4584
  ########################################
4503
- # ../pyenv/pyenv.py
4504
- """
4505
- TODO:
4506
- - custom tags
4507
- - 'aliases'
4508
- - https://github.com/pyenv/pyenv/pull/2966
4509
- - https://github.com/pyenv/pyenv/issues/218 (lol)
4510
- - probably need custom (temp?) definition file
4511
- - *or* python-build directly just into the versions dir?
4512
- - optionally install / upgrade pyenv itself
4513
- - new vers dont need these custom mac opts, only run on old vers
4514
- """
4515
-
4516
-
4517
- ##
4518
-
4519
-
4520
- class Pyenv:
4521
- def __init__(
4522
- self,
4523
- *,
4524
- root: ta.Optional[str] = None,
4525
- ) -> None:
4526
- if root is not None and not (isinstance(root, str) and root):
4527
- raise ValueError(f'pyenv_root: {root!r}')
4528
-
4529
- super().__init__()
4530
-
4531
- self._root_kw = root
4532
-
4533
- @async_cached_nullary
4534
- async def root(self) -> ta.Optional[str]:
4535
- if self._root_kw is not None:
4536
- return self._root_kw
4537
-
4538
- if shutil.which('pyenv'):
4539
- return await asyncio_subprocesses.check_output_str('pyenv', 'root')
4540
-
4541
- d = os.path.expanduser('~/.pyenv')
4542
- if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
4543
- return d
4544
-
4545
- return None
4546
-
4547
- @async_cached_nullary
4548
- async def exe(self) -> str:
4549
- return os.path.join(check.not_none(await self.root()), 'bin', 'pyenv')
4550
-
4551
- async def version_exes(self) -> ta.List[ta.Tuple[str, str]]:
4552
- if (root := await self.root()) is None:
4553
- return []
4554
- ret = []
4555
- vp = os.path.join(root, 'versions')
4556
- if os.path.isdir(vp):
4557
- for dn in os.listdir(vp):
4558
- ep = os.path.join(vp, dn, 'bin', 'python')
4559
- if not os.path.isfile(ep):
4560
- continue
4561
- ret.append((dn, ep))
4562
- return ret
4563
-
4564
- async def installable_versions(self) -> ta.List[str]:
4565
- if await self.root() is None:
4566
- return []
4567
- ret = []
4568
- s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
4569
- for l in s.splitlines():
4570
- if not l.startswith(' '):
4571
- continue
4572
- l = l.strip()
4573
- if not l:
4574
- continue
4575
- ret.append(l)
4576
- return ret
4577
-
4578
- async def update(self) -> bool:
4579
- if (root := await self.root()) is None:
4580
- return False
4581
- if not os.path.isdir(os.path.join(root, '.git')):
4582
- return False
4583
- await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
4584
- return True
4585
+ # ../pyenv/install.py
4585
4586
 
4586
4587
 
4587
4588
  ##
@@ -4819,7 +4820,27 @@ class PyenvVersionInstaller:
4819
4820
  return exe
4820
4821
 
4821
4822
 
4822
- ##
4823
+ ########################################
4824
+ # ../providers/inject.py
4825
+
4826
+
4827
+ def bind_interp_providers() -> InjectorBindings:
4828
+ lst: ta.List[InjectorBindingOrBindings] = [
4829
+ inj.bind_array(InterpProvider),
4830
+ inj.bind_array_type(InterpProvider, InterpProviders),
4831
+
4832
+ inj.bind(RunningInterpProvider, singleton=True),
4833
+ inj.bind(InterpProvider, to_key=RunningInterpProvider, array=True),
4834
+
4835
+ inj.bind(SystemInterpProvider, singleton=True),
4836
+ inj.bind(InterpProvider, to_key=SystemInterpProvider, array=True),
4837
+ ]
4838
+
4839
+ return inj.as_bindings(*lst)
4840
+
4841
+
4842
+ ########################################
4843
+ # ../pyenv/provider.py
4823
4844
 
4824
4845
 
4825
4846
  class PyenvInterpProvider(InterpProvider):
@@ -4949,25 +4970,6 @@ class PyenvInterpProvider(InterpProvider):
4949
4970
  return Interp(exe, version)
4950
4971
 
4951
4972
 
4952
- ########################################
4953
- # ../providers/inject.py
4954
-
4955
-
4956
- def bind_interp_providers() -> InjectorBindings:
4957
- lst: ta.List[InjectorBindingOrBindings] = [
4958
- inj.bind_array(InterpProvider),
4959
- inj.bind_array_type(InterpProvider, InterpProviders),
4960
-
4961
- inj.bind(RunningInterpProvider, singleton=True),
4962
- inj.bind(InterpProvider, to_key=RunningInterpProvider, array=True),
4963
-
4964
- inj.bind(SystemInterpProvider, singleton=True),
4965
- inj.bind(InterpProvider, to_key=SystemInterpProvider, array=True),
4966
- ]
4967
-
4968
- return inj.as_bindings(*lst)
4969
-
4970
-
4971
4973
  ########################################
4972
4974
  # ../pyenv/inject.py
4973
4975