Skip to content

Commit d574bed

Browse files
authored
chore: add black pre-commit formatting (#20)
1 parent 5dfcde0 commit d574bed

8 files changed

Lines changed: 166 additions & 61 deletions

File tree

.github/workflows/test.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,20 @@ jobs:
2727
- uses: actions/checkout@v4
2828
with:
2929
persist-credentials: false
30+
- uses: actions/setup-python@v5
31+
with:
32+
python-version: '3.13'
3033
- uses: actions/setup-go@v5
3134
with:
3235
go-version: '1.22'
36+
- name: Install pre-commit
37+
run: python -m pip install pre-commit==3.8.0
3338
- name: Install shellcheck
3439
run: sudo apt-get update && sudo apt-get install -y shellcheck
3540
- name: Install actionlint
3641
run: go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.7
42+
- name: Run pre-commit
43+
run: pre-commit run --all-files
3744
- name: Run actionlint
3845
run: actionlint
3946
- name: Run shellcheck

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
minimum_pre_commit_version: "3.8.0"
2+
3+
default_language_version:
4+
python: python3
5+
6+
repos:
7+
- repo: https://github.com/psf/black
8+
rev: 25.9.0
9+
hooks:
10+
- id: black

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[tool.black]
2+
line-length = 120
3+
target-version = ["py313"]

scripts/cb_engine.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ def run_seed(repo: str, out: str, source_sha: str) -> None:
121121
print(f"Seeded static-analysis baseline in {out} (clusters: {summary or 'none'})")
122122

123123

124-
def run_head(repo: str, out: str, name: str, run_id: str, depth: int, base_ref: str, target_ref: str, source_sha: str) -> None:
124+
def run_head(
125+
repo: str, out: str, name: str, run_id: str, depth: int, base_ref: str, target_ref: str, source_sha: str
126+
) -> None:
125127
from codeboarding_workflows.analysis import BaselineUnavailableError, run_full, run_incremental
126128
from diagram_analysis.exceptions import IncrementalCacheMissingError
127129

@@ -243,7 +245,9 @@ def main(argv=None) -> int:
243245
elif args.cmd == "seed":
244246
run_seed(args.repo, args.out, args.source_sha)
245247
elif args.cmd == "head":
246-
run_head(args.repo, args.out, args.name, args.run_id, args.depth, args.base_ref, args.target_ref, args.source_sha)
248+
run_head(
249+
args.repo, args.out, args.name, args.run_id, args.depth, args.base_ref, args.target_ref, args.source_sha
250+
)
247251
elif args.cmd == "health":
248252
Path(args.issues_out).write_text(str(run_health(args.artifact_dir, args.repo, args.name)))
249253
elif args.cmd == "validate-base":

scripts/diff_to_mermaid.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
# GitHub's mermaid config caps (config.schema.yaml defaults; NOT raisable on
3131
# GitHub). Exceeding either renders a red error box with no diagram, so we stay
3232
# comfortably under and degrade to a changed-only / text fallback instead.
33-
MAX_EDGES = 480 # hard cap 500
34-
MAX_TEXT = 45_000 # hard cap 50000 chars
33+
MAX_EDGES = 480 # hard cap 500
34+
MAX_TEXT = 45_000 # hard cap 50000 chars
3535

3636
# Primer-ish fills that read on both light and dark GitHub backgrounds. White
3737
# label text is set explicitly so it survives dark mode.
@@ -89,8 +89,7 @@ def _has_method_changes(base: dict, current: dict) -> bool:
8989
base_by_file = _methods_by_file(base)
9090
current_by_file = _methods_by_file(current)
9191
return any(
92-
base_by_file.get(fp, set()) != current_by_file.get(fp, set())
93-
for fp in set(base_by_file) | set(current_by_file)
92+
base_by_file.get(fp, set()) != current_by_file.get(fp, set()) for fp in set(base_by_file) | set(current_by_file)
9493
)
9594

9695

@@ -122,7 +121,11 @@ def _diff_relations(base_rels: list, current_rels: list) -> list:
122121
continue
123122

124123
if len(base_group) == 1 and len(current_group) == 1:
125-
status = "unchanged" if (base_group[0].get("relation") or "") == (current_group[0].get("relation") or "") else "modified"
124+
status = (
125+
"unchanged"
126+
if (base_group[0].get("relation") or "") == (current_group[0].get("relation") or "")
127+
else "modified"
128+
)
126129
result.append({**current_group[0], "diff_status": status})
127130
continue
128131

@@ -227,9 +230,17 @@ def _sanitize(name: str) -> str:
227230
# node label and breaks the whole diagram, so escape the shape chars too — not
228231
# just ``#`` and ``"``.
229232
_ESC_MAP = {
230-
"&": "#amp;", '"': "#quot;", "<": "#lt;", ">": "#gt;",
231-
"[": "#91;", "]": "#93;", "(": "#40;", ")": "#41;",
232-
"{": "#123;", "}": "#125;", "|": "#124;",
233+
"&": "#amp;",
234+
'"': "#quot;",
235+
"<": "#lt;",
236+
">": "#gt;",
237+
"[": "#91;",
238+
"]": "#93;",
239+
"(": "#40;",
240+
")": "#41;",
241+
"{": "#123;",
242+
"}": "#125;",
243+
"|": "#124;",
233244
}
234245

235246

@@ -338,8 +349,7 @@ def touches(r: dict, side_id: str, side_name: str) -> bool:
338349
rels = [
339350
r
340351
for r in relations
341-
if r.get("diff_status") in CHANGED
342-
or (touches(r, "src_id", "src_name") and touches(r, "dst_id", "dst_name"))
352+
if r.get("diff_status") in CHANGED or (touches(r, "src_id", "src_name") and touches(r, "dst_id", "dst_name"))
343353
]
344354
return kept, rels
345355

@@ -379,8 +389,7 @@ def _count_changed_components(components: list) -> int:
379389
def _has_changed_relations(components: list, relations: list) -> bool:
380390
"""Recursively: is any relation (at any nesting level) added/modified/deleted?"""
381391
return _has_changes([], relations) or any(
382-
_has_changed_relations(c.get("components") or [], c.get("components_relations") or [])
383-
for c in components or []
392+
_has_changed_relations(c.get("components") or [], c.get("components_relations") or []) for c in components or []
384393
)
385394

386395

@@ -522,9 +531,18 @@ def main() -> int:
522531
p.add_argument("--out", required=True, type=Path, help="Where to write the ```mermaid block")
523532
p.add_argument("--direction", default="LR", choices=["LR", "TD", "TB", "RL", "BT"])
524533
p.add_argument("--changed-only", action="store_true", help="Render only changed components + incident edges")
525-
p.add_argument("--no-edge-labels", dest="edge_labels", action="store_false", help="Draw arrows without relation labels")
526-
p.add_argument("--render-depth", type=int, default=1, help="Component levels to draw: 1=top-level flat, 2=+one nesting level, ...")
527-
p.add_argument("--font-size", type=int, default=None, help="Node label font size in px (bigger label ⇒ bigger node)")
534+
p.add_argument(
535+
"--no-edge-labels", dest="edge_labels", action="store_false", help="Draw arrows without relation labels"
536+
)
537+
p.add_argument(
538+
"--render-depth",
539+
type=int,
540+
default=1,
541+
help="Component levels to draw: 1=top-level flat, 2=+one nesting level, ...",
542+
)
543+
p.add_argument(
544+
"--font-size", type=int, default=None, help="Node label font size in px (bigger label ⇒ bigger node)"
545+
)
528546
p.add_argument("--node-padding", type=int, default=None, help="Interior padding around each node label")
529547
p.add_argument("--node-spacing", type=int, default=None, help="Space between nodes in the same rank")
530548
p.add_argument("--rank-spacing", type=int, default=None, help="Space between ranks")

tests/test_build_cta.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def test_no_proxy_links_editor_to_https_listing_no_get_extension(self):
4343
def test_no_proxy_vscode_marketplace_https_no_banner_at_zero(self):
4444
out = bc.build_cta("", "o", "r", "1", repo_with()) # neither dir, no issues
4545
self.assertIn(
46-
"[**Open in VS Code →**](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding)", out
46+
"[**Open in VS Code →**](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding)",
47+
out,
4748
)
4849
self.assertNotIn("vscode:extension", out) # custom scheme stripped by GitHub
4950
self.assertNotIn("Get the extension", out)

tests/test_cb_engine.py

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@
1616
import cb_engine # noqa: E402
1717

1818
_STUBBED = [
19-
"codeboarding_workflows", "codeboarding_workflows.analysis",
20-
"diagram_analysis", "diagram_analysis.exceptions",
21-
"health", "health.models", "health.runner",
22-
"static_analyzer", "static_analyzer.analysis_cache",
19+
"codeboarding_workflows",
20+
"codeboarding_workflows.analysis",
21+
"diagram_analysis",
22+
"diagram_analysis.exceptions",
23+
"health",
24+
"health.models",
25+
"health.runner",
26+
"static_analyzer",
27+
"static_analyzer.analysis_cache",
2328
"static_analyzer.cluster_helpers",
2429
]
2530

@@ -85,46 +90,70 @@ def test_base_calls_run_full(self):
8590
def test_main_parses_depth_as_int(self):
8691
rf = _Rec()
8792
self._install(run_full=rf)
88-
cb_engine.main([
89-
"base",
90-
"--repo", "/repo",
91-
"--out", "/out",
92-
"--name", "myrepo",
93-
"--run-id", "rid-base",
94-
"--depth", "2",
95-
"--source-sha", "abc123",
96-
])
93+
cb_engine.main(
94+
[
95+
"base",
96+
"--repo",
97+
"/repo",
98+
"--out",
99+
"/out",
100+
"--name",
101+
"myrepo",
102+
"--run-id",
103+
"rid-base",
104+
"--depth",
105+
"2",
106+
"--source-sha",
107+
"abc123",
108+
]
109+
)
97110
self.assertEqual(rf.calls[0]["depth_level"], 2)
98111

99112
def test_main_sets_github_action_source(self):
100113
rf = _Rec()
101114
self._install(run_full=rf)
102115
with patch.dict(os.environ, {}, clear=True):
103-
cb_engine.main([
104-
"base",
105-
"--repo", "/repo",
106-
"--out", "/out",
107-
"--name", "myrepo",
108-
"--run-id", "rid-base",
109-
"--depth", "2",
110-
"--source-sha", "abc123",
111-
])
116+
cb_engine.main(
117+
[
118+
"base",
119+
"--repo",
120+
"/repo",
121+
"--out",
122+
"/out",
123+
"--name",
124+
"myrepo",
125+
"--run-id",
126+
"rid-base",
127+
"--depth",
128+
"2",
129+
"--source-sha",
130+
"abc123",
131+
]
132+
)
112133
self.assertEqual(os.environ["CODEBOARDING_SOURCE"], "github_action")
113134

114135
def test_main_rejects_invalid_depth(self):
115136
for depth in ("0", "4", "x"):
116137
with self.subTest(depth=depth):
117138
with redirect_stderr(StringIO()):
118139
with self.assertRaises(SystemExit):
119-
cb_engine.main([
120-
"base",
121-
"--repo", "/repo",
122-
"--out", "/out",
123-
"--name", "myrepo",
124-
"--run-id", "rid-base",
125-
"--depth", depth,
126-
"--source-sha", "abc123",
127-
])
140+
cb_engine.main(
141+
[
142+
"base",
143+
"--repo",
144+
"/repo",
145+
"--out",
146+
"/out",
147+
"--name",
148+
"myrepo",
149+
"--run-id",
150+
"rid-base",
151+
"--depth",
152+
depth,
153+
"--source-sha",
154+
"abc123",
155+
]
156+
)
128157

129158
def test_head_uses_incremental(self):
130159
ri, rf = _Rec(), _Rec()
@@ -239,7 +268,9 @@ def save(self, res, source_sha=None):
239268
log.append(("save", res, source_sha))
240269

241270
sa = _mod("static_analyzer", get_static_analysis=get_static_analysis)
242-
sa.cluster_helpers = _mod("static_analyzer.cluster_helpers", build_all_cluster_results=build_all_cluster_results)
271+
sa.cluster_helpers = _mod(
272+
"static_analyzer.cluster_helpers", build_all_cluster_results=build_all_cluster_results
273+
)
243274
sa.analysis_cache = _mod("static_analyzer.analysis_cache", StaticAnalysisCache=_Cache)
244275
return log, results
245276

@@ -288,9 +319,13 @@ def get(self):
288319

289320
_mod("health.models", Severity=Severity)
290321
_mod("health.runner", run_health_checks=lambda sa, repo_name, repo_path: report)
291-
_mod("health", )
322+
_mod(
323+
"health",
324+
)
292325
_mod("static_analyzer.analysis_cache", StaticAnalysisCache=_Cache)
293-
_mod("static_analyzer", )
326+
_mod(
327+
"static_analyzer",
328+
)
294329
return Severity
295330

296331
def test_counts_warning_and_critical(self):

tests/test_diff_to_mermaid.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,14 @@ def test_parallel_relation_deletion_is_not_label_modification(self):
7676

7777
class TestRender(unittest.TestCase):
7878
def _diff(self):
79-
base = {"components": [comp("A"), comp("B"), comp("Gone")], "components_relations": [rel("A", "B"), rel("A", "Gone")]}
80-
head = {"components": [comp("A", {"x.py": ["f"]}), comp("B"), comp("New")], "components_relations": [rel("A", "B"), rel("A", "New")]}
79+
base = {
80+
"components": [comp("A"), comp("B"), comp("Gone")],
81+
"components_relations": [rel("A", "B"), rel("A", "Gone")],
82+
}
83+
head = {
84+
"components": [comp("A", {"x.py": ["f"]}), comp("B"), comp("New")],
85+
"components_relations": [rel("A", "B"), rel("A", "New")],
86+
}
8187
return dm.build_diff(base, head)
8288

8389
def test_flat_default_has_no_subgraphs(self):
@@ -88,8 +94,14 @@ def test_flat_default_has_no_subgraphs(self):
8894
self.assertTrue(linkstyle_indices_in_range(text))
8995

9096
def test_nested_subgraphs_balanced_and_valid(self):
91-
base = {"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[rel("c1", "c2")])], "components_relations": []}
92-
head = {"components": [comp("P", subs=[comp("c1"), comp("c3")], subrels=[rel("c1", "c3")])], "components_relations": []}
97+
base = {
98+
"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[rel("c1", "c2")])],
99+
"components_relations": [],
100+
}
101+
head = {
102+
"components": [comp("P", subs=[comp("c1"), comp("c3")], subrels=[rel("c1", "c3")])],
103+
"components_relations": [],
104+
}
93105
text, _ = dm.render_mermaid(dm.build_diff(base, head), render_depth=2)
94106
sg = sum(1 for line in text.splitlines() if line.strip().startswith("subgraph "))
95107
en = sum(1 for line in text.splitlines() if line.strip() == "end")
@@ -164,16 +176,25 @@ def test_nested_method_change_highlights_collapsed_parent(self):
164176
self.assertIn("class n_P modified;", text)
165177

166178
def test_nested_relation_change_highlights_collapsed_parent(self):
167-
base = {"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[rel("c1", "c2", "uses")])], "components_relations": []}
168-
head = {"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[rel("c1", "c2", "calls")])], "components_relations": []}
179+
base = {
180+
"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[rel("c1", "c2", "uses")])],
181+
"components_relations": [],
182+
}
183+
head = {
184+
"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[rel("c1", "c2", "calls")])],
185+
"components_relations": [],
186+
}
169187
text, meta = dm.render_mermaid(dm.build_diff(base, head), render_depth=1)
170188
self.assertEqual(meta["n_changed"], 0)
171189
self.assertTrue(meta["changed"])
172190
self.assertIn("class n_P modified;", text)
173191

174192
def test_changed_only_keeps_nested_change(self):
175193
base = {"components": [comp("P", subs=[comp("c1"), comp("c2")], subrels=[])], "components_relations": []}
176-
head = {"components": [comp("P", subs=[comp("c1", {"x.py": ["f"]}), comp("c2")], subrels=[])], "components_relations": []}
194+
head = {
195+
"components": [comp("P", subs=[comp("c1", {"x.py": ["f"]}), comp("c2")], subrels=[])],
196+
"components_relations": [],
197+
}
177198
text, meta = dm.render_mermaid(dm.build_diff(base, head), render_depth=2, changed_only=True)
178199
self.assertIsNotNone(text)
179200
self.assertTrue(meta["changed"])
@@ -183,8 +204,14 @@ def test_changed_only_keeps_nested_change(self):
183204
self.assertNotIn('n_c2["c2"]', text)
184205

185206
def test_changed_only_prunes_unchanged_children_of_modified_parent(self):
186-
base = {"components": [comp("P", {"p.py": ["old"]}, subs=[comp("c1"), comp("c2")], subrels=[])], "components_relations": []}
187-
head = {"components": [comp("P", {"p.py": ["old", "new"]}, subs=[comp("c1"), comp("c2")], subrels=[])], "components_relations": []}
207+
base = {
208+
"components": [comp("P", {"p.py": ["old"]}, subs=[comp("c1"), comp("c2")], subrels=[])],
209+
"components_relations": [],
210+
}
211+
head = {
212+
"components": [comp("P", {"p.py": ["old", "new"]}, subs=[comp("c1"), comp("c2")], subrels=[])],
213+
"components_relations": [],
214+
}
188215
text, meta = dm.render_mermaid(dm.build_diff(base, head), render_depth=2, changed_only=True)
189216
self.assertIsNotNone(text)
190217
self.assertTrue(meta["changed"])

0 commit comments

Comments
 (0)