Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/lob_hlpr/lib_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,27 @@ class FirmwareID:
# Date group, matches everything inside the parentheses, made entirely optional
r"(?:\s*\((?P<date>.+?)\))?$",
)
_IDENTIFIER_RE_ORIGIN = re.compile(
# Name group, non-greedy match up to the first +
r"^(?P<name>.+?)\+"
# Version group, matches a semantic versioning pattern
r"(?P<version>[0-9]+(?:\.[0-9]+){2}(?:-[\w]+)?(?:-\d+-g[0-9a-f]+)?)"
# Optional variant group, matches anything after a '+' until a space or end
r"(?:\+(?P<variant>[^\s]+))?"
# Optional additional group,
# matches before parentheses if not directly next to variant
r"(?:\s+(?P<additional>[^\(\)]+?))?"
# Date group, matches everything inside the parentheses, made entirely optional
r"(?:\s*\((?P<date>.+?)\))?$",
)
"""Precompiled regex for parsing all parts of a firmware identifier string."""

def __post_init__(self):
"""Parses the firmware identifier string and updates attributes accordingly."""
try:
m = self._IDENTIFIER_RE.match(self.id_string)
if not m:
m = self._IDENTIFIER_RE_ORIGIN.match(self.id_string)
Copy link

Copilot AI May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding an explicit error check for when both regex matches return None, so that a clear ValueError is raised rather than relying on a later exception. For example, after attempting both matches, check if m is None and then raise ValueError with an informative message.

Suggested change
m = self._IDENTIFIER_RE_ORIGIN.match(self.id_string)
m = self._IDENTIFIER_RE_ORIGIN.match(self.id_string)
if not m:
raise ValueError(
f"Invalid firmware identifier string: '{self.id_string}'. "
"It does not match the expected formats."
)

Copilot uses AI. Check for mistakes.
groups = m.groupdict()
self.name = groups["name"]
self.version = FirmwareVersion(groups["version"])
Expand Down
11 changes: 11 additions & 0 deletions tests/test_firmware_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ def test_valid_firmware_id_with_variants():
assert firmware_id.built == "2024-03-11T13:57:40"


def test_valid_firmware_id_with_origin_pass():
"""Tests if the firmware ID can be created with 'origin' in the version string."""
id_str = "app-nrf91-origin+0.3.7+hw4 TZ3 (Mar 5 2025 08:36:26)"
firmware_id = FirmwareID(id_str)
assert firmware_id.name == "app-nrf91-origin"
assert isinstance(firmware_id.version, FirmwareVersion)
assert firmware_id.version.version_string == "0.3.7"
assert firmware_id.variants == ["hw4"]
assert firmware_id.built == "2025-03-05T08:36:26"


def test_valid_firmware_id_unknown():
"""Tests if the firmware ID can be created with 'unknown' in the version string."""
id_str = "app-nrf9160-wmbus v0.0.0-unknown+hw3 TZ2 (Oct 12 2023 10:45:49)"
Expand Down