我怎样才能轻松制作自己的自定义 setup.py 命令?

How can I easily make my own custom setup.py commands?

在 JavaScript 项目中,我可以在 package.json 中指定以下内容:

{
  "name": "dredd",
  "version": "1.0.4",
  "description": "API Blueprint testing tool",
  "main": "lib/dredd.js",
  "bin": {
    "dredd": "bin/dredd"
  },
  "scripts": {
    "lint": "coffeelint ./src",
    "pretest": "npm run lint",
    "test": "find ./test/ -name '*-test.coffee' | xargs mocha --compilers 'coffee:coffee-script/register' --reporter spec --timeout 120000 --recursive",
    ...
    "coveralls": "./scripts/coveralls.sh",
    ...
  },
  "dependencies": {
    ...

请参阅 scripts 部分。我可以定义任何 "script" 的任何名称和任何实现。然后我可以运行它作为npm run <name>(有些特殊的我什至可以直接运行作为npm <name>,例如npm test)。 "script" 的实现可以只是一个命令或一行,其工作方式与我的 shell 中的工作方式相同(虽然可移植性是我需要自己实现的,npm对我没有帮助,但没关系)。 npm 正确传播任何给定的参数以及退出代码。所以综上所述,我可以:

所以它类似于rakemakegruntgulp等,但是内置且非常简单,非常容易操作。我在 Python 项目中错过了这样的事情。我觉得 setup.py 是一个文件,最初是作为 单一入口点 到 Python 项目,我喜欢这个想法,我愿意接受它.理想情况下,我想要这样的东西:

python setup.py test
python setup.py lint
python setup.py <my command>

我想要单一入口点,因为我希望我的项目直观。人们不应该被迫阅读我的项目的文档或 .travis.yml 到 运行 linter、测试等。他们不需要关心我是使用 nosetests 还是 py.test 进行测试,还是使用 flake8 进行 linting。那是实现细节。

然而,这显然并不容易实现。例如。我希望我的 lint 命令在内部包含 flake8 以及其他内容。然后我希望我的 test 命令也总是 运行 lint 在 运行 本身之前。

我已经看到一些通过子classing Command 和在 setup.py 中的 setup(...) 中声明此类命令来实现自定义命令的方法,但是我找不到好的官方文档。此外,许多文章都是旧的,很难理解 2016 年的现状和当前的最佳实践,因为历史上有多个项目处理包装(distutilssetuptoolspipeasy_install, ...)。即使我能够实现 class 并调用它并在 python setup.py --help 上列出命令,我仍然必须自己实现子进程部分、退出代码、标准输入、标准错误和参数重定向.这对我的场景来说太复杂了,我想让我的项目保持简单并专注于它自己的目的。

测试框架通常采用实现一些重量级自定义命令的方式,这些命令可以设置为 python setup.py test 的替代品。实现有数百行,如果我想在命令中添加一些东西(例如,如果我想在每次调用 python setup.py testflake8 && pytest tests),那似乎很难实现。

我见过很多人放弃正确设计命令,因为它们太难写了,而且他们 just do following:

# setup.py
if sys.argv[0] == 'test':
    # do something

if sys.argv[0] == 'publish':
    # do something

setup(...

这很简单,但它没有集成到现有的 setup.py 基础架构中,如果我只想调用 shell 命令,我仍然需要自己实现所有子流程工作。另外我不确定是否可以轻松调用原始命令(例如,我想用一些东西包装 python setup.py test,所以我会写下 if sys.argv[0] == 'test': 块并做一些自定义的事情,但我怎样才能继续之后进行测试?同样,找不到这方面的文档...)。

以上两个选项都不容易编写或维护。但是,我认为 Python 生态系统中没有我正在寻找的东西。我想我必须遗漏一些东西。有人能至少指出我正确的方向吗?至少向我解释 setuptoolsdistutils 的情况,我在哪里可以找到所有相关文档?

超级简单的准系统方法。不过需要在您的系统上安装 flake8

class Flake8(Command):
    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        errno = os.system("flake8 <dir-needing-linting>")
        sys.exit(errno)