如何轻松制作自己的自定义setup.py命令?

时间:2016-01-28 08:35:56

标签: python shell setup.py

在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部分。我可以定义任何名称和任何实现的任何“脚本”。然后我可以将其作为npm run <name>运行(还有一些我可以直接以npm <name>运行的特殊项,例如npm test)。 “脚本”的实现可以只是一个命令或一行,其工作方式与我在shell中的工作方式相同(虽然可移植性是我自己需要实现的,npm对我没有帮助它,但没关系)。 npm正确传播任何给定的参数,也退出代码。总而言之,我可以:

  • 定义自定义命令
  • 定义pre<name>命令,这些命令将在调用<name>命令之前执行
  • 这些命令基本上可以是在我的终端中运行的任何内容
  • 他们正确地传播参数并退出代码,开箱即用
  • 理想情况下,父命令可以列出可用脚本

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

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

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

然而,这显然不容易实现。例如。我希望我的lint命令在内部包含flake8,也可能包含其他内容。然后我希望我的test命令在运行之前始终运行lint

我已经看到了一些通过子类化Command来实现自定义命令的方法,并在setup(...)的{​​{1}}中声明了这样的命令,但我发现没有很好的官方文档。此外,许多文章都很陈旧,很难理解2016年的当前状态和当前最佳实践,因为历史上有多个涉及包装的项目(setup.pydistutilssetuptoolspip,......)。即使我能够实现该类并调用它并在easy_install上列出命令,我仍然必须自己实现子进程部分,退出代码,stdin,stderr和参数重定向。这对我的场景来说太复杂了,我想让我的项目变得简单并专注于它自己的目的。

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

我见过许多人因为难以编写并且just do following而无法正确设计命令而辞职:

python setup.py test

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

上述两种选项都不易编写或维护。但是,我不相信我在Python生态系统中正在寻找什么。我想我必须遗漏一些东西。有人能指出我至少朝着正确的方向前进吗?至少用if sys.argv[0] == 'test':setuptools向我解释一下情况,在哪里可以找到所有相关文档?

1 个答案:

答案 0 :(得分:1)

超级简单的准系统方法。需要在您的系统上安装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)