构建 .pex 文件¶
你可以使用 pex
工具建立 .pex 文件,该工具在你 pip install pex
时可用。在 virtualenv 中这样做,然后你可以使用 pex 来启动自己:
$ pex pex requests -c pex -o ~/bin/pex
这个命令创建了一个包含 pex 和 requests 的 pex 文件,使用名为 “pex” 的控制台脚本,将其保存在 ~/bin/pex。在这一点上,假设 ~/bin 在你的 $PATH 上,那么你可以在任何 virtualenv 内或外使用 pex。
构建 .pex 文件的第二个最简单的方法是使用 bdist_pex
setuptools 命令,如果你 pip install pex
就可以使用。例如,从源代码克隆和构建 pip:
$ git clone https://github.com/pypa/pip && cd pip
$ python setup.py bdist_pex
running bdist_pex
Writing pip to dist/pip-7.2.0.dev0.pex
下面对这两种情况进行了更详细的描述。
调用 pex
工具¶
pex
工具没有必要的参数,默认情况下会构建一个空环境并调用它。当没有指定入口点时,”invocation” 意味着启动一个解释器:
$ pex
Python 3.6.2 (default, Jul 20 2017, 03:52:27)
[GCC 7.1.1 20170630] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
这创建了一个短暂的环境,只存在于 pex
命令调用的时间内,并在退出时立即被垃圾回收。
你可以通过指定 --python=PATH
来定制使用哪个解释器。PATH 可以是 Python 二进制文件的绝对路径,也可以是环境中 Python 解释器的名称,例如
$ pex
Python 3.6.2 (default, Jul 20 2017, 03:52:27)
[GCC 7.1.1 20170630] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> print "This won't work!"
File "<console>", line 1
print "This won't work!"
^
SyntaxError: Missing parentheses in call to 'print'
>>>
$ pex --python=python2.7
Python 2.7.13 (default, Jul 21 2017, 03:24:34)
[GCC 7.1.1 20170630] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> print "This works."
This works.
指定要求¶
要求使用与 pip
和 setuptools
预期相同的形式来指定,例如 flask
,setuptools==2.1.2
,Django>=1.4,<1.6
。这些被指定为 pex 的参数,可以指定任何数字(包括 0)。例如,要启动一个带有 flask
和 psutil>1
的环境:
$ pex flask 'psutil>1'
Python 3.6.2 (default, Jul 20 2017, 03:52:27)
[GCC 7.1.1 20170630] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
然后你可以像其他方式一样导入和操作模块:
>>> import flask
>>> import psutil
>>> ...
方便的是,pip freeze
的输出(一个被钉住的依赖列表)可以直接传递给 pex
。这提供了一个方便的方法,可以将虚拟环境冻结到 PEX 文件中。
$ pex $(pip freeze) -o my_application.pex
也可以使用 requirements.txt
文件,就像使用 pip
一样。
$ pex -r requirements.txt -o my_application.pex
指定入口点¶
入口点定义了环境的执行方式,可以用三种方式之一来指定。
pex <options> – script.py¶
如上所述,如果没有指定入口点,默认行为是模拟一个解释器。首先我们创建一个简单的 flask 应用程序:
$ cat <<EOF > flask_hello_world.py
> from flask import Flask
> app = Flask(__name__)
>
> @app.route('/')
> def hello_world():
> return 'hello world!'
>
> app.run()
> EOF
然后,像一个解释器一样,如果一个源文件被指定为 pex 的参数,它就会被调用:
$ pex flask -- ./flask_hello_world.py
* Running on http://127.0.0.1:5000/
pex -m¶
你的代码可能在 PEX 文件中,也可能是标准库中的某个预定的入口点。pex -m
的行为与 python -m
非常相似。考虑一下 python -m pydoc
:
$ python -m pydoc
pydoc - the Python documentation tool
pydoc.py <name> ...
Show text documentation on something. <name> may be the name of a
Python keyword, topic, function, module, or package, or a dotted
reference to a class or function within a module or module in a
...
这可以用 -m pydoc
的 pex
工具来模拟。
$ pex -m pydoc
pydoc - the Python documentation tool
tmpInGItD <name> ...
Show text documentation on something. <name> may be the name of a
Python keyword, topic, function, module, or package, or a dotted
reference to a class or function within a module or module in a
...
参数将在命令行的 --
后面不加转义地传递。 因此,为了在 Flask 中的 flask.app
包上获得 pydoc 帮助:
$ pex flask -m pydoc -- flask.app
Help on module flask.app in flask:
NAME
flask.app
FILE
/private/var/folders/rd/_tjz8zts3g14md1kmf38z6w80000gn/T/tmp3PCy5a/.deps/Flask-0.10.1-py2-none-any.whl/flask/app.py
DESCRIPTION
flask.app
~~~~~~~~~
诸如此类。
入口点也可以采取 package:target
的形式,比如 sphinx:main
或 fabric.main:main
分别代表 Sphinx 和 Fabric。这大致相当于运行一个脚本,做 import sys, from package import target; sys.exit(target())
。
这可能是调用 Python 应用程序的一种强大方式,而不需要 pip install
任何东西,例如一次性调用 Sphinx 的 readthedocs 主题可用:
$ pex sphinx==1.2.2 sphinx_rtd_theme -e sphinx:main -- --help
Sphinx v1.2.2
Usage: /tmp/tmpydcp6kox [options] sourcedir outdir [filenames...]
General options
^^^^^^^^^^^^^^^
-b <builder> builder to use; default is html
-a write all files; default is to only write new and changed files
-E don't use a saved environment, always read all files
...
尽管 sys.exit 被盲目地应用于目标函数的返回值,但由于 sys.exit
语义非常灵活,这可能会达到你想要的效果。请咨询你的目标函数和 sys.exit 文档以确定。
几乎可以肯定的是,你可以选择指定一个由应用程序导出的控制台脚本,如下文所述。
pex -c¶
如果你不知道你喜欢的 python 包的控制台脚本的 package:target
,pex 允许你使用 -c
来指定一个由发行版定义的控制台脚本。例如,Fabric 在 pip 安装时提供了 fab
工具:
$ pex Fabric -c fab -- --help
Fatal error: Couldn't find any fabfiles!
Remember that -f can be used to specify fabfile path, and use -h for help.
Aborting.
即使是由发行版的 “scripts” 部分定义的脚本也可以使用,例如,用 boto:
$ pex boto -c mturk
usage: mturk [-h] [-P] [--nicknames PATH]
{bal,hit,hits,new,extend,expire,rm,as,approve,reject,unreject,bonus,notify,give-qual,revoke-qual}
...
mturk: error: too few arguments
注意:如果你运行 pex -c
,遇到类似 pex.pex_builder.InvalidExecutableSpecification: Could not find script 'mainscript.py' in any distribution within PEX!
这样的错误,请仔细检查你的 setup.py,确保 mainscript.py
包含在你的 setup 的 scripts
阵列中。如果你使用 console_scripts
并遇到这个错误,请仔细检查你的 console_scripts
语法 - 关于 scripts
和 console_scripts
的进一步信息可以在 Python 打包文档 中找到。
保存 .pex 文件¶
上面的每个命令都在操纵短暂的 PEX 环境–这些环境只在 pex 命令的有效期内存在,并立即被垃圾回收。
如果指定了 -o PATH
选项,环境的 PEX 文件就会保存在磁盘的 PATH
处。例如,我们可以像上面那样打包一个独立的 Sphinx:
$ pex sphinx sphinx_rtd_theme -c sphinx -o sphinx.pex
而不是执行环境,它被保存到磁盘:
$ ls -l sphinx.pex
-rwxr-xr-x 1 wickman wheel 4988494 Mar 11 17:48 sphinx.pex
这是一个可执行的环境,可以像以前一样执行:
$ ./sphinx.pex --help
Sphinx v1.2.2
Usage: ./sphinx.pex [options] sourcedir outdir [filenames...]
General options
^^^^^^^^^^^^^^^
-b <builder> builder to use; default is html
-a write all files; default is to only write new and changed files
-E don't use a saved environment, always read all files
...
和以前一样,进入点不是必须的,如果没有指定,PEX 将默认为直接进入一个解释器。如果用 --python
指定了另一个解释器,例如 pypy,它将成为 PEX 文件中默认的 hashbang:
$ pex --python=pypy flask -o flask-pypy.pex
The hashbang of the PEX file specifies PyPy:
$ head -1 flask-pypy.pex
#!/usr/bin/env pypy
and when invoked uses the environment PyPy:
$ ./flask-pypy.pex
Python 2.7.3 (87aa9de10f9c, Nov 24 2013, 20:57:21)
[PyPy 2.2.1 with GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import flask
To specify an explicit Python shebang line (e.g. from a non-standard location or not on $PATH),
you can use the --python-shebang
option:
$ dist/pex --python-shebang='/Users/wickman/Python/CPython-3.4.2/bin/python3.4' -o my.pex
$ head -1 my.pex
#!/Users/wickman/Python/CPython-3.4.2/bin/python3.4
Furthermore, this can be manipulated at runtime using the PEX_PYTHON
environment variable.
Tailoring requirement resolution¶
In general, pex
honors the same options as pip when it comes to resolving packages. Like pip,
by default pex
fetches artifacts from PyPI. This can be disabled with --no-index
.
If PyPI fetching is disabled, you will need to specify a search repository via -f/--find-links
.
This may be a directory on disk or a remote simple http server.
For example, you can delegate artifact fetching and resolution to pip wheel
for whatever
reason – perhaps you’re running a firewalled mirror – but continue to package with pex:
$ pip wheel -w /tmp/wheelhouse sphinx sphinx_rtd_theme
$ pex -f /tmp/wheelhouse --no-index -e sphinx:main -o sphinx.pex sphinx sphinx_rtd_theme
Tailoring PEX execution at build time¶
There are a few options that can tailor how PEX environments are invoked. These can be found
by running pex --help
. Every flag mentioned here has a corresponding environment variable
that can be used to override the runtime behavior which can be set directly in your environment,
or sourced from a .pexrc
file (checking for ~/.pexrc
first, then for a relative .pexrc
).
--zip-safe
/--not-zip-safe
¶
Whether or not to treat the environment as zip-safe. By default PEX files are listed as zip safe.
If --not-zip-safe
is specified, the source of the PEX will be written to disk prior to
invocation rather than imported via the zipimporter. NOTE: Distribution zip-safe bits will still
be honored even if the PEX is marked as zip-safe. For example, included .eggs may be marked as
zip-safe and invoked without the need to write to disk. Wheels are always marked as not-zip-safe
and written to disk prior to PEX invocation. --not-zip-safe
forces --always-write-cache
.
--always-write-cache
¶
Always write all packaged dependencies within the PEX to disk prior to invocation. This forces the zip-safe bit of any dependency to be ignored.
--inherit-path
¶
By default, PEX environments are completely scrubbed empty of any packages installed on the global site path.
Setting --inherit-path
allows packages within site-packages to be considered as candidate distributions
to be included for the execution of this environment. This is strongly discouraged as it circumvents one of
the biggest benefits of using .pex files, however there are some cases where it can be advantageous (for example
if a package does not package correctly an an egg or wheel.)
--ignore-errors
¶
If not all of the PEX environment’s dependencies resolve correctly (e.g. you are overriding the current
Python interpreter with PEX_PYTHON
) this forces the PEX file to execute despite this. Can be useful
in certain situations when particular extensions may not be necessary to run a particular command.
--platform
¶
The (abbreviated) platform to build the PEX for. This will look for wheels for the particular platform.
The abbreviated platform is described by a string of the form PLATFORM-IMPL-PYVER-ABI
, where
PLATFORM
is the platform (e.g. linux-x86_64
, macosx-10.4-x86_64
), IMPL
is the python
implementation abbreviation (cp
or pp
), PYVER
is either a two or more digit string
representing the python version (e.g., 36
or 310
) or else a component dotted version
string (e.g., 3.6
or 3.10.1
) and ABI
is the ABI tag (e.g., cp36m
, cp27mu
,
abi3
, none
). A complete example: linux_x86_64-cp-36-cp36m
.
Constraints: when --platform
is used the
environment marker
python_full_version
will not be available if PYVER
is not given as a three component dotted
version since python_full_version
is meant to have 3 digits (e.g., 3.8.10
). If a
python_full_version
environment marker is encountered during a resolve, an
UndefinedEnvironmentName
exception will be raised. To remedy this, either specify the full
version in the platform (e.g, linux_x86_64-cp-3.8.10-cp38
) or use --complete-platform
instead.
--complete-platform
¶
The completely specified platform to build the PEX for. This will look for wheels for the particular platform.
The complete platform can be either a path to a file containing JSON data or else a JSON object
literal. In either case, the JSON object is expected to have two fields with any other fields
ignored. The marker_environment
field should have an object value with string field values
corresponding to
PEP-508 marker environment
entries. It is OK to only have a subset of valid marker environment fields but it is not valid to
present entries not defined in PEP-508. The compatible_tags
field should have an array of
strings value containing the compatible tags in order from most specific first to least
specific last as defined in PEP-425. Pex can create
complete platform JSON for you by running it on the target platform like so:
pex3 interpreter inspect --markers --tags
. For more options, particularly to select the desired
target interpreter see: pex3 interpreter inspect --help
.
Tailoring PEX execution at runtime¶
Tailoring of PEX execution can be done at runtime by setting various environment variables. The source of truth for these environment variables can be found in the pex.variables API.
Using bdist_pex
¶
pex provides a convenience command for use in setuptools. python setup.py
bdist_pex
is a simple way to build executables for Python projects that
adhere to standard naming conventions.
bdist_pex
¶
The default behavior of bdist_pex
is to build an executable using the
console script of the same name as the package. For example, pip has three
entry points: pip
, pip2
and pip2.7
if you’re using Python 2.7. Since
there exists an entry point named pip
in the console_scripts
section
of the entry points, that entry point is chosen and an executable pex is produced. The pex file
will have the version number appended, e.g. pip-7.2.0.pex
.
If no console scripts are provided, or the only console scripts available do not bear the same name as the package, then an environment pex will be produced. An environment pex is a pex file that drops you into an interpreter with all necessary dependencies but stops short of invoking a specific module or function.
bdist_pex --bdist-all
¶
If you would like to build all the console scripts defined in the package instead of
just the namesake script, --bdist-all
will write all defined entry_points but omit
version numbers and the .pex
suffix. This can be useful if you would like to
virtually install a Python package somewhere on your $PATH
without doing something
scary like sudo pip install
:
$ git clone https://github.com/sphinx-doc/sphinx && cd sphinx
$ python setup.py bist_pex --bdist-all --bdist-dir=$HOME/bin
running bdist_pex
Writing sphinx-apidoc to /Users/wickman/bin/sphinx-apidoc
Writing sphinx-build to /Users/wickman/bin/sphinx-build
Writing sphinx-quickstart to /Users/wickman/bin/sphinx-quickstart
Writing sphinx-autogen to /Users/wickman/bin/sphinx-autogen
$ sphinx-apidoc --help | head -1
Usage: sphinx-apidoc [options] -o <output_path> <module_path> [exclude_path, ...]
Using Pants¶
The Pants build system can build pex files. See here for details.