Skip to content

Commit dab3fdf

Browse files
committed
fixup
1 parent c001c05 commit dab3fdf

15 files changed

Lines changed: 202 additions & 84 deletions

barcode/__init__.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@
3131
from barcode.version import version # noqa: F401
3232

3333
if TYPE_CHECKING:
34+
from typing import Any
35+
3436
from barcode.base import Barcode
3537
from barcode.writer import BaseWriter
3638

37-
__BARCODE_MAP: dict[str, type[Barcode]] = {
39+
__BARCODE_MAP: dict[str, type[Barcode[Any]]] = {
3840
"codabar": CODABAR,
3941
"code128": Code128,
4042
"code39": Code39,
@@ -65,25 +67,28 @@
6567

6668
@overload
6769
def get(
68-
name: str, code: str, writer: BaseWriter | None = None, options: dict | None = None
69-
) -> Barcode: ...
70+
name: str,
71+
code: str,
72+
writer: BaseWriter[Any] | None = None,
73+
options: dict | None = None,
74+
) -> Barcode[Any]: ...
7075

7176

7277
@overload
7378
def get(
7479
name: str,
7580
code: None = None,
76-
writer: BaseWriter | None = None,
81+
writer: BaseWriter[Any] | None = None,
7782
options: dict | None = None,
78-
) -> type[Barcode]: ...
83+
) -> type[Barcode[Any]]: ...
7984

8085

