misc.python.materialize.cli.fmt
fmt — formats Rust, Python & Protobuf files in parallel.
1# Copyright Materialize, Inc. and contributors. All rights reserved. 2# 3# Use of this software is governed by the Business Source License 4# included in the LICENSE file at the root of this repository. 5# 6# As of the Change Date specified in that file, in accordance with 7# the Business Source License, use of this software will be governed 8# by the Apache License, Version 2.0. 9 10"""fmt — formats Rust, Python & Protobuf files in parallel.""" 11 12import argparse 13import json 14import math 15import os 16import subprocess 17 18from materialize import MZ_ROOT 19from materialize.parallel_task import TaskSpec, run_parallel 20 21# Rust sources live in more than one workspace, and `cargo metadata` only ever 22# reports the one it is pointed at. The `src/*/fuzz` cargo-fuzz crates attach to 23# the `test/cargo-fuzz` workspace, which the root workspace does not include, so 24# without a second invocation here they go unformatted entirely. 25RUST_MANIFESTS = ["Cargo.toml", "test/cargo-fuzz/Cargo.toml"] 26 27 28def main() -> int: 29 parser = argparse.ArgumentParser(prog="fmt") 30 parser.add_argument("--check", action="store_true") 31 args = parser.parse_args() 32 33 tasks: list[tuple[str, TaskSpec]] = [ 34 ("rustfmt", _rustfmt_fn(check=args.check)), 35 ("buf", _buf_cmd(check=args.check)), 36 ] 37 38 if not os.environ.get("MZDEV_NO_PYTHON"): 39 tasks += [ 40 ("black", _black_cmd(check=args.check)), 41 ("ruff", _ruff_cmd(check=args.check)), 42 ("ruff-dbt", _ruff_dbt_cmd(check=args.check)), 43 ] 44 45 return 1 if run_parallel(tasks, spinner_suffix="formatters") else 0 46 47 48def _rustfmt_fn(*, check: bool): 49 """Return a callable that runs cargo metadata + parallel rustfmt.""" 50 51 def run() -> tuple[bool, str]: 52 ncpus = os.cpu_count() or 8 53 54 kinds = {"lib", "bin", "bench", "test", "example", "proc-macro", "custom-build"} 55 # Keyed by edition: `gen` is an identifier in 2021 but a reserved keyword 56 # in 2024, so rustfmt cannot even parse a file at the wrong edition. 57 paths_by_edition: dict[str, list[str]] = {} 58 for manifest in RUST_MANIFESTS: 59 result = subprocess.run( 60 [ 61 "cargo", 62 "metadata", 63 "--no-deps", 64 "--format-version=1", 65 f"--manifest-path={manifest}", 66 ], 67 capture_output=True, 68 text=True, 69 ) 70 if result.returncode != 0: 71 return False, result.stderr.strip() 72 73 meta = json.loads(result.stdout) 74 for pkg in meta["packages"]: 75 for t in pkg["targets"]: 76 if kinds & set(t["kind"]): 77 paths_by_edition.setdefault(pkg["edition"], []).append( 78 t["src_path"] 79 ) 80 if not paths_by_edition: 81 return True, "" 82 83 # Split into batches and run rustfmt in parallel. 84 batches = [] 85 for edition, paths in paths_by_edition.items(): 86 batch_size = math.ceil(len(paths) / ncpus) 87 batches += [ 88 (edition, paths[i : i + batch_size]) 89 for i in range(0, len(paths), batch_size) 90 ] 91 92 cmd_base = ["rustfmt", "--config", "error_on_line_overflow=true"] 93 if check: 94 cmd_base.append("--check") 95 96 procs = [ 97 subprocess.Popen( 98 cmd_base + [f"--edition={edition}"] + batch, 99 stdout=subprocess.PIPE, 100 stderr=subprocess.STDOUT, 101 ) 102 for edition, batch in batches 103 ] 104 105 all_output = [] 106 all_ok = True 107 for proc in procs: 108 stdout, _ = proc.communicate() 109 if proc.returncode != 0: 110 all_ok = False 111 out = stdout.decode("utf-8").strip() 112 if out: 113 all_output.append(out) 114 115 return all_ok, "\n".join(all_output) 116 117 return run 118 119 120def _buf_cmd(*, check: bool) -> list[str]: 121 if check: 122 return ["buf", "format", "src", "--diff", "--exit-code"] 123 return ["buf", "format", "src", "-w"] 124 125 126def _black_cmd(*, check: bool) -> list[str]: 127 args = "--check --quiet" if check else "--quiet" 128 return [ 129 "bash", 130 "-c", 131 f'. misc/shlib/shlib.bash && git_files "*.py" | xargs bin/pyactivate -m black {args}', 132 ] 133 134 135def _ruff_cmd(*, check: bool) -> list[str]: 136 fix = "" if check else " --fix" 137 return [ 138 "bash", 139 "-c", 140 f'. misc/shlib/shlib.bash && git_files "*.py" | grep -v "^misc/dbt-materialize/" | xargs bin/pyactivate -m ruff{fix}', 141 ] 142 143 144def _ruff_dbt_cmd(*, check: bool) -> list[str]: 145 fix = "" if check else " --fix" 146 return [ 147 "bash", 148 "-c", 149 f'. misc/shlib/shlib.bash && git_files "misc/dbt-materialize/*.py" | xargs bin/pyactivate -m ruff --target-version=py38{fix}', 150 ] 151 152 153if __name__ == "__main__": 154 os.chdir(MZ_ROOT) 155 exit(main())
RUST_MANIFESTS =
['Cargo.toml', 'test/cargo-fuzz/Cargo.toml']
def
main() -> int:
29def main() -> int: 30 parser = argparse.ArgumentParser(prog="fmt") 31 parser.add_argument("--check", action="store_true") 32 args = parser.parse_args() 33 34 tasks: list[tuple[str, TaskSpec]] = [ 35 ("rustfmt", _rustfmt_fn(check=args.check)), 36 ("buf", _buf_cmd(check=args.check)), 37 ] 38 39 if not os.environ.get("MZDEV_NO_PYTHON"): 40 tasks += [ 41 ("black", _black_cmd(check=args.check)), 42 ("ruff", _ruff_cmd(check=args.check)), 43 ("ruff-dbt", _ruff_dbt_cmd(check=args.check)), 44 ] 45 46 return 1 if run_parallel(tasks, spinner_suffix="formatters") else 0