Every line fits the hundred columns it was configured for

`E501` across 69 lines, wrapped rather than exempted.

The prose ones — module docstrings in `menu`, `node_detail_screen`,
`pathline`, `pathgraph`, `walk_screen`, `trace_runner` and `test_about` — are
reflowed as paragraphs, so the block keeps its shape instead of gaining one
short orphan line. Nothing they say changed.

The code ones mostly gained a line break inside a call. Where a break would
have read worse than a name, the sub-expression got one instead: `reach` in
`topology`'s quality clamp, `plain_cells` in `pathline`, `numbering` in the
trace list, `listed` in `flash.py`'s refusal, and a small `shape`/`is_strip`
helper in two tests that were comparing or matching the same shape twice on one
line. Four docstring summaries lost a word they were not using.

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:22:52 -04:00
1 parent ca0023f5b7
commit b5006cc415
39 files changed
+188 -81

No files matched your search

+5 -1
View File
@@ -18,5 +18,9 @@ def copyright_notice() -> str:
never drift apart.
"""
year = datetime.now().year
span = str(COPYRIGHT_START_YEAR) if year <= COPYRIGHT_START_YEAR else f"{COPYRIGHT_START_YEAR}-{year}"
span = (
str(COPYRIGHT_START_YEAR)
if year <= COPYRIGHT_START_YEAR
else f"{COPYRIGHT_START_YEAR}-{year}"
)
return f"Copyright © {span} {__author__}"
+5 -1
View File
@@ -300,7 +300,11 @@ def _telemetry_apply(field_name: str) -> Callable[[Device, Any, dict], Awaitable
"""Build an apply that updates one telemetry mode, preserving the others."""
async def apply(device: Device, value: Any, snapshot: dict) -> None:
base = value if field_name == "telemetry_mode_base" else snapshot.get("telemetry_mode_base", 0)
base = (
value
if field_name == "telemetry_mode_base"
else snapshot.get("telemetry_mode_base", 0)
)
loc = value if field_name == "telemetry_mode_loc" else snapshot.get("telemetry_mode_loc", 0)
env = value if field_name == "telemetry_mode_env" else snapshot.get("telemetry_mode_env", 0)
await device.set_telemetry_modes(int(base or 0), int(loc or 0), int(env or 0))
+3 -1
View File
@@ -625,7 +625,9 @@ class TraceResult:
origin = device_label
for hop in self.hops:
destination = hop.node or device_label
edges.append(HopEdge(index=hop.index, origin=origin, destination=destination, snr=hop.snr))
edges.append(
HopEdge(index=hop.index, origin=origin, destination=destination, snr=hop.snr)
)
origin = destination
return edges
+5 -2
View File
@@ -213,7 +213,8 @@ class Link:
if snr is not None:
span = _SNR_CEIL_DB - _SNR_FLOOR_DB
frac = (snr - _SNR_FLOOR_DB) / span
quality = min(_QUALITY_MAX, max(_QUALITY_MIN, _QUALITY_MIN + frac * (_QUALITY_MAX - _QUALITY_MIN)))
reach = _QUALITY_MIN + frac * (_QUALITY_MAX - _QUALITY_MIN)
quality = min(_QUALITY_MAX, max(_QUALITY_MIN, reach))
return evidence * recency * quality
@@ -718,7 +719,9 @@ class MeshTopology:
out.sort(key=lambda s: (-s.strength, s.node))
return out
def scenarios(self, target: str, *, device_route: tuple[str, ...] | None = None) -> list[PathScenario]:
def scenarios(
self, target: str, *, device_route: tuple[str, ...] | None = None
) -> list[PathScenario]:
"""Rank the candidate outbound routes for reaching ``target``.
Three families, deduplicated in this priority order:
+3 -2
View File
@@ -327,8 +327,9 @@ def trace_timeout(hop_count: int) -> float:
:data:`TRACE_TIMEOUT_CEILING_S`.
``hop_count`` is the number of hops in the *transmitted* path, which already includes
the mirrored return leg (see :meth:`~meshterm.core.connection.MeshCoreDevice._trace_path_to_contact`),
so it maps one-to-one onto relay transmissions and must not be doubled here.
the mirrored return leg (see
:meth:`~meshterm.core.connection.MeshCoreDevice._trace_path_to_contact`), so it maps
one-to-one onto relay transmissions and must not be doubled here.
Args:
hop_count: Hops in the walked path (return leg included). ``0`` (or less) means the
+2 -1
View File
@@ -59,7 +59,8 @@ class ChannelsTool(Tool):
changes = await manage_channels(ctx)
message = (
f"[ok]✓[/ok] applied [brand]{changes}[/brand] channel change{'' if changes == 1 else 's'}"
f"[ok]✓[/ok] applied [brand]{changes}[/brand] "
f"channel change{'' if changes == 1 else 's'}"
if changes
else None
)
+3 -1
View File
@@ -233,7 +233,9 @@ class CourierTool(Tool):
help=self.help, no_args_is_help=True, rich_markup_mode="rich"
)
@courier_app.command("queue", help="Queue a message for delivery when the contact is next heard")
@courier_app.command(
"queue", help="Queue a message for delivery when the contact is next heard"
)
def _queue_cmd(
contact: str = typer.Argument(..., help="The recipient contact's name"),
text: list[str] = typer.Argument(..., help="The message to queue"),
+3 -1
View File
@@ -285,7 +285,9 @@ class TraceTool(Tool):
"""
from ..cli import run_tool_command
@app.command(name=self.name, help="Run a single path trace to a target and show per-hop SNR")
@app.command(
name=self.name, help="Run a single path trace to a target and show per-hop SNR"
)
def _trace(
target: str = typer.Option(..., "--target", "-t", help="Target node name/prefix"),
path: str | None = typer.Option(
+3 -1
View File
@@ -272,7 +272,9 @@ class TxOptimizeTool(Tool):
if int(params.get("samples", 3)) > MAX_SAMPLES:
ctx.ui.note(f"[warn]capping at {MAX_SAMPLES} traces per level[/warn]")
ctx.ui.note(f"[muted]logging in to[/muted] [brand]{admin_node.name}[/brand] [muted]…[/muted]")
ctx.ui.note(
f"[muted]logging in to[/muted] [brand]{admin_node.name}[/brand] [muted]…[/muted]"
)
await self._login(ctx, admin_node, params)
ctx.ui.note(
+2 -1
View File
@@ -181,7 +181,8 @@ def _state_intensity(row: str) -> str:
bright = False
elif part == "1":
bright = True
if any(_DIM_FG.fullmatch(p) for p in parts) and not any(p in _SAYS_INTENSITY for p in parts):
dim = any(_DIM_FG.fullmatch(p) for p in parts)
if dim and not any(p in _SAYS_INTENSITY for p in parts):
out.append("\x1b[" + ";".join(["1" if bright else "22"] + parts) + "m")
else:
out.append(found.group())
+3 -1
View File
@@ -557,7 +557,9 @@ def _draw_tile(frame: _Frame, layers: list[Layer], z: int, x: int, y: int) -> No
if len(ring) >= 2:
vp.draw_line(project(ring, waterway.extent), rgb, prio)
if feat.name and not frame.coarse:
_add_line_label(frame, feat.rings, waterway.extent, z, x, y, feat.name, _WATER_LABEL)
_add_line_label(
frame, feat.rings, waterway.extent, z, x, y, feat.name, _WATER_LABEL
)
# Roads / rail.
transportation = by_name.get("transportation")
+3 -2
View File
@@ -1030,8 +1030,9 @@ async def _auto_reconnect(ctx: AppContext, dialog: ReconnectDialog) -> None:
The dead link is released up front (:meth:`~meshterm.context.AppContext.release_link`) so
the peripheral is free to advertise while we wait for it. Each transport then waits for its
own evidence that the device is reachable before touching the radio, so an attempt is only ever made against something that is actually there. Serial
waits for the OS to re-enumerate the port the connection was opened on. Bluetooth listens
own evidence that the device is reachable before touching the radio, so an attempt is only
ever made against something that is actually there. Serial waits for the OS to re-enumerate
the port the connection was opened on. Bluetooth listens
for the companion's advertisement (:func:`~meshterm.core.discovery.find_ble_device`), which
is the same question asked of the only registry BLE has, and hands the resulting live
handle to :meth:`~meshterm.context.AppContext.reconnect` — a power-cycled peripheral is a
+3 -2
View File
@@ -365,8 +365,9 @@ class NodeDetailScreen(Screen):
:meth:`selected_spec`); Esc resolves :data:`CANCEL` to leave.
Two axes of navigation, matching the app's spatial feel: ``Tab``/``Shift+Tab`` switches
which view fills the stage, and ``↑↓`` moves the cursor *within* the active tab — through its route list (on the Routes tab, the selection drives the
graph highlight and Enter arms a trace on the picked route) and action rows. The page
which view fills the stage, and ``↑↓`` moves the cursor *within* the active tab — through
its route list (on the Routes tab, the selection drives the graph highlight and Enter arms
a trace on the picked route) and action rows. The page
itself never scrolls: the identity header, tab strip, and stage are pinned, the stage
is sized to the viewport, and the route list windows itself into the leftover rows —
``PgUp/PgDn`` page the cursor through it, ``Home/End`` jump it to the ends.
+4 -1
View File
@@ -874,7 +874,10 @@ class PacketViewer(Screen):
readings = (entry.raw or {}).get("trace_snrs") if isinstance(entry.raw, dict) else None
if readings:
hops = len(readings)
return f"{hops} hop{'' if hops == 1 else 's'} walked — a trace logs each leg, not its relays"
return (
f"{hops} hop{'' if hops == 1 else 's'} walked "
"— a trace logs each leg, not its relays"
)
return "direct — no relays"
def _packet_rows(self, entry: PacketEntry) -> list[tuple[str, RenderableType]]:
+4 -1
View File
@@ -645,7 +645,10 @@ class PathComposerScreen(Screen):
entries.append((i, render_to_ansi(text, width)))
if not any(kind == "hop" for kind, _ in rows):
if any(kind == "fetch" for kind, _ in rows):
note = "(no observed links from here — fetch the repeater's neighbours, or type a hex hash)"
note = (
"(no observed links from here — fetch the repeater's neighbours, "
"or type a hex hash)"
)
else:
note = "(no observed links from here — type a hex hash to force a hop)"
# One entry per wrapped line: a multi-line string in a single entry would
+6 -6
View File
@@ -155,9 +155,9 @@ _GRAPH_PAD_DOTS = 6
#: the more so the shallower the curve's end tangent. Spanning the whole gap removes the corner,
#: so the stroke stays an even, thin arc end to end; the nodes still seat level because the curve's
#: end tangents are kept horizontal (see :data:`_BEND_K`), giving each marker a flat point to sit on
#: without a platform run. (In a very tight column a full-lane shift is unavoidably steep right up to
#: the node, so its marker sits on a gentle slope rather than dead level — the honest cost of keeping
#: the stroke thin there.)
#: without a platform run. (In a very tight column a full-lane shift is unavoidably steep right
#: up to the node, so its marker sits on a gentle slope rather than dead level — the honest cost
#: of keeping the stroke thin there.)
_CURVE_SPAN = 1.0
#: The shortest horizontal run a lane change is given even for a one-lane hop, so a tight
@@ -712,9 +712,9 @@ def _route(
the eased level→curve→level of the shift. The shift spans the whole column gap
(:data:`_CURVE_SPAN`) rather than a centred stretch flanked by flat platforms: a platform would
meet the climbing curve at a corner, and that corner draws a heavy braille *knee*, so the curve
runs continuously node to node instead and the marker seats on the curve's own level end tangent.
The endpoints, sitting at the centre of the lane band, make the origin's diverging peels and
us's converging merges fall out of this one rule — no endpoint special case.
runs continuously node to node instead and the marker seats on the curve's own level end
tangent. The endpoints, sitting at the centre of the lane band, make the origin's diverging
peels and us's converging merges fall out of this one rule — no endpoint special case.
``vias`` are an edge's bypass waypoints (:func:`_bypass_vias`), threaded between the two
markers in x order: the run applies the same level-or-shift grammar anchor to anchor —
+8 -6
View File
@@ -24,8 +24,9 @@ drawn*, so every surface the survey found can eventually route through it:
(:func:`cut_mark`), and that crack is what says the segment continues; only chips
crack, an arrow line still ellipsizes. A row that also carries the app-wide
opens-further-prompts ``…`` hangs it *outside* the budget
(:func:`with_action_mark`) — chrome may not cost the route cells. ``auto`` (the default) picks powerline exactly
when the terminal can draw it (:func:`~meshterm.ui.termfont.powerline_enabled` —
(:func:`with_action_mark`) — chrome may not cost the route cells. ``auto`` (the default)
picks powerline exactly when the terminal can draw it
(:func:`~meshterm.ui.termfont.powerline_enabled` —
a recommended font, a glyph-capable renderer, or the user's override) and falls
back to arrows everywhere else, so no terminal ever sees tofu.
* **Three overflow answers**, matching the three patterns the surfaces already use:
@@ -606,7 +607,8 @@ class PathLine:
nothing where the font has none).
"""
if plain:
return [self._plain_hop(hop).cell_len for hop in hops], cell_len(self._separator), 0, 0, 0
plain_cells = [self._plain_hop(hop).cell_len for hop in hops]
return plain_cells, cell_len(self._separator), 0, 0, 0
widths = [
cell_len(hop.label) if hop.gap
else self._chip(hop, self._chip_fill(hop)).cell_len
@@ -916,9 +918,9 @@ class PathLine:
The insertion slot outranks everything — it is the one chip that isn't a node,
and it wears the ``cursor`` white so it reads as chrome among identities rather
than as a hop with an unlucky hue (white sits outside the node spectrum, and the
chip ink is already picked to stay readable on it). The fade comes next, outranking identity including
our own: a dimmed hop is one nobody composed (an automatic landing back home, a
mirrored return leg), and our end must recede with the rest of that automatic
chip ink is already picked to stay readable on it). The fade comes next, outranking
identity including our own: a dimmed hop is one nobody composed (an automatic landing
back home, a mirrored return leg), and our end must recede with the rest of that automatic
half rather than keep its yellow among the greys. Plain mode says the same thing
by fading the name.
"""
+1 -1
View File
@@ -201,7 +201,7 @@ class Ui:
banner: Any = None,
footnote: str | None = None,
) -> str | None:
"""Ask for a Bluetooth companion's pairing PIN on the startup splash; ``None`` if cancelled."""
"""Ask for a Bluetooth companion's pairing PIN on the splash; ``None`` if cancelled."""
raise NotImplementedError
async def prompt_text_startup(
+4 -2
View File
@@ -388,7 +388,9 @@ def _node_sections(
)
out.append(Text())
out.append(_heading("Rhythm", f"receptions by local time of day · {_slice_note(slice_minutes)}"))
out.append(
_heading("Rhythm", f"receptions by local time of day · {_slice_note(slice_minutes)}")
)
out.extend(
axis_chart(
timeline_rows(slots, rows=_CHART_ROWS), max(slots), len(slots) // 2,
@@ -1152,7 +1154,7 @@ async def _routing_prefix_bytes(ctx: AppContext) -> int:
async def _contact_resolvers(
ctx: AppContext,
) -> tuple[NodeResolver, Callable[[str | None], int | None], NodeResolver]:
"""A ``(name, type, key)`` resolver trio over the device's contacts, best-effort like the prefix read.
"""A ``(name, type, key)`` resolver trio over the contacts, best-effort like the prefix read.
All three fill the picker's (and arrivals') blanks from what the companion knows but the
stored observations never carried: the name resolver names a node the device holds as a
+2 -1
View File
@@ -1177,7 +1177,8 @@ 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), strict=True):
numbering = range(len(self._traces), 0, -1)
for number, trace in zip(numbering, reversed(self._traces), strict=True):
rows.append(self._trace_row(number, trace))
return Group(*rows)
+2 -1
View File
@@ -346,7 +346,8 @@ class TextScreen(_KeylessDialog):
if self._byte_limit is not None:
over = self._used_bytes() - self._byte_limit
if over > 0:
self._error = f"Too long by {over} byte{'s' if over != 1 else ''} — trim to send."
plural = "s" if over != 1 else ""
self._error = f"Too long by {over} byte{plural} — trim to send."
return
if self._validate is not None:
result = self._validate(value)
+3 -2
View File
@@ -29,8 +29,9 @@ only its immediate neighbourhood:
* the **link list** beneath names every way onward as a selectable row, strongest
observed link first — so the cursor's home, row 0, is always the strongest link out of
here: type glyph, name, hash, SNR with a quality bar, the evidence behind the link
(samples, sources, age), and how many links continue onward from that node. The highlighted row's marker and label light white on the canvas. The
list scrolls *within* the screen — the canvas, legend, and heading hold still, and
(samples, sources, age), and how many links continue onward from that node. The highlighted
row's marker and label light white on the canvas. The list scrolls *within* the screen — the
canvas, legend, and heading hold still, and
faint ``↑/↓ n more`` markers bracket the window — with ↑↓ moving one row and
PgUp/PgDn a windowful.
+3 -1
View File
@@ -1347,7 +1347,9 @@ def tx_opt_summary(result: TxOptResult) -> Panel:
(f"{result.original_tx if result.original_tx is not None else '?'}\n", ""),
("status ", "muted"), Text.from_markup(applied),
)
return Panel(body, title="[accent]TX optimization[/accent]", border_style="accent", expand=False)
return Panel(
body, title="[accent]TX optimization[/accent]", border_style="accent", expand=False
)
# -- battery gauge --------------------------------------------------------------------------
+3 -2
View File
@@ -109,9 +109,10 @@ def check_drive_is_ours(drive, force=False):
if "6." in softdev:
problems.append(f"{softdev} expects the app at 0x26000; this build is linked for S140 v7")
if problems and not force:
listed = "\n - ".join(problems)
sys.exit(
"ERROR: refusing to flash {} --\n - {}\n"
"Unplug the other board, or re-run with --force if you are sure.".format(drive, "\n - ".join(problems))
f"ERROR: refusing to flash {drive} --\n - {listed}\n"
"Unplug the other board, or re-run with --force if you are sure."
)
return True
+3 -2
View File
@@ -4,8 +4,9 @@ The pages carry no state and no controls, so what is worth pinning is what they
and how they are framed: the live package facts they must never drift from, the
indent block their prose hangs in, the derived footer that only names the pager when
there is something to page, the landmarks their sections leave for the sticky heading
and the section jump, and the section they hang under in the main menu. How markdown itself is drawn is ``test_markdown``'s subject; width discipline on
both platforms is the gallery's (``test_gallery``).
and the section jump, and the section they hang under in the main menu. How markdown
itself is drawn is ``test_markdown``'s subject; width discipline on both platforms is
the gallery's (``test_gallery``).
"""
from __future__ import annotations
+10 -4
View File
@@ -87,7 +87,10 @@ def test_battery_cell_colours_by_band_while_the_dots_carry_the_detail() -> None:
assert _glyph_style(battery_cell(7)) == "batt.low"
# The band holds across a band's worth of rungs, so only the fill moves inside it.
# (Below 6.25% the alarm takes the cell over, which the next test covers.)
turns = [p for p in range(8, 101) if _glyph_style(battery_cell(p)) != _glyph_style(battery_cell(p - 1))]
turns = [
p for p in range(8, 101)
if _glyph_style(battery_cell(p)) != _glyph_style(battery_cell(p - 1))
]
assert turns == [25, 50]
assert len({battery_cell(p).plain[0] for p in range(25, 50)}) == 2 # two rungs per band
@@ -115,9 +118,12 @@ def test_battery_cell_charging_sweeps_bottom_to_full_holding_the_percent() -> No
def test_battery_cell_charging_keeps_the_bands_colour_while_the_fill_sweeps() -> None:
"""The sweep is an animation, not a reading: colour still answers for the real charge."""
assert {_glyph_style(battery_cell(20, charging=True, frame=f)) for f in range(5)} == {"batt.low"}
assert {_glyph_style(battery_cell(40, charging=True, frame=f)) for f in range(5)} == {"batt.mid"}
assert {_glyph_style(battery_cell(90, charging=True, frame=f)) for f in range(5)} == {"batt.full"}
charging = {_glyph_style(battery_cell(20, charging=True, frame=f)) for f in range(5)}
assert charging == {"batt.low"}
charging = {_glyph_style(battery_cell(40, charging=True, frame=f)) for f in range(5)}
assert charging == {"batt.mid"}
charging = {_glyph_style(battery_cell(90, charging=True, frame=f)) for f in range(5)}
assert charging == {"batt.full"}
def test_battery_cell_charging_never_alarms_however_empty_the_pack_is() -> None:
+10 -3
View File
@@ -193,7 +193,8 @@ def test_decrypt_channel_text_rejects_a_fingerprint_collision() -> None:
def test_decrypt_channel_text_rejects_malformed_hex() -> None:
"""Garbage hex fields fail closed instead of raising."""
assert decrypt_channel_text("zz", "zzzz", "zzzzzzzzzzzzzzzz", [("x", random_secret())]) is None
assert decrypt_channel_text("00", "0000", "00" * 15, [("x", random_secret())]) is None # not block-aligned
# a 15-byte body is not block-aligned
assert decrypt_channel_text("00", "0000", "00" * 15, [("x", random_secret())]) is None
# -- QR rendering -------------------------------------------------------------
@@ -374,10 +375,16 @@ class _ScriptedUi:
async def select(self, title: str, items: list, *, default=None): # noqa: ANN001, ANN201
return self._selects.pop(0) if self._selects else "__back__"
async def text(self, title: str, *, default: str = "", validate=None, help_text: str = "", password: bool = False): # noqa: ANN001, ANN201
async def text( # noqa: ANN001, ANN201
self, title: str, *, default: str = "", validate=None,
help_text: str = "", password: bool = False,
):
return self._texts.pop(0)
async def dialog(self, prompt, buttons, *, title: str = "", default: int = 0, keys=None, danger: bool = False, destructive: bool = False): # noqa: ANN001, ANN201
async def dialog( # noqa: ANN001, ANN201
self, prompt, buttons, *, title: str = "", default: int = 0, keys=None,
danger: bool = False, destructive: bool = False,
):
return self._dialogs.pop(0)
+7 -3
View File
@@ -484,7 +484,9 @@ def test_title_preview_column_aligns_regardless_of_label_length(repo: Repository
"""The preview starts at the same column whether the label is short or (clipped) long."""
ctx = _RowCtx(repo)
short = Conversation(label="A", is_channel=True, channel_idx=0, channel_id="c0")
long = Conversation(label="A much longer channel name here", is_channel=True, channel_idx=1, channel_id="c1")
long = Conversation(
label="A much longer channel name here", is_channel=True, channel_idx=1, channel_id="c1"
)
m0 = ChatMessage(text="X: hello", is_channel=True, channel_id="c0")
m1 = ChatMessage(text="Y: hello", is_channel=True, channel_id="c1")
l0 = _title(ctx, short, {"chan:c0": m0}, _NO_KEYS).plain
@@ -493,7 +495,7 @@ 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."""
"""Channel rows lead with an openness glyph: # name-derived, 🌐 public, 🔒 private."""
ctx = _RowCtx(repo)
def head(conv):
return _title(ctx, conv, {}, _NO_KEYS).plain.split(" ", 1)[0]
@@ -1140,7 +1142,9 @@ def test_channel_transcript_groups_by_sender() -> None:
ChatMessage(text="Alice: hi", is_channel=True, channel_idx=0, created_at=at(0)),
ChatMessage(text="Alice: again", is_channel=True, channel_idx=0, created_at=at(6)),
ChatMessage(text="Bob: yo", is_channel=True, channel_idx=0, created_at=at(8)),
ChatMessage(text="hello all", outbound=True, is_channel=True, channel_idx=0, created_at=at(9)),
ChatMessage(
text="hello all", outbound=True, is_channel=True, channel_idx=0, created_at=at(9)
),
]
screen = ChatScreen(conv, messages, send=None, names={}, session=_StubSession())
rendered = _strip_ansi("\n".join(screen._render_grouped(80)))
+2 -1
View File
@@ -296,7 +296,8 @@ def test_recent_observations_rehydrate_the_packet_payload_class(tmp_path: Path)
)
repo.record_observation(run, _obs(node="a1b2", kind="advert")) # a plain advert nearby
window = {o.node or "": o for o in repo.recent_observations(since=utcnow() - timedelta(hours=2))}
recent = repo.recent_observations(since=utcnow() - timedelta(hours=2))
window = {o.node or "": o for o in recent}
assert window[""].raw is not None and window[""].raw["payload_typename"] == "TRACE"
assert window["a1b2"].raw is None # an advert keeps no packet raw, as before
repo.close()
+2 -1
View File
@@ -115,7 +115,8 @@ def _contacts_names(counts, sort: str, prefix_bytes: int = 3):
Contact(name="Alice", public_key="3d63c6" + "00" * 29, key_prefix="3d63c6"),
]
group = contacts_table(
"Homestead", "aabbcc" + "00" * 29, contacts, prefix_bytes, counts, ContactsSort.from_name(sort)
"Homestead", "aabbcc" + "00" * 29, contacts, prefix_bytes, counts,
ContactsSort.from_name(sort),
)
table = group.renderables[0] # (table, blank line, legend)
names = [c.plain if hasattr(c, "plain") else str(c) for c in table.columns[1].cells]
+2 -1
View File
@@ -33,7 +33,8 @@ class FakeDevice:
self.contacts_calls += 1
if self.contact_rows is not None:
return list(self.contact_rows)
return [Contact(name=f"contact-{self.contacts_calls}")] # fresh identity per fetch, to spot refreshes
# a fresh identity per fetch, so a re-read is visible
return [Contact(name=f"contact-{self.contacts_calls}")]
async def get_self_info(self) -> dict:
self.self_info_calls += 1
+2 -1
View File
@@ -130,7 +130,8 @@ def test_bare_text_default_emoji_are_left_exactly_as_measured() -> None:
for bare in (_ROAD, _WEB):
assert cell_len(bare) == 1 and cache[bare] == 1
# A heading built around one costs its own cells and no more, so the row frames flush.
assert cell_len(f"── {_ROAD} Longest distance ──") == len("── ") + 1 + len(" Longest distance ──")
caption = cell_len(f"── {_ROAD} Longest distance ──")
assert caption == len("── ") + 1 + len(" Longest distance ──")
def test_flags_measure_two_cells_in_both_authorities() -> None:
+3 -1
View File
@@ -302,7 +302,9 @@ def _chat(cols: int, rows: int) -> Screen:
)
messages = [
ChatMessage(text="on my way, should be there soon", outbound=False, peer="d4e5f6a7"),
ChatMessage(text="sounds good, see you shortly", outbound=True, peer="d4e5f6a7", acked=True),
ChatMessage(
text="sounds good, see you shortly", outbound=True, peer="d4e5f6a7", acked=True
),
]
return ChatScreen(
conv, messages, send=None, names={"d4e5f6a7": "Alice"}, session=_GallerySession(cols, rows),
+10 -3
View File
@@ -112,7 +112,8 @@ def test_decode_tile_filter_skips_the_layers_the_map_never_draws() -> None:
"""The layers left undecoded are the ones that made a tile expensive."""
from meshterm.ui.map_render import DRAWN_LAYERS
narrow = {layer.name: layer for layer in decode_tile(_FIXTURE.read_bytes(), layers=DRAWN_LAYERS)}
drawn = decode_tile(_FIXTURE.read_bytes(), layers=DRAWN_LAYERS)
narrow = {layer.name: layer for layer in drawn}
for skipped in ("housenumber", "poi", "mountain_peak"):
assert skipped in narrow, f"{skipped} should still be named"
assert not narrow[skipped].features, f"{skipped} should not have been decoded"
@@ -905,7 +906,10 @@ def test_decoded_layers_survive_a_round_trip() -> None:
restored = loads_layers(dumps_layers(original, stamp="x"), stamp="x")
assert restored is not None
assert [(layer.name, layer.extent) for layer in restored] == [(layer.name, layer.extent) for layer in original]
def shape(layers):
return [(layer.name, layer.extent) for layer in layers]
assert shape(restored) == shape(original)
for before, after in zip(original, restored, strict=True):
assert after.features == before.features
@@ -2282,7 +2286,10 @@ def test_map_paints_without_waiting_for_the_ground(monkeypatch) -> None: # noqa
calls: list[tuple] = []
real = ms.render_map
monkeypatch.setattr(ms, "render_map", lambda vp, tiles, m, **k: calls.append(tiles) or real(vp, tiles, m, **k))
monkeypatch.setattr(
ms, "render_map",
lambda vp, tiles, m, **k: calls.append(tiles) or real(vp, tiles, m, **k),
)
started: list = []
screen = _async_map(monkeypatch, started)
+11 -3
View File
@@ -60,9 +60,17 @@ def test_channel_arrivals_match_by_decrypted_content(tmp_path: Path) -> None:
repo, run = _repo(tmp_path)
now = utcnow()
wire = "Alice: hi mesh"
_record_frame(repo, run, when=now - timedelta(seconds=5), path="3d63", raw=_grp_txt_raw(SECRET, wire))
_record_frame(repo, run, when=now - timedelta(seconds=3), path="3d63,a1b2", raw=_grp_txt_raw(SECRET, wire))
_record_frame(repo, run, when=now - timedelta(seconds=1), path="", raw=_grp_txt_raw(SECRET, "Alice: other"))
_record_frame(
repo, run, when=now - timedelta(seconds=5), path="3d63", raw=_grp_txt_raw(SECRET, wire)
)
_record_frame(
repo, run, when=now - timedelta(seconds=3), path="3d63,a1b2",
raw=_grp_txt_raw(SECRET, wire),
)
_record_frame(
repo, run, when=now - timedelta(seconds=1), path="",
raw=_grp_txt_raw(SECRET, "Alice: other"),
)
message = ChatMessage(text=wire, is_channel=True, created_at=now)
arrivals = channel_arrivals(repo, message, channel_name="#general", secret=SECRET)
+24 -9
View File
@@ -247,7 +247,7 @@ def test_route_line_hash_width_follows_our_path_hash_mode() -> None:
def test_route_line_marks_the_best_route_and_its_context() -> None:
"""The winner wears ★ best and trails its bottleneck SNR and sample count, on the context line."""
"""The winner wears ★ best and trails its bottleneck SNR and sample count on the context row."""
_path, context = _route_line(
"Far", FAR.public_key, ("3d63c6429436",), "best", 6.5, 4,
resolve=make_node_resolver([HUB]), node_known=True,
@@ -301,7 +301,7 @@ def _view(topo, suggested, device_route, target, node_label, contacts, name_key=
def test_routes_view_draws_evidence_and_notes_its_absence() -> None:
"""With route evidence the view carries selectable routes; without any, a muted note stands in."""
"""With route evidence the view carries selectable routes; without, a muted note stands in."""
topo = _topo_with_route()
view = _view(topo, topo.suggested("f2c24f54551e"), ("3d63c6429436",),
"f2c24f54551e", "Far", [HUB, FAR])
@@ -322,7 +322,7 @@ def test_routes_view_draws_evidence_and_notes_its_absence() -> None:
def test_routes_view_puts_the_contact_on_the_left_and_us_on_the_right() -> None:
"""The graph reads node → us (the inbound direction): the contact on the left, our star right."""
"""The graph reads node → us (the inbound direction): the contact left, our star right."""
topo = _topo_with_route()
view = _view(topo, topo.suggested("f2c24f54551e"), ("3d63c6429436",),
"f2c24f54551e", "Far", [HUB, FAR])
@@ -422,7 +422,7 @@ def test_good_alternatives_keeps_observed_routes_and_drops_outliers() -> None:
def test_route_freshness_drops_a_route_with_a_long_quiet_hop() -> None:
"""A route counts as stale — and is dropped — once its stalest hop goes quiet past the horizon."""
"""A route counts as stale — and is dropped — once its stalest hop falls past the horizon."""
from datetime import timedelta
from meshterm.persistence.repository import TracedPath
@@ -687,8 +687,14 @@ def test_node_detail_routes_stage_spends_the_caption_and_top_air_on_the_fan() ->
screen.handle("tab") # onto Routes
screen.note_viewport(viewport)
lines = screen.render_body(49)
strip_idx = next(i for i, ln in enumerate(lines) if "Routes" in _plain([ln]) and "│" in _plain([ln]))
rule_idx = next(i for i, ln in enumerate(lines) if set(_plain([ln])) == {"─"} and i > strip_idx)
def is_strip(ln):
return "Routes" in _plain([ln]) and "│" in _plain([ln])
strip_idx = next(i for i, ln in enumerate(lines) if is_strip(ln))
rule_idx = next(
i for i, ln in enumerate(lines)
if set(_plain([ln])) == {"─"} and i > strip_idx
)
return [_plain([ln]) for ln in lines[strip_idx + 2 : rule_idx]] # past the strip's own rule
set_platform(PICOCALC)
@@ -976,7 +982,10 @@ def test_route_labels_light_through_a_coalesced_hop() -> None:
routes = _RoutesView(
routes=[
_Route(draw=("bf61f2fb1d9e", "3d63c6429436"), spec="s0", path=Text("wide"), context=Text("")),
_Route(
draw=("bf61f2fb1d9e", "3d63c6429436"), spec="s0",
path=Text("wide"), context=Text(""),
),
_Route(draw=("bf61f2fb1d9e", "3d"), spec="s1", path=Text("short"), context=Text("")),
],
glyph_of=lambda n: ("●", "#ffffff"),
@@ -996,7 +1005,10 @@ def test_node_detail_route_list_windows_inside_the_page() -> None:
"""
routes = _RoutesView(
routes=[
_Route(draw=(f"{i:x}{i:x}" * 6,), spec=f"s{i}", path=Text(f"route {i}"), context=Text(""))
_Route(
draw=(f"{i:x}{i:x}" * 6,), spec=f"s{i}",
path=Text(f"route {i}"), context=Text(""),
)
for i in range(12)
],
glyph_of=lambda n: ("●", "#ffffff"),
@@ -1026,7 +1038,10 @@ def test_node_detail_route_cursor_clamps_at_both_ends() -> None:
"""
routes = _RoutesView(
routes=[
_Route(draw=(f"{i:x}{i:x}" * 6,), spec=f"s{i}", path=Text(f"route {i}"), context=Text(""))
_Route(
draw=(f"{i:x}{i:x}" * 6,), spec=f"s{i}",
path=Text(f"route {i}"), context=Text(""),
)
for i in range(12)
],
glyph_of=lambda n: ("●", "#ffffff"),
+3 -2
View File
@@ -313,8 +313,9 @@ def test_a_shared_relay_draws_a_single_marker() -> None:
def _marker_rows(lines, glyph): # noqa: ANN001, ANN202
"""The distinct canvas row indices a marker glyph is drawn on (endpoints ``★``, relays ``●``)."""
return sorted({i for i, ln in enumerate(_ANSI.sub("", "\n".join(lines)).splitlines()) if glyph in ln})
"""The distinct canvas rows a marker glyph is drawn on (endpoints ``★``, relays ``●``)."""
plain = _ANSI.sub("", "\n".join(lines)).splitlines()
return sorted({i for i, ln in enumerate(plain) if glyph in ln})
def test_even_lane_count_centres_the_endpoints() -> None:
+13 -4
View File
@@ -143,12 +143,21 @@ def test_hourly_series_groups_by_clock_hour(tmp_path: Path) -> None:
base = now.replace(minute=0, second=0, microsecond=0) # top of the current UTC hour
three_h = base - timedelta(hours=3) # top of a busy hour, three back
for node in ("aa" * 6, "bb" * 6):
repo.record_observation(run, Observation(node=node, observed_at=three_h + timedelta(minutes=5)))
repo.record_observation(run, Observation(node="aa" * 6, observed_at=three_h + timedelta(minutes=20)))
repo.record_observation(
run, Observation(node=node, observed_at=three_h + timedelta(minutes=5))
)
repo.record_observation(
run, Observation(node="cc" * 6, kind="packet", path="", observed_at=three_h + timedelta(minutes=40))
run, Observation(node="aa" * 6, observed_at=three_h + timedelta(minutes=20))
)
repo.record_observation(
run,
Observation(
node="cc" * 6, kind="packet", path="", observed_at=three_h + timedelta(minutes=40)
),
)
repo.record_observation(
run, Observation(node="aa" * 6, observed_at=base + timedelta(minutes=1))
)
repo.record_observation(run, Observation(node="aa" * 6, observed_at=base + timedelta(minutes=1)))
series = repo.hourly_series(now - timedelta(days=1))
by_hour = {iso: (pkts, nodes) for iso, pkts, nodes in series}
# Keys are local wall-clock hours, so form the expected keys via the same rotation.
+3 -2
View File
@@ -168,8 +168,9 @@ def test_walk_selected_link_lights_the_route_that_reaches_it() -> None:
topo = MeshTopology(US, contacts=[hub, alice, bob])
y = topo.canonical(hub.public_key)
topo.add_walk([topo.self_id, y], snrs=[10.0], when=stale, source="trace") # approach: green
topo.add_walk([y, topo.canonical(alice.public_key)], snrs=[0.0], when=stale, source="trace") # amber
topo.add_walk([y, topo.canonical(bob.public_key)], snrs=[-15.0], when=stale, source="trace") # red
# amber, then red
topo.add_walk([y, topo.canonical(alice.public_key)], snrs=[0.0], when=stale, source="trace")
topo.add_walk([y, topo.canonical(bob.public_key)], snrs=[-15.0], when=stale, source="trace")
screen = WalkScreen(
session=_FakeSession(),
topo=topo,