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