8186
def get(
8287
name: str,
8388
code: str | None = None,
84-
writer: BaseWriter | None = None,
89+
writer: BaseWriter[Any] | None = None,
8590
options: dict | None = None,
86-
) -> Barcode | type[Barcode]:
91+
) -> Barcode[Any] | type[Barcode[Any]]:
8792
"""Helper method for getting a generator or even a generated code.
8893
8994
:param name: The name of the type of barcode desired.
@@ -96,7 +101,7 @@ def get(
96101
generating.
97102
"""
98103
options = options or {}
99-
barcode: type[Barcode]
104+
barcode: type[Barcode[Any]]
100105
try:
101106
barcode = __BARCODE_MAP[name.lower()]
102107
except KeyError as e:
@@ -107,14 +112,14 @@ def get(
107112
return barcode
108113

109114

110-
def get_class(name: str) -> type[Barcode]:
115+
def get_class(name: str) -> type[Barcode[Any]]:
111116
return get_barcode(name)
112117

113118

114119
def generate(
115120
name: str,
116121
code: str,
117-
writer: BaseWriter | None = None,
122+
writer: BaseWriter[Any] | None = None,
118123
output: str | os.PathLike | BinaryIO | None = None,
119124
writer_options: dict | None = None,
120125
text: str | None = None,

barcode/base.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@
55
from typing import TYPE_CHECKING
66
from typing import ClassVar
77
from typing import Generic
8-
from typing import TypeVar
8+
from typing import cast
99

1010
from barcode.writer import BaseWriter
1111
from barcode.writer import SVGWriter
1212
from barcode.writer import T_Output
1313

1414
if TYPE_CHECKING:
15+
from collections.abc import Callable
16+
from typing import Any
1517
from typing import BinaryIO
1618

17-
W = TypeVar("W", bound=BaseWriter[object])
1819

19-
20-
class Barcode(Generic[W, T_Output]):
20+
class Barcode(Generic[T_Output]):
2121
name = ""
2222

2323
digits = 0
2424

25-
default_writer = SVGWriter
25+
default_writer: ClassVar[Callable[[], BaseWriter[Any]]] = SVGWriter
2626

2727
default_writer_options: ClassVar[dict] = {
2828
"module_width": 0.2,
@@ -36,11 +36,28 @@ class Barcode(Generic[W, T_Output]):
3636
"text": "",
3737
}
3838

39-
writer: W
39+
writer: BaseWriter[T_Output]
4040

41-
def __init__(self, code: str, writer: W | None = None, **options) -> None:
41+
def __init__(
42+
self,
43+
code: str,
44+
writer: BaseWriter[T_Output] | None = None,
45+
**options,
46+
) -> None:
4247
raise NotImplementedError
4348

49+
def _resolve_writer(
50+
self,
51+
writer: BaseWriter[T_Output] | None,
52+
) -> BaseWriter[T_Output]:
53+
if writer is not None:
54+
return writer
55+
# When no writer is given, T_Output falls back to its default (bytes),
56+
# which matches what the default writer (SVGWriter) renders. Accessing
57+
# default_writer through the class keeps mypy from binding it like a
58+
# method.
59+
return cast("BaseWriter[T_Output]", type(self).default_writer())
60+
4461
def to_ascii(self) -> str:
4562
code_list = self.build()
4663
if not len(code_list) == 1:
@@ -80,7 +97,7 @@ def save(
8097
8198
:returns: The full filename with extension.
8299
"""
83-
output: T_Output = self.render(options, text) if text else self.render(options)
100+
output = self.render(options, text) if text else self.render(options)
84101

85102
return self.writer.save(filename, output)
86103

barcode/codabar.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@
77

88
__docformat__ = "restructuredtext en"
99

10+
from typing import TYPE_CHECKING
11+
1012
from barcode.base import Barcode
1113
from barcode.charsets import codabar
1214
from barcode.errors import BarcodeError
1315
from barcode.errors import IllegalCharacterError
16+
from barcode.writer import T_Output
17+
18+
if TYPE_CHECKING:
19+
from barcode.writer import BaseWriter
1420

1521

16-
class CODABAR(Barcode):
22+
class CODABAR(Barcode[T_Output]):
1723
"""Initializes a new CODABAR instance.
1824
1925
:param code: Codabar (NW-7) string that matches [ABCD][0-9$:/.+-]+[ABCD]
@@ -25,9 +31,15 @@ class CODABAR(Barcode):
2531

2632
name = "Codabar (NW-7)"
2733

28-
def __init__(self, code, writer=None, narrow=2, wide=5) -> None:
34+
def __init__(
35+
self,
36+
code,
37+
writer: BaseWriter[T_Output] | None = None,
38+
narrow=2,
39+
wide=5,
40+
) -> None:
2941
self.code = code
30-
self.writer = writer or self.default_writer()
42+
self.writer = self._resolve_writer(writer)
3143
self.narrow = narrow
3244
self.wide = wide
3345

barcode/codex.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from barcode.errors import BarcodeError
1515
from barcode.errors import IllegalCharacterError
1616
from barcode.errors import NumberOfDigitsError
17+
from barcode.writer import T_Output
1718

1819
if TYPE_CHECKING:
1920
from collections.abc import Collection
@@ -40,12 +41,17 @@ def check_code(code: str, name: str, allowed: Collection[str]) -> None:
4041
)
4142

4243

43-
class Code39(Barcode):
44+
class Code39(Barcode[T_Output]):
4445
"""A Code39 barcode implementation"""
4546

4647
name = "Code 39"
4748

48-
def __init__(self, code: str, writer=None, add_checksum: bool = True) -> None:
49+
def __init__(
50+
self,
51+
code: str,
52+
writer: BaseWriter[T_Output] | None = None,
53+
add_checksum: bool = True,
54+
) -> None:
4955
r"""
5056
:param code: Code 39 string without \* and without checksum.
5157
:param writer: A ``barcode.writer`` instance used to render the barcode
@@ -56,7 +62,7 @@ def __init__(self, code: str, writer=None, add_checksum: bool = True) -> None:
5662
self.code = code.upper()
5763
if add_checksum:
5864
self.code += self.calculate_checksum()
59-
self.writer = writer or self.default_writer()
65+
self.writer = self._resolve_writer(writer)
6066
check_code(self.code, self.name, code39.REF)
6167

6268
def __str__(self) -> str:
@@ -83,13 +89,17 @@ def build(self) -> list[str]:
8389
result = code39.MIDDLE.join(chars)
8490
return [result]
8591

86-
def render(self, writer_options=None, text=None):
92+
def render(
93+
self,
94+
writer_options: dict | None = None,
95+
text: str | None = None,
96+
) -> T_Output:
8797
options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE}
8898
options.update(writer_options or {})
8999
return super().render(options, text)
90100

91101

92-
class PZN7(Code39):
102+
class PZN7(Code39[T_Output]):
93103
"""Initializes new German number for pharmaceutical products.
94104
95105
:param pzn: Code to render.
@@ -100,7 +110,7 @@ class PZN7(Code39):
100110

101111
digits = 6
102112

103-
def __init__(self, pzn, writer=None) -> None:
113+
def __init__(self, pzn, writer: BaseWriter[T_Output] | None = None) -> None:
104114
pzn = pzn[: self.digits]
105115
if not pzn.isdigit():
106116
raise IllegalCharacterError("PZN can only contain numbers.")
@@ -124,13 +134,13 @@ def calculate_checksum(self):
124134
return checksum
125135

126136

127-
class PZN8(PZN7):
137+
class PZN8(PZN7[T_Output]):
128138
"""Will be fully added in v0.9."""
129139

130140
digits = 7
131141

132142

133-
class Code128(Barcode):
143+
class Code128(Barcode[T_Output]):
134144
"""Initializes a new Code128 instance. The checksum is added automatically
135145
when building the bars.
136146
@@ -141,12 +151,11 @@ class Code128(Barcode):
141151
name = "Code 128"
142152
_charset: Literal["A", "B", "C"]
143153
code: str
144-
writer: BaseWriter
145154
buffer: str
146155

147-
def __init__(self, code: str, writer=None) -> None:
156+
def __init__(self, code: str, writer: BaseWriter[T_Output] | None = None) -> None:
148157
self.code = code
149-
self.writer = writer or self.default_writer()
158+
self.writer = self._resolve_writer(writer)
150159
self._charset = "C"
151160
self._digit_buffer = "" # Accumulate pairs of digits for charset C
152161
check_code(self.code, self.name, code128.ALL)
@@ -307,13 +316,17 @@ def build(self) -> list[str]:
307316
code += "11"
308317
return [code]
309318

310-
def render(self, writer_options=None, text=None):
319+
def render(
320+
self,
321+
writer_options: dict | None = None,
322+
text: str | None = None,
323+
) -> T_Output:
311324
options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE}
312325
options.update(writer_options or {})
313326
return super().render(options, text)
314327

315328

316-
class Gs1_128(Code128): # noqa: N801
329+
class Gs1_128(Code128[T_Output]): # noqa: N801
317330
"""
318331
following the norm, a gs1-128 barcode is a subset of code 128 barcode,
319332
it can be generated by prepending the code with the FNC1 character
@@ -325,7 +338,7 @@ class Gs1_128(Code128): # noqa: N801
325338

326339
FNC1_CHAR = "\xf1"
327340

328-
def __init__(self, code, writer=None) -> None:
341+
def __init__(self, code, writer: BaseWriter[T_Output] | None = None) -> None:
329342
code = self.FNC1_CHAR + code
330343
super().__init__(code, writer)
331344

0 commit comments

Comments
 (0)