用pex打包本地模块

Packaging local module with pex

我正在尝试用 pex 打包我的本地模块,但我似乎做不到。

我创建了一个简单的项目:

→ python --version
Python 2.7.10
→ pex --version
pex 1.1.15
→ tree .
.
├── bla
│   ├── __init__.py
│   └── main.py
└── setup.py

bla/__init__.py

import main

bla/main.py

if __name__ == '__main__':
   print 'yo'

这对我来说似乎是最简单的项目。

→ pex -v . -o v.pex --disable-cache
Traceback (most recent call last):
  File "/Users/Charly/repos/load_tester/venv/bin/pex", line 11, in <module>
   sys.exit(main())
 File "/Users/Charly/repos/load_tester/venv/lib/python2.7/site-packages/pex/bin/pex.py", line 540, in main
    pex_builder = build_pex(reqs, options, resolver_options_builder)
 File "/Users/Charly/repos/load_tester/venv/lib/python2.7/site-packages/pex/bin/pex.py", line 475, in build_pex
    resolvables = [Resolvable.get(arg, resolver_option_builder) for arg in args]
  File "/Users/Charly/repos/load_tester/venv/lib/python2.7/site-packages/pex/resolvable.py", line 61, in get
    raise cls.InvalidRequirement('Unknown requirement type: %s' % resolvable_string)
 pex.resolvable.InvalidRequirement: Unknown requirement type: .

也尝试过 python setup.py bdist_pex 但是失败了,因为找不到命令。

我好像真的误解了一些基本的东西,但我想不通。

一种方法是:

  1. create 使用 python setup.py sdist
  2. 创建源分发(tarball、zip 文件等)
  3. 然后 运行 pex 命令与 -f DIST_DIR 开关

    例如。 pex $(pip freeze) -o aflaskapp.pex -e 'aflaskapp.app' -f dist -v

我发现了一些额外的细节。如果你 运行:

pex .

它将尝试使用目录中的 setup.py 构建。如果没有,那么 pex 将失败。

默认情况下,pex 将缓存本地包输出,因此如果您更改 setup.py 然后使用命令确保您的更改应用到下一个 pex 运行.

pex . --disable-cache

我最近与 pex 试图让它包含本地模块发生了一些争执。我学到的是:

  1. 必须为您的模块提供一个有效的setup.py文件才能使它工作,并且:
  2. 必须指定应用程序的入口点

由于多种原因,这很难弄清楚。通过阅读文档,我能够推断出在我的案例中正确的命令应该是这样的:

$ pex . -v -e usersnotifier:main -o usersnotifier.pex

但是,当我尝试这样做时,我一直收到错误提示:

pex.resolvable.InvalidRequirement: Unknown requirement type: .

针对此错误的网络搜索出现了——因为它 第一次 命中——this Github issue, which is still open as I type this. A spent a long while thinking that the above command wasn't working because of this bug. I attempted to downgrade setuptools and made a half-dozen other fruitless attempts to 'fix' the problem before this SO answer 暗示有必要提供 setup.py 文件。 (那个 Github 问题原来是一个转移注意力的问题。据我所知,它提到的 setuptools 错误已经被修复。)

所以...我写了一个 setup.py 文件。起初,我一直收到 Unknown requirement type: . 错误,但后来我意识到我的 setup.py 只是包含一个 dead-obvious 印刷错误。在这种情况下,pex 发出的错误消息实际上非常清楚,但紧随其后的是 large-ish 堆栈跟踪和 Unknown requirement type: . 消息。我只是没有密切注意并且错过它的时间比我承认的要长。

我终于注意到了我的拼写错误并修复了它,但是 我的 setup.py 中的另一个 缺陷未能包含我的本地模块。 pex 在这种情况下有效,但生成的文件没有:

$ pex . -v -e usersnotifier:main -o usersnotifier.pex --disable-cache                                                                                                                     
  usersnotifier 0.1: Resolving distributions :: Packaging paho-mqtt    
  pyinotify 0.9.6
  paho-mqtt 1.3.1
pex: Building pex: 2704.3ms                                        
pex:   Resolving distributions: 2393.2ms
pex:       Packaging usersnotifier: 319.3ms
pex:       Packaging pyinotify: 347.4ms
pex:       Packaging paho-mqtt: 361.1ms
Saving PEX file to usersnotifier.pex

$ ./usersnotifier.pex 
Traceback (most recent call last):
  File ".bootstrap/_pex/pex.py", line 367, in execute
  File ".bootstrap/_pex/pex.py", line 293, in _wrap_coverage
  File ".bootstrap/_pex/pex.py", line 325, in _wrap_profiling
  File ".bootstrap/_pex/pex.py", line 410, in _execute
  File ".bootstrap/_pex/pex.py", line 468, in execute_entry
  File ".bootstrap/_pex/pex.py", line 482, in execute_pkg_resources
  File ".bootstrap/pkg_resources/__init__.py", line 2297, in resolve
ImportError: No module named 'usersnotifier'

这是最终对我有用的 bare-bones setup.py

from setuptools import setup                                                                                                                                                              

setup(
    name='usersnotifier',
    version='0.1',
    py_modules=['usersnotifier', 'userswatcher'],
    install_requires=[
        'paho-mqtt>=1.3.1',
        'pyinotify>=0.9.6',
    ],
    include_package_data=True,
    zip_safe=False
)

之前它不起作用的原因是我不小心将参数 py_module 传递给 setup() 而不是 py_modules (复数)。 ¯\_(ツ)_/¯

@cmcginty 对这个问题的回答中提到了我遇到的最后一个障碍,即:除非您的模块版本号发生变化,否则 pex 将 cache/reuse 上次您 运行 它。因此,如果您修复 setup.py 和 re-run pex 中的问题,它实际上不会合并您的更改,除非您:a) 修改版本号,或 b) 通过 --disable-cache 调用时 pex.

在一天结束的时候,整个事情变成了一个练习,写一个合适的 setup.py,运行ning:

$ pex . -v -e mymodule:main -o mymodule.pex --disable-cache

以下是我可以提供的一些技巧(可能是给未来的我):

提示 1

使用 python setup.py sdist 测试您的 setup.py 文件。令人惊讶的是 很容易搞砸,在您确定您的包裹内容正确之前,涉及 pex 毫无意义。在 运行ning python setup.py sdist 之后,尝试将它生成的源码包(位于 dist 文件夹中)安装到一个新的 venv 中,看看它是否包含您期望的所有文件。只有在 pex 之后才继续调用 这是有效的。

提示 2

总是--disable-cache 传递给 pex 除非你有充分的理由不这样做。

提示 3

在解决所有这些问题时,我发现我可以 运行:

$ unzip mymodule.pex

提取PEX文件的内容。这有助于解决 sdist 包内容与 pex-ified 应用程序之间存在的任何差异。