diff --git a/LICENSE b/LICENSE index 0d277e7..68a49da 100644 --- a/LICENSE +++ b/LICENSE @@ -1,15 +1,24 @@ +This is free and unencumbered software released into the public domain. - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2004 Sam Hocevar - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/NEWS.rst b/NEWS.rst index c9fbc35..afa44ae 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -2,6 +2,19 @@ News ==== +0.3.8 +===== + +Changed +------- + +- Add alternative documentation parsing backend: ugrep +- Small improvements to docs and error messages +- Updated Alpine demo container base image +- Use localtraps option everywhere we TRAP +- Document zsh-autoenv and direnv zpy-friendly configuration +- Increase uniformity of behavior between uv pip sync and pip-sync + 0.3.7 ===== diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 4ee245c..9fcae72 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -1,6 +1,6 @@ # yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json -site_name: 'zpy: Manage Python Environments in Zsh' +site_name: "zpy: Manage Python Environments in Zsh" site_url: https://andydecleyre.github.io/zpy/ repo_url: https://github.com/andydecleyre/zpy docs_dir: ../src @@ -40,20 +40,21 @@ theme: nav: - index.md - Guide: - - start.md - - new_proj.md - - new_proj/activate.md - - new_proj/pipacs.md - - new_proj/pipac.md - - new_proj/pips_envin.md - - new_proj/upgrade.md - - new_proj/pyproject.md - - new_proj/run.md - - existing_projects.md - - pipz.md - - python_versions.md + - start.md + - new_proj.md + - new_proj/activate.md + - new_proj/pipacs.md + - new_proj/pipac.md + - new_proj/pips_envin.md + - new_proj/upgrade.md + - new_proj/pyproject.md + - new_proj/run.md + - existing_projects.md + - pipz.md + - python_versions.md - Details: - - help_all.md - - deps.md - - install.md - - completions.md + - help_all.md + - deps.md + - install.md + - completions.md + - direnv.md diff --git a/doc/mkdocs/requirements.txt b/doc/mkdocs/requirements.txt index 75a4042..74aba29 100644 --- a/doc/mkdocs/requirements.txt +++ b/doc/mkdocs/requirements.txt @@ -1,29 +1,29 @@ -babel==2.15.0 # via mkdocs-material -certifi==2024.2.2 # via requests +babel==2.16.0 # via mkdocs-material +certifi==2024.8.30 # via requests charset-normalizer==3.3.2 # via requests click==8.1.7 # via mkdocs colorama==0.4.6 # via mkdocs-material ghp-import==2.1.0 # via mkdocs -idna==3.7 # via requests +idna==3.10 # via requests jinja2==3.1.4 # via mkdocs, mkdocs-material -markdown==3.6 # via mkdocs, mkdocs-material, pymdown-extensions +markdown==3.7 # via mkdocs, mkdocs-material, pymdown-extensions markupsafe==2.1.5 # via jinja2, mkdocs mergedeep==1.3.4 # via mkdocs, mkdocs-get-deps -mkdocs==1.6.0 # via -r requirements.in, mkdocs-material +mkdocs==1.6.1 # via -r requirements.in, mkdocs-material mkdocs-get-deps==0.2.0 # via mkdocs -mkdocs-material==9.5.21 # via -r requirements.in +mkdocs-material==9.5.34 # via -r requirements.in mkdocs-material-extensions==1.3.1 # via mkdocs-material -packaging==24.0 # via mkdocs -paginate==0.5.6 # via mkdocs-material +packaging==24.1 # via mkdocs +paginate==0.5.7 # via mkdocs-material pathspec==0.12.1 # via mkdocs -platformdirs==4.2.1 # via mkdocs-get-deps +platformdirs==4.3.3 # via mkdocs-get-deps pygments==2.18.0 # via mkdocs-material -pymdown-extensions==10.8.1 # via mkdocs-material +pymdown-extensions==10.9 # via mkdocs-material python-dateutil==2.9.0.post0 # via ghp-import -pyyaml==6.0.1 # via mkdocs, mkdocs-get-deps, pymdown-extensions, pyyaml-env-tag +pyyaml==6.0.2 # via mkdocs, mkdocs-get-deps, pymdown-extensions, pyyaml-env-tag pyyaml-env-tag==0.1 # via mkdocs -regex==2024.4.28 # via mkdocs-material -requests==2.31.0 # via mkdocs-material +regex==2024.9.11 # via mkdocs-material +requests==2.32.3 # via mkdocs-material six==1.16.0 # via python-dateutil -urllib3==2.2.1 # via requests -watchdog==4.0.0 # via mkdocs +urllib3==2.2.3 # via requests +watchdog==5.0.2 # via mkdocs diff --git a/doc/src/deps.md b/doc/src/deps.md index d73bc8a..5bcf525 100644 --- a/doc/src/deps.md +++ b/doc/src/deps.md @@ -98,5 +98,6 @@ with optional additions for more colorful output, alternative json parsers, and - a pcre tool -- provided by pcregrep/pcre-tools, pcre2grep/pcre2-tools, - [ripgrep](https://repology.org/project/ripgrep/versions), + [ripgrep (>=14.0.0)](https://repology.org/project/ripgrep/versions), + [ugrep](https://repology.org/project/ugrep/versions), or Zsh with pcre enabled diff --git a/doc/src/direnv.md b/doc/src/direnv.md new file mode 100644 index 0000000..209a255 --- /dev/null +++ b/doc/src/direnv.md @@ -0,0 +1,64 @@ +# Automatic Activation + +If you'd like your shell to automatically activate and deactivate venvs +when you switch directories, there are a few tools to make that happen. + +Here's how to configure them to do so in a zpy-friendly way. +If you notice room for improvement, or your favorite tool is missing, +please open an issue or discussion on GitHub. + +=== "zsh-autoenv" + + [zsh-autoenv](https://github.com/Tarrasch/zsh-autoenv) + runs any Zsh code you want in your current shell, + making it the simplest tool to configure for this job. + + In any project folder, create the following two files: + + `.autoenv.zsh`: + + ```zsh + a8 ${0:h} + ``` + + `.autoenv_leave.zsh`: + + ```zsh + if [[ $VIRTUAL_ENV ]] envout + ``` + + The zero in `${0:h}` is the path of the `.autoenv.zsh` file, + and the `:h` expansion gets that path's parent. + This ensures the proper project folder is used, + even if you're activating the script by entering a deeper subdirectory. + +=== "direnv" + + [direnv](https://github.com/direnv/direnv/) + runs Bash (not Zsh) and exports variables. + We'll create a self-contained script for each of `a8` and `venvs_path`, + so that we can easily call them from Bash. + + Assuming `~/.local/bin` is in your `PATH`, run + + ```console + $ zpy mkbin a8 ~/.local/bin/ + $ zpy mkbin venvs_path ~/.local/bin/ + ``` + + Now define a Bash function within the file `~/.config/direnv/direnvrc`: + + ```bash + layout_zpy () { + a8 + export VIRTUAL_ENV="$(venvs_path)/venv" + PATH_add "$VIRTUAL_ENV/bin" + export VENV_ACTIVE=1 + } + ``` + + In any project folder, create `.envrc`: + + ```bash + layout zpy + ``` diff --git a/doc/src/new_proj/pipacs.md b/doc/src/new_proj/pipacs.md index a9ac175..38865c8 100644 --- a/doc/src/new_proj/pipacs.md +++ b/doc/src/new_proj/pipacs.md @@ -17,7 +17,7 @@ we'll use `pipacs` to: [venv] % pipacs beautifulsoup4 ``` -![Animated demo: pipacs](https://gist.githubusercontent.com/AndydeCleyre/b422097e220806b31c4d1c80ed0ed6b5/raw/ee65dd02265b3e5e7b85996bc6dfd22175a3b78c/guide_pipacs.svg?sanitize=true) +![Animated demo: pipacs](https://github.com/AndydeCleyre/zpy/blob/assets/guide_pipacs.gif?raw=true) Being such a popular package, we were able to tab-complete the name `beautifulsoup4`, diff --git a/doc/src/new_proj/pyproject.md b/doc/src/new_proj/pyproject.md index ad1ec46..2807d3d 100644 --- a/doc/src/new_proj/pyproject.md +++ b/doc/src/new_proj/pyproject.md @@ -34,7 +34,7 @@ we can populate it with entries from our `requirements.in` files: [venv] % pypc ``` -![Animated demo: pypc](https://gist.github.com/AndydeCleyre/c8cad3380bd475706815969b07733a55/raw/5a4b327bf699819c96c8126de4ef60546c5cccea/pypc_demo.svg?sanitize=true) +![Animated demo: pypc](https://github.com/AndydeCleyre/zpy/blob/assets/pypc.gif?raw=true) The dependencies have been injected according to [PEP 621](https://www.python.org/dev/peps/pep-0621/), diff --git a/doc/src/new_proj/run.md b/doc/src/new_proj/run.md index 701f233..618cd17 100644 --- a/doc/src/new_proj/run.md +++ b/doc/src/new_proj/run.md @@ -15,7 +15,7 @@ We can ensure we've got a proper environment and run it: [venv] % ./do_thing.py ``` -![Animated demo: envin, run script](https://gist.github.com/AndydeCleyre/27bfa0e10a1b42191f777530a500263e/raw/0554370718428b26755902f5c6b12375cabc2d59/envin_do_thing.svg?sanitize=true) +![Animated demo: envin, run script](https://github.com/AndydeCleyre/zpy/blob/assets/envin_do_thing.gif?raw=true) But what if we don't want to manually activate its environment? diff --git a/doc/src/pipz.md b/doc/src/pipz.md index f370c60..b7dd3b9 100644 --- a/doc/src/pipz.md +++ b/doc/src/pipz.md @@ -34,11 +34,7 @@ The paths printed on the first three lines of output may be overridden with the `ZPY_PIPZ_BINS`, respectively. -![Animated demo: pipz install, list](https://gist.github.com/AndydeCleyre/5ad45d78336fc2cc4625b0dc6b450849/raw/777e77607786beb65b2d6e00fb27c507c5e7abfa/pipz_install_list.svg?sanitize=true) - -!!! note - - The last frame is mangled in this animation, but not in real usage. +![Animated demo: pipz install, list](https://github.com/AndydeCleyre/zpy/blob/assets/pipz_install_list.gif?raw=true) Example installing an app package from git: diff --git a/doc/src/start.md b/doc/src/start.md index c5d52fd..19cff04 100644 --- a/doc/src/start.md +++ b/doc/src/start.md @@ -1,5 +1,11 @@ # Get started +!!! tip + + You can flip through these docs with `n` and `p`, or `.` and `,`. + +## Install locally + Aside from Zsh and Python, the only dependency you're likely to *need* is [`fzf`](https://github.com/junegunn/fzf). For more details and recommended package manager commands, see [Dependencies](deps.md). @@ -17,9 +23,6 @@ For now, let's just source it in the current session: % . ~/.zpy/zpy.plugin.zsh ``` -The user-facing functions are all available as subcommands to `zpy`. -Try typing `zpy`, then a space, then tab. - !!! tip Everything zpy does will generally be much faster if uv is installed. @@ -29,26 +32,52 @@ Try typing `zpy`, then a space, then tab. % pipz install uv ``` -!!! info +## Try it in a container, instead + +Using either podman or docker, launch a temporary container based on Ubuntu, Alpine, or Fedora: + +=== "Ubuntu" + + ```console + $ podman run --net=host -it --rm -e TERM=$TERM quay.io/andykluger/zpy-ubuntu:master + $ docker run --net=host -it --rm -e TERM=$TERM quay.io/andykluger/zpy-ubuntu:master + ``` + +=== "Alpine" + + ```console + $ podman run --net=host -it --rm -e TERM=$TERM quay.io/andykluger/zpy-alpine:master + $ docker run --net=host -it --rm -e TERM=$TERM quay.io/andykluger/zpy-alpine:master + ``` - By default, each function is *also* available directly, as a "top-level" command[^1]. - This can be prevented by explicitly specifying a list of functions to expose, - *before* sourcing the plugin. - This example will expose only the `pipz` and `zpy` functions - (the rest remaining available as subcommands): +=== "Fedora" ```console - % zstyle ':zpy:*' exposed-funcs pipz zpy + $ podman run --net=host -it --rm -e TERM=$TERM quay.io/andykluger/zpy-fedora:master + $ docker run --net=host -it --rm -e TERM=$TERM quay.io/andykluger/zpy-fedora:master ``` +## Functions or Subcommands + +The user-facing functions are all available as subcommands to `zpy`. +Try typing `zpy`, then a space, then tab. + +By default, each function is *also* available directly, as a "top-level" command[^1]. +This can be **prevented** by explicitly specifying a list of functions to expose, +*before* sourcing the plugin. +This example will expose only the `pipz` and `zpy` functions +(the rest remaining available as subcommands): + +```console +% zstyle ':zpy:*' exposed-funcs pipz zpy +``` + [^1]: Well, except for `zpy mkbin` and `zpy help`. +## Moving on + From here, you may want to: - continue to the [next page](new_proj.md), for an idea of how these tools can help manage a project - jump to the [full reference](help_all.md) - jump to [`pipz`](pipz.md), a [pipx](https://pypa.github.io/pipx/) alternative - -!!! tip - - You can flip through these docs with `n` and `p`, or `.` and `,`. diff --git a/mk/ctnr/zcomet.sh b/mk/ctnr/zcomet.sh index 8b2b956..ee4a187 100755 --- a/mk/ctnr/zcomet.sh +++ b/mk/ctnr/zcomet.sh @@ -32,7 +32,7 @@ case $distro in alias ctnr_mkuser="ctnr_run useradd -m -s /bin/zsh" ;; alpine) - basetag=${2:-3.19} + basetag=${2:-3.20} pkgs="$pkgs git sudo" fat="$fat /var/cache/apk/*" alias ctnr_pkg="ctnr_run apk -q --no-progress" diff --git a/zpy.plugin.zsh b/zpy.plugin.zsh index ac3d753..6cc9369 100644 --- a/zpy.plugin.zsh +++ b/zpy.plugin.zsh @@ -1,6 +1,6 @@ autoload -Uz zargs zmodload -mF zsh/files 'b:zf_(chmod|ln|mkdir|rm)' -zmodload zsh/pcre 2>/dev/null +zmodload zsh/pcre 2>/dev/null || true zmodload zsh/mapfile ZPY_SRC=${0:P} @@ -129,7 +129,8 @@ ZPY_PROCS=${${$(nproc 2>/dev/null):-$(sysctl -n hw.logicalcpu 2>/dev/null)}:-4} } } -## fallback basic pcregrep-like func for our needs; not preferred to pcre2grep or ripgrep +## fallback basic pcregrep-like func for our needs; not preferred to pcre2grep or ugrep, +## but maybe preferred ripgrep, which had a relevant bug before its 14.0.0 release .zpy_zpcregrep () { # emulate -L zsh # like: '$1$4$5$7' @@ -176,7 +177,10 @@ ZPY_PROCS=${${$(nproc 2>/dev/null):-$(sysctl -n hw.logicalcpu 2>/dev/null)}:-4} local cmd_doc=() subcmd_doc=() rehash - if (( $+commands[pcre2grep] )) { + if (( $+commands[ugrep] )) { + cmd_doc=(ugrep -P --format='%[predoc]#%[fname]#%[usage]#%~') + subcmd_doc=(ugrep -P --format='%[usage]#%[postdoc]#%~') + } elif (( $+commands[pcre2grep] )) { cmd_doc=(pcre2grep -M -O '$1$4$6') subcmd_doc=(pcre2grep -M -O '$1$2') } elif { zmodload -e zsh/pcre } { @@ -189,14 +193,14 @@ ZPY_PROCS=${${$(nproc 2>/dev/null):-$(sysctl -n hw.logicalcpu 2>/dev/null)}:-4} cmd_doc=(rg --no-config --color never -NU -r '$predoc$fname$usage') subcmd_doc=(rg --no-config --color never -NU -r '$usage$postdoc') } else { - local lines=( - 'zpy documentation functions require one of' - '- zsh built with --enable-pcre' - '- rg (ripgrep)' - '- pcre2grep (pcre2/pcre2-tools)' + .zpy_log error \ + 'zpy documentation functions require a search tool' 'Any one of:' \ + '- zsh built with --enable-pcre' \ + '- rg (ripgrep >=14.0.0)' \ + '- ugrep' \ + '- pcre2grep (pcre2/pcre2-tools)' \ '- pcregrep (pcre/pcre-tools)' - ) - .zpy_log error $lines + .zpy_log tip Suggestion "Install ugrep with your distro's package manager." return 1 } @@ -428,7 +432,7 @@ ZPY_PROCS=${${$(nproc 2>/dev/null):-$(sysctl -n hw.logicalcpu 2>/dev/null)}:-4} # --read-relative-to-input pip-sync -q --pip-args --disable-pip-version-check $reqstxts } else { - uv pip sync -p python $reqstxts + uv pip sync -p python --allow-empty-requirements $reqstxts } ret=$? @@ -912,7 +916,7 @@ ZPY_PROCS=${${$(nproc 2>/dev/null):-$(sysctl -n hw.logicalcpu 2>/dev/null)}:-4} # Pass -i to interactively choose the project. # Pass --py to use another interpreter and named venv. .zpy_ui_activate () { # [--py pypy|current] [-i|] - emulate -L zsh + emulate -L zsh -o localtraps if [[ $1 == --help ]] { .zpy_ui_help ${0[9,-1]}; return } local envin_args=() venv_name=venv interactive @@ -1967,7 +1971,7 @@ jsonfile.write_text(dumps(data, indent=4)) # Package manager for venv-isolated scripts (pipx clone). .zpy_ui_pipz () { # [install|uninstall|upgrade|list|inject|reinstall|cd|runpip|runpkg] [...] - emulate -L zsh +o promptsubst -o globdots + emulate -L zsh +o promptsubst -o globdots -o localtraps [[ $ZPY_PIPZ_PROJECTS && $ZPY_PIPZ_BINS && $ZPY_VENVS_HOME && $ZPY_PROCS ]] || return local reply REPLY