Compare commits

...

8 commits

Author SHA1 Message Date
c5d9d8cb65
chore: bump version 2023-05-26 19:19:27 -05:00
b349cef943
chore: remove outdated build rules 2023-05-26 19:19:17 -05:00
85689a0788
docs: update logo/README for newer release 2023-05-26 19:13:23 -05:00
c999b3a123
chore: revert version 2023-05-26 18:50:51 -05:00
aaa63467b9
docs: don't use a subdirectory 2023-05-26 18:45:17 -05:00
e3ebca86d2
chore: remove harcoded path 2023-05-26 18:40:20 -05:00
034403b7a8
docs: add minimal docs to implement remote deployment 2023-05-26 18:38:48 -05:00
9aea8acc53
feat: add the viv manage command.....
that's how it started then it turned into a more aggressive refactor
whoops
2023-05-26 18:38:07 -05:00
14 changed files with 660 additions and 175 deletions

27
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: GitHub Pages
on:
push:
tags: ["v*.*.*"]
workflow_dispatch:
jobs:
deploy-docs:
runs-on: ubuntu-22.04
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v3
- name: Add Source to Docs
run: cp ./src/viv/viv.py docs/viv.py
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.ref == 'refs/heads/main' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# publish_dir: ./site
publish_dir: ./docs

View file

@ -10,26 +10,20 @@ types: ## run mypy
bump-version: ## update version and tag commit bump-version: ## update version and tag commit
@echo "bumping to version => $(VERSION)" @echo "bumping to version => $(VERSION)"
@sed -i 's/__version__ = ".*"/__version__ = "$(VERSION)"/g' src/viv/viv.py @sed -i 's/__version__ = ".*"/__version__ = "$(VERSION)"/g' src/viv/viv.py
@sed 's/--branch .* g/--branch $(VERSION) g/g' README.md @sed -i 's/install -r .*/install -r v$(VERSION)/g' README.md
@git add src/viv/viv.py && git commit -m "chore: bump version" @git add src/viv/viv.py README.md && git commit -m "chore: bump version" --no-verify
@git tag v$(VERSION) @git tag v$(VERSION)
venv: ## generate environment venv: ## generate environment
pdm install pdm install
install: ## symlink to $PREFIX # TAPES = demo freeze list-info-remove
ln -sf $(shell pwd)/src/viv/viv.py $(PREFIX)/viv # GIFS := $(foreach n, $(TAPES), docs/$(n).gif)
# docs: $(GIFS) ## generate usage examples
uninstall: ## delete $(PREFIX)/viv # docs/%.gif: docs/%.tape
rm $(PREFIX)/viv # viv rm $$(viv l -q)
# cd docs; vhs < $*.tape
TAPES = demo freeze list-info-remove
GIFS := $(foreach n, $(TAPES), docs/$(n).gif)
docs: $(GIFS) ## generate usage examples
docs/%.gif: docs/%.tape
viv rm $$(viv l -q)
cd docs; vhs < $*.tape
clean: ## remove build artifacts clean: ## remove build artifacts
rm -rf {build,dist} rm -rf {build,dist}

View file

