Skip to content

biip - The high-level API

Biip interprets the data in barcodes.

>>> import biip

An ambiguous value may be interpreted as different formats. In the following example, the value can be interpreted as either a GTIN or a GS1 Message.

>>> result = biip.parse("96385074")
>>> pprint(result.gtin)
Gtin(
    value='96385074',
    format=GtinFormat.GTIN_8,
    prefix=GS1Prefix(
        value='00009',
        usage='GS1 US'
    ),
    company_prefix=GS1CompanyPrefix(
        value='0000963'
    ),
    payload='9638507',
    check_digit=4
)
>>> pprint(result.gs1_message)
GS1Message(
    value='96385074',
    element_strings=[
        GS1ElementString(
            ai=GS1ApplicationIdentifier(
                ai='96',
                description='Company internal information',
                data_title='INTERNAL',
                separator_required=True,
                format='N2+X..90'
            ),
            value='385074',
            pattern_groups=[
                '385074'
            ]
        )
    ]
)

In the next example, the value is only valid as a GS1 Message and the GTIN parser returns an error explaining why the value cannot be interpreted as a GTIN. If a format includes check digits, Biip always control them and fail if the check digits are incorrect.

>>> result = biip.parse("15210527")
>>> pprint(result.gtin)
None
>>> result.gtin_error
"Invalid GTIN check digit for '15210527': Expected 4, got 7."
>>> pprint(result.gs1_message)
GS1Message(
    value='15210527',
    element_strings=[
        GS1ElementString(
            ai=GS1ApplicationIdentifier(
                ai='15',
                description='Best before date (YYMMDD)',
                data_title='BEST BEFORE or BEST BY',
                separator_required=False,
                format='N2+N6'
            ),
            value='210527',
            pattern_groups=[
                '210527'
            ],
            date=datetime.date(2021, 5, 27)
        )
    ]
)

If a value cannot be interpreted as any supported format, you still get a ParseResult object with all result fields set to None.

>>> result = biip.parse("123")
>>> pprint(result)
ParseResult(
    value='123',
    gtin_error="Failed to parse '123' as GTIN: Expected 8, 12, 13, or 14 digits, got 3.",
    upc_error="Failed to parse '123' as UPC: Expected 6, 7, 8, or 12 digits, got 3.",
    sscc_error="Failed to parse '123' as SSCC: Expected 18 digits, got 3.",
    gs1_message_error="Failed to match '123' with GS1 AI (12) pattern '^12(\\d{2}(?:0\\d|1[0-2])(?:[0-2]\\d|3[01]))$'."
)

BiipException

Bases: Exception

Base class for all custom exceptions raised by the library.

Source code in src/biip/_exceptions.py
class BiipException(Exception):  # noqa: N818
    """Base class for all custom exceptions raised by the library."""

EncodeError

Bases: BiipException

Error raised if encoding of a value fails.

Source code in src/biip/_exceptions.py
class EncodeError(BiipException):
    """Error raised if encoding of a value fails."""

ParseConfig dataclass

Configuration options for parsers.

Biip strives to do the right thing by default, but you can customize some of its the behavior by setting these options.

Source code in src/biip/_config.py
@dataclass(frozen=True)
class ParseConfig:
    """Configuration options for parsers.

    Biip strives to do the right thing by default, but you can customize
    some of its the behavior by setting these options.
    """

    gs1_element_strings_verify_date: bool = True
    """Whether to verify that the date in a GS1 element string is valid.

    According to the GS1 General Specification, dates are required to contain a
    valid year and month. Only the day of month can be left as zeros, which
    should be interpreted as the last day of the month.

    Some barcodes include a GS1 element string with all zero dates, requiring
    this check to be disabled.
    """

    rcn_region: RcnRegion | str | None = None
    """The geographical region to use when parsing RCNs.

    Restricted Circulation Numbers (RCN) have different meanings in different
    geographical regions. Specify your region here so that Biip can use the
    right parsing rules.

    This must be set to extract variable weight or price from GTINs.
    """

    rcn_verify_variable_measure: bool = True
    """
    Whether to verify that the variable measure in a RCN matches its check digit.

    Some companies use the variable measure check digit for other purposes,
    requiring this check to be disabled.
    """

    separator_chars: Iterable[str] = ("\x1d",)
    """
    Characters to look for as separators between fields in GS1 messages.

    Defaults to the "group separator" character (byte `0x1d` in ASCII), which is
    commonly returned by barcode scanners in place of the FNC1 barcode symbol
    that separates fields.

    If variable-length fields in the middle of the message are not terminated
    with a separator character, the parser might greedily consume the rest of
    the message.
    """

