mirror of
https://github.com/daylinmorgan/viv.git
synced 2024-11-14 13:07:54 -06:00
deploy: b5b02f82a6
This commit is contained in:
parent
85614aee2c
commit
1b2ec11c2f
1 changed files with 104 additions and 33 deletions
131
viv.py
131
viv.py
|
@ -52,7 +52,7 @@ from typing import (
|
|||
from urllib.error import HTTPError
|
||||
from urllib.request import urlopen
|
||||
|
||||
__version__ = "23.5a2-2-gebb657c-dev"
|
||||
__version__ = "23.5a3-10-gf2c156d-dev"
|
||||
|
||||
|
||||
class Config:
|
||||
|
@ -226,7 +226,8 @@ class Ansi:
|
|||
Args:
|
||||
output: text output from subprocess, usually from p.stdout
|
||||
"""
|
||||
|
||||
if not output:
|
||||
return
|
||||
echo("subprocess output:")
|
||||
new_output = [f"{self.red}->{self.end} {line}" for line in output.splitlines()]
|
||||
sys.stdout.write("\n".join(new_output) + "\n")
|
||||
|
@ -559,11 +560,12 @@ class ViVenv:
|
|||
name: str = "",
|
||||
path: Path | None = None,
|
||||
) -> None:
|
||||
self.spec = spec
|
||||
self.spec = self._validate_spec(spec)
|
||||
self.exe = str(Path(sys.executable).resolve()) if track_exe else "N/A"
|
||||
self.id = id if id else get_hash(spec, track_exe)
|
||||
self.name = name if name else self.id
|
||||
self.path = path if path else c.venvcache / self.name
|
||||
self.exists = self.name in [d.name for d in c.venvcache.iterdir()]
|
||||
|
||||
@classmethod
|
||||
def load(cls, name: str) -> "ViVenv":
|
||||
|
@ -583,6 +585,18 @@ class ViVenv:
|
|||
|
||||
return vivenv
|
||||
|
||||
def _validate_spec(self, spec: List[str]) -> List[str]:
|
||||
"""ensure spec is at least of sequence of strings
|
||||
|
||||
Args:
|
||||
spec: sequence of package specifications
|
||||
"""
|
||||
if not set(map(type, spec)) == {str}:
|
||||
error("unexepected input in package spec")
|
||||
error(f"check your packages definitions: {spec}", code=1)
|
||||
|
||||
return sorted(spec)
|
||||
|
||||
def create(self, quiet: bool = False) -> None:
|
||||
if not quiet:
|
||||
echo(f"new unique vivenv -> {self.name}")
|
||||
|
@ -636,12 +650,9 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> Path:
|
|||
track_exe: if true make env python exe specific
|
||||
name: use as vivenv name, if not provided id is used
|
||||
"""
|
||||
validate_spec(packages)
|
||||
vivenv = ViVenv(list(packages), track_exe=track_exe, name=name)
|
||||
|
||||
if vivenv.name not in [d.name for d in c.venvcache.iterdir()] or os.getenv(
|
||||
"VIV_FORCE"
|
||||
):
|
||||
if not vivenv.exists or os.getenv("VIV_FORCE"):
|
||||
vivenv.create()
|
||||
vivenv.install_pkgs()
|
||||
vivenv.dump_info(write=True)
|
||||
|
@ -650,18 +661,6 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> Path:
|
|||
return vivenv.path
|
||||
|
||||
|
||||
def validate_spec(spec: Tuple[str, ...]) -> None:
|
||||
"""ensure spec is at least of sequence of strings
|
||||
|
||||
Args:
|
||||
spec: sequence of package specifications
|
||||
"""
|
||||
# ? make this a part of ViVenv?
|
||||
if not set(map(type, spec)) == {str}:
|
||||
error("unexepected input in package spec")
|
||||
error(f"check your packages definitions: {spec}", code=1)
|
||||
|
||||
|
||||
def modify_sys_path(new_path: Path) -> None:
|
||||
# remove user-site
|
||||
for i, path in enumerate(sys.path):
|
||||
|
@ -1025,7 +1024,7 @@ class Viv:
|
|||
f"{pip_path} {' '.join(args.cmd)}"
|
||||
if args.exe == "pip"
|
||||
else f"{python_path} {' '.join(args.cmd)}"
|
||||
)
|
||||
) + " ".join(args.rest)
|
||||
|
||||
echo(f"executing {cmd}")
|
||||
run(shlex.split(cmd), verbose=True)
|
||||
|
@ -1048,16 +1047,19 @@ class Viv:
|
|||
make_executable(src)
|
||||
echo("symlinking cli")
|
||||
|
||||
if not cli.is_file():
|
||||
if cli.is_file() and confirm(
|
||||
f"Existing file at {a.style(str(cli),'bold')}, "
|
||||
"would you like to overwrite it?"
|
||||
):
|
||||
cli.unlink()
|
||||
cli.symlink_to(src)
|
||||
else:
|
||||
cli.unlink()
|
||||
cli.symlink_to(src)
|
||||
|
||||
echo("Remember to include the following line in your shell rc file:")
|
||||
sys.stderr.write(
|
||||
' export PYTHONPATH="$PYTHONPATH:$HOME/'
|
||||
f'{src.relative_to(Path.home())}"\n'
|
||||
f'{src.relative_to(Path.home()).parent}"\n'
|
||||
)
|
||||
|
||||
def manage(self, args: Namespace) -> None:
|
||||
|
@ -1179,6 +1181,10 @@ class Viv:
|
|||
"`python3 <(curl -fsSL gh.dayl.in/viv/viv.py) manage install`"
|
||||
)
|
||||
|
||||
def _pick_bin(self, args: Namespace) -> Tuple[str, str]:
|
||||
default = re.split(r"[=><~!*]+", args.reqs[0])[0]
|
||||
return default, (default if not args.bin else args.bin)
|
||||
|
||||
def shim(self, args: Namespace) -> None:
|
||||
"""\
|
||||
generate viv-powered cli apps
|
||||
|
@ -1192,8 +1198,7 @@ class Viv:
|
|||
if not args.reqs:
|
||||
error("please specify at lease one dependency", code=1)
|
||||
|
||||
default_bin = re.split(r"[=><~!*]+", args.reqs[0])[0]
|
||||
bin = default_bin if not args.bin else args.bin
|
||||
default_bin, bin = self._pick_bin(args)
|
||||
output = (
|
||||
c.binparent / default_bin if not args.output else args.output.absolute()
|
||||
)
|
||||
|
@ -1240,6 +1245,43 @@ class Viv:
|
|||
|
||||
make_executable(output)
|
||||
|
||||
def run(self, args: Namespace) -> None:
|
||||
"""\
|
||||
run an app w/ an on-demand venv
|
||||
|
||||
examples:
|
||||
viv r pycowsay -- "Viv isn't venv\!"
|
||||
viv r rich -b python -- -m rich
|
||||
"""
|
||||
if not args.reqs:
|
||||
error("please specify at lease one dependency", code=1)
|
||||
|
||||
_, 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.exists 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.install_pkgs()
|
||||
vivenv.dump_info(write=True)
|
||||
|
||||
sys.exit(subprocess.run([vivenv.path / "bin" / bin, *args.rest]).returncode)
|
||||
|
||||
def _get_subcmd_parser(
|
||||
self,
|
||||
subparsers: _SubParsersAction[ArgumentParser],
|
||||
|
@ -1409,11 +1451,10 @@ class Viv:
|
|||
"purge", help="remove traces of viv", aliases="p", parents=[p_manage_shared]
|
||||
).set_defaults(func=self.manage, cmd="purge")
|
||||
|
||||
(
|
||||
p_shim := self._get_subcmd_parser(
|
||||
p_shim = self._get_subcmd_parser(
|
||||
subparsers, "shim", parents=[p_freeze_shim_shared]
|
||||
)
|
||||
).set_defaults(func=self.shim, cmd="shim")
|
||||
|
||||
p_shim.add_argument(
|
||||
"-f",
|
||||
"--freeze",
|
||||
|
@ -1427,11 +1468,41 @@ class Viv:
|
|||
type=Path,
|
||||
metavar="<path>",
|
||||
)
|
||||
p_shim.add_argument("-b", "--bin", help="console_script/script to invoke")
|
||||
p_shim.add_argument(
|
||||
"-b", "--bin", help="console_script/script to invoke", metavar="<bin>"
|
||||
)
|
||||
|
||||
p_run = self._get_subcmd_parser(subparsers, "run")
|
||||
|
||||
p_run.add_argument(
|
||||
"-r",
|
||||
"--requirements",
|
||||
help="path/to/requirements.txt file",
|
||||
metavar="<path>",
|
||||
)
|
||||
p_run.add_argument(
|
||||
"-k",
|
||||
"--keep",
|
||||
help="preserve environment",
|
||||
action="store_true",
|
||||
)
|
||||
p_run.add_argument("reqs", help="requirements specifiers", nargs="*")
|
||||
|
||||
p_run.add_argument(
|
||||
"-b", "--bin", help="console_script/script to invoke", metavar="<bin>"
|
||||
)
|
||||
|
||||
if "--" in sys.argv:
|
||||
i = sys.argv.index("--")
|
||||
args = parser.parse_args(sys.argv[1:i])
|
||||
args.rest = sys.argv[i + 1 :]
|
||||
else:
|
||||
args = parser.parse_args()
|
||||
args.rest = []
|
||||
|
||||
args.func(args)
|
||||
args.func(
|
||||
args,
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
|
Loading…
Reference in a new issue