Skip to content

Commit 21832c2

Browse files
maresbWhyNotHugo
authored andcommitted
Enforce that Barcode.build() returns a singleton list
Also add some type hints.
1 parent f88fa0e commit 21832c2

7 files changed

Lines changed: 61 additions & 43 deletions

File tree

barcode/base.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,17 @@ class Barcode:
3535
writer: BaseWriter
3636

3737
def to_ascii(self) -> str:
38-
code = self.build()
39-
for i, line in enumerate(code):
40-
code[i] = line.replace("1", "X").replace("0", " ")
41-
return "\n".join(code)
38+
code_list = self.build()
39+
if not len(code_list) == 1:
40+
raise RuntimeError("Code list must contain a single element.")
41+
code = code_list[0]
42+
return code.replace("1", "X").replace("0", " ")
4243

4344
def __repr__(self) -> str:
4445
return f"<{self.__class__.__name__}({self.get_fullcode()!r})>"
4546

4647
def build(self) -> list[str]:
48+
"""Return a singleton list with a string encoding the barcode as 1s and 0s."""
4749
raise NotImplementedError
4850

4951
def get_fullcode(self):
@@ -101,5 +103,8 @@ def render(self, writer_options: dict | None = None, text: str | None = None):
101103
else:
102104
options["text"] = self.get_fullcode()
103105
self.writer.set_options(options)
104-
code = self.build()
105-
return self.writer.render(code)
106+
code_list = self.build()
107+
if not len(code_list) == 1:
108+
raise RuntimeError("Code list must contain a single element.")
109+
code = code_list[0]
110+
return self.writer.render([code])

barcode/codabar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __str__(self) -> str:
4141
def get_fullcode(self):
4242
return self.code
4343

44-
def build(self):
44+
def build(self) -> list[str]:
4545
try:
4646
data = (
4747
codabar.STARTSTOP[self.code[0]] + "n"

barcode/codex.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
"""
55
from __future__ import annotations
66

7+
from typing import TYPE_CHECKING
78
from typing import Collection
9+
from typing import Literal
810

911
from barcode.base import Barcode
1012
from barcode.charsets import code39
@@ -13,6 +15,9 @@
1315
from barcode.errors import IllegalCharacterError
1416
from barcode.errors import NumberOfDigitsError
1517

18+
if TYPE_CHECKING:
19+
from barcode.writer import BaseWriter
20+
1621
__docformat__ = "restructuredtext en"
1722

1823
# Sizes
@@ -66,12 +71,13 @@ def calculate_checksum(self):
6671
return k
6772
return None
6873

69-
def build(self):
74+
def build(self) -> list[str]:
7075
chars = [code39.EDGE]
7176
for char in self.code:
7277
chars.append(code39.MAP[char][1])
7378
chars.append(code39.EDGE)
74-
return [code39.MIDDLE.join(chars)]
79+
result = code39.MIDDLE.join(chars)
80+
return [result]
7581

7682
def render(self, writer_options=None, text=None):
7783
options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE}
@@ -135,8 +141,12 @@ class Code128(Barcode):
135141
"""
136142

137143
name = "Code 128"
144+
_charset: Literal["A", "B", "C"]
145+
code: str
146+
writer: BaseWriter
147+
buffer: str
138148

139-
def __init__(self, code, writer=None) -> None:
149+
def __init__(self, code: str, writer=None) -> None:
140150
self.code = code
141151
self.writer = writer or self.default_writer()
142152
self._charset = "B"
@@ -147,13 +157,15 @@ def __str__(self) -> str:
147157
return self.code
148158

149159
@property
150-
def encoded(self):
160+
def encoded(self) -> list[int]:
151161
return self._build()
152162

153-
def get_fullcode(self):
163+
def get_fullcode(self) -> str:
154164
return self.code
155165

156-
def _new_charset(self, which):
166+
def _new_charset(self, which: Literal["A", "B", "C"]) -> list[int]:
167+
if which == self._charset:
168+
raise ValueError(f"Already in charset {which}")
157169
if which == "A":
158170
code = self._convert("TO_A")
159171
elif which == "B":
@@ -163,11 +175,11 @@ def _new_charset(self, which):
163175
self._charset = which
164176
return [code]
165177

166-
def _maybe_switch_charset(self, pos):
178+
def _maybe_switch_charset(self, pos: int) -> list[int]:
167179
char = self.code[pos]
168180
next_ = self.code[pos : pos + 10]
169181

170-
def look_next():
182+
def look_next() -> bool:
171183
digits = 0
172184
for c in next_:
173185
if c.isdigit():
@@ -176,7 +188,7 @@ def look_next():
176188
break
177189
return digits > 3
178190

179-
codes = []
191+
codes: list[int] = []
180192
if self._charset == "C" and not char.isdigit():
181193
if char in code128.B:
182194
codes = self._new_charset("B")
@@ -197,7 +209,7 @@ def look_next():
197209
codes = self._new_charset("B")
198210
return codes
199211

200-
def _convert(self, char):
212+
def _convert(self, char: str) -> int:
201213
if self._charset == "A":
202214
return code128.A[char]
203215
if self._charset == "B":
@@ -215,19 +227,19 @@ def _convert(self, char):
215227
f"Character {char} could not be converted in charset {self._charset}."
216228
)
217229

