Compare commits

...

73 commits

Author SHA1 Message Date
852ce927c0
- 2024-07-12 12:33:22 -05:00
28c268b8f0
dir!=files 2024-07-12 12:30:53 -05:00
247f80921d
fix: add key to remove sort 2024-07-12 12:25:05 -05:00
960d7acf26
ci: use latest branch over tag 2024-07-12 11:43:52 -05:00
68e12d643c
tests: add python v3.12 2024-07-12 11:40:33 -05:00
cf2414c0d1
bump 2024.1004 -> 2024.1005 2024-07-12 11:38:45 -05:00
303831bdb8
fix(#23): add pip version check 2024-07-09 12:03:06 -05:00
8aced00ace
docs: fix url 2024-06-18 14:07:12 -05:00
1b8bcb4454
feat(#19): add --generate flag for viv shim 2024-04-18 15:02:00 -05:00
c43f2f027c
chore: bump packaging to v24 2024-04-18 15:02:00 -05:00
4c236e96eb
fix(#18): use --bin to determine default filename 2024-04-18 15:02:00 -05:00
c5bc03f497
chore: remove newlines 2024-01-19 10:20:51 -06:00
8fb6648e8e
docs: remove old mkdocs config 2024-01-19 10:20:39 -06:00
f97a617ac9
docs: remove redundant phrase 2024-01-10 11:36:29 -06:00
9eba6a303f
refactor: abstract script/cache behavior more 2024-01-09 17:58:33 -06:00
6ef2f765d6
chore: update type checking 2024-01-09 02:29:44 -06:00
0e5ac7c9c9
refactor: add PYTHONPATH to manage show --system for debugging 2024-01-09 02:17:10 -06:00
9d237d3b96
docs: minor changes 2024-01-09 02:00:43 -06:00
6357a99189
ci: map not list 2024-01-09 01:44:17 -06:00
b822fe432c
ci: only run tests when source changes 2024-01-09 01:43:43 -06:00
032e23af95
docs: finish updating docs 2024-01-09 01:41:34 -06:00
13cb21435d
docs: frogmouth is a cooler demo 2024-01-05 17:23:34 -06:00
38b6e4d746
bump 2023.1003 -> 2024.1004 2024-01-05 17:21:04 -06:00
be3c7f83c9
docs: actually document the things 2024-01-05 17:20:57 -06:00
5dacf8ab60
refactor: abstract _uses_viv + add tests 2024-01-04 15:53:04 -06:00
e31d401585
fix: remove erroneous debug print 2024-01-04 14:14:58 -06:00
f53d00e8e2
feat: add viv.run() to accompany inline metatdata approach 2024-01-03 13:49:27 -06:00
f21b90d962
fix: use _get_user with _update_cache 2024-01-03 13:47:47 -06:00
1bb48eedbc
chore: RIP PEP722 2024-01-03 13:13:24 -06:00
5fb501aa2c fix: run vendored test on package 2024-01-03 13:09:19 -06:00
b2a2597816 fix: use most recent tomli 2024-01-03 13:09:19 -06:00
523a9a9d06 feat: vendor packaging + add support for requires python check
This is a mostly small change albeit with addition of more embedded
code from pypa/packaging to simplify supporting a requires-python check
with PEP723 style syntax.
2024-01-03 13:09:19 -06:00
f16ded2a83 fix: use updated pep723 syntax 2024-01-03 13:09:19 -06:00
c8051f9513 refactor: add tomllib preference 2024-01-03 13:09:19 -06:00
12d7f6c322 feat: add preliminary support for pep723 2024-01-03 13:09:19 -06:00
976f9fc0fa
feat: hide stacktraces 2024-01-02 12:40:46 -06:00
6700178d68
chore: ensure directories for dev-install 2024-01-02 12:26:10 -06:00
b378d47253
chore: update pre-commit to only use ruff 2024-01-02 12:25:09 -06:00
5c769fc905
fix: generate bin directory first 2024-01-02 12:22:47 -06:00
96eb08af6a
chore: some cleanup 2023-12-28 08:52:46 -06:00
726cef3092
build: why make when we can do 2023-12-28 08:27:03 -06:00
8c8bd616af
fix: typo on run example 2023-12-18 12:28:18 -06:00
3673e4940c
docs: furo -> shibuya 2023-12-18 12:27:44 -06:00
72ca25c302
fix(#15): use a username for semi-ephemeral 2023-12-11 13:17:02 -06:00
153d9fe774
fix: don't make extra directory in ephemeral directory 2023-10-23 12:53:12 -05:00
873310d900
docs: update outdated command 2023-10-18 13:58:19 -05:00
29d9d965b3 build: task.mk by default 2023-10-18 09:30:37 -05:00
tripleee
ad544f59d9 Makefile: small portability and usability fixes
* clean: don't use Bash syntax
2023-10-18 09:30:37 -05:00
323a194ff0
fix: remove deprecated command from docs 2023-10-16 13:48:41 -05:00
64d55811a2
ci: add flow to release workflow 2023-10-07 16:46:40 -05:00
2bc512314b
bump 2023.1002 -> 2023.1003 2023-10-07 16:41:33 -05:00
8986a436c0
docs: update svg generation with new commands 2023-10-07 16:41:23 -05:00
e6741792ee
build: add back complete lock file 2023-10-07 14:26:46 -05:00
134f220b4f
ci: pull_request not pr 2023-10-07 14:24:03 -05:00
173411dc9e
docs: switch to sphinx and splitup readme 2023-10-07 14:20:56 -05:00
c05e2ae22a
ci: add pr to test 2023-10-07 11:07:21 -05:00
tripleee
7e5c5746ee README.md: typo fixes and formatting tweaks
Mainly punctuation and other minutiae
2023-10-06 12:24:11 -05:00
d665f7ac6b
docs: update main help screenshot 2023-10-02 12:37:06 -05:00
0e262cc0f9
refactor: consolidate cli commands 2023-09-28 13:36:30 -05:00
d71542f5d2
fix: don't skip args if non run command using run 2023-09-27 16:06:22 -05:00
7286e90a36
feat: add experimental support for windows 2023-09-27 15:53:27 -05:00
24787a746c
style: implict types fine and properly escape things 2023-09-27 14:15:10 -05:00
4b3d1fbea5
tests: add some basic integration tests for python api 2023-09-27 13:34:10 -05:00
da77259862
fix: accept args with -s to make shebangs work better 2023-09-27 12:42:32 -05:00
2e916cab5f
bump 2023.1001 -> 2023.1002 2023-09-26 15:13:07 -05:00
62c3270826
fix: don't move local scripts just add viv.py to the path 2023-09-26 15:13:00 -05:00
3fe4110e06
build: fix release regex 2023-09-21 14:50:46 -05:00
ac02151a51
chore: sorry mypy forgot you were there 2023-08-30 14:32:41 -05:00
fa04385997
chore: add issue template 2023-08-30 12:29:41 -05:00
cb7d90f0e1
fix: add padding to size in list 2023-08-30 12:00:42 -05:00
4cdfb4fc50
refactor: drop unnecessary dict for list 2023-08-30 11:42:32 -05:00
68a6f3155a
feat: add system flag to viv manage show 2023-08-30 11:15:25 -05:00
f91d6eb4b4
fix: actually set date value 2023-08-29 16:05:42 -05:00
36 changed files with 3488 additions and 915 deletions

32
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Bug Report
description: File a bug report
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
A bug is when something works differently than it is expected to.
## Remember to search before filing a new report
Please search for this bug in the issue tracker, and use a bug report title that
would have made your bug report turn up in the search results for your search query.
- type: textarea
id: version
attributes:
label: "Viv & python/pip version info"
description: Please include the output of `viv manage show --system` or `python3 <(curl -fsSL viv.dayl.in/viv.py) manage show --system`
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to Reproduce and Observed Behavior
description: What exactly can someone else do, in order to observe the problem that you observed? Include the output and all error messages.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What did you expect to happen instead?
validations:
required: true

View file

@ -9,24 +9,27 @@ permissions:
contents: write contents: write
jobs: jobs:
run-tests:
uses: ./.github/workflows/test.yml
build-n-publish: build-n-publish:
needs: run-tests
permissions: permissions:
id-token: write id-token: write
uses: ./.github/workflows/pypi.yml uses: ./.github/workflows/pypi.yml
create-release: create-release:
needs: build-n-publish
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: update latest tag - name: update latest branch
uses: richardsimko/update-tag@v1 run: |
with: git checkout -B latest
tag_name: latest git push --force-with-lease -u origin latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Generate New Release - name: Generate New Release
run: gh release create ${{ github.ref }} run: gh release create ${{ github.ref }}

37
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: 🧪 Run Tests
on:
pull_request:
workflow_call:
workflow_dispatch:
push:
paths:
- 'src/**/*.py'
- 'tests/**/*.py'
- 'pyproject.toml'
jobs:
run-tests:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8','3.9','3.10','3.11', '3.12']
os:
- ubuntu-latest
- windows-latest
# - macos-latest
steps:
- uses: actions/checkout@v4
- name: Set up PDM
uses: pdm-project/setup-pdm@v3
with:
python-version: ${{ matrix.python-version}}
- name: Install dependencies
run: pdm sync -d -G test
- name: Run Tests
run: pdm run -v pytest tests

7
.gitignore vendored
View file

@ -170,8 +170,11 @@ poetry.toml
# End of https://www.toptal.com/developers/gitignore/api/python # End of https://www.toptal.com/developers/gitignore/api/python
.task.mk .task.mk
# copy of README.md
docs/index.md
docs/cli.md docs/cli.md
docs/viv.py docs/viv.py
docs/svgs docs/svgs
docs/public
/tests/.viv-cache
/scripts/tomli
/scripts/packaging

View file

@ -1,13 +1,9 @@
# See https://pre-commit.com for more information # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/astral-sh/ruff-pre-commit
rev: 23.3.0 rev: v0.1.10
hooks:
- id: black
language_version: python
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.270'
hooks: hooks:
- id: ruff-format
- id: ruff - id: ruff
args: [--fix, --exit-non-zero-on-fix, --show-fixes] args: [ --fix ]

View file

@ -1,6 +0,0 @@
USAGE={a.bold}{a.cyan} viv tasks{a.end}:\n
PHONIFY=1
HELP_SEP={a.b_blue}>>>{a.end}
-include .task.mk
$(if $(filter help,$(MAKECMDGOALS)),$(if $(wildcard .task.mk),,.task.mk: ; curl -fsSL https://raw.githubusercontent.com/daylinmorgan/task.mk/v23.1.2/task.mk -o .task.mk))

View file

@ -1,38 +0,0 @@
VERSION ?= $(shell git describe --tags --always --dirty=-dev | sed 's/^v//g')
venv: ## generate environment
pdm install
assets/viv-help.svg:
FORCE_COLOR=1 viv --help | yartsu -t 'viv --help' -w 70 -o $@
.PHONY: dev-install
dev-install:
ln -sf $(PWD)/src/viv/viv.py ~/.local/share/viv/viv.py
## docs |> update docs files
docs: docs/viv.py docs/index.md
docs/viv.py: src/viv/viv.py
@cp $< $@
docs/index.md: README.md
@printf -- '---\nhide: [navigation]\n---\n\n' > $@
@cat $< >> $@
examples/black: .FORCE
rm -f $@
viv shim black -y -s -f -o $@
clean: ## remove build artifacts
rm -rf {build,dist}
EXAMPLES = cli.py sys_path.py exe_specific.py frozen_import.py named_env.py scrape.py
generate-example-vivens: ##
for f in $(EXAMPLES); \
do python examples/$$f; done
.FORCE:
.PHONY: .FORCE
-include .task.cfg.mk

View file

@ -29,20 +29,18 @@
Try before you buy! Try before you buy!
```sh ```sh
python3 <(curl -fsSL viv.dayl.in/viv.py) run pycowsay -- "viv isn't venv\!" python3 <(curl -fsSL viv.dayl.in/viv.py) run frogmouth -- gh daylinmorgan/viv
``` ```
--- ---
`Viv` is a standalone dependency-free `venv` creator [^1]. `Viv` is a standalone dependency-free `venv` creator (just needs python + pip).
`Viv` helps you ignore silly things like managing temporary or rarely used virtual environments, `Viv` helps you ignore silly things like managing temporary or rarely used virtual environments,
while still unleashing the full power of python scripting with it's entire ecosystem at your disposal. while still unleashing the full power of python scripting with it's entire ecosystem at your disposal.
`Viv`'s uncompromising insistence on portability means that it will always: `Viv`'s uncompromising insistence on portability means that it will always,
only use the standard library and never exceed a single script.
1. only use the standard library See the [documentation](https://viv.dayl.in) or the [examples](https://github.com/daylinmorgan/viv/tree/main/examples) to get started.
2. never exceed a single script.
For that reason any usage of the `cli` can be accomplished using a remote copy as seen in the below install command.
## Setup ## Setup
@ -52,8 +50,9 @@ Run the below command to install `viv`.
python3 <(curl -fsSL viv.dayl.in/viv.py) manage install python3 <(curl -fsSL viv.dayl.in/viv.py) manage install
``` ```
To access `viv` from within scripts you should add it's location to your `PYTHONPATH`. To access `viv` from within scripts you should add its location to your `PYTHONPATH`.
By default `viv` will be installed to `$XDG_DATA_HOME/viv` or `~/.local/share/viv` you can customize this with `--src`. By default `viv` will be installed to `$XDG_DATA_HOME/viv` or `~/.local/share/viv`;
you can customize this with `--src`.
```sh ```sh
export PYTHONPATH="$PYTHONPATH:$HOME/.local/share/viv" export PYTHONPATH="$PYTHONPATH:$HOME/.local/share/viv"
@ -65,34 +64,48 @@ export PYTHONPATH="$PYTHONPATH:$HOME/.local/share/viv"
pip install viv pip install viv
``` ```
Why is this *not recommended*? Mainly, because `viv` is all about hacking your `sys.path`. Why is this *not recommended?* Mainly because `viv` is all about hacking your `sys.path`.
Placing it in it's own virtual environment or installing in a user site directory may complicate this endeavor. Placing it in its own virtual environment or installing in a user site directory may complicate this endeavor.
## Usage ## Usage
In any python script with external dependencies you can add this line, In any Python script with external dependencies you can add this line
to automate `vivenv` creation and installation of dependencies. to automate `vivenv` creation and installation of dependencies.
As a cli:
```sh
viv run frogmouth -- gh daylinmorgan/viv
```
As a python module:
```python ```python
__import__("viv").use("click") __import__("viv").use("click")
``` ```
As an app installer:
```sh
viv shim ruff
```
To remove all `vivenvs` you can use the below command: To remove all `vivenvs` you can use the below command:
```sh ```sh
viv cache remove $(viv list -q) viv env remove $(viv list -q)
``` ```
To remove `viv` all together you can use the included `purge` command: To remove `viv` altogether you can use the included `purge` command:
```sh ```sh
python3 <(curl -fsSL viv.dayl.in/viv.py) manage purge python3 <(curl -fsSL viv.dayl.in/viv.py) manage purge
``` ```
## Equivalent commands from alternatives ## Equivalent commands from alternatives
### [pip-run](https://github.com/jaraco/pip-run) ### [pip-run](https://github.com/jaraco/pip-run)
```sh ```sh
pip-run cowsay -- -m cowsay "moove over, pip-run" pip-run cowsay -- -m cowsay "moove over, pip-run"
python3 <(curl -fsSL viv.dayl.in/viv.py) run cowsay -- "moove over, pip-run" python3 <(curl -fsSL viv.dayl.in/viv.py) run cowsay -- "moove over, pip-run"
@ -100,7 +113,7 @@ python3 <(curl -fsSL viv.dayl.in/viv.py) run cowsay -- "moove over, pip-run"
```sh ```sh
python -m pip-run requests -- -c "import requests; print(requests.get('https://pypi.org/project/pip-run').status_code)" python -m pip-run requests -- -c "import requests; print(requests.get('https://pypi.org/project/pip-run').status_code)"
python -m viv requests -b python -- -c "import requests; print(requests.get('https://pypi.org/project/viv').status_code)" python -m viv run requests -b python -- -c "import requests; print(requests.get('https://pypi.org/project/viv').status_code)"
``` ```
### [pipx](https://github.com/pypa/pipx/) ### [pipx](https://github.com/pypa/pipx/)
@ -118,19 +131,19 @@ python3 <(curl -fsSL viv.dayl.in/viv.py) run \
## Bonus: use `viv` with just standalone snippet (37LOC) ## Bonus: use `viv` with just standalone snippet (37LOC)
`--standalone` will auto-generate a mini function version of `viv` to accomplish the same basic task as using a local copy of `viv`. `--standalone` will auto-generate a mini-function version of `viv`
After generating this standalone `shim` you can freely use this script across unix machines which have `python>3.8`. to accomplish the same basic task as using a local copy of `viv`.
See [examples/black](https://github.com/daylinmorgan/viv/blob/dev/examples/black) for output of below command. After generating this standalone shim you can freely use this script
across Unix machines which have Python > 3.8.
See [examples/black](https://github.com/daylinmorgan/viv/blob/dev/examples/black)
for output of the below command.
`viv freeze` also supports `--standalone` `viv freeze` also supports `--standalone`.
```sh ```sh
python3 <(curl -fsSL viv.dayl.in/viv.py) shim black -o ./black --standalone --freeze python3 <(curl -fsSL viv.dayl.in/viv.py) shim black -o ./black --standalone --freeze
``` ```
[^1]: You do need to have `pip` but surely you have `pip` already.
[conda-shield]: https://img.shields.io/conda/vn/conda-forge/viv [conda-shield]: https://img.shields.io/conda/vn/conda-forge/viv
[conda-url]: https://anaconda.org/conda-forge/viv [conda-url]: https://anaconda.org/conda-forge/viv
[pypi-shield]: https://img.shields.io/pypi/v/viv [pypi-shield]: https://img.shields.io/pypi/v/viv

View file

@ -1,4 +1,4 @@
<svg class="rich-terminal shadow" viewBox="0 0 890.3333333333334 545.9333333333333" xmlns="http://www.w3.org/2000/svg"> <svg class="rich-terminal shadow" viewBox="0 0 890.3333333333334 521.5333333333333" xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io & yartsu https://github.com/daylinmorgan/yartsu --> <!-- Generated with Rich https://www.textualize.io & yartsu https://github.com/daylinmorgan/yartsu -->
<style> <style>
@ -19,14 +19,14 @@
font-weight: 700; font-weight: 700;
} }
.terminal-1538990771-matrix { .terminal-4099597553-matrix {
font-family: Fira Code, monospace; font-family: Fira Code, monospace;
font-size: 20px; font-size: 20px;
line-height: 24.4px; line-height: 24.4px;
font-variant-east-asian: full-width; font-variant-east-asian: full-width;
} }
.terminal-1538990771-title { .terminal-4099597553-title {
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
font-family: arial; font-family: arial;
@ -36,102 +36,98 @@
-webkit-filter: drop-shadow( 2px 5px 2px rgba(0, 0, 0, .7)); -webkit-filter: drop-shadow( 2px 5px 2px rgba(0, 0, 0, .7));
filter: drop-shadow( 2px 5px 2px rgba(0, 0, 0, .7)); filter: drop-shadow( 2px 5px 2px rgba(0, 0, 0, .7));
} }
.terminal-1538990771-r1 { fill: #94e2d5;font-weight: bold } .terminal-4099597553-r1 { fill: #94e2d5;font-weight: bold }
.terminal-1538990771-r2 { fill: #c6d0f5 } .terminal-4099597553-r2 { fill: #c6d0f5 }
.terminal-1538990771-r3 { fill: #f5c2e7;font-weight: bold } .terminal-4099597553-r3 { fill: #f5c2e7;font-weight: bold }
.terminal-1538990771-r4 { fill: #c6d0f5;font-weight: bold } .terminal-4099597553-r4 { fill: #c6d0f5;font-weight: bold }
.terminal-1538990771-r5 { fill: #f9e2af;font-weight: bold } .terminal-4099597553-r5 { fill: #f9e2af;font-weight: bold }
</style> </style>
<defs> <defs>
<clipPath id="terminal-1538990771-clip-terminal"> <clipPath id="terminal-4099597553-clip-terminal">
<rect x="0" y="0" width="853.0" height="462.59999999999997" /> <rect x="0" y="0" width="853.0" height="438.2" />
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-0"> <clipPath id="terminal-4099597553-line-0">
<rect x="0" y="1.5" width="854" height="24.65"/> <rect x="0" y="1.5" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-1"> <clipPath id="terminal-4099597553-line-1">
<rect x="0" y="25.9" width="854" height="24.65"/> <rect x="0" y="25.9" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-2"> <clipPath id="terminal-4099597553-line-2">
<rect x="0" y="50.3" width="854" height="24.65"/> <rect x="0" y="50.3" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-3"> <clipPath id="terminal-4099597553-line-3">
<rect x="0" y="74.7" width="854" height="24.65"/> <rect x="0" y="74.7" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-4"> <clipPath id="terminal-4099597553-line-4">
<rect x="0" y="99.1" width="854" height="24.65"/> <rect x="0" y="99.1" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-5"> <clipPath id="terminal-4099597553-line-5">
<rect x="0" y="123.5" width="854" height="24.65"/> <rect x="0" y="123.5" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-6"> <clipPath id="terminal-4099597553-line-6">
<rect x="0" y="147.9" width="854" height="24.65"/> <rect x="0" y="147.9" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-7"> <clipPath id="terminal-4099597553-line-7">
<rect x="0" y="172.3" width="854" height="24.65"/> <rect x="0" y="172.3" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-8"> <clipPath id="terminal-4099597553-line-8">
<rect x="0" y="196.7" width="854" height="24.65"/> <rect x="0" y="196.7" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-9"> <clipPath id="terminal-4099597553-line-9">
<rect x="0" y="221.1" width="854" height="24.65"/> <rect x="0" y="221.1" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-10"> <clipPath id="terminal-4099597553-line-10">
<rect x="0" y="245.5" width="854" height="24.65"/> <rect x="0" y="245.5" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-11"> <clipPath id="terminal-4099597553-line-11">
<rect x="0" y="269.9" width="854" height="24.65"/> <rect x="0" y="269.9" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-12"> <clipPath id="terminal-4099597553-line-12">
<rect x="0" y="294.3" width="854" height="24.65"/> <rect x="0" y="294.3" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-13"> <clipPath id="terminal-4099597553-line-13">
<rect x="0" y="318.7" width="854" height="24.65"/> <rect x="0" y="318.7" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-14"> <clipPath id="terminal-4099597553-line-14">
<rect x="0" y="343.1" width="854" height="24.65"/> <rect x="0" y="343.1" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-15"> <clipPath id="terminal-4099597553-line-15">
<rect x="0" y="367.5" width="854" height="24.65"/> <rect x="0" y="367.5" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-16"> <clipPath id="terminal-4099597553-line-16">
<rect x="0" y="391.9" width="854" height="24.65"/> <rect x="0" y="391.9" width="854" height="24.65"/>
</clipPath> </clipPath>
<clipPath id="terminal-1538990771-line-17">
<rect x="0" y="416.3" width="854" height="24.65"/>
</clipPath>
</defs> </defs>
<rect fill="#1e1e2e" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="10.1667" y="1" width="870" height="511.6" rx="8"/><text class="terminal-1538990771-title" fill="#c6d0f5" text-anchor="middle" x="435" y="27">viv&#160;--help</text> <rect fill="#1e1e2e" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="10.1667" y="1" width="870" height="487.2" rx="8"/><text class="terminal-4099597553-title" fill="#c6d0f5" text-anchor="middle" x="435" y="27">viv&#160;--help</text>
<g transform="translate(32,22)"> <g transform="translate(32,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/> <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/> <circle cx="22" cy="0" r="7" fill="#febc2e"/>
<circle cx="44" cy="0" r="7" fill="#28c840"/> <circle cx="44" cy="0" r="7" fill="#28c840"/>
</g> </g>
<g transform="translate(18.166666666666664, 41) scale(.95)" clip-path="url(#terminal-1538990771-clip-terminal)"> <g transform="translate(18.166666666666664, 41) scale(.95)" clip-path="url(#terminal-4099597553-clip-terminal)">
<g class="terminal-1538990771-matrix"> <g class="terminal-4099597553-matrix">
<text class="terminal-1538990771-r1" x="0" y="20" textLength="61" clip-path="url(#terminal-1538990771-line-0)">usage</text><text class="terminal-1538990771-r2" x="61" y="20" textLength="353.8" clip-path="url(#terminal-1538990771-line-0)">:&#160;viv&#160;[-h]&#160;[-V]&#160;&lt;sub-cmd&gt;&#160;...</text><text class="terminal-1538990771-r2" x="854" y="20" textLength="12.2" clip-path="url(#terminal-1538990771-line-0)"> <text class="terminal-4099597553-r1" x="0" y="20" textLength="61" clip-path="url(#terminal-4099597553-line-0)">usage</text><text class="terminal-4099597553-r2" x="61" y="20" textLength="353.8" clip-path="url(#terminal-4099597553-line-0)">:&#160;viv&#160;[-h]&#160;[-V]&#160;&lt;sub-cmd&gt;&#160;...</text><text class="terminal-4099597553-r2" x="854" y="20" textLength="12.2" clip-path="url(#terminal-4099597553-line-0)">
</text><text class="terminal-1538990771-r2" x="854" y="44.4" textLength="12.2" clip-path="url(#terminal-1538990771-line-1)"> </text><text class="terminal-4099597553-r2" x="854" y="44.4" textLength="12.2" clip-path="url(#terminal-4099597553-line-1)">
</text><text class="terminal-1538990771-r3" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-2)">v</text><text class="terminal-1538990771-r1" x="12.2" y="68.8" textLength="24.4" clip-path="url(#terminal-1538990771-line-2)">iv</text><text class="terminal-1538990771-r3" x="48.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-2)">i</text><text class="terminal-1538990771-r1" x="61" y="68.8" textLength="48.8" clip-path="url(#terminal-1538990771-line-2)">sn&#x27;t</text><text class="terminal-1538990771-r3" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-2)">v</text><text class="terminal-1538990771-r1" x="134.2" y="68.8" textLength="36.6" clip-path="url(#terminal-1538990771-line-2)">env</text><text class="terminal-1538990771-r2" x="854" y="68.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-2)"> </text><text class="terminal-4099597553-r3" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-2)">v</text><text class="terminal-4099597553-r1" x="12.2" y="68.8" textLength="24.4" clip-path="url(#terminal-4099597553-line-2)">iv</text><text class="terminal-4099597553-r3" x="48.8" y="68.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-2)">i</text><text class="terminal-4099597553-r1" x="61" y="68.8" textLength="48.8" clip-path="url(#terminal-4099597553-line-2)">sn&#x27;t</text><text class="terminal-4099597553-r3" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-2)">v</text><text class="terminal-4099597553-r1" x="134.2" y="68.8" textLength="36.6" clip-path="url(#terminal-4099597553-line-2)">env</text><text class="terminal-4099597553-r2" x="854" y="68.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-2)">
</text><text class="terminal-1538990771-r2" x="0" y="93.2" textLength="183" clip-path="url(#terminal-1538990771-line-3)">command&#160;line:&#160;`</text><text class="terminal-1538990771-r4" x="183" y="93.2" textLength="475.8" clip-path="url(#terminal-1538990771-line-3)">viv&#160;run&#160;typer&#160;rich-click&#160;-s&#160;./script.py</text><text class="terminal-1538990771-r2" x="658.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1538990771-line-3)">`</text><text class="terminal-1538990771-r2" x="854" y="93.2" textLength="12.2" clip-path="url(#terminal-1538990771-line-3)"> </text><text class="terminal-4099597553-r2" x="0" y="93.2" textLength="183" clip-path="url(#terminal-4099597553-line-3)">command&#160;line:&#160;`</text><text class="terminal-4099597553-r4" x="183" y="93.2" textLength="475.8" clip-path="url(#terminal-4099597553-line-3)">viv&#160;run&#160;typer&#160;rich-click&#160;-s&#160;./script.py</text><text class="terminal-4099597553-r2" x="658.8" y="93.2" textLength="12.2" clip-path="url(#terminal-4099597553-line-3)">`</text><text class="terminal-4099597553-r2" x="854" y="93.2" textLength="12.2" clip-path="url(#terminal-4099597553-line-3)">
</text><text class="terminal-1538990771-r2" x="0" y="117.6" textLength="146.4" clip-path="url(#terminal-1538990771-line-4)">python&#160;api:&#160;</text><text class="terminal-1538990771-r4" x="146.4" y="117.6" textLength="536.8" clip-path="url(#terminal-1538990771-line-4)">__import__(&quot;viv&quot;).use(&quot;typer&quot;,&#160;&quot;rich-click&quot;)</text><text class="terminal-1538990771-r2" x="854" y="117.6" textLength="12.2" clip-path="url(#terminal-1538990771-line-4)"> </text><text class="terminal-4099597553-r2" x="0" y="117.6" textLength="146.4" clip-path="url(#terminal-4099597553-line-4)">python&#160;api:&#160;</text><text class="terminal-4099597553-r4" x="146.4" y="117.6" textLength="536.8" clip-path="url(#terminal-4099597553-line-4)">__import__(&quot;viv&quot;).use(&quot;typer&quot;,&#160;&quot;rich-click&quot;)</text><text class="terminal-4099597553-r2" x="854" y="117.6" textLength="12.2" clip-path="url(#terminal-4099597553-line-4)">
</text><text class="terminal-1538990771-r2" x="854" y="142" textLength="12.2" clip-path="url(#terminal-1538990771-line-5)"> </text><text class="terminal-4099597553-r2" x="854" y="142" textLength="12.2" clip-path="url(#terminal-4099597553-line-5)">
</text><text class="terminal-1538990771-r1" x="0" y="166.4" textLength="85.4" clip-path="url(#terminal-1538990771-line-6)">options</text><text class="terminal-1538990771-r2" x="85.4" y="166.4" textLength="12.2" clip-path="url(#terminal-1538990771-line-6)">:</text><text class="terminal-1538990771-r2" x="854" y="166.4" textLength="12.2" clip-path="url(#terminal-1538990771-line-6)"> </text><text class="terminal-4099597553-r1" x="0" y="166.4" textLength="85.4" clip-path="url(#terminal-4099597553-line-6)">options</text><text class="terminal-4099597553-r2" x="85.4" y="166.4" textLength="12.2" clip-path="url(#terminal-4099597553-line-6)">:</text><text class="terminal-4099597553-r2" x="854" y="166.4" textLength="12.2" clip-path="url(#terminal-4099597553-line-6)">
</text><text class="terminal-1538990771-r5" x="24.4" y="190.8" textLength="24.4" clip-path="url(#terminal-1538990771-line-7)">-h</text><text class="terminal-1538990771-r2" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-1538990771-line-7)">,&#160;</text><text class="terminal-1538990771-r5" x="73.2" y="190.8" textLength="73.2" clip-path="url(#terminal-1538990771-line-7)">--help</text><text class="terminal-1538990771-r2" x="146.4" y="190.8" textLength="463.6" clip-path="url(#terminal-1538990771-line-7)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1538990771-r2" x="854" y="190.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-7)"> </text><text class="terminal-4099597553-r5" x="24.4" y="190.8" textLength="24.4" clip-path="url(#terminal-4099597553-line-7)">-h</text><text class="terminal-4099597553-r2" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-4099597553-line-7)">,&#160;</text><text class="terminal-4099597553-r5" x="73.2" y="190.8" textLength="73.2" clip-path="url(#terminal-4099597553-line-7)">--help</text><text class="terminal-4099597553-r2" x="146.4" y="190.8" textLength="463.6" clip-path="url(#terminal-4099597553-line-7)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-4099597553-r2" x="854" y="190.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-7)">
</text><text class="terminal-1538990771-r5" x="24.4" y="215.2" textLength="24.4" clip-path="url(#terminal-1538990771-line-8)">-V</text><text class="terminal-1538990771-r2" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-1538990771-line-8)">,&#160;</text><text class="terminal-1538990771-r5" x="73.2" y="215.2" textLength="109.8" clip-path="url(#terminal-1538990771-line-8)">--version</text><text class="terminal-1538990771-r2" x="183" y="215.2" textLength="512.4" clip-path="url(#terminal-1538990771-line-8)">&#160;&#160;&#160;&#160;show&#160;program&#x27;s&#160;version&#160;number&#160;and&#160;exit</text><text class="terminal-1538990771-r2" x="854" y="215.2" textLength="12.2" clip-path="url(#terminal-1538990771-line-8)"> </text><text class="terminal-4099597553-r5" x="24.4" y="215.2" textLength="24.4" clip-path="url(#terminal-4099597553-line-8)">-V</text><text class="terminal-4099597553-r2" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-4099597553-line-8)">,&#160;</text><text class="terminal-4099597553-r5" x="73.2" y="215.2" textLength="109.8" clip-path="url(#terminal-4099597553-line-8)">--version</text><text class="terminal-4099597553-r2" x="183" y="215.2" textLength="512.4" clip-path="url(#terminal-4099597553-line-8)">&#160;&#160;&#160;&#160;show&#160;program&#x27;s&#160;version&#160;number&#160;and&#160;exit</text><text class="terminal-4099597553-r2" x="854" y="215.2" textLength="12.2" clip-path="url(#terminal-4099597553-line-8)">
</text><text class="terminal-1538990771-r2" x="854" y="239.6" textLength="12.2" clip-path="url(#terminal-1538990771-line-9)"> </text><text class="terminal-4099597553-r2" x="854" y="239.6" textLength="12.2" clip-path="url(#terminal-4099597553-line-9)">
</text><text class="terminal-1538990771-r1" x="0" y="264" textLength="134.2" clip-path="url(#terminal-1538990771-line-10)">subcommands</text><text class="terminal-1538990771-r2" x="134.2" y="264" textLength="12.2" clip-path="url(#terminal-1538990771-line-10)">:</text><text class="terminal-1538990771-r2" x="854" y="264" textLength="12.2" clip-path="url(#terminal-1538990771-line-10)"> </text><text class="terminal-4099597553-r1" x="0" y="264" textLength="134.2" clip-path="url(#terminal-4099597553-line-10)">subcommands</text><text class="terminal-4099597553-r2" x="134.2" y="264" textLength="12.2" clip-path="url(#terminal-4099597553-line-10)">:</text><text class="terminal-4099597553-r2" x="854" y="264" textLength="12.2" clip-path="url(#terminal-4099597553-line-10)">
</text><text class="terminal-1538990771-r5" x="24.4" y="288.4" textLength="109.8" clip-path="url(#terminal-1538990771-line-11)">&lt;sub-cmd&gt;</text><text class="terminal-1538990771-r2" x="854" y="288.4" textLength="12.2" clip-path="url(#terminal-1538990771-line-11)"> </text><text class="terminal-4099597553-r5" x="24.4" y="288.4" textLength="109.8" clip-path="url(#terminal-4099597553-line-11)">&lt;sub-cmd&gt;</text><text class="terminal-4099597553-r2" x="854" y="288.4" textLength="12.2" clip-path="url(#terminal-4099597553-line-11)">
</text><text class="terminal-1538990771-r5" x="48.8" y="312.8" textLength="97.6" clip-path="url(#terminal-1538990771-line-12)">list&#160;(l)</text><text class="terminal-1538990771-r2" x="146.4" y="312.8" textLength="231.8" clip-path="url(#terminal-1538990771-line-12)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;list&#160;vivenvs</text><text class="terminal-1538990771-r2" x="854" y="312.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-12)"> </text><text class="terminal-4099597553-r5" x="48.8" y="312.8" textLength="97.6" clip-path="url(#terminal-4099597553-line-12)">list&#160;(l)</text><text class="terminal-4099597553-r2" x="146.4" y="312.8" textLength="231.8" clip-path="url(#terminal-4099597553-line-12)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;list&#160;vivenvs</text><text class="terminal-4099597553-r2" x="854" y="312.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-12)">
</text><text class="terminal-1538990771-r5" x="48.8" y="337.2" textLength="97.6" clip-path="url(#terminal-1538990771-line-13)">shim&#160;(s)</text><text class="terminal-1538990771-r2" x="146.4" y="337.2" textLength="439.2" clip-path="url(#terminal-1538990771-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;viv-powered&#160;cli&#160;apps</text><text class="terminal-1538990771-r2" x="854" y="337.2" textLength="12.2" clip-path="url(#terminal-1538990771-line-13)"> </text><text class="terminal-4099597553-r5" x="48.8" y="337.2" textLength="97.6" clip-path="url(#terminal-4099597553-line-13)">shim&#160;(s)</text><text class="terminal-4099597553-r2" x="146.4" y="337.2" textLength="439.2" clip-path="url(#terminal-4099597553-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;viv-powered&#160;cli&#160;apps</text><text class="terminal-4099597553-r2" x="854" y="337.2" textLength="12.2" clip-path="url(#terminal-4099597553-line-13)">
</text><text class="terminal-1538990771-r5" x="48.8" y="361.6" textLength="85.4" clip-path="url(#terminal-1538990771-line-14)">run&#160;(r)</text><text class="terminal-1538990771-r2" x="134.2" y="361.6" textLength="585.6" clip-path="url(#terminal-1538990771-line-14)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;run&#160;an&#160;app/script&#160;with&#160;an&#160;on-demand&#160;venv</text><text class="terminal-1538990771-r2" x="854" y="361.6" textLength="12.2" clip-path="url(#terminal-1538990771-line-14)"> </text><text class="terminal-4099597553-r5" x="48.8" y="361.6" textLength="85.4" clip-path="url(#terminal-4099597553-line-14)">run&#160;(r)</text><text class="terminal-4099597553-r2" x="134.2" y="361.6" textLength="585.6" clip-path="url(#terminal-4099597553-line-14)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;run&#160;an&#160;app/script&#160;with&#160;an&#160;on-demand&#160;venv</text><text class="terminal-4099597553-r2" x="854" y="361.6" textLength="12.2" clip-path="url(#terminal-4099597553-line-14)">
</text><text class="terminal-1538990771-r5" x="48.8" y="386" textLength="85.4" clip-path="url(#terminal-1538990771-line-15)">exe&#160;(e)</text><text class="terminal-1538990771-r2" x="134.2" y="386" textLength="536.8" clip-path="url(#terminal-1538990771-line-15)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;run&#160;binary/script&#160;in&#160;existing&#160;vivenv</text><text class="terminal-1538990771-r2" x="854" y="386" textLength="12.2" clip-path="url(#terminal-1538990771-line-15)"> </text><text class="terminal-4099597553-r5" x="48.8" y="386" textLength="85.4" clip-path="url(#terminal-4099597553-line-15)">env&#160;(e)</text><text class="terminal-4099597553-r2" x="134.2" y="386" textLength="427" clip-path="url(#terminal-4099597553-line-15)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;manage&#160;the&#160;viv&#160;vivenv&#160;cache</text><text class="terminal-4099597553-r2" x="854" y="386" textLength="12.2" clip-path="url(#terminal-4099597553-line-15)">
</text><text class="terminal-1538990771-r5" x="48.8" y="410.4" textLength="109.8" clip-path="url(#terminal-1538990771-line-16)">cache&#160;(c)</text><text class="terminal-1538990771-r2" x="158.6" y="410.4" textLength="402.6" clip-path="url(#terminal-1538990771-line-16)">&#160;&#160;&#160;&#160;&#160;&#160;manage&#160;the&#160;viv&#160;vivenv&#160;cache</text><text class="terminal-1538990771-r2" x="854" y="410.4" textLength="12.2" clip-path="url(#terminal-1538990771-line-16)"> </text><text class="terminal-4099597553-r5" x="48.8" y="410.4" textLength="122" clip-path="url(#terminal-4099597553-line-16)">freeze&#160;(f)</text><text class="terminal-4099597553-r2" x="170.8" y="410.4" textLength="561.2" clip-path="url(#terminal-4099597553-line-16)">&#160;&#160;&#160;&#160;&#160;create&#160;import&#160;statement&#160;from&#160;package&#160;spec</text><text class="terminal-4099597553-r2" x="854" y="410.4" textLength="12.2" clip-path="url(#terminal-4099597553-line-16)">
</text><text class="terminal-1538990771-r5" x="48.8" y="434.8" textLength="122" clip-path="url(#terminal-1538990771-line-17)">freeze&#160;(f)</text><text class="terminal-1538990771-r2" x="170.8" y="434.8" textLength="561.2" clip-path="url(#terminal-1538990771-line-17)">&#160;&#160;&#160;&#160;&#160;create&#160;import&#160;statement&#160;from&#160;package&#160;spec</text><text class="terminal-1538990771-r2" x="854" y="434.8" textLength="12.2" clip-path="url(#terminal-1538990771-line-17)"> </text><text class="terminal-4099597553-r5" x="48.8" y="434.8" textLength="122" clip-path="url(#terminal-4099597553-line-17)">manage&#160;(m)</text><text class="terminal-4099597553-r2" x="170.8" y="434.8" textLength="268.4" clip-path="url(#terminal-4099597553-line-17)">&#160;&#160;&#160;&#160;&#160;manage&#160;viv&#160;itself</text><text class="terminal-4099597553-r2" x="854" y="434.8" textLength="12.2" clip-path="url(#terminal-4099597553-line-17)">
</text><text class="terminal-1538990771-r5" x="48.8" y="459.2" textLength="122" clip-path="url(#terminal-1538990771-line-18)">manage&#160;(m)</text><text class="terminal-1538990771-r2" x="170.8" y="459.2" textLength="268.4" clip-path="url(#terminal-1538990771-line-18)">&#160;&#160;&#160;&#160;&#160;manage&#160;viv&#160;itself</text><text class="terminal-1538990771-r2" x="854" y="459.2" textLength="12.2" clip-path="url(#terminal-1538990771-line-18)">
</text> </text>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

23
docs/conf.py Normal file
View file

@ -0,0 +1,23 @@
project = "Viv"
copyright = "2023, Daylin Morgan"
author = "Daylin Morgan"
extensions = ["myst_parser", "sphinx_copybutton"]
myst_enable_extensions = ["colon_fence", "deflist"]
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
html_extra_path = ["viv.py"]
html_theme = "shibuya"
# html_static_path = ["_static"]
html_logo = "../assets/logo.svg"
html_theme_options = {
"github_url": "https://github.com/daylinmorgan/viv",
"nav_links": [
{"title": "Documentation", "url": "installation"},
{"title": "CLI Reference", "url": "cli"},
],
}

45
docs/configuration.md Normal file
View file

@ -0,0 +1,45 @@
# Configuration
## Environment Variables
`VIV_RUN_MODE`
: **ephemeral** (default):
: `viv run` will generate a temporary directory that is removed following execution
: **semi-ephemeral**
: `viv run` will set the `VIV_CACHE` directory to `$TEMPDIR/viv-ephemeral-cache-$USER`
: persist
: `viv run` will always use the standard `VIV_CACHE` which maximizes reusable vivenvs
`VIV_CACHE`
: Path to use for vivenv cache by default `$XDG_CACHE_HOME/viv` or `$HOME/.cache/viv`
`VIV_LOG_PATH`
: Path to use for log file by default `$XDG_DATA_HOME/viv/viv.log` or `$HOME/.local/share/viv/viv.log`
`VIV_BIN_DIR`
: Path to use for shims by default `$HOME/.local/bin`
`VIV_NO_SETUPTOOLS`
: Don't add setuptools to generated vivenvs.
: Many legacy packages expect setuptools to be available
and don't appropriately declare it as a dependency.
To minimize frustration `setuptools` is added to every dependency
list.
`VIV_FORCE`
: Remove existence check and recreate vivenv
`VIV_SPEC`
: Space separated list of dependencies in addition to those in script
`VIV_VERBOSE`
: Show `pip` output in real time
`VIV_DEBUG`
: Set log level to `DEBUG`
`FORCE_COLOR`
: Force output to use ANSI escape codes
`NO_COLOR`
: Remove all ANSI escape codes from output

40
docs/index.md Normal file
View file

@ -0,0 +1,40 @@
---
layout: landing
cover: http://viv.dayl.in/_static/logo.svg
desciption: Viv isn't venv
---
# viv isn't venv
Try before you buy!
```sh
python3 <(curl -fsSL viv.dayl.in/viv.py) run frogmouth -- gh daylinmorgan/viv
```
:::{container} buttons
[Docs](/installation)
[Github](https://github.com/daylinmorgan/viv)
:::
`Viv` is a standalone dependency-free `venv` creator (just needs python + pip).
`Viv` helps you ignore silly things like managing temporary or rarely used virtual environments,
while still unleashing the full power of python scripting with it's entire ecosystem at your disposal.
`Viv`'s uncompromising insistence on portability means that it will always,
only use the standard library and never exceed a single script.
```{toctree}
:hidden:
:maxdepth: 2
installation.md
usage.md
configuration.md
vs-others.md
cli.md
```

56
docs/installation.md Normal file
View file

@ -0,0 +1,56 @@
# Installation
## Automated
Run the below command to install `viv`.
```sh
python3 <(curl -fsSL viv.dayl.in/viv.py) manage install
```
To access `viv` from within scripts you should add its location to your `PYTHONPATH`.
By default `viv` will be installed to `$XDG_DATA_HOME/viv` or `~/.local/share/viv`,
and symlinked to `$XDG_BIN_HOME` or `~/.local/bin/.`
You can customize these locations at install with with `--src` and `--cli` respectively.
```sh
export PYTHONPATH="$PYTHONPATH:$HOME/.local/share/viv"
# or
export PYTHONPATH="$PYTHONPATH:$(viv manage show --pythonpath)"
```
:::{note}
You can install an older version by specifying `--ref`
:::
## Manual
Viv is a single standalone script meaning all that's really necessary is
that it exists locally and is appropriately added to your path.
You can get the latest stable version from [here](https://viv.dayl.in/viv.py)
or from [github](https://github.com/daylinmorgan/viv/blob/latest/src/viv/viv.py).
Must be added to your `$PATH` for use as a CLI app and `$PYTHONPATH` for uses as a python module.
## Never
Viv is a standalone script accessible at `https://viv.dayl.in/viv.py`
meaning it's not strictly necessary you every actually install it.
Every instance of the `viv` CLI can be seamlessly replaced with `python3 <(curl -fsSL viv.dayl.in/viv.py)`.
## PyPI Options (Not Recommended)
Why is this *not recommended?*
Mainly because `viv` is all about hacking your `sys.path`.
Placing it in its own virtual environment
or installing in a user site directory may complicate this endeavor.
```sh
pip install viv
# or
pipx install viv
```
*Note*: If installed by `pipx`, it should still be possible to
use `viv` as a module by adding it to the `$PYTHONPATH`.
See above for more info.

202
docs/usage.md Normal file
View file

@ -0,0 +1,202 @@
# 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 `--`:
```sh
viv run frogmouth -- gh daylinmorgan/viv
```
Run a python module use the `-b/--bin` flag and specify `python`:
```sh
viv run rich -b python -- -m rich
```
Make an ephemeral `jupyter` environment with your needed deps:
```sh
viv run jupyter pandas -r requirements-dev.txt -- notebook
```
Generate an executable shell script that will on demand create a vivenv as needed:
```sh
viv shim ruff
```
Output:
```python
#!/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:
```sh
viv run rich typer -b python -- ./cli.py --help
```
Using `-s/--script`:
```sh
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:
```python
#!/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
:::
:::{seealso}
Check out [PEP723](https://peps.python.org/pep-0723/) 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.
```python
__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.
```python
__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:
```sh
viv freeze rich typer
```
Output:
```python
__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:
```sh
viv freeze rich typer --path rel
```
Output:
```python
__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`:
```sh
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:
```sh
viv list --filter "created-before:2024-01-01"
```
Associated with a particular file:
```sh
viv list --filter "files:./script.py"
```
Or no files:
```sh
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:
```sh
viv env remove $(viv list -q)
```
To remove `viv` all together you can use the included `purge` command:
```sh
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](https://github.com/daylinmorgan/viv/blob/dev/examples/black) for output of below command.
`viv freeze` also supports `--standalone`
```sh
python3 <(curl -fsSL viv.dayl.in/viv.py) shim black -o ./black --standalone --freeze
```

31
docs/vs-others.md Normal file
View file

@ -0,0 +1,31 @@
# Vs Others
## Equivalent commands from alternatives
### [pip-run](https://github.com/jaraco/pip-run)
```sh
pip-run cowsay -- -m cowsay "moove over, pip-run"
python3 <(curl -fsSL viv.dayl.in/viv.py) run cowsay -- "moove over, pip-run"
```
```sh
python -m pip-run requests -- -c "import requests; print(requests.get('https://pypi.org/project/pip-run').status_code)"
python -m viv run requests -b python -- -c "import requests; print(requests.get('https://pypi.org/project/viv').status_code)"
```
### [pipx](https://github.com/pypa/pipx/)
```sh
pipx install pycowsay
viv shim pycowsay
```
```sh
pipx run https://gist.githubusercontent.com/cs01/fa721a17a326e551ede048c5088f9e0f/raw/6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py
python3 <(curl -fsSL viv.dayl.in/viv.py) run \
-s https://gist.githubusercontent.com/cs01/fa721a17a326e551ede048c5088f9e0f/raw/6bdfbb6e9c1132b1c38fdd2f195d4a24c540c324/pipx-demo.py
```

View file

@ -7,6 +7,7 @@ it will generate a new vivenv.
It may be important to require a exe specificty if you are frequently running It may be important to require a exe specificty if you are frequently running
different version of pythons and rely on c extension modules as in numpy. different version of pythons and rely on c extension modules as in numpy.
""" """
__import__("viv").use("numpy", "plotext", track_exe=True) # noqa __import__("viv").use("numpy", "plotext", track_exe=True) # noqa
import numpy as np import numpy as np

View file

@ -6,6 +6,7 @@ This import statement was generated using
Using viv freeze ensures future runs of this Using viv freeze ensures future runs of this
script will use the same essential environment script will use the same essential environment
""" """
__import__("viv").use( __import__("viv").use(
"numpy==1.24.0", "numpy==1.24.0",
"pandas==1.5.2", "pandas==1.5.2",

View file

@ -1,17 +0,0 @@
#!/usr/bin/env -S viv run -s
# In order to run, this script needs the following 3rd party libraries
#
# Script Dependencies:
# requests
# rich # Needed for the output
#
# # Not needed - just to show that fragments in URLs do not
# # get treated as comments
# pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=2e15a2d4e7e9f394a9c7a6c905c6a239402a6442
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

15
examples/pep723.py Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env -S viv run -s
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich import print
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
print([(k, v["title"]) for k, v in data.items()][:10])

17
examples/pep723_run.py Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
__import__("viv").run()
import requests
from rich import print
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
print([(k, v["title"]) for k, v in data.items()][:10])

0
examples/stopwatch.py Executable file → Normal file
View file

View file

@ -2,14 +2,13 @@
""" """
Embed the viv.py on the sys.path at runtime rather than using PYTHONPATH Embed the viv.py on the sys.path at runtime rather than using PYTHONPATH
""" """
import sys import sys
old_sys_path = sys.path.copy() # noqa old_sys_path = sys.path.copy() # noqa
__import__("sys").path.append( __import__("sys").path.append(__import__("os").path.expanduser("~/.local/share/viv")) # noqa # isort: off
__import__("os").path.expanduser("~/.local/share/viv")
) # noqa # isort: off
__import__("viv").use("rich") # noqa # isort: off __import__("viv").use("rich") # noqa # isort: off
from difflib import unified_diff from difflib import unified_diff

View file

@ -1,35 +0,0 @@
site_name: viv
site_url: https://viv.dayl.in
repo_url: https://github.com/daylinmorgan/viv
edit_uri: edit/main/docs/
repo_name: daylinmorgan/viv
nav:
- home: index.md
- cli: cli.md
theme:
name: material
logo: https://raw.githubusercontent.com/daylinmorgan/viv/main/assets/logo.svg
favicon: https://raw.githubusercontent.com/daylinmorgan/viv/main/assets/logo.svg
features:
- navigation.tabs
- navigation.indexes
palette:
# Palette toggle for dark mode
- scheme: slate
primary: deep purple
toggle:
icon: material/brightness-4
name: Switch to light mode
# Palette toggle for light mode
- scheme: default
primary: deep purple
toggle:
icon: material/brightness-7
name: Switch to dark mode
markdown_extensions:
- footnotes

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import shutil
from pathlib import Path from pathlib import Path
import nox import nox
@ -42,11 +43,11 @@ def docs(session):
if not Path("docs/svgs").is_dir(): if not Path("docs/svgs").is_dir():
svgs(session) svgs(session)
session.run("make", "docs", external=True) shutil.copyfile("src/viv/viv.py", "docs/viv.py")
if session.interactive: if session.interactive:
session.run("mkdocs", "serve") session.run("sphinx-autobuild", "docs", "site", "--port", "8787")
else: else:
session.run("mkdocs", "build") session.run("sphinx-build", "docs", "site")
@nox.session @nox.session
@ -54,10 +55,7 @@ def release(session):
session.run("./scripts/release.py", external=True) session.run("./scripts/release.py", external=True)
# @nox.session( @nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12"])
# python=["3.8", "3.9", "3.10", "3.11"] def test(session):
# ) pdm_install(session, "test")
# def test(session): session.run("pytest", "tests/")
# pdm_install(session,'test')
# session.run('pytest')
#

882
pdm.lock

File diff suppressed because it is too large Load diff

View file

@ -21,18 +21,28 @@ viv = "viv:main"
[tool.pdm] [tool.pdm]
version = { source = "scm" } version = { source = "scm" }
# need python 3.9 for these which I usually have anyways
ignore_package_warnings = ["sphinx*", "myst-parser"]
[tool.pdm.dev-dependencies] [tool.pdm.dev-dependencies]
dev = [ dev = [
"pre-commit>=3",
"mypy>=0.991", "mypy>=0.991",
"astor>=0.8.1",
] ]
docs = [ docs = [
"mkdocs-material>=9.1.15", "sphinx",
"yartsu" "sphinx-autobuild",
"sphinx-copybutton",
"myst-parser",
"shibuya",
"yartsu",
]
test = [
"pytest",
"sampleproject"
] ]
[tool.ruff] [tool.ruff.lint]
select = ["E","F","I"] select = ["E","F","I"]
ignore = ["E402"] ignore = ["E402"]

View file

@ -15,7 +15,6 @@ CLI_DOC_PATH = DOCS_PATH / "cli.md"
cmds := dict.fromkeys( cmds := dict.fromkeys(
( (
"list", "list",
"exe",
"manage", "manage",
"freeze", "freeze",
"shim", "shim",
@ -24,7 +23,10 @@ CLI_DOC_PATH = DOCS_PATH / "cli.md"
[], [],
) )
).update( ).update(
{"manage": ["update", "purge", "show", "install"], "cache": ["info", "remove"]}, {
"manage": ["update", "purge", "show", "install"],
"env": ["exe", "info", "remove"],
},
) )
@ -33,7 +35,7 @@ cli_doc = """\
hide: [navigation] hide: [navigation]
--- ---
# cli # CLI Reference
![help](./svgs/viv-help.svg) ![help](./svgs/viv-help.svg)
""" """

View file

@ -46,11 +46,14 @@ def release():
next = f"{datetime.now().year}.{inc_build(build)}" next = f"{datetime.now().year}.{inc_build(build)}"
msg = f"bump {current} -> {next}" msg = f"bump {current} -> {next}"
FILE.write_text( FILE.write_text(
re.sub(r'__version__ = "[\d\.]+"', f'__version__ = "{next}"', FILE.read_text()) re.sub(r'__version__ = ".*"', f'__version__ = "{next}"', FILE.read_text())
) )
subprocess.run(["git", "add", FILE]) subprocess.run(["git", "add", FILE])
subprocess.run(["git", "commit", "-m", msg, "--no-verify"]) subprocess.run(["git", "commit", "-m", msg, "--no-verify"])
subprocess.run(["git", "tag", f"v{next}"]) subprocess.run(["git", "tag", f"v{next}"])
FILE.write_text(
re.sub(r'__version__ = ".*"', f'__version__ = "{next}-dev"', FILE.read_text())
)
if __name__ == "__main__": if __name__ == "__main__":

222
scripts/vendor.py Executable file
View file

@ -0,0 +1,222 @@
#!/usr/bin/env python3
import ast
import re
import subprocess
import textwrap
from pathlib import Path
from typing import List
import astor
def remove_docs_and_comments(code):
parsed = ast.parse(code)
for node in ast.walk(parsed):
if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str):
# set value to empty string which should be ignored by astor.to_source
node.value = ast.Constant(value="")
elif (
(isinstance(node, ast.FunctionDef) or isinstance(node, ast.ClassDef))
and len(node.body) == 1
and isinstance(node.body[0], ast.Expr)
and isinstance(node.body[0].value, ast.Constant)
):
# add pass to empty functions and class definition
node.body = [ast.Pass()]
formatted_code = astor.to_source(parsed)
pattern = r'^.*"""""".*$' # remove empty """"""
formatted_code = re.sub(pattern, "", formatted_code, flags=re.MULTILINE)
return formatted_code
class Package:
def __init__(
self,
name: str,
url: str,
files: List[tuple[str, List[List[int]]]],
rev: str,
basepath: Path,
imports: str = "",
prefix: str = "",
suffix: str = "",
indent: bool = False,
):
self.name = name
self.files = files
self.url = url
self.rev = rev
self.basepath = basepath
self.imports = imports
self.prefix = prefix
self.suffix = suffix
self.indent = indent
self.ensure()
self.generate_vendored_source()
self.replace_identifiers()
def ensure(self):
dir = Path(__file__).parent / self.name
if not dir.is_dir():
subprocess.run(["git", "clone", self.url, dir])
subprocess.run(["git", "-C", dir, "checkout", self.rev])
@property
def start_delim(self) -> str:
return f"#### START VENDORED {self.name.upper()} ####"
@property
def end_delim(self) -> str:
return f"#### END VENDORED {self.name.upper()} ####"
def generate_vendored_source(self):
self.src_text = ""
for f, slices in self.files:
og_text = (self.basepath / f"{f}.py").read_text()
for indices in slices:
self.src_text = "\n".join(
(
self.src_text,
*[
line
for line in og_text.splitlines()[slice(*indices)]
if line.strip("\r\n")
],
)
)
def replace_identifiers(self):
patterns = set.union(
*[
set(re.findall(regex, self.src_text, re.MULTILINE))
for regex in (
r"^class (?P<class>[a-zA-Z_]*)(?:\(.*\))?:",
r"^(?P<ident>[a-zA-Z_]*) =",
r"^def (?P<function>[a-zA-Z_]+)\(",
)
]
) - {
"Key",
} # prevent KeyError false positive by leaving Key alone
for pat in patterns:
self.src_text = re.sub(
r'(?P<lead>[\s("\[={])' + pat,
f"\g<lead>v_{self.name}_{pat}",
self.src_text,
)
def insert(self, base_text: str) -> str:
start, rest = re.split(self.start_delim, base_text)
_, rest = re.split(self.end_delim, base_text)
src = textwrap.indent(
remove_docs_and_comments(self.src_text.strip()),
prefix=" " * (4 if self.indent else 0),
)
return "\n".join(
(
start.strip(),
"\n",
self.start_delim,
self.prefix + self.imports + src + self.suffix,
self.end_delim,
"\n",
rest.strip(),
)
)
PACKAGES = [
Package(
name="packaging",
url="https://github.com/pypa/packaging.git",
# v24.0
rev="7a983f7f0068669ead9d4f7571be24d6c0d83eb9",
files=(
("_structures", [[5, 61]]),
("version", [[17, 563]]),
("utils", [[54, 100]]),
("specifiers", [[18, 1017]]),
),
basepath=Path(__file__).parent / "packaging/src/packaging",
prefix="""
# MODIFIED FROM https://github.com/pypa/packaging
# see repo for original licenses
# This software is made available under the terms of *either* of the licenses
# found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
# under the terms of *both* these licenses.
""",
imports="""
import abc # noqa
import itertools # noqa
import re # noqa
from typing import ( # noqa
Any,
Callable,
Iterable,
Iterator,
List,
NamedTuple,
Optional,
Set,
SupportsInt,
Tuple,
TypeVar,
Union,
)
""",
suffix="""
Version = v_packaging_Version
SpecifierSet = v_packaging_SpecifierSet
""",
),
Package(
name="tomli",
url="https://github.com/hukkin/tomli.git",
# rev="2.0.1",
rev="a6138675bcca68eea5b8abec7c2ec06d57f965a0",
files=(
("_types", [[7, 11]]),
("_re", [[14, 107]]),
("_parser", [[20, 691]]),
),
prefix="""
if sys.version_info >= (3, 11):
from tomllib import loads as toml_loads
else:
# MODIFIED FROM https://github.com/hukkin/tomli
# see below for original license
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
# Licensed to PSF under a Contributor Agreement.
""",
imports="""
import string # noqa
from collections.abc import Iterable # noqa
from functools import lru_cache # noqa
from datetime import date, datetime, time, timedelta, timezone, tzinfo # noqa
from types import MappingProxyType # noqa
from typing import IO, Any, Callable, NamedTuple # noqa
""",
basepath=Path(__file__).parent / "tomli/src/tomli",
suffix="""
toml_loads = v_tomli_loads
""",
indent=True,
),
]
def main():
viv_source_path = Path(__file__).parent.parent / "src/viv/viv.py"
viv_source = viv_source_path.read_text()
for pkg in PACKAGES:
viv_source = pkg.insert(viv_source)
viv_source_path.write_text(viv_source)
if __name__ == "__main__":
main()

View file

@ -1 +1 @@
from .viv import __version__, use, main # noqa from .viv import __version__, use, main, run # noqa

File diff suppressed because it is too large Load diff

45
task Executable file
View file

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# VERSION = $(git describe --tags --always --dirty=-dev | sed 's/^v//g')
function task:venv {
: "setup up pdm venv"
pdm install
}
function task:dev-install {
: "symlink development version"
mkdir -p ~/.local/share/viv
mkdir -p ~/.local/bin
ln -sf "$(pwd)/src/viv/viv.py" ~/.local/share/viv/viv.py
ln -sf ~/.local/share/viv/viv.py ~/.local/bin/viv
}
function task:_black {
: "generate black example shim"
rm -f examples/black
viv shim black -y -s -f -o examples/black
}
function task:clean {
: "clean build artifacts"
rm -rf build dist
}
function task:examples {
: "run examples to generate vivenvs"
examples="cli sys_path exe_specific frozen_import named_env scrape"
for f in $examples; do
python "examples/$f.py"
done
}
function task:_help-logo {
FORCE_COLOR=1 viv --help | yartsu -t 'viv --help' -w 70 -o assets/viv-help.svg
}
# ---- do-task boilerplate ----
function task:help { : "Show this help"; echo "do:";w=$(("$(compgen -A function | wc -L)" - 3));while read -r name; do [[ ! $name =~ ^task:_ ]] && [[ $name =~ ^task: ]] && paste <(printf '\033[1;32m%*s\033[0m\n' "$w" "${name#task:}") <(type "$name" | sed -nEe 's/^[[:space:]]*: ?"(.*)";/\1/p'); done < <(compgen -A function); }
while read -r name; do [[ $name == "task:$1" ]] && { shift; task="$name"; }; done < <(compgen -A function)
[[ -n "$1" && -z "$task" ]] && printf "\033[1;31m%s\033\0[m is not a task\n" "$1"
"${task:-task:help}" "$@" && exit "$?"

12
tests/conftest.py Normal file
View file

@ -0,0 +1,12 @@
import os
import shutil
from pathlib import Path
cache = (Path(__file__).parent / ".viv-cache").absolute()
if cache.is_dir():
shutil.rmtree(cache)
# remove local settings
os.environ = {k: v for k, v in os.environ.items() if not k.startswith("VIV_")}
os.environ["VIV_CACHE"] = str(cache)

52
tests/test_readers.py Normal file
View file

@ -0,0 +1,52 @@
import pytest
from viv.viv import _read_metadata_block, _uses_viv, _Viv_Mode
RUN_METADATA_SCRIPT = """
#!/usr/bin/env -S viv run -s
# /// script
# requires-python = ">3.10"
# dependencies = [
# "rich"
# ]
# ///
"""
USE_SCRIPT = """
#!/usr/bin/env python3
__import__("viv").use("rich")
from rich import print
print("pretty!")
"""
def test_metadata():
metadata = _read_metadata_block(RUN_METADATA_SCRIPT)
assert metadata == {"requires-python": ">3.10", "dependencies": ["rich"]}
def test_uses():
assert _uses_viv(RUN_METADATA_SCRIPT) == _Viv_Mode.NONE
assert (
_uses_viv(RUN_METADATA_SCRIPT + """\n__import__("viv").run()\n""")
== _Viv_Mode.RUN
)
assert _uses_viv(USE_SCRIPT) == _Viv_Mode.USE
assert _uses_viv("# from viv import use") == _Viv_Mode.NONE
def test_uses_fail(caplog):
with pytest.raises(SystemExit):
_uses_viv("""__import__("viv").run()\n__import__("viv").use()""")
with pytest.raises(SystemExit):
_uses_viv("""__import__("viv").unknown()""")
assert [
(
"viv",
40,
"Unexpected number of viv references in script.\n"
"Expected only 1, found: run, use",
),
("viv", 40, "Unknown function unknown associated with viv."),
] == caplog.record_tuples

17
tests/test_use.py Normal file
View file

@ -0,0 +1,17 @@
import sys
import pytest
from viv import use
def test_use():
with pytest.raises(ImportError):
import pyjokes # noqa
use("pyjokes")
import pyjokes # noqa
with pytest.raises(ImportError):
from sample.simple import add_one # noqa
assert len([p for p in sys.path if "site-packages" in p]) == 1

17
tests/test_vendored.py Normal file
View file

@ -0,0 +1,17 @@
from viv.viv import SpecifierSet, Version, toml_loads
def test_packaging():
assert Version("3.6") in SpecifierSet(">=3.6")
def test_tomli():
assert {"requires-python": ">3.6", "dependencies": ["rich", "typer"]} == toml_loads(
"""
requires-python = ">3.6"
dependencies = [
"rich",
"typer"
]
"""
)