Gerenciamento de Ambientes Python com pyenv

Rodrigo Vieira
OperacionalTI
Published in
6 min readMar 17, 2019
Photo by Hitesh Choudhary on Unsplash

Eu preciso começar este artigo justificando porque não utilizar o Python do próprio sistema operacional e mencionar as alternativas que estão ao nosso alcance.

Ao gerenciar as dependências do seu projeto diretamente através do Python do S.O. você corre o risco de bagunçar as dependências de ambos, do seu projeto e também do sistema operacional. No Linux e no MacOS são diversos os utilitários do sistema que dependem do Python pré-instalado. Se você pisar na bola aqui, o seu gerenciador de pacotes yum ou apt, pode deixar de funcionar.

A solução mais simples e mais utilizada é o uso do pippara isolar as dependências do seu projeto, não interferindo nos pacotes Python do seu sistema operacional. Mas ainda assim, você fica refém da versão do interpretador Python disponível no seu sistema. Se você possui um projeto e quer testá-lo em diferentes versões do Python, 2.7, 3.6, 3.8, etc, este não será um cenário favorável. Até mesmo o recente pipenve seus concorrentes ainda nos deixam dependentes da versão do Python disponibilizada pelo S.O.

Senhoras e senhores, com vocês “pyenv”

O pyenv nos liberta da dependência do Python disponibilizado pelo sistema, permitindo a instalação de diferentes versões do Python simultaneamente no mesmo S.O. Além disso você ainda pode continuar utilizando ambientes virtuais para isolar as dependências dos seus projetos, ativando e desativando automaticamente esses ambientes ao entrar ou sair de suas pastas.

Para times que trabalham com containers, o pyenv torna possível que a aplicação seja desenvolvida exatamente na mesma versão do Python que será utilizada no Build do container e consequentemente em produção, evitando surpresas amargas quando um dev usa Python 3.7.2 na máquina de desenvolvimento, mas o seu pipeline faz o build com Python 3.6.8 por exemplo.

Instalando o pyenv

Requisitos para instalar o pyenv no MacOS Mojave (10.14):

brew install pkg-config readline xz zlip

É necessário possuir o homebrew instalado.

Se você utiliza Linux, veja os links de referência ao final do artigo para encontrar as dependências do Pyenv para Linux. Há também uma versão para Windows.

Instalando o pyenv via pyenv-installer

O script a seguir está disponível no repositório do projeto:

$ curl https://pyenv.run | bash

Ao final da execução do script acima você terá disponível em seu sistema:

  1. pyenv: o próprio.
  2. pyenv-virtualenv: plugin para gerenciar ambientes virtuais.
  3. pyenv-update: plugin para atualizar o pyenv.
  4. pyenv-doctor: plugin para verificar a instalação do pyenv e suas dependências.
  5. pyenv-which-ext: plugin para procurar por comandos do sistema através do pyenv.

Após a instalação, adicione as seguintes linhas ao seu arquivo ~/.bash_profile:

export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

Instalando o Python através do pyenv

Para evitar problemas de dependências de bibliotecas no MacOS Mojave eu tive que adicionar as seguintes linhas no meu arquivo ~/.bash_profile:

# For compilers to find zlib you may need to set:
export LDFLAGS="${LDFLAGS} -L/usr/local/opt/zlib/lib"
export CPPFLAGS="${CPPFLAGS} -I/usr/local/opt/zlib/include"
# For pkg-config to find zlib you may need to set:
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH} /usr/local/opt/zlib/lib/pkgconfig"
alias pyenv='CFLAGS="-I$(xcrun --show-sdk-path)/usr/include" pyenv '

Lista todas as versões Python disponíveis através do pyenv:

pyenv install --list

Feito isso, podemos instalar as versões desejadas do Python. No meu caso as últimas versões do Python 2 e do Python 3 disponíveis na data em que estou escrevendo este artigo:

# Faz o download, compila e instala o Python 2.7.16
pyenv install -v 2.7.16
# Faz o download, compila e instala o Python 3.7.2
pyenv install -v 3.7.2

Cada comando acima levará alguns minutos para compilar o Python na sua máquina, fique atento por mensagens de alerta ou de erro. No meu caso o Python não era compilado sem exportar as variáveis $LDFLAGS, $CPPFLAGS e $PKG_CONFIG_PATH. Já a extensão do sqlite3 não era disponibilizada sem prefixar o comando pyenv com a definição da variável $CFLAGS.

Lista as versões Python instaladas no seu sistema:

pyenv versions

Meu ambiente de desenvolvimento Python