@ -1,9 +1,8 @@
# Viv # viv
<!-- PROJECT DEMO -->
<div align="center"> <div align="center">
<a href="https://github.com/daylinmorgan/viv"> <a href="https://github.com/daylinmorgan/viv">
<img src="https://raw.githubusercontent.com/daylinmorgan/viv/main/docs/demo.gif" alt="Logo" width=600 > <img src="https://raw.githubusercontent.com/daylinmorgan/viv/main/assets/logo.svg" alt="Logo" width=500 >
</a> </a>
<p align="center"> <p align="center">
viv isn't venv viv isn't venv
@ -11,8 +10,6 @@
</div> </div>
<br /> <br />
See [usage](https://github.com/daylinmorgan/viv/blob/main/docs/usage.md) for more demo gifs.
--- ---
Python is a great choice to quickly prototype or accomplish small tasks in scripts. Python is a great choice to quickly prototype or accomplish small tasks in scripts.
@ -26,27 +23,22 @@ prior to loading of any of the external modules.
These `venvs` can be identified by name or by their specification. These `venvs` can be identified by name or by their specification.
In any case they will be re-used across scripts (and generated on-demand, if needed). In any case they will be re-used across scripts (and generated on-demand, if needed).
**Importantly**, `viv` will remove your user site directory (`python -m 'import site;print(site.USER_SITE)'`), **Importantly**, `viv` will also remove your user site directory.
to ensure the script isn't using anything outside the standard library and the `viv`-managed `venv`. (view with: `python -m 'import site;print(site.USER_SITE)'`).
## Setup ## Setup
### Manual (Recommended) Run the below command to install `viv`.
Start by cloning the repo and symlinking the script for access to the CLI.
By default it will symlink `./src/viv/viv.py` to `~/bin/viv`.
You can set `PREFIX` to symlink to a different location.
```sh ```sh
git clone --depth 1 --branch v22.12a3 git@github.com:daylinmorgan/viv.git ~/.viv python3 <(curl -fsSL gh.dayl.in/viv/viv.py) manage install -r v23.5a1
cd ~/.viv
make install # or PREFIX=~/.local/bin make install
``` ```
Place this directory on the python path in your rc file. To access `viv` from within scripts you should add it's 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`.
```sh ```sh
export PYTHONPATH="$PYTHONPATH:$HOME/.viv/src" export PYTHONPATH="$PYTHONPATH:$HOME/.local/share/viv"
``` ```
Advanced users may recognize that principally, Advanced users may recognize that principally,
@ -72,7 +64,7 @@ to automate `vivenv` creation and installation of dependencies.
__import__("viv").use("click") __import__("viv").use("click")
``` ```
To remove all `vivenvs`: To remove all `vivenvs` you can use the below command:
```sh ```sh
viv remove $(viv list -q) viv remove $(viv list -q)
@ -80,9 +72,7 @@ viv remove $(viv list -q)
# Standalone Viv # Standalone Viv
*Requires* `python>=3.8` Supposing you want to increase the portability of your script while still employing the principles of `viv`.
Supposing you want to increase the portability of your script while still employing `viv`.
The below function can be freely pasted at the top of your scripts and requires The below function can be freely pasted at the top of your scripts and requires
no modification of your PYTHONPATH or import of additional modules (including downloading/installing `viv`). no modification of your PYTHONPATH or import of additional modules (including downloading/installing `viv`).
@ -117,8 +107,6 @@ def _viv_use(*pkgs: str, track_exe: bool = False, name: str = "") -> None:
_viv_use("markdown-it-py==2.2.0", "mdurl==0.1.2", "Pygments==2.14.0", "rich==13.3.2") # noqa _viv_use("markdown-it-py==2.2.0", "mdurl==0.1.2", "Pygments==2.14.0", "rich==13.3.2") # noqa
# fmt: on # fmt: on
# >>>>> code golfed with <3 # >>>>> code golfed with <3
``` ```
## Alternatives ## Alternatives

323
assets/logo.svg Normal file
View file

@ -0,0 +1,323 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
id="svg2233"
viewBox="0 0 286.80808 184.19898"
version="1.0"
xml:space="preserve"
width="286.80807"
height="184.19897"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
id="defs2235"><linearGradient
id="linearGradient7463"><stop
id="stop7465"
style="stop-color:#d1e3e2;stop-opacity:.54680"
offset="0" /><stop
id="stop3853"
style="stop-color:#eeeeee;stop-opacity:.61569"
offset=".5" /><stop
id="stop7467"
style="stop-color:#d1e3e2;stop-opacity:.54510"
offset="1" /></linearGradient><filter
id="filter3662"
x="-0.0066908929"
y="-0.044310786"
width="1.0133818"
height="1.0886216"><feGaussianBlur
id="feGaussianBlur3664"
stdDeviation="0.98997704" /></filter><radialGradient
id="radialGradient2603"
fx="272.03"
fy="553.58002"
gradientUnits="userSpaceOnUse"
cy="707.21997"
cx="271.98999"
gradientTransform="matrix(0.56638235,-0.00114952,1.732559e-4,0.0853702,23.42315,270.93592)"
r="210"><stop
id="stop11688"
style="stop-color:#000000;stop-opacity:.48768"
offset="0" /><stop
id="stop11690"
style="stop-color:#000000;stop-opacity:0"
offset="1" /></radialGradient><linearGradient
id="linearGradient2605"
y2="782.23999"
gradientUnits="userSpaceOnUse"
x2="287.59"
gradientTransform="matrix(1.157,0,0,1.2361,-49.276,-172.53)"
y1="739.82001"
x1="287.59"><stop
id="stop3673"
style="stop-color:#000000;stop-opacity:.10345"
offset="0" /><stop
id="stop3675"
style="stop-color:#000000;stop-opacity:0"
offset="1" /></linearGradient><linearGradient
id="linearGradient2669"
y2="775.28003"
gradientUnits="userSpaceOnUse"
x2="255.53"
gradientTransform="matrix(0.49986408,0,0,0.49986408,22.394217,-45.390032)"
y1="488.28"
x1="292.42001"><stop
id="stop3879"
style="stop-color:#ffffff;stop-opacity:.43429"
offset="0" /><stop
id="stop3881"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" /></linearGradient><linearGradient
id="linearGradient2672"
y2="460.76001"
xlink:href="#linearGradient7463"
gradientUnits="userSpaceOnUse"
x2="128.5"
gradientTransform="matrix(0.49986408,0,0,0.49986408,22.394217,-45.318973)"
y1="405.76001"
x1="440.75" /><linearGradient
id="linearGradient2675"
y2="226.81"
xlink:href="#linearGradient7463"
gradientUnits="userSpaceOnUse"
x2="371.47"
gradientTransform="matrix(-0.68684145,0,0,-0.68684145,349.40033,489.25029)"
y1="401.95999"
x1="181.42" /><linearGradient
id="linearGradient2678"
y2="460.76001"
xlink:href="#linearGradient7463"
gradientUnits="userSpaceOnUse"
x2="128.5"
gradientTransform="matrix(0.49986408,0,0,0.49986408,19.707478,97.509558)"
y1="405.76001"
x1="440.75" /><linearGradient
id="linearGradient2683"
y2="226.81"
xlink:href="#linearGradient7463"
gradientUnits="userSpaceOnUse"
x2="371.47"
gradientTransform="matrix(-0.50539246,0.71869007,0,-0.68010507,280.36657,219.24055)"
y1="401.95999"
x1="181.42" /><linearGradient
id="linearGradient2686"
y2="226.81"
xlink:href="#linearGradient7463"
gradientUnits="userSpaceOnUse"
x2="371.47"
gradientTransform="matrix(-0.13654685,-0.11142041,0,-0.68684145,286.98927,501.88457)"
y1="401.95999"
x1="181.42" /><linearGradient
id="linearGradient2689"
y2="226.81"
xlink:href="#linearGradient7463"
gradientUnits="userSpaceOnUse"
x2="371.47"
gradientTransform="matrix(-0.68684145,0,0,-0.68684145,364.94802,462.39001)"
y1="401.95999"
x1="181.42" /><linearGradient
id="linearGradient2795"><stop
style="stop-color:#b8b8b8;stop-opacity:0.49803922"
offset="0"
id="stop2797" /><stop
style="stop-color:#7f7f7f;stop-opacity:0"
offset="1"
id="stop2799" /></linearGradient><linearGradient
id="linearGradient2787"><stop
style="stop-color:#7f7f7f;stop-opacity:0.5"
offset="0"
id="stop2789" /><stop
style="stop-color:#7f7f7f;stop-opacity:0"
offset="1"
id="stop2791" /></linearGradient><linearGradient
id="linearGradient3676"><stop
style="stop-color:#b2b2b2;stop-opacity:0.5"
offset="0"
id="stop3678" /><stop
style="stop-color:#b3b3b3;stop-opacity:0"
offset="1"
id="stop3680" /></linearGradient><linearGradient
id="linearGradient3236"><stop
style="stop-color:#f4f4f4;stop-opacity:1"
offset="0"
id="stop3244" /><stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop3240" /></linearGradient><linearGradient
id="linearGradient4671"><stop
style="stop-color:#ffd43b;stop-opacity:1"
offset="0"
id="stop4673" /><stop
style="stop-color:#ffe873;stop-opacity:1"
offset="1"
id="stop4675" /></linearGradient><linearGradient
id="linearGradient4689"><stop
style="stop-color:#5a9fd4;stop-opacity:1"
offset="0"
id="stop4691" /><stop
style="stop-color:#306998;stop-opacity:1"
offset="1"
id="stop4693" /></linearGradient><linearGradient
x1="224.23996"
y1="144.75717"
x2="-65.308502"
y2="144.75717"
id="linearGradient2987"
xlink:href="#linearGradient4671"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(100.2702,99.61116)" /><linearGradient
x1="172.94208"
y1="77.475983"
x2="26.670298"
y2="76.313133"
id="linearGradient2990"
xlink:href="#linearGradient4689"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(100.2702,99.61116)" /><linearGradient
x1="224.23996"
y1="144.75717"
x2="-65.308502"
y2="144.75717"
id="linearGradient2255"
xlink:href="#linearGradient4671"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.562541,0,0,0.567972,-11.5974,-7.60954)" /><linearGradient
x1="172.94208"
y1="76.176224"
x2="26.670298"
y2="76.313133"
id="linearGradient2258"
xlink:href="#linearGradient4689"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.562541,0,0,0.567972,-11.5974,-7.60954)" /><radialGradient
cx="61.518883"
cy="132.28575"
r="29.036913"
fx="61.518883"
fy="132.28575"
id="radialGradient2801"
xlink:href="#linearGradient2795"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.177966,0,108.7434)" /><linearGradient
x1="150.96111"
y1="192.35176"
x2="112.03144"
y2="137.27299"
id="linearGradient1475"
xlink:href="#linearGradient4671"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.562541,0,0,0.567972,-9.399749,-5.305317)" /><linearGradient
x1="26.648937"
y1="20.603781"
x2="135.66525"
y2="114.39767"
id="linearGradient1478"
xlink:href="#linearGradient4689"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.562541,0,0,0.567972,-9.399749,-5.305317)" /><radialGradient
cx="61.518883"
cy="132.28575"
r="29.036913"
fx="61.518883"
fy="132.28575"
id="radialGradient1480"
xlink:href="#linearGradient2795"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.382716e-8,-0.296405,1.43676,4.683673e-7,-128.544,150.5202)" /></defs><g
id="layer4"
transform="translate(-34.19,-165.32103)"><path
id="path1648"
style="display:none;fill:none;stroke:#ffffff;stroke-width:1px"
d="m 255.17,667.94 c 105.62,-29.82 164.49,-18.7 304.85,-6.95 82.52,6.91 180.3,-11.42 237.14,-8.54 95.12,4.81 189.98,28.25 321.14,20.54" /><path
id="path11576"
style="fill:url(#radialGradient2603);fill-rule:evenodd;stroke-width:0.710589"
d="M 177.59404,304.59654 C 98.434384,304.59654 34.19,318.7799 34.19,329.71587 34.1914,340.64474 98.434384,349.52 177.59404,349.52 c 79.15965,0 143.40404,-8.87526 143.40404,-19.80413 0,-10.93597 -64.24439,-25.11933 -143.40404,-25.11933 z m -60.86198,3.58848 h 115.25759 c 1.96834,0.22028 5.15888,0.90955 6.95667,2.70734 l 24.96301,19.31382 c 1.83332,1.83332 0.98061,4.78227 -1.91859,4.78227 H 97.503511 c -2.657604,0 -4.37723,-2.55812 -3.538734,-4.12142 L 111.16104,311.0629 c 1.5633,-1.86174 2.83525,-2.74998 5.57102,-2.87788 z" /><path
id="path3669"
style="fill:url(#linearGradient2605);filter:url(#filter3662)"
transform="matrix(0.49986408,0,0,0.49986408,22.394217,-45.390034)"
d="m 169.09,708.78 c -6.32,0.32 -9.29,2.55 -12.9,7.16 l -39.85,48.94 c -1.93,3.85 2.1,10.21 8.25,10.21 h 380.72 c 6.71,0 8.64,-7.31 4.41,-11.84 l -57.78,-47.78 c -4.16,-4.46 -11.54,-6.14 -16.1,-6.69 h -9.75 c 2.65,0.85 5.26,2.09 7.13,3.97 l 49.94,38.66 c 3.66,3.66 1.98,9.56 -3.82,9.56 H 150.28 c -5.32,0 -8.79,-5.13 -7.12,-8.25 l 34.43,-39.6 c 1.66,-1.97 3.15,-3.39 5,-4.34 z" /><path
id="path3660"
style="fill:#000000;fill-opacity:0.068571;filter:url(#filter3662)"
transform="matrix(0.49986408,0,0,0.49986408,22.394217,-45.390034)"
d="m 176.84,714 c -1.54,0.96 -2.86,2.27 -4.31,4 l -35.72,41.06 c -1.73,3.24 1.86,8.57 7.38,8.56 H 485.5 c 6.01,0 7.77,-6.1 3.97,-9.9 l -4.66,-3.6 c 1.04,3.31 -0.96,6.85 -5.47,6.85 H 150.28 c -5.32,0 -8.79,-5.13 -7.12,-8.25 z" /><path
id="path3873"
d="m 266.56693,326.90194 h -6.3882 l 5.96185,-130.62764 h 6.3882 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3871"
d="M 99.500268,329.10477 H 93.119175 V 196.01138 h 6.381093 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3867"
d="m 114.54344,310.55128 c -2.65049,-0.0995 -4.74673,-0.52584 -5.39337,-4.1001 l -15.83193,19.39909 c -0.390824,6.17502 0.483201,8.12914 4.213795,7.54646 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3869"
d="m 234.71832,310.56549 c 2.60075,-0.0924 5.30099,-2.416 5.08782,-5.50707 l 26.83185,20.10968 c -0.44767,6.8714 -1.76937,9.05291 -3.90824,8.73315 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3863"
d="m 111.01892,169.85601 c 0.0782,-1.26911 -2.01097,-1.64359 -2.94894,-0.0185 L 95.002237,190.6109 c -1.009037,1.65568 -1.819109,2.40179 -1.819109,5.72735 l 3.389511,-2.99158 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3865"
d="m 241.19178,170.08553 c -0.46898,-1.181 1.98966,-1.58248 3.34688,-0.73901 l 25.21882,20.3193 c 1.48513,1.25774 2.48706,2.8992 2.59365,6.30293 l -3.78034,-2.6434 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3857"
d="m 115.53827,306.08878 h -6.38109 V 170.07487 h 6.38109 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3859"
d="m 239.8843,305.9893 c 0,1.94701 -2.54391,4.76805 -5.89789,4.76805 H 114.72109 c -3.26871,0 -5.53549,-1.06588 -5.53549,-4.76805 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3861"
d="m 239.95536,306.08878 h -6.38109 V 170.07487 h 6.38109 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3855"
d="m 245.44822,170.0834 c -1.81201,-1.53914 -6.69376,-4.76237 -10.04063,-4.76237 H 114.76373 c -3.07685,0 -5.08782,2.03939 -6.85719,4.76237 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3849"
d="M 230.37661,307.51706 H 117.73399 c -2.37337,0 -5.09493,-3.05553 -5.09493,-5.22283 V 171.24024 c 0,-1.09147 2.13888,-3.62046 3.69507,-3.61832 l 116.2311,0.16556 c 2.0465,0 3.3753,2.2341 3.3753,3.62969 V 303.2386 c 0,2.27389 -1.9257,4.24933 -5.56392,4.27775 z"
style="fill:url(#linearGradient2689);stroke-width:0.710589" /><path
id="path3845"
style="fill:url(#linearGradient2686);stroke-width:0.710589"
d="m 262.15417,327.49884 -24.10319,-19.1646 c -1.208,-0.95929 -0.81718,-5.03808 -0.81718,-5.03808 V 172.29191 c 0,-3.14365 5.68472,-4.25501 7.5962,-2.71232 l 25.33962,20.47705 c 1.52777,1.23643 2.36626,4.13563 2.36626,6.90693 l -5.80551,127.2026 c 0,5.40759 -2.23125,5.63498 -4.5762,3.33267 z" /><path
id="path3837"
style="fill:url(#linearGradient2683);stroke-width:0.710589"
d="m 108.77346,312.39881 -12.001856,12.85456 c -2.138874,2.2881 -3.496099,8.60524 -3.496099,3.83008 l -0.1208,-131.33112 c -0.0071,-0.93798 0.774542,-4.15695 1.840426,-5.83394 l 13.259599,-20.81317 c 0.67506,-1.05238 3.22607,-1.03959 3.22607,1.69121 v 133.21418 c 0,2.24547 -1.71252,5.11625 -2.70734,6.3882 z" /><path
id="path7416"
d="m 272.53588,196.45195 c 0,-2.11045 -0.87402,-4.87465 -2.38758,-6.3882 H 95.371743 c -1.094307,1.49934 -2.238356,3.98641 -2.238356,6.3882 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3835"
d="m 265.72844,329.05503 c 0,3.82297 -1.50645,5.94763 -3.6098,5.94763 H 97.801959 c -3.403723,0 -4.668572,-2.62208 -4.668572,-6.0329 z"
style="fill:#9ab7c1;stroke-width:0.710589" /><path
id="path3831"
d="m 116.73917,308.18502 h 115.25048 c 1.96834,0.22028 5.15888,0.90245 6.95667,2.70024 l 24.96301,19.32803 c 1.82621,1.83332 0.98061,4.78226 -1.91149,4.78226 H 97.510617 c -2.66471,0 -4.391442,-2.57233 -3.552946,-4.12852 l 17.203369,-19.78991 c 1.5633,-1.86885 2.84236,-2.7642 5.57813,-2.8921 z"
style="fill:url(#linearGradient2678);stroke-width:0.710589" /><path
id="rect7391"
d="M 262.18259,332.01108 H 98.56229 c -2.373369,0 -3.474782,-0.68927 -3.474782,-2.84947 V 196.72908 c 0,-1.09431 0.696377,-2.24547 2.259674,-2.24547 l 171.657068,0.16344 c 0.98062,0.007 2.50128,0.3624 2.50128,1.75516 l -5.7842,131.33112 c 0,2.26678 0.10659,4.24222 -3.53874,4.27775 z"
style="fill:url(#linearGradient2675);stroke-width:0.710589" /><g
id="g2303"
transform="matrix(1.0141096,0,0,1.0141096,120.25928,195.81719)"><path
id="path1948"
style="fill:url(#linearGradient1478);fill-opacity:1"
d="m 60.510156,6.3979729 c -4.583653,0.021298 -8.960939,0.4122177 -12.8125,1.09375 C 36.35144,9.4962267 34.291407,13.691825 34.291406,21.429223 v 10.21875 h 26.8125 v 3.40625 h -26.8125 -10.0625 c -7.792459,0 -14.6157592,4.683717 -16.7500002,13.59375 -2.46182,10.212966 -2.5710151,16.586023 0,27.25 1.9059283,7.937852 6.4575432,13.593748 14.2500002,13.59375 h 9.21875 v -12.25 c 0,-8.849902 7.657144,-16.656248 16.75,-16.65625 h 26.78125 c 7.454951,0 13.406253,-6.138164 13.40625,-13.625 v -25.53125 c 0,-7.266339 -6.12998,-12.7247775 -13.40625,-13.9375001 -4.605987,-0.7667253 -9.385097,-1.1150483 -13.96875,-1.09375 z m -14.5,8.2187501 c 2.769547,0 5.03125,2.298646 5.03125,5.125 -2e-6,2.816336 -2.261703,5.09375 -5.03125,5.09375 -2.779476,-1e-6 -5.03125,-2.277415 -5.03125,-5.09375 -1e-6,-2.826353 2.251774,-5.125 5.03125,-5.125 z" /><path
id="path1950"
style="fill:url(#linearGradient1475);fill-opacity:1"
d="m 91.228906,35.054223 v 11.90625 c 0,9.230755 -7.825895,16.999999 -16.75,17 h -26.78125 c -7.335833,0 -13.406249,6.278483 -13.40625,13.625 v 25.531247 c 0,7.26634 6.318588,11.54032 13.40625,13.625 8.487331,2.49561 16.626237,2.94663 26.78125,0 6.750155,-1.95439 13.406253,-5.88761 13.40625,-13.625 V 92.897973 h -26.78125 v -3.40625 h 26.78125 13.406254 c 7.79246,0 10.69625,-5.435408 13.40624,-13.59375 2.79933,-8.398886 2.68022,-16.475776 0,-27.25 -1.92578,-7.757441 -5.60387,-13.59375 -13.40624,-13.59375 z m -15.0625,64.65625 c 2.779478,3e-6 5.03125,2.277417 5.03125,5.093747 -2e-6,2.82635 -2.251775,5.125 -5.03125,5.125 -2.76955,0 -5.03125,-2.29865 -5.03125,-5.125 2e-6,-2.81633 2.261697,-5.093747 5.03125,-5.093747 z" /><path
id="path1894"
style="opacity:0.44382;fill:url(#radialGradient1480);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(0.73406,0,0,0.809524,16.24958,27.00935)"
d="m 110.46717,132.28575 a 48.948285,8.6066792 0 1 1 -97.896571,0 48.948285,8.6066792 0 1 1 97.896571,0 z" /></g><path
id="rect7393"
style="fill:url(#linearGradient2672);stroke-width:0.710589"
d="m 115.10481,165.35798 c -2.95605,0 -4.89596,1.63364 -5.74867,2.88997 l -13.749902,21.88686 c -0.753225,1.30748 -0.09238,2.24546 1.129837,2.24546 l 170.733305,0.24871 c 2.26678,-0.17765 3.29003,-0.86692 0.0426,-3.33977 l -24.7285,-20.29159 c -1.7978,-1.80135 -4.60462,-3.63964 -8.10783,-3.63964 z" /><path
id="path3875"
d="M 262.18259,332.01108 H 98.56229 c -2.373369,0 -3.474782,-0.68927 -3.474782,-2.84947 v -76.63706 c 48.163742,12.18661 119.151622,17.80737 173.504602,-1.47802 l -2.87078,76.6868 c 0,2.26678 0.10659,4.24222 -3.53874,4.27775 z"
style="fill:url(#linearGradient2669);stroke-width:0.710589" /></g><metadata
id="metadata49"><rdf:RDF><cc:Work><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" /><dc:publisher><cc:Agent
rdf:about="http://openclipart.org/"><dc:title>Openclipart</dc:title></cc:Agent></dc:publisher><dc:date>2007-10-31T17:22:52</dc:date><dc:description>A strip of Poll box by BenBois</dc:description><dc:source>https://openclipart.org/detail/7782/transparent-cube-by-molumen</dc:source><dc:creator><cc:Agent><dc:title>molumen</dc:title></cc:Agent></dc:creator><dc:subject><rdf:Bag><rdf:li>box</rdf:li><rdf:li>cube</rdf:li><rdf:li>glass</rdf:li><rdf:li>plexiglass</rdf:li><rdf:li>remix</rdf:li><rdf:li>shape</rdf:li><rdf:li>transparent</rdf:li></rdf:Bag></dc:subject></cc:Work><cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/"><cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" /><cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" /><cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /></cc:License></rdf:RDF></metadata></svg>

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 KiB

