omdev 0.0.0.dev181__py3-none-any.whl → 0.0.0.dev183__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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