218-
def _try_to_optimize(self, encoded):
230+
def _try_to_optimize(self, encoded: list[int]) -> list[int]:
219231
if encoded[1] in code128.TO:
220232
encoded[:2] = [code128.TO[encoded[1]]]
221233
return encoded
222234

223-
def _calculate_checksum(self, encoded):
235+
def _calculate_checksum(self, encoded: list[int]) -> int:
224236
cs = [encoded[0]]
225237
for i, code_num in enumerate(encoded[1:], start=1):
226238
cs.append(i * code_num)
227239
return sum(cs) % 103
228240

229-
def _build(self):
230-
encoded = [code128.START_CODES[self._charset]]
241+
def _build(self) -> list[int]:
242+
encoded: list[int] = [code128.START_CODES[self._charset]]
231243
for i, char in enumerate(self.code):
232244
encoded.extend(self._maybe_switch_charset(i))
233245
code_num = self._convert(char)
@@ -240,7 +252,7 @@ def _build(self):
240252
self._buffer = ""
241253
return self._try_to_optimize(encoded)
242254

243-
def build(self):
255+
def build(self) -> list[str]:
244256
encoded = self._build()
245257
encoded.append(self._calculate_checksum(encoded))
246258
code = ""

barcode/ean.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ def calculate_checksum(self, value: str | None = None) -> int:
9494
oddsum = sum(int(x) for x in ean_without_checksum[-1::-2])
9595
return (10 - ((evensum + oddsum * 3) % 10)) % 10
9696

97-
def build(self):
97+
def build(self) -> list[str]:
9898
"""Builds the barcode pattern from `self.ean`.
9999
100100
:returns: The pattern as string
101-
:rtype: String
101+
:rtype: List containing the string as a single element
102102
"""
103103
code = self.EDGE[:]
104104
pattern = _ean.LEFT_PATTERN[int(self.ean[0])]
@@ -110,15 +110,16 @@ def build(self):
110110
code += self.EDGE
111111
return [code]
112112

113-
def to_ascii(self):
113+
def to_ascii(self) -> str:
114114
"""Returns an ascii representation of the barcode.
115115
116116
:rtype: String
117117
"""
118-
code = self.build()
119-
for i, line in enumerate(code):
120-
code[i] = line.replace("G", "|").replace("1", "|").replace("0", " ")
121-
return "\n".join(code)
118+
code_list = self.build()
119+
if not len(code_list) == 1:
120+
raise RuntimeError("Code list must contain a single element.")
121+
code = code_list[0]
122+
return code.replace("G", "|").replace("1", "|").replace("0", " ")
122123

123124
def render(self, writer_options=None, text=None):
124125
options = {"module_width": SIZES["SC2"]}
@@ -171,11 +172,10 @@ class EuropeanArticleNumber8(EuropeanArticleNumber13):
171172

172173
digits = 7
173174

174-
def build(self):
175+
def build(self) -> list[str]:
175176
"""Builds the barcode pattern from `self.ean`.
176177
177-
:returns: The pattern as string
178-
:rtype: String
178+
:returns: A list containing the string as a single element
179179
"""
180180
code = self.EDGE[:]
181181
for number in self.ean[:4]:

barcode/itf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def __str__(self) -> str:
4848
def get_fullcode(self):
4949
return self.code
5050

51-
def build(self):
51+
def build(self) -> list[str]:
5252
data = itf.START
5353
for i in range(0, len(self.code), 2):
5454
bars_digit = int(self.code[i])

barcode/upc.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ def sum_(x, y):
7777

7878
return 10 - check
7979

80-
def build(self):
80+
def build(self) -> list[str]:
8181
"""Builds the barcode pattern from 'self.upc'
8282
8383
:return: The pattern as string
84-
:rtype: str
84+
:rtype: List containing the string as a single element
8585
"""
8686
code = _upc.EDGE[:]
8787

@@ -97,16 +97,17 @@ def build(self):
9797

9898
return [code]
9999

100-
def to_ascii(self):
100+
def to_ascii(self) -> str:
101101
"""Returns an ascii representation of the barcode.
102102
103103
:rtype: str
104104
"""
105105

106-
code = self.build()
107-
for i, line in enumerate(code):
108-
code[i] = line.replace("1", "|").replace("0", "_")
109-
return "\n".join(code)
106+
code_list = self.build()
107+
if len(code_list) != 1:
108+
raise RuntimeError("Code list must contain a single element.")
109+
code = code_list[0]
110+
return code.replace("1", "|").replace("0", "_")
110111

111112
def render(self, writer_options=None, text=None):
112113
options = {"module_width": 0.33}

barcode/writer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,9 @@ def _create_text(self, xpos, ypos):
362362
attributes = {
363363
"x": SIZE.format(xpos),
364364
"y": SIZE.format(ypos),
365-
"style": "fill:{};font-size:{}pt;text-anchor:middle;".format(
366-
self.foreground,
367-
self.font_size,
365+
"style": (
366+
f"fill:{self.foreground};"
367+
f"font-size:{self.font_size}pt;text-anchor:middle;"
368368
),
369369
}
370370
_set_attributes(element, **attributes)

0 commit comments

Comments
 (0)