mirror of
https://github.com/daylinmorgan/viv.git
synced 2024-12-22 10:40:44 -06:00
feat: add script mode to viv run
This commit is contained in:
parent
1b180d5b0b
commit
853e6e63ae
2 changed files with 85 additions and 42 deletions
|
@ -34,7 +34,6 @@ select = ["E","F","I"]
|
||||||
ignore = ["E402"]
|
ignore = ["E402"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
warn_return_any = true
|
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
disallow_untyped_defs = true
|
disallow_untyped_defs = true
|
||||||
warn_unused_configs = true
|
warn_unused_configs = true
|
||||||
|
|
126
src/viv/viv.py
126
src/viv/viv.py
|
@ -50,7 +50,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.5a5"
|
__version__ = "23.5a5-9-gaf1a87d-dev"
|
||||||
|
|
||||||
|
|
||||||
class Spinner:
|
class Spinner:
|
||||||
|
@ -831,31 +831,34 @@ def resolve_deps(args: Namespace) -> List[str]:
|
||||||
return resolved_spec
|
return resolved_spec
|
||||||
|
|
||||||
|
|
||||||
def fetch_source(reference: str) -> str:
|
def fetch_script(url: str) -> str:
|
||||||
try:
|
try:
|
||||||
r = urlopen(
|
r = urlopen(url)
|
||||||
"https://raw.githubusercontent.com/daylinmorgan/viv/"
|
except (HTTPError, ValueError) as e:
|
||||||
+ reference
|
error(f"Failed to fetch from remote url:\n {a.bold}{url}{a.end}")
|
||||||
+ "/src/viv/viv.py"
|
echo(
|
||||||
|
"see below:" + a.style("-> ", "red").join(["\n"] + repr(e).splitlines()),
|
||||||
|
style="red",
|
||||||
)
|
)
|
||||||
except HTTPError as e:
|
|
||||||
error(
|
|
||||||
"Issue updating viv see below:"
|
|
||||||
+ a.style("-> ", "red").join(["\n"] + repr(e).splitlines())
|
|
||||||
)
|
|
||||||
if "404" in repr(e):
|
|
||||||
echo("Please check your reference is valid.", style="red")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
src = r.read()
|
return r.read().decode("utf-8")
|
||||||
(hash := hashlib.sha256()).update(src)
|
|
||||||
|
|
||||||
|
def fetch_source(reference: str) -> str:
|
||||||
|
src = fetch_script(
|
||||||
|
"https://raw.githubusercontent.com/daylinmorgan/viv/"
|
||||||
|
+ reference
|
||||||
|
+ "/src/viv/viv.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
(hash := hashlib.sha256()).update(src.encode())
|
||||||
sha256 = hash.hexdigest()
|
sha256 = hash.hexdigest()
|
||||||
|
|
||||||
cached_src_file = c.srccache / f"{sha256}.py"
|
cached_src_file = c.srccache / f"{sha256}.py"
|
||||||
|
|
||||||
if not cached_src_file.is_file():
|
if not cached_src_file.is_file():
|
||||||
with cached_src_file.open("w") as f:
|
cached_src_file.write_text(src)
|
||||||
f.write(src.decode())
|
|
||||||
|
|
||||||
return sha256
|
return sha256
|
||||||
|
|
||||||
|
@ -871,7 +874,12 @@ class Config:
|
||||||
"""viv config manager"""
|
"""viv config manager"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._cache = Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) / "viv"
|
self._cache = Path(
|
||||||
|
os.getenv(
|
||||||
|
"VIV_CACHE",
|
||||||
|
Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) / "viv",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _ensure(self, p: Path) -> Path:
|
def _ensure(self, p: Path) -> Path:
|
||||||
p.mkdir(parents=True, exist_ok=True)
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
@ -1193,40 +1201,65 @@ class Viv:
|
||||||
|
|
||||||
def run(self, args: Namespace) -> None:
|
def run(self, args: Namespace) -> None:
|
||||||
"""\
|
"""\
|
||||||
run an app with an on-demand venv
|
run an app/script with an on-demand venv
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
viv r pycowsay -- "viv isn't venv\!"
|
viv r pycowsay -- "viv isn't venv\!"
|
||||||
viv r rich -b python -- -m rich
|
viv r rich -b python -- -m rich
|
||||||
|
viv r -s <remote python script>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_, bin = self._pick_bin(args)
|
if args.script:
|
||||||
spec = combined_spec(args.reqs, args.requirements)
|
env = os.environ
|
||||||
vivenv = ViVenv(spec)
|
name = args.script.split("/")[-1]
|
||||||
|
|
||||||
# TODO: respect a VIV_RUN_MODE env variable as the same as keep i.e.
|
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
||||||
# ephemeral (default), semi-ephemeral (persist inside /tmp), or
|
tmppath = Path(tmpdir)
|
||||||
# persist (use c.cache)
|
script = tmppath / name
|
||||||
|
if not self.local_source:
|
||||||
|
(tmppath / "viv.py").write_text(
|
||||||
|
fetch_script(
|
||||||
|
"https://raw.githubusercontent.com/daylinmorgan/viv/script-runner/src/viv/viv.py"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not vivenv.loaded or os.getenv("VIV_FORCE"):
|
script.write_text(fetch_script(args.script))
|
||||||
if not args.keep:
|
if not args.keep:
|
||||||
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
env.update({"VIV_CACHE": tmpdir})
|
||||||
vivenv.path = Path(tmpdir)
|
|
||||||
|
sys.exit(
|
||||||
|
subprocess.run(
|
||||||
|
[sys.executable, script, *args.rest], env=env
|
||||||
|
).returncode
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_, bin = self._pick_bin(args)
|
||||||
|
spec = combined_spec(args.reqs, args.requirements)
|
||||||
|
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 os.getenv("VIV_FORCE"):
|
||||||
|
if not args.keep:
|
||||||
|
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
||||||
|
vivenv.path = Path(tmpdir)
|
||||||
|
vivenv.create()
|
||||||
|
vivenv.install_pkgs()
|
||||||
|
sys.exit(
|
||||||
|
subprocess.run(
|
||||||
|
[vivenv.path / "bin" / bin, *args.rest]
|
||||||
|
).returncode
|
||||||
|
)
|
||||||
|
else:
|
||||||
vivenv.create()
|
vivenv.create()
|
||||||
vivenv.install_pkgs()
|
vivenv.install_pkgs()
|
||||||
sys.exit(
|
|
||||||
subprocess.run(
|
|
||||||
[vivenv.path / "bin" / bin, *args.rest]
|
|
||||||
).returncode
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
vivenv.create()
|
|
||||||
vivenv.install_pkgs()
|
|
||||||
|
|
||||||
vivenv.touch()
|
vivenv.touch()
|
||||||
vivenv.meta.write()
|
vivenv.meta.write()
|
||||||
|
|
||||||
sys.exit(subprocess.run([vivenv.path / "bin" / bin, *args.rest]).returncode)
|
sys.exit(subprocess.run([vivenv.path / "bin" / bin, *args.rest]).returncode)
|
||||||
|
|
||||||
|
|
||||||
class Arg:
|
class Arg:
|
||||||
|
@ -1267,6 +1300,9 @@ class Cli:
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
("remove",): [Arg("vivenv", help="name/hash of vivenv", nargs="*")],
|
("remove",): [Arg("vivenv", help="name/hash of vivenv", nargs="*")],
|
||||||
|
("run",): [
|
||||||
|
Arg("-s", "--script", help="remote script to run", metavar="<script>")
|
||||||
|
],
|
||||||
("exe", "info"): [Arg("vivenv", help="name/hash of vivenv")],
|
("exe", "info"): [Arg("vivenv", help="name/hash of vivenv")],
|
||||||
("list", "info"): [
|
("list", "info"): [
|
||||||
Arg(
|
Arg(
|
||||||
|
@ -1398,7 +1434,7 @@ class Cli:
|
||||||
self.parsers[grp].add_argument(*arg.args, **arg.kwargs)
|
self.parsers[grp].add_argument(*arg.args, **arg.kwargs)
|
||||||
|
|
||||||
def _validate_args(self, args: Namespace) -> None:
|
def _validate_args(self, args: Namespace) -> None:
|
||||||
if args.func.__name__ in ("freeze", "shim", "run"):
|
if args.func.__name__ in ("freeze", "shim"):
|
||||||
if not args.reqs:
|
if not args.reqs:
|
||||||
error("must specify a requirement", code=1)
|
error("must specify a requirement", code=1)
|
||||||
if args.func.__name__ in ("freeze", "shim"):
|
if args.func.__name__ in ("freeze", "shim"):
|
||||||
|
@ -1439,6 +1475,14 @@ class Cli:
|
||||||
+ " shouldn't be used with a git-based installation",
|
+ " shouldn't be used with a git-based installation",
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
|
if args.func.__name__ == "run":
|
||||||
|
if not (args.reqs or args.script):
|
||||||
|
error("must specify a requirement or --script", code=1)
|
||||||
|
if args.script and args.reqs:
|
||||||
|
error(
|
||||||
|
"script mode does not support additional requirements currently",
|
||||||
|
code=1,
|
||||||
|
)
|
||||||
|
|
||||||
def _get_subcmd_parser(
|
def _get_subcmd_parser(
|
||||||
self,
|
self,
|
||||||
|
|
Loading…
Reference in a new issue