feat: simplify run logic and add support for VIV_RUN_MODE

This commit is contained in:
Daylin Morgan 2023-08-09 10:51:50 -05:00
parent 0269b46ab1
commit d3e1afdd55
Signed by: daylin
GPG key ID: C1E52E7DD81DF79F

View file

@ -32,6 +32,7 @@ from argparse import (
_SubParsersAction, _SubParsersAction,
) )
from argparse import ArgumentParser as StdArgParser from argparse import ArgumentParser as StdArgParser
from contextlib import contextmanager
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
@ -55,7 +56,7 @@ from typing import (
from urllib.error import HTTPError from urllib.error import HTTPError
from urllib.request import urlopen from urllib.request import urlopen
__version__ = "23.8a3-2-g9079ecf-dev" __version__ = "23.8a3-5-g5ae4cf5-dev"
class Spinner: class Spinner:
@ -122,6 +123,7 @@ def _path_ok(p: Path) -> Path:
class Env: class Env:
defaults = dict( defaults = dict(
viv_bin_dir=Path.home() / ".local" / "bin", viv_bin_dir=Path.home() / ".local" / "bin",
viv_run_mode="ephemeral",
xdg_cache_home=Path.home() / ".cache", xdg_cache_home=Path.home() / ".cache",
xdg_data_home=Path.home() / ".local" / "share", xdg_data_home=Path.home() / ".local" / "share",
) )
@ -954,6 +956,28 @@ class ViVenv:
def files_exist(self) -> bool: def files_exist(self) -> bool:
return len([f for f in self.meta.files if Path(f).is_file()]) == 0 return len([f for f in self.meta.files if Path(f).is_file()]) == 0
@contextmanager
def use(self, keep: bool = True) -> None:
run_mode = Env().viv_run_mode
_path = self.path
try:
if keep or run_mode == "persist":
yield
elif run_mode == "ephemeral":
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
self.set_path(Path(tmpdir))
yield
elif run_mode == "semi-ephemeral":
(
ephemeral_cache := (
Path(tempfile.gettempdir()) / "viv-ephemeral-cache" / "venvs"
)
).mkdir(exist_ok=True, parents=True)
self.set_path(ephemeral_cache / self.name)
yield
finally:
self.set_path(_path)
def show(self) -> None: def show(self) -> None:
_id = ( _id = (
self.meta.id[:8] self.meta.id[:8]
@ -1024,14 +1048,6 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> Path:
return vivenv.path return vivenv.path
def get_venvs() -> Dict[str, ViVenv]:
vivenvs = {}
for p in Cfg().cache_venv.iterdir():
vivenv = ViVenv.load(p.name)
vivenvs[vivenv.name] = vivenv
return vivenvs
def combined_spec(reqs: List[str], requirements: Path) -> List[str]: def combined_spec(reqs: List[str], requirements: Path) -> List[str]:
if requirements: if requirements:
with requirements.open("r") as f: with requirements.open("r") as f:
@ -1635,17 +1651,9 @@ class Viv:
env = os.environ env = os.environ
name = script.split("/")[-1] name = script.split("/")[-1]
# TODO: reduce boilerplate and dry out
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir: with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
tmppath = Path(tmpdir) tmppath = Path(tmpdir)
scriptpath = tmppath / name scriptpath = tmppath / name
if not self.local_source:
(tmppath / "viv.py").write_text(
# TODO: use latest tag once ready
fetch_script(
"https://raw.githubusercontent.com/daylinmorgan/viv/dev/src/viv/viv.py"
)
)
if Path(script).is_file(): if Path(script).is_file():
script_text = Path(script).read_text() script_text = Path(script).read_text()
@ -1661,6 +1669,14 @@ class Viv:
"`viv` API can't be used in the same script" "`viv` API can't be used in the same script"
) )
if not self.local_source and viv_used:
(tmppath / "viv.py").write_text(
# TODO: use latest tag once ready
fetch_script(
"https://raw.githubusercontent.com/daylinmorgan/viv/dev/src/viv/viv.py"
)
)
scriptpath.write_text(script_text) scriptpath.write_text(script_text)
if not keep: if not keep:
@ -1708,25 +1724,17 @@ class Viv:
_, bin = self._pick_bin(reqs, bin) _, bin = self._pick_bin(reqs, bin)
vivenv = ViVenv(spec) vivenv = ViVenv(spec)
# TODO: respect a VIV_RUN_MODE env variable as the same as keep i.e.
# ephemeral (default), semi-ephemeral (persist inside /tmp), or
# persist (use c.cache)
if not vivenv.loaded or Env().viv_force: if not vivenv.loaded or Env().viv_force:
if not keep: with vivenv.use(keep=keep):
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
vivenv.set_path(Path(tmpdir))
vivenv.create() vivenv.create()
vivenv.install_pkgs() vivenv.install_pkgs()
vivenv.bin_exists(bin) vivenv.bin_exists(bin)
subprocess_run_quit([vivenv.path / "bin" / bin, *rest])
else:
vivenv.create()
vivenv.install_pkgs()
# TODO: refactor this logic elsewhere
if keep or Env().run_mode != "ephemeral":
vivenv.touch() vivenv.touch()
vivenv.meta.write() vivenv.meta.write(vivenv.path / "vivmeta.json")
vivenv.bin_exists(bin)
subprocess_run_quit([vivenv.path / "bin" / bin, *rest]) subprocess_run_quit([vivenv.path / "bin" / bin, *rest])