使用pyenv管理多個Python版本依賴環境

背景

從接觸Python以來,一直都是採用virtualenv和virtualenvwrapper來管理不同項目的依賴環境,通過workon、mkvirtualenv等命令進行虛擬環境切換,很是愉快。

然而,最近想讓項目能兼容更多的Python版本,例如至少同時兼容Python2.7和Python3.3+,就發現採用之前的方式行不通了。

最大的問題在於,在本地計算機同時安裝Python2.7和Python3後,即使分別針對兩個Python版本安裝了virtualenv和virtualenvwrapper,也無法讓兩個Python版本的workon、mkvirtualenv命令同時生效。另外一方面,要想在本地計算機安裝多個Python版本,會發現安裝的成本都比較高,實現方式也不夠優雅。

幸運地是,針對該痛點,已經存在一個比較成熟的方案,那就是pyenv。

如下是官方的介紹。

pyenv lets you easily switch between multiple versions of Python. It』s simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well.

This project was forked from rbenv and ruby-build, and modified for Python.

本文就針對pyenv最核心的功能進行介紹。

基本原理

如果要講解pyenv的工作原理,基本上採用一句話就可以概括,那就是:修改系統環境變數PATH。

對於系統環境變數PATH,相信大家都不陌生,裡面包含了一串由冒號分隔的路徑,例如/usr/local/bin:/usr/bin:/bin。每當在系統中執行一個命令時,例如python或pip,操作系統就會在PATH的所有路徑中從左至右依次尋找對應的命令。因為是依次尋找,因此排在左邊的路徑具有更高的優先順序。

而pyenv做的,就是在PATH最前面插入一個$(pyenv root)/shims目錄。這樣,pyenv就可以通過控制shims目錄中的Python版本號,來靈活地切換至我們所需的Python版本。

如果還想了解更多細節,可以查看pyenv的文檔介紹及其源碼實現。

環境初始化

pyenv的安裝方式包括多種,重點推薦採用pyenv-installer的方式,原因主要有兩點:

  • 通過pyenv-installer可一鍵安裝pyenv全家桶,後續也可以很方便地實現一鍵升級;

  • pyenv-installer的安裝方式基於GitHub,可保證總是使用到最新版本的pyenv,並且Python版本庫也是最新最全的。

install && config

通過如下命令安裝pyenv全家桶。

1n

$ curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bashn

內容除了包含pyenv以外,還包含如下插件:

  • pyenv-doctor

  • pyenv-installer

  • pyenv-update

  • pyenv-virtualenv

  • pyenv-which-ext

安裝完成後,pyenv命令還沒有加進系統的環境變數,需要將如下內容加到~/.zshrc中,然後執行source ~/.zshrc。

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

完成以上操作後,pyenv就安裝完成了。

$ pyenv -vpyenv 1.0.8n

如果不確定pyenv的環境是否安裝正常,可以通過pyenv doctor命令對環境進行檢測。

$ pyenv doctorCloning /Users/Leo/.pyenv/plugins/pyenv-doctor/bin/.....Installing python-pyenv-doctor...BUILD FAILED (OS X 10.12.3 using python-build 20160602)Last 10 log lines:checking for memory.h... yeschecking for strings.h... yeschecking for inttypes.h... yeschecking for stdint.h... yeschecking for unistd.h... yeschecking openssl/ssl.h usability... nochecking openssl/ssl.h presence... nochecking for openssl/ssl.h... noconfigure: error: OpenSSL development header is not installed.make: *** No targets specified and no makefile found. Stop.Problem(s) detected while checking system.n

通過檢測,可以發現本地環境可能存在的問題,例如,從以上輸出可以看出,本地的OpenSSL development header還沒有安裝。根據提示的問題,逐一進行修復,直到檢測不再出現問題為止。

update

通過pyenv update命令,可以更新pyenv全家桶的所有內容。

