mirror of
https://github.com/daylinmorgan/viv.git
synced 2024-11-13 20:47:53 -06:00
refactor: use named args/types
This commit is contained in:
parent
fc7a77175c
commit
9b533c9c32
1 changed files with 227 additions and 173 deletions
400
src/viv/viv.py
400
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-7-g6bf9a91-dev"
|
__version__ = "23.5a5-10-g5326324-dev"
|
||||||
|
|
||||||
|
|
||||||
class Spinner:
|
class Spinner:
|
||||||
|
@ -117,7 +117,7 @@ class Env:
|
||||||
xdg_data_home=Path.home() / ".local" / "share",
|
xdg_data_home=Path.home() / ".local" / "share",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr: str) -> Any:
|
||||||
if not attr.startswith("_") and (defined := getattr(self, f"_{attr}")):
|
if not attr.startswith("_") and (defined := getattr(self, f"_{attr}")):
|
||||||
return defined
|
return defined
|
||||||
else:
|
else:
|
||||||
|
@ -129,11 +129,11 @@ class Env:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _viv_spec(self) -> List[str]:
|
def _viv_spec(self) -> List[str]:
|
||||||
return filter(None, os.getenv("VIV_SPEC", "").split(" "))
|
return [i for i in os.getenv("VIV_SPEC", "").split(" ") if i]
|
||||||
|
|
||||||
|
|
||||||
class Cache:
|
class Cache:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self.base = Env().viv_cache
|
self.base = Env().viv_cache
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -153,7 +153,9 @@ class Cache:
|
||||||
class Cfg:
|
class Cfg:
|
||||||
@property
|
@property
|
||||||
def src(self) -> Path:
|
def src(self) -> Path:
|
||||||
return Path(Env().xdg_data_home) / "viv" / "viv.py"
|
p = Path(Env().xdg_data_home) / "viv" / "viv.py"
|
||||||
|
p.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
class Ansi:
|
class Ansi:
|
||||||
|
@ -856,8 +858,8 @@ def combined_spec(reqs: List[str], requirements: Path) -> List[str]:
|
||||||
return reqs
|
return reqs
|
||||||
|
|
||||||
|
|
||||||
def resolve_deps(args: Namespace) -> List[str]:
|
def resolve_deps(reqs: List[str], requirements: Path) -> List[str]:
|
||||||
spec = combined_spec(args.reqs, args.requirements)
|
spec = combined_spec(reqs, requirements)
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"pip",
|
"pip",
|
||||||
|
@ -918,16 +920,18 @@ def make_executable(path: Path) -> None:
|
||||||
|
|
||||||
|
|
||||||
def uses_viv(txt: str) -> bool:
|
def uses_viv(txt: str) -> bool:
|
||||||
return re.search(
|
return bool(
|
||||||
"""
|
re.search(
|
||||||
|
"""
|
||||||
\s*__import__\(\s*["']viv["']\s*\).use\(.*
|
\s*__import__\(\s*["']viv["']\s*\).use\(.*
|
||||||
|
|
|
|
||||||
from\ viv\ import\ use
|
from\ viv\ import\ use
|
||||||
|
|
|
|
||||||
import\ viv
|
import\ viv
|
||||||
""",
|
""",
|
||||||
txt,
|
txt,
|
||||||
re.VERBOSE,
|
re.VERBOSE,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -979,7 +983,7 @@ class Viv:
|
||||||
else:
|
else:
|
||||||
error(f"no matches found for {name_id}", code=1)
|
error(f"no matches found for {name_id}", code=1)
|
||||||
|
|
||||||
def remove(self, args: Namespace) -> None:
|
def remove(self, vivenvs: List[str]) -> None:
|
||||||
"""\
|
"""\
|
||||||
remove a vivenv
|
remove a vivenv
|
||||||
|
|
||||||
|
@ -987,7 +991,7 @@ class Viv:
|
||||||
`viv rm $(viv l -q)`
|
`viv rm $(viv l -q)`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for name in args.vivenv:
|
for name in vivenvs:
|
||||||
vivenv = self._match_vivenv(name)
|
vivenv = self._match_vivenv(name)
|
||||||
if vivenv.path.is_dir():
|
if vivenv.path.is_dir():
|
||||||
echo(f"removing {vivenv.name}")
|
echo(f"removing {vivenv.name}")
|
||||||
|
@ -998,11 +1002,19 @@ class Viv:
|
||||||
code=1,
|
code=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
def freeze(self, args: Namespace) -> None:
|
def freeze(
|
||||||
|
self,
|
||||||
|
reqs: List[str],
|
||||||
|
requirements: Path,
|
||||||
|
keep: bool,
|
||||||
|
standalone: bool,
|
||||||
|
path: str,
|
||||||
|
args: Namespace,
|
||||||
|
) -> None:
|
||||||
"""create import statement from package spec"""
|
"""create import statement from package spec"""
|
||||||
|
|
||||||
spec = resolve_deps(args)
|
spec = resolve_deps(reqs, requirements)
|
||||||
if args.keep:
|
if keep:
|
||||||
# re-create env again since path's are hard-coded
|
# re-create env again since path's are hard-coded
|
||||||
vivenv = ViVenv(spec)
|
vivenv = ViVenv(spec)
|
||||||
|
|
||||||
|
@ -1018,26 +1030,26 @@ class Viv:
|
||||||
|
|
||||||
echo("see below for import statements\n")
|
echo("see below for import statements\n")
|
||||||
|
|
||||||
if args.standalone:
|
if standalone:
|
||||||
sys.stdout.write(t.standalone(spec))
|
sys.stdout.write(t.standalone(spec))
|
||||||
return
|
return
|
||||||
|
|
||||||
if args.path and not self.local_source:
|
if path and not self.local_source:
|
||||||
error("No local viv found to import from", code=1)
|
error("No local viv found to import from", code=1)
|
||||||
|
|
||||||
sys.stdout.write(t.frozen_import(args.path, self.local_source, spec))
|
sys.stdout.write(t.frozen_import(path, self.local_source, spec))
|
||||||
|
|
||||||
def list(self, args: Namespace) -> None:
|
def list(self, quiet: bool, full: bool, use_json: bool) -> None:
|
||||||
"""list all vivenvs"""
|
"""list all vivenvs"""
|
||||||
|
|
||||||
if args.quiet:
|
if quiet:
|
||||||
sys.stdout.write("\n".join(self.vivenvs) + "\n")
|
sys.stdout.write("\n".join(self.vivenvs) + "\n")
|
||||||
elif len(self.vivenvs) == 0:
|
elif len(self.vivenvs) == 0:
|
||||||
echo("no vivenvs setup")
|
echo("no vivenvs setup")
|
||||||
elif args.full:
|
elif full:
|
||||||
for _, vivenv in self.vivenvs.items():
|
for _, vivenv in self.vivenvs.items():
|
||||||
vivenv.tree()
|
vivenv.tree()
|
||||||
elif args.json:
|
elif use_json:
|
||||||
sys.stdout.write(
|
sys.stdout.write(
|
||||||
json.dumps({k: v.meta.__dict__ for k, v in self.vivenvs.items()})
|
json.dumps({k: v.meta.__dict__ for k, v in self.vivenvs.items()})
|
||||||
)
|
)
|
||||||
|
@ -1045,7 +1057,7 @@ class Viv:
|
||||||
for _, vivenv in self.vivenvs.items():
|
for _, vivenv in self.vivenvs.items():
|
||||||
vivenv.show()
|
vivenv.show()
|
||||||
|
|
||||||
def exe(self, args: Namespace) -> None:
|
def exe(self, vivenv_id: str, cmd: str, rest: List[str]) -> None:
|
||||||
"""\
|
"""\
|
||||||
run binary/script in existing vivenv
|
run binary/script in existing vivenv
|
||||||
|
|
||||||
|
@ -1054,31 +1066,31 @@ class Viv:
|
||||||
viv exe <vivenv> python -- script.py
|
viv exe <vivenv> python -- script.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
vivenv = self._match_vivenv(args.vivenv)
|
vivenv = self._match_vivenv(vivenv_id)
|
||||||
bin = vivenv.path / "bin" / args.cmd
|
bin = vivenv.path / "bin" / cmd
|
||||||
|
|
||||||
if not bin.exists():
|
if not bin.exists():
|
||||||
error(f"{args.cmd} does not exist in {vivenv.name}", code=1)
|
error(f"{cmd} does not exist in {vivenv.name}", code=1)
|
||||||
|
|
||||||
cmd = [bin, *args.rest]
|
full_cmd = [str(bin), *rest]
|
||||||
|
|
||||||
run(cmd, verbose=True)
|
run(full_cmd, verbose=True)
|
||||||
|
|
||||||
def info(self, args: Namespace) -> None:
|
def info(self, vivenv_id: str, use_json: bool) -> None:
|
||||||
"""get metadata about a vivenv"""
|
"""get metadata about a vivenv"""
|
||||||
vivenv = self._match_vivenv(args.vivenv)
|
vivenv = self._match_vivenv(vivenv_id)
|
||||||
metadata_file = vivenv.path / "vivmeta.json"
|
metadata_file = vivenv.path / "vivmeta.json"
|
||||||
|
|
||||||
if not metadata_file.is_file():
|
if not metadata_file.is_file():
|
||||||
error(f"Unable to find metadata for vivenv: {args.vivenv}", code=1)
|
error(f"Unable to find metadata for vivenv: {vivenv_id}", code=1)
|
||||||
if args.json:
|
if use_json:
|
||||||
sys.stdout.write(json.dumps(vivenv.meta.__dict__))
|
sys.stdout.write(json.dumps(vivenv.meta.__dict__))
|
||||||
else:
|
else:
|
||||||
vivenv.tree()
|
vivenv.tree()
|
||||||
|
|
||||||
def _install_local_src(self, sha256: str, src: Path, cli: Path, yes: bool) -> None:
|
def _install_local_src(self, sha256: str, src: Path, cli: Path, yes: bool) -> None:
|
||||||
echo("updating local source copy of viv")
|
echo("updating local source copy of viv")
|
||||||
shutil.copy(Cache.src / f"{sha256}.py", src)
|
shutil.copy(Cache().src / f"{sha256}.py", src)
|
||||||
make_executable(src)
|
make_executable(src)
|
||||||
echo("symlinking cli")
|
echo("symlinking cli")
|
||||||
|
|
||||||
|
@ -1097,103 +1109,133 @@ class Viv:
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_new_version(self, ref: str) -> Tuple[str, str]:
|
def _get_new_version(self, ref: str) -> Tuple[str, str]:
|
||||||
sys.path.append(str(Cache.src))
|
sys.path.append(str(Cache().src))
|
||||||
return (sha256 := fetch_source(ref)), __import__(sha256).__version__
|
return (sha256 := fetch_source(ref)), __import__(sha256).__version__
|
||||||
|
|
||||||
def manage(self, args: Namespace) -> None:
|
def manage(self) -> None:
|
||||||
"""manage viv itself"""
|
"""manage viv itself"""
|
||||||
|
|
||||||
if args.subcmd == "show":
|
def manage_show(
|
||||||
if args.pythonpath:
|
self,
|
||||||
if self.local and self.local_source:
|
pythonpath: bool = False,
|
||||||
sys.stdout.write(str(self.local_source.parent) + "\n")
|
) -> None:
|
||||||
else:
|
"""manage viv itself"""
|
||||||
error("expected to find a local installation", code=1)
|
if pythonpath:
|
||||||
|
if self.local and self.local_source:
|
||||||
|
sys.stdout.write(str(self.local_source.parent) + "\n")
|
||||||
else:
|
else:
|
||||||
echo("Current:")
|
error("expected to find a local installation", code=1)
|
||||||
sys.stderr.write(
|
else:
|
||||||
t.show(
|
echo("Current:")
|
||||||
cli=shutil.which("viv"),
|
sys.stderr.write(
|
||||||
running=self.running_source,
|
t.show(
|
||||||
local=self.local_source,
|
cli=shutil.which("viv"),
|
||||||
)
|
running=self.running_source,
|
||||||
|
local=self.local_source,
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
elif args.subcmd == "update":
|
def manage_update(
|
||||||
sha256, next_version = self._get_new_version(args.ref)
|
self,
|
||||||
|
ref: str,
|
||||||
|
src: Path,
|
||||||
|
cli: Path,
|
||||||
|
yes: bool,
|
||||||
|
) -> None:
|
||||||
|
sha256, next_version = self._get_new_version(ref)
|
||||||
|
|
||||||
if self.local_version == next_version:
|
if self.local_version == next_version:
|
||||||
echo(f"no change between {args.ref} and local version")
|
echo(f"no change between {ref} and local version")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if confirm(
|
if confirm(
|
||||||
"Would you like to perform the above installation steps?",
|
"Would you like to perform the above installation steps?",
|
||||||
t.update(self.local_source, args.cli, self.local_version, next_version),
|
t.update(self.local_source, cli, self.local_version, next_version),
|
||||||
yes=args.yes,
|
yes=yes,
|
||||||
):
|
):
|
||||||
self._install_local_src(
|
self._install_local_src(
|
||||||
sha256,
|
sha256,
|
||||||
Path(
|
Path(
|
||||||
args.src if not self.local_source else self.local_source,
|
src if not self.local_source else self.local_source,
|
||||||
),
|
),
|
||||||
args.cli,
|
cli,
|
||||||
args.yes,
|
yes,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif args.subcmd == "install":
|
def manage_install(
|
||||||
sha256, downloaded_version = self._get_new_version(args.ref)
|
self,
|
||||||
|
ref: str,
|
||||||
|
src: Path,
|
||||||
|
cli: Path,
|
||||||
|
yes: bool,
|
||||||
|
) -> None:
|
||||||
|
sha256, downloaded_version = self._get_new_version(ref)
|
||||||
|
|
||||||
echo(f"Downloaded version: {downloaded_version}")
|
echo(f"Downloaded version: {downloaded_version}")
|
||||||
|
|
||||||
# TODO: see if file is actually where
|
# TODO: see if file is actually where
|
||||||
# we are about to install and give more instructions
|
# we are about to install and give more instructions
|
||||||
|
|
||||||
if confirm(
|
if confirm(
|
||||||
"Would you like to perform the above installation steps?",
|
"Would you like to perform the above installation steps?",
|
||||||
t.install(args.src, args.cli),
|
t.install(src, cli),
|
||||||
yes=args.yes,
|
yes=yes,
|
||||||
):
|
):
|
||||||
self._install_local_src(sha256, args.src, args.cli)
|
self._install_local_src(sha256, src, cli, yes)
|
||||||
|
|
||||||
elif args.subcmd == "purge":
|
def manage_purge(
|
||||||
to_remove = []
|
self,
|
||||||
if Cache.base.is_dir():
|
ref: str,
|
||||||
to_remove.append(Cache.base)
|
src: Path,
|
||||||
if args.src.is_file():
|
cli: Path,
|
||||||
to_remove.append(
|
yes: bool,
|
||||||
args.src.parent if args.src == (Cfg().src) else args.src
|
) -> None:
|
||||||
)
|
to_remove = []
|
||||||
if self.local_source and self.local_source.is_file():
|
if Cache().base.is_dir():
|
||||||
if self.local_source.parent.name == "viv":
|
to_remove.append(Cache().base)
|
||||||
to_remove.append(self.local_source.parent)
|
if src.is_file():
|
||||||
|
to_remove.append(src.parent if src == (Cfg().src) else src)
|
||||||
|
if self.local_source and self.local_source.is_file():
|
||||||
|
if self.local_source.parent.name == "viv":
|
||||||
|
to_remove.append(self.local_source.parent)
|
||||||
|
else:
|
||||||
|
to_remove.append(self.local_source)
|
||||||
|
|
||||||
|
if cli.is_file():
|
||||||
|
to_remove.append(cli)
|
||||||
|
|
||||||
|
to_remove = list(set(to_remove))
|
||||||
|
if confirm(
|
||||||
|
"Remove the above files/directories?",
|
||||||
|
"\n".join(f" - {a.red}{p}{a.end}" for p in to_remove) + "\n",
|
||||||
|
yes=yes,
|
||||||
|
):
|
||||||
|
for p in to_remove:
|
||||||
|
if p.is_dir():
|
||||||
|
shutil.rmtree(p)
|
||||||
else:
|
else:
|
||||||
to_remove.append(self.local_source)
|
p.unlink()
|
||||||
|
|
||||||
if args.cli.is_file():
|
echo(
|
||||||
to_remove.append(args.cli)
|
"to re-install use: "
|
||||||
|
"`python3 <(curl -fsSL viv.dayl.in/viv.py) manage install`"
|
||||||
|
)
|
||||||
|
|
||||||
to_remove = list(set(to_remove))
|
def _pick_bin(self, reqs: List[str], bin: str) -> Tuple[str, str]:
|
||||||
if confirm(
|
default = re.split(r"[=><~!*]+", reqs[0])[0]
|
||||||
"Remove the above files/directories?",
|
return default, (default if not bin else bin)
|
||||||
"\n".join(f" - {a.red}{p}{a.end}" for p in to_remove) + "\n",
|
|
||||||
yes=args.yes,
|
|
||||||
):
|
|
||||||
for p in to_remove:
|
|
||||||
if p.is_dir():
|
|
||||||
shutil.rmtree(p)
|
|
||||||
else:
|
|
||||||
p.unlink()
|
|
||||||
|
|
||||||
echo(
|
def shim(
|
||||||
"to re-install use: "
|
self,
|
||||||
"`python3 <(curl -fsSL viv.dayl.in/viv.py) manage install`"
|
reqs: List[str],
|
||||||
)
|
requirements: Path,
|
||||||
|
bin: str,
|
||||||
def _pick_bin(self, args: Namespace) -> Tuple[str, str]:
|
output: Path,
|
||||||
default = re.split(r"[=><~!*]+", args.reqs[0])[0]
|
freeze: bool,
|
||||||
return default, (default if not args.bin else args.bin)
|
yes: bool,
|
||||||
|
path: str,
|
||||||
def shim(self, args: Namespace) -> None:
|
standalone: bool,
|
||||||
|
) -> None:
|
||||||
"""\
|
"""\
|
||||||
generate viv-powered cli apps
|
generate viv-powered cli apps
|
||||||
|
|
||||||
|
@ -1201,33 +1243,35 @@ class Viv:
|
||||||
viv shim black
|
viv shim black
|
||||||
viv shim yartsu -o ~/bin/yartsu --standalone
|
viv shim yartsu -o ~/bin/yartsu --standalone
|
||||||
"""
|
"""
|
||||||
default_bin, bin = self._pick_bin(args)
|
default_bin, bin = self._pick_bin(reqs, bin)
|
||||||
output = (
|
output = Env().viv_bin_dir / default_bin if not output else output.absolute()
|
||||||
Env().viv_bin_dir / default_bin
|
|
||||||
if not args.output
|
|
||||||
else args.output.absolute()
|
|
||||||
)
|
|
||||||
|
|
||||||
if output.is_file():
|
if output.is_file():
|
||||||
error(f"{output} already exists...exiting", code=1)
|
error(f"{output} already exists...exiting", code=1)
|
||||||
|
|
||||||
if args.freeze:
|
if freeze:
|
||||||
spec = resolve_deps(args)
|
spec = resolve_deps(reqs, requirements)
|
||||||
else:
|
else:
|
||||||
spec = combined_spec(args.reqs, args.requirements)
|
spec = combined_spec(reqs, requirements)
|
||||||
|
|
||||||
if confirm(
|
if confirm(
|
||||||
f"Write shim for {a.style(bin,'bold')} to {a.style(output,'green')}?",
|
f"Write shim for {a.bold}{bin}{a.end} to {a.green}{output}{a.end}?",
|
||||||
yes=args.yes,
|
yes=yes,
|
||||||
):
|
):
|
||||||
with output.open("w") as f:
|
with output.open("w") as f:
|
||||||
f.write(
|
f.write(t.shim(path, self.local_source, standalone, spec, bin))
|
||||||
t.shim(args.path, self.local_source, args.standalone, spec, bin)
|
|
||||||
)
|
|
||||||
|
|
||||||
make_executable(output)
|
make_executable(output)
|
||||||
|
|
||||||
def run(self, args: Namespace) -> None:
|
def run(
|
||||||
|
self,
|
||||||
|
reqs: List[str],
|
||||||
|
requirements: Path,
|
||||||
|
script: str,
|
||||||
|
keep: bool,
|
||||||
|
rest: List[str],
|
||||||
|
bin: str,
|
||||||
|
) -> None:
|
||||||
"""\
|
"""\
|
||||||
run an app/script with an on-demand venv
|
run an app/script with an on-demand venv
|
||||||
|
|
||||||
|
@ -1237,27 +1281,28 @@ class Viv:
|
||||||
viv r -s <remote python script>
|
viv r -s <remote python script>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
spec = combined_spec(args.reqs, args.requirements)
|
spec = combined_spec(reqs, requirements)
|
||||||
|
|
||||||
if args.script:
|
if script:
|
||||||
env = os.environ
|
env = os.environ
|
||||||
name = args.script.split("/")[-1]
|
name = script.split("/")[-1]
|
||||||
|
|
||||||
# TODO: reduce boilerplate and dry out
|
# 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)
|
||||||
script = tmppath / name
|
scriptpath = tmppath / name
|
||||||
if not self.local_source:
|
if not self.local_source:
|
||||||
(tmppath / "viv.py").write_text(
|
(tmppath / "viv.py").write_text(
|
||||||
|
# TODO: use latest tag once ready
|
||||||
fetch_script(
|
fetch_script(
|
||||||
"https://raw.githubusercontent.com/daylinmorgan/viv/script-runner/src/viv/viv.py"
|
"https://raw.githubusercontent.com/daylinmorgan/viv/script-runner/src/viv/viv.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
script_text = fetch_script(args.script)
|
script_text = fetch_script(script)
|
||||||
viv_used = uses_viv(script_text)
|
viv_used = uses_viv(script_text)
|
||||||
script.write_text(script_text)
|
scriptpath.write_text(script_text)
|
||||||
|
|
||||||
if not args.keep:
|
if not keep:
|
||||||
env.update({"VIV_CACHE": tmpdir})
|
env.update({"VIV_CACHE": tmpdir})
|
||||||
os.environ["VIV_CACHE"] = tmpdir
|
os.environ["VIV_CACHE"] = tmpdir
|
||||||
|
|
||||||
|
@ -1266,7 +1311,7 @@ class Viv:
|
||||||
|
|
||||||
sys.exit(
|
sys.exit(
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[sys.executable, script, *args.rest], env=env
|
[sys.executable, scriptpath, *rest], env=env
|
||||||
).returncode
|
).returncode
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -1280,12 +1325,12 @@ class Viv:
|
||||||
|
|
||||||
sys.exit(
|
sys.exit(
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[vivenv.path / "bin" / "python", script, *args.rest]
|
[vivenv.path / "bin" / "python", script, *rest]
|
||||||
).returncode
|
).returncode
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_, bin = self._pick_bin(args)
|
_, 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.
|
# TODO: respect a VIV_RUN_MODE env variable as the same as keep i.e.
|
||||||
|
@ -1293,14 +1338,14 @@ class Viv:
|
||||||
# persist (use c.cache)
|
# persist (use c.cache)
|
||||||
|
|
||||||
if not vivenv.loaded or Env().viv_force:
|
if not vivenv.loaded or Env().viv_force:
|
||||||
if not args.keep:
|
if not keep:
|
||||||
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
with tempfile.TemporaryDirectory(prefix="viv-") as tmpdir:
|
||||||
vivenv.path = Path(tmpdir)
|
vivenv.path = Path(tmpdir)
|
||||||
vivenv.create()
|
vivenv.create()
|
||||||
vivenv.install_pkgs()
|
vivenv.install_pkgs()
|
||||||
sys.exit(
|
sys.exit(
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[vivenv.path / "bin" / bin, *args.rest]
|
[vivenv.path / "bin" / bin, *rest]
|
||||||
).returncode
|
).returncode
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -1310,7 +1355,7 @@ class Viv:
|
||||||
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, *rest]).returncode)
|
||||||
|
|
||||||
|
|
||||||
class Arg:
|
class Arg:
|
||||||
|
@ -1350,17 +1395,22 @@ class Cli:
|
||||||
metavar="<path>",
|
metavar="<path>",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
("remove",): [Arg("vivenv", help="name/hash of vivenv", nargs="*")],
|
("remove",): [
|
||||||
|
Arg("vivenvs", help="name/hash of vivenv", nargs="*", metavar="vivenv")
|
||||||
|
],
|
||||||
("run",): [
|
("run",): [
|
||||||
Arg("-s", "--script", help="remote script to run", metavar="<script>")
|
Arg("-s", "--script", help="remote script to run", metavar="<script>")
|
||||||
],
|
],
|
||||||
("exe", "info"): [Arg("vivenv", help="name/hash of vivenv")],
|
("exe", "info"): [
|
||||||
|
Arg("vivenv_id", help="name/hash of vivenv", metavar="vivenv")
|
||||||
|
],
|
||||||
("list", "info"): [
|
("list", "info"): [
|
||||||
Arg(
|
Arg(
|
||||||
"--json",
|
"--json",
|
||||||
help="name:metadata json for vivenvs ",
|
help="name:metadata json for vivenvs ",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
default=False,
|
default=False,
|
||||||
|
dest="use_json",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
("freeze", "shim"): [
|
("freeze", "shim"): [
|
||||||
|
@ -1390,6 +1440,7 @@ class Cli:
|
||||||
"--requirements",
|
"--requirements",
|
||||||
help="path/to/requirements.txt file",
|
help="path/to/requirements.txt file",
|
||||||
metavar="<path>",
|
metavar="<path>",
|
||||||
|
type=Path,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
("run", "shim"): [
|
("run", "shim"): [
|
||||||
|
@ -1467,7 +1518,7 @@ class Cli:
|
||||||
self.viv = viv
|
self.viv = viv
|
||||||
self.parser = ArgumentParser(prog=viv.name, description=t.description)
|
self.parser = ArgumentParser(prog=viv.name, description=t.description)
|
||||||
self._cmd_arg_group_map()
|
self._cmd_arg_group_map()
|
||||||
self._make_parsers()
|
self.parsers = self._make_parsers()
|
||||||
self._add_args()
|
self._add_args()
|
||||||
|
|
||||||
def _cmd_arg_group_map(self) -> None:
|
def _cmd_arg_group_map(self) -> None:
|
||||||
|
@ -1479,8 +1530,8 @@ class Cli:
|
||||||
for cmd in grp:
|
for cmd in grp:
|
||||||
self.cmd_arg_group_map.setdefault(cmd, []).append(grp)
|
self.cmd_arg_group_map.setdefault(cmd, []).append(grp)
|
||||||
|
|
||||||
def _make_parsers(self) -> None:
|
def _make_parsers(self) -> Dict[Sequence[str] | str, ArgumentParser]:
|
||||||
self.parsers = {**{grp: ArgumentParser(add_help=False) for grp in self.args}}
|
return {**{grp: ArgumentParser(add_help=False) for grp in self.args}}
|
||||||
|
|
||||||
def _add_args(self) -> None:
|
def _add_args(self) -> None:
|
||||||
for grp, args in self.args.items():
|
for grp, args in self.args.items():
|
||||||
|
@ -1505,30 +1556,30 @@ class Cli:
|
||||||
if args.path and args.standalone:
|
if args.path and args.standalone:
|
||||||
error("-p/--path and -s/--standalone are mutually exclusive", code=1)
|
error("-p/--path and -s/--standalone are mutually exclusive", code=1)
|
||||||
|
|
||||||
if args.func.__name__ == "manage":
|
if args.func.__name__ == "manage_install" and self.viv.local_source:
|
||||||
if args.subcmd == "install" and self.viv.local_source:
|
error(f"found existing viv installation at {self.viv.local_source}")
|
||||||
error(f"found existing viv installation at {self.viv.local_source}")
|
echo(
|
||||||
echo(
|
"use "
|
||||||
"use "
|
+ a.style("viv manage update", "bold")
|
||||||
+ a.style("viv manage update", "bold")
|
+ " to modify current installation.",
|
||||||
+ " to modify current installation.",
|
style="red",
|
||||||
style="red",
|
)
|
||||||
)
|
sys.exit(1)
|
||||||
sys.exit(1)
|
|
||||||
if args.subcmd == "update":
|
|
||||||
if not self.viv.local_source:
|
|
||||||
error(
|
|
||||||
a.style("viv manage update", "bold")
|
|
||||||
+ " should be used with an exisiting installation",
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.viv.git:
|
if args.func.__name__ == "manage_update":
|
||||||
error(
|
if not self.viv.local_source:
|
||||||
a.style("viv manage update", "bold")
|
error(
|
||||||
+ " shouldn't be used with a git-based installation",
|
a.style("viv manage update", "bold")
|
||||||
1,
|
+ " should be used with an exisiting installation",
|
||||||
)
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.viv.git:
|
||||||
|
error(
|
||||||
|
a.style("viv manage update", "bold")
|
||||||
|
+ " shouldn't be used with a git-based installation",
|
||||||
|
1,
|
||||||
|
)
|
||||||
if args.func.__name__ == "run":
|
if args.func.__name__ == "run":
|
||||||
if not (args.reqs or args.script):
|
if not (args.reqs or args.script):
|
||||||
error("must specify a requirement or --script", code=1)
|
error("must specify a requirement or --script", code=1)
|
||||||
|
@ -1584,7 +1635,8 @@ class Cli:
|
||||||
for k in self.cmd_arg_group_map[f"{cmd}|{subcmd}"]
|
for k in self.cmd_arg_group_map[f"{cmd}|{subcmd}"]
|
||||||
],
|
],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
).set_defaults(func=getattr(self.viv, cmd), subcmd=subcmd)
|
# ).set_defaults(func=getattr(self.viv, cmd), subcmd=subcmd)
|
||||||
|
).set_defaults(func=getattr(self.viv, f"{cmd}_{subcmd}"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self._get_subcmd_parser(
|
self._get_subcmd_parser(
|
||||||
|
@ -1599,11 +1651,13 @@ class Cli:
|
||||||
args.rest = sys.argv[i + 1 :]
|
args.rest = sys.argv[i + 1 :]
|
||||||
else:
|
else:
|
||||||
args = self.parser.parse_args()
|
args = self.parser.parse_args()
|
||||||
args.rest = []
|
if args.func.__name__ in ("run", "exe"):
|
||||||
|
args.rest = []
|
||||||
|
|
||||||
self._validate_args(args)
|
self._validate_args(args)
|
||||||
args.func(
|
func = args.__dict__.pop("func")
|
||||||
args,
|
func(
|
||||||
|
**vars(args),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue