Configure Python develop tool chain
- use pyenv to manage the develop environments + install several Python versions (3.7 - 3.10 and 2.7) + each version receives its own copies of black, pipenv, and poetry - add two more virtual environments based off the latest version: + "interactive" => default environment optimized for interactive usage with with black, bpython, and ipython (also receives accidental `pip install`s) + "utils" => hosts various globally available tools/apps (e.g., mackup and youtube-dl) - add installation and update scripts for the entire tool chain - set up completions for bash and zsh - set up convenient aliases - configure bpython - configure poetry
This commit is contained in:
parent
1646011389
commit
0319e614b8
9 changed files with 233 additions and 1 deletions
31
.bashrc
31
.bashrc
|
@ -5,6 +5,12 @@
|
||||||
[[ $- != *i* ]] && return
|
[[ $- != *i* ]] && return
|
||||||
|
|
||||||
|
|
||||||
|
# Check if a command can be found on the $PATH
|
||||||
|
_command_exists() {
|
||||||
|
command -v "$1" 1>/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Enable XON/XOFF software flow control
|
# Enable XON/XOFF software flow control
|
||||||
stty -ixon
|
stty -ixon
|
||||||
|
@ -69,6 +75,13 @@ if ! shopt -oq posix; then
|
||||||
fi
|
fi
|
||||||
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)"
|
||||||
|
|
||||||
# Add tab completion for all aliases to commands with completion functions
|
# Add tab completion for all aliases to commands with completion functions
|
||||||
# (must come after bash completions have been set up)
|
# (must come after bash completions have been set up)
|
||||||
# Source: https://superuser.com/a/437508
|
# Source: https://superuser.com/a/437508
|
||||||
|
@ -193,5 +206,21 @@ _prompt_jobs() { # Indicate running background jobs with a"%"
|
||||||
(( $(jobs -rp | wc -l) )) && printf "\033[0;32m %\033[0m"
|
(( $(jobs -rp | wc -l) )) && printf "\033[0;32m %\033[0m"
|
||||||
}
|
}
|
||||||
|
|
||||||
PS1='${chroot:+($_debian_chroot)}\w$(_prompt_git)$(_prompt_jobs) > '
|
_prompt_pyenv() { # Mimic zsh's pyenv/venv integration
|
||||||
|
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='... '
|
||||||
|
|
7
.config/bpython/config
Normal file
7
.config/bpython/config
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[general]
|
||||||
|
|
||||||
|
# Make `bpython` and `python` share their history
|
||||||
|
hist_file = ~/.python_history
|
||||||
|
|
||||||
|
# No limit
|
||||||
|
hist_length = 0
|
|
@ -1,3 +1,6 @@
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
# Vim
|
# Vim
|
||||||
# Source: https://github.com/github/gitignore/blob/main/Global/Vim.gitignore
|
# Source: https://github.com/github/gitignore/blob/main/Global/Vim.gitignore
|
||||||
#
|
#
|
||||||
|
|
3
.config/pypoetry/config.toml
Normal file
3
.config/pypoetry/config.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[virtualenvs]
|
||||||
|
create = true
|
||||||
|
in-project = true
|
|
@ -86,6 +86,25 @@ alias more='less'
|
||||||
alias tree='tree -C --dirsfirst'
|
alias tree='tree -C --dirsfirst'
|
||||||
|
|
||||||
|
|
||||||
|
# Make working with Python more convenient
|
||||||
|
|
||||||
|
# Interactive shells
|
||||||
|
alias py='python'
|
||||||
|
alias bpy='bpython'
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
# Various one-line utilities
|
# Various one-line 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'
|
||||||
|
|
|
@ -9,6 +9,15 @@ _in_zsh() {
|
||||||
[ -n "$ZSH_VERSION" ]
|
[ -n "$ZSH_VERSION" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepend-to-path () { # if not already there
|
||||||
|
if [ -d "$1" ] ; then
|
||||||
|
case :$PATH: in
|
||||||
|
*:$1:*) ;;
|
||||||
|
*) PATH=$1:$PATH ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Configure the keyboard:
|
# Configure the keyboard:
|
||||||
|
@ -19,6 +28,16 @@ _command_exists xcape && xcape -e "Caps_Lock=Escape"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_init_pyenv () { # used further below as well
|
||||||
|
_command_exists pyenv || return
|
||||||
|
|
||||||
|
eval "$(pyenv init -)"
|
||||||
|
eval "$(pyenv virtualenv-init -)"
|
||||||
|
}
|
||||||
|
_init_pyenv
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# Working with files and folders
|
# Working with files and folders
|
||||||
# ==============================
|
# ==============================
|
||||||
|
@ -174,6 +193,94 @@ 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
|
||||||
|
# Important: The first version also holds the "interactive" and "utils" environments)
|
||||||
|
_py3_versions=('3.10.6' '3.9.13' '3.8.13' '3.7.13')
|
||||||
|
_py2_version='2.7.18'
|
||||||
|
|
||||||
|
# Each Python version receives its own copy of black, pipenv, and poetry
|
||||||
|
# (e.g., to avoid possible integration problems between pyenv and poetry
|
||||||
|
# Source: https://github.com/python-poetry/poetry/issues/5252#issuecomment-1055697424)
|
||||||
|
_py3_site_packages=('black' 'pipenv' 'poetry')
|
||||||
|
|
||||||
|
# The pyenv virtualenv "utils" contains some globally available tools (e.g., mackup)
|
||||||
|
_py3_utils=('mackup' 'youtube-dl')
|
||||||
|
|
||||||
|
# Important: this REMOVES the old ~/.pyenv installation
|
||||||
|
_install_pyenv() {
|
||||||
|
echo "(Re-)Installing pyenv"
|
||||||
|
|
||||||
|
# Ensure that pyenv is on the $PATH
|
||||||
|
# (otherwise, the pyenv installer emits warnings)
|
||||||
|
mkdir -p "$PYENV_ROOT/bin"
|
||||||
|
prepend-to-path "$PYENV_ROOT/bin"
|
||||||
|
|
||||||
|
# Remove old pyenv for clean install
|
||||||
|
rm -rf "$PYENV_ROOT" >/dev/null
|
||||||
|
|
||||||
|
# Run the official pyenv installer
|
||||||
|
curl https://pyenv.run | bash
|
||||||
|
|
||||||
|
# Make pyenv usable after this installation in the same shell session
|
||||||
|
_init_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 "Installing/updating Python $_py2_version"
|
||||||
|
pyenv install --skip-existing $_py2_version
|
||||||
|
pyenv rehash # needed on a first install
|
||||||
|
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 "Installing/updating Python $version"
|
||||||
|
pyenv install --skip-existing $version
|
||||||
|
pyenv rehash # needed on a first install
|
||||||
|
|
||||||
|
# Start the new environment with the latest pip and setuptools versions
|
||||||
|
PYENV_VERSION=$version pip install --upgrade pip setuptools
|
||||||
|
PYENV_VERSION=$version python -c "import sys; print(sys.version)"
|
||||||
|
|
||||||
|
# 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 "Installing/updating the global Python utilities"
|
||||||
|
pyenv virtualenv $_py3_versions[1] 'utils'
|
||||||
|
pyenv rehash # needed on a first install
|
||||||
|
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
|
||||||
|
echo "Installing/updating the default/interactive Python environment"
|
||||||
|
pyenv virtualenv $_py3_versions[1] 'interactive'
|
||||||
|
pyenv rehash # needed on a first install
|
||||||
|
PYENV_VERSION='interactive' pip install --upgrade pip setuptools
|
||||||
|
# Install some tools to make interactive usage nicer
|
||||||
|
PYENV_VERSION='interactive' pip install --upgrade black bpython ipython
|
||||||
|
|
||||||
|
# Put all Python binaries/virtualenvs and the utilities on the $PATH
|
||||||
|
pyenv global 'interactive' $_py3_versions 'utils' $_py2_version
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# =============================
|
# =============================
|
||||||
# Automate the update machinery
|
# Automate the update machinery
|
||||||
# =============================
|
# =============================
|
||||||
|
@ -280,6 +387,21 @@ _update_zplug() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_update_python() {
|
||||||
|
echo 'Updating the Python tool chain'
|
||||||
|
|
||||||
|
if _command_exists pyenv; then
|
||||||
|
pyenv update
|
||||||
|
create-or-update-python-envs
|
||||||
|
fi
|
||||||
|
|
||||||
|
if _command_exists zsh-pip-cache-packages; then
|
||||||
|
zsh-pip-clear-cache
|
||||||
|
zsh-pip-cache-packages
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
run-private-scripts() { # in the Nextcloud
|
run-private-scripts() { # in the Nextcloud
|
||||||
if [ -d "$HOME/data/getraenkemarkt/shell" ]; then
|
if [ -d "$HOME/data/getraenkemarkt/shell" ]; then
|
||||||
for file in $HOME/data/getraenkemarkt/shell/*.sh; do
|
for file in $HOME/data/getraenkemarkt/shell/*.sh; do
|
||||||
|
@ -298,6 +420,7 @@ update-machine() {
|
||||||
_command_exists snap && sudo snap refresh && _remove_old_snaps
|
_command_exists snap && sudo snap refresh && _remove_old_snaps
|
||||||
_update_repositories
|
_update_repositories
|
||||||
_update_zsh
|
_update_zsh
|
||||||
|
_update_python
|
||||||
|
|
||||||
sudo --reset-timestamp
|
sudo --reset-timestamp
|
||||||
}
|
}
|
||||||
|
|
5
.profile
5
.profile
|
@ -11,6 +11,10 @@ export REPOS="$HOME/repos"
|
||||||
|
|
||||||
export LESSHISTFILE="$HOME/.lesshst"
|
export LESSHISTFILE="$HOME/.lesshst"
|
||||||
|
|
||||||
|
export PYENV_ROOT="$HOME/.pyenv"
|
||||||
|
# No need for *.pyc files on a dev machine
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
export PSQLRC="$HOME/.psqlrc"
|
export PSQLRC="$HOME/.psqlrc"
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +30,7 @@ prepend-to-path () { # if not already there
|
||||||
|
|
||||||
prepend-to-path "$HOME/bin"
|
prepend-to-path "$HOME/bin"
|
||||||
prepend-to-path "$HOME/.local/bin"
|
prepend-to-path "$HOME/.local/bin"
|
||||||
|
prepend-to-path "$PYENV_ROOT/bin"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
24
.zshrc
24
.zshrc
|
@ -5,6 +5,12 @@
|
||||||
[[ $- != *i* ]] && return
|
[[ $- != *i* ]] && return
|
||||||
|
|
||||||
|
|
||||||
|
# Check if a command can be found on the $PATH
|
||||||
|
_command_exists() {
|
||||||
|
command -v "$1" 1>/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Enable Powerlevel10k instant prompt
|
# Enable Powerlevel10k instant prompt
|
||||||
if [ -r "${XDG_CACHE_HOME:-$HOME/.cache}/zsh/p10k-instant-prompt-${(%):-%n}.zsh" ]; then
|
if [ -r "${XDG_CACHE_HOME:-$HOME/.cache}/zsh/p10k-instant-prompt-${(%):-%n}.zsh" ]; then
|
||||||
|
@ -61,7 +67,10 @@ plugins=(
|
||||||
dirhistory
|
dirhistory
|
||||||
dotenv # config in ~/.zshenv; `_update_repositories` temporarily disables this
|
dotenv # config in ~/.zshenv; `_update_repositories` temporarily disables this
|
||||||
git-escape-magic
|
git-escape-magic
|
||||||
|
invoke # completions for invoke
|
||||||
jsontools
|
jsontools
|
||||||
|
pip # completions for pip
|
||||||
|
poetry # completions for poetry
|
||||||
z
|
z
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -124,6 +133,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; alternatively use
|
||||||
|
# _command_exists invoke && eval "$(invoke --print-completion-script=zsh)"
|
||||||
|
|
||||||
|
_command_exists nox && eval "$(register-python-argcomplete nox)"
|
||||||
|
|
||||||
|
# pip -> see plugins above; alternatively use
|
||||||
|
# _command_exists pip && eval "$(pip completion --zsh)"
|
||||||
|
|
||||||
|
_command_exists pipx && eval "$(register-python-argcomplete pipx)"
|
||||||
|
|
||||||
|
# poetry -> see plugins above; no alternative here
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Define key bindings
|
# Define key bindings
|
||||||
|
|
||||||
|
|
19
README.md
19
README.md
|
@ -24,3 +24,22 @@ Further, `zsh` is set up
|
||||||
Otherwise, `~/.profile` is probably *not* sourced.
|
Otherwise, `~/.profile` is probably *not* sourced.
|
||||||
|
|
||||||
**Important**: Don't forget to back up your current dotfiles!
|
**Important**: Don't forget to back up your current dotfiles!
|
||||||
|
|
||||||
|
|
||||||
|
### Python Development Environments
|
||||||
|
|
||||||
|
The develop environments for Python are managed via [`pyenv`](https://github.com/pyenv/pyenv).
|
||||||
|
|
||||||
|
To set them up, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
create-or-update-python-envs
|
||||||
|
```
|
||||||
|
|
||||||
|
Several Python versions are installed.
|
||||||
|
Additionally, two `virtualenv`s, called "interactive" and "utils", are also created:
|
||||||
|
- "interactive" is the default environment, and
|
||||||
|
- "utils" hosts globally available utilities
|
||||||
|
(e.g., [youtube-dl](https://github.com/ytdl-org/youtube-dl/)).
|
||||||
|
|
||||||
|
Use `pyenv local ...` to specify a particular Python binary for a project.
|
||||||
|
|
Loading…
Reference in a new issue