From f88fa0e0aae29a02d21d8d49e21f162384a1ae61 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 21:05:36 +0200 Subject: [PATCH 01/11] Raise a runtime error when converting missing character I'm not 100% sure this is correct, but I think a None value would lead to an error later on anyways. --- barcode/codex.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index ef72187..69f7842 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -211,9 +211,9 @@ def _convert(self, char): value = int(self._buffer) self._buffer = "" return value - return None - return None - return None + raise RuntimeError( + f"Character {char} could not be converted in charset {self._charset}." + ) def _try_to_optimize(self, encoded): if encoded[1] in code128.TO: From 21832c2144357c809e24f78744c43f4e90f99db4 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 21:08:10 +0200 Subject: [PATCH 02/11] Enforce that Barcode.build() returns a singleton list Also add some type hints. --- barcode/base.py | 17 +++++++++++------ barcode/codabar.py | 2 +- barcode/codex.py | 42 +++++++++++++++++++++++++++--------------- barcode/ean.py | 20 ++++++++++---------- barcode/itf.py | 2 +- barcode/upc.py | 15 ++++++++------- barcode/writer.py | 6 +++--- 7 files changed, 61 insertions(+), 43 deletions(-) diff --git a/barcode/base.py b/barcode/base.py index d5296ae..ba1f3a3 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -35,15 +35,17 @@ class Barcode: writer: BaseWriter def to_ascii(self) -> str: - code = self.build() - for i, line in enumerate(code): - code[i] = line.replace("1", "X").replace("0", " ") - return "\n".join(code) + code_list = self.build() + if not len(code_list) == 1: + raise RuntimeError("Code list must contain a single element.") + code = code_list[0] + return code.replace("1", "X").replace("0", " ") def __repr__(self) -> str: return f"<{self.__class__.__name__}({self.get_fullcode()!r})>" def build(self) -> list[str]: + """Return a singleton list with a string encoding the barcode as 1s and 0s.""" raise NotImplementedError def get_fullcode(self): @@ -101,5 +103,8 @@ def render(self, writer_options: dict | None = None, text: str | None = None): else: options["text"] = self.get_fullcode() self.writer.set_options(options) - code = self.build() - return self.writer.render(code) + code_list = self.build() + if not len(code_list) == 1: + raise RuntimeError("Code list must contain a single element.") + code = code_list[0] + return self.writer.render([code]) diff --git a/barcode/codabar.py b/barcode/codabar.py index 6395459..aecd8ab 100644 --- a/barcode/codabar.py +++ b/barcode/codabar.py @@ -41,7 +41,7 @@ def __str__(self) -> str: def get_fullcode(self): return self.code - def build(self): + def build(self) -> list[str]: try: data = ( codabar.STARTSTOP[self.code[0]] + "n" diff --git a/barcode/codex.py b/barcode/codex.py index 69f7842..8e4c146 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -4,7 +4,9 @@ """ from __future__ import annotations +from typing import TYPE_CHECKING from typing import Collection +from typing import Literal from barcode.base import Barcode from barcode.charsets import code39 @@ -13,6 +15,9 @@ from barcode.errors import IllegalCharacterError from barcode.errors import NumberOfDigitsError +if TYPE_CHECKING: + from barcode.writer import BaseWriter + __docformat__ = "restructuredtext en" # Sizes @@ -66,12 +71,13 @@ def calculate_checksum(self): return k return None - def build(self): + def build(self) -> list[str]: chars = [code39.EDGE] for char in self.code: chars.append(code39.MAP[char][1]) chars.append(code39.EDGE) - return [code39.MIDDLE.join(chars)] + result = code39.MIDDLE.join(chars) + return [result] def render(self, writer_options=None, text=None): options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE} @@ -135,8 +141,12 @@ class Code128(Barcode): """ name = "Code 128" + _charset: Literal["A", "B", "C"] + code: str + writer: BaseWriter + buffer: str - def __init__(self, code, writer=None) -> None: + def __init__(self, code: str, writer=None) -> None: self.code = code self.writer = writer or self.default_writer() self._charset = "B" @@ -147,13 +157,15 @@ def __str__(self) -> str: return self.code @property - def encoded(self): + def encoded(self) -> list[int]: return self._build() - def get_fullcode(self): + def get_fullcode(self) -> str: return self.code - def _new_charset(self, which): + def _new_charset(self, which: Literal["A", "B", "C"]) -> list[int]: + if which == self._charset: + raise ValueError(f"Already in charset {which}") if which == "A": code = self._convert("TO_A") elif which == "B": @@ -163,11 +175,11 @@ def _new_charset(self, which): self._charset = which return [code] - def _maybe_switch_charset(self, pos): + def _maybe_switch_charset(self, pos: int) -> list[int]: char = self.code[pos] next_ = self.code[pos : pos + 10] - def look_next(): + def look_next() -> bool: digits = 0 for c in next_: if c.isdigit(): @@ -176,7 +188,7 @@ def look_next(): break return digits > 3 - codes = [] + codes: list[int] = [] if self._charset == "C" and not char.isdigit(): if char in code128.B: codes = self._new_charset("B") @@ -197,7 +209,7 @@ def look_next(): codes = self._new_charset("B") return codes - def _convert(self, char): + def _convert(self, char: str) -> int: if self._charset == "A": return code128.A[char] if self._charset == "B": @@ -215,19 +227,19 @@ def _convert(self, char): f"Character {char} could not be converted in charset {self._charset}." ) - def _try_to_optimize(self, encoded): + def _try_to_optimize(self, encoded: list[int]) -> list[int]: if encoded[1] in code128.TO: encoded[:2] = [code128.TO[encoded[1]]] return encoded - def _calculate_checksum(self, encoded): + def _calculate_checksum(self, encoded: list[int]) -> int: cs = [encoded[0]] for i, code_num in enumerate(encoded[1:], start=1): cs.append(i * code_num) return sum(cs) % 103 - def _build(self): - encoded = [code128.START_CODES[self._charset]] + def _build(self) -> list[int]: + encoded: list[int] = [code128.START_CODES[self._charset]] for i, char in enumerate(self.code): encoded.extend(self._maybe_switch_charset(i)) code_num = self._convert(char) @@ -240,7 +252,7 @@ def _build(self): self._buffer = "" return self._try_to_optimize(encoded) - def build(self): + def build(self) -> list[str]: encoded = self._build() encoded.append(self._calculate_checksum(encoded)) code = "" diff --git a/barcode/ean.py b/barcode/ean.py index 028d20a..c65d17c 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -94,11 +94,11 @@ def calculate_checksum(self, value: str | None = None) -> int: oddsum = sum(int(x) for x in ean_without_checksum[-1::-2]) return (10 - ((evensum + oddsum * 3) % 10)) % 10 - def build(self): + def build(self) -> list[str]: """Builds the barcode pattern from `self.ean`. :returns: The pattern as string - :rtype: String + :rtype: List containing the string as a single element """ code = self.EDGE[:] pattern = _ean.LEFT_PATTERN[int(self.ean[0])] @@ -110,15 +110,16 @@ def build(self): code += self.EDGE return [code] - def to_ascii(self): + def to_ascii(self) -> str: """Returns an ascii representation of the barcode. :rtype: String """ - code = self.build() - for i, line in enumerate(code): - code[i] = line.replace("G", "|").replace("1", "|").replace("0", " ") - return "\n".join(code) + code_list = self.build() + if not len(code_list) == 1: + raise RuntimeError("Code list must contain a single element.") + code = code_list[0] + return code.replace("G", "|").replace("1", "|").replace("0", " ") def render(self, writer_options=None, text=None): options = {"module_width": SIZES["SC2"]} @@ -171,11 +172,10 @@ class EuropeanArticleNumber8(EuropeanArticleNumber13): digits = 7 - def build(self): + def build(self) -> list[str]: """Builds the barcode pattern from `self.ean`. - :returns: The pattern as string - :rtype: String + :returns: A list containing the string as a single element """ code = self.EDGE[:] for number in self.ean[:4]: diff --git a/barcode/itf.py b/barcode/itf.py index 428dd4d..7d9c2fe 100644 --- a/barcode/itf.py +++ b/barcode/itf.py @@ -48,7 +48,7 @@ def __str__(self) -> str: def get_fullcode(self): return self.code - def build(self): + def build(self) -> list[str]: data = itf.START for i in range(0, len(self.code), 2): bars_digit = int(self.code[i]) diff --git a/barcode/upc.py b/barcode/upc.py index 37b9f76..9093b8f 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -77,11 +77,11 @@ def sum_(x, y): return 10 - check - def build(self): + def build(self) -> list[str]: """Builds the barcode pattern from 'self.upc' :return: The pattern as string - :rtype: str + :rtype: List containing the string as a single element """ code = _upc.EDGE[:] @@ -97,16 +97,17 @@ def build(self): return [code] - def to_ascii(self): + def to_ascii(self) -> str: """Returns an ascii representation of the barcode. :rtype: str """ - code = self.build() - for i, line in enumerate(code): - code[i] = line.replace("1", "|").replace("0", "_") - return "\n".join(code) + code_list = self.build() + if len(code_list) != 1: + raise RuntimeError("Code list must contain a single element.") + code = code_list[0] + return code.replace("1", "|").replace("0", "_") def render(self, writer_options=None, text=None): options = {"module_width": 0.33} diff --git a/barcode/writer.py b/barcode/writer.py index 27ca933..3c1d618 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -362,9 +362,9 @@ def _create_text(self, xpos, ypos): attributes = { "x": SIZE.format(xpos), "y": SIZE.format(ypos), - "style": "fill:{};font-size:{}pt;text-anchor:middle;".format( - self.foreground, - self.font_size, + "style": ( + f"fill:{self.foreground};" + f"font-size:{self.font_size}pt;text-anchor:middle;" ), } _set_attributes(element, **attributes) From 17b0b93db2648803c9ef07ad22d18a4e71591b0d Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 22:15:31 +0200 Subject: [PATCH 03/11] Fix SVG DOM implementation to minidom It's part of the standard library, and we're not aiming for performance --- barcode/writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 3c1d618..c3462ae 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -2,7 +2,7 @@ import gzip import os -import xml.dom +import xml.dom.minidom from typing import TYPE_CHECKING from typing import BinaryIO from typing import Callable @@ -58,7 +58,7 @@ def _set_attributes(element, **attributes): def create_svg_object(with_doctype=False): - imp = xml.dom.getDOMImplementation() + imp = xml.dom.minidom.getDOMImplementation() doctype = imp.createDocumentType( "svg", "-//W3C//DTD SVG 1.1//EN", From 0a8856c5d0e48af7c4038e03297795595a5782ab Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 22:41:49 +0200 Subject: [PATCH 04/11] Enforce that code is a singleton list for BaseWriter.render --- barcode/writer.py | 103 +++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index c3462ae..781fcfc 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -4,6 +4,7 @@ import os import xml.dom.minidom from typing import TYPE_CHECKING +from typing import Any from typing import BinaryIO from typing import Callable from typing import TypedDict @@ -96,6 +97,21 @@ class BaseWriter: """ _callbacks: Callbacks + module_width: float + module_height: float + font_path: str + font_size: float + quiet_zone: float + background: str | int + foreground: str | int + text: str + human: str + text_distance: float + text_line_distance: float + center_text: bool + guard_height_factor: float + margin_top: float + margin_bottom: float def __init__( self, @@ -204,65 +220,60 @@ def packed(self, line: str) -> Generator[tuple[int, float], str, None]: yield (-c, self.guard_height_factor) c = 1 - def render(self, code): + def render(self, code: list[str]) -> Any: """Renders the barcode to whatever the inheriting writer provides, using the registered callbacks. :parameters: code : List - List of strings matching the writer spec + List consisting of a single string matching the writer spec (only contain 0 or 1 or G). """ if self._callbacks["initialize"] is not None: self._callbacks["initialize"](code) ypos = self.margin_top base_height = self.module_height - for cc, line in enumerate(code): - # Left quiet zone is x startposition - xpos = self.quiet_zone - bxs = xpos # x start of barcode - text: InternalText = { - "start": [], # The x start of a guard - "end": [], # The x end of a guard - "xpos": [], # The x position where to write a text block - # Flag that indicates if the previous mod was part of an guard block: - "was_guard": False, - } - for mod, height_factor in self.packed(line): - if mod < 1: - color = self.background - else: - color = self.foreground - - if text["was_guard"] and height_factor == 1: - # The current guard ended, store its x position - text["end"].append(xpos) - text["was_guard"] = False - elif not text["was_guard"] and height_factor != 1: - # A guard started, store its x position - text["start"].append(xpos) - text["was_guard"] = True - - self.module_height = base_height * height_factor - # remove painting for background colored tiles? - self._callbacks["paint_module"]( - xpos, ypos, self.module_width * abs(mod), color - ) - xpos += self.module_width * abs(mod) + if len(code) != 1: + raise NotImplementedError("Only one line of code is supported") + line = code[0] + # Left quiet zone is x startposition + xpos = self.quiet_zone + bxs = xpos # x start of barcode + text: InternalText = { + "start": [], # The x start of a guard + "end": [], # The x end of a guard + "xpos": [], # The x position where to write a text block + # Flag that indicates if the previous mod was part of an guard block: + "was_guard": False, + } + for mod, height_factor in self.packed(line): + if mod < 1: + color = self.background else: - if height_factor != 1: + color = self.foreground + + if text["was_guard"] and height_factor == 1: + # The current guard ended, store its x position text["end"].append(xpos) - self.module_height = base_height - - bxe = xpos - # Add right quiet zone to every line, except last line, - # quiet zone already provided with background, - # should it be removed completely? - if (cc + 1) != len(code): - self._callbacks["paint_module"]( - xpos, ypos, self.quiet_zone, self.background - ) - ypos += self.module_height + text["was_guard"] = False + elif not text["was_guard"] and height_factor != 1: + # A guard started, store its x position + text["start"].append(xpos) + text["was_guard"] = True + + self.module_height = base_height * height_factor + # remove painting for background colored tiles? + self._callbacks["paint_module"]( + xpos, ypos, self.module_width * abs(mod), color + ) + xpos += self.module_width * abs(mod) + else: + if height_factor != 1: + text["end"].append(xpos) + self.module_height = base_height + + bxe = xpos + ypos += self.module_height if self.text and self._callbacks["paint_text"] is not None: if not text["start"]: From 55ca95210908c3952c4eb5dc7287395a8d3c808e Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 23:43:33 +0200 Subject: [PATCH 05/11] More type annotations --- barcode/__init__.py | 45 ++++++++++++++++++++++++++++++++++----------- barcode/base.py | 3 +++ barcode/writer.py | 35 +++++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 8c54f8c..822c13e 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -5,8 +5,10 @@ """ from __future__ import annotations +import os from typing import TYPE_CHECKING from typing import BinaryIO +from typing import overload from barcode.codabar import CODABAR from barcode.codex import PZN @@ -28,11 +30,10 @@ from barcode.version import version # noqa: F401 if TYPE_CHECKING: - import os - + from barcode.base import Barcode from barcode.writer import BaseWriter -__BARCODE_MAP = { +__BARCODE_MAP: dict[str, type[Barcode]] = { "codabar": CODABAR, "code128": Code128, "code39": Code39, @@ -61,12 +62,29 @@ PROVIDED_BARCODES.sort() +@overload +def get( + name: str, code: str, writer: BaseWriter | None = None, options: dict | None = None +) -> Barcode: + ... + + +@overload +def get( + name: str, + code: None = None, + writer: BaseWriter | None = None, + options: dict | None = None, +) -> type[Barcode]: + ... + + def get( name: str, code: str | None = None, writer: BaseWriter | None = None, options: dict | None = None, -): +) -> Barcode | type[Barcode]: """Helper method for getting a generator or even a generated code. :param name: The name of the type of barcode desired. @@ -79,6 +97,7 @@ def get( generating. """ options = options or {} + barcode: type[Barcode] try: barcode = __BARCODE_MAP[name.lower()] except KeyError as e: @@ -89,15 +108,15 @@ def get( return barcode -def get_class(name: str): +def get_class(name: str) -> type[Barcode]: return get_barcode(name) def generate( name: str, code: str, - writer: BaseWriter | None = None, - output: str | (os.PathLike | (BinaryIO | None)) = None, + writer: BaseWriter | None, + output: str | os.PathLike | BinaryIO, writer_options: dict | None = None, text: str | None = None, ) -> str | None: @@ -113,6 +132,9 @@ def generate( """ from barcode.base import Barcode + if output is None: + raise TypeError("'output' cannot be None") + writer = writer or Barcode.default_writer() writer.set_options(writer_options or {}) @@ -120,11 +142,12 @@ def generate( if isinstance(output, str): return barcode.save(output, writer_options, text) - if output: - barcode.write(output, writer_options, text) + if isinstance(output, os.PathLike): + with open(output, "wb") as fp: + barcode.write(fp, writer_options, text) return None - - raise TypeError("'output' cannot be None") + barcode.write(output, writer_options, text) + return None get_barcode = get diff --git a/barcode/base.py b/barcode/base.py index ba1f3a3..d6bb738 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -34,6 +34,9 @@ class Barcode: writer: BaseWriter + def __init__(self, code: str, writer: BaseWriter | None = None, **options) -> None: + raise NotImplementedError + def to_ascii(self) -> str: code_list = self.build() if not len(code_list) == 1: diff --git a/barcode/writer.py b/barcode/writer.py index 781fcfc..4981e52 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -15,6 +15,9 @@ from typing import Generator from typing import Literal + from PIL.Image import Image as T_Image + from PIL.ImageDraw import ImageDraw as T_ImageDraw + class InternalText(TypedDict): start: list end: list @@ -45,11 +48,11 @@ class Callbacks(TypedDict): Image = ImageDraw = ImageFont = None -def mm2px(mm, dpi: int): +def mm2px(mm: float, dpi: int) -> float: return (mm * dpi) / 25.4 -def pt2mm(pt): +def pt2mm(pt: float) -> float: return pt * 0.352777778 @@ -58,8 +61,9 @@ def _set_attributes(element, **attributes): element.setAttribute(key, value) -def create_svg_object(with_doctype=False): +def create_svg_object(with_doctype=False) -> xml.dom.minidom.Document: imp = xml.dom.minidom.getDOMImplementation() + assert imp is not None doctype = imp.createDocumentType( "svg", "-//W3C//DTD SVG 1.1//EN", @@ -317,13 +321,13 @@ def __init__(self) -> None: self._create_text, self._finish, ) - self.compress = False - self.with_doctype = True - self._document = None - self._root = None - self._group = None + self.compress: bool = False + self.with_doctype: bool = True + self._document: xml.dom.minidom.Document + self._root: xml.dom.minidom.Element + self._group: xml.dom.minidom.Element - def _init(self, code): + def _init(self, code: list[str]): width, height = self.calculate_size(len(code[0]), len(code)) self._document = create_svg_object(self.with_doctype) self._root = self._document.documentElement @@ -438,16 +442,18 @@ def __init__(self, format="PNG", mode="RGB", dpi=300) -> None: self.format = format self.mode = mode self.dpi = dpi - self._image = None - self._draw = None + self._image: T_Image + self._draw: T_ImageDraw - def _init(self, code): + def _init(self, code: list[str]) -> None: + if ImageDraw is None: + raise RuntimeError("Pillow not found. Cannot create image.") width, height = self.calculate_size(len(code[0]), len(code)) size = (int(mm2px(width, self.dpi)), int(mm2px(height, self.dpi))) self._image = Image.new(self.mode, size, self.background) self._draw = ImageDraw.Draw(self._image) - def _paint_module(self, xpos, ypos, width, color): + def _paint_module(self, xpos: float, ypos: float, width: float, color): size = [ (mm2px(xpos, self.dpi), mm2px(ypos, self.dpi)), ( @@ -458,6 +464,7 @@ def _paint_module(self, xpos, ypos, width, color): self._draw.rectangle(size, outline=color, fill=color) def _paint_text(self, xpos, ypos): + assert ImageFont is not None font_size = int(mm2px(pt2mm(self.font_size), self.dpi)) if font_size <= 0: return @@ -472,7 +479,7 @@ def _paint_text(self, xpos, ypos): ) ypos += pt2mm(self.font_size) / 2 + self.text_line_distance - def _finish(self) -> Image: + def _finish(self) -> T_Image: return self._image def save(self, filename: str, output) -> str: From a59f7eac85912029729e95db7f3ef99ed8313f54 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 23:44:05 +0200 Subject: [PATCH 06/11] Assume Pillow --- barcode/writer.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 4981e52..fd1cf25 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -32,20 +32,15 @@ class Callbacks(TypedDict): try: - import Image - import ImageDraw - import ImageFont + from PIL import Image + from PIL import ImageDraw + from PIL import ImageFont except ImportError: - try: - from PIL import Image - from PIL import ImageDraw - from PIL import ImageFont - except ImportError: - import logging - - log = logging.getLogger("pyBarcode") - log.info("Pillow not found. Image output disabled") - Image = ImageDraw = ImageFont = None + import logging + + log = logging.getLogger("pyBarcode") + log.info("Pillow not found. Image output disabled") + Image = ImageDraw = ImageFont = None def mm2px(mm: float, dpi: int) -> float: From 50d2e289f3fcf6a39da29caef230b127f280a6ca Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Sun, 28 Apr 2024 23:44:54 +0200 Subject: [PATCH 07/11] Enforce singletons in ImageWriter and SVGWriter --- barcode/writer.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index fd1cf25..3638ce2 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -323,7 +323,10 @@ def __init__(self) -> None: self._group: xml.dom.minidom.Element def _init(self, code: list[str]): - width, height = self.calculate_size(len(code[0]), len(code)) + if len(code) != 1: + raise NotImplementedError("Only one line of code is supported") + line = code[0] + width, height = self.calculate_size(len(line), 1) self._document = create_svg_object(self.with_doctype) self._root = self._document.documentElement attributes = { @@ -443,7 +446,10 @@ def __init__(self, format="PNG", mode="RGB", dpi=300) -> None: def _init(self, code: list[str]) -> None: if ImageDraw is None: raise RuntimeError("Pillow not found. Cannot create image.") - width, height = self.calculate_size(len(code[0]), len(code)) + if len(code) != 1: + raise NotImplementedError("Only one line of code is supported") + line = code[0] + width, height = self.calculate_size(len(line), 1) size = (int(mm2px(width, self.dpi)), int(mm2px(height, self.dpi))) self._image = Image.new(self.mode, size, self.background) self._draw = ImageDraw.Draw(self._image) From f6a036e88a33b02557146b56d7fd4f1363227d9f Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 19:51:43 +0200 Subject: [PATCH 08/11] Fix bad info in docstring --- barcode/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/barcode/base.py b/barcode/base.py index d6bb738..95223fb 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -48,7 +48,10 @@ def __repr__(self) -> str: return f"<{self.__class__.__name__}({self.get_fullcode()!r})>" def build(self) -> list[str]: - """Return a singleton list with a string encoding the barcode as 1s and 0s.""" + """Return a single-element list with a string encoding the barcode. + + Typically the string consists of 1s and 0s, although it can contain + other characters such as G for guard lines (e.g. in EAN13).""" raise NotImplementedError def get_fullcode(self): From bf627b41965cbef6377b6cf0f2d86198ca7e897b Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 19:52:24 +0200 Subject: [PATCH 09/11] Remove "-> Any" type hint --- barcode/writer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 3638ce2..b3d32fd 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -4,7 +4,6 @@ import os import xml.dom.minidom from typing import TYPE_CHECKING -from typing import Any from typing import BinaryIO from typing import Callable from typing import TypedDict @@ -219,7 +218,7 @@ def packed(self, line: str) -> Generator[tuple[int, float], str, None]: yield (-c, self.guard_height_factor) c = 1 - def render(self, code: list[str]) -> Any: + def render(self, code: list[str]): """Renders the barcode to whatever the inheriting writer provides, using the registered callbacks. From 76ce2b47b782de631361371247a245b762596630 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 19:55:42 +0200 Subject: [PATCH 10/11] Make writer and output optional params of generate --- barcode/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 822c13e..1a16b4e 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -115,8 +115,8 @@ def get_class(name: str) -> type[Barcode]: def generate( name: str, code: str, - writer: BaseWriter | None, - output: str | os.PathLike | BinaryIO, + writer: BaseWriter | None = None, + output: str | os.PathLike | BinaryIO | None = None, writer_options: dict | None = None, text: str | None = None, ) -> str | None: From 469a798ba836b8585c417a10fdccca314e5acebf Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Tue, 9 Jul 2024 14:53:25 +0200 Subject: [PATCH 11/11] Minimally make CI pass for _convert --- barcode/codex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/barcode/codex.py b/barcode/codex.py index 8e4c146..b82c020 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -209,7 +209,7 @@ def look_next() -> bool: codes = self._new_charset("B") return codes - def _convert(self, char: str) -> int: + def _convert(self, char: str): if self._charset == "A": return code128.A[char] if self._charset == "B": @@ -223,6 +223,7 @@ def _convert(self, char: str) -> int: value = int(self._buffer) self._buffer = "" return value + return None raise RuntimeError( f"Character {char} could not be converted in charset {self._charset}." )