Agora que já temos o pyenv e as versões mais recentes da série 2 e 3 instaladas em nosso sistema, vamos configurar nosso ambiente para:

  1. Deixar o Python do sistema como padrão para não prejudicar as ferramentas do sistema.
  2. Criar um ambiente chamado “tools3” com ferramentas de linha de comando de uso frequente e não ficar instalando em todos os ambientes virtuais que criar.
  3. Criar um ambiente chamado jupyter3 para instalar o jupyter notebook e seu kernel.
  4. Criar o ambiente “tools2” para ferramentas que só funcionam no Python2.
  5. Configurar o pyenv para buscar um comando na seguinte ordem de ambientes: system, tools3, jupyter3 e por último tools2

Criando os ambientes e instalando suas dependências

# Cria os virtualenvs
pyenv virtualenv 3.7.2 tools3
pyenv virtualenv 3.7.2 jupyter3
# Ativa o ambiente tools3 e instala dependências
pyenv activate tools3
pip install --upgrade pip awscli ansible bpython
pip deactivate
# Ativa o ambiente jupyter3 e instala suas dependências
pip activate jupyter3
pip install --upgrade pip jupyter
python -m ipykernel install --user
pyenv deactivate
# Define a ordem de utilização dos ambientes virtuais
pyenv global system tools3 jupyter3 tools2

Para verificar se o pyenv está buscando os binários na ordem dos ambientes virtuais definida acima podemos usar a extensão whichinstalada junto com o pyenv:

# Especificamente no meu ambiente os paths são os seguintes:rodrigo@MacDoJimmy:~$ pyenv which python2
/Users/rodrigo/.pyenv/versions/tools2/bin/python2
rodrigo@MacDoJimmy:~$ pyenv which python3
/Users/rodrigo/.pyenv/versions/tools3/bin/python3
rodrigo@MacDoJimmy:~$ pyenv which aws
/Users/rodrigo/.pyenv/versions/tools3/bin/aws
rodrigo@MacDoJimmy:~$ pyenv which bpython
/Users/rodrigo/.pyenv/versions/tools3/bin/bpython
rodrigo@MacDoJimmy:~$ pyenv which ipython
/Users/rodrigo/.pyenv/versions/jupyter3/bin/ipython

Dessa forma eu não preciso ficar trocando de ambientes sempre que quiser executar os comandos “aws”, “bpython”, “ipython”, “python2” ou “python3”. Basta executar o comando e o pyenv, através do esquema de shims, sabe exatamente onde buscá-lo.

Atualizar ambientes

Além disso, se eu quiser adicionar, remover ou atualizar alguma dependência em algum dos ambientes virtuais criados acima, basta ativar o ambiente, utilizar o pip e desativar o ambiente:

# Atualiza awscli, ansible e bpython do virtualenv tools3
pyenv activate tools3
pip install --update awscli ansible bpython
pyenv deactivate

Criando virtualenvs por projetos

Por último, mas não menos importante, todos estamos acostumados aos arquivos requirements.txt, que definem as dependências por projeto. Com o pyenv esse processo continua extremamente simples:

# Cria o virtualenv projetoX
pyenv virtualenv 3.7.2 projetoX
# cria a pasta do projeto
mkdir ~/code/projetoX
# entra na pasta do projeto
cd -
# Cria o arquivo .python-version,
# especificando projetoX como ambiente do projeto.
# Devido aquelas duas linhas "eval ..."
# adicionadas ao seu .bash_profile
# o pyenv é capaz de ativar/desativar automaticamente
# o ambiente virtual ao entrar/sair das pastas dos projetos ;-)
pyenv local projetoX
pip install Django
pip freeze > requirements.txt

Perceba que fora da pasta do projeto, sua versão do Python obedece a ordem definida via “pyenv global”:

# Python do sistema é o padrão
cd ~
python -V
Python 2.7.10

Porém ao entrar na pasta de um projeto onde exista o arquivo .python-version:

# Dentro do virtualenv, temos o Python que escolhemos ao criá-lo
cd ~/code/projetoX
python -V
Python 3.7.2

Conclusão

  • Desenvolvemos a funcionalidade de possuir múltiplas versões do Python instaladas no sistema operacional sem interferir na versão padrão do fabricante e nas suas dependências.
  • Criamos múltiplos virtualenvs e definimos a ordem para que o sistema busque nossos utilitários Python nesses ambientes virtuais.
  • Por fim, criamos ambientes isolados por projeto, com ativação automática sempre que acessarmos a pasta do projeto.

Para alguns pode parecer trabalhoso, no entanto o workflow diário se resume a criar ambientes virtuais por projeto e utilizá-los normalmente com o pip:

pyenv virtualenv 3.7.2 projetoA
mkdir projetoA && cd -
pyenv local projetoA

Referencias:

--

--