View file

@ -1,11 +0,0 @@
#!/usr/bin/env python3
__import__("viv").use("pyfiglet==0.8.post1") # noqa
from pyfiglet import Figlet
if __name__ == "__main__":
f = Figlet(font="slant")
figtxt = f.renderText("viv").splitlines()
figtxt[-2] += " isn't venv!"
print("\n".join(figtxt))

View file

@ -1,23 +0,0 @@
Set Height 750
Set Width 1500
Set Theme "Catppuccin Mocha"
Output ./demo.gif
Type "viv list"
Enter
Sleep 2s
Type "python ../examples/cli.py --help"
Enter
Sleep 10s
Type "viv list"
Enter
Sleep 3s
Type "viv info 841"
Enter
Sleep 3s
Type "python ../examples/cli.py hello 'prospective viv user!'"
Enter
Sleep 2s
Type "python ../examples/cli.py goodbye 'prospective viv user!'"
Enter
Sleep 2s

View file

@ -1,23 +0,0 @@
Set Height 750
Set Width 1500
Set Theme "Catppuccin Mocha"
Output ./freeze.gif
Type "viv freeze --help"
Enter
Sleep 2s
Type "viv list"
Enter
Sleep 2s
Type "viv freeze requests bs4"
Enter
Sleep 7s
Type "viv list"
Enter
Sleep 3s
Type "viv freeze requests bs4 --keep --path relative"
Enter
Sleep 7s
Type "viv list"
Enter
Sleep 5s

