包管理现状

  • 打包:poetry
  • 工具安装虚拟环境隔离:pipx
  • 开发部署:mkvirtualenv/生产环境 venv
  • 多Python解释器版本:pyenv

pipx

poetry

2019-09-27 update: 弃用,参考 http://greyli.com/do-not-use-pipenv/
选用 poetry

pipenv

pipenvPython官方推荐的包管理工具。可以说,它集成了virtualenv, pippyenv三者的功能。其目的旨在集合了所有的包管理工具的长处,如: npm, yarn, composer等的优点。
它能够自动为项目创建和管理虚拟环境,从Pipfile文件添加或删除安装的包,同时生成Pipfile.lock来锁定安装包的版本和依赖信息,避免构建错误。
pipenv主要解决了如下问题:

  • 不用再单独使用pipvirtualenv, 现在它们合并在一起了
  • 不用再维护requirements.txt, 使用PipfilePipfile.lock来代替
  • 可以使用多个python版本(python2python3)
  • 在安装了pyenv的条件下,可以自动安装需要的Python版本

安装

为了方便使用, 建议全局安装

  1. pip3 install pipenv

基本概念

  • 虚拟环境如果不存在的话,会自动创建
  • install命令没有传递参数指定安装包时,所有[packages]里指定的包都会被安装
  • pipenv --three可以初始化一个python3版本的虚拟环境
  • pipenv --two可以初始化一个python2版本的虚拟环境

添加shell补齐

如果使用的是bash, 可添加下面语句到.bashrc.bash_profile

  1. eval "$(pipenv --completion)"

pipenv基本使用

建立虚拟环境

pipenv install —dev

激活虚拟环境

cd project_dir
pipenv shell

删除虚拟环境

pipenv —rm

