PyCon 2018 之 Python 未來的依賴管理工具 pipenv

iTwocucao發表於2018-05-25

0x00 前言

PyCon 2018 有很多精彩的演講,今天的文章裡,介紹一下 K 神的演講 『Python 未來的包管理工具 pipenv』

Kenneth Reitz 出品,必屬精品。

0x01 Python packaging 進化歷史

『上古時代』的 Pythonist 是這樣安裝依賴包的。

curl http://pypi.python.org/packages/alsdasdl/requests.tar.gz | tar zxf
cd requests/
python setup.py install
複製程式碼

這個問題初看起來不是問題,但是隨著你安裝程式的增多就知道有多麼痛苦了。

  • 有的依賴庫依賴別的庫你怎麼解決?比如 pandas 需要安裝 numpy
  • 有的依賴庫依賴 c 庫怎麼辦?比如 LXML
  • 在 python2.6.5 下,如果我需要安裝兩個不同版本的 Django 開發不同的軟體怎麼辦?難道只能動態複製檔案到 site-packages 裡面?

後來,我們是這樣安裝包的。

easy_install requests
複製程式碼

我們可以直接從 pypi 進行安裝了。但尼瑪,為什麼 easy_install 安裝很 easy, 但是沒有 easy_uninstall?

好,2010 年後,我們繼續前進:

  • 可以通過 pip 替代 easy_install 了。
  • 可以通過 virtualenv 管理專案的依賴庫了。雖然說,還是不能像 ruby gem 一樣同時把多個版本的的軟體裝在同一個系統裡。
  • 可以通過 requirements 鎖依賴了。

但,同期的其他程式語言社群分別出現瞭如下的包管理工具:

  • node -> yarn && npm , 有 lockfile
  • php -> composer , 有 lockfile
  • rust -> cargo , 有 lockfile
  • ruby -> bundler , 有 lockfile

而我大 Python 居然沒跟上潮流

python -> pip && virtualenv/venv , 無 lockfile

PS: Python3.3 之後,預設可以直接使用 venv 模組,不需要再安裝 virtualenv 了。但還是需要手動,並且用起來比較反直覺。

有人說,我可以通過 requirements 來?版本呀

那我們聊聊 requirements.txt

  • 如果你使用 pip freeze 來形成這個檔案,則不直觀,完全看不出來哪個依賴庫依賴哪個依賴。
  • 如果你直接手動指定你所需要的庫,比如 flask 的話,似乎又有些太直觀了。
  • 如果能有一個東西,既可以表示 freeze 的結果 (what you want),又可以表示你需要的庫 (what you need). 就好了。

這當然可以考慮用兩份 requirements 來解決。先安裝 what you need 用來開發,然後 freeze 為 what you want 去部署。也就是最後生成 requirements-dev.txt , 然後最後 freeze 成 requirements-prod.txt

這其實還是有一些可以優化的地方的,鋪墊了這麼多 K 神肯定是來介紹他的 pipenv 的。

比如說,我想檢視,本專案的依賴庫,直接 pipenv graph

coverage==4.5.1
fabric==2.0.1
  - cryptography [required: >=1.1, installed: 2.2.2]
    - asn1crypto [required: >=0.21.0, installed: 0.24.0]
    - cffi [required: >=1.7, installed: 1.11.5]
      - pycparser [required: Any, installed: 2.18]
    - idna [required: >=2.1, installed: 2.6]
    - six [required: >=1.4.1, installed: 1.11.0]
  - invoke [required: <2.0,>=1.0, installed: 1.0.0]
  - paramiko [required: >=2.4, installed: 2.4.1]
    - bcrypt [required: >=3.1.3, installed: 3.1.4]
      - cffi [required: >=1.1, installed: 1.11.5]
        - pycparser [required: Any, installed: 2.18]
      - six [required: >=1.4.1, installed: 1.11.0]
    - cryptography [required: >=1.5, installed: 2.2.2]
      - asn1crypto [required: >=0.21.0, installed: 0.24.0]
      - cffi [required: >=1.7, installed: 1.11.5]
        - pycparser [required: Any, installed: 2.18]
      - idna [required: >=2.1, installed: 2.6]
      - six [required: >=1.4.1, installed: 1.11.0]
    - pyasn1 [required: >=0.1.7, installed: 0.4.2]
    - pynacl [required: >=1.0.1, installed: 1.2.1]
      - cffi [required: >=1.4.1, installed: 1.11.5]
        - pycparser [required: Any, installed: 2.18]
      - six [required: Any, installed: 1.11.0]