9
docs/index.html Normal file
View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<link rel="canonical" href="https://github.com/daylinmorgan/viv"/>
<meta name="robots" content="noindex">
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta http-equiv="refresh" content="0; url=https://github.com/daylinmorgan/viv"/>
</head>
</html>

View file

@ -1,30 +0,0 @@
Set Height 750
Set Width 1500
Set Theme "Catppuccin Mocha"
Output ./list-info-remove.gif
Type "cat demo.py"
Enter
Sleep 500ms
Type "python demo.py"
Enter
Sleep 3s
Type "viv list -h"
Enter
Sleep 500ms
Type "viv list -q"
Enter
Sleep 1s
Type "viv info -h"
Enter
Sleep 500ms
Type "viv info "
Sleep 1s
Type "f8"
Enter
Sleep 1s
Type "viv remove -h"
Enter
Type "viv rm f8"
Enter
Sleep 2s

View file

@ -1,18 +0,0 @@
# Usage
*NOTE*: these demo gif's are a work in progress. If you'd like to see them you can run `vhs` locally with `make docs`.
<div align="center">
# Demo
<img src="https://raw.githubusercontent.com/daylinmorgan/viv/main/docs/demo.gif" alt="demo" width=600 >
# Freeze
<img src="https://raw.githubusercontent.com/daylinmorgan/viv/main/docs/freeze.gif" alt="demo" width=600 >
# List | Info | Remove
<img src="https://raw.githubusercontent.com/daylinmorgan/viv/main/docs/list-info-remove.gif" alt="demo" width=600 >
</div>

