Skip to content

Commit 3916009

Browse files
committed
pylint cleanups
1 parent 280323d commit 3916009

2 files changed

Lines changed: 35 additions & 16 deletions

File tree

bin/inject_nanopb_options.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import re
2222
import sys
2323
from pathlib import Path
24-
from typing import Any, Dict, List, Optional, Tuple
24+
from typing import Any, Dict, List, Tuple
2525

2626
# IntSize enum values from nanopb.proto
2727
INT_SIZE_ENUM = {8: "IS_8", 16: "IS_16", 32: "IS_32", 64: "IS_64"}
@@ -72,7 +72,7 @@ def parse_options_file(
7272
specific: Dict[Tuple[str, ...], Dict[str, Any]] = {}
7373
wildcard: Dict[str, Dict[str, Any]] = {}
7474

75-
with open(path) as f:
75+
with open(path, encoding="utf-8") as f:
7676
for line in f:
7777
# Strip inline comments
7878
comment_pos = line.find("#")
@@ -287,6 +287,7 @@ def inject_into_proto(
287287

288288

289289
def main() -> int:
290+
"""Parse an .options file and inject its constraints into a .proto file in-place."""
290291
if len(sys.argv) != 3:
291292
print(
292293
f"Usage: {sys.argv[0]} <options_file> <proto_file>",
@@ -312,13 +313,13 @@ def main() -> int:
312313
print(f" [{opts_path.name}] No injectable options found, skipping.")
313314
return 0
314315

315-
content = proto_path.read_text()
316+
content = proto_path.read_text(encoding="utf-8")
316317

317318
# After regen-protobufs.sh's sed fixup, the nanopb import path is:
318319
nanopb_import_path = "meshtastic/protobuf/nanopb.proto"
319320

320321
modified = inject_into_proto(content, specific, wildcard, nanopb_import_path)
321-
proto_path.write_text(modified)
322+
proto_path.write_text(modified, encoding="utf-8")
322323

323324
print(
324325
f" [{opts_path.name}] Injected {len(specific)} specific + "

meshtastic/tests/test_inject_nanopb_options.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,18 @@
1212
import sys
1313
import textwrap
1414
from pathlib import Path
15-
from typing import Any, Dict, Tuple
1615
from unittest.mock import patch
1716

1817
import pytest
1918

19+
from meshtastic.protobuf import (
20+
atak_pb2,
21+
config_pb2,
22+
mesh_pb2,
23+
nanopb_pb2,
24+
telemetry_pb2,
25+
)
26+
2027
# ---------------------------------------------------------------------------
2128
# Load bin/inject_nanopb_options.py as a module without adding it to the
2229
# package. __main__ guard means no side-effects on import.
@@ -55,26 +62,31 @@ def _load_inject_module():
5562

5663
@pytest.mark.unit
5764
def test_parse_value_integer():
65+
"""parse_value converts a decimal string to int."""
5866
assert parse_value("40") == 40
5967

6068

6169
@pytest.mark.unit
6270
def test_parse_value_negative_integer():
71+
"""parse_value handles negative integer strings."""
6372
assert parse_value("-1") == -1
6473

6574

6675
@pytest.mark.unit
6776
def test_parse_value_true():
77+
"""parse_value converts 'true' to Python True."""
6878
assert parse_value("true") is True
6979

7080

7181
@pytest.mark.unit
7282
def test_parse_value_false():
83+
"""parse_value converts 'false' to Python False."""
7384
assert parse_value("false") is False
7485

7586

7687
@pytest.mark.unit
7788
def test_parse_value_string():
89+
"""parse_value returns non-numeric, non-boolean strings as-is."""
7890
assert parse_value("IS_8") == "IS_8"
7991

8092

@@ -113,7 +125,7 @@ def test_parse_specific(tmp_path):
113125
def test_parse_multilevel(tmp_path):
114126
"""Three-part pattern (Route.Link.uid) produces a 3-tuple key."""
115127
f = _write_options(tmp_path, "*Route.Link.uid max_size:48\n")
116-
specific, wildcard = parse_options_file(f)
128+
specific, _ = parse_options_file(f)
117129
assert ("Route", "Link", "uid") in specific
118130
assert specific[("Route", "Link", "uid")] == {"max_size": 48}
119131

@@ -181,12 +193,14 @@ def test_parse_int_and_bool_values(tmp_path):
181193

182194
@pytest.mark.unit
183195
def test_message_path_matches_simple():
196+
"""A single-element path matches the current message on the stack."""
184197
stack = [("message", "User")]
185198
assert message_path_matches(stack, ("User",))
186199

187200

188201
@pytest.mark.unit
189202
def test_message_path_matches_nested():
203+
"""Both a 1-element and 2-element path match correctly against a nested stack."""
190204
stack = [("message", "Config"), ("message", "DeviceConfig")]
191205
assert message_path_matches(stack, ("DeviceConfig",))
192206
assert message_path_matches(stack, ("Config", "DeviceConfig"))
@@ -201,6 +215,7 @@ def test_message_path_matches_with_oneof_in_stack():
201215

202216
@pytest.mark.unit
203217
def test_message_path_no_match():
218+
"""A path with the wrong message name does not match."""
204219
stack = [("message", "User")]
205220
assert not message_path_matches(stack, ("Route",))
206221

@@ -220,11 +235,13 @@ def test_message_path_multilevel_partial_match():
220235

221236
@pytest.mark.unit
222237
def test_format_max_size():
238+
"""max_size is rendered as an integer literal."""
223239
assert format_nanopb_opts({"max_size": 40}) == "(nanopb).max_size = 40"
224240

225241

226242
@pytest.mark.unit
227243
def test_format_int_size_as_enum():
244+
"""int_size numeric values are rendered as IS_8/IS_16/IS_32/IS_64 enum names."""
228245
assert format_nanopb_opts({"int_size": 8}) == "(nanopb).int_size = IS_8"
229246
assert format_nanopb_opts({"int_size": 16}) == "(nanopb).int_size = IS_16"
230247
assert format_nanopb_opts({"int_size": 32}) == "(nanopb).int_size = IS_32"
@@ -233,11 +250,13 @@ def test_format_int_size_as_enum():
233250

234251
@pytest.mark.unit
235252
def test_format_bool_true():
253+
"""True is rendered as the proto literal 'true'."""
236254
assert format_nanopb_opts({"fixed_length": True}) == "(nanopb).fixed_length = true"
237255

238256

239257
@pytest.mark.unit
240258
def test_format_bool_false():
259+
"""False is rendered as the proto literal 'false'."""
241260
assert format_nanopb_opts({"fixed_length": False}) == "(nanopb).fixed_length = false"
242261

243262

@@ -293,6 +312,7 @@ def test_inject_merges_with_existing_options():
293312

294313
@pytest.mark.unit
295314
def test_inject_int_size_uses_enum_name():
315+
"""int_size values are written as IS_N enum names, not raw integers."""
296316
proto = """\
297317
syntax = "proto3";
298318
import "meshtastic/protobuf/mesh.proto";
@@ -335,8 +355,6 @@ def test_inject_specific_not_leaking_to_other_messages():
335355
}
336356
"""
337357
result = _inject(proto, specific={("User", "id"): {"max_size": 16}})
338-
lines = result.splitlines()
339-
user_line = next(l for l in lines if "User" not in l and "id = 1" in l and "Other" not in l.split("message")[0] if "message" not in l)
340358
# Easier: count annotations — should be exactly one
341359
assert result.count("(nanopb).max_size = 16") == 1
342360

@@ -400,6 +418,7 @@ def test_inject_optional_qualifier_preserved():
400418

401419
@pytest.mark.unit
402420
def test_inject_repeated_qualifier_preserved():
421+
"""The 'repeated' qualifier is kept when a field gets an annotation."""
403422
proto = """\
404423
syntax = "proto3";
405424
import "meshtastic/protobuf/mesh.proto";
@@ -413,6 +432,7 @@ def test_inject_repeated_qualifier_preserved():
413432

414433
@pytest.mark.unit
415434
def test_inject_multiple_options_on_one_field():
435+
"""Multiple options from the same pattern are all injected on one field."""
416436
proto = """\
417437
syntax = "proto3";
418438
import "meshtastic/protobuf/mesh.proto";
@@ -510,14 +530,6 @@ def test_inject_noop_when_no_options():
510530
# embedded in the serialized descriptors.
511531
# ===========================================================================
512532

513-
from meshtastic.protobuf import ( # noqa: E402 (after local helpers)
514-
atak_pb2,
515-
config_pb2,
516-
mesh_pb2,
517-
nanopb_pb2,
518-
telemetry_pb2,
519-
)
520-
521533

522534
def _field_opts(descriptor, *path):
523535
"""Walk a descriptor by field/nested-type path and return its nanopb opts.
@@ -534,12 +546,14 @@ def _field_opts(descriptor, *path):
534546

535547
@pytest.mark.unit
536548
def test_descriptor_user_long_name():
549+
"""User.long_name has max_size = 40 from mesh.options."""
537550
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["User"], "long_name")
538551
assert opts.max_size == 40
539552

540553

541554
@pytest.mark.unit
542555
def test_descriptor_user_short_name():
556+
"""User.short_name has max_size = 5 from mesh.options."""
543557
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["User"], "short_name")
544558
assert opts.max_size == 5
545559

@@ -554,12 +568,14 @@ def test_descriptor_wildcard_macaddr():
554568

555569
@pytest.mark.unit
556570
def test_descriptor_meshpacket_hop_limit():
571+
"""MeshPacket.hop_limit has int_size = IS_8 from mesh.options."""
557572
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["MeshPacket"], "hop_limit")
558573
assert opts.int_size == nanopb_pb2.IS_8
559574

560575

561576
@pytest.mark.unit
562577
def test_descriptor_routediscovery_snr_towards():
578+
"""RouteDiscovery.snr_towards has max_count = 8 and int_size = IS_8 from mesh.options."""
563579
opts = _field_opts(
564580
mesh_pb2.DESCRIPTOR.message_types_by_name["RouteDiscovery"], "snr_towards"
565581
)
@@ -569,6 +585,7 @@ def test_descriptor_routediscovery_snr_towards():
569585

570586
@pytest.mark.unit
571587
def test_descriptor_data_payload():
588+
"""Data.payload has max_size = 233 from mesh.options."""
572589
opts = _field_opts(mesh_pb2.DESCRIPTOR.message_types_by_name["Data"], "payload")
573590
assert opts.max_size == 233
574591

@@ -600,6 +617,7 @@ def test_descriptor_multilevel_nested_route_link_uid():
600617

601618
@pytest.mark.unit
602619
def test_descriptor_telemetry_environment_one_wire_temperature():
620+
"""EnvironmentMetrics.one_wire_temperature has max_count = 8 from telemetry.options."""
603621
env = telemetry_pb2.DESCRIPTOR.message_types_by_name["EnvironmentMetrics"]
604622
opts = _field_opts(env, "one_wire_temperature")
605623
assert opts.max_count == 8

0 commit comments

Comments
 (0)