【问题标题】:Best way to configure pylint with bazel使用 bazel 配置 pylint 的最佳方法
【发布时间】:2017-11-15 09:36:50
【问题描述】:

我使用 vim 作为我的编辑器和 Syntastic 插件。我正在尝试了解将 pylint 与 Bazel 等工具一起使用的惯用方式。

pylint 有一个 init-hook 命令行参数,可用于动态操作 sys.hook。我正在考虑编写一个包装脚本来执行此操作,但我不确定如何确定作为“init-hook”命令传递的正确内容。

【问题讨论】:

  • 我看不到 Bazel 和 PyLint 之间的联系,或者您想要实现的目标。您如何设想它们之间的互动?
  • @László 大概:让 pylint 通过 bazel 运行,就像其他类型的测试一样。

标签: bazel pylint


【解决方案1】:

我知道的最好方法是运行 pylint 作为测试的一部分。

理想情况下,您应该为每个文件设置一个 linting 规则,这样当该文件更改时,您只需重新 lint 那个文件。不过,这可能是不切实际的。

规模的另一端是有一个规则来检查项目中的所有文件。即使只有一个文件发生更改,该规则也会重新整理所有文件。所以这是低效的。

在我看来,一个好的中间立场是每个 Bazel 包都有一条 linting 规则。

例如,假设您在工作区@local_pylint_config//:pylint 下将 pylint 作为二进制文件,我推荐以下模式:

sh_test(
    name = "lint_test",
    srcs = ["lint_test.sh"],       # this doesn't have to do anything
    data = ["lint_files.out"],
)

genrule(
    name = "lint_files",
    srcs = glob(["**/*.py"]),
    outs = ["lint_files.out"],
    tools = ["@local_pylint_config//:pylint"],
    cmd = "$(location @local_pylint_config//:pylint) $(SRCS) >&/dev/null && md5sum $$(echo $(SRCS) | sort) > $@",
)

注意事项:

  • 如果可以构建其依赖项“lint_files”,则测试通过,如果 lint 成功,则测试成功。因此,当且仅当 linting 成功时,测试才会成功。
  • 我正在使用 genrule 来确保为正确的配置构建“pylint”规则。
  • 我将 pylint 的输出重定向到 /dev/null 以减少构建噪音。
  • 我正在计算所有源的校验和并将其写入输出文件,以便编写一个完全依赖于源内容的唯一输出,而不是其他任何内容(例如,不是当前时间)。我正在对源文件进行排序以确保输出是确定性的。如果我不使用md5sum 而只是touch'ed 输出文件,则输出的内容将独立于源的内容,因此下游测试规则不会重新运行。
  • 但是,使用... && date > $@ 而不是对源进行校验和也足够了,因为如果任何源文件发生更改,Bazel 将重建 genrule(并因此重新 lint 源文件),从而产生不同的输出,因为那么当前时间就会改变。但是,使用校验和是确定性的。

【讨论】:

  • 似乎是一个很好的权衡,每个包都有一个 lint 结果。您能否详细说明如何:在@local_pylint_config//:pylint 下的工作区中将 pylint 作为二进制文件?此外,是否可以将genrule 位放入类似third_party/pylint.BUILD 的文件中以便重用?
  • Re: pylint,我的意思是创建一个名为“local_pylint_config”的本地存储库,其 BUILD 文件包含一个用于包装 pylint 二进制文件的目标。示例:在WORKSPACE 文件中,添加new_local_repository(name="local_pylint_config", path="/usr/bin", build_file_content="""sh_binary(name="pylint-bin", srcs=["pylint"], visibility=["//visibility:public"])""")。或者:实现一个 Starlark repo 规则,发现 pylint 在哪里,实例化一个类似于我的示例的 BUILD 文件。
  • 普通和 Starlark 回购规则示例:stackoverflow.com/a/50017400/7778502(仅供参考:“Skylark”已重命名为“Starlark”)
【解决方案2】:

您可以创建调用 python 文件的 py_test 调用,它会自动扭曲对pylintpytest --pylint 的调用。为了在工作区中获得更多可重用的东西,请在 py_test 周围创建一个宏。我在Experimentations on Bazel: Python (3), linter & pytest中解释了详细的解决方案,并附有源代码链接。

tools/pytest/pytest_wrapper.py中创建python工具(包装调用pytest,或者只调用pylint)

import sys
import pytest

# if using 'bazel test ...'
if __name__ == "__main__":
    sys.exit(pytest.main(sys.argv[1:]))


tools/pytest/defs.bzl中创建宏

"""Wrap pytest"""

load("@rules_python//python:defs.bzl", "py_test")
load("@my_python_deps//:requirements.bzl", "requirement")

def pytest_test(name, srcs, deps = [], args = [], data = [], **kwargs):
    """
        Call pytest
    """
    py_test(
        name = name,
        srcs = [
            "//tools/pytest:pytest_wrapper.py",
        ] + srcs,
        main = "//tools/pytest:pytest_wrapper.py",
        args = [
            "--capture=no",
            "--black",
            "--pylint",
            "--pylint-rcfile=$(location //tools/pytest:.pylintrc)",
            # "--mypy",
        ] + args + ["$(location :%s)" % x for x in srcs],
        python_version = "PY3",
        srcs_version = "PY3",
        deps = deps + [
            requirement("pytest"),
            requirement("pytest-black"),
            requirement("pytest-pylint"),
            # requirement("pytest-mypy"),
        ],
        data = [
            "//tools/pytest:.pylintrc",
        ] + data,
        **kwargs
    )

公开tools/pytest/BUILD.bazel的部分资源

exports_files([
    "pytest_wrapper.py",
    ".pylintrc",
])


从你的包中调用它BUILD.bazel

load("//tools/pytest:defs.bzl", "pytest_test")
...

pytest_test(
    name = "test",
    srcs = glob(["*.py"]),
    deps = [
        ...
    ],
)

然后调用bazel test //... pylint, pytest, back,... 是测试流程的一部分

【讨论】:

    猜你喜欢
    • 2018-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-29
    • 2018-10-20
    • 1970-01-01
    相关资源
    最近更新 更多