Skip to content

Commit 3f0aa98

Browse files
committed
Attempt to type-check writers
1 parent 1e14c62 commit 3f0aa98

File tree

3 files changed

+48
-13
lines changed

3 files changed

+48
-13
lines changed

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.
@@ -307,11 +314,12 @@ def render(self, code: list[str]):
307314

308315
return self._callbacks["finish"]()
309316

317+
@abstractmethod
310318
def write(self, content, fp: BinaryIO) -> None:
311319
raise NotImplementedError
312320

313321

314-
class SVGWriter(BaseWriter):
322+
class SVGWriter(BaseWriter[bytes]):
315323
def __init__(self) -> None:
316324
super().__init__(
317325
self._init,
@@ -398,7 +406,7 @@ def _finish(self) -> bytes:
398406
indent=4 * " ", newl=os.linesep, encoding="UTF-8"
399407
)
400408

401-
def save(self, filename: str, output) -> str:
409+
def save(self, filename: str, output: bytes) -> str:
402410
if self.compress:
403411
_filename = f"{filename}.svgz"
404412
with gzip.open(_filename, "wb") as f:
@@ -419,7 +427,21 @@ def write(self, content, fp: BinaryIO) -> None:
419427

420428

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

425447
class ImageWriter(BaseWriter): # type: ignore[no-redef]
@@ -497,7 +519,7 @@ def _paint_text(self, xpos, ypos):
497519
def _finish(self) -> T_Image:
498520
return self._image
499521

500-
def save(self, filename: str, output) -> str:
522+
def save(self, filename: str, output: T_Image) -> str:
501523
filename = f"{filename}.{self.format.lower()}"
502524
output.save(filename, self.format.upper())
503525
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)