Compare commits

...

4 commits

View file

@ -52,7 +52,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.5a2-2-gebb657c-dev" __version__ = "23.5a2-5-g4e6acac-dev"
class Config: class Config:
@ -226,7 +226,8 @@ class Ansi:
Args: Args:
output: text output from subprocess, usually from p.stdout output: text output from subprocess, usually from p.stdout
""" """
if not output:
return
echo("subprocess output:") echo("subprocess output:")
new_output = [f"{self.red}->{self.end} {line}" for line in output.splitlines()] new_output = [f"{self.red}->{self.end} {line}" for line in output.splitlines()]
sys.stdout.write("\n".join(new_output) + "\n") sys.stdout.write("\n".join(new_output) + "\n")
@ -559,7 +560,7 @@ class ViVenv:
name: str = "", name: str = "",
path: Path | None = None, path: Path | None = 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.exe = str(Path(sys.executable).resolve()) if track_exe else "N/A"
self.id = id if id else get_hash(spec, track_exe) self.id = id if id else get_hash(spec, track_exe)
self.name = name if name else self.id self.name = name if name else self.id
@ -583,6 +584,18 @@ class ViVenv:
return vivenv return vivenv
def _validate_spec(self, spec: Tuple[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)
else:
return sorted(spec)
def create(self, quiet: bool = False) -> None: def create(self, quiet: bool = False) -> None:
if not quiet: if not quiet:
echo(f"new unique vivenv -> {self.name}") echo(f"new unique vivenv -> {self.name}")
@ -636,7 +649,6 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> Path:
track_exe: if true make env python exe specific track_exe: if true make env python exe specific
name: use as vivenv name, if not provided id is used name: use as vivenv name, if not provided id is used
""" """
validate_spec(packages)
vivenv = ViVenv(list(packages), track_exe=track_exe, name=name) 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( if vivenv.name not in [d.name for d in c.venvcache.iterdir()] or os.getenv(
@ -650,18 +662,6 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> Path:
return vivenv.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: def modify_sys_path(new_path: Path) -> None:
# remove user-site # remove user-site
for i, path in enumerate(sys.path): for i, path in enumerate(sys.path):
@ -1025,7 +1025,7 @@ class Viv:
f"{pip_path} {' '.join(args.cmd)}" f"{pip_path} {' '.join(args.cmd)}"
if args.exe == "pip" if args.exe == "pip"
else f"{python_path} {' '.join(args.cmd)}" else f"{python_path} {' '.join(args.cmd)}"
) ) + " ".join(args.rest)
echo(f"executing {cmd}") echo(f"executing {cmd}")
run(shlex.split(cmd), verbose=True) run(shlex.split(cmd), verbose=True)
@ -1240,6 +1240,42 @@ class Viv:
make_executable(output) 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)
default_bin = re.split(r"[=><~!*]+", args.reqs[0])[0]
bin = default_bin if not args.bin else args.bin
spec = combined_spec(args.reqs, args.requirements)
vivenv = ViVenv(spec)
if vivenv.name not in [d.name for d in c.venvcache.iterdir()] 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( def _get_subcmd_parser(
self, self,
subparsers: _SubParsersAction[ArgumentParser], subparsers: _SubParsersAction[ArgumentParser],
@ -1409,11 +1445,10 @@ class Viv:
"purge", help="remove traces of viv", aliases="p", parents=[p_manage_shared] "purge", help="remove traces of viv", aliases="p", parents=[p_manage_shared]
).set_defaults(func=self.manage, cmd="purge") ).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] subparsers, "shim", parents=[p_freeze_shim_shared]
) )
).set_defaults(func=self.shim, cmd="shim")
p_shim.add_argument( p_shim.add_argument(
"-f", "-f",
"--freeze", "--freeze",
@ -1427,11 +1462,41 @@ class Viv:
type=Path, type=Path,
metavar="<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 = parser.parse_args()
args.rest = []
args.func(args) args.func(
args,
)
def main() -> None: def main() -> None: