mirror of
https://github.com/daylinmorgan/viv.git
synced 2024-11-13 20:47:53 -06:00
Compare commits
5 commits
f53d00e8e2
...
13cb21435d
Author | SHA1 | Date | |
---|---|---|---|
13cb21435d | |||
38b6e4d746 | |||
be3c7f83c9 | |||
5dacf8ab60 | |||
e31d401585 |
11 changed files with 431 additions and 203 deletions
|
@ -29,7 +29,7 @@
|
|||
|
||||
Try before you buy!
|
||||
```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
|
||||
```
|
||||
---
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ 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"]
|
||||
|
@ -13,4 +14,10 @@ html_theme = "shibuya"
|
|||
# html_static_path = ["_static"]
|
||||
html_logo = "../assets/logo.svg"
|
||||
|
||||
html_theme_options = {"github_url": "https://github.com/daylinmorgan/viv"}
|
||||
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
45
docs/configuration.md
Normal 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` the 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 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 appropriate declare it as 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
|
|
@ -1,31 +1,41 @@
|
|||
---
|
||||
hide-toc: true
|
||||
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 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].
|
||||
:::{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:
|
||||
|
||||
1. only use the standard library
|
||||
2. never exceed a single script.
|
||||
`Viv`'s uncompromising insistence on portability means that it will always,
|
||||
only use the standard library and never exceed a single script.
|
||||
|
||||
Documentation is currently a WIP please see the [cli reference](./cli.md) and the [README](https://github.com/daylinmorgan/viv)
|
||||
|
||||
```{toctree}
|
||||
:hidden:
|
||||
:maxdepth: 2
|
||||
|
||||
installation.md
|
||||
usage.md
|
||||
configuration.md
|
||||
vs-others.md
|
||||
cli.md
|
||||
```
|
||||
|
||||
|
||||
|
|
56
docs/installation.md
Normal file
56
docs/installation.md
Normal 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.
|
213
docs/usage.md
213
docs/usage.md
|
@ -1,12 +1,181 @@
|
|||
# Usage
|
||||
|
||||
In any python script with external dependencies you can add this line,
|
||||
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
|
||||
```
|
||||
|
||||
To 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`, and `files`.
|
||||
|
||||
To remove all `vivenvs` you can use the below command:
|
||||
|
||||
```sh
|
||||
|
@ -18,35 +187,8 @@ To remove `viv` all together you can use the included `purge` command:
|
|||
```sh
|
||||
python3 <(curl -fsSL viv.dayl.in/viv.py) manage purge
|
||||
```
|
||||
## 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
|
||||
```
|
||||
|
||||
## Bonus: use `viv` with just standalone snippet (37LOC)
|
||||
## 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`.
|
||||
|
@ -58,16 +200,3 @@ See [examples/black](https://github.com/daylinmorgan/viv/blob/dev/examples/black
|
|||
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-url]: https://anaconda.org/conda-forge/viv
|
||||
[pypi-shield]: https://img.shields.io/pypi/v/viv
|
||||
[pypi-url]: https://pypi.org/project/viv
|
||||
[stars-shield]: https://img.shields.io/github/stars/daylinmorgan/viv.svg
|
||||
[stars-url]: https://github.com/daylinmorgan/viv/stargazers
|
||||
[issues-shield]: https://img.shields.io/github/issues/daylinmorgan/viv.svg
|
||||
[issues-url]: https://github.com/daylinmorgan/viv/issues
|
||||
[license-shield]: https://img.shields.io/github/license/daylinmorgan/viv.svg
|
||||
[license-url]: https://github.com/daylinmorgan/viv/blob/main/LICENSE
|
||||
|
|
31
docs/vs-others.md
Normal file
31
docs/vs-others.md
Normal 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
|
||||
```
|
||||
|
||||
|
106
pdm.lock
106
pdm.lock
|
@ -5,7 +5,7 @@
|
|||
groups = ["default", "dev", "docs", "test"]
|
||||
strategy = ["cross_platform"]
|
||||
lock_version = "4.4.1"
|
||||
content_hash = "sha256:67b99f093f433bb1a56dc393d51c615b65e94ea9ad397a5460c5a2a29a21cb22"
|
||||
content_hash = "sha256:527163383ae4b3489c9082bc814dacd347ee94ad6615c214ba63496eb1bdad2a"
|
||||
|
||||
[[package]]
|
||||
name = "alabaster"
|
||||
|
@ -50,16 +50,6 @@ files = [
|
|||
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfgv"
|
||||
version = "3.4.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Validate configuration and produce human readable error messages."
|
||||
files = [
|
||||
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
|
||||
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.3.2"
|
||||
|
@ -155,15 +145,6 @@ files = [
|
|||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
summary = "Distribution utilities"
|
||||
files = [
|
||||
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
|
||||
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.20.1"
|
||||
|
@ -184,26 +165,6 @@ files = [
|
|||
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.13.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A platform independent file lock."
|
||||
files = [
|
||||
{file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
|
||||
{file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.33"
|
||||
requires_python = ">=3.8"
|
||||
summary = "File identification library for Python"
|
||||
files = [
|
||||
{file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
|
||||
{file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
|
@ -436,19 +397,6 @@ files = [
|
|||
{file = "myst_parser-2.0.0.tar.gz", hash = "sha256:ea929a67a6a0b1683cdbe19b8d2e724cd7643f8aa3e7bb18dd65beac3483bead"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodeenv"
|
||||
version = "1.8.0"
|
||||
requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||
summary = "Node.js virtual environment builder"
|
||||
dependencies = [
|
||||
"setuptools",
|
||||
]
|
||||
files = [
|
||||
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
|
||||
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
|
@ -468,16 +416,6 @@ files = [
|
|||
{file = "peppercorn-0.6.tar.gz", hash = "sha256:96d7681d7a04545cfbaf2c6fb66de67b29cfc42421aa263e4c78f2cbb85be4c6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.1.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
files = [
|
||||
{file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"},
|
||||
{file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.3.0"
|
||||
|
@ -488,23 +426,6 @@ files = [
|
|||
{file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.5.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
dependencies = [
|
||||
"cfgv>=2.0.0",
|
||||
"identify>=1.0.0",
|
||||
"nodeenv>=0.11.1",
|
||||
"pyyaml>=5.1",
|
||||
"virtualenv>=20.10.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"},
|
||||
{file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.17.2"
|
||||
|
@ -632,16 +553,6 @@ files = [
|
|||
{file = "sampleproject-3.0.0.tar.gz", hash = "sha256:117ed88e5db073bb92969a7545745fd977ee85b7019706dd256a64058f70963d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "69.0.3"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
files = [
|
||||
{file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"},
|
||||
{file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shibuya"
|
||||
version = "2024.1.2"
|
||||
|
@ -840,21 +751,6 @@ files = [
|
|||
{file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.25.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Virtual Python Environment builder"
|
||||
dependencies = [
|
||||
"distlib<1,>=0.3.7",
|
||||
"filelock<4,>=3.12.2",
|
||||
"platformdirs<5,>=3.9.1",
|
||||
]
|
||||
files = [
|
||||
{file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
|
||||
{file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yartsu"
|
||||
version = "23.5.1b1"
|
||||
|
|
|
@ -21,20 +21,21 @@ viv = "viv:main"
|
|||
|
||||
[tool.pdm]
|
||||
version = { source = "scm" }
|
||||
# need python 3.9 for these which I usually have anyways
|
||||
ignore_package_warnings = ["sphinx*", "myst-parser"]
|
||||
|
||||
[tool.pdm.dev-dependencies]
|
||||
dev = [
|
||||
"pre-commit>=3",
|
||||
"mypy>=0.991",
|
||||
"astor>=0.8.1",
|
||||
]
|
||||
docs = [
|
||||
"sphinx",
|
||||
"sphinx-autobuild",
|
||||
"sphinx-copybutton",
|
||||
"myst-parser",
|
||||
"shibuya",
|
||||
"yartsu"
|
||||
"sphinx",
|
||||
"sphinx-autobuild",
|
||||
"sphinx-copybutton",
|
||||
"myst-parser",
|
||||
"shibuya",
|
||||
"yartsu",
|
||||
]
|
||||
test = [
|
||||
"pytest",
|
||||
|
|
|
@ -33,6 +33,7 @@ from argparse import (
|
|||
from argparse import ArgumentParser as StdArgParser
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
from textwrap import dedent, fill
|
||||
|
@ -53,7 +54,7 @@ from typing import (
|
|||
Union,
|
||||
)
|
||||
|
||||
__version__ = "2023.1003-pep723"
|
||||
__version__ = "2024.1004"
|
||||
|
||||
|
||||
#### START VENDORED TOMLI ####
|
||||
|
@ -2864,41 +2865,43 @@ def make_executable(path: Path) -> None:
|
|||
os.chmod(path, mode)
|
||||
|
||||
|
||||
# TODO: abstract/deduplicate these functions
|
||||
def _uses_viv_use(txt: str) -> bool:
|
||||
return bool(
|
||||
re.search(
|
||||
class _Viv_Mode(Enum):
|
||||
NONE = 0
|
||||
USE = 1
|
||||
RUN = 2
|
||||
|
||||
|
||||
def _uses_viv(txt: str) -> _Viv_Mode:
|
||||
matches = [
|
||||
match.group("mode")
|
||||
for match in re.finditer(
|
||||
r"""
|
||||
^(?!\#)\s*
|
||||
(?:__import__\(\s*["']viv["']\s*\).use)
|
||||
|
|
||||
(?:from\ viv\ import\ use)
|
||||
#|
|
||||
#(?:import\ viv)
|
||||
|
|
||||
(?:viv.use)
|
||||
""",
|
||||
^(?!\#)\s* # ignore comments/shebangs
|
||||
(
|
||||
(?:__import__\(\s*["']viv["']\s*\)\.)
|
||||
|
|
||||
(?:from\s+viv\s+import\s+)
|
||||
|
|
||||
(?:viv\.)
|
||||
)
|
||||
(?P<mode>(\w+))
|
||||
""",
|
||||
txt,
|
||||
re.VERBOSE | re.MULTILINE,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _uses_viv_run(txt: str) -> bool:
|
||||
return bool(
|
||||
re.search(
|
||||
r"""
|
||||
^(?!\#)\s*
|
||||
(?:__import__\(\s*["']viv["']\s*\).run)
|
||||
|
|
||||
(?:from\ viv\ import\ run)
|
||||
|
|
||||
(?:viv.run)
|
||||
""",
|
||||
txt,
|
||||
re.VERBOSE | re.MULTILINE,
|
||||
]
|
||||
if len(matches) == 0:
|
||||
return _Viv_Mode.NONE
|
||||
elif len(matches) > 1:
|
||||
err_quit(
|
||||
"Unexpected number of viv references in script.\n"
|
||||
"Expected only 1, found: "
|
||||
+ ", ".join((a.style(match, "bold") for match in matches))
|
||||
)
|
||||
)
|
||||
elif (match := matches[0]) in {"run", "use"}:
|
||||
return _Viv_Mode[match.upper()]
|
||||
else:
|
||||
err_quit(f"Unknown function {a.bold}{matches[0]}{a.end} associated with viv.")
|
||||
|
||||
|
||||
METADATA_BLOCK = (
|
||||
|
@ -3437,21 +3440,20 @@ class Viv:
|
|||
script_text = fetch_script(script)
|
||||
scriptpath.write_text(script_text)
|
||||
|
||||
has_use = _uses_viv_use(script_text)
|
||||
has_run = _uses_viv_run(script_text)
|
||||
mode = _uses_viv(script_text)
|
||||
metadata = _read_metadata_block(script_text)
|
||||
deps = metadata.get("dependencies", [])
|
||||
|
||||
if requires := metadata.get("requires-python", ""):
|
||||
_check_python(requires)
|
||||
|
||||
if has_use and deps:
|
||||
if mode == _Viv_Mode.USE and deps:
|
||||
error(
|
||||
"Script Dependencies block and "
|
||||
"`viv.use` API can't be used in the same script"
|
||||
)
|
||||
|
||||
if not self.local_source and (has_use or has_run):
|
||||
if not self.local_source and mode != _Viv_Mode.NONE:
|
||||
log.debug("fetching remote copy to use for python api")
|
||||
(tmppath / "viv.py").write_text(
|
||||
fetch_script(
|
||||
|
@ -3461,7 +3463,7 @@ class Viv:
|
|||
|
||||
self._update_cache(env, keep, tmpdir)
|
||||
|
||||
if has_use:
|
||||
if mode == _Viv_Mode.USE:
|
||||
log.debug(f"script invokes viv.use passing along spec: \n '{spec}'")
|
||||
subprocess_run_quit(
|
||||
[sys.executable, "-S", scriptpath, *rest],
|
||||
|
@ -3471,9 +3473,8 @@ class Viv:
|
|||
PYTHONPATH=":".join((str(tmppath), env.get("PYTHONPATH", ""))),
|
||||
),
|
||||
)
|
||||
elif has_run:
|
||||
elif mode == _Viv_Mode.RUN:
|
||||
log.debug("script invokes viv.run letting subprocess handle deps")
|
||||
print(tmppath)
|
||||
subprocess_run_quit(
|
||||
[sys.executable, "-S", scriptpath, *rest],
|
||||
env=dict(
|
||||
|
|
52
tests/test_readers.py
Normal file
52
tests/test_readers.py
Normal 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
|
Loading…
Reference in a new issue