From 6eeb89cba4cb4092e1501570a96fd82aed88ad0f Mon Sep 17 00:00:00 2001 From: Reid 'arrdem' McKenzie Date: Thu, 2 Sep 2021 22:10:12 -0600 Subject: [PATCH] Get black working as a linter --- tools/black/BUILD | 10 ++++++ tools/black/__main__.py | 57 +++++++++++++++++++++++++++++++++ tools/black/black.bzl | 71 +++++++++++++++++++++++++++++++++++++++++ tools/flake8/flake8.bzl | 2 +- 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tools/black/BUILD create mode 100644 tools/black/__main__.py create mode 100644 tools/black/black.bzl diff --git a/tools/black/BUILD b/tools/black/BUILD new file mode 100644 index 0000000..a6aa310 --- /dev/null +++ b/tools/black/BUILD @@ -0,0 +1,10 @@ +py_binary( + name = "black", + main = "__main__.py", + deps = [ + py_requirement("black"), + ], + visibility = [ + "//visibility:public" + ], +) diff --git a/tools/black/__main__.py b/tools/black/__main__.py new file mode 100644 index 0000000..c4a3e54 --- /dev/null +++ b/tools/black/__main__.py @@ -0,0 +1,57 @@ +"""A shim to black which knows how to tee output.""" + +import argparse +from contextlib import contextmanager +import sys + +from black import patched_main + + +parser = argparse.ArgumentParser() +parser.add_argument("--output-file", default=None) + + +class Tee(object): + """Tee all I/O to a file and stdout.""" + + def __init__(self, name, mode): + self._file = open(name, mode) + self._stdout = sys.stdout + + def __enter__(self): + sys.stdout = self + return self + + def __exit__(self, *args, **kwargs): + sys.stdout = self._stdout + self.close() + + def write(self, data): + self._file.write(data) + self._stdout.write(data) + + def flush(self): + self._file.flush() + self._stdout.flush() + + def close(self): + self._file.close() + + +@contextmanager +def nullctx(): + yield + + +if __name__ == "__main__": + opts, args = parser.parse_known_args() + + if opts.output_file: + print("Teeig output....") + ctx = Tee(opts.output_file, "w") + else: + ctx = nullctx() + + with ctx: + sys.argv = [sys.argv[0]] + args + patched_main() diff --git a/tools/black/black.bzl b/tools/black/black.bzl new file mode 100644 index 0000000..96a96e9 --- /dev/null +++ b/tools/black/black.bzl @@ -0,0 +1,71 @@ +"""Linting for Python using Aspects.""" + +# Hacked up from https://github.com/bazelbuild/rules_rust/blob/main/rust/private/clippy.bzl +# +# Usage: +# bazel build --aspects="//tools/flake8:flake8.bzl%flake8_aspect" --output_groups=flake8_checks +# +# Note that the build directive can be inserted to .bazelrc to make it part of the default behavior + +def _black_aspect_impl(target, ctx): + if hasattr(ctx.rule.attr, 'srcs'): + black = ctx.attr._black.files_to_run + config = ctx.attr._config.files.to_list()[0] + + files = [] + for src in ctx.rule.attr.srcs: + for f in src.files.to_list(): + if f.extension == "py": + files.append(f) + + if files: + report = ctx.actions.declare_file(ctx.label.name + ".black.report") + else: + return [] + + args = ["--check", "--output-file", report.path] + for f in files: + args.append(f.path) + + ctx.actions.run( + executable = black, + inputs = files, + tools = ctx.attr._config.files.to_list() + ctx.attr._black.files.to_list(), + arguments = args, + outputs = [report], + mnemonic = "Black", + ) + + return [ + OutputGroupInfo(black_checks = depset([report])) + ] + + return [] + + +black_aspect = aspect( + implementation = _black_aspect_impl, + attr_aspects = ['deps'], + attrs = { + '_black': attr.label(default=":black"), + '_config': attr.label( + default="//:setup.cfg", + executable=False, + allow_single_file=True + ), + } +) + + +def _black_rule_impl(ctx): + ready_targets = [dep for dep in ctx.attr.deps if "black_checks" in dir(dep[OutputGroupInfo])] + files = depset([], transitive = [dep[OutputGroupInfo].black_checks for dep in ready_targets]) + return [DefaultInfo(files = files)] + + +black = rule( + implementation = _black_rule_impl, + attrs = { + 'deps' : attr.label_list(aspects = [black_aspect]), + }, +) diff --git a/tools/flake8/flake8.bzl b/tools/flake8/flake8.bzl index 2e97623..9295d98 100644 --- a/tools/flake8/flake8.bzl +++ b/tools/flake8/flake8.bzl @@ -19,7 +19,7 @@ def _flake8_aspect_impl(target, ctx): files.append(f) if files: - report = ctx.actions.declare_file(ctx.label.name + ".report") + report = ctx.actions.declare_file(ctx.label.name + ".flake.report") else: return []