Skip to content
Open
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
10 changes: 8 additions & 2 deletions baudot/__init__.py
Original file line number Diff line number Diff line change
@@ -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",
]
13 changes: 11 additions & 2 deletions baudot/codecs/__init__.py
Original file line number Diff line number Diff line change
@@ -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",
]
24 changes: 19 additions & 5 deletions baudot/codecs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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
"""
Expand All @@ -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
"""
Expand Down
10 changes: 8 additions & 2 deletions baudot/codecs/ita1_baudot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand Down
10 changes: 8 additions & 2 deletions baudot/codecs/ita2_baudot_murray.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -24,7 +30,7 @@
],
}

US_TABLE = {
US_TABLE: Dict[Shift, List[Value]] = {
Letters: STANDARD_TABLE[Letters].copy(),
Figures: [
'\x00', '3', '\n', '-', ' ', '\x07', '8', '7',
Expand Down
7 changes: 4 additions & 3 deletions baudot/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand Down
15 changes: 12 additions & 3 deletions baudot/handlers/__init__.py
Original file line number Diff line number Diff line change
@@ -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",
]
11 changes: 8 additions & 3 deletions baudot/handlers/core.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
"""
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


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"""
4 changes: 2 additions & 2 deletions baudot/handlers/hexbytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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')
Expand Down
2 changes: 1 addition & 1 deletion baudot/handlers/tape.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
Empty file added baudot/py.typed
Empty file.
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down