Usage¶
Viv works by ensuring scripts or a given command are run in an appropriate environment with all specified dependencies.
Tip
viv is a single script and available at viv.dayl.in/viv.py
meaning every instance of viv in these examples could be python3 <(curl -fsSL viv.dayl.in/viv.py)
Run CLI Apps¶
Run a python app that provides an entrypoint and separate args with --:
viv run frogmouth -- gh daylinmorgan/viv
Run a python module use the -b/--bin flag and specify python:
viv run rich -b python -- -m rich
Make an ephemeral jupyter environment with your needed deps:
viv run jupyter pandas -r requirements-dev.txt -- notebook
Generate an executable shell script that will on demand create a vivenv as needed:
viv shim ruff
Output:
#!/usr/bin/env python3
# AUTOGENERATED by viv (v2023.1003)
# see `python3 <(curl -fsSL viv.dayl.in/viv.py) --help`
import subprocess
import sys
if __name__ == "__main__":
vivenv = __import__("viv").use("ruff") # noqa
sys.exit(subprocess.run([vivenv / "bin" / "ruff", *sys.argv[1:]]).returncode)
Run Python Scripts¶
It’s possible to use the viv CLI to run a python script.
There are several options for invoking a script.
Using the interpreter:
viv run rich typer -b python -- ./cli.py --help
Using -s/--script:
viv run rich typer -s ./cli.py -- --help
# or with a remote script
viv run rich -s https://raw.githubusercontent.com/Textualize/rich/master/examples/fullscreen.py
If viv is available on your path it’s possible to
invoke it with embedded metadata thanks to shebangs:
#!/usr/bin/env -S viv run --keep --script
# /// script
# requires-python = ">3.10"
# dependencies = [
# "matplotlib",
# "pandas"
# ]
# ///
Note
If using a shebang on a python script -s/--script must be the last argument
See also
Check out PEP723 for more info about inline script metadata.
In any python script with external dependencies you can also add this line prior to imports
to automate vivenv creation and installation of dependencies.
__import__("viv").use("click")
If your dependencies are sensitive to the version of python
(numpy, cpython-based apps, etc. ) then you can specify track_exe.
Which will lead to viv creating a unique vivenv based on the detected python executable.
__import__("viv").use("numpy", track_exe=True)
If you’d like to pin your dependencies to a resolved environment you
can use the convenience command viv freeze to output a list of pinned packages.
Command:
viv freeze rich typer
Output:
__import__("viv").use("rich==13.7.0", "typer==0.9.0", "click==8.1.7", "markdown-it-py==3.0.0", "Pygments==2.17.2", "typing_extensions==4.9.0", "mdurl==0.1.2") # noqa
Additionally, you can make this work regardless of PYTHONPATH by using --path.
Command:
viv freeze rich typer --path rel
Output:
__import__("sys").path.append(__import__("os").path.expanduser("~/.local/share/viv/")) # noqa
__import__("viv").use("rich==13.7.0", "typer==0.9.0", "click==8.1.7", "markdown-it-py==3.0.0", "Pygments==2.17.2", "typing_extensions==4.9.0", "mdurl==0.1.2") # noqa
Manage Viv¶
Interacting with the viv cache¶
Depending on how you invoke viv, it will persist it’s virtual environments (vivenvs).
To see all currently existing vivenvs use can use viv list.
Viv will attempt to track any usages of the vivenvs including the scripts that invoke them.
You can remove any existing vivenvs using viv env remove:
viv env remove d4b342b3
To get more information about vivenvs you can use viv list --verbose or viv env info <hash>
Note
For commands that expect a vivenv hash/name you can use as few characters as you
as you like and viv will match it against the existing vivenvs in the cache.
You can list vivenvs given a criteria using --filter for example:
Created before 2024-01-01:
viv list --filter "created-before:2024-01-01"
Associated with a particular file:
viv list --filter "files:./script.py"
Or no files:
viv list --filter "files:None"
Note
--filter "files:None" will also apply to vivenvs
in which the original file is no longer on the disk
The available filtering criteria are accessed-after,
accessed-before, created-before, created-after, spec and files.
To remove all vivenvs you can use the below command:
viv env remove $(viv list -q)
To remove viv all together you can use the included purge command:
python3 <(curl -fsSL viv.dayl.in/viv.py) manage purge
Bonus: viv standalone¶
--standalone will auto-generate a mini function version of viv to accomplish the same basic task as using a local copy of viv.
After generating this standalone shim you can freely use this script across unix machines which have python>3.8.
See examples/black for output of below command.
viv freeze also supports --standalone
python3 <(curl -fsSL viv.dayl.in/viv.py) shim black -o ./black --standalone --freeze