Como as bibliotecas do ambiente virtual funcionam

Você já pensou em como as bibliotecas de ambiente virtual funcionam no Python? Neste artigo, proponho me familiarizar com o conceito principal usado por todas as bibliotecas de ambientes, como virtualenv, virtualenvwrapper, conda, pipenv.

Inicialmente, no Python não havia capacidade interna de criar ambientes, e esse recurso foi implementado como um hack. Como se viu, todas as bibliotecas são baseadas em um recurso muito simples do interpretador python.

Quando o Python inicia o intérprete, ele começa a procurar o diretório com os módulos (site-packages). A pesquisa começa com o diretório pai em relação à localização física do executável do intérprete (python.exe). Se a pasta do módulo não for encontrada, o Python aumentará um nível e o fará até o diretório raiz ser atingido. Para entender que esse é um diretório com módulos, o Python procura o módulo os, que deve estar no arquivo os.py e é necessário para o python funcionar.

Vamos imaginar que nosso intérprete esteja localizado em /usr/dev/lang/bin/python . Em seguida, os caminhos de pesquisa terão a seguinte aparência:

 /usr/dev/lang/lib/python3.7/os.py /usr/dev/lib/python3.7/os.py /usr/lib/python3.7/os.py /lib/python3.7/os.py 

Como você pode ver, o Python adiciona um prefixo especial ( lib/python$VERSION/os.py ) ao nosso caminho. Assim que o intérprete encontra a primeira correspondência (a presença do arquivo os.py), ele altera sys.prefix e sys.exec_prefix para esse caminho (com o prefixo removido). Se, por algum motivo, nenhuma correspondência for encontrada, o caminho padrão será usado, compilado no intérprete.

Agora vamos ver como uma das bibliotecas mais antigas e famosas, virtualenv, faz isso.

 user@arb:/usr/home/test# virtualenv ENV Running virtualenv with interpreter /usr/bin/python3 New python executable in /usr/home/test/ENV/bin/python3 Also creating executable in /usr/home/test/ENV/bin/python Installing setuptools, pkg_resources, pip, wheel...done. 

Após a execução, ele cria diretórios adicionais:

 user@arb:/usr/home/test/ENV# tree -L 3 . ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── activate_this.py │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.7 │ ├── python │ ├── python-config │ ├── python3 -> python │ ├── python3.7 -> python │ └── wheel ├── include │ └── python3.7m -> /usr/include/python3.7m ├── lib │ └── python3.7 │ ├── __future__.py -> /usr/lib/python3.7/__future__.py │ ├── __pycache__ │ ├── _bootlocale.py -> /usr/lib/python3.7/_bootlocale.py │ ├── _collections_abc.py -> /usr/lib/python3.7/_collections_abc.py │ ├── _dummy_thread.py -> /usr/lib/python3.7/_dummy_thread.py │ ├── _weakrefset.py -> /usr/lib/python3.7/_weakrefset.py │ ├── abc.py -> /usr/lib/python3.7/abc.py │ ├── base64.py -> /usr/lib/python3.7/base64.py │ ├── bisect.py -> /usr/lib/python3.7/bisect.py │ ├── codecs.py -> /usr/lib/python3.7/codecs.py │ ├── collections -> /usr/lib/python3.7/collections │ ├── config-3.7m-darwin -> /usr/lib/python3.7/config-3.7m-darwin │ ├── copy.py -> /usr/lib/python3.7/copy.py │ ├── copyreg.py -> /usr/lib/python3.7/copyreg.py │ ├── distutils │ ├── encodings -> /usr/lib/python3.7/encodings │ ├── enum.py -> /usr/lib/python3.7/enum.py │ ├── fnmatch.py -> /usr/lib/python3.7/fnmatch.py │ ├── functools.py -> /usr/lib/python3.7/functools.py │ ├── genericpath.py -> /usr/lib/python3.7/genericpath.py │ ├── hashlib.py -> /usr/lib/python3.7/hashlib.py │ ├── heapq.py -> /usr/lib/python3.7/heapq.py │ ├── hmac.py -> /usr/lib/python3.7/hmac.py │ ├── imp.py -> /usr/lib/python3.7/imp.py │ ├── importlib -> /usr/lib/python3.7/importlib │ ├── io.py -> /usr/lib/python3.7/io.py │ ├── keyword.py -> /usr/lib/python3.7/keyword.py │ ├── lib-dynload -> /usr/lib/python3.7/lib-dynload │ ├── linecache.py -> /usr/lib/python3.7/linecache.py │ ├── locale.py -> /usr/lib/python3.7/locale.py │ ├── no-global-site-packages.txt │ ├── ntpath.py -> /usr/lib/python3.7/ntpath.py │ ├── operator.py -> /usr/lib/python3.7/operator.py │ ├── orig-prefix.txt │ ├── os.py -> /usr/lib/python3.7/os.py │ ├── posixpath.py -> /usr/lib/python3.7/posixpath.py │ ├── random.py -> /usr/lib/python3.7/random.py │ ├── re.py -> /usr/lib/python3.7/re.py │ ├── readline.so -> /usr/lib/python3.7/lib-dynload/readline.cpython-37m-darwin.so │ ├── reprlib.py -> /usr/lib/python3.7/reprlib.py │ ├── rlcompleter.py -> /usr/lib/python3.7/rlcompleter.py │ ├── shutil.py -> /usr/lib/python3.7/shutil.py │ ├── site-packages │ ├── site.py │ ├── sre_compile.py -> /usr/lib/python3.7/sre_compile.py │ ├── sre_constants.py -> /usr/lib/python3.7/sre_constants.py │ ├── sre_parse.py -> /usr/lib/python3.7/sre_parse.py │ ├── stat.py -> /usr/lib/python3.7/stat.py │ ├── struct.py -> /usr/lib/python3.7/struct.py │ ├── tarfile.py -> /usr/lib/python3.7/tarfile.py │ ├── tempfile.py -> /usr/lib/python3.7/tempfile.py │ ├── token.py -> /usr/lib/python3.7/token.py │ ├── tokenize.py -> /usr/lib/python3.7/tokenize.py │ ├── types.py -> /usr/lib/python3.7/types.py │ ├── warnings.py -> /usr/lib/python3.7/warnings.py │ └── weakref.py -> /usr/lib/python3.7/weakref.py └── pip-selfcheck.json 

