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
|
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
|
# Key Bindings
|
||||||
|
|
@ -234,5 +241,22 @@ _prompt_jobs() {
|
||||||
(( $(jobs -rp | wc -l) )) && echo -e "\033[0;32m %\033[0m"
|
(( $(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='... '
|
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'
|
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
|
# Aliases for various utilities
|
||||||
alias datetime='date +"%Y-%m-%d %H:%M:%S %z (%Z)"'
|
alias datetime='date +"%Y-%m-%d %H:%M:%S %z (%Z)"'
|
||||||
alias datetime-iso='date --iso-8601=seconds'
|
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
|
# Backup old dotfiles
|
||||||
rm -rf "$HOME/.dotfiles.bak" >/dev/null
|
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} && \
|
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'} | \
|
/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME checkout 2>&1 | egrep "\s+\." | awk {'print $1'} | \
|
||||||
xargs -I{} mv {} "$HOME/.dotfiles.bak"/{}
|
xargs -I{} mv {} "$HOME/.dotfiles.bak"/{}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,16 @@ in_zsh() {
|
||||||
[ -n "$ZSH_VERSION" ]
|
[ -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)"
|
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:
|
# Configure the keyboard:
|
||||||
# - make right alt and menu keys the compose key, e.g., for umlauts
|
# - make right alt and menu keys the compose key, e.g., for umlauts
|
||||||
# - make caps lock a ctrl modifier and Esc key
|
# - 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
|
# 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
|
# 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
|
# Wrapper to run several update functions at once
|
||||||
update-machine() {
|
update-machine() {
|
||||||
sudo --validate || return
|
sudo --validate || return
|
||||||
|
|
@ -295,9 +443,14 @@ update-machine() {
|
||||||
remove-old-snaps
|
remove-old-snaps
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
update-python
|
||||||
|
|
||||||
update-dotfiles
|
update-dotfiles
|
||||||
update-zsh
|
update-zsh
|
||||||
|
|
||||||
|
echo -e '\nUpdating the configs managed by mackup'
|
||||||
|
mackup restore --force
|
||||||
|
|
||||||
echo -e '\nUpdating password store\n'
|
echo -e '\nUpdating password store\n'
|
||||||
pass git pull
|
pass git pull
|
||||||
echo
|
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 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"
|
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/dotenv", from:oh-my-zsh
|
||||||
zplug "plugins/dirhistory", from:oh-my-zsh
|
zplug "plugins/dirhistory", from:oh-my-zsh
|
||||||
zplug "plugins/git-escape-magic", 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/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 "plugins/z", from:oh-my-zsh
|
||||||
|
|
||||||
zplug "romkatv/powerlevel10k", as:theme, depth:1
|
zplug "romkatv/powerlevel10k", as:theme, depth:1
|
||||||
|
|
@ -141,6 +144,21 @@ zstyle ':completion:*:warnings' format 'No matches for: %d'
|
||||||
zstyle ':completion:*' group-name ''
|
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
|
# 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.
|
Otherwise, `~/.profile` is probably *not* sourced.
|
||||||
|
|
||||||
Don't worry: Your current dotfiles are backed up in the `~/.dotfiles.bak` folder!
|
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