From 875ddeddd763f550286d142aad37ac266056049d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 3 Aug 2024 21:46:29 +0200 Subject: [PATCH] Ignore redundant separator characters Fixes #176 --- src/biip/gs1/_messages.py | 19 +++++++------------ tests/gs1/test_messages.py | 30 ++++++++++++++++-------------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/biip/gs1/_messages.py b/src/biip/gs1/_messages.py index 1bf97ee7..99243dac 100644 --- a/src/biip/gs1/_messages.py +++ b/src/biip/gs1/_messages.py @@ -66,7 +66,8 @@ def parse( A message object with one or more element strings. Raises: - ParseError: If a fixed-length field ends with a separator character. + ValueError: If the ``separator_char`` isn't exactly 1 character long. + ParseError: If the parsing fails. """ value = value.strip() element_strings: List[GS1ElementString] = [] @@ -83,17 +84,11 @@ def parse( rest = rest[len(element_string) :] - if rest.startswith(tuple(separator_chars)): - if element_string.ai.fnc1_required: - rest = rest[1:] - else: - separator_char = rest[0] - msg = ( - f"Element String {element_string.as_hri()!r} has fixed length " - "and should not end with a separator character. " - f"Separator character {separator_char!r} found in {value!r}." - ) - raise ParseError(msg) + # Separator characters are accepted inbetween any element string, + # even if the AI doesn't require it. See GS1 General Specifications, + # section 7.8.6 for details. + while rest.startswith(tuple(separator_chars)): + rest = rest[1:] return cls(value=value, element_strings=element_strings) diff --git a/tests/gs1/test_messages.py b/tests/gs1/test_messages.py index 9ed23a14..00e11cf5 100644 --- a/tests/gs1/test_messages.py +++ b/tests/gs1/test_messages.py @@ -150,20 +150,22 @@ def test_parse_fails_if_unparsed_data_left(value: str, error: str) -> None: assert str(exc_info.value) == error -def test_parse_fails_if_fixed_length_field_ends_with_separator_char() -> None: - # 15... is a fixed length date. - # \x1d is the default separator character in an illegal position. - # 10... is any other field. - value = "15210526\x1d100329" - - with pytest.raises(ParseError) as exc_info: - GS1Message.parse(value) - - assert str(exc_info.value) == ( - r"Element String '(15)210526' has fixed length and " - r"should not end with a separator character. " - r"Separator character '\x1d' found in '15210526\x1d100329'." - ) +@pytest.mark.parametrize( + ("value", "expected_hri"), + [ + # A separator at the end of the string is never needed. + ("15210526100329\x1d", "(15)210526(10)0329"), + # 15... is a fixed length date, and should not end with a separator. + ("15210526\x1d100329", "(15)210526(10)0329"), + # Double separator chars are ignored. + ( + "0107032069804988100329\x1d\x1d15210526", + "(01)07032069804988(10)0329(15)210526", + ), + ], +) +def test_parse_ignores_redundant_separator_chars(value: str, expected_hri: str) -> None: + assert GS1Message.parse(value).as_hri() == expected_hri def test_parse_strips_surrounding_whitespace() -> None: