diff --git a/src/smllib/reader.py b/src/smllib/reader.py index 6b03e4e..d8d2401 100644 --- a/src/smllib/reader.py +++ b/src/smllib/reader.py @@ -12,8 +12,13 @@ class SmlStreamReader: def __init__(self, build_ctx: Optional[CTX_HINT] = None, crc: Literal['kermit', 'x25'] = 'x25') -> None: self.bytes: bytes = b'' self.build_ctx: CTX_HINT = build_ctx if build_ctx is not None else create_context() - self.crc_func: Callable[[Union[memoryview, bytes]], int] = { - 'x25': crc_module.x25.get_crc, 'kermit': crc_module.kermit.get_crc}[crc] + + # This makes it easy to patch additional crc functions to the module + try: + self.crc_func: Callable[[Union[memoryview, bytes]], int] = getattr(crc_module, crc).get_crc + except AttributeError: + available = [f'"{n:s}"' for n in dir(crc_module) if not n.startswith('_')] + raise ValueError(f'Unsupported CRC "{crc}"! Available: {", ".join(available):s}') def add(self, _bytes: bytes) -> None: self.bytes += _bytes diff --git a/tests/test_crc.py b/tests/test_crc.py index d27491f..f801306 100644 --- a/tests/test_crc.py +++ b/tests/test_crc.py @@ -1,7 +1,12 @@ +import inspect +import typing from binascii import a2b_hex +from typing import Union import pytest +from smllib import SmlStreamReader +from smllib import crc as crc_module from smllib.crc.x25 import get_crc as x25_get_crc @@ -17,3 +22,38 @@ def test_crc_x25(msg) -> None: crc_msg = f'{_msg[-3]:02x}{_msg[-2]:02x}' crc_calc = f'{x25_get_crc(_msg[:-4]):04x}' assert crc_msg == crc_calc + + +def _get_signature() -> inspect.Signature: + return inspect.Signature( + parameters=[ + inspect.Parameter('buf', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Union[memoryview, bytes]) + ], + return_annotation=int + ) + + +@pytest.mark.parametrize('name', (n for n in dir(crc_module) if not n.startswith('_'))) +def test_signature_crc_funcs(name: str) -> None: + crc_impl = getattr(crc_module, name) + crc_sig = inspect.signature(crc_impl.get_crc) + assert crc_sig == _get_signature() + + +def test_type_hint_reader() -> None: + signature = _get_signature() + + a = SmlStreamReader() + hint = typing.get_type_hints(a.crc_func) + assert hint.pop('return') is signature.return_annotation + + assert hint + for name in hint: + assert hint[name] == signature.parameters[name].annotation + + +def test_invalid_crc_name() -> None: + + with pytest.raises(ValueError) as e: + SmlStreamReader(crc='asfd') + assert str(e.value) == 'Unsupported CRC "asfd"! Available: "kermit", "x25"'