Como você pode ver, o ambiente virtual foi criado copiando o binário Python para uma pasta local (ENV / bin / python). Também podemos notar que a pasta pai contém links simbólicos para os arquivos da biblioteca padrão do python. Não podemos criar um link simbólico para o arquivo executável, pois o intérprete ainda o renomeará para o caminho real.

Agora vamos ativar nosso ambiente:

 user@arb:/usr/home/test# source ENV/bin/activate 

Este comando altera a variável de ambiente $ PATH para que o comando python aponte para nossa versão local do python. Isso é conseguido substituindo o caminho local da pasta bin no início da linha $ PATH, para que o caminho local tenha precedência sobre todos os caminhos à direita.

 export "/usr/home/test/ENV/bin:$PATH" echo $PATH 

Se você executar o script nesse ambiente, ele será executado usando o binário em /usr/home/test/ENV/bin/python . O intérprete usará esse caminho como ponto de partida para encontrar módulos. No nosso caso, os módulos da biblioteca padrão serão encontrados no caminho /usr/home/test/ENV/lib/python3.7/ .

Este é o principal truque, graças ao qual todas as bibliotecas para trabalhar com ambientes virtuais funcionam.

Melhorias no Python 3


A partir da versão Python 3.3, um novo padrão apareceu, chamado PEP 405 , que introduz um novo mecanismo para ambientes leves.

Este PEP adiciona uma etapa extra ao processo de busca. Se você criar o pyenv.cfg configuração pyenv.cfg , em vez de copiar o binário Python e todos os seus módulos, poderá simplesmente indicar sua localização nesta configuração.

Esse recurso é usado ativamente pelo módulo venv padrão, que apareceu no Python 3.

 user@arb:/usr/home/test2# python3 -m venv ENV user@arb:/usr/home/test2# tree -L 3 . └── ENV ├── bin │ ├── activate │ ├── activate.csh │ ├── activate.fish │ ├── easy_install │ ├── easy_install-3.7 │ ├── pip │ ├── pip3 │ ├── pip3.5 │ ├── python -> python3 │ └── python3 -> /usr/bin/python3 ├── include ├── lib │ └── python3.7 ├── lib64 -> lib ├── pyvenv.cfg └── share └── python-wheels 

 user@arb:/usr/home/test2# cat ENV/pyvenv.cfg home = /usr/bin include-system-site-packages = false version = 3.7.0 user@arb:/usr/home/test2# readlink ENV/bin/python3 /usr/bin/python3 

Graças a esta configuração, em vez de copiar o binário, o venv simplesmente cria um link para ele. Se o parâmetro include-system-site-packages alterado para true , todos os módulos da biblioteca padrão serão automaticamente acessíveis no ambiente virtual.

Apesar dessas mudanças, a maioria das bibliotecas de terceiros para trabalhar com ambientes virtuais usa a abordagem antiga.

PS: Eu sou o autor deste artigo, você pode fazer qualquer pergunta.

Source: https://habr.com/ru/post/pt418579/


All Articles