pipenv 命令

  1. $ pipenv
  2. Usage: pipenv [OPTIONS] COMMAND [ARGS]...
  3. Options:
  4. --update Update Pipenv & pip to latest. # 更新pipenv和pip到最新版本
  5. --where Output project home information. # 获取项目路径
  6. --venv Output virtualenv information. # 获取虚拟环境的路径
  7. --py Output Python interpreter information. # 获取python解释器的路径
  8. --envs Output Environment Variable options. # 输出当前的环境变量
  9. --rm Remove the virtualenv. # 删除虚拟环境
  10. --bare Minimal output.
  11. --completion Output completion (to be eval'd).
  12. --man Display manpage.
  13. --three / --two Use Python 3/2 when creating virtualenv. # 使用python3/2创建虚拟环境
  14. --python TEXT Specify which version of Python virtualenv should use. # 指定python的版本信息
  15. --site-packages Enable site-packages for the virtualenv.
  16. --jumbotron An easter egg, effectively.
  17. --version Show the version and exit.
  18. -h, --help Show this message and exit.
  19. Usage Examples:
  20. Create a new project using Python 3.6, specifically:
  21. $ pipenv --python 3.6
  22. Install all dependencies for a project (including dev):
  23. $ pipenv install --dev
  24. Create a lockfile containing pre-releases:
  25. $ pipenv lock --pre
  26. Show a graph of your installed dependencies:
  27. $ pipenv graph
  28. Check your installed dependencies for security vulnerabilities:
  29. $ pipenv check
  30. Install a local setup.py into your virtual environment/Pipfile:
  31. $ pipenv install -e .
  32. Commands:
  33. check Checks for security vulnerabilities and against PEP 508 markers
  34. provided in Pipfile.
  35. graph Displays currently–installed dependency graph information.
  36. install Installs provided packages and adds them to Pipfile, or (if none
  37. is given), installs all packages.
  38. lock Generates Pipfile.lock.
  39. open View a given module in your editor.
  40. run Spawns a command installed into the virtualenv.
  41. shell Spawns a shell within the virtualenv.
  42. uninstall Un-installs a provided package and removes it from Pipfile.
  43. update Uninstalls all packages, and re-installs package(s) in [packages]
  44. to latest compatible versions.

常用命令介绍

  1. # 安装包
  2. $ pipenv install
  3. # 激活当前项目的虚拟环境
  4. $ pipenv shell
  5. # 安装开发依赖包
  6. $ pipenv install pytest --dev
  7. # 图形显示包依赖关系
  8. $ pipenv graph
  9. # 生成lockfile
  10. $ pipenv lock
  11. # 删除所有的安装包
  12. $ pipenv uninstall --all

高级技巧

导入requirements.txt

当在执行pipenv install命令的时候,如果有一个requirements.txt文件,那么会自动从requirements.txt文件导入安装包信息并创建一个Pipfile文件。
同样可以使用$ pipenv install -r path/to/requirements.txt来导入requirements.txt文件
注意:
默认情况下,我们都是在requirements.txt文件里指定了安装包的版本信息的,在导入requirements.txt文件时,版本信息也会被自动写Pipfile文件里, 但是这个信息我们一般不需要保存在Pipfile文件里,需要手动更新Pipfile来删除版本信息

指定安装包的版本信息

为了安装指定版本的包信息,可以使用:

  1. $ pipenv install requests==2.13.0

这个命令也会自动更新Pipfile文件

指定Python的版本信息

在创建虚拟环境的时候,我们可以指定使用的python版本信息,类似pyenv

  1. $ pipenv --python 3
  2. $ pipenv --python 3.6
  3. $ pipenv --python 2.7.14

pipenv会自动扫描系统寻找合适的版本信息,如果找不到的话,同时又安装了pyenv, 它会自动调用pyenv下载对应的版本的python

指定安装包的源

如果我们需要在安装包时,从一个源下载一个安装包,然后从另一个源下载另一个安装包,我们可以通过下面的方式配置

  1. [[source]]
  2. url = "https://pypi.python.org/simple"
  3. verify_ssl = true
  4. name = "pypi"
  5. [[source]]
  6. url = "http://pypi.home.kennethreitz.org/simple"
  7. verify_ssl = false
  8. name = "home"
  9. [dev-packages]
  10. [packages]
  11. requests = {version="*", index="home"}
  12. maya = {version="*", index="pypi"}
  13. records = "*"

如上设置了两个源:

同时指定requests包从home源下载,maya包从pypi源下载

生成requirements.txt文件

我们也可以从PipfilePipfile.lock文件来生成requirements.txt

  1. # 生成requirements.txt文件
  2. $ pipenv lock -r
  3. # 生成dev-packages的requirements.txt文件
  4. # pipenv lock -r -d

检查安全隐患

pipenv包含了safety模块,可以让我们坚持安装包是否存在安全隐患。

  1. $ cat Pipfile
  2. [packages]
  3. django = "==1.10.1"
  4. $ pipenv check
  5. Checking PEP 508 requirements
  6. Passed!
  7. Checking installed package safety
  8. 33075: django >=1.10,<1.10.3 resolved (1.10.1 installed)!
  9. 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.
  10. 33076: django >=1.10,<1.10.3 resolved (1.10.1 installed)!
  11. 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.
  12. 33300: django >=1.10,<1.10.7 resolved (1.10.1 installed)!
  13. CVE-2017-7233: Open redirect and possible XSS attack via user-supplied numeric redirect URLs
  14. ============================================================================================
  15. Django relies on user input in some cases (e.g.
  16. :func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
  17. to redirect the user to an "on success" URL. The security check for these
  18. redirects (namely ``django.utils.http.is_safe_url()``) considered some numeric
  19. URLs (e.g. ``http:999999999``) "safe" when they shouldn't be.
  20. Also, if a developer relies on ``is_safe_url()`` to provide safe redirect
  21. targets and puts such a URL into a link, they could suffer from an XSS attack.
  22. CVE-2017-7234: Open redirect vulnerability in ``django.views.static.serve()``
  23. =============================================================================
  24. A maliciously crafted URL to a Django site using the
  25. :func:`~django.views.static.serve` view could redirect to any other domain. The
  26. view no longer does any redirects as they don't provide any known, useful
  27. functionality.
  28. Note, however, that this view has always carried a warning that it is not
  29. hardened for production use and should be used only as a development aid.

编码风格检查

pipenv默认集成了flake8, 可以用来检测编码风格

  1. $ cat t.py
  2. import requests
  3. $ pipenv check --style t.py
  4. t.py:1:1: F401 'requests' imported but unused
  5. t.py:1:16: W292 no newline at end of file

浏览模块代码

  1. # 用编辑器打开requests模块
  2. $ pipenv open requests

自动加载环境变量.env

如果项目根目录下有.env文件,$ pipenv shell$ pipenv run会自动加载它。

  1. $ cat .env
  2. HELLO=WORLD
  3. $ pipenv run python
  4. Loading .env environment variables
  5. Python 2.7.13 (default, Jul 18 2017, 09:17:00)
  6. [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
  7. Type "help", "copyright", "credits" or "license" for more information.
  8. >>> import os
  9. >>> os.environ['HELLO']
  10. 'WORLD'

自定义虚拟环境的路径

默认情况下,pipenv使用pew来管理虚拟环境的路径,我们可以自定义WORKON_HOME环境变量来设置虚拟环境的路径。比如:

  1. export WORKON_HOME=~/.venvs

我们也可以通过设置环境变量PIPENV_VENV_IN_PROJECT使虚拟环境在每个项目的根目录下project/.venv

自动激活虚拟环境

配合virtualenv-autodetect和设置PIPENV_VENV_IN_PROJECT环境变量可以自动激活虚拟环境。
.bashrc.bash_profile中配置如下

  1. export PIPENV_VENV_IN_PROJECT=1
  2. source /path/to/virtualenv-autodetect.sh

如果使用了oh-my-zsh, 可以直接使用它的插件形式

  1. # 安装插件
  2. $ git@github.com:RobertDeRose/virtualenv-autodetect.git ~/.oh-my-zsh/custom/plugins

再修改.zshrc文件启动插件

  1. # 找到启动plugins的行添加启用插件
  2. plugins=(... virtualenv-autodetect)

通过环境变量配置pipenv

pipenv内置了很多环境变量,可以通过设置这些环境变量来配置pipenv

  1. $ pipenv --envs
  2. The following environment variables can be set, to do various things:
  3. - PIPENV_MAX_DEPTH
  4. - PIPENV_SHELL
  5. - PIPENV_DOTENV_LOCATION
  6. - PIPENV_HIDE_EMOJIS
  7. - PIPENV_CACHE_DIR
  8. - PIPENV_VIRTUALENV
  9. - PIPENV_MAX_SUBPROCESS
  10. - PIPENV_COLORBLIND
  11. - PIPENV_VENV_IN_PROJECT # 在项目根路径下创建虚拟环境
  12. - PIPENV_MAX_ROUNDS
  13. - PIPENV_USE_SYSTEM
  14. - PIPENV_SHELL_COMPAT
  15. - PIPENV_USE_HASHES
  16. - PIPENV_NOSPIN
  17. - PIPENV_PIPFILE
  18. - PIPENV_INSTALL_TIMEOUT
  19. - PIPENV_YES
  20. - PIPENV_SHELL_FANCY
  21. - PIPENV_DONT_USE_PYENV
  22. - PIPENV_DONT_LOAD_ENV
  23. - PIPENV_DEFAULT_PYTHON_VERSION # 设置创建虚拟环境时默认的python版本信息,如: 3.6
  24. - PIPENV_SKIP_VALIDATION
  25. - PIPENV_TIMEOUT

需要修改某个默认配置时,只需要把它添加到.bashrc.bash_profile文件里即可。

常见问题

pipenv install时报错pip.exceptions.InstallationError: Command "python setup.py egg_info" failed with error code 1

错误原因是pipenv是用python2安装的,解决办法是使用pip3重新安装pipenv

  1. $ pip unintall pipenv
  2. $ pip3 install pipenv

在项目目录里运行pipenv时报错AttributeError: module 'enum' has no attribute 'IntFlag'

  1. $ pipenv
  2. Failed to import the site module
  3. Traceback (most recent call last):
  4. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site.py", line 544, in <module>
  5. main()
  6. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site.py", line 530, in main
  7. known_paths = addusersitepackages(known_paths)
  8. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site.py", line 282, in addusersitepackages
  9. user_site = getusersitepackages()
  10. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site.py", line 258, in getusersitepackages
  11. user_base = getuserbase() # this will also set USER_BASE
  12. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site.py", line 248, in getuserbase
  13. USER_BASE = get_config_var('userbase')
  14. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/sysconfig.py", line 601, in get_config_var
  15. return get_config_vars().get(name)
  16. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/sysconfig.py", line 580, in get_config_vars
  17. import _osx_support
  18. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/_osx_support.py", line 4, in <module>
  19. import re
  20. File "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/python3.6/re.py", line 142, in <module>
  21. class RegexFlag(enum.IntFlag):
  22. AttributeError: module 'enum' has no attribute 'IntFlag'

是因为在项目目录里运行pipenv命令时,项目虚拟环境的python版本低于3.6.4, 由于IntFlag是从python3.6.4才开始集成到python内置模块的。当激活了项目的虚拟环境之后, 环境变量PYTHONPATH会被设置为当前虚拟环境的site-packages目录,因此pipenv依赖的IntFlag无法找到。 解决办法是在运行pipenv时设置环境变量PYTHONPATH为空

  1. $ PYTHONPATH= pipenv

参考

https://crazygit.wiseturtles.com/2018/01/08/pipenv-tour/