From 69a040eb3e70adf733e8656f3784d9f574b94765 Mon Sep 17 00:00:00 2001 From: Toshiki Kamiya <108583064+tkamiya22@users.noreply.github.com> Date: Sun, 21 Jul 2024 07:43:15 +0900 Subject: [PATCH 1/5] refactor: add type hints --- src/rmqrcode/console.py | 8 +- src/rmqrcode/encoder/alphanumeric_encoder.py | 4 +- src/rmqrcode/encoder/encoder_base.py | 12 +-- src/rmqrcode/encoder/numeric_encoder.py | 4 +- .../format/alignment_pattern_coordinates.py | 2 +- src/rmqrcode/format/mask.py | 2 +- src/rmqrcode/format/rmqr_versions.py | 23 ++++- src/rmqrcode/qr_image.py | 11 ++- src/rmqrcode/rmqrcode.py | 96 +++++++++++-------- src/rmqrcode/segments.py | 53 +++++++--- src/rmqrcode/util/error_correction.py | 6 +- src/rmqrcode/util/galois_fields.py | 4 +- src/rmqrcode/util/utilities.py | 8 +- 13 files changed, 150 insertions(+), 83 deletions(-) diff --git a/src/rmqrcode/console.py b/src/rmqrcode/console.py index 66c7bfc..17eff35 100644 --- a/src/rmqrcode/console.py +++ b/src/rmqrcode/console.py @@ -1,6 +1,8 @@ #!/usr/bin/env python import argparse import sys +from pathlib import Path +from typing import Union from rmqrcode import ( DataTooLongError, @@ -12,12 +14,12 @@ ) -def _show_error_and_exit(msg): +def _show_error_and_exit(msg: str): print(msg, file=sys.stderr) sys.exit(1) -def _make_qr(data, ecc, version, fit_strategy): +def _make_qr(data: str, ecc: ErrorCorrectionLevel, version: Union[str, None], fit_strategy: FitStrategy): if version is None: qr = rMQR.fit(data, ecc=ecc, fit_strategy=fit_strategy) else: @@ -30,7 +32,7 @@ def _make_qr(data, ecc, version, fit_strategy): return qr -def _save_image(qr, output): +def _save_image(qr: rMQR, output: Union[str, bytes, Path]): image = QRImage(qr) try: image.save(output) diff --git a/src/rmqrcode/encoder/alphanumeric_encoder.py b/src/rmqrcode/encoder/alphanumeric_encoder.py index 2a0839f..72dbf68 100644 --- a/src/rmqrcode/encoder/alphanumeric_encoder.py +++ b/src/rmqrcode/encoder/alphanumeric_encoder.py @@ -70,8 +70,8 @@ def _encoded_bits(cls, data): return res @classmethod - def _group_by_2characters(cls, data): - res = [] + def _group_by_2characters(cls, data: str): + res: list[str] = [] while data != "": res.append(data[:2]) data = data[2:] diff --git a/src/rmqrcode/encoder/encoder_base.py b/src/rmqrcode/encoder/encoder_base.py index ccb07ce..a215a55 100644 --- a/src/rmqrcode/encoder/encoder_base.py +++ b/src/rmqrcode/encoder/encoder_base.py @@ -6,7 +6,7 @@ class EncoderBase(ABC): @classmethod @abstractmethod - def mode_indicator(cls): + def mode_indicator(cls) -> str: """Mode indicator defined in the Table 2. Returns: @@ -17,7 +17,7 @@ def mode_indicator(cls): @classmethod @abstractmethod - def encode(cls, data, character_count_indicator_length): + def encode(cls, data: str, character_count_indicator_length: int) -> str: """Encodes data and returns it. Args: @@ -42,7 +42,7 @@ def encode(cls, data, character_count_indicator_length): @classmethod @abstractmethod - def _encoded_bits(cls, data): + def _encoded_bits(cls, data: str) -> str: """Encodes data and returns it. This method encodes the raw data without the meta data like the mode @@ -59,7 +59,7 @@ def _encoded_bits(cls, data): @classmethod @abstractmethod - def length(cls, data): + def length(cls, data: str, character_count_indicator_length: int) -> int: """Compute the length of the encoded bits. Args: @@ -73,7 +73,7 @@ def length(cls, data): @classmethod @abstractmethod - def characters_num(cls, data): + def characters_num(cls, data: str) -> int: """Returns the number of the characters of the data. Args: @@ -87,7 +87,7 @@ def characters_num(cls, data): @classmethod @abstractmethod - def is_valid_characters(cls, data): + def is_valid_characters(cls, data: str) -> bool: """Checks whether the data does not include invalid character. Args: diff --git a/src/rmqrcode/encoder/numeric_encoder.py b/src/rmqrcode/encoder/numeric_encoder.py index 208e7f5..95b570e 100644 --- a/src/rmqrcode/encoder/numeric_encoder.py +++ b/src/rmqrcode/encoder/numeric_encoder.py @@ -22,8 +22,8 @@ def _encoded_bits(cls, data): return res @classmethod - def _group_by_3characters(cls, data): - res = [] + def _group_by_3characters(cls, data: str): + res: list[str] = [] while data != "": res.append(data[:3]) data = data[3:] diff --git a/src/rmqrcode/format/alignment_pattern_coordinates.py b/src/rmqrcode/format/alignment_pattern_coordinates.py index 7b95c3f..ab05be5 100644 --- a/src/rmqrcode/format/alignment_pattern_coordinates.py +++ b/src/rmqrcode/format/alignment_pattern_coordinates.py @@ -1,4 +1,4 @@ -AlignmentPatternCoordinates = { +AlignmentPatternCoordinates: dict[int, list[int]] = { 27: [], 43: [21], 59: [19, 39], diff --git a/src/rmqrcode/format/mask.py b/src/rmqrcode/format/mask.py index 4ce92b7..329b1bf 100644 --- a/src/rmqrcode/format/mask.py +++ b/src/rmqrcode/format/mask.py @@ -1,2 +1,2 @@ -def mask(x, y): +def mask(x: int, y: int): return (y // 2 + x // 3) % 2 == 0 diff --git a/src/rmqrcode/format/rmqr_versions.py b/src/rmqrcode/format/rmqr_versions.py index 561b6c8..cc1e744 100644 --- a/src/rmqrcode/format/rmqr_versions.py +++ b/src/rmqrcode/format/rmqr_versions.py @@ -1,7 +1,28 @@ +from typing import TypedDict + from ..encoder import AlphanumericEncoder, ByteEncoder, KanjiEncoder, NumericEncoder +from ..encoder.encoder_base import EncoderBase from .error_correction_level import ErrorCorrectionLevel -rMQRVersions = { + +class BlockDef(TypedDict): + num: int + c: int + k: int + + +class rMQRVersion(TypedDict): + version_indicator: int + height: int + width: int + remainder_bits: int + character_count_indicator_length: dict[type[EncoderBase], int] + codewords_total: int + blocks: dict[ErrorCorrectionLevel, list[BlockDef]] + number_of_data_bits: dict[ErrorCorrectionLevel, int] + + +rMQRVersions: dict[str, rMQRVersion] = { "R7x43": { "version_indicator": 0b00000, "height": 7, diff --git a/src/rmqrcode/qr_image.py b/src/rmqrcode/qr_image.py index a2ee637..10304c4 100644 --- a/src/rmqrcode/qr_image.py +++ b/src/rmqrcode/qr_image.py @@ -1,8 +1,13 @@ +from pathlib import Path +from typing import Union + from PIL import Image, ImageDraw +from .rmqrcode import rMQR + class QRImage: - def __init__(self, qr, module_size=10): + def __init__(self, qr: rMQR, module_size: int = 10): self._module_size = module_size qr_list = qr.to_list() self._img = Image.new("RGB", (len(qr_list[0]) * module_size, len(qr_list) * module_size), (255, 255, 255)) @@ -20,10 +25,10 @@ def get_ndarray(self): return np.array(self._img) - def save(self, name): + def save(self, name: Union[str, bytes, Path]): self._img.save(name) - def _make_image(self, qr_list): + def _make_image(self, qr_list: list[list[int]]): draw = ImageDraw.Draw(self._img) for y in range(len(qr_list)): for x in range(len(qr_list[0])): diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index c546062..d7b491e 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -20,6 +20,7 @@ """ import logging +from typing import TypedDict, Union from . import encoder from . import segments as qr_segments @@ -30,13 +31,20 @@ from .format.error_correction_level import ErrorCorrectionLevel from .format.generator_polynomials import GeneratorPolynomials from .format.mask import mask -from .format.rmqr_versions import rMQRVersions +from .format.rmqr_versions import BlockDef, rMQRVersions from .util.error_correction import compute_bch, compute_reed_solomon from .util.utilities import split_into_8bits QUIET_ZONE_MODULES = 2 +class _OKVersion(TypedDict): + version: str + width: int + height: int + segments: list[qr_segments.Segment] + + class rMQR: """A class to make an rMQR Code.""" @@ -55,7 +63,9 @@ def _init_logger(): return logger @staticmethod - def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): + def fit( + data: str, ecc: ErrorCorrectionLevel = ErrorCorrectionLevel.M, fit_strategy: FitStrategy = FitStrategy.BALANCED + ): """Compute optimized rMQR code with the rMQROptimizer class. Args: @@ -72,7 +82,7 @@ def fit(data, ecc=ErrorCorrectionLevel.M, fit_strategy=FitStrategy.BALANCED): """ return rMQROptimizer.compute(data, ecc, fit_strategy) - def _optimized_segments(self, data): + def _optimized_segments(self, data: str): """Returns optimized segments computed by SegmentOptimizer. Args: @@ -85,7 +95,13 @@ def _optimized_segments(self, data): optimizer = qr_segments.SegmentOptimizer() return optimizer.compute(data, self.version_name(), self._error_correction_level) - def __init__(self, version, ecc, with_quiet_zone=True, logger=None): + def __init__( + self, + version: str, + ecc: ErrorCorrectionLevel, + with_quiet_zone: bool = True, + logger: Union[logging.Logger, None] = None, + ): self._logger = logger or rMQR._init_logger() if not rMQR.validate_version(version): @@ -97,9 +113,9 @@ def __init__(self, version, ecc, with_quiet_zone=True, logger=None): self._width = self._qr_version["width"] self._error_correction_level = ecc self._qr = rMQRCore(self._width, self._height) - self._segments = [] + self._segments: list[qr_segments.Segment] = [] - def add_segment(self, data, encoder_class=encoder.ByteEncoder): + def add_segment(self, data: str, encoder_class: type[encoder.encoder_base.EncoderBase] = encoder.ByteEncoder): """Adds the segment. A segment consists of data and an encoding mode. @@ -115,7 +131,7 @@ def add_segment(self, data, encoder_class=encoder.ByteEncoder): """ self._segments.append({"data": data, "encoder_class": encoder_class}) - def add_segments(self, segments): + def add_segments(self, segments: list[qr_segments.Segment]): """Add the segments. Args: @@ -187,7 +203,7 @@ def _encode_data(self): return res - def _append_terminator_if_possible(self, data, data_bits_max): + def _append_terminator_if_possible(self, data: str, data_bits_max: int): """Appends the terminator. This method appends the terminator at the end of data and returns the @@ -259,7 +275,7 @@ def width(self): """ return self._width - def value_at(self, x, y): + def value_at(self, x: int, y: int): """DEPRECATED: Returns the color at the point of (x, y). Returns: @@ -272,7 +288,7 @@ def value_at(self, x, y): """ return self._qr[y][x] - def to_list(self, with_quiet_zone=True): + def to_list(self, with_quiet_zone: bool = True): """Converts to two-dimensional list and returns it. The value is 1 for the dark module and 0 for the light module. @@ -284,7 +300,7 @@ def to_list(self, with_quiet_zone=True): list: Converted list. """ - res = [] + res: list[list[int]] = [] if with_quiet_zone: for y in range(QUIET_ZONE_MODULES): res.append([0] * (self.width() + QUIET_ZONE_MODULES * 2)) @@ -296,7 +312,7 @@ def to_list(self, with_quiet_zone=True): res = self._qr.to_binary_list() return res - def __str__(self, with_quiet_zone=True): + def __str__(self, with_quiet_zone: bool = True): res = f"rMQR Version R{self._height}x{self._width}:\n" res += self._qr.__str__(with_quiet_zone) return res @@ -310,7 +326,7 @@ def _compute_format_info(self): format_information_data = format_information_data << 12 | reminder_polynomial return format_information_data - def _make_codewords(self, encoded_data, codewords_num): + def _make_codewords(self, encoded_data: str, codewords_num: int): """Makes codeword sequence from encoded data. If the length of generated codeword sequence is less than the `codewords_num`, @@ -335,7 +351,7 @@ def _make_codewords(self, encoded_data, codewords_num): codewords.append("00010001") return codewords - def _split_into_blocks(self, codewords, blocks_definition): + def _split_into_blocks(self, codewords: list[str], blocks_definition: list[BlockDef]): """Splits codewords into several blocks. Args: @@ -347,7 +363,7 @@ def _split_into_blocks(self, codewords, blocks_definition): """ data_idx = 0 - blocks = [] + blocks: list[Block] = [] for block_definition in blocks_definition: for i in range(block_definition["num"]): data_codewords_num = block_definition["k"] @@ -359,7 +375,7 @@ def _split_into_blocks(self, codewords, blocks_definition): data_idx += data_codewords_num return blocks - def _make_final_codewords(self, blocks): + def _make_final_codewords(self, blocks: list["Block"]): """Makes the final message codeword sequence. This method computes the final codeword sequence from the given blocks. For example, @@ -383,7 +399,7 @@ def _make_final_codewords(self, blocks): list: The list of codeword strings. """ - final_codewords = [] + final_codewords: list[str] = [] # Add data codewords # The last block always has the most codewords. for i in range(blocks[-1].data_length()): @@ -410,7 +426,7 @@ def _make_final_codewords(self, blocks): return final_codewords @staticmethod - def validate_version(version_name): + def validate_version(version_name: str): """Check if the given version_name is valid Args: @@ -437,7 +453,7 @@ class rMQROptimizer: """A class to compute optimized rMQR code for input data.""" @staticmethod - def compute(data, ecc, fit_strategy): + def compute(data: str, ecc: ErrorCorrectionLevel, fit_strategy: FitStrategy): """Attempts to make an rMQR have optimized version for given data. Args: @@ -452,9 +468,9 @@ def compute(data, ecc, fit_strategy): rmqrcode.DataTooLongError: If the data is too long to encode. """ - ok_versions = [] - determined_width = set() - determined_height = set() + ok_versions: list[_OKVersion] = [] + determined_width: set[int] = set() + determined_height: set[int] = set() for version_name, qr_version in rMQRVersions.items(): optimizer = qr_segments.SegmentOptimizer() @@ -481,17 +497,17 @@ def compute(data, ecc, fit_strategy): if fit_strategy == FitStrategy.MINIMIZE_WIDTH: - def sort_key(x): + def sort_key(x: _OKVersion): return x["width"] elif fit_strategy == FitStrategy.MINIMIZE_HEIGHT: - def sort_key(x): + def sort_key(x: _OKVersion): return x["height"] elif fit_strategy == FitStrategy.BALANCED: - def sort_key(x): + def sort_key(x: _OKVersion): return x["height"] * 9 + x["width"] selected = sorted(ok_versions, key=sort_key)[0] @@ -504,12 +520,12 @@ def sort_key(x): class rMQRCore: "A class correspond to a grid of modules of rMQR code." - def __init__(self, width, height): + def __init__(self, width: int, height: int): self._width = width self._height = height self._qr = [[Color.UNDEFINED for x in range(self._width)] for y in range(self._height)] - def get_data(self, x, y): + def get_data(self, x: int, y: int): """Returns the module value at x-th column and y-th row. Args: @@ -629,7 +645,7 @@ def _put_timing_pattern_vertical(self): if self._qr[i][j] == Color.UNDEFINED: self._qr[i][j] = color - def put_format_information(self, format_information): + def put_format_information(self, format_information: int): """Format information placement. Args: @@ -642,7 +658,7 @@ def put_format_information(self, format_information): self._put_format_information_finder_pattern_side(format_information) self._put_format_information_finder_sub_pattern_side(format_information) - def _put_format_information_finder_pattern_side(self, format_information): + def _put_format_information_finder_pattern_side(self, format_information: int): """Format information placement (finder pattern side). This method computes masked format information data and puts it. The mask @@ -664,7 +680,7 @@ def _put_format_information_finder_pattern_side(self, format_information): dj = n // 5 self._qr[si + di][sj + dj] = Color.BLACK if format_information >> n & 1 else Color.WHITE - def _put_format_information_finder_sub_pattern_side(self, format_information): + def _put_format_information_finder_sub_pattern_side(self, format_information: int): """Format information placement (finder sub pattern side). This method computes masked format information data and puts it. The mask @@ -695,7 +711,7 @@ def _put_format_information_finder_sub_pattern_side(self, format_information): Color.BLACK if format_information >> 17 & 1 else Color.WHITE ) - def put_data(self, final_codewords, remainder_bits_num): + def put_data(self, final_codewords: list[str], remainder_bits_num: int): """Symbol character placement. This method puts data into the encoding region of the rMQR Code. The data @@ -715,7 +731,7 @@ def put_data(self, final_codewords, remainder_bits_num): mask_area = self._put_final_codewords(final_codewords, remainder_bits_num) self._apply_mask(mask_area) - def _put_final_codewords(self, final_codewords, reminder_bits_num): + def _put_final_codewords(self, final_codewords: list[str], reminder_bits_num: int): """Puts the final codeword sequence. This method puts the final codeword sequence into the encoding region of the rMQR Code. @@ -778,7 +794,7 @@ def _put_final_codewords(self, final_codewords, reminder_bits_num): return mask_area - def _apply_mask(self, mask_area): + def _apply_mask(self, mask_area: list[list[bool]]): """Data masking. This method applies the data mask. @@ -801,7 +817,7 @@ def _apply_mask(self, mask_area): elif self._qr[y][x] == Color.WHITE: self._qr[y][x] = Color.BLACK - def __str__(self, with_quiet_zone=True): + def __str__(self, with_quiet_zone: bool = True): show = { Color.WHITE: "_", Color.BLACK: "X", @@ -841,13 +857,13 @@ class Block: """ - def __init__(self, data_codewords_num, ecc_codewords_num): + def __init__(self, data_codewords_num: int, ecc_codewords_num: int): self._data_codewords_num = data_codewords_num - self._data_codewords = [] + self._data_codewords: list[str] = [] self._ecc_codewords_num = ecc_codewords_num - self._ecc_codewords = [] + self._ecc_codewords: list[str] = [] - def set_data_and_compute_ecc(self, data_codewords): + def set_data_and_compute_ecc(self, data_codewords: list[str]): """Set data and compute ecc. Args: @@ -860,7 +876,7 @@ def set_data_and_compute_ecc(self, data_codewords): self._data_codewords = data_codewords self._compute_ecc_codewords() - def get_data_at(self, index): + def get_data_at(self, index: int): """Get data codeword at the index. Args: @@ -872,7 +888,7 @@ def get_data_at(self, index): """ return self._data_codewords[index] - def get_ecc_at(self, index): + def get_ecc_at(self, index: int): """Get ecc codeword at the index. Args: diff --git a/src/rmqrcode/segments.py b/src/rmqrcode/segments.py index 1dc0fd6..75260de 100644 --- a/src/rmqrcode/segments.py +++ b/src/rmqrcode/segments.py @@ -1,8 +1,23 @@ +from typing import TypedDict + from . import encoder from .errors import DataTooLongError +from .format.error_correction_level import ErrorCorrectionLevel from .format.rmqr_versions import rMQRVersions -encoders = [ + +class Best(TypedDict): + cost: int + index: tuple[int, int, int] + """n, mode, unfilled_length""" + + +class Segment(TypedDict): + data: str + encoder_class: type[encoder.encoder_base.EncoderBase] + + +encoders: list[type[encoder.encoder_base.EncoderBase]] = [ encoder.NumericEncoder, encoder.AlphanumericEncoder, encoder.ByteEncoder, @@ -10,7 +25,7 @@ ] -def compute_length(segments, version_name): +def compute_length(segments: list[Segment], version_name: str): """Computes the sum of length of the segments. Args: @@ -44,10 +59,14 @@ class SegmentOptimizer: INF = 100000 def __init__(self): - self.dp = [[[self.INF for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1)] - self.parents = [[[-1 for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1)] - - def compute(self, data, version, ecc): + self.dp: list[list[list[int]]] = [ + [[self.INF for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1) + ] + self.parents: list[list[list[tuple[int, int, int]]]] = [ + [[-1 for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1) + ] + + def compute(self, data: str, version: str, ecc: ErrorCorrectionLevel): """Computes the optimize segmentation for the given data. Args: @@ -75,7 +94,7 @@ def compute(self, data, version, ecc): segments = self._compute_segments(path, data) return segments - def _compute_costs(self, data): + def _compute_costs(self, data: str): """Computes costs by dynamic programming. This method computes costs of the dynamic programming table. Define @@ -119,7 +138,7 @@ def _compute_costs(self, data): self.dp[n + 1][new_mode][new_length] = self.dp[n][mode][unfilled_length] + cost self.parents[n + 1][new_mode][new_length] = (n, mode, unfilled_length) - def _compute_new_state_without_mode_changing(self, character, new_mode, unfilled_length): + def _compute_new_state_without_mode_changing(self, character: str, new_mode: int, unfilled_length: int): """Computes the new state values without mode changing. Args: @@ -144,9 +163,11 @@ def _compute_new_state_without_mode_changing(self, character, new_mode, unfilled elif encoder_class == encoder.KanjiEncoder: new_length = 0 cost = 13 + else: + raise NotImplementedError() return (cost, new_length) - def _compute_new_state_with_mode_changing(self, character, new_mode, unfilled_length): + def _compute_new_state_with_mode_changing(self, character: str, new_mode: int, unfilled_length: int): """Computes the new state values with mode changing. Args: @@ -164,10 +185,12 @@ def _compute_new_state_with_mode_changing(self, character, new_mode, unfilled_le new_length = 1 elif encoder_class in [encoder.ByteEncoder, encoder.KanjiEncoder]: new_length = 0 + else: + raise NotImplementedError() cost = encoder_class.length(character, character_count_indicator_length) return (cost, new_length) - def _find_best(self, data): + def _find_best(self, data: str) -> Best: """Find the index which has the minimum costs. Args: @@ -179,7 +202,7 @@ def _find_best(self, data): """ best = self.INF - best_index = (-1, -1) + best_index: tuple[int, int, int] = (-1, -1) for mode in range(4): for unfilled_length in range(3): if self.dp[len(data)][mode][unfilled_length] < best: @@ -187,7 +210,7 @@ def _find_best(self, data): best_index = (len(data), mode, unfilled_length) return {"cost": best, "index": best_index} - def _reconstruct_path(self, best_index): + def _reconstruct_path(self, best_index: tuple[int, int, int]): """Reconstructs the path. Args: @@ -197,7 +220,7 @@ def _reconstruct_path(self, best_index): list: The path of minimum cost in the dynamic programming table. """ - path = [] + path: list[tuple[int, int, int]] = [] index = best_index while index[0] != 0: path.append(index) @@ -205,7 +228,7 @@ def _reconstruct_path(self, best_index): path.reverse() return path - def _compute_segments(self, path, data): + def _compute_segments(self, path: list[tuple[int, int, int]], data: str): """Computes the segments. This method computes the segments. The adjacent characters has same mode are merged. @@ -218,7 +241,7 @@ def _compute_segments(self, path, data): list: The list of segments. """ - segments = [] + segments: list[Segment] = [] current_segment_data = "" current_mode = -1 for p in path: diff --git a/src/rmqrcode/util/error_correction.py b/src/rmqrcode/util/error_correction.py index 14943a5..cb72424 100644 --- a/src/rmqrcode/util/error_correction.py +++ b/src/rmqrcode/util/error_correction.py @@ -2,7 +2,7 @@ from .utilities import msb, to_binary -def compute_bch(data): +def compute_bch(data: int): data <<= 12 g = 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0 @@ -17,7 +17,7 @@ def compute_bch(data): gf = GaloisFields() -def compute_reed_solomon(data, g, num_error_codewords): +def compute_reed_solomon(data: list[str], g: list[int], num_error_codewords: int): f = list(map(lambda x: int(x, 2), data)) for i in range(num_error_codewords): @@ -30,7 +30,7 @@ def compute_reed_solomon(data, g, num_error_codewords): for j in range(len(g)): f[i + j] ^= gf.e2i[(g[j] + mult) % 255] - rs_codewords = [] + rs_codewords: list[str] = [] for i in range(num_error_codewords): rs_codewords.append(to_binary(f[-num_error_codewords + i], 8)) diff --git a/src/rmqrcode/util/galois_fields.py b/src/rmqrcode/util/galois_fields.py index 4a0cdf7..f32396a 100644 --- a/src/rmqrcode/util/galois_fields.py +++ b/src/rmqrcode/util/galois_fields.py @@ -1,7 +1,7 @@ # GF(2^8) class GaloisFields: - e2i = {} - i2e = {} + e2i: dict[int, int] = {} + i2e: dict[int, int] = {} def __init__(self): # Irreducible polynomial in GF(2^8) diff --git a/src/rmqrcode/util/utilities.py b/src/rmqrcode/util/utilities.py index 1f4f4c4..1011a62 100644 --- a/src/rmqrcode/util/utilities.py +++ b/src/rmqrcode/util/utilities.py @@ -1,13 +1,13 @@ -def msb(n): +def msb(n: int): return len(bin(n)) - 2 -def to_binary(data, len): +def to_binary(data: int, len: int): return bin(data)[2:].zfill(len) -def split_into_8bits(data): - codewords = [] +def split_into_8bits(data: str): + codewords: list[str] = [] while len(data) >= 8: codewords.append(data[:8]) data = data[8:] From 57db7db2fc0cdb48a842936c9cae8f582d3b9987 Mon Sep 17 00:00:00 2001 From: Toshiki Kamiya <108583064+tkamiya22@users.noreply.github.com> Date: Sun, 21 Jul 2024 08:18:07 +0900 Subject: [PATCH 2/5] refactor: Rename an argument to match super class --- src/rmqrcode/encoder/byte_encoder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmqrcode/encoder/byte_encoder.py b/src/rmqrcode/encoder/byte_encoder.py index 99a9518..197cf07 100644 --- a/src/rmqrcode/encoder/byte_encoder.py +++ b/src/rmqrcode/encoder/byte_encoder.py @@ -7,9 +7,9 @@ def mode_indicator(cls): return "011" @classmethod - def _encoded_bits(cls, s): + def _encoded_bits(cls, data): res = "" - encoded = s.encode("utf-8") + encoded = data.encode("utf-8") for byte in encoded: res += bin(byte)[2:].zfill(8) return res From 559ece584182f3234e943089c4634e180b33e1c5 Mon Sep 17 00:00:00 2001 From: Toshiki Kamiya <108583064+tkamiya22@users.noreply.github.com> Date: Sun, 21 Jul 2024 08:21:19 +0900 Subject: [PATCH 3/5] refactor: Change sentinels to match the type --- src/rmqrcode/segments.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmqrcode/segments.py b/src/rmqrcode/segments.py index 75260de..73e44ab 100644 --- a/src/rmqrcode/segments.py +++ b/src/rmqrcode/segments.py @@ -63,7 +63,7 @@ def __init__(self): [[self.INF for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1) ] self.parents: list[list[list[tuple[int, int, int]]]] = [ - [[-1 for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1) + [[(-1, -1, -1) for n in range(3)] for mode in range(4)] for length in range(self.MAX_CHARACTER + 1) ] def compute(self, data: str, version: str, ecc: ErrorCorrectionLevel): @@ -202,7 +202,7 @@ def _find_best(self, data: str) -> Best: """ best = self.INF - best_index: tuple[int, int, int] = (-1, -1) + best_index: tuple[int, int, int] = (-1, -1, -1) for mode in range(4): for unfilled_length in range(3): if self.dp[len(data)][mode][unfilled_length] < best: From d2d3d220dd96a7aa064f459815dd7af9707893e8 Mon Sep 17 00:00:00 2001 From: Toshiki Kamiya <108583064+tkamiya22@users.noreply.github.com> Date: Sun, 21 Jul 2024 08:23:10 +0900 Subject: [PATCH 4/5] refactor: Remove deprecated method which cannot invoke --- src/rmqrcode/rmqrcode.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index d7b491e..0e07cfa 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -275,19 +275,6 @@ def width(self): """ return self._width - def value_at(self, x: int, y: int): - """DEPRECATED: Returns the color at the point of (x, y). - - Returns: - rmqrcode.Color: The color of rMQRCode at the point of (x, y). - - Note: - This method is deprecated. Use to_list() alternatively. - This not includes the quiet zone. - - """ - return self._qr[y][x] - def to_list(self, with_quiet_zone: bool = True): """Converts to two-dimensional list and returns it. From 256e1e297601d6eb1d5b5d87188c8ce474960679 Mon Sep 17 00:00:00 2001 From: Toshiki Kamiya <108583064+tkamiya22@users.noreply.github.com> Date: Sun, 21 Jul 2024 08:33:53 +0900 Subject: [PATCH 5/5] refactor: Remove invalid method --- src/rmqrcode/rmqrcode.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/rmqrcode/rmqrcode.py b/src/rmqrcode/rmqrcode.py index 0e07cfa..5646368 100644 --- a/src/rmqrcode/rmqrcode.py +++ b/src/rmqrcode/rmqrcode.py @@ -809,30 +809,25 @@ def __str__(self, with_quiet_zone: bool = True): Color.WHITE: "_", Color.BLACK: "X", Color.UNDEFINED: "?", - True: "X", - False: "_", } res = "" if with_quiet_zone: - res += (show[False] * (self._width + QUIET_ZONE_MODULES * 2) + "\n") * QUIET_ZONE_MODULES + res += (show[Color.WHITE] * (self._width + QUIET_ZONE_MODULES * 2) + "\n") * QUIET_ZONE_MODULES for y in range(self._height): if with_quiet_zone: - res += show[False] * QUIET_ZONE_MODULES + res += show[Color.WHITE] * QUIET_ZONE_MODULES for x in range(self._width): - if self._qr[y][x] in show: - res += show[self._qr[y][x]] - else: - res += self._qr.get_data[y][x] + res += show[self._qr[y][x]] if with_quiet_zone: - res += show[False] * QUIET_ZONE_MODULES + res += show[Color.WHITE] * QUIET_ZONE_MODULES res += "\n" if with_quiet_zone: - res += (show[False] * (self._width + QUIET_ZONE_MODULES * 2) + "\n") * QUIET_ZONE_MODULES + res += (show[Color.WHITE] * (self._width + QUIET_ZONE_MODULES * 2) + "\n") * QUIET_ZONE_MODULES return res