Set up Python develop environments
- use `pyenv` to manage the various develop environments + install several Python binaries + each environment receives its own `poetry` install - add two virtual environments: + "interactive" => default environment with no library, which receives accidental `pip install`s + "utils" => hosts various globally available tools/apps (e.g., `mackup`) - add installation and update scripts
This commit is contained in:
parent
5c3a914658
commit
dcdb32585a
8 changed files with 240 additions and 2 deletions
26
.bashrc
26
.bashrc
|
@ -156,6 +156,13 @@ if ! shopt -oq posix; then
|
|||
fi
|
||||
|
||||
|
||||
# Enable completions for various tools
|
||||
command_exists invoke && eval "$(invoke --print-completion-script=bash)"
|
||||
command_exists nox && eval "$(register-python-argcomplete nox)"
|
||||
command_exists pip && eval "$(pip completion --bash)"
|
||||
command_exists pipx && eval "$(register-python-argcomplete pipx)"
|
||||
command_exists poetry && eval "$(poetry completions bash)"
|
||||
|
||||
|
||||
# ============
|
||||
# Key Bindings
|
||||
|
@ -234,5 +241,22 @@ _prompt_jobs() {
|
|||
(( $(jobs -rp | wc -l) )) && echo -e "\033[0;32m %\033[0m"
|
||||
}
|
||||
|
||||
PS1='${chroot:+($_debian_chroot)}\w$(_prompt_git)$(_prompt_jobs) > '
|
||||
# Mimic zsh's pyenv/venv integration
|
||||
_prompt_pyenv() {
|
||||
if [ -n "$VIRTUAL_ENV" ]; then
|
||||
echo -e "\033[0;36m py $(python -c "import os, sys; (hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)) and print(os.path.basename(sys.prefix))")\033[0m"
|
||||
elif [ -n "$PYENV_VERSION" ]; then
|
||||
if [ "$PYENV_VERSION" != "system" ]; then
|
||||
echo -e "\033[0;36m py $PYENV_VERSION\033[0m"
|
||||
fi
|
||||
elif [ -f "$(pwd)/.python-version" ]; then
|
||||
echo -e "\033[0;36m py $(cat .python-version | sed ':a;N;$!ba;s/\n/:/g')\033[0m"
|
||||
fi
|
||||
}
|
||||
|
||||
# Disable the default prompts set by pyenv and venv
|
||||
export PYENV_VIRTUALENV_DISABLE_PROMPT=1
|
||||
export VIRTUAL_ENV_DISABLE_PROMPT=1
|
||||
|
||||
PS1='${chroot:+($_debian_chroot)}\w$(_prompt_git)$(_prompt_jobs)$(_prompt_pyenv) > '
|
||||
PS2='... '
|
||||
|
|
3
.config/pypoetry/config.toml
Normal file
3
.config/pypoetry/config.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[virtualenvs]
|
||||
create = true
|
||||
in-project = true
|
|
@ -80,6 +80,23 @@ alias more='less'
|
|||
alias tree='tree -C --dirsfirst'
|
||||
|
||||
|
||||
# Make working with Python more convenient
|
||||
|
||||
alias py='python'
|
||||
alias ipy='ipython'
|
||||
|
||||
if command_exists poetry; then
|
||||
alias pr='poetry run'
|
||||
fi
|
||||
|
||||
if command_exists pyenv; then
|
||||
alias pyvenvs='pyenv virtualenvs --bare --skip-aliases'
|
||||
alias pyver='pyenv version'
|
||||
alias pyvers='pyenv versions --skip-aliases'
|
||||
alias pywhich='pyenv which'
|
||||
fi
|
||||
|
||||
|
||||
# Aliases for various utilities
|
||||
alias datetime='date +"%Y-%m-%d %H:%M:%S %z (%Z)"'
|
||||
alias datetime-iso='date --iso-8601=seconds'
|
||||
|
|
|
@ -20,7 +20,7 @@ git clone --bare git@git.webartifex.biz:alexander/dotfiles.git "$HOME/.dotfiles"
|
|||
|
||||
# Backup old dotfiles
|
||||
rm -rf "$HOME/.dotfiles.bak" >/dev/null
|
||||
mkdir -p $HOME/.dotfiles.bak/.config/{bat,flameshot,git,Nextcloud,pop-system-updater,psql,shell} && \
|
||||
mkdir -p $HOME/.dotfiles.bak/.config/{bat,flameshot,git,Nextcloud,pop-system-updater,psql,pypoetry,shell} && \
|
||||
mkdir -p $HOME/.dotfiles.bak/.vim/{after/ftplugin,backup,swap,undo} && \
|
||||
/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | \
|
||||
xargs -I{} mv {} "$HOME/.dotfiles.bak"/{}
|
||||
|
|
|
@ -12,6 +12,16 @@ in_zsh() {
|
|||
[ -n "$ZSH_VERSION" ]
|
||||
}
|
||||
|
||||
# Prepend a folder to $PATH if it is not already there
|
||||
_prepend_to_path () {
|
||||
if [ -d "$1" ] ; then
|
||||
case :$PATH: in
|
||||
*:$1:*) ;;
|
||||
*) PATH=$1:$PATH ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
# =========================
|
||||
|
@ -30,6 +40,13 @@ fi
|
|||
command_exists lesspipe && eval "$(SHELL=/bin/sh lesspipe)"
|
||||
|
||||
|
||||
# Initialize pyenv if it is installed
|
||||
if command_exists pyenv; then
|
||||
eval "$(pyenv init -)"
|
||||
eval "$(pyenv virtualenv-init -)"
|
||||
fi
|
||||
|
||||
|
||||
# Configure the keyboard:
|
||||
# - make right alt and menu keys the compose key, e.g., for umlauts
|
||||
# - make caps lock a ctrl modifier and Esc key
|
||||
|
@ -38,6 +55,32 @@ command_exists xcape && xcape -e "Caps_Lock=Escape"
|
|||
|
||||
|
||||
|
||||
# ==========================
|
||||
# Command not found handlers
|
||||
# ==========================
|
||||
|
||||
|
||||
# Check if an unknown command is in a local Python venv
|
||||
command_not_found_handle() {
|
||||
if [ -x ".venv/bin/$1" ]; then
|
||||
echo 'You forgot to activate the virtualenv' 1>&2
|
||||
exe=".venv/bin/$1"
|
||||
shift
|
||||
"$exe" "$@"
|
||||
return $?
|
||||
else
|
||||
echo "$1: command not found" 1>&2
|
||||
return 127
|
||||
fi
|
||||
}
|
||||
|
||||
# zsh uses another name for the handler
|
||||
command_not_found_handler() {
|
||||
command_not_found_handle "$@"
|
||||
}
|
||||
|
||||
|
||||
|
||||
# ==============================
|
||||
# Working with files and folders
|
||||
# ==============================
|
||||
|
@ -194,6 +237,89 @@ genemail() {
|
|||
|
||||
|
||||
|
||||
# ===================================================
|
||||
# Set up & maintain the Python (develop) environments
|
||||
# ===================================================
|
||||
|
||||
|
||||
# TODO: This needs to be updated regularly (or find an automated solution)
|
||||
# The Python versions `pyenv` creates (in descending order)
|
||||
_py3_versions=('3.10.5' '3.9.13' '3.8.13' '3.7.13')
|
||||
_py2_version='2.7.18'
|
||||
|
||||
# Each Python environment uses its own `poetry` installation to avoid
|
||||
# integration problems between `pyenv` and `poetry`
|
||||
# Source: https://github.com/python-poetry/poetry/issues/5252#issuecomment-1055697424
|
||||
_py3_site_packages=('poetry')
|
||||
|
||||
# The pyenv virtualenv "utils" contains some globally available tools (e.g., `mackup`)
|
||||
_py3_utils=('mackup')
|
||||
|
||||
install-pyenv() {
|
||||
echo -e "\nInstalling pyenv\n"
|
||||
|
||||
# The official installer does a bit more than the `git clone`s below
|
||||
# `curl https://pyenv.run | bash`
|
||||
git clone https://github.com/pyenv/pyenv.git "$HOME/.pyenv"
|
||||
git clone https://github.com/pyenv/pyenv-doctor.git "$HOME/.pyenv/plugins/pyenv-doctor"
|
||||
git clone https://github.com/pyenv/pyenv-update.git "$HOME/.pyenv/plugins/pyenv-update"
|
||||
git clone https://github.com/pyenv/pyenv-virtualenv.git "$HOME/.pyenv/plugins/pyenv-virtualenv"
|
||||
git clone https://github.com/pyenv/pyenv-which-ext.git "$HOME/.pyenv/plugins/pyenv-which-ext"
|
||||
|
||||
# On a first install, "$PYENV_ROOT/bin" is NOT on the $PATH
|
||||
_prepend_to_path "$PYENV_ROOT/bin"
|
||||
}
|
||||
|
||||
re-install-pyenv() {
|
||||
echo -e "\nRemoving pyenv\n"
|
||||
rm -rf "$HOME/.pyenv" >/dev/null
|
||||
install-pyenv
|
||||
}
|
||||
|
||||
create-or-update-python-envs() {
|
||||
command_exists pyenv || install-pyenv
|
||||
|
||||
eval "$(pyenv init --path)"
|
||||
|
||||
# Keep a legacy Python 2.7, just in case
|
||||
echo -e "\nInstalling/updating Python $_py2_version\n"
|
||||
pyenv install --skip-existing $_py2_version
|
||||
PYENV_VERSION=$_py2_version pip install --upgrade pip setuptools
|
||||
PYENV_VERSION=$_py2_version python -c "import sys; print sys.version"
|
||||
|
||||
for version in ${_py3_versions[@]}; do
|
||||
echo -e "\nInstalling/updating Python $version\n"
|
||||
pyenv install --skip-existing $version
|
||||
|
||||
# Start the new environment with the latest `pip` and `setuptools` versions
|
||||
PYENV_VERSION=$version pip install --upgrade pip setuptools
|
||||
|
||||
# Put the specified utilities in the fresh environments (or update them)
|
||||
for lib in ${_py3_site_packages[@]}; do
|
||||
PYENV_VERSION=$version pip install --upgrade $lib
|
||||
done
|
||||
done
|
||||
|
||||
# Create a virtualenv based off the latest Python version to host global utilities
|
||||
echo -e "\nInstalling/updating global Python utilities\n"
|
||||
pyenv virtualenv $_py3_versions[1] 'utils'
|
||||
PYENV_VERSION='utils' pip install --upgrade pip setuptools
|
||||
for util in ${_py3_utils[@]}; do
|
||||
PYENV_VERSION='utils' pip install --upgrade $util
|
||||
done
|
||||
|
||||
# Create a virtualenv based off the latest Python version for interactive usage
|
||||
# (This virtualenv is empty and is the target of accidental `pip install`s)
|
||||
echo -e "\nInstalling/updating the default/interactive Python environment\n"
|
||||
pyenv virtualenv $_py3_versions[1] 'interactive'
|
||||
PYENV_VERSION='interactive' pip install --upgrade pip setuptools
|
||||
|
||||
# Put all Python binaries and the utilities on the $PATH
|
||||
pyenv global 'interactive' $_py3_versions 'utils' $_py2_version
|
||||
}
|
||||
|
||||
|
||||
|
||||
# =============================
|
||||
# Automate the update machinery
|
||||
# =============================
|
||||
|
@ -278,6 +404,28 @@ update-zsh() {
|
|||
}
|
||||
|
||||
|
||||
# Update the entire Python tool chain
|
||||
update-python() {
|
||||
echo -e '\nUpdating the Python tool chain\n'
|
||||
|
||||
if command_exists pyenv; then
|
||||
echo -e '\nUpdating pyenv\n'
|
||||
pyenv update
|
||||
echo
|
||||
|
||||
echo -e '\nUpdating Python environments\n'
|
||||
create-or-update-python-envs
|
||||
echo
|
||||
fi
|
||||
|
||||
if command_exists zsh-pip-cache-packages; then
|
||||
echo -e '\nUpdating pip packages cache\n'
|
||||
zsh-pip-clear-cache
|
||||
zsh-pip-cache-packages
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Wrapper to run several update functions at once
|
||||
update-machine() {
|
||||
sudo --validate || return
|
||||
|
@ -295,9 +443,14 @@ update-machine() {
|
|||
remove-old-snaps
|
||||
fi
|
||||
|
||||
update-python
|
||||
|
||||
update-dotfiles
|
||||
update-zsh
|
||||
|
||||
echo -e '\nUpdating the configs managed by mackup'
|
||||
mackup restore --force
|
||||
|
||||
echo -e '\nUpdating password store\n'
|
||||
pass git pull
|
||||
echo
|
||||
|
|
5
.profile
5
.profile
|
@ -34,6 +34,11 @@ export BAT_CONFIG_PATH="$HOME/.config/bat/config"
|
|||
|
||||
export LESSHISTFILE="${XDG_CACHE_HOME:-$HOME/.cache}/.lesshst"
|
||||
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
_prepend_to_path "$PYENV_ROOT/bin"
|
||||
# No need for *.pyc files on a dev machine
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
export PSQLRC="$HOME/.psqlrc"
|
||||
|
||||
|
||||
|
|
18
.zshrc
18
.zshrc
|
@ -102,7 +102,10 @@ zplug "plugins/command-not-found", from:oh-my-zsh
|
|||
zplug "plugins/dotenv", from:oh-my-zsh
|
||||
zplug "plugins/dirhistory", from:oh-my-zsh
|
||||
zplug "plugins/git-escape-magic", from:oh-my-zsh
|
||||
zplug "plugins/invoke", from:oh-my-zsh # completions for `invoke`
|
||||
zplug "plugins/jsontools", from:oh-my-zsh
|
||||
zplug "plugins/pip", from:oh-my-zsh # completions for `pip`
|
||||
zplug "plugins/poetry", from:oh-my-zsh # completions for `poetry`
|
||||
zplug "plugins/z", from:oh-my-zsh
|
||||
|
||||
zplug "romkatv/powerlevel10k", as:theme, depth:1
|
||||
|
@ -141,6 +144,21 @@ zstyle ':completion:*:warnings' format 'No matches for: %d'
|
|||
zstyle ':completion:*' group-name ''
|
||||
|
||||
|
||||
# Enable completions for various tools
|
||||
|
||||
# invoke -> see plugins above
|
||||
# command_exists invoke && eval "$(invoke --print-completion-script=zsh)"
|
||||
|
||||
command_exists nox && eval "$(register-python-argcomplete nox)"
|
||||
|
||||
# pip -> see plugins above
|
||||
# command_exists pip && eval "$(pip completion --zsh)"
|
||||
|
||||
command_exists pipx && eval "$(register-python-argcomplete pipx)"
|
||||
|
||||
# poetry -> see plugins above
|
||||
|
||||
|
||||
|
||||
# ============
|
||||
# Key Bindings
|
||||
|
|
18
README.md
18
README.md
|
@ -23,3 +23,21 @@ Furthermore, `zsh` is set up with [`oh-my-zsh`](https://ohmyz.sh/) and `zplug`.
|
|||
Otherwise, `~/.profile` is probably *not* sourced.
|
||||
|
||||
Don't worry: Your current dotfiles are backed up in the `~/.dotfiles.bak` folder!
|
||||
|
||||
|
||||
### Python Development Environments
|
||||
|
||||
The develop environments for Python are managed by [`pyenv`](https://github.com/pyenv/pyenv).
|
||||
|
||||
To set them up, run:
|
||||
|
||||
```bash
|
||||
install-pyenv && create-or-update-python-envs
|
||||
```
|
||||
|
||||
Several Python binaries are installed.
|
||||
Additionally, two `virtualenv`s, "interactive" and "utils", are also created:
|
||||
- "interactive" is the default environment with *no* libraries installed, and
|
||||
- "utils" hosts globally available utilities.
|
||||
|
||||
Use `pyenv local ...` to specify a particular Python binary for a project.
|
||||
|
|
Loading…
Reference in a new issue