本文原文是 setup.py tricks

在开始之前,我要声明我们要解释的是‘技巧’。不是最佳实践,并且在至少一种情况下,它是不可取的。

说到不明智的做法,在某时我将写一篇“setup.py 陷阱”的博客文章,我相信你应该绝不会在 setup.py 模块做的事情。

技巧

这些技巧使得我的 Python 包管理更容易一点。在你试图实现它们之前,我建议你最少有创建新包的基础经验。有两种方式可以学习到 Python 包的知识,一个是 New Library Sprint(适合初学者),另外一个是 Python Packaging User Guide(更高级)。

\’python setup.py publish\’

这是一切开始的地方。某天我看到了一些 Tom Christie 的代码,并发现了 Django Rest 框架setup.py 模块的 python setup.py publish 命令。它看起来像这样:

# setup.py
import os
import sys

# I\'ll discuss version tricks in a future blog post.
version = \"42.0.0\"

if sys.argv[-1] == \'publish\':
    os.system(\"python setup.py sdist upload\")
    os.system(\"python setup.py bdist_wheel upload\")
    print(\"You probably want to also tag the version now:\")
    print(\"  git tag -a %s -m \'version %s\'\" % (version, version))
    print(\"  git push --tags\")
    sys.exit()

# Below this point is the rest of the setup() function

了不起的是使用的这个技术我没有发现有稍微隐秘的 python setup.py sdist upload 命令,或者实际上隐秘的是 python setup.py bdist_wheel upload。当我把其中一个包 push 到 PyPI 上去的时候,我仅仅需要键入:

$ python setup.py publish

非常简单。

\’python setup.py tag\’

Tom Christie 的 python setup.py publish 命令的问题是它强迫我打印输出 git tag 命令。好吧,让我们说实话吧,它强迫我复制/粘贴我的输出到我的屏幕上。因此,对于我自己,我‘创造’了 python setup.py tag 命令:

# setup.py

if sys.argv[-1] == \'tag\':
    os.system(\"git tag -a %s -m \'version %s\'\" % (version, version))
    os.system(\"git push --tags\")
    sys.exit()

相当漂亮,是不是?现在我不需要记住一些隐秘的 git 命令。然后我获得了更短的 python setup.py publish 命令:

if sys.argv[-1] == \'publish\':
    os.system(\"python setup.py sdist upload\")
    os.system(\"python setup.py bdist_wheel upload\")
    sys.exit()

当我需要一个版本发布,我提交我的代码然后键入:

$ python setup.py publish
$ python setup.py tag

为什么我部组合这些命令?好吧,你不会支持 put 东西像 \’RC1\’ 或 \’-alpha\’ 到你的 PyPI 版本名字。通过分离这些命令,我可以更细粒度的控制包发布。我鼓励在 git tags 中对 alpha,beta,和 候选版本分类,而不是正式 PyPI 版本。

\’python setup.py test\’

我很确定我的一些读者使用这个技巧会有一个严重的问题。实际上,依赖于管理 Python 包架构的响应,它可能会被移动到我即将出版的“陷阱”的博客文章。

我喜欢 py.test。我已经写了一遍关于使用 py.test 的博客,我尝试在任何地方使用它。我们应该如何配合进 python setup.py test。恰好在那个时刻,使得我舒服的是使用 py.test 在 setup.py 中加入指定的类。

幸运的是,有另外一种方式:

if sys.argv[-1] == \'test\':
    test_requirements = [
        \'pytest\',
        \'flake8\',
        \'coverage\'
    ]
    try:
        modules = map(__import__, test_requirements)
    except ImportError as e:
        err_msg = e.message.replace(\"No module named \", \"\")
        msg = \"%s is not installed. Install your test requirments.\" % err_msg
        raise ImportError(msg)
    os.system(\'py.test\')
    sys.exit()

这意味着我用一个简单的添加代码使用了 py.testpython setup.py test

$ python setup.py test

理论上,可以运行 pip install 安装缺失的包,或者从一个 requirements 文件中调用它们。尽管如此,因为这些技巧,我喜欢让事情简单明了。如果我得到足够明确的结果,我将更新这个示例来包含调用 pip 来安装缺失的 requirements。

注意,这并不意味着我不使用 tox,我使用 tox 来调用我 python setup.py test 的版本。