Skip to content

Commit c001c05

Browse files
committed
Attempt to type-check writers
1 parent ef61ffe commit c001c05

3 files changed

Lines changed: 48 additions & 13 deletions

File tree

‎barcode/base.py‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44

55
from typing import TYPE_CHECKING
66
from typing import ClassVar
7+
from typing import Generic
8+
from typing import TypeVar
79

810
from barcode.writer import BaseWriter
911
from barcode.writer import SVGWriter
12+
from barcode.writer import T_Output
1013

1114
if TYPE_CHECKING:
1215
from typing import BinaryIO
1316

17+
W = TypeVar("W", bound=BaseWriter[object])
1418

15-
class Barcode:
19+
20+
class Barcode(Generic[W, T_Output]):
1621
name = ""
1722

1823
digits = 0
@@ -31,9 +36,9 @@ class Barcode:
3136
"text": "",
3237
}
3338

34-
writer: BaseWriter
39+
writer: W
3540

36-
def __init__(self, code: str, writer: BaseWriter | None = None, **options) -> None:
41+
def __init__(self, code: str, writer: W | None = None, **options) -> None:
3742
raise NotImplementedError
3843

3944
def to_ascii(self) -> str:
@@ -62,7 +67,10 @@ def get_fullcode(self):
6267
raise NotImplementedError
6368

6469
def save(
65-
self, filename: str, options: dict | None = None, text: str | None = None
70+
self,
71+
filename: str,
72+
options: dict | None = None,
73+
text: str | None = None,
6674
) -> str:
6775
"""Renders the barcode and saves it in `filename`.
6876
@@ -72,7 +80,7 @@ def save(
7280
7381
:returns: The full filename with extension.
7482
"""
75-
output = self.render(options, text) if text else self.render(options)
83+
output: T_Output = self.render(options, text) if text else self.render(options)
7684

7785
return self.writer.save(filename, output)
7886

@@ -92,7 +100,11 @@ def write(
92100
output = self.render(options, text)
93101
self.writer.write(output, fp)
94102

95-
def render(self, writer_options: dict | None = None, text: str | None = None):
103+
def render(
104+
self,
105+
writer_options: dict | None = None,
106+
text: str | None = None,
107+
) -> T_Output:
96108
"""Renders the barcode using `self.writer`.
97109
98110
:param writer_options: Options for `self.writer`, see writer docs for details.

‎barcode/writer.py‎

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
import gzip
44
import os
55
import xml.dom.minidom
6+
from abc import ABC
7+
from abc import abstractmethod
68
from typing import TYPE_CHECKING
79
from typing import BinaryIO
810
from typing import Callable
11+
from typing import Generic
912
from typing import TypedDict
13+
from typing import TypeVar
1014

1115
from barcode.version import version
1216

@@ -78,8 +82,10 @@ def create_svg_object(with_doctype: bool = False) -> xml.dom.minidom.Document:
7882
COMMENT = f"Autogenerated with python-barcode {version}"
7983
PATH = os.path.dirname(os.path.abspath(__file__))
8084

85+
T_Output = TypeVar("T_Output")
8186

82-
class BaseWriter:
87+
88+
class BaseWriter(ABC, Generic[T_Output]):
8389
"""Baseclass for all writers.
8490
8591
Initializes the basic writer options. Child classes can add more attributes and can
@@ -164,7 +170,8 @@ def calculate_size(self, modules_per_line: int, number_of_lines: int) -> tuple:
164170
height += self.text_line_distance * (number_of_text_lines - 1)
165171
return width, height
166172

167-
def save(self, filename: str, output) -> str:
173+
@abstractmethod
174+
def save(self, filename: str, output: T_Output) -> str:
168175
"""Saves the rendered output to `filename`.
169176
170177
:param filename: Filename without extension.
@@ -305,11 +312,12 @@ def render(self, code: list[str]):
305312

306313
return self._callbacks["finish"]()
307314

315+
@abstractmethod
308316
def write(self, content, fp: BinaryIO) -> None:
309317
raise NotImplementedError
310318

311319

312-
class SVGWriter(BaseWriter):
320+
class SVGWriter(BaseWriter[bytes]):
313321
def __init__(self) -> None:
314322
super().__init__(
315323
self._init,
@@ -396,7 +404,7 @@ def _finish(self) -> bytes:
396404
indent=4 * " ", newl=os.linesep, encoding="UTF-8"
397405
)
398406

399-
def save(self, filename: str, output) -> str:
407+
def save(self, filename: str, output: bytes) -> str:
400408
if self.compress:
401409
_filename = f"{filename}.svgz"
402410
with gzip.open(_filename, "wb") as f:
@@ -417,7 +425,21 @@ def write(self, content, fp: BinaryIO) -> None:
417425

418426

419427
if Image is None:
420-
ImageWriter: type | None = None
428+
if TYPE_CHECKING:
429+
430+
class ImageWriter(BaseWriter):
431+
def __init__(
432+
self,
433+
format: str = "PNG",
434+
mode: str = "RGB",
435+
dpi: int = 300,
436+
) -> None: ...
437+
438+
def save(self, filename: str, output: T_Image) -> str: ...
439+
440+
def write(self, content, fp: BinaryIO) -> None: ...
441+
else:
442+
ImageWriter = None
421443
else:
422444

423445
class ImageWriter(BaseWriter): # type: ignore[no-redef]
@@ -495,7 +517,7 @@ def _paint_text(self, xpos, ypos):
495517
def _finish(self) -> T_Image:
496518
return self._image
497519

498-
def save(self, filename: str, output) -> str:
520+
def save(self, filename: str, output: T_Image) -> str:
499521
filename = f"{filename}.{self.format.lower()}"
500522
output.save(filename, self.format.upper())
501523
return filename

‎tests/test_writers.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66
from barcode import EAN13
77
from barcode.writer import ImageWriter
8+
from barcode.writer import Image
89
from barcode.writer import SVGWriter
910

1011
PATH = os.path.dirname(os.path.abspath(__file__))
1112
TESTPATH = os.path.join(PATH, "test_outputs")
1213

13-
if ImageWriter is not None:
14+
if Image is not None:
1415

1516
def test_saving_image_to_byteio() -> None:
1617
assert ImageWriter is not None # workaround for mypy

0 commit comments

Comments
 (0)