diff --git a/baudot/__init__.py b/baudot/__init__.py index fef9753..de8c88f 100644 --- a/baudot/__init__.py +++ b/baudot/__init__.py @@ -1,6 +1,12 @@ - """ Baudot – Tools for handling stateful 5-bit encoding """ -from .core import encode, encode_str, decode, decode_to_str +from .core import decode, decode_to_str, encode, encode_str + +__all__ = [ + "encode", + "encode_str", + "decode", + "decode_to_str", +] diff --git a/baudot/codecs/__init__.py b/baudot/codecs/__init__.py index ce31756..2bea9f3 100644 --- a/baudot/codecs/__init__.py +++ b/baudot/codecs/__init__.py @@ -1,9 +1,18 @@ - """ Codecs are the tools used to convert encoded-data (5-bit digits) into Unicode characters and back. """ -from .core import BaudotCodec, SimpleTabledCodec, Shift +from .core import BaudotCodec, Shift, SimpleTabledCodec from .ita1_baudot import ITA1_CONTINENTAL, ITA1_UK from .ita2_baudot_murray import ITA2_STANDARD, ITA2_US + +__all__ = [ + "BaudotCodec", + "SimpleTabledCodec", + "Shift", + "ITA1_CONTINENTAL", + "ITA1_UK", + "ITA2_STANDARD", + "ITA2_US", +] diff --git a/baudot/codecs/core.py b/baudot/codecs/core.py index edfad87..8111983 100644 --- a/baudot/codecs/core.py +++ b/baudot/codecs/core.py @@ -28,13 +28,17 @@ class BaudotCodec(ABC): """ @abstractmethod - def encode(self, value: Value, state: Shift) -> Tuple[int, Shift]: + def encode( + self, + value: Value, + state: Optional[Shift] + ) -> Tuple[int, Optional[Shift]]: """ Abstract method for encoding a single character or state shift """ @abstractmethod - def decode(self, code: int, state: Shift) -> Union[str, Shift]: + def decode(self, code: int, state: Optional[Shift]) -> Value: """ Abstract method for decoding a single code. """ @@ -71,7 +75,11 @@ def __init__(self, name: str, tables: Dict[Shift, Table]): self.encoding_any: Dict[Value, int] = enc_any self.encoding_others: Dict[Value, Set[Tuple[int, Shift]]] = enc_others - def encode(self, value: Value, state: Shift) -> Tuple[int, Shift]: + def encode( + self, + value: Value, + state: Optional[Shift] + ) -> Tuple[int, Optional[Shift]]: """ Get the code of the given character of Shift for this codec. @@ -134,7 +142,7 @@ def decode(self, code: int, state: Optional[Shift]) -> Value: return self.decoding_table[state][code] -def _verify_tables(tables: Dict[Shift, Table]): +def _verify_tables(tables: Dict[Shift, Table]) -> None: """ Function for verifying that a given input table is correct """ @@ -155,7 +163,13 @@ def _verify_tables(tables: Dict[Shift, Table]): raise IncoherentTable("Shifts in the tables don't match their keys") -def _make_simple_encoding_table(tables: Dict[Shift, Table]): +def _make_simple_encoding_table( + tables: Dict[Shift, Table] + ) -> Tuple[ + Dict[Value, Tuple[int, Shift]], + Dict[Value, int], + Dict[Value, Set[Tuple[int, Shift]]], + ]: """ Generates the encoding tables by reversing the decoding table """ diff --git a/baudot/codecs/ita1_baudot.py b/baudot/codecs/ita1_baudot.py index 4bff746..170841d 100644 --- a/baudot/codecs/ita1_baudot.py +++ b/baudot/codecs/ita1_baudot.py @@ -2,13 +2,19 @@ Codec definitions for first-generation Baudot codes (a.k.a. ITA1) """ # pylint: disable=invalid-name +from __future__ import annotations + +from typing import Dict, List, TYPE_CHECKING from .core import Shift, SimpleTabledCodec +if TYPE_CHECKING: + from .core import Value + Figures = Shift('Figures') Letters = Shift('Letters') -CONTINENTAL_TABLE = { +CONTINENTAL_TABLE: Dict[Shift, List[Value]] = { Letters: [ ' ', 'A', 'E', 'É', 'Y', 'U', 'I', 'O', Figures, 'J', 'G', 'H', 'B', 'C', 'F', 'D', @@ -23,7 +29,7 @@ ], } -UK_TABLE = { +UK_TABLE: Dict[Shift, List[Value]] = { Letters: [ ' ', 'A', 'E', '/', 'Y', 'U', 'I', 'O', Figures, 'J', 'G', 'H', 'B', 'C', 'F', 'D', diff --git a/baudot/codecs/ita2_baudot_murray.py b/baudot/codecs/ita2_baudot_murray.py index db8cc99..b9112cd 100644 --- a/baudot/codecs/ita2_baudot_murray.py +++ b/baudot/codecs/ita2_baudot_murray.py @@ -3,13 +3,19 @@ (a.k.a. Baudot-Murray or ITA2) """ # pylint: disable=invalid-name +from __future__ import annotations + +from typing import Dict, List, TYPE_CHECKING from .core import Shift, SimpleTabledCodec +if TYPE_CHECKING: + from .core import Value + Letters = Shift('Letters') Figures = Shift('Figures') -STANDARD_TABLE = { +STANDARD_TABLE: Dict[Shift, List[Value]] = { Letters: [ '\x00', 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K', @@ -24,7 +30,7 @@ ], } -US_TABLE = { +US_TABLE: Dict[Shift, List[Value]] = { Letters: STANDARD_TABLE[Letters].copy(), Figures: [ '\x00', '3', '\n', '-', ' ', '\x07', '8', '7', diff --git a/baudot/core.py b/baudot/core.py index c792bc3..a85d11d 100644 --- a/baudot/core.py +++ b/baudot/core.py @@ -9,7 +9,7 @@ from .codecs import BaudotCodec, Shift -def encode(stream: TextIOBase, codec: BaudotCodec, writer: BaudotWriter): +def encode(stream: TextIOBase, codec: BaudotCodec, writer: BaudotWriter) -> None: """ Encode unicode characters from an input stream to an output writer, using the given codec. @@ -30,6 +30,7 @@ def encode(stream: TextIOBase, codec: BaudotCodec, writer: BaudotWriter): buffer.append(code) if new_state != state: + assert new_state is not None state_code, _ = codec.encode(new_state, None) buffer.append(state_code) state = new_state @@ -38,7 +39,7 @@ def encode(stream: TextIOBase, codec: BaudotCodec, writer: BaudotWriter): writer.write(buffer.pop(-1)) -def encode_str(chars: str, codec: BaudotCodec, writer: BaudotWriter): +def encode_str(chars: str, codec: BaudotCodec, writer: BaudotWriter) -> None: """ Encode unicode characters from an input string to an output writer, using the given codec. @@ -51,7 +52,7 @@ def encode_str(chars: str, codec: BaudotCodec, writer: BaudotWriter): encode(stream, codec, writer) -def decode(reader: BaudotReader, codec: BaudotCodec, stream: TextIOBase): +def decode(reader: BaudotReader, codec: BaudotCodec, stream: TextIOBase) -> None: """ Decode a baudot code stream from a reader to a unicode stream, using a given codec. diff --git a/baudot/handlers/__init__.py b/baudot/handlers/__init__.py index dc03f80..eec777a 100644 --- a/baudot/handlers/__init__.py +++ b/baudot/handlers/__init__.py @@ -1,9 +1,18 @@ - """ The handlers are interfaces to read and write 5-bit data from a variety of formats. """ -from .core import BaudotWriter, BaudotReader -from .tape import TapeReader, TapeWriter, TapeConfig +from .core import BaudotReader, BaudotWriter from .hexbytes import HexBytesReader, HexBytesWriter +from .tape import TapeConfig, TapeReader, TapeWriter + +__all__ = [ + "BaudotWriter", + "BaudotReader", + "TapeReader", + "TapeWriter", + "TapeConfig", + "HexBytesReader", + "HexBytesWriter", +] diff --git a/baudot/handlers/core.py b/baudot/handlers/core.py index 09e3695..7242e60 100644 --- a/baudot/handlers/core.py +++ b/baudot/handlers/core.py @@ -1,18 +1,23 @@ """ Core utilities for Baudot input/output handlers """ +from __future__ import annotations from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterator class BaudotReader(ABC): """Abstract Base Class for a reader""" - def __iter__(self): + def __iter__(self) -> Iterator[int]: return self @abstractmethod - def __next__(self): + def __next__(self) -> int: pass @@ -20,5 +25,5 @@ class BaudotWriter(ABC): """Abstract Base Class for a writer""" @abstractmethod - def write(self, code: int): + def write(self, code: int) -> None: """Write a single code to the output""" diff --git a/baudot/handlers/hexbytes.py b/baudot/handlers/hexbytes.py index be3bffd..2c06636 100644 --- a/baudot/handlers/hexbytes.py +++ b/baudot/handlers/hexbytes.py @@ -16,7 +16,7 @@ class HexBytesReader(BaudotReader): def __init__(self, stream: BufferedIOBase): self.stream = stream - def __next__(self): + def __next__(self) -> int: hex_byte = self.stream.read(2) if not hex_byte: raise StopIteration() @@ -38,7 +38,7 @@ class HexBytesWriter(BaudotWriter): def __init__(self, stream: BufferedIOBase): self.stream = stream - def write(self, code: int): + def write(self, code: int) -> None: """Writes a code as an hexadecimal value""" if not 0 <= code < 32: raise WriteError('Invalid 5-bit character code') diff --git a/baudot/handlers/tape.py b/baudot/handlers/tape.py index 87e104e..3696bba 100644 --- a/baudot/handlers/tape.py +++ b/baudot/handlers/tape.py @@ -61,7 +61,7 @@ def __init__(self, stream: TextIOBase, config: TapeConfig = DEFAULT_TAPE): self.stream = stream self.config = config - def write(self, code: int): + def write(self, code: int) -> None: """Writes a code to tape""" if not 0 <= code < 32: raise WriteError('Invalid 5-bit character code') diff --git a/baudot/py.typed b/baudot/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2f38968 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[tool.mypy] +files = "baudot" +strict = true +enable_error_code = [ + "ignore-without-code", + "redundant-expr", + "truthy-bool", + "truthy-iterable", +] +warn_unreachable = true diff --git a/setup.py b/setup.py index f0e2976..5da3379 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ license='LGPLv3', python_requires='>=3.7.0', packages=find_packages(exclude=('tests',)), + package_data={"baudot": ["py.typed"]}, test_requires=["pytest", "hypothesis", "coverage", "pylint"], classifiers=[ 'Development Status :: 4 - Beta',