flake8==3.5.0
  - mccabe [required: >=0.6.0,<0.7.0, installed: 0.6.1]
  - pycodestyle [required: <2.4.0,>=2.0.0, installed: 2.3.1]
  - pyflakes [required: >=1.5.0,<1.7.0, installed: 1.6.0]
# 其他省略
複製程式碼

假如我要檢查我當前開發環境的依賴是不是有些小問題。直接執行 check

$ pipenv check
Checking PEP 508 requirements…
Passed!
Checking installed package safety…

33075: django >=1.10,<1.10.3 resolved (1.10.1 installed)!
Django before 1.8.x before 1.8.16, 1.9.x before 1.9.11, and 1.10.x before 1.10.3, when settings.DEBUG is True, allow remote attackers to conduct DNS rebinding attacks by leveraging failure to validate the HTTP Host header against settings.ALLOWED_HOSTS.

33076: django >=1.10,<1.10.3 resolved (1.10.1 installed)!
Django 1.8.x before 1.8.16, 1.9.x before 1.9.11, and 1.10.x before 1.10.3 use a hardcoded password for a temporary database user created when running tests with an Oracle database, which makes it easier for remote attackers to obtain access to the database server by leveraging failure to manually specify a password in the database settings TEST dictionary.

33300: django >=1.10,<1.10.7 resolved (1.10.1 installed)!
CVE-2017-7233: Open redirect and possible XSS attack via user-supplied numeric redirect URLs
============================================================================================

Django relies on user input in some cases  (e.g.
:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
to redirect the user to an "on success" URL. The security check for these
redirects (namely ``django.utils.http.is_safe_url()``) considered some numeric
URLs (e.g. ``http:999999999``) "safe" when they shouldn't be.

Also, if a developer relies on ``is_safe_url()`` to provide safe redirect
targets and puts such a URL into a link, they could suffer from an XSS attack.

CVE-2017-7234: Open redirect vulnerability in ``django.views.static.serve()``
=============================================================================

A maliciously crafted URL to a Django site using the
:func:`~django.views.static.serve` view could redirect to any other domain. The
view no longer does any redirects as they don't provide any known, useful
functionality.

Note, however, that this view has always carried a warning that it is not
hardened for production use and should be used only as a development aid.
複製程式碼

如何嘗鮮?我最近更新到了之前寫的一個庫(程式碼寫的慘不忍賭,最近準備重構,勿噴)

git clone git@github.com:twocucao/YaPyLib.git
cd YaPyLib/
brew install pipenv
pipenv --three
pipenv install --dev
pipenv shell
複製程式碼

記住幾個命令

pipenv --venv # 檢視 venv 位置
pipenv --python 3.6.5
exit 退出 pipenv shell
複製程式碼

當進入專案的時候,就執行 pipenv shell 自動 source 一下環境。嗯,但總覺得假如我維護了 30 個專案,每次開一個終端 cd 到對應的目錄,然後執行 pipenv shell 真的很麻煩啊。

嗯,沒錯,要是能將這個過程自動化就好了。

本人假設你用的 zsh , 新增下面的配置到 zsh 中。

function auto_pipenv_shell {
    if [ ! -n "${PIPENV_ACTIVE+1}" ]; then
        if [ -f "Pipfile" ] ; then
            pipenv shell
        fi
    fi
}

function cd {
    builtin cd "$@"
    auto_pipenv_shell
}

auto_pipenv_shell
複製程式碼

至於其他的功能,參考官網自己摸索吧。

FAQ 環節

FAQ 環節有一個問題非常有趣,應該把 lockfile 放在 git 倉庫裡面嗎?

k 神是這麼回答的 yes。 這個問題很久之前就在 issue 上回答過了

https://github.com/pypa/pipenv/issues/598

我剛開始覺得不提交會好一些,後來覺得 track 一下也無妨。

寫在最後

在用 npm / yarn 寫 node 程式 /vuejs 應用的時候,特別希望 python 圈子裡面能出一個類似於包管理工具。後來果然有了 pipenv, 今年 2 月份的時候把自己的專案遷移過來,發現 pipenv 用起來很挺舒服的。

pipenv 是 Python 未來的依賴管理工具。火速用上吧。

相關文章