gs1_element_strings_verify_date class-attribute instance-attribute

gs1_element_strings_verify_date: bool = True

Whether to verify that the date in a GS1 element string is valid.

According to the GS1 General Specification, dates are required to contain a valid year and month. Only the day of month can be left as zeros, which should be interpreted as the last day of the month.

Some barcodes include a GS1 element string with all zero dates, requiring this check to be disabled.

rcn_region class-attribute instance-attribute

rcn_region: RcnRegion | str | None = None

The geographical region to use when parsing RCNs.

Restricted Circulation Numbers (RCN) have different meanings in different geographical regions. Specify your region here so that Biip can use the right parsing rules.

This must be set to extract variable weight or price from GTINs.

rcn_verify_variable_measure class-attribute instance-attribute

rcn_verify_variable_measure: bool = True

Whether to verify that the variable measure in a RCN matches its check digit.

Some companies use the variable measure check digit for other purposes, requiring this check to be disabled.

separator_chars class-attribute instance-attribute

separator_chars: Iterable[str] = ('\x1d',)

Characters to look for as separators between fields in GS1 messages.

Defaults to the "group separator" character (byte 0x1d in ASCII), which is commonly returned by barcode scanners in place of the FNC1 barcode symbol that separates fields.

If variable-length fields in the middle of the message are not terminated with a separator character, the parser might greedily consume the rest of the message.

ParseError

Bases: BiipException

Error raised if parsing of barcode data fails.

Source code in src/biip/_exceptions.py
class ParseError(BiipException):
    """Error raised if parsing of barcode data fails."""

ParseResult dataclass

Results from a successful barcode parsing.

Source code in src/biip/_parser.py
@dataclass(frozen=True)
class ParseResult:
    """Results from a successful barcode parsing."""

    value: str
    """The raw value. Only stripped of surrounding whitespace."""

    symbology_identifier: SymbologyIdentifier | None = None
    """The Symbology Identifier, if any."""

    gtin: Gtin | None = None
    """The extracted [GTIN][biip.gtin.Gtin], if any.

    Is also set if a GS1 Message containing a GTIN was successfully parsed."""

    gtin_error: str | None = None
    """The GTIN parse error, if parsing as a GTIN was attempted and failed."""

    upc: Upc | None = None
    """The extracted [UPC][biip.upc.Upc], if any."""

    upc_error: str | None = None
    """The UPC parse error, if parsing as an UPC was attempted and failed."""

    sscc: Sscc | None = None
    """The extracted [SSCC][biip.sscc.Sscc], if any.

    Is also set if a GS1 Message containing an SSCC was successfully parsed.
    """

    sscc_error: str | None = None
    """The SSCC parse error, if parsing as an SSCC was attempted and failed."""

    gs1_message: GS1Message | None = None
    """The extracted [GS1 Message][biip.gs1_messages.GS1Message], if any."""

    gs1_message_error: str | None = None
    """The GS1 Message parse error.

    If parsing as a GS1 Message was attempted and failed.
    """

    gs1_web_uri: GS1WebURI | None = None
    """The extracted [GS1 Web URI][biip.gs1_web_uris.GS1WebURI], if any."""

    gs1_web_uri_error: str | None = None
    """The GS1 Web URI parse error.

    If parsing as a GS1 Web URI was attempted and failed.
    """

    def __rich_repr__(self) -> Iterator[tuple[str, Any] | tuple[str, Any, Any]]:
        # Skip printing fields with default values
        yield "value", self.value
        yield "symbology_identifier", self.symbology_identifier, None
        yield "gtin", self.gtin, None
        yield "gtin_error", self.gtin_error, None
        yield "upc", self.upc, None
        yield "upc_error", self.upc_error, None
        yield "sscc", self.sscc, None
        yield "sscc_error", self.sscc_error, None
        yield "gs1_message", self.gs1_message, None
        yield "gs1_message_error", self.gs1_message_error, None
        yield "gs1_web_uri", self.gs1_web_uri, None
        yield "gs1_web_uri_error", self.gs1_web_uri_error, None