View file

@ -9,8 +9,6 @@ viv shim black -o ~/bin/black
""" """
import sys import sys
sys.path.append("/home/daylin/.viv/src") # noqa
import subprocess import subprocess
import viv import viv

View file

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

View file

@ -21,6 +21,8 @@ import sys
import tempfile import tempfile
import threading import threading
import time import time
from urllib.request import urlopen
from urllib.error import HTTPError
import venv import venv
from argparse import SUPPRESS, Action from argparse import SUPPRESS, Action
from argparse import ArgumentParser as StdArgParser from argparse import ArgumentParser as StdArgParser
@ -39,6 +41,7 @@ from types import TracebackType
from typing import ( from typing import (
Any, Any,
Dict, Dict,
Generator,
List, List,
NoReturn, NoReturn,
Optional, Optional,
@ -46,10 +49,9 @@ from typing import (
TextIO, TextIO,
Tuple, Tuple,
Type, Type,
Generator,
) )
__version__ = "22.12a3-41-g5c40210-dev" __version__ = "23.5a1"
@dataclass @dataclass
@ -59,9 +61,22 @@ class Config:
venvcache: Path = ( venvcache: Path = (
Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) / "viv" / "venvs" Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) / "viv" / "venvs"
) )
srccache: Path = (
Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache")) / "viv" / "src"
)
srcdefault: Path = (
Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local" / "share"))
/ "viv"
/ "viv.py"
)
def __post_init__(self) -> None: def __post_init__(self) -> None:
self.venvcache.mkdir(parents=True, exist_ok=True) self.venvcache.mkdir(parents=True, exist_ok=True)
self.srccache.mkdir(
parents=True,
exist_ok=True,
)
self.srcdefault.parent.mkdir(parents=True, exist_ok=True)
c = Config() c = Config()
@ -235,6 +250,9 @@ class Ansi:
else: else:
return (row,) return (row,)
def viv_preamble(self, style: str = "magenta", sep: str = "::") -> str:
return f"{self.cyan}Viv{self.end}{self.__dict__[style]}{sep}{self.end}"
def table( def table(
self, rows: Tuple[Tuple[str, Sequence[str]], ...], header_style: str = "cyan" self, rows: Tuple[Tuple[str, Sequence[str]], ...], header_style: str = "cyan"
) -> None: ) -> None:
@ -299,12 +317,27 @@ def echo(
msg: str, style: str = "magenta", newline: bool = True, fd: TextIO = sys.stderr msg: str, style: str = "magenta", newline: bool = True, fd: TextIO = sys.stderr
) -> None: ) -> None:
"""output general message to stdout""" """output general message to stdout"""
output = f"{a.cyan}Viv{a.end}{a.__dict__[style]}::{a.end} {msg}" output = f"{a.viv_preamble(style)} {msg}"
if newline: if newline:
output += "\n" output += "\n"
fd.write(output) fd.write(output)
def confirm(question: str, context: str = "") -> bool:
sys.stderr.write(context)
sys.stderr.write(
a.viv_preamble(sep="?? ") + question + a.style(" (Y)es/(n)o: ", "yellow")
)
while True:
ans = input().strip().lower()
if ans in ("y", "yes"):
return True
elif ans in ("n", "no"):
return False
sys.stdout.write("Please select (Y)es or (n)o. ")
sys.stdout.write("\n")
def run( def run(
command: List[str], command: List[str],
spinmsg: str = "", spinmsg: str = "",
@ -448,7 +481,7 @@ class ViVenv:
a.table((("key", "value"), *((k, v) for k, v in info.items()))) a.table((("key", "value"), *((k, v) for k, v in info.items())))
def use(*packages: str, track_exe: bool = False, name: str = "") -> None: def use(*packages: str, track_exe: bool = False, name: str = "") -> ViVenv:
"""create a vivenv and append to sys.path """create a vivenv and append to sys.path
Args: Args:
@ -467,6 +500,7 @@ def use(*packages: str, track_exe: bool = False, name: str = "") -> None:
vivenv.dump_info(write=True) vivenv.dump_info(write=True)
modify_sys_path(vivenv.path) modify_sys_path(vivenv.path)
return vivenv
def validate_spec(spec: Tuple[str, ...]) -> None: def validate_spec(spec: Tuple[str, ...]) -> None:
@ -515,8 +549,7 @@ STANDALONE_TEMPLATE = r"""
# >>>>> code golfed with <3 # >>>>> code golfed with <3
""" # noqa """ # noqa
STANDALONE_TEMPLATE_USE = r""" STANDALONE_TEMPLATE_USE = r"""def _viv_use(*pkgs: str, track_exe: bool = False, name: str = "") -> None:
def _viv_use(*pkgs: str, track_exe: bool = False, name: str = "") -> None:
i,s,m,e,spec=__import__,str,map,lambda x: True if x else False,[*pkgs] i,s,m,e,spec=__import__,str,map,lambda x: True if x else False,[*pkgs]
if not {{*m(type,pkgs)}}=={{s}}: raise ValueError(f"spec: {{pkgs}} is invalid") if not {{*m(type,pkgs)}}=={{s}}: raise ValueError(f"spec: {{pkgs}} is invalid")
ge,sys,P,ew=i("os").getenv,i("sys"),i("pathlib").Path,i("sys").stderr.write ge,sys,P,ew=i("os").getenv,i("sys"),i("pathlib").Path,i("sys").stderr.write
@ -535,9 +568,27 @@ def _viv_use(*pkgs: str, track_exe: bool = False, name: str = "") -> None:
i("json").dump({{"created":s(i("datetime").datetime.today()),"id":_id,"spec":spec,"exe":exe}},f) i("json").dump({{"created":s(i("datetime").datetime.today()),"id":_id,"spec":spec,"exe":exe}},f)
sys.path = [p for p in (*sys.path,s(*(env/"lib").glob("py*/si*"))) if p!=i("site").USER_SITE] sys.path = [p for p in (*sys.path,s(*(env/"lib").glob("py*/si*"))) if p!=i("site").USER_SITE]
_viv_use({spec}) _viv_use({spec})
"""[ # noqa """ # noqa
1:
] SHOW_TEMPLATE = f"""
{a.style('Version', 'bold')}: {{version}}
{a.style('CLI', 'bold')}: {{cli}}
{a.style('Running Source', 'bold')}: {{running_src}}
{a.style('Local Source', 'bold')}: {{local_src}}
"""
INSTALL_TEMPLATE = f"""
Install viv.py to {a.green}{{src_location}}{a.end}
Symlink {a.bold}{{src_location}}{a.end} to {a.bold}{{cli_location}}{a.end}
"""
UPDATE_TEMPLATE = f"""
Update source at {a.green}{{src_location}}{a.end}
Symlink {a.bold}{{src_location}}{a.end} to {a.bold}{{cli_location}}{a.end}
Version {a.bold}{{local_version}}{a.end} -> {a.bold}{{next_version}}{a.end}
"""
def noqa(txt: str) -> str: def noqa(txt: str) -> str:
@ -691,7 +742,7 @@ class CustomHelpFormatter(RawDescriptionHelpFormatter, HelpFormatter):
# determine the required width and the entry label # determine the required width and the entry label
help_position = min(self._action_max_length + 2, self._max_help_position) help_position = min(self._action_max_length + 2, self._max_help_position)
help_width = max(self._width - help_position, 11) help_width = max(self._width - help_position, 11)
action_width = help_position - self._current_indent - 2 action_width = help_position - self._current_indent
action_header = self._format_action_invocation(action) action_header = self._format_action_invocation(action)
action_header_len = len(a.escape(action_header)) action_header_len = len(a.escape(action_header))
@ -763,7 +814,8 @@ class ArgumentParser(StdArgParser):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.formatter_class = lambda prog: CustomHelpFormatter( self.formatter_class = lambda prog: CustomHelpFormatter(
prog, max_help_position=35 prog,
max_help_position=35,
) )
def error(self, message: str) -> NoReturn: def error(self, message: str) -> NoReturn:
@ -776,18 +828,76 @@ class ArgumentParser(StdArgParser):
description = f""" description = f"""
{a.tagline()} {a.tagline()}
to create/activate a vivenv:
{a.style('create/activate a vivenv','underline')} - from command line: `{a.style("viv -h","bold")}`
from command line: - within python script: {a.style('__import__("viv").use("typer", "rich-click")','bold')}
`{a.style("viv -h","bold")}`
within python script:
{a.style('__import__("viv").use("typer", "rich-click")','bold')}
""" """
def fetch_source(reference: str) -> str:
try:
r = urlopen(
"https://raw.githubusercontent.com/daylinmorgan/viv/"
+ reference
+ "/src/viv/viv.py"
)
except HTTPError as e:
error(
"Issue updating viv see below:"
+ a.style("-> ", "red").join(["\n"] + repr(e).splitlines())
)
if "404" in repr(e):
echo("Please check your reference is valid.", style="red")
sys.exit(1)
src = r.read()
(hash := hashlib.sha256()).update(src)
sha256 = hash.hexdigest()
cached_src_file = c.srccache / f"{sha256}.py"
if not cached_src_file.is_file():
with cached_src_file.open("w") as f:
f.write(src.decode())
return sha256
def make_executable(path: Path) -> None:
"""apply an executable bit for all users with read access"""
mode = os.stat(path).st_mode
mode |= (mode & 0o444) >> 2 # copy R bits to X
os.chmod(path, mode)
class Viv: class Viv:
def __init__(self) -> None: def __init__(self) -> None:
self.vivenvs = get_venvs() self.vivenvs = get_venvs()
self._get_sources()
self.name = (
"viv" if self.local else "python3 <(curl -fsSL gh.dayl.in/viv/viv.py)"
)
def _get_sources(self) -> None:
self.local_source: Path | str
self.running_source = Path(__file__).resolve()
self.local = not str(self.running_source).startswith("/proc/")
if self.local:
self.local_source = self.running_source
self.local_version = __version__
else:
try:
_local_viv = __import__("viv")
self.local_source = (
_local_viv.__file__ if _local_viv.__file__ else "Not Found"
)
self.local_version = _local_viv.__version__
except ImportError:
self.local_source = self.local_version = "Not Found"
self.git = self.local_source != "Not Found" and str(self.local_source).endswith(
"src/viv/__init__.py"
)
def _match_vivenv(self, name_id: str) -> ViVenv: # type: ignore[return] def _match_vivenv(self, name_id: str) -> ViVenv: # type: ignore[return]
# TODO: improve matching algorithm to favor names over id's # TODO: improve matching algorithm to favor names over id's
@ -895,11 +1005,116 @@ class Viv:
vivenv.dump_info() vivenv.dump_info()
def _install_local_src(self, sha256: str, src: Path, cli: Path) -> None:
echo("updating local source copy of viv")
shutil.copy(c.srccache / f"{sha256}.py", src)
make_executable(src)
echo("symlinking cli")
if not cli.is_file():
cli.symlink_to(src)
else:
cli.unlink()
cli.symlink_to(src)
echo("Remember to include the following line in your shell rc file:")
sys.stderr.write(
' export PYTHONPATH="$PYTHONPATH:$HOME/'
f'{src.relative_to(Path.home())}"\n'
)
def manage(self, args: Namespace) -> None:
"""manage viv itself"""
if args.cmd == "show":
echo("Current:")
sys.stdout.write(
SHOW_TEMPLATE.format(
version=__version__,
cli=shutil.which("viv"),
running_src=self.running_source,
local_src=self.local_source,
)
)
elif args.cmd == "update":
if self.local_source == "Not Found":
error(
a.style("viv manage update", "bold")
+ " should be used with an exisiting installation",
1,
)
if self.git:
error(
a.style("viv manage update", "bold")
+ " shouldn't be used with a git-based installation",
1,
)
sha256 = fetch_source(args.reference)
sys.path.append(str(c.srccache))
next_version = __import__(sha256).__version__
if self.local_version == next_version:
echo(f"no change between {args.reference} and local version")
sys.exit(0)
if confirm(
"Would you like to perform the above installation steps?",
UPDATE_TEMPLATE.format(
src_location=self.local_source,
local_version=self.local_version,
cli_location=args.cli,
next_version=next_version,
),
):
self._install_local_src(
sha256,
Path(
args.src
if self.local_source == "Not Found"
else self.local_source,
),
args.cli,
)
elif args.cmd == "install":
if not self.local_source == "Not Found":
error(f"found existing viv installation at {self.local_source}")
echo(
"use "
+ a.style("viv manage update", "bold")
+ " to modify current installation.",
style="red",
)
sys.exit(1)
sha256 = fetch_source(args.reference)
sys.path.append(str(c.srccache))
downloaded_version = __import__(sha256).__version__
echo(f"Downloaded version: {downloaded_version}")
# TODO: see if file is actually where
# we are about to install and give more instructions
if confirm(
"Would you like to perform the above installation steps?",
INSTALL_TEMPLATE.format(
src_location=args.src,
cli_location=args.cli,
),
):
self._install_local_src(sha256, args.src, args.cli)
def _get_subcmd_parser( def _get_subcmd_parser(
self, subparsers: _SubParsersAction[ArgumentParser], name: str, **kwargs: Any self,
subparsers: _SubParsersAction[ArgumentParser],
name: str,
attr: Optional[str] = None,
**kwargs: Any,
) -> ArgumentParser: ) -> ArgumentParser:
aliases = kwargs.pop("aliases", [name[0]]) aliases = kwargs.pop("aliases", [name[0]])
cmd = getattr(self, name) cmd = getattr(self, attr if attr else name)
parser: ArgumentParser = subparsers.add_parser( parser: ArgumentParser = subparsers.add_parser(
name, name,
help=cmd.__doc__.splitlines()[0], help=cmd.__doc__.splitlines()[0],
@ -914,7 +1129,7 @@ class Viv:
def cli(self) -> None: def cli(self) -> None:
"""cli entrypoint""" """cli entrypoint"""
parser = ArgumentParser(description=description) parser = ArgumentParser(prog=self.name, description=description)
parser.add_argument( parser.add_argument(
"-V", "-V",
"--version", "--version",
@ -927,10 +1142,7 @@ class Viv:
) )
p_vivenv_arg = ArgumentParser(add_help=False) p_vivenv_arg = ArgumentParser(add_help=False)
p_vivenv_arg.add_argument("vivenv", help="name/hash of vivenv") p_vivenv_arg.add_argument("vivenv", help="name/hash of vivenv")
p_list = self._get_subcmd_parser( p_list = self._get_subcmd_parser(subparsers, "list")
subparsers,
"list",
)
p_list.add_argument( p_list.add_argument(
"-q", "-q",
@ -955,16 +1167,15 @@ class Viv:
nargs="*", nargs="*",
) )
p_exe_python = p_exe_sub.add_parser( p_exe_sub.add_parser(
"python", "python",
help="run command with python", help="run command with python",
parents=[p_vivenv_arg, p_exe_shared], parents=[p_vivenv_arg, p_exe_shared],
) ).set_defaults(func=self.exe, exe="python")
p_exe_pip = p_exe_sub.add_parser(
p_exe_sub.add_parser(
"pip", help="run command with pip", parents=[p_vivenv_arg, p_exe_shared] "pip", help="run command with pip", parents=[p_vivenv_arg, p_exe_shared]
) ).set_defaults(func=self.exe, exe="pip")
p_exe_python.set_defaults(func=self.exe, exe="python")
p_exe_pip.set_defaults(func=self.exe, exe="pip")
p_remove = self._get_subcmd_parser( p_remove = self._get_subcmd_parser(
subparsers, subparsers,
@ -1008,6 +1219,46 @@ class Viv:
"info", "info",
parents=[p_vivenv_arg], parents=[p_vivenv_arg],
) )
p_manage_shared = ArgumentParser(add_help=False)
p_manage_shared.add_argument(
"-r",
"--reference",
help="git reference (branch/tag/commit)",
default="main",
)
p_manage_shared.add_argument(
"-s",
"--src",
help="path/to/source_file",
default=c.srcdefault,
)
p_manage_shared.add_argument(
"-c",
"--cli",
help="path/to/cli (symlink to src)",
default=Path.home() / "bin" / "viv",
)
p_manage_sub = self._get_subcmd_parser(
subparsers,
name="manage",
).add_subparsers(title="subcommand", metavar="<sub-cmd>", required=True)
p_manage_sub.add_parser(
"install", help="install viv", aliases="i", parents=[p_manage_shared]
).set_defaults(func=self.manage, cmd="install")
p_manage_sub.add_parser(
"update",
help="update viv version",
aliases="u",
parents=[p_manage_shared],
).set_defaults(func=self.manage, cmd="update")
p_manage_sub.add_parser(
"show", help="show current installation info", aliases="s"
).set_defaults(func=self.manage, cmd="show")
args = parser.parse_args() args = parser.parse_args()