$ pyenv updateUpdating /Users/Leo/.pyenv...From https://github.com/yyuu/pyenv* branch master -> FETCH_HEADAlready up-to-date.Updating /Users/Leo/.pyenv/plugins/pyenv-doctor...From https://github.com/yyuu/pyenv-doctor* branch master -> FETCH_HEADAlready up-to-date.Updating /Users/Leo/.pyenv/plugins/pyenv-installer...From https://github.com/yyuu/pyenv-installer* branch master -> FETCH_HEADAlready up-to-date.Updating /Users/Leo/.pyenv/plugins/pyenv-update...From https://github.com/yyuu/pyenv-update* branch master -> FETCH_HEADAlready up-to-date.Updating /Users/Leo/.pyenv/plugins/pyenv-virtualenv...From https://github.com/yyuu/pyenv-virtualenv* branch master -> FETCH_HEADAlready up-to-date.Updating /Users/Leo/.pyenv/plugins/pyenv-which-ext...From https://github.com/yyuu/pyenv-which-ext* branch master -> FETCH_HEADAlready up-to-date.n

pyenv的核心使用方法

pyenv的主要功能如下:

$ pyenv -hUsage: pyenv <command> [<args>]Some useful pyenv commands are:commands List all available pyenv commandslocal Set or show the local application-specific Python versionglobal Set or show the global Python versionshell Set or show the shell-specific Python versioninstall Install a Python version using python-builduninstall Uninstall a specific Python versionrehash Rehash pyenv shims (run this after installing executables)version Show the current Python version and its originversions List all Python versions available to pyenvwhich Display the full path to an executablewhence List all Python versions that contain the given executableSee `pyenv help <command> for information on a specific command.For full documentation, see: https://github.com/yyuu/pyenv#readmen

查看所有可安裝的Python版本

$ pyenv install --listAvailable versions:2.1.3...2.7.122.7.13...3.5.33.6.03.6-dev3.6.13.7-devn

需要注意的是,如果是採用brew命令安裝的pyenv,可能會發現Python版本庫中沒有最新的Python版本。所以建議還是通過GitHub源碼方式安裝pyenv。

安裝指定版本的Python環境

$ pyenv install 3.6.0Downloading Python-3.6.0.tar.xz...-> https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tar.xzInstalling Python-3.6.0...Installed Python-3.6.0 to /Users/Leo/.pyenv/versions/3.6.0n

查看當前系統中所有可用的Python版本

$ pyenv versions* system (set by /Users/Leo/.pyenv/version)2.7.133.6.0n

切換Python版本

pyenv可以從三個維度來管理Python環境,簡稱為:當前系統、當前目錄、當前shell。這三個維度的優先順序從左到右依次升高,即當前系統的優先順序最低、當前shell的優先順序最高。

如果想修改系統全局的Python環境,可以採用pyenv global PYTHON_VERSION命令。該命令執行後會在$(pyenv root)目錄(默認為~/.pyenv)中創建一個名為version的文件(如果該文件已存在,則修改該文件的內容),裡面記錄著系統全局的Python版本號。

$ pyenv global 2.7.13$ cat ~/.pyenv/version2.7.13$ pyenv version2.7.13 (set by /Users/Leo/.pyenv/version)$ pyenv global 3.6.0$ cat ~/.pyenv/version3.6.0$ pyenv version3.6.0 (set by /Users/Leo/.pyenv/version)n

通常情況下,對於特定的項目,我們可能需要切換不同的Python環境,這個時候就可以通過pyenv local PYTHON_VERSION命令來修改當前目錄的Python環境。命令執行後,會在當前目錄中生成一個.python-version文件(如果該文件已存在,則修改該文件的內容),裡面記錄著當前目錄使用的Python版本號。

$ cat ~/.pyenv/version2.7.13$ pyenv local 3.6.0$ cat .python-version3.6.0$ cat ~/.pyenv/version2.7.13$ pyenv version3.6.0 (set by /Users/Leo/MyProjects/.python-version)$ pip -Vpip 9.0.1 from /Users/Leo/.pyenv/versions/3.6.0/lib/python3.6/site-packages (python 3.6)n

可以看出,當前目錄中的.python-version配置優先於系統全局的~/.pyenv/version配置。

另外一種情況,通過執行pyenv shell PYTHON_VERSION命令,可以修改當前shell的Python環境。執行該命令後,會在當前shell session(Terminal窗口)中創建一個名為PYENV_VERSION的環境變數,然後在當前shell的任意目錄中都會採用該環境變數設定的Python版本。此時,當前系統和當前目錄中設定的Python版本均會被忽略。

$ echo $PYENV_VERSION$ pyenv shell 3.6.0$ echo $PYENV_VERSION3.6.0$ cat .python-version2.7.13$ pyenv version3.6.0 (set by PYENV_VERSION environment variable)n

顧名思義,當前shell的Python環境僅在當前shell中生效,重新打開一個新的shell後,該環境也就失效了。如果想在當前shell中取消shell級別的Python環境,採用unset命令重置PYENV_VERSION環境變數即可。

$ cat .python-version2.7.13$ pyenv version3.6.0 (set by PYENV_VERSION environment variable)$ unset PYENV_VERSION$ pyenv version2.7.13 (set by /Users/Leo/MyProjects/.python-version)n

管理多個依賴庫環境

經過以上操作,我們在本地計算機中就可以安裝多個版本的Python運行環境,並可以按照實際需求進行靈活地切換。然而,很多時候在同一個Python版本下,我們仍然希望能根據項目進行環境分離,就跟之前我們使用virtualenv一樣。

在pyenv中,也包含這麼一個插件,pyenv-virtualenv,可以實現同樣的功能。

使用方式如下:

$ pyenv virtualenv PYTHON_VERSION PROJECT_NAMEn

其中,PYTHON_VERSION是具體的Python版本號,例如,3.6.0,PROJECT_NAME是我們自定義的項目名稱。比較好的實踐方式是,在PROJECT_NAME也帶上Python的版本號,以便於識別。

現假設我們有XDiff這麼一個項目,想針對Python 2.7.13和Python 3.6.0分別創建一個虛擬環境,那就可以依次執行如下命令。

$ pyenv virtualenv 3.6.0 py36_XDiff$ pyenv virtualenv 2.7.13 py27_XDiffn

創建完成後,通過執行pyenv virtualenvs命令,就可以看到本地所有的項目環境。

$ pyenv virtualenvs2.7.13/envs/py27_XDiff (created from /Users/Leo/.pyenv/versions/2.7.13)* 3.6.0/envs/py36_XDiff (created from /Users/Leo/.pyenv/versions/3.6.0)py27_XDiff (created from /Users/Leo/.pyenv/versions/2.7.13)py36_XDiff (created from /Users/Leo/.pyenv/versions/3.6.0)n

通過這種方式,在同一個Python版本下我們也可以創建多個虛擬環境,然後在各個虛擬環境中分別維護依賴庫環境。

例如,py36_XDiff虛擬環境位於/Users/Leo/.pyenv/versions/3.6.0/envs目錄下,而其依賴庫位於/Users/Leo/.pyenv/versions/3.6.0/lib/python3.6/site-packages中。

$ pip -Vpip 9.0.1 from /Users/Leo/.pyenv/versions/3.6.0/lib/python3.6/site-packages (python 3.6)n

後續在項目開發過程中,我們就可以通過pyenv local XXX或pyenv activate PROJECT_NAME命令來切換項目的Python環境。

? MyProjects pyenv local py27_XDiff(py27_XDiff) ? MyProjects pyenv versionpy27_XDiff (set by /Users/Leo/MyProjects/.python-version)(py27_XDiff) ? MyProjects python -VPython 2.7.13(py27_XDiff) ? MyProjects pip -Vpip 9.0.1 from /Users/Leo/.pyenv/versions/2.7.13/envs/py27_XDiff/lib/python2.7/site-packages (python 2.7)n

可以看出,切換環境後,pip命令對應的目錄也隨之改變,即始終對應著當前的Python虛擬環境。

對應的,採用pyenv deactivate命令退出當前項目的Python虛擬環境。

如果想移除某個項目環境,可以通過如下命令實現。

$ pyenv uninstall PROJECT_NAMEn

以上便是日常開發工作中常用的pyenv命令,基本可以滿足絕大多數依賴庫環境管理方面的需求。

歡迎關注我的博客和微信公眾號 DebugTalk

使用pyenv管理多個Python版本依賴環境


推薦閱讀:

在Mac系統下python如何安裝第三方函數庫?
[Python] Marvel和DC哪個更政治正確
有哪些有趣的反爬蟲手段?

TAG:Python | Python教程 | Python库 |