gs1_message class-attribute instance-attribute

gs1_message: GS1Message | None = None

The extracted GS1 Message, if any.

gs1_message_error class-attribute instance-attribute

gs1_message_error: str | None = None

The GS1 Message parse error.

If parsing as a GS1 Message was attempted and failed.

gs1_web_uri class-attribute instance-attribute

gs1_web_uri: GS1WebURI | None = None

The extracted GS1 Web URI, if any.

gs1_web_uri_error class-attribute instance-attribute

gs1_web_uri_error: str | None = None

The GS1 Web URI parse error.

If parsing as a GS1 Web URI was attempted and failed.

gtin class-attribute instance-attribute

gtin: Gtin | None = None

The extracted GTIN, if any.

Is also set if a GS1 Message containing a GTIN was successfully parsed.

gtin_error class-attribute instance-attribute

gtin_error: str | None = None

The GTIN parse error, if parsing as a GTIN was attempted and failed.

sscc class-attribute instance-attribute

sscc: Sscc | None = None

The extracted SSCC, if any.

Is also set if a GS1 Message containing an SSCC was successfully parsed.

sscc_error class-attribute instance-attribute

sscc_error: str | None = None

The SSCC parse error, if parsing as an SSCC was attempted and failed.

symbology_identifier class-attribute instance-attribute

symbology_identifier: SymbologyIdentifier | None = None

The Symbology Identifier, if any.

upc class-attribute instance-attribute

upc: Upc | None = None

The extracted UPC, if any.

upc_error class-attribute instance-attribute

upc_error: str | None = None

The UPC parse error, if parsing as an UPC was attempted and failed.

value instance-attribute

value: str

The raw value. Only stripped of surrounding whitespace.

parse

parse(
    value: str, *, config: ParseConfig | None = None
) -> ParseResult

Identify data format and parse data.

The current strategy is:

  1. If Symbology Identifier prefix indicates a GTIN or GS1 Message, attempt to parse and validate as that.
  2. Else, if not Symbology Identifier, attempt to parse with all parsers.

Parameters:

  • value (str) –

    The data to classify and parse.

  • config (ParseConfig | None, default: None ) –

    Configuration options for parsers.

Returns:

  • ParseResult

    A ParseResult object with the results and errors from all parsers.

Source code in src/biip/_parser.py
def parse(
    value: str,
    *,
    config: ParseConfig | None = None,
) -> ParseResult:
    """Identify data format and parse data.

    The current strategy is:

    1. If Symbology Identifier prefix indicates a GTIN or GS1 Message,
       attempt to parse and validate as that.
    2. Else, if not Symbology Identifier, attempt to parse with all parsers.

    Args:
        value: The data to classify and parse.
        config: Configuration options for parsers.

    Returns:
        A `ParseResult` object with the results and errors from all parsers.
    """
    if config is None:
        config = ParseConfig()

    value = value.strip()
    result = ParseResult(value=value)

    # Extract Symbology Identifier
    if value.startswith("]"):
        result = replace(
            result, symbology_identifier=SymbologyIdentifier.extract(value)
        )
        assert result.symbology_identifier
        value = value[len(result.symbology_identifier) :]

    # Select parsers
    queue: ParseQueue = []
    if result.symbology_identifier is not None:
        if result.symbology_identifier.gs1_symbology in GS1Symbology.with_gtin():
            queue.append((_parse_gtin, value))
        if (
            result.symbology_identifier.gs1_symbology
            in GS1Symbology.with_gs1_messages()
        ):
            queue.append((_parse_gs1_message, value))
        if result.symbology_identifier.gs1_symbology in GS1Symbology.with_gs1_web_uri():
            queue.append((_parse_gs1_web_uri, value))
    elif value.startswith("http"):
        queue.append((_parse_gs1_web_uri, value))
    if not queue:
        # If we're not able to select a subset based on Symbology Identifiers,
        # run all parsers on the full value.
        queue = [
            (_parse_gs1_message, value),
            (_parse_gtin, value),
            (_parse_sscc, value),
            (_parse_upc, value),
        ]

    # Work through queue of parsers and the values to run them on. Any parser may
    # add additional work to the queue. Only the first result for a field is kept.
    while queue:
        (parse_func, val) = queue.pop(0)
        result = parse_func(val, config, queue, result)

    return result