Every zip says whether its sides are the same length

`B905` over 56 sites, and the answer differs by idiom rather than by file.

21 were the sliding window `zip(xs, xs[1:])` — ragged by exactly one, on
purpose, so `strict=` is the wrong question entirely. Those become
`itertools.pairwise`, which is what they always meant and needs no flag: the
walk's SNR stops, the heat ladder, the route chains in `topology`/`records`,
every polyline in `mapcanvas`, and the graph's edge sets in `pathgraph`.

The other 35 pair sequences that must line up, so they now say `strict=True`
and a mismatch raises instead of silently truncating the longer side. Running
them under that assertion is what sorted the two groups: the first pass put
`strict=True` everywhere and 208 tests failed, all of them a sliding window.

One goes the other way. `mvt._decode_feature` reads tags off a network tile,
where an odd-length list means the tile is malformed — and this decoder answers
that with a shrug everywhere else (`if not geom_ints: return None`, bounds-
checked key and value indices). It keeps `strict=False`, now with a comment
saying why, so a bad basemap tile still can't crash the map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5 committed 2026-09-06 20:12:07 -04:00
1 parent 71b16ec7b7
commit 61b6387269
31 files changed
+80 -67

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
"""Diagnose one emoji for the MeshTerm terminal-aligned width machinery.
r"""Diagnose one emoji for the MeshTerm terminal-aligned width machinery.
Given an emoji on the command line, report its codepoints and how the three width
authorities measure it (wcwidth, Rich, prompt_toolkit), then classify it and print the
@@ -131,9 +131,9 @@ def measure(name, cols, rows, reps):
# How different are the two frames, in rows?
rows_a, rows_b = f_a.split("\n"), f_b.split("\n")
changed = sum(1 for x, y in zip(rows_a, rows_b) if x != y)
changed = sum(1 for x, y in zip(rows_a, rows_b, strict=True) if x != y)
rows_far = f_far.split("\n")
changed_far = sum(1 for x, y in zip(rows_a, rows_far) if x != y)
changed_far = sum(1 for x, y in zip(rows_a, rows_far, strict=True) if x != y)
return {
"compose": compose_ms,
"parse": parse_ms,
+1 -1
View File
@@ -576,7 +576,7 @@ def rank_contacts(
reasons=_reasons(sig, terms, weights),
)
for contact, sig, score, terms in zip(
contacts, gathered, scores, per_contact_terms
contacts, gathered, scores, per_contact_terms, strict=True
)
]
ranked.sort(key=lambda scored: (-scored.score, scored.contact.name.casefold()))
+3 -1
View File
@@ -234,7 +234,9 @@ def _decode_feature(buf: bytes, keys: list[str], values: list[Any]) -> Feature |
if not geom_ints:
return None
tags: dict[str, Any] = {}
for k, v in zip(tag_ints[0::2], tag_ints[1::2]):
# A malformed tile may carry an odd tag list; drop the dangling key rather than
# raise — the rest of this decoder is deliberately lenient about bad tiles.
for k, v in zip(tag_ints[0::2], tag_ints[1::2], strict=False):
if 0 <= k < len(keys) and 0 <= v < len(values):
tags[keys[k]] = values[v]
return Feature(geom_type=geom_type, rings=_decode_geometry(geom_ints), tags=tags)
+3 -2
View File
@@ -34,6 +34,7 @@ from __future__ import annotations
import math
from collections.abc import Callable, Sequence
from dataclasses import dataclass
from itertools import pairwise
from ..core.geo import haversine_km
from ..core.models import Hop, TraceResult
@@ -69,7 +70,7 @@ def first_repeated_edge(nodes: Sequence[str]) -> tuple[str, str] | None:
"""
walked = [n.strip().lower() for n in nodes if n and n.strip()]
seen: set[tuple[str, str]] = set()
for pair in zip(walked, walked[1:]):
for pair in pairwise(walked):
if pair in seen:
return pair
seen.add(pair)
@@ -306,7 +307,7 @@ def compute_walk_stats(
complete = True
leg_km: float | None = None
leg_link: tuple[str | None, str | None] | None = None
for i, (a, b) in enumerate(zip(points, points[1:])):
for i, (a, b) in enumerate(pairwise(points)):
if a is None or b is None:
complete = False
continue
+3 -3
View File
@@ -35,7 +35,7 @@ import math
import statistics
from dataclasses import dataclass, field
from datetime import datetime
from itertools import chain
from itertools import chain, pairwise
from ..core.models import Contact, utcnow
from ..persistence.repository import NeighbourLink, PacketPath, TracedPath
@@ -823,7 +823,7 @@ class MeshTopology:
weakest_strength: float | None = None
weakest_snr: float | None = None
samples = 0
for a, b in zip(chain, chain[1:]):
for a, b in pairwise(chain):
link = self.link(a, b)
if link is None:
return 0.0, None, samples
@@ -912,7 +912,7 @@ class MeshTopology:
def path_cost(path: list[str]) -> float:
total = 0.0
for a, b in zip(path, path[1:]):
for a, b in pairwise(path):
total += min((c for v, c in neighbors.get(a, ()) if v == b), default=math.inf)
return total
+2 -2
View File
@@ -431,7 +431,7 @@ def axis_caption(
taken: list[tuple[int, int]] = [] # placed [start, end) spans, in order
ticks: list[int] = [] # the chart column each label points at
ok = True
for frac, label in zip(fractions, labels):
for frac, label in zip(fractions, labels, strict=True):
if frac == 0.0:
start = 0
ref = 0
@@ -626,7 +626,7 @@ def axis_chart(
marks = y_axis_labels(peak, len(chart_rows), lo=floor)
label_w = label_w or max([1, *(len(mark) for mark in marks)])
out: list[Text] = []
for mark, row in zip(marks, chart_rows):
for mark, row in zip(marks, chart_rows, strict=True):
line = Text(f"{mark:>{label_w}} " + ("┤" if mark else "│"), style=style)
line.append_text(row)
line.append("├" if mark else "│", style=style)
+1 -1
View File
@@ -141,7 +141,7 @@ class MapScreen(Screen):
@property
def fkey_lane(self):
"""The PicoCalc lane: three ways to frame the view, then the zoom rocker.
r"""The PicoCalc lane: three ways to frame the view, then the zoom rocker.
The map is the one screen that repurposes the nav actions wholesale: PgUp/PgDn
zoom, Home reframes, and ``end`` is bound to nothing at all — so the shared lane's
+3 -2
View File
@@ -16,6 +16,7 @@ from __future__ import annotations
import unicodedata
from dataclasses import dataclass
from itertools import pairwise
from ..platforms import Platform, on_platform
from .marks import RGB, parse_hex # noqa: F401 - canonical home; re-exported for importers
@@ -177,7 +178,7 @@ class MapCanvas:
self, points: list[tuple[float, float]], color: RGB, priority: int
) -> None:
"""Rasterize a polyline through ``points`` (dot coordinates) as braille dots."""
for (x0, y0), (x1, y1) in zip(points, points[1:]):
for (x0, y0), (x1, y1) in pairwise(points):
self._segment(x0, y0, x1, y1, color, priority)
def _segment(
@@ -232,7 +233,7 @@ class MapCanvas:
edges: list[tuple[float, float, float, float]] = []
ys: list[float] = []
for ring in rings:
for (x0, y0), (x1, y1) in zip(ring, ring[1:]):
for (x0, y0), (x1, y1) in pairwise(ring):
if y0 != y1:
edges.append((x0, y0, x1, y1))
ys.extend((y0, y1))
+1 -1
View File
@@ -522,7 +522,7 @@ async def _menu_loop(ctx: AppContext, session: TuiSession) -> None:
name_w = max((label.cell_len for label in labels), default=0)
items: list = []
current_category: str | None = None
for tool, row in zip(tools, labels):
for tool, row in zip(tools, labels, strict=True):
if tool.category != current_category:
current_category = tool.category
items.append(section_heading(current_category))
+1 -1
View File
@@ -333,7 +333,7 @@ def column_header(lanes: Sequence[Lane], width: int, *, indent: int = 2) -> str:
def line() -> str:
out = " " * indent
for lane, form in zip(lanes, picked):
for lane, form in zip(lanes, picked, strict=True):
label = lane.forms[form]
out += label + " " * max(0, lane.width - cell_len(label))
return out
+2 -1
View File
@@ -78,6 +78,7 @@ import re
import time
from dataclasses import dataclass, field, replace
from datetime import datetime
from itertools import pairwise
from typing import TYPE_CHECKING
from rich.cells import cell_len
@@ -2013,7 +2014,7 @@ def _route_is_fresh(topo, hops: tuple[str, ...], target: str, now: datetime) ->
is treated as fresh — we have no age to hold against it.
"""
chain = [topo.self_id, *hops, target]
for a, b in zip(chain, chain[1:]):
for a, b in pairwise(chain):
link = topo.link(a, b)
if link is None:
return False
+9 -9
View File
@@ -104,7 +104,7 @@ from __future__ import annotations
from collections.abc import Callable, Sequence
from dataclasses import dataclass
from itertools import permutations, product
from itertools import pairwise, permutations, product
from math import ceil
from .mapcanvas import MapCanvas
@@ -375,7 +375,7 @@ def revisited_hops(hops: Sequence[str]) -> tuple[str, ...]:
def _split_revisits(layers: Sequence[PathLayer]) -> list[PathLayer]:
"""Give every revisit *within* a path its own node id, so no walk folds into a cycle.
r"""Give every revisit *within* a path its own node id, so no walk folds into a cycle.
The k-th occurrence of a hop in one layer becomes ``hop\\x00#k`` (the first keeps the bare
id), which leaves the flow acyclic and lets the balanced rank spread the walk evenly again.
@@ -555,7 +555,7 @@ def render_path_graph(
if node not in seen:
seen.add(node)
ordered_nodes.append(node)
edges.update(zip(seq, seq[1:]))
edges.update(pairwise(seq))
xfrac = _balanced_x(ordered_nodes, edges)
@@ -667,9 +667,9 @@ def render_path_graph(
# dropping out where it overlaps another. A two-way pair (``bidir``, above) draws as the
# one honest vertical rather than a lane change.
edge_style: dict[frozenset[str], tuple[int, RGB]] = {}
for layer, seq in sorted(zip(drawn, seqs), key=lambda ls: _draw_rank(ls[0])):
for layer, seq in sorted(zip(drawn, seqs, strict=True), key=lambda ls: _draw_rank(ls[0])):
rank = _draw_rank(layer)
for u, v in zip(seq, seq[1:]):
for u, v in pairwise(seq):
key = frozenset((u, v))
prev = edge_style.get(key)
if prev is None or rank > prev[0]:
@@ -731,7 +731,7 @@ def _route(
(xu, yu), (xv, yv) = (xv, yv), (xu, yu)
anchors: list[tuple[float, float]] = [(xu, yu), *sorted(vias), (xv, yv)]
pts: list[tuple[float, float]] = [anchors[0]]
for (xa, ya), (xb, yb) in zip(anchors, anchors[1:]):
for (xa, ya), (xb, yb) in pairwise(anchors):
if ya == yb:
pts.append((xb, yb))
continue
@@ -845,7 +845,7 @@ def bidir_clusters(sequences: Sequence[tuple[str, ...]]) -> list[tuple[str, ...]
Returns each cluster's member ids in first-appearance order (endpoints excluded); a lone
node or the tidy two-node pair is not a cluster and is not returned.
"""
edges = {pair for seq in sequences for pair in zip(seq, seq[1:])}
edges = {pair for seq in sequences for pair in pairwise(seq)}
ordered = list(dict.fromkeys(node for seq in sequences for node in seq))
rep = _merge_bidir_pairs(ordered, edges)
groups: dict[str, list[str]] = {}
@@ -1047,7 +1047,7 @@ def _bypass_vias(
level: list[tuple[frozenset[str], float, int, int]] = []
seen: set[frozenset[str]] = set()
for seq in seqs:
for u, v in zip(seq, seq[1:]):
for u, v in pairwise(seq):
key = frozenset((u, v))
if key in seen or key in bidir:
continue
@@ -1328,7 +1328,7 @@ def _balance_sides(
best_assign: dict[int, int] | None = None
best_key: tuple[int, int, int] | None = None
for combo in product((-1, 1), repeat=len(routes)):
assign = dict(zip(routes, combo))
assign = dict(zip(routes, combo, strict=True))
if any(assign[d] != assign[s] for d, s in tie.items()): # a nest split off its sibling
continue
deep_above, deep_below = flanks(assign)
+1 -1
View File
@@ -211,7 +211,7 @@ async def _rank(ctx: AppContext, contacts: list[Contact]) -> list[ScoredContact]
watch = getattr(ctx, "watch_store", None)
admin = getattr(ctx, "admin_store", None)
for contact, node in zip(contacts, nodes):
for contact, node in zip(contacts, nodes, strict=True):
found = signals.get(node)
if found is None:
continue
+2 -2
View File
@@ -446,10 +446,10 @@ class RecordDialog(Screen):
canvas.draw_line(closed, _AREA_TONE, priority=0)
# Hops first, then our own node last, so the star always wins its cell — a hop that
# projects onto the same cell can never hide us.
for v, (dx, dy) in zip(verts, ring):
for v, (dx, dy) in zip(verts, ring, strict=True):
if not v.is_self:
canvas.marker(int(round(dx)), int(round(dy)), v.glyph, v.color)
for v, (dx, dy) in zip(verts, ring):
for v, (dx, dy) in zip(verts, ring, strict=True):
if v.is_self:
canvas.marker(int(round(dx)), int(round(dy)), v.glyph, v.color)
return canvas.to_ansi_lines()
+4 -4
View File
@@ -668,7 +668,7 @@ def _ticks_fit(picks: list[int], labels: list[str], centers: list[int], chars: i
label would drop ticks a variable-width axis has ample room for.
"""
last_end = -_TICK_GAP
for i, label in zip(picks, labels):
for i, label in zip(picks, labels, strict=True):
cell = max(0, min(chars - 1, centers[i]))
start = max(0, min(chars - len(label), cell - len(label) // 2))
if start < last_end + _TICK_GAP:
@@ -707,9 +707,9 @@ def _fit_ticks(
continue # rounding fused two picks — this many will not fit cleanly
labels = label_of(picks)
if _ticks_fit(picks, labels, centers, chars):
return list(zip((centers[i] for i in picks), labels))
return list(zip((centers[i] for i in picks), labels, strict=True))
picks = [0] if n == 1 else [0, n - 1]
return list(zip((centers[i] for i in picks), label_of(picks)))
return list(zip((centers[i] for i in picks), label_of(picks), strict=True))
def _day_ticks(shown: list, chars: int) -> list[tuple[int, str]]:
@@ -912,7 +912,7 @@ def _day_columns(values: list[int], chars: int) -> list:
:data:`~meshterm.ui.braillechart.GAP` marks a day-boundary notch).
"""
out: list = [GAP] * (chars * 2)
for value, (start, width) in zip(values, _day_spans(len(values), chars)):
for value, (start, width) in zip(values, _day_spans(len(values), chars), strict=True):
out[start : start + width] = [value] * width
return out
+1 -1
View File
@@ -1177,7 +1177,7 @@ class TraceScreen(Screen):
rows.append(spin)
elif self._status:
rows.append(Text(self._status, style="err"))
for number, trace in zip(range(len(self._traces), 0, -1), reversed(self._traces)):
for number, trace in zip(range(len(self._traces), 0, -1), reversed(self._traces), strict=True):
rows.append(self._trace_row(number, trace))
return Group(*rows)
+1 -1
View File
@@ -372,7 +372,7 @@ def _banner_lines(banner: Sequence[str], cols: int) -> list[str]:
return []
widths = [cell_len(Text.from_ansi(row).plain) for row in banner]
width = max(widths)
padded = [fold_text(row) + " " * (width - w) for row, w in zip(banner, widths)]
padded = [fold_text(row) + " " * (width - w) for row, w in zip(banner, widths, strict=True)]
return _center(padded, cols)
+3 -3
View File
@@ -267,7 +267,7 @@ def _changed_rows(before: str, after: str) -> list[int] | None:
old, new = before.split("\n"), after.split("\n")
if len(old) != len(new):
return None
return [i for i, (a, b) in enumerate(zip(old, new)) if a != b]
return [i for i, (a, b) in enumerate(zip(old, new, strict=True)) if a != b]
def _has_wide_glyph(text: str) -> bool:
@@ -680,7 +680,7 @@ class TuiSession:
self._scrub_columns(cols - count, cols)
def _scrub_rows(self, rows: Sequence[int]) -> bool:
"""Force prompt_toolkit to rewrite these whole terminal rows on the next diff.
r"""Force prompt_toolkit to rewrite these whole terminal rows on the next diff.
The row-wise twin of :meth:`_scrub_columns`, and the cheap form of the wide-glyph
repaint (:meth:`_emit`): every column of each listed row is sentinelled, so pt finds
@@ -1539,7 +1539,7 @@ class TuiSession:
return bool(self._float_layers())
def _emit(self, text: str, layer: str = "base") -> ANSI:
"""Wrap a composed frame as prompt_toolkit :class:`ANSI`, repainting whole *rows*
r"""Wrap a composed frame as prompt_toolkit :class:`ANSI`, repainting whole *rows*
when it holds a glyph the terminal may draw narrower than pt reserves for it.
prompt_toolkit paints differentially: it rewrites only the cells that changed since
+4 -3
View File
@@ -57,6 +57,7 @@ from __future__ import annotations
import math
from collections import deque
from datetime import datetime
from itertools import pairwise
from typing import TYPE_CHECKING
from rich.cells import cell_len
@@ -201,10 +202,10 @@ def _snr_rgb(snr: float | None) -> RGB:
if snr >= _SNR_STOPS[-1][0]:
return _SNR_STOPS[-1][1]
(x0, c0), (x1, c1) = next(
(lo, hi) for lo, hi in zip(_SNR_STOPS, _SNR_STOPS[1:]) if lo[0] <= snr <= hi[0]
(lo, hi) for lo, hi in pairwise(_SNR_STOPS) if lo[0] <= snr <= hi[0]
)
f = (snr - x0) / (x1 - x0)
return tuple(round(a + (b - a) * f) for a, b in zip(c0, c1)) # type: ignore[return-value]
return tuple(round(a + (b - a) * f) for a, b in zip(c0, c1, strict=True)) # type: ignore[return-value]
def _scaled(rgb: RGB, factor: float) -> RGB:
@@ -1039,7 +1040,7 @@ class WalkScreen(Screen):
# marker, in cells, doubled into dot space.
rx = max(floor, 2 * (width - 2 - _FAN_BASE_LABEL_W)) - ax
phi = _FAN_HALF_ANGLE * min(1.0, (len(keys) - 1) / 5.0)
for i, (node, row, label) in enumerate(zip(keys, rows, labels)):
for i, (node, row, label) in enumerate(zip(keys, rows, labels, strict=True)):
angle = 0.0 if len(keys) == 1 else -phi + (2 * phi) * i / (len(keys) - 1)
x = ax + rx * math.cos(angle * _FAN_X_FLATTEN)
room = 2 * (width - 2 - min(cell_len(label), _FAN_LABEL_W))
+3 -2
View File
@@ -6,6 +6,7 @@ import math
from collections.abc import Callable, Sequence
from dataclasses import dataclass, field
from datetime import datetime
from itertools import pairwise
from typing import TYPE_CHECKING
from rich import box
@@ -935,10 +936,10 @@ def _recency_gradient(secs: float | None) -> str:
r, g, b = _HEAT_STOPS[-1][1]
else:
(x0, c0), (x1, c1) = next(
(lo, hi) for lo, hi in zip(_HEAT_STOPS, _HEAT_STOPS[1:]) if lo[0] <= x <= hi[0]
(lo, hi) for lo, hi in pairwise(_HEAT_STOPS) if lo[0] <= x <= hi[0]
)
f = (x - x0) / (x1 - x0)
r, g, b = (round(a + (bb - a) * f) for a, bb in zip(c0, c1))
r, g, b = (round(a + (bb - a) * f) for a, bb in zip(c0, c1, strict=True))
return f"#{r:02x}{g:02x}{b:02x}"
+2 -1
View File
@@ -495,7 +495,8 @@ def test_title_preview_column_aligns_regardless_of_label_length(repo: Repository
def test_title_leads_with_openness_glyph(repo: Repository) -> None:
"""Channel rows lead with an openness glyph: # name-derived, 🌐 fixed-key public, 🔒 private."""
ctx = _RowCtx(repo)
head = lambda conv: _title(ctx, conv, {}, _NO_KEYS).plain.split(" ", 1)[0]
def head(conv):
return _title(ctx, conv, {}, _NO_KEYS).plain.split(" ", 1)[0]
named = Conversation(
label="#general", is_channel=True, channel_id="c0", secret=derive_secret("#general")
)
+1 -1
View File
@@ -41,7 +41,7 @@ def _rank(*specs) -> list: # noqa: ANN001
"""Rank ``(name, signal-overrides)`` pairs, returning the scored contacts strongest first."""
contacts = [_contact(name) for name, _ in specs]
signals = {}
for contact, (_, overrides) in zip(contacts, specs):
for contact, (_, overrides) in zip(contacts, specs, strict=True):
node = contact.public_key[:12]
signals[node] = ContactSignals(node=node, **{**_BASE, **overrides})
return rank_contacts(contacts, signals)
+3 -3
View File
@@ -615,7 +615,7 @@ def test_render_map_brightens_piled_markers() -> None:
crowd = [MapMarker(f"n{i}", 45.50, -73.57) for i in range(8)]
piled = _glyph_color(render_map(vp, {}, crowd), "●")
# Washed toward white: every channel is brighter than the base cyan.
assert all(p > b for p, b in zip(piled, base))
assert all(p > b for p, b in zip(piled, base, strict=True))
assert piled != base
@@ -684,7 +684,7 @@ def test_the_ghost_ground_lands_where_the_pan_put_it() -> None:
assert len(before) == len(after)
matched = sum(
1
for old, new in zip(before, after)
for old, new in zip(before, after, strict=True)
if old[shift:].rstrip() and new.rstrip() == old[shift:].rstrip()
)
assert matched > len(before) // 2, "the ground did not slide with the view"
@@ -906,7 +906,7 @@ def test_decoded_layers_survive_a_round_trip() -> None:
assert restored is not None
assert [(l.name, l.extent) for l in restored] == [(l.name, l.extent) for l in original]
for before, after in zip(original, restored):
for before, after in zip(original, restored, strict=True):
assert after.features == before.features
+4 -4
View File
@@ -56,7 +56,7 @@ def test_menu_rows_align_descriptions_in_display_cells() -> None:
from rich.cells import cell_len
starts = {cell_len(r.title.plain[: r.title.plain.index(d)]) for r, d in
zip(rows, ["Restart it", "Second"])}
zip(rows, ["Restart it", "Second"], strict=True)}
assert len(starts) == 1 # one shared description column
@@ -219,14 +219,14 @@ def test_marked_label_lines_up_a_mixed_list_when_told_its_lane() -> None:
marked_label(_WIDE, "View archived contacts", "", lane=lane),
]
starts = {cell_len(row.plain[: row.plain.index(word)])
for row, word in zip(rows, ("Purge", "View"))}
for row, word in zip(rows, ("Purge", "View"), strict=True)}
assert starts == {lane + 1}
# Without the lane each mark measures itself, which is right for a list whose rows all
# lead with the same icon — and is exactly what misaligns a mixed one.
solo = [marked_label(_NARROW, "A", "err"), marked_label(_WIDE, "B", "")]
assert len({cell_len(row.plain[: row.plain.index(letter)])
for row, letter in zip(solo, ("A", "B"))}) == 2
for row, letter in zip(solo, ("A", "B"), strict=True)}) == 2
def test_the_main_menu_starts_every_title_in_the_same_cell() -> None:
@@ -252,7 +252,7 @@ def test_the_main_menu_starts_every_title_in_the_same_cell() -> None:
assert widths == {1, 2}, "a menu of one icon width would prove nothing"
starts = {
label.cell_len - cell_len(tool.title or tool.name)
for tool, label in zip(tools, labels)
for tool, label in zip(tools, labels, strict=True)
}
assert len(starts) == 1, "a title starts a column early"
+7 -6
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import re
from itertools import pairwise
import pytest
@@ -197,7 +198,7 @@ def test_revisited_path_folds_into_a_cycle_that_piles_its_relays() -> None:
hops = ("7f", "c1", "8e", "da", "ee", "c1", "27")
seq = (SRC_NODE, *hops, DST_NODE)
ordered = list(dict.fromkeys(seq))
x = _balanced_x(ordered, set(zip(seq, seq[1:])))
x = _balanced_x(ordered, set(pairwise(seq)))
# Evenly spread, these eight nodes would sit 1/7 apart and the four loop members would
# span three of those gaps; folded, the whole loop fits inside a single one.
loop = [x[node] for node in ("c1", "8e", "da", "ee")]
@@ -217,12 +218,12 @@ def test_allow_duplicate_nodes_keeps_x_strictly_rising_along_the_walk() -> None:
[layer] = _split_revisits([PathLayer(hops, WHITE, 3)])
seq = (SRC_NODE, *layer.hops, DST_NODE)
ordered = list(dict.fromkeys(seq))
x = _balanced_x(ordered, set(zip(seq, seq[1:])))
x = _balanced_x(ordered, set(pairwise(seq)))
walked = [x[node] for node in seq]
assert walked == sorted(walked) and len(set(walked)) == len(walked)
assert x[SRC_NODE] == 0.0 and x[DST_NODE] == 1.0
# A single walk with nothing else to balance against spreads dead evenly end to end.
gaps = [b - a for a, b in zip(walked, walked[1:])]
gaps = [b - a for a, b in pairwise(walked)]
assert max(gaps) - min(gaps) < 1e-9
@@ -390,7 +391,7 @@ def _packed_lanes(layers, width=60, max_rows=15): # noqa: ANN001
if node not in seen:
seen.add(node)
ordered.append(node)
edges.update(zip(seq, seq[1:]))
edges.update(pairwise(seq))
xfrac = _balanced_x(ordered, edges)
best = max(range(len(drawn)), key=lambda j: drawn[j].priority)
owner: dict[str, int] = {}
@@ -632,7 +633,7 @@ def _vias_for(layers, width=60, max_rows=15): # noqa: ANN001
if node not in seen:
seen.add(node)
ordered.append(node)
edges.update(zip(seq, seq[1:]))
edges.update(pairwise(seq))
xfrac = _balanced_x(ordered, edges)
best = max(range(len(drawn)), key=lambda j: drawn[j].priority)
owner: dict[str, int] = {}
@@ -774,7 +775,7 @@ def test_bidir_pair_does_not_jam_a_sibling_relay_against_the_origin() -> None:
]
seqs = [(SRC_NODE, *layer.hops, DST_NODE) for layer in layers]
ordered = list(dict.fromkeys(n for seq in seqs for n in seq))
edges = {pair for seq in seqs for pair in zip(seq, seq[1:])}
edges = {pair for seq in seqs for pair in pairwise(seq)}
x = _balanced_x(ordered, edges)
assert x[SRC_NODE] == 0.0 and x[DST_NODE] == 1.0
assert x["cc"] == pytest.approx(1 / 3) # its natural spot, not jammed toward 0
+1 -1
View File
@@ -57,7 +57,7 @@ def _char_styles(text) -> list[tuple[str, str]]: # noqa: ANN001
for span in text.spans:
for i in range(span.start, span.end):
styles[i] = str(span.style)
return list(zip(text.plain, styles))
return list(zip(text.plain, styles, strict=True))
def test_plain_mode_matches_the_app_wide_arrow_presentation() -> None:
+2 -1
View File
@@ -8,6 +8,7 @@ from __future__ import annotations
import re
from datetime import timedelta
from itertools import pairwise
from pathlib import Path
from types import SimpleNamespace
@@ -344,7 +345,7 @@ def test_day_columns_widths_differ_by_at_most_one_dot_and_interleave() -> None:
notches = [i for i, c in enumerate(cols) if c is GAP] # one per day boundary
assert len(notches) == 12
edges = [-1, *notches, len(cols)] # each bar runs between two notches
widths = [b - a - 1 for a, b in zip(edges, edges[1:])]
widths = [b - a - 1 for a, b in pairwise(edges)]
assert len(widths) == 13 and set(widths) == {3, 4}
first_wide = widths.index(4)
last_wide = len(widths) - 1 - widths[::-1].index(4)
+2 -2
View File
@@ -1241,7 +1241,7 @@ def test_device_picker_builds_aligned_columns(tmp_path) -> None:
assert rows[-2].strip().endswith("Add a network device…") # no caveat tag trailing it
assert rows[-1].strip().endswith("Quit")
device_rows = rows[:2]
assert all(port in row for port, row in zip(("COM5", "/dev/ttyUSB0"), device_rows))
assert all(port in row for port, row in zip(("COM5", "/dev/ttyUSB0"), device_rows, strict=True))
assert device_rows[0].index("COM5") == device_rows[1].index("/dev/ttyUSB0")
@@ -2765,7 +2765,7 @@ def test_the_rows_a_dialog_does_not_reach_come_back_unchanged() -> None:
session.push(ButtonDialog("Sure?", [("Cancel", 0), ("Yes", 1)]))
second = session._plain_frame().split("\n")
unchanged = sum(1 for a, b in zip(first, second) if a == b)
unchanged = sum(1 for a, b in zip(first, second, strict=True) if a == b)
assert unchanged >= 10, "the box should only rewrite the rows it covers"
+1 -1
View File
@@ -135,7 +135,7 @@ def test_walk_graph_labels_names_in_full_when_the_canvas_has_room() -> None:
]
topo = MeshTopology(US, contacts=long_names)
when = utcnow()
for c, snr in zip(long_names, (8.0, 1.0)):
for c, snr in zip(long_names, (8.0, 1.0), strict=True):
topo.add_walk([topo.self_id, topo.canonical(c.public_key)], snrs=[snr], when=when,
source="trace")
screen = WalkScreen(
+6 -3
View File
@@ -238,7 +238,8 @@ def test_alert_row_hues_the_label_by_resolved_key(tmp_path: Path) -> None:
from meshterm.ui.theme import name_style
from meshterm.ui.watchtower_screen import _alert_lanes
key_of = lambda label: "d4" * 6 if label == "Roof" else None
def key_of(label):
return "d4" * 6 if label == "Roof" else None
alert = Alert(ident=1, when=utcnow(), kind="silence", label="Roof", message="quiet")
row = _alert_lanes(alert, key_of)
at = row.plain.index("Roof")
@@ -261,8 +262,10 @@ def test_alert_row_leads_node_name_with_type_glyph(tmp_path: Path) -> None:
from meshterm.ui.watchtower_screen import _alert_lanes
from meshterm.ui.widgets import _DEFAULT_GLYPH, _NODE_GLYPHS
key_of = lambda label: None
type_of = lambda label: 2 if label == "Roof" else None # Roof advertises as a repeater
def key_of(label):
return None
def type_of(label):
return 2 if label == "Roof" else None # Roof advertises as a repeater
glyph, glyph_style = _NODE_GLYPHS[2]
alert = Alert(ident=1, when=utcnow(), kind="silence", label="Roof", message="quiet")