From 204f3e565caa9089b9aa427e623021ec8c2643f4 Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Wed, 23 Dec 2020 15:02:20 -0300 Subject: [PATCH 001/192] Adds support for longer guard bar to EAN13 and EAN8 Adds a parameter to both classes that controls if we want longer guard bars and then while building the code to be generated, use "G" instead of "1" in those places we will need to have a longer bar. During the write, interpret every "G" the same as "1" but add an height factor to the mod list. To avoid changing the entire code base to add a height parameter, set the self.module_height for every bar generated. --- barcode/ean.py | 27 +++++++++++++++++---------- barcode/writer.py | 10 ++++++++-- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index 0b4ab5b..3d13591 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -43,7 +43,7 @@ class EuropeanArticleNumber13(Barcode): digits = 12 - def __init__(self, ean, writer=None, no_checksum=False): + def __init__(self, ean, writer=None, no_checksum=False, guardbar=False): ean = ean[: self.digits] if not ean.isdigit(): raise IllegalCharacterError("EAN code can only contain numbers.") @@ -64,6 +64,13 @@ def __init__(self, ean, writer=None, no_checksum=False): ) else: self.ean = "{}{}".format(ean, self.calculate_checksum()) + + if guardbar: + self.EDGE = _ean.EDGE.replace("1", "G") + self.MIDDLE = _ean.MIDDLE.replace("1", "G") + else: + self.EDGE = _ean.EDGE + self.MIDDLE = _ean.MIDDLE self.writer = writer or Barcode.default_writer() def __str__(self): @@ -92,14 +99,14 @@ def build(self): :returns: The pattern as string :rtype: String """ - code = _ean.EDGE[:] + code = self.EDGE[:] pattern = _ean.LEFT_PATTERN[int(self.ean[0])] for i, number in enumerate(self.ean[1:7]): code += _ean.CODES[pattern[i]][int(number)] - code += _ean.MIDDLE + code += self.MIDDLE for number in self.ean[7:]: code += _ean.CODES["C"][int(number)] - code += _ean.EDGE + code += self.EDGE return [code] def to_ascii(self): @@ -109,7 +116,7 @@ def to_ascii(self): """ code = self.build() for i, line in enumerate(code): - code[i] = line.replace("1", "|").replace("0", " ") + code[i] = line.replace("G", "|").replace("1", "|").replace("0", " ") return "\n".join(code) def render(self, writer_options=None, text=None): @@ -154,8 +161,8 @@ class EuropeanArticleNumber8(EuropeanArticleNumber13): digits = 7 - def __init__(self, ean, writer=None): - EuropeanArticleNumber13.__init__(self, ean, writer) + def __init__(self, ean, writer=None, guardbar=False): + EuropeanArticleNumber13.__init__(self, ean, writer, guardbar=guardbar) def build(self): """Builds the barcode pattern from `self.ean`. @@ -163,13 +170,13 @@ def build(self): :returns: The pattern as string :rtype: String """ - code = _ean.EDGE[:] + code = self.EDGE[:] for number in self.ean[:4]: code += _ean.CODES["A"][int(number)] - code += _ean.MIDDLE + code += self.MIDDLE for number in self.ean[4:]: code += _ean.CODES["C"][int(number)] - code += _ean.EDGE + code += self.EDGE return [code] diff --git a/barcode/writer.py b/barcode/writer.py index e3fbbff..3e845b5 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -177,6 +177,7 @@ def render(self, code): if self._callbacks["initialize"] is not None: self._callbacks["initialize"](code) ypos = 1.0 + base_height = self.module_height for cc, line in enumerate(code): """ Pack line to list give better gfx result, otherwise in can @@ -191,14 +192,19 @@ def render(self, code): c += 1 else: if line[i] == "1": - mlist.append(c) + mlist.append((c, 1)) + elif line[i] == "G": + mlist.append((c, 1.1)) else: - mlist.append(-c) + mlist.append((-c, 1)) c = 1 # Left quiet zone is x startposition xpos = self.quiet_zone bxs = xpos # x start of barcode for mod in mlist: + self.module_height = base_height * mod[1] + mod = mod[0] + if mod < 1: color = self.background else: From c4e0b7f1faef48d5532998bb4c5a76d45851a155 Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Wed, 23 Dec 2020 15:23:31 -0300 Subject: [PATCH 002/192] Tests support for longer guard bar to EAN13 and EAN8 --- tests/test_builds.py | 7 +++++++ tests/test_writers.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/tests/test_builds.py b/tests/test_builds.py index fa99476..6ad76bd 100755 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -6,3 +6,10 @@ def test_ean8_builds(): ean = get_barcode("ean8", "40267708") bc = ean.build() assert ref == bc[0] + +def test_ean8_builds_with_longer_bars(): + ref = "G0G01000110001101001001101011110G0G01000100100010011100101001000G0G" + ean = get_barcode("ean8", "40267708", options={"guardbar": True}) + bc = ean.build() + assert ref == bc[0] + diff --git a/tests/test_writers.py b/tests/test_writers.py index df178b2..e4b1585 100644 --- a/tests/test_writers.py +++ b/tests/test_writers.py @@ -36,3 +36,11 @@ def test_saving_svg_to_byteio(): with open(f"{TESTPATH}/somefile.svg", "wb") as f: EAN13("100000011111", writer=SVGWriter()).write(f) + +def test_saving_svg_to_byteio_with_guardbar(): + rv = BytesIO() + EAN13(str(100000902922), writer=SVGWriter(), guardbar=True).write(rv) + + with open(f"{TESTPATH}/somefile_guardbar.svg", "wb") as f: + EAN13("100000011111", writer=SVGWriter(), guardbar=True).write(f) + From 85e3665a0dc56de4903657060ffd15bf1c852717 Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Wed, 23 Dec 2020 19:43:01 -0300 Subject: [PATCH 003/192] Returns the ean with spaces if the guardbar is enabled --- barcode/ean.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/barcode/ean.py b/barcode/ean.py index 3d13591..d9639f5 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -65,6 +65,7 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=False): else: self.ean = "{}{}".format(ean, self.calculate_checksum()) + self.guardbar = guardbar if guardbar: self.EDGE = _ean.EDGE.replace("1", "G") self.MIDDLE = _ean.MIDDLE.replace("1", "G") @@ -77,6 +78,8 @@ def __str__(self): return self.ean def get_fullcode(self): + if self.guardbar: + return self.ean[0] + " " + self.ean[1:7] + " " + self.ean[7:] + " >" return self.ean def calculate_checksum(self): @@ -179,6 +182,10 @@ def build(self): code += self.EDGE return [code] + def get_fullcode(self): + if self.guardbar: + return "< " + self.ean[:4] + " " + self.ean[4:] + " >" + return self.ean class EuropeanArticleNumber14(EuropeanArticleNumber13): """Represents an EAN-14 barcode. See EAN13's __init__ for details. From b8a5c67447f083de8b94536f98a2ba8a3fd8373f Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Wed, 23 Dec 2020 21:08:55 -0300 Subject: [PATCH 004/192] Put the EAN13 text in the expected position if the guards are enabled --- barcode/writer.py | 64 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 3e845b5..c9bb1af 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -201,19 +201,42 @@ def render(self, code): # Left quiet zone is x startposition xpos = self.quiet_zone bxs = xpos # x start of barcode + text = { + "start": [], # The x start of a guard + "end": [], # The x end of a guard + "xpos": [], # The x position where to write a text block + "was_guard": False # Flag that indicates if the previous mod + # was part of an guard block + } for mod in mlist: - self.module_height = base_height * mod[1] + height_factor = mod[1] mod = mod[0] 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) + else: + if height_factor != 1: + 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, @@ -223,14 +246,41 @@ def render(self, code): xpos, ypos, self.quiet_zone, self.background ) ypos += self.module_height + if self.text and self._callbacks["paint_text"] is not None: - ypos += self.text_distance - if self.center_text: - # better center position for text - xpos = bxs + ((bxe - bxs) / 2.0) + if not text["start"]: + # If we don't have any start value, print the entire ean + ypos += self.text_distance + if self.center_text: + # better center position for text + xpos = bxs + ((bxe - bxs) / 2.0) + else: + xpos = bxs + self._callbacks["paint_text"](xpos, ypos) else: - xpos = bxs - self._callbacks["paint_text"](xpos, ypos) + # Else, divide the ean into blocks and print each block + # in the expected position. + text["xpos"] = [bxs - 4 * self.module_width] + + # Calculates the position of the text by getting the difference + # between a guard end and the next start + text["start"].pop(0) + for (s, e) in zip(text["start"], text["end"]): + text["xpos"].append(e + (s - e) / 2) + + # The last text block is always put after the last guard end + text["xpos"].append(text["end"][-1] + 4 * self.module_width) + + # Split the ean into its blocks + self.text = self.text.split(" ") + + ypos += pt2mm(self.font_size) + + blocks = self.text + for (text, xpos) in zip(blocks, text["xpos"]): + self.text = text + self._callbacks["paint_text"](xpos, ypos) + return self._callbacks["finish"]() From 40ac94c730e8b4e7a657aea1e61237ab09ba9914 Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Wed, 23 Dec 2020 21:52:22 -0300 Subject: [PATCH 005/192] Adds EAN8_GUARD and EAN13_GUARD Its easier for the user to use and doesn't cause backward incompatibilities. There must be a user somewhere that relies on the older behavior. With these classes we also can include the guards on the manual tests. --- barcode/__init__.py | 4 ++++ barcode/ean.py | 19 +++++++++++++++++++ tests/test_manually.py | 2 ++ 3 files changed, 25 insertions(+) diff --git a/barcode/__init__.py b/barcode/__init__.py index 571fdea..03487f5 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -13,8 +13,10 @@ from barcode.codex import Gs1_128 from barcode.codex import PZN from barcode.ean import EAN13 +from barcode.ean import EAN13_GUARD from barcode.ean import EAN14 from barcode.ean import EAN8 +from barcode.ean import EAN8_GUARD from barcode.ean import JAN from barcode.errors import BarcodeNotFoundError from barcode.isxn import ISBN10 @@ -26,7 +28,9 @@ __BARCODE_MAP = { "ean8": EAN8, + "ean8-guard": EAN8_GUARD, "ean13": EAN13, + "ean13-guard": EAN13_GUARD, "ean": EAN13, "gtin": EAN14, "ean14": EAN14, diff --git a/barcode/ean.py b/barcode/ean.py index d9639f5..e17393b 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -128,6 +128,14 @@ def render(self, writer_options=None, text=None): return Barcode.render(self, options, text) +class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13): + + name = "EAN-13 with guards" + + def __init__(self, *args, guardbar=True, **kwargs): + return super().__init__(*args, guardbar=guardbar, **kwargs) + + class JapanArticleNumber(EuropeanArticleNumber13): """Initializes JAN barcode. @@ -187,6 +195,15 @@ def get_fullcode(self): return "< " + self.ean[:4] + " " + self.ean[4:] + " >" return self.ean + +class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8): + + name = "EAN-8 with guards" + + def __init__(self, *args, guardbar=True, **kwargs): + return super().__init__(*args, guardbar=guardbar, **kwargs) + + class EuropeanArticleNumber14(EuropeanArticleNumber13): """Represents an EAN-14 barcode. See EAN13's __init__ for details. @@ -218,5 +235,7 @@ def sum_(x, y): # Shortcuts EAN14 = EuropeanArticleNumber14 EAN13 = EuropeanArticleNumber13 +EAN13_GUARD = EuropeanArticleNumber13WithGuard EAN8 = EuropeanArticleNumber8 +EAN8_GUARD = EuropeanArticleNumber8WithGuard JAN = JapanArticleNumber diff --git a/tests/test_manually.py b/tests/test_manually.py index 337573c..6ab7686 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -32,7 +32,9 @@ TESTCODES = ( ("ean8", "40267708"), + ("ean8-guard", "40267708"), ("ean13", "5901234123457"), + ("ean13-guard", "5901234123457"), ("ean14", "12345678911230"), ("upca", "36000291453"), ("jan", "4901234567894"), From a806aa3985955014ab4d4ba24dff127fcf87bb3d Mon Sep 17 00:00:00 2001 From: Fernando Date: Wed, 23 Dec 2020 22:55:25 -0300 Subject: [PATCH 006/192] Fix small glitch at the end of all guard bars Looks like images are rendered with the guards slightly "fatter" in their lower end. Making the white lines the same length as the G-lines seems to fix it. Co-authored-by: Hugo Barrera --- barcode/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index c9bb1af..de32b64 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -196,7 +196,7 @@ def render(self, code): elif line[i] == "G": mlist.append((c, 1.1)) else: - mlist.append((-c, 1)) + mlist.append((-c, 1.1)) c = 1 # Left quiet zone is x startposition xpos = self.quiet_zone From c8b44582171d2dc5a0a1cf751292e326adfa9748 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 24 Dec 2020 13:06:49 +0100 Subject: [PATCH 007/192] Add changelog entry for #101 --- docs/changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index b44e87d..de522ff 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,12 @@ Changelog --------- +Unreleased +~~~~~~~~~~ + +* The default dimensions have changed slightly. This is so that the results of + generating a PNG and an SVG look more alike. + v0.13.1 ~~~~~~~ From ba2a034f6367a654739e609265341478b4ef90a9 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 24 Dec 2020 13:08:23 +0100 Subject: [PATCH 008/192] Update pre-commit hooks --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 20fe897..c418dbb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,14 +3,14 @@ # vim: set nospell: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.4.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.5 + rev: v2.3.6 hooks: - id: reorder-python-imports - repo: https://github.com/psf/black @@ -18,11 +18,11 @@ repos: hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: "3.8.3" # pick a git hash / tag to point to + rev: "3.8.4" # pick a git hash / tag to point to hooks: - id: flake8 additional_dependencies: [flake8-comprehensions, flake8-import-order, flake8-bugbear] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.782' # Use the sha / tag you want to point at + rev: 'v0.790' # Use the sha / tag you want to point at hooks: - id: mypy From 6adb011d2ea34673e5a7ce8e9c615f9032635933 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 24 Dec 2020 13:58:20 +0100 Subject: [PATCH 009/192] Fix linting --- barcode/writer.py | 10 +++++----- tests/test_builds.py | 2 +- tests/test_writers.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index a1e3066..b6f9494 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -200,11 +200,11 @@ def render(self, code): xpos = self.quiet_zone bxs = xpos # x start of barcode text = { - "start": [], # The x start of a guard - "end": [], # The x end of a guard - "xpos": [], # The x position where to write a text block - "was_guard": False # Flag that indicates if the previous mod - # was part of an guard block + "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 in mlist: height_factor = mod[1] diff --git a/tests/test_builds.py b/tests/test_builds.py index 6ad76bd..512c2c5 100755 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -7,9 +7,9 @@ def test_ean8_builds(): bc = ean.build() assert ref == bc[0] + def test_ean8_builds_with_longer_bars(): ref = "G0G01000110001101001001101011110G0G01000100100010011100101001000G0G" ean = get_barcode("ean8", "40267708", options={"guardbar": True}) bc = ean.build() assert ref == bc[0] - diff --git a/tests/test_writers.py b/tests/test_writers.py index e4b1585..6a876eb 100644 --- a/tests/test_writers.py +++ b/tests/test_writers.py @@ -37,10 +37,10 @@ def test_saving_svg_to_byteio(): with open(f"{TESTPATH}/somefile.svg", "wb") as f: EAN13("100000011111", writer=SVGWriter()).write(f) + def test_saving_svg_to_byteio_with_guardbar(): rv = BytesIO() EAN13(str(100000902922), writer=SVGWriter(), guardbar=True).write(rv) with open(f"{TESTPATH}/somefile_guardbar.svg", "wb") as f: EAN13("100000011111", writer=SVGWriter(), guardbar=True).write(f) - From 95d3f05cb32697cd0697c7593dd342dbc26059aa Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Mon, 28 Dec 2020 23:23:38 -0300 Subject: [PATCH 010/192] Facilitates sub-classing and user customization by using super() Using super() instead of hard-coding the class name enables the class to be sub-classed more easily. --- barcode/codex.py | 6 +++--- barcode/ean.py | 11 ++++------- barcode/isxn.py | 6 +++--- barcode/itf.py | 2 +- barcode/upc.py | 2 +- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index 870c8c2..0d88648 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -71,7 +71,7 @@ def build(self): def render(self, writer_options=None, text=None): options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE} options.update(writer_options or {}) - return Barcode.render(self, options, text) + return super().render(options, text) class PZN7(Code39): @@ -98,7 +98,7 @@ def __init__(self, pzn, writer=None): ) self.pzn = pzn self.pzn = "{}{}".format(pzn, self.calculate_checksum()) - Code39.__init__(self, "PZN-{}".format(self.pzn), writer, add_checksum=False) + super().__init__("PZN-{}".format(self.pzn), writer, add_checksum=False) def get_fullcode(self): return "PZN-{}".format(self.pzn) @@ -248,7 +248,7 @@ def build(self): def render(self, writer_options=None, text=None): options = {"module_width": MIN_SIZE, "quiet_zone": MIN_QUIET_ZONE} options.update(writer_options or {}) - return Barcode.render(self, options, text) + return super().render(options, text) class Gs1_128(Code128): diff --git a/barcode/ean.py b/barcode/ean.py index e17393b..5d2e86b 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -125,7 +125,7 @@ def to_ascii(self): def render(self, writer_options=None, text=None): options = {"module_width": SIZES["SC2"]} options.update(writer_options or {}) - return Barcode.render(self, options, text) + return super().render(options, text) class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13): @@ -150,12 +150,12 @@ class JapanArticleNumber(EuropeanArticleNumber13): valid_country_codes = list(range(450, 460)) + list(range(490, 500)) - def __init__(self, jan, writer=None): - if int(jan[:3]) not in JapanArticleNumber.valid_country_codes: + def __init__(self, jan, *args, **kwargs): + if int(jan[:3]) not in self.valid_country_codes: raise WrongCountryCodeError( "Country code isn't between 450-460 or 490-500." ) - EuropeanArticleNumber13.__init__(self, jan, writer) + super().__init__(jan, *args, **kwargs) class EuropeanArticleNumber8(EuropeanArticleNumber13): @@ -172,9 +172,6 @@ class EuropeanArticleNumber8(EuropeanArticleNumber13): digits = 7 - def __init__(self, ean, writer=None, guardbar=False): - EuropeanArticleNumber13.__init__(self, ean, writer, guardbar=guardbar) - def build(self): """Builds the barcode pattern from `self.ean`. diff --git a/barcode/isxn.py b/barcode/isxn.py index 3d73ce1..90ca5fb 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -48,7 +48,7 @@ def __init__(self, isbn, writer=None): if isbn[:3] == "979": if isbn[3:5] not in ("10", "11"): raise BarcodeError("ISBN must start with 97910 or 97911.") - EuropeanArticleNumber13.__init__(self, isbn, writer) + super().__init__(isbn, writer) class InternationalStandardBookNumber10(InternationalStandardBookNumber13): @@ -71,7 +71,7 @@ def __init__(self, isbn, writer=None): isbn = isbn[: self.digits] self.isbn10 = isbn self.isbn10 = "{}{}".format(isbn, self._calculate_checksum()) - InternationalStandardBookNumber13.__init__(self, "978" + isbn, writer) + super().__init__("978" + isbn, writer) def _calculate_checksum(self): tmp = sum(x * int(y) for x, y in enumerate(self.isbn10[:9], start=1)) % 11 @@ -104,7 +104,7 @@ def __init__(self, issn, writer=None): issn = issn[: self.digits] self.issn = issn self.issn = "{}{}".format(issn, self._calculate_checksum()) - EuropeanArticleNumber13.__init__(self, self.make_ean(), writer) + super().__init__(self.make_ean(), writer) def _calculate_checksum(self): tmp = ( diff --git a/barcode/itf.py b/barcode/itf.py index 4978dcd..377bc5f 100644 --- a/barcode/itf.py +++ b/barcode/itf.py @@ -73,4 +73,4 @@ def render(self, writer_options, text=None): "quiet_zone": MIN_QUIET_ZONE, } options.update(writer_options or {}) - return Barcode.render(self, options, text) + return super().render(options, text) diff --git a/barcode/upc.py b/barcode/upc.py index c3ad3c8..cdd6ed2 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -108,7 +108,7 @@ def to_ascii(self): def render(self, writer_options=None, text=None): options = {"module_width": 0.33} options.update(writer_options or {}) - return Barcode.render(self, options, text) + return super().render(options, text) UPCA = UniversalProductCodeA From 545fef683371730f5cde30c5d9c302c11f853649 Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Mon, 28 Dec 2020 23:25:02 -0300 Subject: [PATCH 011/192] Facilitates sub-classing and user customization by using self This allows changing the default_writer/default_writer_options for some sub-classes of Barcode without affecting all of them. --- barcode/base.py | 2 +- barcode/codex.py | 4 ++-- barcode/ean.py | 2 +- barcode/itf.py | 2 +- barcode/upc.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/barcode/base.py b/barcode/base.py index 7b7b0fe..c6fa312 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -93,7 +93,7 @@ def render(self, writer_options=None, text=None): :returns: Output of the writers render method. """ - options = Barcode.default_writer_options.copy() + options = self.default_writer_options.copy() options.update(writer_options or {}) if options["write_text"] or text is not None: if text is not None: diff --git a/barcode/codex.py b/barcode/codex.py index 0d88648..da3caf7 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -45,7 +45,7 @@ def __init__(self, code: str, writer=None, add_checksum: bool = True): self.code = code.upper() if add_checksum: self.code += self.calculate_checksum() - self.writer = writer or Barcode.default_writer() + self.writer = writer or self.default_writer() check_code(self.code, self.name, code39.REF) def __str__(self): @@ -133,7 +133,7 @@ class Code128(Barcode): def __init__(self, code, writer=None): self.code = code - self.writer = writer or Barcode.default_writer() + self.writer = writer or self.default_writer() self._charset = "B" self._buffer = "" check_code(self.code, self.name, code128.ALL) diff --git a/barcode/ean.py b/barcode/ean.py index 5d2e86b..07c7ac2 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -72,7 +72,7 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=False): else: self.EDGE = _ean.EDGE self.MIDDLE = _ean.MIDDLE - self.writer = writer or Barcode.default_writer() + self.writer = writer or self.default_writer() def __str__(self): return self.ean diff --git a/barcode/itf.py b/barcode/itf.py index 377bc5f..b79b4bd 100644 --- a/barcode/itf.py +++ b/barcode/itf.py @@ -36,7 +36,7 @@ def __init__(self, code, writer=None, narrow=2, wide=5): if len(code) % 2 != 0: code = "0" + code self.code = code - self.writer = writer or Barcode.default_writer() + self.writer = writer or self.default_writer() self.narrow = narrow self.wide = wide diff --git a/barcode/upc.py b/barcode/upc.py index cdd6ed2..4ac84be 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -41,7 +41,7 @@ def __init__(self, upc, writer=None, make_ean=False): ) self.upc = upc self.upc = "{}{}".format(upc, self.calculate_checksum()) - self.writer = writer or Barcode.default_writer() + self.writer = writer or self.default_writer() def __str__(self): if self.ean: From afe780a04ddb5e1f4900a175d66bba0e51177add Mon Sep 17 00:00:00 2001 From: Fernando Governatore Date: Tue, 29 Dec 2020 00:29:15 -0300 Subject: [PATCH 012/192] Extracts method packed from render Makes the render method easier to understand and the packing easier to override. Avoids building a list unnecessarily by yielding the values one by one. Updates documentation to allow the character "G"(guard) in the writer spec. Also adds `guard_height_factor` to allow changing the default proportion between the guard bars and normal ones. --- barcode/writer.py | 54 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index b6f9494..9b939bb 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -98,6 +98,7 @@ def __init__( self.text_distance = 5 self.text_line_distance = 1 self.center_text = True + self.guard_height_factor = 1.1 def calculate_size(self, modules_per_line, number_of_lines): """Calculates the size of the barcode in pixel. @@ -163,6 +164,33 @@ def set_options(self, options): if hasattr(self, key): setattr(self, key, val) + def packed(self, line): + """ + Pack line to list give better gfx result, otherwise in can + result in aliasing gaps + '11010111' -> [2, -1, 1, -1, 3] + + This method will yield a sequence of pairs (width, height_factor). + + :parameters: + line: String + A string matching the writer spec + (only contain 0 or 1 or G). + """ + line += " " + c = 1 + for i in range(0, len(line) - 1): + if line[i] == line[i + 1]: + c += 1 + else: + if line[i] == "1": + yield (c, 1) + elif line[i] == "G": + yield (c, self.guard_height_factor) + else: + yield (-c, self.guard_height_factor) + c = 1 + def render(self, code): """Renders the barcode to whatever the inheriting writer provides, using the registered callbacks. @@ -170,32 +198,13 @@ def render(self, code): :parameters: code : List List of strings matching the writer spec - (only contain 0 or 1). + (only contain 0 or 1 or G). """ if self._callbacks["initialize"] is not None: self._callbacks["initialize"](code) ypos = 1.0 base_height = self.module_height for cc, line in enumerate(code): - """ - Pack line to list give better gfx result, otherwise in can - result in aliasing gaps - '11010111' -> [2, -1, 1, -1, 3] - """ - line += " " - c = 1 - mlist = [] - for i in range(0, len(line) - 1): - if line[i] == line[i + 1]: - c += 1 - else: - if line[i] == "1": - mlist.append((c, 1)) - elif line[i] == "G": - mlist.append((c, 1.1)) - else: - mlist.append((-c, 1.1)) - c = 1 # Left quiet zone is x startposition xpos = self.quiet_zone bxs = xpos # x start of barcode @@ -206,10 +215,7 @@ def render(self, code): # Flag that indicates if the previous mod was part of an guard block: "was_guard": False, } - for mod in mlist: - height_factor = mod[1] - mod = mod[0] - + for mod, height_factor in self.packed(line): if mod < 1: color = self.background else: From 582969a4121ba0ff3b8c8cd7e7064f4ef2734963 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 29 Dec 2020 11:48:32 +0100 Subject: [PATCH 013/192] Fix bad indentation --- barcode/writer.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 9b939bb..139e525 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -180,16 +180,16 @@ def packed(self, line): line += " " c = 1 for i in range(0, len(line) - 1): - if line[i] == line[i + 1]: - c += 1 - else: - if line[i] == "1": - yield (c, 1) - elif line[i] == "G": - yield (c, self.guard_height_factor) - else: - yield (-c, self.guard_height_factor) - c = 1 + if line[i] == line[i + 1]: + c += 1 + else: + if line[i] == "1": + yield (c, 1) + elif line[i] == "G": + yield (c, self.guard_height_factor) + else: + yield (-c, self.guard_height_factor) + c = 1 def render(self, code): """Renders the barcode to whatever the inheriting writer provides, From 8fa7b0c23dfe2c5184b6c0cfd773aeec2cf6c707 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Mar 2021 20:01:01 +0100 Subject: [PATCH 014/192] Rename master branch to main --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 6e33e32..bd04901 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ python-barcode :target: https://github.com/WhyNotHugo/python-barcode/actions :alt: CI status -.. image:: https://codecov.io/gh/WhyNotHugo/python-barcode/branch/master/graph/badge.svg +.. image:: https://codecov.io/gh/WhyNotHugo/python-barcode/branch/main/graph/badge.svg :target: https://codecov.io/gh/WhyNotHugo/python-barcode :alt: Build coverage @@ -22,7 +22,7 @@ python-barcode :alt: downloads .. image:: https://img.shields.io/pypi/l/python-barcode.svg - :target: https://github.com/WhyNotHugo/python-barcode/blob/master/LICENCE + :target: https://github.com/WhyNotHugo/python-barcode/blob/main/LICENCE :alt: licence **python-barcode** provides a simple way to create barcodes in Python. From 9a81a93db8bc5a9702a6aeadb2f0b2ad7c115cad Mon Sep 17 00:00:00 2001 From: Thane Gill Date: Fri, 19 Mar 2021 13:53:14 -0700 Subject: [PATCH 015/192] Only add COMMENT if it exists. This allows setting it to `None` --- barcode/writer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index 139e525..1dc0f00 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -308,7 +308,8 @@ def _init(self, code): "height": SIZE.format(height), } _set_attributes(self._root, **attributes) - self._root.appendChild(self._document.createComment(COMMENT)) + if COMMENT: + self._root.appendChild(self._document.createComment(COMMENT)) # create group for easier handling in 3rd party software # like corel draw, inkscape, ... group = self._document.createElement("g") From 1fe41b223f234253a3352009d2e38791e36a861d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 7 Apr 2021 09:34:25 +0200 Subject: [PATCH 016/192] Add some missing changelog entries --- docs/changelog.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index de522ff..cc098c9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,13 @@ Unreleased * The default dimensions have changed slightly. This is so that the results of generating a PNG and an SVG look more alike. +* Previous versions included an empty text element for SVGs with no comment. + This is no longer the case. +* Some internals have been improved so as to allow better subclassing. + Subclasses of ``Barcode`` can now override ``default_writer_options`` and + ``default_writer()``. +* A ``guardbar`` parameter has been added to EAN barcodes. This renders + barcodes with guardars (longer bars). v0.13.1 ~~~~~~~ @@ -17,6 +24,8 @@ v0.13.0 * Added support for transparent backgrounds. This is done by setting the ``mode`` option for a writer to ``RGBA``. +* Dropped support for Python 3.5. +* Added support for Python 3.9. v0.12.0 ~~~~~~~ From 341d09b8c0dae57583ee56a309b08282cbdcb89f Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 7 Apr 2021 09:46:45 +0200 Subject: [PATCH 017/192] Symlink index.rst -> contents.rst RTD uses index.html as the default page, so without this, docs have no index page. --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) create mode 120000 docs/index.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 120000 index 0000000..a3419c6 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1 @@ +contents.rst \ No newline at end of file From 83a0b4e87755f8dcb22c9cf35c52f6f3ae7a6993 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 7 Apr 2021 09:47:41 +0200 Subject: [PATCH 018/192] Don't auto-collapse the sidebar --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index e19d18e..468ef53 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -61,7 +61,6 @@ "github_user": "WhyNotHugo", "github_repo": "python-barcode", "github_banner": "true", - "sidebar_collapse": False, } graphviz_output_format = "svg" From 1a1e5e591d2e4eb4a6b2c4741ef922c4d846e63a Mon Sep 17 00:00:00 2001 From: Ashleigh Udoh Date: Tue, 6 Jul 2021 15:11:24 +0100 Subject: [PATCH 019/192] remove is-even check from Code128 look_next() Okay so I'm not totally sure I understand the logic behind the is-even check here. It was added as part of a commit intended to optimise the barcode width. So, from what I can see, there are two rules for whether to switch to 128C: - If the 128C section is followed by another charset switch, it should be at least 5 digits long to save space. - If it is the last charset of the barcode, it should be at least 4 digits long to save space. As long as these two things are true, you will always see a space saving over not switching. Now, the code only checks if it's at least 4 digits, but that's fine since it's simpler and just means that there is a case where we will switch to 128C without saving any width, but also without incurring any additional width. Correct me if I'm wrong, but I don't think evenness comes into it - a single digit encoded in 128C does not itself save space, but may as well be included in the rest of the 128C block which we already know will save space because it's at least 4 characters long. Because of the evenness check, there's a case where we can create a longer barcode. When the barcode starts with a sequence of 5 digits, the first digit will be encoded in 128B because of the odd number of digits, incurring an additional charset switch and widening the barcode. I'm wondering if anyone has an example of a barcode which is shorter because of the evenness check. Otherwise I think we should go ahead and remove it as proposed. --- barcode/codex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/codex.py b/barcode/codex.py index da3caf7..6f277af 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -169,7 +169,7 @@ def look_next(): digits += 1 else: break - return digits > 3 and (digits % 2) == 0 + return digits > 3 codes = [] if self._charset == "C" and not char.isdigit(): From fa5e6abe6a30ab17e99d989bea368967eacaf10d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Fri, 16 Jul 2021 18:30:27 +0200 Subject: [PATCH 020/192] Update pre-commit hooks --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c418dbb..e916efe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,26 +3,26 @@ # vim: set nospell: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.0.1 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.6 + rev: v2.5.0 hooks: - id: reorder-python-imports - repo: https://github.com/psf/black - rev: "20.8b1" + rev: "21.6b0" hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: "3.8.4" # pick a git hash / tag to point to + rev: "3.9.2" # pick a git hash / tag to point to hooks: - id: flake8 additional_dependencies: [flake8-comprehensions, flake8-import-order, flake8-bugbear] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.790' # Use the sha / tag you want to point at + rev: 'v0.910' # Use the sha / tag you want to point at hooks: - id: mypy From 0f7be92a00221403ae546655e56f80f554a1bcb4 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Fri, 16 Jul 2021 18:33:07 +0200 Subject: [PATCH 021/192] Use pre-commit.ci to run pre-commit hooks on CI --- .github/workflows/checks.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .github/workflows/checks.yml diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml deleted file mode 100644 index 2d40d5f..0000000 --- a/.github/workflows/checks.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Checks - -on: [push, pull_request] - -jobs: - styles: - runs-on: ubuntu-18.04 - name: Linting - steps: - - uses: actions/checkout@master - - uses: actions/setup-python@v1 - - uses: whynothugo/python-linting@master From b2eb45949d3297ca74effa1b2f0f8f4fb4f36af8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 17:46:22 +0000 Subject: [PATCH 022/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/reorder_python_imports: v2.5.0 → v2.6.0](https://github.com/asottile/reorder_python_imports/compare/v2.5.0...v2.6.0) - [github.com/psf/black: 21.6b0 → 21.7b0](https://github.com/psf/black/compare/21.6b0...21.7b0) - https://gitlab.com/pycqa/flake8 → https://github.com/PyCQA/flake8 --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e916efe..f036f2f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,14 +10,14 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/asottile/reorder_python_imports - rev: v2.5.0 + rev: v2.6.0 hooks: - id: reorder-python-imports - repo: https://github.com/psf/black - rev: "21.6b0" + rev: "21.7b0" hooks: - id: black - - repo: https://gitlab.com/pycqa/flake8 + - repo: https://github.com/PyCQA/flake8 rev: "3.9.2" # pick a git hash / tag to point to hooks: - id: flake8 From 1614c7df2e0e7731f9a75e268122c2fd0342d7e3 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 4 Aug 2021 20:14:57 +0200 Subject: [PATCH 023/192] Set up isort --- .pre-commit-config.yaml | 13 ++++++------- setup.cfg | 3 +++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f036f2f..b675a54 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,3 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -# vim: set nospell: repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 @@ -9,10 +6,10 @@ repos: args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - - repo: https://github.com/asottile/reorder_python_imports - rev: v2.6.0 + - repo: https://github.com/pycqa/isort + rev: 5.9.3 hooks: - - id: reorder-python-imports + - id: isort - repo: https://github.com/psf/black rev: "21.7b0" hooks: @@ -21,7 +18,9 @@ repos: rev: "3.9.2" # pick a git hash / tag to point to hooks: - id: flake8 - additional_dependencies: [flake8-comprehensions, flake8-import-order, flake8-bugbear] + additional_dependencies: + - flake8-comprehensions + - flake8-bugbear - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v0.910' # Use the sha / tag you want to point at hooks: diff --git a/setup.cfg b/setup.cfg index 1a7de2b..b14c12e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,6 +3,9 @@ exclude = docs/conf.py max-line-length = 88 extend-ignore = E203, W503 +[isort] +force_single_line=true + [tool:pytest] addopts = -vv From 528c82d03c9cbdca670e3df0489a61c84ffd111d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 4 Aug 2021 20:16:32 +0200 Subject: [PATCH 024/192] Sort imports --- barcode/__init__.py | 8 ++++---- barcode/codex.py | 2 +- barcode/ean.py | 8 +++----- barcode/upc.py | 3 ++- barcode/writer.py | 4 +++- tests/test_writers.py | 1 - 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 03487f5..87753af 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -8,15 +8,15 @@ from typing import Dict from typing import Union -from barcode.codex import Code128 +from barcode.codex import PZN from barcode.codex import Code39 +from barcode.codex import Code128 from barcode.codex import Gs1_128 -from barcode.codex import PZN +from barcode.ean import EAN8 +from barcode.ean import EAN8_GUARD from barcode.ean import EAN13 from barcode.ean import EAN13_GUARD from barcode.ean import EAN14 -from barcode.ean import EAN8 -from barcode.ean import EAN8_GUARD from barcode.ean import JAN from barcode.errors import BarcodeNotFoundError from barcode.isxn import ISBN10 diff --git a/barcode/codex.py b/barcode/codex.py index 6f277af..3f19008 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -3,8 +3,8 @@ :Provided barcodes: Code 39, Code 128, PZN """ from barcode.base import Barcode -from barcode.charsets import code128 from barcode.charsets import code39 +from barcode.charsets import code128 from barcode.errors import BarcodeError from barcode.errors import IllegalCharacterError from barcode.errors import NumberOfDigitsError diff --git a/barcode/ean.py b/barcode/ean.py index 07c7ac2..bcde493 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -8,11 +8,9 @@ from barcode.base import Barcode from barcode.charsets import ean as _ean -from barcode.errors import ( - IllegalCharacterError, - NumberOfDigitsError, - WrongCountryCodeError, -) +from barcode.errors import IllegalCharacterError +from barcode.errors import NumberOfDigitsError +from barcode.errors import WrongCountryCodeError # EAN13 Specs (all sizes in mm) SIZES = { diff --git a/barcode/upc.py b/barcode/upc.py index 4ac84be..81c2cac 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -8,7 +8,8 @@ from barcode.base import Barcode from barcode.charsets import upc as _upc -from barcode.errors import IllegalCharacterError, NumberOfDigitsError +from barcode.errors import IllegalCharacterError +from barcode.errors import NumberOfDigitsError class UniversalProductCodeA(Barcode): diff --git a/barcode/writer.py b/barcode/writer.py index 1dc0f00..07afc75 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -11,7 +11,9 @@ import ImageFont except ImportError: try: - from PIL import Image, ImageDraw, ImageFont # lint:ok + from PIL import Image # lint:ok + from PIL import ImageDraw + from PIL import ImageFont except ImportError: import logging diff --git a/tests/test_writers.py b/tests/test_writers.py index 6a876eb..894184d 100644 --- a/tests/test_writers.py +++ b/tests/test_writers.py @@ -5,7 +5,6 @@ from barcode.writer import ImageWriter from barcode.writer import SVGWriter - PATH = os.path.dirname(os.path.abspath(__file__)) TESTPATH = os.path.join(PATH, "test_outputs") From 5a5f94ccf3283222e6bc7a398e9633ace9796e52 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 4 Aug 2021 20:15:21 +0200 Subject: [PATCH 025/192] Update black setup --- setup.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b14c12e..6571d03 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,9 @@ [flake8] exclude = docs/conf.py +extend-ignore = + E203, # Black-incompatible colon spacing. + W503, # Line jump before binary operator. max-line-length = 88 -extend-ignore = E203, W503 [isort] force_single_line=true From 564b13beb82971ae76784e462204db863033e44d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 4 Aug 2021 20:17:28 +0200 Subject: [PATCH 026/192] Set up pyupgrade --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b675a54..70b2d1a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,11 @@ repos: additional_dependencies: - flake8-comprehensions - flake8-bugbear + - repo: https://github.com/asottile/pyupgrade + rev: v2.23.3 + hooks: + - id: pyupgrade + args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v0.910' # Use the sha / tag you want to point at hooks: From 962754e0de08821bd20e980aa79e5f692023b6c6 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 4 Aug 2021 20:17:54 +0200 Subject: [PATCH 027/192] Run pyupgrade --- barcode/__init__.py | 4 +--- barcode/base.py | 2 +- barcode/codex.py | 8 ++++---- barcode/ean.py | 2 +- barcode/isxn.py | 6 +++--- barcode/pybarcode.py | 2 +- barcode/upc.py | 4 ++-- barcode/writer.py | 12 ++++++------ tests/test_manually.py | 2 +- 9 files changed, 20 insertions(+), 22 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 87753af..d490ae7 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -69,9 +69,7 @@ def get(name, code=None, writer=None, options=None): try: barcode = __BARCODE_MAP[name.lower()] except KeyError: - raise BarcodeNotFoundError( - "The barcode {!r} you requested is not known.".format(name) - ) + raise BarcodeNotFoundError(f"The barcode {name!r} you requested is not known.") if code is not None: return barcode(code, writer, **options) else: diff --git a/barcode/base.py b/barcode/base.py index c6fa312..30ae8d4 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -31,7 +31,7 @@ def to_ascii(self): return "\n".join(code) def __repr__(self): - return "<{}({!r})>".format(self.__class__.__name__, self.get_fullcode()) + return f"<{self.__class__.__name__}({self.get_fullcode()!r})>" def build(self): raise NotImplementedError diff --git a/barcode/codex.py b/barcode/codex.py index 3f19008..74665be 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -94,14 +94,14 @@ def __init__(self, pzn, writer=None): raise IllegalCharacterError("PZN can only contain numbers.") if len(pzn) != self.digits: raise NumberOfDigitsError( - "PZN must have {} digits, not {}.".format(self.digits, len(pzn)) + f"PZN must have {self.digits} digits, not {len(pzn)}." ) self.pzn = pzn - self.pzn = "{}{}".format(pzn, self.calculate_checksum()) - super().__init__("PZN-{}".format(self.pzn), writer, add_checksum=False) + self.pzn = f"{pzn}{self.calculate_checksum()}" + super().__init__(f"PZN-{self.pzn}", writer, add_checksum=False) def get_fullcode(self): - return "PZN-{}".format(self.pzn) + return f"PZN-{self.pzn}" def calculate_checksum(self): sum_ = sum(int(x) * int(y) for x, y in enumerate(self.pzn, start=2)) diff --git a/barcode/ean.py b/barcode/ean.py index bcde493..1ffac4d 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -61,7 +61,7 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=False): ean, ean[self.digits] if len(ean) > self.digits else 0 ) else: - self.ean = "{}{}".format(ean, self.calculate_checksum()) + self.ean = f"{ean}{self.calculate_checksum()}" self.guardbar = guardbar if guardbar: diff --git a/barcode/isxn.py b/barcode/isxn.py index 90ca5fb..3588179 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -70,7 +70,7 @@ def __init__(self, isbn, writer=None): isbn = isbn.replace("-", "") isbn = isbn[: self.digits] self.isbn10 = isbn - self.isbn10 = "{}{}".format(isbn, self._calculate_checksum()) + self.isbn10 = f"{isbn}{self._calculate_checksum()}" super().__init__("978" + isbn, writer) def _calculate_checksum(self): @@ -103,7 +103,7 @@ def __init__(self, issn, writer=None): issn = issn.replace("-", "") issn = issn[: self.digits] self.issn = issn - self.issn = "{}{}".format(issn, self._calculate_checksum()) + self.issn = f"{issn}{self._calculate_checksum()}" super().__init__(self.make_ean(), writer) def _calculate_checksum(self): @@ -118,7 +118,7 @@ def _calculate_checksum(self): return tmp def make_ean(self): - return "977{}00{}".format(self.issn[:7], self._calculate_checksum()) + return f"977{self.issn[:7]}00{self._calculate_checksum()}" def __str__(self): return self.issn diff --git a/barcode/pybarcode.py b/barcode/pybarcode.py index c44723c..b21ca5d 100644 --- a/barcode/pybarcode.py +++ b/barcode/pybarcode.py @@ -45,7 +45,7 @@ def create_barcode(args, parser): writer = SVGWriter() out = os.path.normpath(os.path.abspath(args.output)) name = barcode.generate(args.barcode, args.code, writer, out, opts, args.text) - print("New barcode saved as {}.".format(name)) + print(f"New barcode saved as {name}.") def main(): diff --git a/barcode/upc.py b/barcode/upc.py index 81c2cac..bf7e88d 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -38,10 +38,10 @@ def __init__(self, upc, writer=None, make_ean=False): raise IllegalCharacterError("UPC code can only contain numbers.") if len(upc) != self.digits: raise NumberOfDigitsError( - "UPC must have {} digits, not {}.".format(self.digits, len(upc)) + f"UPC must have {self.digits} digits, not {len(upc)}." ) self.upc = upc - self.upc = "{}{}".format(upc, self.calculate_checksum()) + self.upc = f"{upc}{self.calculate_checksum()}" self.writer = writer or self.default_writer() def __str__(self): diff --git a/barcode/writer.py b/barcode/writer.py index 07afc75..3d0b38f 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -50,7 +50,7 @@ def create_svg_object(with_doctype=False): SIZE = "{0:.3f}mm" -COMMENT = "Autogenerated with python-barcode {}".format(version) +COMMENT = f"Autogenerated with python-barcode {version}" PATH = os.path.dirname(os.path.abspath(__file__)) @@ -322,7 +322,7 @@ def _init(self, code): attributes = { "width": "100%", "height": "100%", - "style": "fill:{}".format(self.background), + "style": f"fill:{self.background}", } _set_attributes(background, **attributes) self._group.appendChild(background) @@ -334,7 +334,7 @@ def _create_module(self, xpos, ypos, width, color): "y": SIZE.format(ypos), "width": SIZE.format(width), "height": SIZE.format(self.module_height), - "style": "fill:{};".format(color), + "style": f"fill:{color};", } _set_attributes(element, **attributes) self._group.appendChild(element) @@ -372,12 +372,12 @@ def _finish(self): def save(self, filename, output): if self.compress: - _filename = "{}.svgz".format(filename) + _filename = f"{filename}.svgz" f = gzip.open(_filename, "wb") f.write(output) f.close() else: - _filename = "{}.svg".format(filename) + _filename = f"{filename}.svg" with open(_filename, "wb") as f: f.write(output) return _filename @@ -449,7 +449,7 @@ def _finish(self): return self._image def save(self, filename, output): - filename = "{}.{}".format(filename, self.format.lower()) + filename = f"{filename}.{self.format.lower()}" output.save(filename, self.format.upper()) return filename diff --git a/tests/test_manually.py b/tests/test_manually.py index 689c167..dc43fe6 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -90,4 +90,4 @@ def append_img(x, y): obj = "\n".join(objects) f.write(HTML.format(version=version, body=obj)) - print("\nNow open {htmlfile} in your browser.".format(htmlfile=HTMLFILE)) + print(f"\nNow open {HTMLFILE} in your browser.") From 722d45eb3f3fe01da23155ddb0856ee0916cddf4 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 4 Aug 2021 20:19:51 +0200 Subject: [PATCH 028/192] Remove useless comment --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70b2d1a..0d7aea7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: "3.9.2" # pick a git hash / tag to point to + rev: "3.9.2" hooks: - id: flake8 additional_dependencies: @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.910' # Use the sha / tag you want to point at + rev: 'v0.910' hooks: - id: mypy From 66e9e496dc54c536977cfa39a3cad5468cd1b09f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 18:07:15 +0000 Subject: [PATCH 029/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.23.3 → v2.24.0](https://github.com/asottile/pyupgrade/compare/v2.23.3...v2.24.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d7aea7..992f633 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.23.3 + rev: v2.24.0 hooks: - id: pyupgrade args: [--py36-plus] From 165701cd1910a292ec7453203d81631b4b160a4e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 18:30:49 +0000 Subject: [PATCH 030/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.7b0 → 21.8b0](https://github.com/psf/black/compare/21.7b0...21.8b0) - [github.com/asottile/pyupgrade: v2.24.0 → v2.25.0](https://github.com/asottile/pyupgrade/compare/v2.24.0...v2.25.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 992f633..2950fff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "21.7b0" + rev: "21.8b0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.24.0 + rev: v2.25.0 hooks: - id: pyupgrade args: [--py36-plus] From 7b425bc45fc0f8939d8d7fd64fa99ebb89110198 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:46:02 +0000 Subject: [PATCH 031/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.25.0 → v2.26.0](https://github.com/asottile/pyupgrade/compare/v2.25.0...v2.26.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2950fff..2c05e34 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.25.0 + rev: v2.26.0 hooks: - id: pyupgrade args: [--py36-plus] From 8bc7ed7c8847e18e765d6cfb7d063e9f0511e925 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 18:56:24 +0000 Subject: [PATCH 032/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.8b0 → 21.9b0](https://github.com/psf/black/compare/21.8b0...21.9b0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c05e34..e99acb8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "21.8b0" + rev: "21.9b0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From a158ec31b4e1400246e715575661f4eaf7de93e4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 18:48:39 +0000 Subject: [PATCH 033/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.26.0 → v2.29.0](https://github.com/asottile/pyupgrade/compare/v2.26.0...v2.29.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e99acb8..be5a2fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.26.0 + rev: v2.29.0 hooks: - id: pyupgrade args: [--py36-plus] From 2aa0f7a484005ac60b49787ccd73fe1ef3441b20 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 5 Oct 2021 22:02:39 +0200 Subject: [PATCH 034/192] Add support for Python 3.10 --- .github/workflows/tests.yml | 2 +- README.rst | 2 +- docs/changelog.rst | 1 + setup.py | 1 + tox.ini | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6917728..e36f877 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ ubuntu-20.04, macOS-10.15, windows-2019 ] - python: [ '3.6', '3.7', '3.8', '3.9' ] + python: [ '3.6', '3.7', '3.8', '3.9', '3.10' ] variant: [ "py", "py-images" ] name: python${{ matrix.python }} on ${{ matrix.os }} ${{ matrix.variant }} steps: diff --git a/README.rst b/README.rst index bd04901..ab1650e 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.6 to 3.9. +Support Python 3.6 to 3.10. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/docs/changelog.rst b/docs/changelog.rst index cc098c9..b3a1101 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,6 +13,7 @@ Unreleased ``default_writer()``. * A ``guardbar`` parameter has been added to EAN barcodes. This renders barcodes with guardars (longer bars). +* Added support for Python 3.10. v0.13.1 ~~~~~~~ diff --git a/setup.py b/setup.py index 6231b73..0cfcdb7 100755 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: Multimedia :: Graphics", "Topic :: Software Development :: Libraries :: Python Modules", ], diff --git a/tox.ini b/tox.ini index 09e934c..bf1cf0c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py36,py37,py38,py39}{,-images} +envlist = {py36,py37,py38,py39,py310}{,-images} skip_missing_interpreters = True [testenv] From 8b32d7a7080099c6c84ebe84b1c90c1215f5e26b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 18:49:06 +0000 Subject: [PATCH 035/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 3.9.2 → 4.0.1](https://github.com/PyCQA/flake8/compare/3.9.2...4.0.1) - [github.com/pre-commit/mirrors-mypy: v0.910 → v0.910-1](https://github.com/pre-commit/mirrors-mypy/compare/v0.910...v0.910-1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be5a2fe..39002b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: "3.9.2" + rev: "4.0.1" hooks: - id: flake8 additional_dependencies: @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.910' + rev: 'v0.910-1' hooks: - id: mypy From 6ad348fa931de6205cddd80c5d91ced8359ab0bf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 19:29:48 +0000 Subject: [PATCH 036/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.9b0 → 21.10b0](https://github.com/psf/black/compare/21.9b0...21.10b0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 39002b1..36191ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "21.9b0" + rev: "21.10b0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From 7a9419f0346c4691c0188f2206f571496c5f209c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Nov 2021 19:20:44 +0000 Subject: [PATCH 037/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 5.9.3 → 5.10.0](https://github.com/pycqa/isort/compare/5.9.3...5.10.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 36191ac..eb34abb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/pycqa/isort - rev: 5.9.3 + rev: 5.10.0 hooks: - id: isort - repo: https://github.com/psf/black From 7817defdb97c9d7ff56ffcea88199503973b2c34 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Nov 2021 19:33:02 +0000 Subject: [PATCH 038/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 5.10.0 → 5.10.1](https://github.com/pycqa/isort/compare/5.10.0...5.10.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eb34abb..33a2752 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/pycqa/isort - rev: 5.10.0 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/psf/black From d4d7ac49165ae7908acdf5cb58d60b383a585282 Mon Sep 17 00:00:00 2001 From: Tetsutaro UEHARA Date: Wed, 17 Nov 2021 18:48:30 +0900 Subject: [PATCH 039/192] Added support for Codabar(NW-7) (#115) --- .gitignore | 2 + barcode/__init__.py | 3 ++ barcode/charsets/codabar.py | 25 +++++++++++++ barcode/codabar.py | 73 +++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 barcode/charsets/codabar.py create mode 100644 barcode/codabar.py diff --git a/.gitignore b/.gitignore index 4584e1a..33f7bda 100755 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ MANIFEST barcode/version.py tests/index.html tests/test_outputs/ +tests/test.py +test.py diff --git a/barcode/__init__.py b/barcode/__init__.py index d490ae7..a36b840 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -8,6 +8,7 @@ from typing import Dict from typing import Union +from barcode.codabar import CODABAR from barcode.codex import PZN from barcode.codex import Code39 from barcode.codex import Code128 @@ -47,6 +48,8 @@ "code128": Code128, "itf": ITF, "gs1_128": Gs1_128, + "codabar": CODABAR, + "nw-7": CODABAR, } PROVIDED_BARCODES = list(__BARCODE_MAP) diff --git a/barcode/charsets/codabar.py b/barcode/charsets/codabar.py new file mode 100644 index 0000000..d0ba077 --- /dev/null +++ b/barcode/charsets/codabar.py @@ -0,0 +1,25 @@ +# W = Wide bar +# w = wide space +# N = Narrow bar +# n = narrow space + +CODES = { + "0": "NnNnNwW", + "1": "NnNnWwN", + "2": "NnNwNnW", + "3": "WwNnNnN", + "4": "NnWnNwN", + "5": "WnNnNwN", + "6": "NwNnNnW", + "7": "NwNnWnN", + "8": "NwWnNnN", + "9": "WnNwNnN", + "-": "NnNwWnN", + "$": "NnWwNnN", + ":": "WnNnWnW", + "/": "WnWnNnW", + ".": "WnWnWnN", + "+": "NnWnWnW", +} + +STARTSTOP = {"A": "NnWwNwN", "B": "NwNwNnW", "C": "NnNwNwW", "D": "NnNwWwN"} diff --git a/barcode/codabar.py b/barcode/codabar.py new file mode 100644 index 0000000..a2247ca --- /dev/null +++ b/barcode/codabar.py @@ -0,0 +1,73 @@ +"""Module: barcode.codabar + +:Provided barcodes: Codabar (NW-7) +""" +__docformat__ = "restructuredtext en" + +from barcode.base import Barcode +from barcode.charsets import codabar +from barcode.errors import BarcodeError +from barcode.errors import IllegalCharacterError + + +class CODABAR(Barcode): + """Initializes a new CODABAR instance. + + :parameters: + code : String + Codabar (NW-7) string that matches [ABCD][0-9$:/.+-]+[ABCD] + writer : barcode.writer Instance + The writer to render the barcode (default: SVGWriter). + narrow: Integer + Width of the narrow elements (default: 2) + wide: Integer + Width of the wide elements (default: 5) + wide/narrow must be in the range 2..3 + """ + + name = "Codabar (NW-7)" + + def __init__(self, code, writer=None, narrow=2, wide=5): + self.code = code + self.writer = writer or self.default_writer() + self.narrow = narrow + self.wide = wide + + def __str__(self): + return self.code + + def get_fullcode(self): + return self.code + + def build(self): + try: + data = ( + codabar.STARTSTOP[self.code[0]] + "n" + ) # Start with [A-D], followed by a narrow space + + except KeyError: + raise BarcodeError("Codabar should start with either A,B,C or D") + + try: + data += "n".join( + [codabar.CODES[c] for c in self.code[1:-1]] + ) # separated by a narrow space + except KeyError: + raise IllegalCharacterError("Codabar can only contain numerics or $:/.+-") + + try: + data += "n" + codabar.STARTSTOP[self.code[-1]] # End with [A-D] + except KeyError: + raise BarcodeError("Codabar should end with either A,B,C or D") + + raw = "" + for e in data: + if e == "W": + raw += "1" * self.wide + if e == "w": + raw += "0" * self.wide + if e == "N": + raw += "1" * self.narrow + if e == "n": + raw += "0" * self.narrow + return [raw] From 276ef55186006443613703b1d95ab727925db865 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Nov 2021 19:46:04 +0000 Subject: [PATCH 040/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.10b0 → 21.11b1](https://github.com/psf/black/compare/21.10b0...21.11b1) - [github.com/asottile/pyupgrade: v2.29.0 → v2.29.1](https://github.com/asottile/pyupgrade/compare/v2.29.0...v2.29.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 33a2752..3528ced 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "21.10b0" + rev: "21.11b1" hooks: - id: black - repo: https://github.com/PyCQA/flake8 @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v2.29.1 hooks: - id: pyupgrade args: [--py36-plus] From bb827489e46272612f7eaa942908858874688859 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Dec 2021 19:57:49 +0000 Subject: [PATCH 041/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.11b1 → 21.12b0](https://github.com/psf/black/compare/21.11b1...21.12b0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3528ced..1fe3dc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "21.11b1" + rev: "21.12b0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From d70b80e6ea688841e26b11efcc2035a4fd4d235e Mon Sep 17 00:00:00 2001 From: Timur Sattarov Date: Fri, 10 Dec 2021 22:48:42 +0400 Subject: [PATCH 042/192] [#135] Optimized SVG output by removing redundant geometry --- barcode/writer.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 3d0b38f..4eabc17 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -328,16 +328,18 @@ def _init(self, code): self._group.appendChild(background) def _create_module(self, xpos, ypos, width, color): - element = self._document.createElement("rect") - attributes = { - "x": SIZE.format(xpos), - "y": SIZE.format(ypos), - "width": SIZE.format(width), - "height": SIZE.format(self.module_height), - "style": f"fill:{color};", - } - _set_attributes(element, **attributes) - self._group.appendChild(element) + # Background rect has been provided already, so skipping "spaces" + if color != self.background: + element = self._document.createElement("rect") + attributes = { + "x": SIZE.format(xpos), + "y": SIZE.format(ypos), + "width": SIZE.format(width), + "height": SIZE.format(self.module_height), + "style": f"fill:{color};", + } + _set_attributes(element, **attributes) + self._group.appendChild(element) def _create_text(self, xpos, ypos): # check option to override self.text with self.human (barcode as From 5ddcbdf02a2a9a1388eb63e9b8292bd4e337722e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Dec 2021 19:36:23 +0000 Subject: [PATCH 043/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.910-1 → v0.920](https://github.com/pre-commit/mirrors-mypy/compare/v0.910-1...v0.920) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1fe3dc6..dd79a53 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.910-1' + rev: 'v0.920' hooks: - id: mypy From 6d0216b033019b856d29bfa1a227a0550e6a2af2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Dec 2021 20:00:34 +0000 Subject: [PATCH 044/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.1.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.1.0) - [github.com/pre-commit/mirrors-mypy: v0.920 → v0.930](https://github.com/pre-commit/mirrors-mypy/compare/v0.920...v0.930) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dd79a53..5d5635f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.920' + rev: 'v0.930' hooks: - id: mypy From d6916e5d3fc72654c8c9c677ca5c2cfaaf1e37ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 19:56:03 +0000 Subject: [PATCH 045/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.29.1 → v2.31.0](https://github.com/asottile/pyupgrade/compare/v2.29.1...v2.31.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d5635f..22a3374 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.31.0 hooks: - id: pyupgrade args: [--py36-plus] From 529fbcb185041d2398b921a3d8d798d3afb61b4d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jan 2022 20:15:30 +0000 Subject: [PATCH 046/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.930 → v0.931](https://github.com/pre-commit/mirrors-mypy/compare/v0.930...v0.931) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 22a3374..d4456d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.930' + rev: 'v0.931' hooks: - id: mypy From 6a9b05863d2c0fe4a2ed88a0ce092a5181cd4388 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 20:55:12 +0000 Subject: [PATCH 047/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.12b0 → 22.1.0](https://github.com/psf/black/compare/21.12b0...22.1.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d4456d0..884c0a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "21.12b0" + rev: "22.1.0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From db169d356b277cb512872457e2f3233c880c6c55 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 22:12:07 +0000 Subject: [PATCH 048/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.31.0 → v2.31.1](https://github.com/asottile/pyupgrade/compare/v2.31.0...v2.31.1) - [github.com/pre-commit/mirrors-mypy: v0.931 → v0.940](https://github.com/pre-commit/mirrors-mypy/compare/v0.931...v0.940) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 884c0a6..e20eb33 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,11 +22,11 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.31.0 + rev: v2.31.1 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.931' + rev: 'v0.940' hooks: - id: mypy From 275c2f775a0f7be31d6057007198399698a82022 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Mar 2022 22:25:28 +0000 Subject: [PATCH 049/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.940 → v0.941](https://github.com/pre-commit/mirrors-mypy/compare/v0.940...v0.941) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e20eb33..4d3df79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.940' + rev: 'v0.941' hooks: - id: mypy From 1163dca844ab4153afe96d155e621697a59cf095 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Mar 2022 19:10:15 +0000 Subject: [PATCH 050/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.941 → v0.942](https://github.com/pre-commit/mirrors-mypy/compare/v0.941...v0.942) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4d3df79..d69eaa5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.941' + rev: 'v0.942' hooks: - id: mypy From 14598a7da78cae6e84ceb9219aab08cbbb796914 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 19:54:06 +0000 Subject: [PATCH 051/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.1.0 → 22.3.0](https://github.com/psf/black/compare/22.1.0...22.3.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d69eaa5..5195783 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "22.1.0" + rev: "22.3.0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From 08b8f765bdddbd61e08cc5997a2864c17ba8254f Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Sun, 10 Apr 2022 22:45:45 +0200 Subject: [PATCH 052/192] Avoid shadowing variable inside for loop --- barcode/writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 4eabc17..042d847 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -283,8 +283,8 @@ def render(self, code): ypos += pt2mm(self.font_size) blocks = self.text - for (text, xpos) in zip(blocks, text["xpos"]): - self.text = text + for (text_, xpos) in zip(blocks, text["xpos"]): + self.text = text_ self._callbacks["paint_text"](xpos, ypos) return self._callbacks["finish"]() From b1e465f7e7836992208da16d0417ff43728f8188 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 20:48:57 +0000 Subject: [PATCH 053/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.1.0 → v4.2.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.1.0...v4.2.0) - [github.com/asottile/pyupgrade: v2.31.1 → v2.32.0](https://github.com/asottile/pyupgrade/compare/v2.31.1...v2.32.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5195783..5ced716 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.2.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.31.1 + rev: v2.32.0 hooks: - id: pyupgrade args: [--py36-plus] From f97a50b06d074c0db32124ab7ea351ced13c7cf9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 20:13:19 +0000 Subject: [PATCH 054/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.942 → v0.950](https://github.com/pre-commit/mirrors-mypy/compare/v0.942...v0.950) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5ced716..39995b3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.942' + rev: 'v0.950' hooks: - id: mypy From cd7fada853826772db2b3985b06beb3883ecc20f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 20:22:38 +0000 Subject: [PATCH 055/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.32.0 → v2.32.1](https://github.com/asottile/pyupgrade/compare/v2.32.0...v2.32.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 39995b3..5fbe2b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.32.0 + rev: v2.32.1 hooks: - id: pyupgrade args: [--py36-plus] From 4305af97afe7758bb8a7aa4014953398d039f1b0 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 08:47:18 +0200 Subject: [PATCH 056/192] Don't install tests module Fixes #153 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0cfcdb7..11646a9 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="python-barcode", - packages=find_packages(), + packages=find_packages(exclude=["tests"]), url="https://github.com/WhyNotHugo/python-barcode", license="MIT", author="Hugo Osvaldo Barrera et al", From c7c7923c1ad5f8271d3fa07beb4a17bf1e96f4c3 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 09:00:23 +0200 Subject: [PATCH 057/192] Update changelog for v0.14.0 --- docs/changelog.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index b3a1101..e59a4a7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,11 +1,11 @@ Changelog --------- -Unreleased +v0.14.0 ~~~~~~~~~~ -* The default dimensions have changed slightly. This is so that the results of - generating a PNG and an SVG look more alike. +* **Breaking**: The default dimensions have changed slightly. This is so that + the results of generating a PNG and an SVG look more alike. * Previous versions included an empty text element for SVGs with no comment. This is no longer the case. * Some internals have been improved so as to allow better subclassing. @@ -14,6 +14,10 @@ Unreleased * A ``guardbar`` parameter has been added to EAN barcodes. This renders barcodes with guardars (longer bars). * Added support for Python 3.10. +* The documentation setup has been redone, hopefully squashing a lot of legacy + quirks. +* Previous versions installed the `tests` module. This was not intentional and + have been fixed. v0.13.1 ~~~~~~~ From f8d31f6f363d419b207405ef2eee02135a553af9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 20:27:54 +0000 Subject: [PATCH 058/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.950 → v0.960](https://github.com/pre-commit/mirrors-mypy/compare/v0.950...v0.960) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5fbe2b8..8c35d33 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.950' + rev: 'v0.960' hooks: - id: mypy From b6cb1bcc5d70a7beb1651393d5fde72aac1a193d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 21:49:31 +0000 Subject: [PATCH 059/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0) - [github.com/asottile/pyupgrade: v2.32.1 → v2.34.0](https://github.com/asottile/pyupgrade/compare/v2.32.1...v2.34.0) - [github.com/pre-commit/mirrors-mypy: v0.960 → v0.961](https://github.com/pre-commit/mirrors-mypy/compare/v0.960...v0.961) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8c35d33..647f438 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.3.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] @@ -22,11 +22,11 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.32.1 + rev: v2.34.0 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.960' + rev: 'v0.961' hooks: - id: mypy From b0024155a118cab78319a3cc2597fa70f4733b7f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 21:51:20 +0000 Subject: [PATCH 060/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 647f438..cf2c463 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "22.3.0" + rev: "22.6.0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From 72ad412c3bf8941f1d032560612902723d0441e4 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 09:13:45 +0200 Subject: [PATCH 061/192] Configure setuptools_scm via pyproject.toml Configuring it via `setup.py` is deprecated. Use `python -m setuptools_scm` to get the version of the current checkout. --- pyproject.toml | 6 ++++++ setup.py | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5723bac --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] + +[tool.setuptools_scm] +write_to = "barcode/version.py" +version_scheme = "post-release" diff --git a/setup.py b/setup.py index 11646a9..c5d8259 100755 --- a/setup.py +++ b/setup.py @@ -32,10 +32,6 @@ "Topic :: Software Development :: Libraries :: Python Modules", ], entry_points={"console_scripts": ["python-barcode = barcode.pybarcode:main"]}, - use_scm_version={ - "version_scheme": "post-release", - "write_to": "barcode/version.py", - }, setup_requires=["setuptools_scm"], extras_require={"images": ["pillow"]}, include_package_data=True, From 4d591122b2aff833a60048cdd4f85ebb2fbe6d1c Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 09:14:12 +0200 Subject: [PATCH 062/192] Stop publishing packages to GitHub These have no added value; the ones from PyPI should be used instead. --- .github/workflows/publish.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c7077f1..4412183 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,25 +6,6 @@ on: - v* jobs: - github-release: - runs-on: ubuntu-18.04 - name: Publish GitHub Release - steps: - - uses: actions/checkout@master - - uses: actions/setup-python@v1 - with: - python-version: 3.7 - architecture: x64 - - name: Install build dependencies - run: pip install wheel - - name: Build packages - run: python setup.py sdist bdist_wheel - - name: Release - uses: softprops/action-gh-release@v1 - with: - files: dist/* - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} pypi: runs-on: ubuntu-18.04 name: Publish package on PyPI From 141ec0d3512a0e66edcd7a6243d670ec4b7869f7 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 09:14:45 +0200 Subject: [PATCH 063/192] Update publishing workflow --- .github/workflows/publish.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4412183..835cb85 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,19 +7,15 @@ on: jobs: pypi: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 name: Publish package on PyPI steps: - - uses: actions/checkout@master - - uses: actions/setup-python@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: - python-version: 3.7 - architecture: x64 - - name: Install build dependencies - run: pip install wheel - - name: Build packages - run: python setup.py sdist bdist_wheel - - name: Publish a Python distribution to PyPI - uses: pypa/gh-action-pypi-publish@master + python-version: 3.10 + - run: pip install build setuptools wheel + - run: python -m build --sdist --wheel --no-isolation + - uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_TOKEN }} From 9907987bbeceba0e6e1b3ca542e552f544916b48 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 09:21:54 +0200 Subject: [PATCH 064/192] Drop support for Python 3.6 --- .github/workflows/tests.yml | 2 +- README.rst | 2 +- docs/changelog.rst | 7 ++++++- setup.py | 1 - tox.ini | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e36f877..baa04d6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ ubuntu-20.04, macOS-10.15, windows-2019 ] - python: [ '3.6', '3.7', '3.8', '3.9', '3.10' ] + python: [ '3.7', '3.8', '3.9', '3.10' ] variant: [ "py", "py-images" ] name: python${{ matrix.python }} on ${{ matrix.os }} ${{ matrix.variant }} steps: diff --git a/README.rst b/README.rst index ab1650e..d2102ed 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.6 to 3.10. +Support Python 3.7 to 3.10. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/docs/changelog.rst b/docs/changelog.rst index e59a4a7..a0a9841 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,9 +1,14 @@ Changelog --------- -v0.14.0 +unreleased ~~~~~~~~~~ +* **Breaking** Dropped support for Python 3.6. + +v0.14.0 +~~~~~~~ + * **Breaking**: The default dimensions have changed slightly. This is so that the results of generating a PNG and an SVG look more alike. * Previous versions included an empty text element for SVGs with no comment. diff --git a/setup.py b/setup.py index c5d8259..03774d8 100755 --- a/setup.py +++ b/setup.py @@ -23,7 +23,6 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/tox.ini b/tox.ini index bf1cf0c..de84c1d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py36,py37,py38,py39,py310}{,-images} +envlist = {py37,py38,py39,py310}{,-images} skip_missing_interpreters = True [testenv] From 5d856a9300a36150ca4b016755bb436b65fca4e2 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 17 May 2022 09:24:22 +0200 Subject: [PATCH 065/192] Set black and pyupgrade to target Python 3.7+ --- .pre-commit-config.yaml | 2 +- pyproject.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cf2c463..7fcc5f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: rev: v2.34.0 hooks: - id: pyupgrade - args: [--py36-plus] + args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v0.961' hooks: diff --git a/pyproject.toml b/pyproject.toml index 5723bac..f4be560 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,6 @@ requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] [tool.setuptools_scm] write_to = "barcode/version.py" version_scheme = "post-release" + +[tool.black] +target-version = ['py37'] From 880c890248d63606d439bdd4a09dfb52e25848d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 21:35:50 +0000 Subject: [PATCH 066/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.34.0 → v2.37.2](https://github.com/asottile/pyupgrade/compare/v2.34.0...v2.37.2) - [github.com/pre-commit/mirrors-mypy: v0.961 → v0.971](https://github.com/pre-commit/mirrors-mypy/compare/v0.961...v0.971) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fcc5f9..65750aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,11 +22,11 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.34.0 + rev: v2.37.2 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.961' + rev: 'v0.971' hooks: - id: mypy From 77c902f8b1998dbfb8c8f74ac644d5929e7846a1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:30:19 +0000 Subject: [PATCH 067/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 4.0.1 → 5.0.2](https://github.com/PyCQA/flake8/compare/4.0.1...5.0.2) - [github.com/asottile/pyupgrade: v2.37.2 → v2.37.3](https://github.com/asottile/pyupgrade/compare/v2.37.2...v2.37.3) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 65750aa..f6f869a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,14 +15,14 @@ repos: hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: "4.0.1" + rev: "5.0.2" hooks: - id: flake8 additional_dependencies: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.37.2 + rev: v2.37.3 hooks: - id: pyupgrade args: [--py37-plus] From 53b52351d4c50a873e0dd5c1c33e7bae16c1b128 Mon Sep 17 00:00:00 2001 From: Bruno Voisin Date: Thu, 4 Aug 2022 10:09:33 +0200 Subject: [PATCH 068/192] Fix deprecation warning about ImageFont.getsize python-barcode/barcode/writer.py:441: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead. width, height = font.getsize(subtext) `ImageFont.getsize` is deprecated since the version 9.2 of Pillow. We use anchor instead of computing the right position to have an aligned text. It is related to the issue #163. The solution was suggested by nulano (contributor of Pillow). --- barcode/writer.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 042d847..68c974d 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -438,13 +438,13 @@ def _paint_text(self, xpos, ypos): font_size = int(mm2px(pt2mm(self.font_size), self.dpi)) font = ImageFont.truetype(self.font_path, font_size) for subtext in self.text.split("\n"): - width, height = font.getsize(subtext) - # determine the maximum width of each line pos = ( - mm2px(xpos, self.dpi) - width // 2, - mm2px(ypos, self.dpi) - height, + mm2px(xpos, self.dpi), + mm2px(ypos, self.dpi), + ) + self._draw.text( + pos, subtext, font=font, fill=self.foreground, anchor="ms" ) - self._draw.text(pos, subtext, font=font, fill=self.foreground) ypos += pt2mm(self.font_size) / 2 + self.text_line_distance def _finish(self): From e1de63792aefc1cd3471dc73ca3049e4c32b1aaa Mon Sep 17 00:00:00 2001 From: Bruno Voisin Date: Thu, 4 Aug 2022 10:31:52 +0200 Subject: [PATCH 069/192] Fix text position With the anchor ms (middle baseline) the text is not exactly at the same position than before. I changed it to md (middle descender) to have the same position. See https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html#text-anchors --- barcode/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index 68c974d..f304703 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -443,7 +443,7 @@ def _paint_text(self, xpos, ypos): mm2px(ypos, self.dpi), ) self._draw.text( - pos, subtext, font=font, fill=self.foreground, anchor="ms" + pos, subtext, font=font, fill=self.foreground, anchor="md" ) ypos += pt2mm(self.font_size) / 2 + self.text_line_distance From df55ffb538b174696bbaa94056bf7219017e28bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Aug 2022 22:16:03 +0000 Subject: [PATCH 070/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 5.0.2 → 5.0.4](https://github.com/PyCQA/flake8/compare/5.0.2...5.0.4) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f6f869a..1a14b4a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: "5.0.2" + rev: "5.0.4" hooks: - id: flake8 additional_dependencies: From aee38744ae49da8289dc208dba9ad2a8c9648422 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 22:43:10 +0000 Subject: [PATCH 071/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.6.0 → 22.8.0](https://github.com/psf/black/compare/22.6.0...22.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1a14b4a..a632826 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "22.6.0" + rev: "22.8.0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 From 7dd1609357612d92bc76b41d47b1a9839f089ad0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 22:38:09 +0000 Subject: [PATCH 072/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.37.3 → v2.38.0](https://github.com/asottile/pyupgrade/compare/v2.37.3...v2.38.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a632826..3c8fb11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.37.3 + rev: v2.38.0 hooks: - id: pyupgrade args: [--py37-plus] From 0bd796a1707a9fa8c624a9774e8d61042e028a44 Mon Sep 17 00:00:00 2001 From: canklot <33100414+canklot@users.noreply.github.com> Date: Wed, 21 Sep 2022 11:30:55 +0300 Subject: [PATCH 073/192] Fix issue #171 calculating checksum before any controls Moved `super().__init__` before calling `_calculate_checksum()` to check if the string contains any letters to prevent value errors that can happen when calculating checksum. I tested the new version with the input `0132354187` and the produced barcode before and after the commit is the same as `9780132352`. --- barcode/isxn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/barcode/isxn.py b/barcode/isxn.py index 3588179..3afa8ed 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -69,9 +69,10 @@ class InternationalStandardBookNumber10(InternationalStandardBookNumber13): def __init__(self, isbn, writer=None): isbn = isbn.replace("-", "") isbn = isbn[: self.digits] + super().__init__("978" + isbn, writer) self.isbn10 = isbn self.isbn10 = f"{isbn}{self._calculate_checksum()}" - super().__init__("978" + isbn, writer) + def _calculate_checksum(self): tmp = sum(x * int(y) for x, y in enumerate(self.isbn10[:9], start=1)) % 11 From 36a11920cf32630457d926d85166dd0ce5822e42 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 08:34:12 +0000 Subject: [PATCH 074/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/isxn.py | 1 - 1 file changed, 1 deletion(-) diff --git a/barcode/isxn.py b/barcode/isxn.py index 3afa8ed..6af9e90 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -72,7 +72,6 @@ def __init__(self, isbn, writer=None): super().__init__("978" + isbn, writer) self.isbn10 = isbn self.isbn10 = f"{isbn}{self._calculate_checksum()}" - def _calculate_checksum(self): tmp = sum(x * int(y) for x, y in enumerate(self.isbn10[:9], start=1)) % 11 From e556c9453c4824389a297578a06b54a4c316a624 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Sep 2022 22:48:24 +0000 Subject: [PATCH 075/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.38.0 → v2.38.2](https://github.com/asottile/pyupgrade/compare/v2.38.0...v2.38.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3c8fb11..2f5cf6f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.38.0 + rev: v2.38.2 hooks: - id: pyupgrade args: [--py37-plus] From 2c4a014fffe292d18f583e5f47ddfb5d73f9ba2d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 22:42:15 +0000 Subject: [PATCH 076/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.971 → v0.981](https://github.com/pre-commit/mirrors-mypy/compare/v0.971...v0.981) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f5cf6f..46afe0a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.971' + rev: 'v0.981' hooks: - id: mypy From 04dbf726ba561a30b0ad44b1e36defe99a5b5ede Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 23:39:11 +0000 Subject: [PATCH 077/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.8.0 → 22.10.0](https://github.com/psf/black/compare/22.8.0...22.10.0) - [github.com/asottile/pyupgrade: v2.38.2 → v3.1.0](https://github.com/asottile/pyupgrade/compare/v2.38.2...v3.1.0) - [github.com/pre-commit/mirrors-mypy: v0.981 → v0.982](https://github.com/pre-commit/mirrors-mypy/compare/v0.981...v0.982) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 46afe0a..c503b5d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: "22.8.0" + rev: "22.10.0" hooks: - id: black - repo: https://github.com/PyCQA/flake8 @@ -22,11 +22,11 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v2.38.2 + rev: v3.1.0 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.981' + rev: 'v0.982' hooks: - id: mypy From 7320d1cfcc011b12c0950d1f87a5cec1e5cbdc91 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 23:17:24 +0000 Subject: [PATCH 078/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.1.0 → v3.2.0](https://github.com/asottile/pyupgrade/compare/v3.1.0...v3.2.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c503b5d..cb574cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.2.0 hooks: - id: pyupgrade args: [--py37-plus] From a9863c1e6ebbd9ca337cb7665e5d4dbc1005d163 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 23:25:07 +0000 Subject: [PATCH 079/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.2.0 → v3.2.2](https://github.com/asottile/pyupgrade/compare/v3.2.0...v3.2.2) - [github.com/pre-commit/mirrors-mypy: v0.982 → v0.990](https://github.com/pre-commit/mirrors-mypy/compare/v0.982...v0.990) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cb574cf..b5dc343 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,11 +22,11 @@ repos: - flake8-comprehensions - flake8-bugbear - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.2.2 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.982' + rev: 'v0.990' hooks: - id: mypy From 2937786ac879a4021e72339f2c3ac83841f2519d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 15 Nov 2022 12:38:00 +0100 Subject: [PATCH 080/192] Mark optional fields explicitly --- barcode/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index a36b840..d0d15cd 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -87,9 +87,9 @@ def generate( name: str, code: str, writer=None, - output: Union[str, os.PathLike, BinaryIO] = None, - writer_options: Dict = None, - text: str = None, + output: Union[str, os.PathLike, BinaryIO, None] = None, + writer_options: Union[Dict, None] = None, + text: Union[str, None] = None, ): """Shortcut to generate a barcode in one line. From 048923921d54e60717830339fd1f79413a0dc119 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Nov 2022 23:01:23 +0000 Subject: [PATCH 081/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.990 → v0.991](https://github.com/pre-commit/mirrors-mypy/compare/v0.990...v0.991) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5dc343..2cd605d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,6 @@ repos: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.990' + rev: 'v0.991' hooks: - id: mypy From aa50315929c526cf2aff3b586d919150a0cd1d82 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 00:57:40 +0000 Subject: [PATCH 082/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0) - [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2cd605d..0a34424 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] @@ -15,7 +15,7 @@ repos: hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: "5.0.4" + rev: "6.0.0" hooks: - id: flake8 additional_dependencies: From aa83b6e28771862f1e47e32b245702e54987a4fd Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 29 Nov 2022 13:00:27 +0100 Subject: [PATCH 083/192] Update syntax of flake8 config --- setup.cfg | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 6571d03..0b66591 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,8 +1,10 @@ [flake8] exclude = docs/conf.py extend-ignore = - E203, # Black-incompatible colon spacing. - W503, # Line jump before binary operator. + # Black-incompatible colon spacing. + E203, + # Line jump before binary operator. + W503, max-line-length = 88 [isort] From 4849f13789a4265be70cc7cc35e17647a00106c9 Mon Sep 17 00:00:00 2001 From: Florian Ostermaier Date: Thu, 24 Nov 2022 21:45:49 +0100 Subject: [PATCH 084/192] fix wrong bar width in ImageWriter --- barcode/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index f304703..d89a70a 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -428,7 +428,7 @@ def _paint_module(self, xpos, ypos, width, color): size = [ (mm2px(xpos, self.dpi), mm2px(ypos, self.dpi)), ( - mm2px(xpos + width, self.dpi), + mm2px(xpos + width, self.dpi) - 1, mm2px(ypos + self.module_height, self.dpi), ), ] From 869b5c2060dcde6da91f13c30dee3ed7cf51837a Mon Sep 17 00:00:00 2001 From: Florian Ostermaier Date: Thu, 24 Nov 2022 23:11:46 +0100 Subject: [PATCH 085/192] replace hard-coded margin with parameters pass 'quiet_zone': 0, 'margin_top': 0, 'margin_bottom': 0, for borderless codes --- barcode/writer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index d89a70a..6a748e9 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -101,6 +101,8 @@ def __init__( self.text_line_distance = 1 self.center_text = True self.guard_height_factor = 1.1 + self.margin_top = 1 + self.margin_bottom = 1 def calculate_size(self, modules_per_line, number_of_lines): """Calculates the size of the barcode in pixel. @@ -115,7 +117,7 @@ def calculate_size(self, modules_per_line, number_of_lines): :rtype: Tuple """ width = 2 * self.quiet_zone + modules_per_line * self.module_width - height = 2.0 + self.module_height * number_of_lines + height = self.margin_bottom + self.margin_top + self.module_height * number_of_lines number_of_text_lines = len(self.text.splitlines()) if self.font_size and self.text: height += ( @@ -204,7 +206,7 @@ def render(self, code): """ if self._callbacks["initialize"] is not None: self._callbacks["initialize"](code) - ypos = 1.0 + ypos = self.margin_top base_height = self.module_height for cc, line in enumerate(code): # Left quiet zone is x startposition From 3643a6511002501acbc7da497939cf8810afdb7c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 22:14:04 +0000 Subject: [PATCH 086/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/writer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index 6a748e9..3a3dcd0 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -117,7 +117,9 @@ def calculate_size(self, modules_per_line, number_of_lines): :rtype: Tuple """ width = 2 * self.quiet_zone + modules_per_line * self.module_width - height = self.margin_bottom + self.margin_top + self.module_height * number_of_lines + height = ( + self.margin_bottom + self.margin_top + self.module_height * number_of_lines + ) number_of_text_lines = len(self.text.splitlines()) if self.font_size and self.text: height += ( From f3a70467193df8cccb54539b19ccf6dcce611f49 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 14 Nov 2022 20:35:34 +0100 Subject: [PATCH 087/192] Add support for Python 3.11 --- .github/workflows/publish.yml | 2 +- .github/workflows/tests.yml | 12 ++++++++++-- README.rst | 2 +- docs/changelog.rst | 1 + setup.py | 1 + tox.ini | 2 +- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 835cb85..490a495 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: - python-version: 3.10 + python-version: 3.7 - run: pip install build setuptools wheel - run: python -m build --sdist --wheel --no-isolation - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index baa04d6..4420802 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,9 +7,17 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04, macOS-10.15, windows-2019 ] - python: [ '3.7', '3.8', '3.9', '3.10' ] + os: [ ubuntu-20.04 ] + python: [ '3.7', '3.8', '3.9', '3.10', '3.11' ] variant: [ "py", "py-images" ] + include: + - os: macOS-10.15 + python: "3.10" + variant: py-images + - os: windows-2019 + python: "3.10" + variant: py-images + - os: windows-2019 name: python${{ matrix.python }} on ${{ matrix.os }} ${{ matrix.variant }} steps: - uses: actions/checkout@v2 diff --git a/README.rst b/README.rst index d2102ed..cbc846d 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.7 to 3.10. +Support Python 3.7 to 3.11. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/docs/changelog.rst b/docs/changelog.rst index a0a9841..d57ef79 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ unreleased ~~~~~~~~~~ * **Breaking** Dropped support for Python 3.6. +* Added support for Python 3.11. v0.14.0 ~~~~~~~ diff --git a/setup.py b/setup.py index 03774d8..57a8bfa 100755 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Multimedia :: Graphics", "Topic :: Software Development :: Libraries :: Python Modules", ], diff --git a/tox.ini b/tox.ini index de84c1d..1cd7c44 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py37,py38,py39,py310}{,-images} +envlist = {py37,py38,py39,py310,py311}{,-images} skip_missing_interpreters = True [testenv] From 3974cb70ee3719a9489c2e17204e45966463ddae Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 31 Jan 2023 10:13:12 +0100 Subject: [PATCH 088/192] Use ruff for checking / linting --- .pre-commit-config.yaml | 20 ++++---------------- pyproject.toml | 19 +++++++++++++++++++ setup.cfg | 12 ------------ tox.ini | 5 ----- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0a34424..154e196 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,27 +6,15 @@ repos: args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - - repo: https://github.com/pycqa/isort - rev: 5.10.1 - hooks: - - id: isort - repo: https://github.com/psf/black rev: "22.10.0" hooks: - id: black - - repo: https://github.com/PyCQA/flake8 - rev: "6.0.0" - hooks: - - id: flake8 - additional_dependencies: - - flake8-comprehensions - - flake8-bugbear - - repo: https://github.com/asottile/pyupgrade - rev: v3.2.2 - hooks: - - id: pyupgrade - args: [--py37-plus] - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v0.991' hooks: - id: mypy + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: 'v0.0.238' + hooks: + - id: ruff diff --git a/pyproject.toml b/pyproject.toml index f4be560..cb899ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,22 @@ version_scheme = "post-release" [tool.black] target-version = ['py37'] + +[tool.ruff] +select = [ + "E", + "F", + "W", + "B0", + "I", + "UP", + # "N", + "C4", + "PT", + # "SIM", + "TID", +] +target-version = "py37" + +[tool.ruff.isort] +force-single-line = true diff --git a/setup.cfg b/setup.cfg index 0b66591..ed5ed80 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,15 +1,3 @@ -[flake8] -exclude = docs/conf.py -extend-ignore = - # Black-incompatible colon spacing. - E203, - # Line jump before binary operator. - W503, -max-line-length = 88 - -[isort] -force_single_line=true - [tool:pytest] addopts = -vv diff --git a/tox.ini b/tox.ini index 1cd7c44..7b5eed7 100644 --- a/tox.ini +++ b/tox.ini @@ -9,8 +9,3 @@ deps = images: Pillow commands = pytest --cov barcode usedevelop = True - -[flake8] -exclude=.tox,build,.eggs -application-import-names=barcode,tests -import-order-style=smarkets From d67451271c059b7938cd3db90e47e5a2c44e5000 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 31 Jan 2023 10:18:56 +0100 Subject: [PATCH 089/192] ruff: Enable and autofix some extra rules --- barcode/base.py | 5 +---- barcode/codex.py | 10 ++++------ barcode/isxn.py | 5 ++--- barcode/writer.py | 5 +---- pyproject.toml | 2 +- tests/test_checksums.py | 18 +++++++++--------- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/barcode/base.py b/barcode/base.py index 30ae8d4..1f90dc4 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -59,10 +59,7 @@ def save(self, filename, options=None, text=None): :returns: The full filename with extension. :rtype: String """ - if text: - output = self.render(options, text) - else: - output = self.render(options) + output = self.render(options, text) if text else self.render(options) _filename = self.writer.save(filename, output) return _filename diff --git a/barcode/codex.py b/barcode/codex.py index 74665be..18eff42 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -183,15 +183,13 @@ def look_next(): elif self._charset == "B": if look_next(): codes = self._new_charset("C") - elif char not in code128.B: - if char in code128.A: - codes = self._new_charset("A") + elif char not in code128.B and char in code128.A: + codes = self._new_charset("A") elif self._charset == "A": if look_next(): codes = self._new_charset("C") - elif char not in code128.A: - if char in code128.B: - codes = self._new_charset("B") + elif char not in code128.A and char in code128.B: + codes = self._new_charset("B") return codes def _convert(self, char): diff --git a/barcode/isxn.py b/barcode/isxn.py index 6af9e90..c886922 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -45,9 +45,8 @@ def __init__(self, isbn, writer=None): self.isbn13 = isbn if isbn[:3] not in ("978", "979"): raise WrongCountryCodeError("ISBN must start with 978 or 979.") - if isbn[:3] == "979": - if isbn[3:5] not in ("10", "11"): - raise BarcodeError("ISBN must start with 97910 or 97911.") + if isbn[:3] == "979" and isbn[3:5] not in ("10", "11"): + raise BarcodeError("ISBN must start with 97910 or 97911.") super().__init__(isbn, writer) diff --git a/barcode/writer.py b/barcode/writer.py index 3a3dcd0..1ccff16 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -348,10 +348,7 @@ def _create_module(self, xpos, ypos, width, color): def _create_text(self, xpos, ypos): # check option to override self.text with self.human (barcode as # human readable data, can be used to print own formats) - if self.human != "": - barcodetext = self.human - else: - barcodetext = self.text + barcodetext = self.human if self.human != "" else self.text for subtext in barcodetext.split("\n"): element = self._document.createElement("text") attributes = { diff --git a/pyproject.toml b/pyproject.toml index cb899ff..ca2bcd9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ select = [ # "N", "C4", "PT", - # "SIM", + "SIM", "TID", ] target-version = "py37" diff --git a/tests/test_checksums.py b/tests/test_checksums.py index f2d4451..513ac1f 100755 --- a/tests/test_checksums.py +++ b/tests/test_checksums.py @@ -3,44 +3,44 @@ def test_code39_checksum(): code39 = get_barcode("code39", "Code39") - assert "CODE39W" == code39.get_fullcode() + assert code39.get_fullcode() == "CODE39W" def test_pzn_checksum(): pzn = get_barcode("pzn", "103940") - assert "PZN-1039406" == pzn.get_fullcode() + assert pzn.get_fullcode() == "PZN-1039406" def test_ean13_checksum(): ean = get_barcode("ean13", "400614457735") - assert "4006144577350" == ean.get_fullcode() + assert ean.get_fullcode() == "4006144577350" def test_ean8_checksum(): ean = get_barcode("ean8", "6032299") - assert "60322999" == ean.get_fullcode() + assert ean.get_fullcode() == "60322999" def test_jan_checksum(): jan = get_barcode("jan", "491400614457") - assert "4914006144575" == jan.get_fullcode() + assert jan.get_fullcode() == "4914006144575" def test_ean14_checksum(): ean = get_barcode("ean14", "1234567891258") - assert "12345678912589" == ean.get_fullcode() + assert ean.get_fullcode() == "12345678912589" def test_isbn10_checksum(): isbn = get_barcode("isbn10", "376926085") - assert "3769260856" == isbn.isbn10 + assert isbn.isbn10 == "3769260856" def test_isbn13_checksum(): isbn = get_barcode("isbn13", "978376926085") - assert "9783769260854" == isbn.get_fullcode() + assert isbn.get_fullcode() == "9783769260854" def test_gs1_128_checksum(): gs1_128 = get_barcode("gs1_128", "00376401856400470087") - assert "00376401856400470087" == gs1_128.get_fullcode() + assert gs1_128.get_fullcode() == "00376401856400470087" From 0746caf10a3630cece29face49eff4d7e013076a Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 31 Jan 2023 10:20:01 +0100 Subject: [PATCH 090/192] ruff: Enable some extra rules --- barcode/codex.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index 18eff42..3b78c5a 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -249,7 +249,7 @@ def render(self, writer_options=None, text=None): return super().render(options, text) -class Gs1_128(Code128): +class Gs1_128(Code128): # noqa: N801 """ following the norm, a gs1-128 barcode is a subset of code 128 barcode, it can be generated by prepending the code with the FNC1 character diff --git a/pyproject.toml b/pyproject.toml index ca2bcd9..3e3d131 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ select = [ "B0", "I", "UP", - # "N", + "N", "C4", "PT", "SIM", From 3309a061f96ddf4f1d266d6c5e27b4e20a386faa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 03:26:10 +0000 Subject: [PATCH 091/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.10.0 → 23.1.0](https://github.com/psf/black/compare/22.10.0...23.1.0) - [github.com/charliermarsh/ruff-pre-commit: v0.0.238 → v0.0.243](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.238...v0.0.243) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 154e196..a6d0649 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/psf/black - rev: "22.10.0" + rev: "23.1.0" hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy @@ -15,6 +15,6 @@ repos: hooks: - id: mypy - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.238' + rev: 'v0.0.243' hooks: - id: ruff From f824d00592daabbe62a9c9a68b7a3113c215207d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 03:26:20 +0000 Subject: [PATCH 092/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/base.py | 1 - barcode/ean.py | 2 -- barcode/writer.py | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/barcode/base.py b/barcode/base.py index 1f90dc4..e9d9f31 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -5,7 +5,6 @@ class Barcode: - name = "" digits = 0 diff --git a/barcode/ean.py b/barcode/ean.py index 1ffac4d..c89408f 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -127,7 +127,6 @@ def render(self, writer_options=None, text=None): class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13): - name = "EAN-13 with guards" def __init__(self, *args, guardbar=True, **kwargs): @@ -192,7 +191,6 @@ def get_fullcode(self): class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8): - name = "EAN-8 with guards" def __init__(self, *args, guardbar=True, **kwargs): diff --git a/barcode/writer.py b/barcode/writer.py index 1ccff16..b23f314 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -275,7 +275,7 @@ def render(self, code): # Calculates the position of the text by getting the difference # between a guard end and the next start text["start"].pop(0) - for (s, e) in zip(text["start"], text["end"]): + for s, e in zip(text["start"], text["end"]): text["xpos"].append(e + (s - e) / 2) # The last text block is always put after the last guard end @@ -287,7 +287,7 @@ def render(self, code): ypos += pt2mm(self.font_size) blocks = self.text - for (text_, xpos) in zip(blocks, text["xpos"]): + for text_, xpos in zip(blocks, text["xpos"]): self.text = text_ self._callbacks["paint_text"](xpos, ypos) From d47dd6aedb930cf319cf6bef04dd8adbabe774ff Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 7 Feb 2023 13:18:48 +0100 Subject: [PATCH 093/192] Fix SIM108 --- barcode/writer.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index b23f314..0239e94 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -261,11 +261,7 @@ def render(self, code): if not text["start"]: # If we don't have any start value, print the entire ean ypos += self.text_distance - if self.center_text: - # better center position for text - xpos = bxs + ((bxe - bxs) / 2.0) - else: - xpos = bxs + xpos = bxs + (bxe - bxs) / 2.0 if self.center_text else bxs self._callbacks["paint_text"](xpos, ypos) else: # Else, divide the ean into blocks and print each block From 03bd2372e1bc858668b6d9c5bd8c1d206775a1e3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 02:11:35 +0000 Subject: [PATCH 094/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.991 → v1.0.0](https://github.com/pre-commit/mirrors-mypy/compare/v0.991...v1.0.0) - [github.com/charliermarsh/ruff-pre-commit: v0.0.243 → v0.0.246](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.243...v0.0.246) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a6d0649..5be184b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,10 +11,10 @@ repos: hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.991' + rev: 'v1.0.0' hooks: - id: mypy - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.243' + rev: 'v0.0.246' hooks: - id: ruff From 95c6e74496469d0b88fa9a85bd71e8da8e2eaf22 Mon Sep 17 00:00:00 2001 From: Robinson Chaplain <51316342+x-N0@users.noreply.github.com> Date: Wed, 1 Mar 2023 13:27:04 -0400 Subject: [PATCH 095/192] Remove not needed str for string in code example --- docs/getting-started.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.rst b/docs/getting-started.rst index e819933..49cc47a 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -43,7 +43,7 @@ Generating SVG files # Write to a file-like object: rv = BytesIO() - EAN13(str("100000902922"), writer=SVGWriter()).write(rv) + EAN13("100000902922", writer=SVGWriter()).write(rv) # Or to an actual file: with open("somefile.svg", "wb") as f: From 6f8556e203c8942f313ee0830e7751411949095b Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 13 Apr 2023 11:32:29 +0200 Subject: [PATCH 096/192] Drop all references to codecov Upstream yanked all existing releases from PyPI as a way to signal deprecation of their library, breaking all pipelines that rely on them. This isn't the first time they've broken all pipelines as a result on having no idea of how CI downstream systems work, so let's just stay away from them. --- .github/workflows/tests.yml | 6 +----- README.rst | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4420802..a8c1b30 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,12 +25,8 @@ jobs: with: python-version: ${{ matrix.python }} - name: Install test dependency - run: pip install tox codecov + run: pip install tox - name: Run tests run: tox env: TOXENV: ${{ matrix.variant }} - - name: Report coverage - run: codecov - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/README.rst b/README.rst index cbc846d..17f06c8 100644 --- a/README.rst +++ b/README.rst @@ -5,10 +5,6 @@ python-barcode :target: https://github.com/WhyNotHugo/python-barcode/actions :alt: CI status -.. image:: https://codecov.io/gh/WhyNotHugo/python-barcode/branch/main/graph/badge.svg - :target: https://codecov.io/gh/WhyNotHugo/python-barcode - :alt: Build coverage - .. image:: https://readthedocs.org/projects/python-barcode/badge/ :target: https://python-barcode.rtfd.org/ :alt: documentation From dc1983b2ab2c052ebbce63cf7388792193b3f7eb Mon Sep 17 00:00:00 2001 From: tendervittles Date: Mon, 19 Jun 2023 23:26:38 -0400 Subject: [PATCH 097/192] upgrade macOS-11 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a8c1b30..47f7018 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: python: [ '3.7', '3.8', '3.9', '3.10', '3.11' ] variant: [ "py", "py-images" ] include: - - os: macOS-10.15 + - os: macOS-11 python: "3.10" variant: py-images - os: windows-2019 From 0fab6aec2030cef6d47842d574a5e5450a4bdfd3 Mon Sep 17 00:00:00 2001 From: tendervittles Date: Tue, 6 Jun 2023 16:30:35 -0400 Subject: [PATCH 098/192] add font_size zero to doc --- docs/writers.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/writers.rst b/docs/writers.rst index f433921..08ca570 100644 --- a/docs/writers.rst +++ b/docs/writers.rst @@ -32,6 +32,7 @@ be set). :font_size: Font size of the text under the barcode in pt as *integer*. + Font size zero suppresses text. Defaults to **10**. :text_distance: From 486028c8ae3c99de6dc6dd20e22ea1543b742ad9 Mon Sep 17 00:00:00 2001 From: Daniel-Jenkins <71265038+Daniel-Jenkins@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:55:43 +0200 Subject: [PATCH 099/192] Update isxn.py to account for ISBN standard changes. As discussed here https://www.isbn-international.org/content/changes-united-states-isbn-prefixes the valid ISBN range is being extended to include ISBN-979-8 --- barcode/isxn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/isxn.py b/barcode/isxn.py index c886922..a44ddbf 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -45,7 +45,7 @@ def __init__(self, isbn, writer=None): self.isbn13 = isbn if isbn[:3] not in ("978", "979"): raise WrongCountryCodeError("ISBN must start with 978 or 979.") - if isbn[:3] == "979" and isbn[3:5] not in ("10", "11"): + if isbn[:3] == "979" and isbn[3:4] not in ("1", "8"): raise BarcodeError("ISBN must start with 97910 or 97911.") super().__init__(isbn, writer) From 22b8636511ac1539ad3d05b24535bab71b362919 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 03:43:47 +0000 Subject: [PATCH 100/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0) - [github.com/pre-commit/mirrors-mypy: v1.0.0 → v1.3.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.0.0...v1.3.0) - [github.com/charliermarsh/ruff-pre-commit: v0.0.246 → v0.0.272](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.246...v0.0.272) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5be184b..c19bca2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,14 +7,14 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/psf/black - rev: "23.1.0" + rev: "23.3.0" hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.0.0' + rev: 'v1.3.0' hooks: - id: mypy - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.246' + rev: 'v0.0.272' hooks: - id: ruff From e6ee24bc6fa3ac6a186233f00958320606fb376b Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:10:44 +0200 Subject: [PATCH 101/192] Specify explicit cause/context for exceptions --- barcode/__init__.py | 4 ++-- barcode/codabar.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index d0d15cd..9165674 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -71,8 +71,8 @@ def get(name, code=None, writer=None, options=None): options = options or {} try: barcode = __BARCODE_MAP[name.lower()] - except KeyError: - raise BarcodeNotFoundError(f"The barcode {name!r} you requested is not known.") + except KeyError as e: + raise BarcodeNotFoundError(f"The barcode {name!r} is not known.") from e if code is not None: return barcode(code, writer, **options) else: diff --git a/barcode/codabar.py b/barcode/codabar.py index a2247ca..91ba559 100644 --- a/barcode/codabar.py +++ b/barcode/codabar.py @@ -46,19 +46,21 @@ def build(self): ) # Start with [A-D], followed by a narrow space except KeyError: - raise BarcodeError("Codabar should start with either A,B,C or D") + raise BarcodeError("Codabar should start with either A,B,C or D") from None try: data += "n".join( [codabar.CODES[c] for c in self.code[1:-1]] ) # separated by a narrow space except KeyError: - raise IllegalCharacterError("Codabar can only contain numerics or $:/.+-") + raise IllegalCharacterError( + "Codabar can only contain numerics or $:/.+-" + ) from None try: data += "n" + codabar.STARTSTOP[self.code[-1]] # End with [A-D] except KeyError: - raise BarcodeError("Codabar should end with either A,B,C or D") + raise BarcodeError("Codabar should end with either A,B,C or D") from None raw = "" for e in data: From 29c96225ab3ce5aa5d837aa8508288fc093ce288 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:12:52 +0200 Subject: [PATCH 102/192] ruff: sort selected rules --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3e3d131..ccef4ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,13 +10,13 @@ target-version = ['py37'] [tool.ruff] select = [ - "E", "F", + "E", "W", - "B0", "I", - "UP", "N", + "UP", + "B0", "C4", "PT", "SIM", From 3c9ab00af281399ceca469361923d7becbd7d8ed Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:13:18 +0200 Subject: [PATCH 103/192] ruff: enable all bugbear rules --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ccef4ee..cfc20b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ select = [ "I", "N", "UP", - "B0", + "B", "C4", "PT", "SIM", From 210cd3a0ab32f12bcd021c7dcc70fae0e6a80e10 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:15:19 +0200 Subject: [PATCH 104/192] ruff: enable a few additional rules --- pyproject.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index cfc20b2..1937011 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,11 +16,22 @@ select = [ "I", "N", "UP", + "YTT", + "BLE", "B", "C4", + "ISC", + "ICN", + "G", + "PIE", + "PYI", "PT", + "Q", + "RSE", "SIM", "TID", + "TCH", + "INT", ] target-version = "py37" From c937ce9a8204f5abbcf22276573ffd3f1d9e0b34 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:16:46 +0200 Subject: [PATCH 105/192] Delete commented-out code --- docs/conf.py | 3 --- pyproject.toml | 1 + tests/test_init.py | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 468ef53..9c7e154 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,9 +8,6 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) import barcode # -- Project information ----------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 1937011..3719329 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ select = [ "TID", "TCH", "INT", + "ERA", ] target-version = "py37" diff --git a/tests/test_init.py b/tests/test_init.py index 33a845d..846da26 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -39,4 +39,4 @@ def test_generate_with_bytesio(): bio = BytesIO() barcode.generate("ean13", "123455559121112", output=bio) # XXX: File is not 100% deterministic; needs to be addressed at some point. - # assert len(bio.getvalue()) == 6127 + # assert len(bio.getvalue()) == 6127 # noqa: ERA001 From 8cdefd72b3404b204a7ad4b6c241f9c1b750b358 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:21:14 +0200 Subject: [PATCH 106/192] Tidy up some superfluous code --- barcode/__init__.py | 11 ++++++----- barcode/base.py | 6 ++---- barcode/codex.py | 14 +++++++++----- barcode/isxn.py | 8 ++++---- barcode/upc.py | 12 ++++++------ barcode/writer.py | 8 ++++---- pyproject.toml | 1 + 7 files changed, 32 insertions(+), 28 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 9165674..bccc8be 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -75,8 +75,8 @@ def get(name, code=None, writer=None, options=None): raise BarcodeNotFoundError(f"The barcode {name!r} is not known.") from e if code is not None: return barcode(code, writer, **options) - else: - return barcode + + return barcode def get_class(name): @@ -111,10 +111,11 @@ def generate( if isinstance(output, str): fullname = barcode.save(output, writer_options, text) return fullname - elif output: + if output: barcode.write(output, writer_options, text) - else: - raise TypeError("'output' cannot be None") + return None + + raise TypeError("'output' cannot be None") get_barcode = get diff --git a/barcode/base.py b/barcode/base.py index e9d9f31..9f9b96a 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -60,8 +60,7 @@ def save(self, filename, options=None, text=None): """ output = self.render(options, text) if text else self.render(options) - _filename = self.writer.save(filename, output) - return _filename + return self.writer.save(filename, output) def write(self, fp, options=None, text=None): """Renders the barcode and writes it to the file like object @@ -98,5 +97,4 @@ def render(self, writer_options=None, text=None): options["text"] = self.get_fullcode() self.writer.set_options(options) code = self.build() - raw = self.writer.render(code) - return raw + return self.writer.render(code) diff --git a/barcode/codex.py b/barcode/codex.py index 3b78c5a..ea3a61d 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -60,6 +60,7 @@ def calculate_checksum(self): for k, v in code39.MAP.items(): if check == v[0]: return k + return None def build(self): chars = [code39.EDGE] @@ -108,8 +109,8 @@ def calculate_checksum(self): checksum = sum_ % 11 if checksum == 10: raise BarcodeError("Checksum can not be 10 for PZN.") - else: - return checksum + + return checksum class PZN8(PZN7): @@ -195,17 +196,20 @@ def look_next(): def _convert(self, char): if self._charset == "A": return code128.A[char] - elif self._charset == "B": + if self._charset == "B": return code128.B[char] - elif self._charset == "C": + if self._charset == "C": if char in code128.C: return code128.C[char] - elif char.isdigit(): + if char.isdigit(): self._buffer += char if len(self._buffer) == 2: value = int(self._buffer) self._buffer = "" return value + return None + return None + return None def _try_to_optimize(self, encoded): if encoded[1] in code128.TO: diff --git a/barcode/isxn.py b/barcode/isxn.py index a44ddbf..5325844 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -76,8 +76,8 @@ def _calculate_checksum(self): tmp = sum(x * int(y) for x, y in enumerate(self.isbn10[:9], start=1)) % 11 if tmp == 10: return "X" - else: - return tmp + + return tmp def __str__(self): return self.isbn10 @@ -113,8 +113,8 @@ def _calculate_checksum(self): ) if tmp == 10: return "X" - else: - return tmp + + return tmp def make_ean(self): return f"977{self.issn[:7]}00{self._calculate_checksum()}" diff --git a/barcode/upc.py b/barcode/upc.py index bf7e88d..b93ca5c 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -47,14 +47,14 @@ def __init__(self, upc, writer=None, make_ean=False): def __str__(self): if self.ean: return "0" + self.upc - else: - return self.upc + + return self.upc def get_fullcode(self): if self.ean: return "0" + self.upc - else: - return self.upc + + return self.upc def calculate_checksum(self): """Calculates the checksum for UPCA/UPC codes @@ -72,8 +72,8 @@ def sum_(x, y): check = (evensum + oddsum * 3) % 10 if check == 0: return 0 - else: - return 10 - check + + return 10 - check def build(self): """Builds the barcode pattern from 'self.upc' diff --git a/barcode/writer.py b/barcode/writer.py index 0239e94..3d36685 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -364,10 +364,10 @@ def _create_text(self, xpos, ypos): def _finish(self): if self.compress: return self._document.toxml(encoding="UTF-8") - else: - return self._document.toprettyxml( - indent=4 * " ", newl=os.linesep, encoding="UTF-8" - ) + + return self._document.toprettyxml( + indent=4 * " ", newl=os.linesep, encoding="UTF-8" + ) def save(self, filename, output): if self.compress: diff --git a/pyproject.toml b/pyproject.toml index 3719329..1ba3434 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ select = [ "PT", "Q", "RSE", + "RET", "SIM", "TID", "TCH", From 07b22758e3a79e31b88af2525743902e648c1b9a Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:23:25 +0200 Subject: [PATCH 107/192] Prefer expansion instead of concatenation Quite a bit simpler to read. --- barcode/charsets/code128.py | 115 +++++++++++++++++------------------- pyproject.toml | 1 + 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/barcode/charsets/code128.py b/barcode/charsets/code128.py index 4941aa2..26fd837 100644 --- a/barcode/charsets/code128.py +++ b/barcode/charsets/code128.py @@ -3,45 +3,40 @@ # Charsets for code 128 _common = ( - ( - " ", - "!", - '"', - "#", - "$", - "%", - "&", - "'", - "(", - ")", - "*", - "+", - ",", - "-", - ".", - "/", - ) - + tuple(string.digits) - + ( - ":", - ";", - "<", - "=", - ">", - "?", - "@", - ) - + tuple(string.ascii_uppercase) - + ( - "[", - "\\", - "]", - "^", - "_", - ) + " ", + "!", + '"', + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "*", + "+", + ",", + "-", + ".", + "/", + *tuple(string.digits), + ":", + ";", + "<", + "=", + ">", + "?", + "@", + *tuple(string.ascii_uppercase), + "[", + "\\", + "]", + "^", + "_", ) -_charset_a = _common + ( +_charset_a = ( + *_common, "\x00", "\x01", "\x02", @@ -51,11 +46,11 @@ "\x06", "\x07", "\x08", - "\x09", - "\x0a", + "\t", + "\n", "\x0b", "\x0c", - "\x0d", + "\r", "\x0e", "\x0f", "\x10", @@ -74,33 +69,31 @@ "\x1d", "\x1e", "\x1f", - "\xf3", - "\xf2", + "ó", + "ò", "SHIFT", "TO_C", "TO_B", - "\xf4", - "\xf1", + "ô", + "ñ", ) _charset_b = ( - _common - + ("`",) - + tuple(string.ascii_lowercase) - + ( - "{", - "|", - "}", - "~", - "\x7f", - "\xf3", - "\xf2", - "SHIFT", - "TO_C", - "\xf4", - "TO_A", - "\xf1", - ) + *_common, + "`", + *tuple(string.ascii_lowercase), + "{", + "|", + "}", + "~", + "\x7f", + "ó", + "ò", + "SHIFT", + "TO_C", + "ô", + "TO_A", + "ñ", ) ALL = set(_common + _charset_a + _charset_b) diff --git a/pyproject.toml b/pyproject.toml index 1ba3434..578b826 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ select = [ "TCH", "INT", "ERA", + "RUF", ] target-version = "py37" From 020909a4d8e79ef8b978c6ed26f1833a3b6d0e07 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:24:15 +0200 Subject: [PATCH 108/192] Remove explicit return for __init__() --- barcode/ean.py | 4 ++-- pyproject.toml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index c89408f..bf311b9 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -130,7 +130,7 @@ class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13): name = "EAN-13 with guards" def __init__(self, *args, guardbar=True, **kwargs): - return super().__init__(*args, guardbar=guardbar, **kwargs) + super().__init__(*args, guardbar=guardbar, **kwargs) class JapanArticleNumber(EuropeanArticleNumber13): @@ -194,7 +194,7 @@ class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8): name = "EAN-8 with guards" def __init__(self, *args, guardbar=True, **kwargs): - return super().__init__(*args, guardbar=guardbar, **kwargs) + super().__init__(*args, guardbar=guardbar, **kwargs) class EuropeanArticleNumber14(EuropeanArticleNumber13): diff --git a/pyproject.toml b/pyproject.toml index 578b826..c02528a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ select = [ "TCH", "INT", "ERA", + "PLE", "RUF", ] target-version = "py37" From c92de7b295e64bbf4114f78027a525ab556ad243 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:25:27 +0200 Subject: [PATCH 109/192] Specify ignored rule explicitly --- barcode/writer.py | 2 +- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index 3d36685..7af2dc2 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -393,7 +393,7 @@ def write(self, content, fp: BinaryIO): ImageWriter = None else: - class ImageWriter(BaseWriter): # type: ignore + class ImageWriter(BaseWriter): # type: ignore[no-redef] format: str mode: str dpi: int diff --git a/pyproject.toml b/pyproject.toml index c02528a..d1d98d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ select = [ "TCH", "INT", "ERA", + "PGH", "PLE", "RUF", ] From a90800c79c39667d8be3541e28af768dcd691bdc Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:26:35 +0200 Subject: [PATCH 110/192] ruff: enable INP rule --- docs/conf.py | 1 + pyproject.toml | 1 + tests/__init__.py | 0 3 files changed, 2 insertions(+) create mode 100644 tests/__init__.py diff --git a/docs/conf.py b/docs/conf.py index 9c7e154..788677e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,4 @@ +# noqa: INP001 # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full diff --git a/pyproject.toml b/pyproject.toml index d1d98d0..15a3c8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ select = [ "ISC", "ICN", "G", + "INP", "PIE", "PYI", "PT", diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From 2735505a4bc0cf8d57b36e5e17c433117f77e948 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:35:13 +0200 Subject: [PATCH 111/192] pre-commit: apply ruff fixes --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c19bca2..1110608 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,3 +18,4 @@ repos: rev: 'v0.0.272' hooks: - id: ruff + args: [--fix, --exit-non-zero-on-fix] From 416aec0ac7717a8861d181cc942e871b278aa6fb Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:38:53 +0200 Subject: [PATCH 112/192] Add some additional type hints --- barcode/__init__.py | 14 ++++++++++---- barcode/base.py | 35 +++++++++++++++++------------------ barcode/codex.py | 7 +++++-- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index bccc8be..c43c578 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -6,6 +6,7 @@ import os from typing import BinaryIO from typing import Dict +from typing import Optional from typing import Union from barcode.codabar import CODABAR @@ -56,16 +57,21 @@ PROVIDED_BARCODES.sort() -def get(name, code=None, writer=None, options=None): +def get( + name: str, + code: Optional[str] = None, + writer=None, + options: Optional[dict] = None, +): """Helper method for getting a generator or even a generated code. - :param str name: The name of the type of barcode desired. - :param str code: The actual information to encode. If this parameter is + :param name: The name of the type of barcode desired. + :param code: The actual information to encode. If this parameter is provided, a generated barcode is returned. Otherwise, the barcode class is returned. :param Writer writer: An alternative writer to use when generating the barcode. - :param dict options: Additional options to be passed on to the barcode when + :param options: Additional options to be passed on to the barcode when generating. """ options = options or {} diff --git a/barcode/base.py b/barcode/base.py index 9f9b96a..c6646f8 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -1,6 +1,10 @@ """barcode.base """ +from typing import List +from typing import Optional + +from barcode.writer import BaseWriter from barcode.writer import SVGWriter @@ -23,7 +27,9 @@ class Barcode: "text": "", } - def to_ascii(self): + writer: BaseWriter + + def to_ascii(self) -> str: code = self.build() for i, line in enumerate(code): code[i] = line.replace("1", "X").replace("0", " ") @@ -32,7 +38,7 @@ def to_ascii(self): def __repr__(self): return f"<{self.__class__.__name__}({self.get_fullcode()!r})>" - def build(self): + def build(self) -> List[str]: raise NotImplementedError def get_fullcode(self): @@ -43,20 +49,16 @@ def get_fullcode(self): """ raise NotImplementedError - def save(self, filename, options=None, text=None): + def save( + self, filename: str, options: Optional[dict] = None, text: Optional[str] = None + ) -> str: """Renders the barcode and saves it in `filename`. - :parameters: - filename : String - Filename to save the barcode in (without filename - extension). - options : Dict - The same as in `self.render`. - text : str - Text to render under the barcode. + :param filename: Filename to save the barcode in (without filename extension). + :param options: The same as in `self.render`. + :param text: Text to render under the barcode. :returns: The full filename with extension. - :rtype: String """ output = self.render(options, text) if text else self.render(options) @@ -77,14 +79,11 @@ def write(self, fp, options=None, text=None): output = self.render(options, text) self.writer.write(output, fp) - def render(self, writer_options=None, text=None): + def render(self, writer_options: Optional[dict] = None, text: Optional[str] = None): """Renders the barcode using `self.writer`. - :parameters: - writer_options : Dict - Options for `self.writer`, see writer docs for details. - text : str - Text to render under the barcode. + :param writer_options: Options for `self.writer`, see writer docs for details. + :param text: Text to render under the barcode. :returns: Output of the writers render method. """ diff --git a/barcode/codex.py b/barcode/codex.py index ea3a61d..5be6086 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -2,6 +2,9 @@ :Provided barcodes: Code 39, Code 128, PZN """ + +from typing import Tuple + from barcode.base import Barcode from barcode.charsets import code39 from barcode.charsets import code128 @@ -16,7 +19,7 @@ MIN_QUIET_ZONE = 2.54 -def check_code(code, name, allowed): +def check_code(code: str, name: str, allowed: Tuple[str, ...]) -> None: wrong = [] for char in code: if char not in allowed: @@ -48,7 +51,7 @@ def __init__(self, code: str, writer=None, add_checksum: bool = True): self.writer = writer or self.default_writer() check_code(self.code, self.name, code39.REF) - def __str__(self): + def __str__(self) -> str: return self.code def get_fullcode(self) -> str: From 285d85d1c3462223d5c8f7f9278dbe25cf700b76 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:47:02 +0200 Subject: [PATCH 113/192] Add a few additional type hints --- barcode/base.py | 2 +- barcode/codabar.py | 4 ++-- barcode/codex.py | 14 +++++++------- barcode/ean.py | 14 +++++++------- barcode/errors.py | 4 ++-- barcode/isxn.py | 10 +++++----- barcode/itf.py | 4 ++-- barcode/upc.py | 4 ++-- barcode/writer.py | 12 ++++++++---- 9 files changed, 36 insertions(+), 32 deletions(-) diff --git a/barcode/base.py b/barcode/base.py index c6646f8..048153d 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -35,7 +35,7 @@ def to_ascii(self) -> str: code[i] = line.replace("1", "X").replace("0", " ") return "\n".join(code) - def __repr__(self): + def __repr__(self) -> str: return f"<{self.__class__.__name__}({self.get_fullcode()!r})>" def build(self) -> List[str]: diff --git a/barcode/codabar.py b/barcode/codabar.py index 91ba559..e080324 100644 --- a/barcode/codabar.py +++ b/barcode/codabar.py @@ -27,13 +27,13 @@ class CODABAR(Barcode): name = "Codabar (NW-7)" - def __init__(self, code, writer=None, narrow=2, wide=5): + def __init__(self, code, writer=None, narrow=2, wide=5) -> None: self.code = code self.writer = writer or self.default_writer() self.narrow = narrow self.wide = wide - def __str__(self): + def __str__(self) -> str: return self.code def get_fullcode(self): diff --git a/barcode/codex.py b/barcode/codex.py index 5be6086..b594e01 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -3,7 +3,7 @@ :Provided barcodes: Code 39, Code 128, PZN """ -from typing import Tuple +from typing import Collection from barcode.base import Barcode from barcode.charsets import code39 @@ -19,7 +19,7 @@ MIN_QUIET_ZONE = 2.54 -def check_code(code: str, name: str, allowed: Tuple[str, ...]) -> None: +def check_code(code: str, name: str, allowed: Collection[str]) -> None: wrong = [] for char in code: if char not in allowed: @@ -37,7 +37,7 @@ class Code39(Barcode): name = "Code 39" - def __init__(self, code: str, writer=None, add_checksum: bool = True): + def __init__(self, code: str, writer=None, add_checksum: bool = True) -> None: r""" :param code: Code 39 string without \* and without checksum. :param writer: A ``barcode.writer`` instance used to render the barcode @@ -92,7 +92,7 @@ class PZN7(Code39): digits = 6 - def __init__(self, pzn, writer=None): + def __init__(self, pzn, writer=None) -> None: pzn = pzn[: self.digits] if not pzn.isdigit(): raise IllegalCharacterError("PZN can only contain numbers.") @@ -135,14 +135,14 @@ class Code128(Barcode): name = "Code 128" - def __init__(self, code, writer=None): + def __init__(self, code, writer=None) -> None: self.code = code self.writer = writer or self.default_writer() self._charset = "B" self._buffer = "" check_code(self.code, self.name, code128.ALL) - def __str__(self): + def __str__(self) -> str: return self.code @property @@ -268,7 +268,7 @@ class Gs1_128(Code128): # noqa: N801 FNC1_CHAR = "\xf1" - def __init__(self, code, writer=None): + def __init__(self, code, writer=None) -> None: code = self.FNC1_CHAR + code super().__init__(code, writer) diff --git a/barcode/ean.py b/barcode/ean.py index bf311b9..6cf1728 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -41,7 +41,7 @@ class EuropeanArticleNumber13(Barcode): digits = 12 - def __init__(self, ean, writer=None, no_checksum=False, guardbar=False): + def __init__(self, ean, writer=None, no_checksum=False, guardbar=False) -> None: ean = ean[: self.digits] if not ean.isdigit(): raise IllegalCharacterError("EAN code can only contain numbers.") @@ -72,7 +72,7 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=False): self.MIDDLE = _ean.MIDDLE self.writer = writer or self.default_writer() - def __str__(self): + def __str__(self) -> str: return self.ean def get_fullcode(self): @@ -129,8 +129,8 @@ def render(self, writer_options=None, text=None): class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13): name = "EAN-13 with guards" - def __init__(self, *args, guardbar=True, **kwargs): - super().__init__(*args, guardbar=guardbar, **kwargs) + def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None: + super().__init__(ean, writer, no_checksum, guardbar) class JapanArticleNumber(EuropeanArticleNumber13): @@ -147,7 +147,7 @@ class JapanArticleNumber(EuropeanArticleNumber13): valid_country_codes = list(range(450, 460)) + list(range(490, 500)) - def __init__(self, jan, *args, **kwargs): + def __init__(self, jan, *args, **kwargs) -> None: if int(jan[:3]) not in self.valid_country_codes: raise WrongCountryCodeError( "Country code isn't between 450-460 or 490-500." @@ -193,8 +193,8 @@ def get_fullcode(self): class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8): name = "EAN-8 with guards" - def __init__(self, *args, guardbar=True, **kwargs): - super().__init__(*args, guardbar=guardbar, **kwargs) + def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None: + super().__init__(ean, writer, no_checksum, guardbar) class EuropeanArticleNumber14(EuropeanArticleNumber13): diff --git a/barcode/errors.py b/barcode/errors.py index 7fb448f..2a55791 100755 --- a/barcode/errors.py +++ b/barcode/errors.py @@ -3,10 +3,10 @@ class BarcodeError(Exception): - def __init__(self, msg): + def __init__(self, msg) -> None: self.msg = msg - def __str__(self): + def __str__(self) -> str: return self.msg diff --git a/barcode/isxn.py b/barcode/isxn.py index 5325844..08551a1 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -40,7 +40,7 @@ class InternationalStandardBookNumber13(EuropeanArticleNumber13): name = "ISBN-13" - def __init__(self, isbn, writer=None): + def __init__(self, isbn, writer=None) -> None: isbn = isbn.replace("-", "") self.isbn13 = isbn if isbn[:3] not in ("978", "979"): @@ -65,7 +65,7 @@ class InternationalStandardBookNumber10(InternationalStandardBookNumber13): digits = 9 - def __init__(self, isbn, writer=None): + def __init__(self, isbn, writer=None) -> None: isbn = isbn.replace("-", "") isbn = isbn[: self.digits] super().__init__("978" + isbn, writer) @@ -79,7 +79,7 @@ def _calculate_checksum(self): return tmp - def __str__(self): + def __str__(self) -> str: return self.isbn10 @@ -98,7 +98,7 @@ class InternationalStandardSerialNumber(EuropeanArticleNumber13): digits = 7 - def __init__(self, issn, writer=None): + def __init__(self, issn, writer=None) -> None: issn = issn.replace("-", "") issn = issn[: self.digits] self.issn = issn @@ -119,7 +119,7 @@ def _calculate_checksum(self): def make_ean(self): return f"977{self.issn[:7]}00{self._calculate_checksum()}" - def __str__(self): + def __str__(self) -> str: return self.issn diff --git a/barcode/itf.py b/barcode/itf.py index b79b4bd..dc1de0e 100644 --- a/barcode/itf.py +++ b/barcode/itf.py @@ -29,7 +29,7 @@ class ITF(Barcode): name = "ITF" - def __init__(self, code, writer=None, narrow=2, wide=5): + def __init__(self, code, writer=None, narrow=2, wide=5) -> None: if not code.isdigit(): raise IllegalCharacterError("ITF code can only contain numbers.") # Length must be even, prepend 0 if necessary @@ -40,7 +40,7 @@ def __init__(self, code, writer=None, narrow=2, wide=5): self.narrow = narrow self.wide = wide - def __str__(self): + def __str__(self) -> str: return self.code def get_fullcode(self): diff --git a/barcode/upc.py b/barcode/upc.py index b93ca5c..0e17c37 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -22,7 +22,7 @@ class UniversalProductCodeA(Barcode): digits = 11 - def __init__(self, upc, writer=None, make_ean=False): + def __init__(self, upc, writer=None, make_ean=False) -> None: """Initializes new UPC-A barcode. :param str upc: The upc number as string. @@ -44,7 +44,7 @@ def __init__(self, upc, writer=None, make_ean=False): self.upc = f"{upc}{self.calculate_checksum()}" self.writer = writer or self.default_writer() - def __str__(self): + def __str__(self) -> str: if self.ean: return "0" + self.upc diff --git a/barcode/writer.py b/barcode/writer.py index 7af2dc2..b63e5ea 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -80,8 +80,12 @@ class BaseWriter: """ def __init__( - self, initialize=None, paint_module=None, paint_text=None, finish=None - ): + self, + initialize=None, + paint_module=None, + paint_text=None, + finish=None, + ) -> None: self._callbacks = { "initialize": initialize, "paint_module": paint_module, @@ -291,7 +295,7 @@ def render(self, code): class SVGWriter(BaseWriter): - def __init__(self): + def __init__(self) -> None: BaseWriter.__init__( self, self._init, self._create_module, self._create_text, self._finish ) @@ -398,7 +402,7 @@ class ImageWriter(BaseWriter): # type: ignore[no-redef] mode: str dpi: int - def __init__(self, format="PNG", mode="RGB"): + def __init__(self, format="PNG", mode="RGB") -> None: """Initialise a new write instance. :params format: The file format for the generated image. This parameter can From 6cee77d09daf288cfbbf4fb63e461d522d35d5aa Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Sat, 1 Jul 2023 16:27:06 +0200 Subject: [PATCH 114/192] Annotate class variable as such See: https://peps.python.org/pep-0526/ --- barcode/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/barcode/base.py b/barcode/base.py index 048153d..35caece 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -1,6 +1,7 @@ """barcode.base """ +from typing import ClassVar from typing import List from typing import Optional @@ -15,7 +16,7 @@ class Barcode: default_writer = SVGWriter - default_writer_options = { + default_writer_options: ClassVar[dict] = { "module_width": 0.2, "module_height": 15.0, "quiet_zone": 6.5, From 4a276fc2add755011f94abb3f6b2a1be9e4b25c5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 03:39:26 +0000 Subject: [PATCH 115/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.3.0 → v1.4.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.3.0...v1.4.1) - [github.com/charliermarsh/ruff-pre-commit: v0.0.272 → v0.0.275](https://github.com/charliermarsh/ruff-pre-commit/compare/v0.0.272...v0.0.275) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1110608..19e3d13 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,11 +11,11 @@ repos: hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.3.0' + rev: 'v1.4.1' hooks: - id: mypy - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.272' + rev: 'v0.0.275' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From 8fa4e6c7686e0b212eb1f7ff55dc70e5c25335f6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Jun 2023 03:39:47 +0000 Subject: [PATCH 116/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/__init__.py | 3 +-- barcode/codex.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index c43c578..1879742 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -115,8 +115,7 @@ def generate( barcode = get(name, code, writer) if isinstance(output, str): - fullname = barcode.save(output, writer_options, text) - return fullname + return barcode.save(output, writer_options, text) if output: barcode.write(output, writer_options, text) return None diff --git a/barcode/codex.py b/barcode/codex.py index b594e01..152f572 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -237,8 +237,7 @@ def _build(self): encoded.extend(self._new_charset("B")) encoded.append(self._convert(self._buffer[0])) self._buffer = "" - encoded = self._try_to_optimize(encoded) - return encoded + return self._try_to_optimize(encoded) def build(self): encoded = self._build() From 68eb9f48a584e4f6103359f6ab271857990c46be Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 6 Jul 2023 00:51:14 +0200 Subject: [PATCH 117/192] Update changelog --- docs/changelog.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index d57ef79..fccfeff 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,11 +1,14 @@ Changelog --------- -unreleased +v0.15.0 ~~~~~~~~~~ * **Breaking** Dropped support for Python 3.6. * Added support for Python 3.11. +* Fixed compatibility with Pillow 10.0. +* Updated ISBN to support newer allocated ranges. +* Improved type hints. v0.14.0 ~~~~~~~ From 34e514a1ec0933cc74ee059c28d36081569e85a5 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 6 Jul 2023 00:55:12 +0200 Subject: [PATCH 118/192] Add missing dependency to release script. --- .github/workflows/publish.yml | 2 +- docs/changelog.rst | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 490a495..eec74f7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/setup-python@v3 with: python-version: 3.7 - - run: pip install build setuptools wheel + - run: pip install build setuptools wheel build setuptools_scm - run: python -m build --sdist --wheel --no-isolation - uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/docs/changelog.rst b/docs/changelog.rst index fccfeff..a9e7c5b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog --------- +v0.15.1 +~~~~~~~~~~ + +* Add missing dependency to release script. + v0.15.0 ~~~~~~~~~~ From 26fd881e777a0410129bf4bf877ad383ce0e30e4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 04:04:23 +0000 Subject: [PATCH 119/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.3.0 → 23.7.0](https://github.com/psf/black/compare/23.3.0...23.7.0) - https://github.com/charliermarsh/ruff-pre-commit → https://github.com/astral-sh/ruff-pre-commit - [github.com/astral-sh/ruff-pre-commit: v0.0.275 → v0.0.280](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.275...v0.0.280) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 19e3d13..393803b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,15 +7,15 @@ repos: - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/psf/black - rev: "23.3.0" + rev: "23.7.0" hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v1.4.1' hooks: - id: mypy - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.275' + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: 'v0.0.280' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From aaa738ffbbb21f5084f7153655de45c89b4ac5e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 04:04:33 +0000 Subject: [PATCH 120/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/ean.py | 9 ++------- barcode/pybarcode.py | 10 ++-------- tests/test_manually.py | 6 +----- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index 6cf1728..bf47765 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -47,19 +47,14 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=False) -> None: raise IllegalCharacterError("EAN code can only contain numbers.") if len(ean) != self.digits: raise NumberOfDigitsError( - "EAN must have {} digits, not {}.".format( - self.digits, - len(ean), - ) + f"EAN must have {self.digits} digits, not {len(ean)}." ) self.ean = ean # If no checksum if no_checksum: # Add a thirteen char if given in parameter, # otherwise pad with zero - self.ean = "{}{}".format( - ean, ean[self.digits] if len(ean) > self.digits else 0 - ) + self.ean = f"{ean}{ean[self.digits] if len(ean) > self.digits else 0}" else: self.ean = f"{ean}{self.calculate_checksum()}" diff --git a/barcode/pybarcode.py b/barcode/pybarcode.py index b21ca5d..b18ae92 100644 --- a/barcode/pybarcode.py +++ b/barcode/pybarcode.py @@ -25,17 +25,11 @@ def list_types(args, parser=None): def create_barcode(args, parser): args.type = args.type.upper() if args.type != "SVG" and args.type not in IMG_FORMATS: - parser.error( - "Unknown type {type}. Try list action for available types.".format( - type=args.type - ) - ) + parser.error(f"Unknown type {args.type}. Try list action for available types.") args.barcode = args.barcode.lower() if args.barcode not in barcode.PROVIDED_BARCODES: parser.error( - "Unknown barcode {bc}. Try list action for available barcodes.".format( - bc=args.barcode - ) + f"Unknown barcode {args.barcode}. Try list action for available barcodes." ) if args.type != "SVG": opts = {"format": args.type} diff --git a/tests/test_manually.py b/tests/test_manually.py index dc43fe6..f396be0 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -67,11 +67,7 @@ def append_img(x, y): else: options["center_text"] = True filename = bcode.save(os.path.join(TESTPATH, codename), options=options) - print( - "Code: {}, Input: {}, Output: {}".format( - bcode.name, code, bcode.get_fullcode() - ) - ) + print(f"Code: {bcode.name}, Input: {code}, Output: {bcode.get_fullcode()}") append(os.path.basename(filename), bcode.name) if ImageWriter is not None: bcodec = get_barcode_class(codename) From 13538c812f9c835a0787be01a49bd4a9499fa5ca Mon Sep 17 00:00:00 2001 From: fra87 <5077242+fra87@users.noreply.github.com> Date: Sun, 27 Aug 2023 15:10:01 +0200 Subject: [PATCH 121/192] Exposed EAN options on ISBN13 constructor (#210) Expose the no_checksum and guardbar parameters also for the ISBN13 constructor. --- barcode/isxn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barcode/isxn.py b/barcode/isxn.py index 08551a1..7438208 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -40,14 +40,14 @@ class InternationalStandardBookNumber13(EuropeanArticleNumber13): name = "ISBN-13" - def __init__(self, isbn, writer=None) -> None: + def __init__(self, isbn, writer=None, no_checksum=False, guardbar=False) -> None: isbn = isbn.replace("-", "") self.isbn13 = isbn if isbn[:3] not in ("978", "979"): raise WrongCountryCodeError("ISBN must start with 978 or 979.") if isbn[:3] == "979" and isbn[3:4] not in ("1", "8"): raise BarcodeError("ISBN must start with 97910 or 97911.") - super().__init__(isbn, writer) + super().__init__(isbn, writer, no_checksum, guardbar) class InternationalStandardBookNumber10(InternationalStandardBookNumber13): From daf138b6971b5f3761d9f50f79b50b2e4886ce49 Mon Sep 17 00:00:00 2001 From: fra87 <5077242+fra87@users.noreply.github.com> Date: Mon, 28 Aug 2023 20:08:37 +0200 Subject: [PATCH 122/192] Fixed erroneous calculation in calculate_checksum --- barcode/ean.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index bf47765..7ea8e33 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -84,9 +84,10 @@ def calculate_checksum(self): def sum_(x, y): return int(x) + int(y) - - evensum = reduce(sum_, self.ean[-2::-2]) - oddsum = reduce(sum_, self.ean[-1::-2]) + ean_without_checksum = self.ean[:self.digits] + + evensum = reduce(sum_, ean_without_checksum[-2::-2]) + oddsum = reduce(sum_, ean_without_checksum[-1::-2]) return (10 - ((evensum + oddsum * 3) % 10)) % 10 def build(self): From 996eaf93606719dc05e5bbdc85cbe668e3f59625 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:11:04 +0000 Subject: [PATCH 123/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/ean.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index 7ea8e33..8aec773 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -84,8 +84,9 @@ def calculate_checksum(self): def sum_(x, y): return int(x) + int(y) - ean_without_checksum = self.ean[:self.digits] - + + ean_without_checksum = self.ean[: self.digits] + evensum = reduce(sum_, ean_without_checksum[-2::-2]) oddsum = reduce(sum_, ean_without_checksum[-1::-2]) return (10 - ((evensum + oddsum * 3) % 10)) % 10 From e4cc9193763094beaa4e9a438b57d0b506c4d8cb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 06:30:57 +0000 Subject: [PATCH 124/192] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.4.1 → v1.5.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.4.1...v1.5.1) - [github.com/astral-sh/ruff-pre-commit: v0.0.280 → v0.0.287](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.280...v0.0.287) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 393803b..8bdd7d7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,11 +11,11 @@ repos: hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.4.1' + rev: 'v1.5.1' hooks: - id: mypy - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.0.280' + rev: 'v0.0.287' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From d107a47515cf7354116c53bc466320b1e730d8cf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 06:31:37 +0000 Subject: [PATCH 125/192] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- barcode/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index b63e5ea..513f762 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -189,7 +189,7 @@ def packed(self, line): """ line += " " c = 1 - for i in range(0, len(line) - 1): + for i in range(len(line) - 1): if line[i] == line[i + 1]: c += 1 else: From ed8db72fbf7d051da7037420b201b5caf8a88d4d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:55:02 +0200 Subject: [PATCH 126/192] Drop support for python 3.7 Two barcode implementation were actually already broken for 3.7. Rather than fix them, drop 3.7 entirely, since its extended support is ending next week anyway. --- README.rst | 2 +- docs/changelog.rst | 2 +- pyproject.toml | 4 ++-- setup.py | 1 - tox.ini | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 17f06c8..8e1eb6c 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.7 to 3.11. +Support Python 3.8 to 3.11. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/docs/changelog.rst b/docs/changelog.rst index a9e7c5b..c384f4d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,7 +9,7 @@ v0.15.1 v0.15.0 ~~~~~~~~~~ -* **Breaking** Dropped support for Python 3.6. +* **Breaking** Dropped support for Python 3.6 and 3.7. * Added support for Python 3.11. * Fixed compatibility with Pillow 10.0. * Updated ISBN to support newer allocated ranges. diff --git a/pyproject.toml b/pyproject.toml index 15a3c8f..525a3c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ write_to = "barcode/version.py" version_scheme = "post-release" [tool.black] -target-version = ['py37'] +target-version = ['py38'] [tool.ruff] select = [ @@ -39,7 +39,7 @@ select = [ "PLE", "RUF", ] -target-version = "py37" +target-version = "py38" [tool.ruff.isort] force-single-line = true diff --git a/setup.py b/setup.py index 57a8bfa..853961b 100755 --- a/setup.py +++ b/setup.py @@ -23,7 +23,6 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/tox.ini b/tox.ini index 7b5eed7..3d1f037 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py37,py38,py39,py310,py311}{,-images} +envlist = {py38,py39,py310,py311}{,-images} skip_missing_interpreters = True [testenv] From 177e1301dcf0fff9c350497244d3ca9a4f4ffe18 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 20 Jun 2023 15:56:30 +0200 Subject: [PATCH 127/192] Use vermin to check compat with older Pythons --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8bdd7d7..a9ec28d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,3 +19,8 @@ repos: hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] + - repo: https://github.com/netromdk/vermin + rev: v1.5.2 + hooks: + - id: vermin + args: ['-t=3.8-', '--violations'] From af91e214c2d46bc19fcaafa15b9927109c7e50df Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:15:53 +0200 Subject: [PATCH 128/192] Add some type hints for BaseWriter --- barcode/writer.py | 65 +++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 513f762..d6cc731 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -1,10 +1,30 @@ +from __future__ import annotations + import gzip import os import xml.dom +from typing import TYPE_CHECKING from typing import BinaryIO +from typing import Callable +from typing import TypedDict from barcode.version import version +if TYPE_CHECKING: + + class InternalText(TypedDict): + start: list + end: list + xpos: list + was_guard: bool + + class Callbacks(TypedDict): + initialize: Callable | None + paint_module: Callable + paint_text: Callable | None + finish: Callable + + try: import Image import ImageDraw @@ -61,30 +81,27 @@ class BaseWriter: attributes and can set them directly or using `self.set_options(option=value)`. - :parameters: - initialize : Function - Callback for initializing the inheriting writer. - Is called: `callback_initialize(raw_code)` - paint_module : Function - Callback for painting one barcode module. - Is called: `callback_paint_module(xpos, ypos, width, color)` - paint_text : Function - Callback for painting the text under the barcode. - Is called: `callback_paint_text(xpos, ypos)` using `self.text` - as text. - finish : Function - Callback for doing something with the completely rendered - output. - Is called: `return callback_finish()` and must return the - rendered output. + :param initialize: Callback for initializing the inheriting writer. + Is called: `callback_initialize(raw_code)` + :param paint_module: + Callback for painting one barcode module. + Is called: `callback_paint_module(xpos, ypos, width, color)` + :param paint_text: Callback for painting the text under the barcode. + Is called: `callback_paint_text(xpos, ypos)` using `self.text` + as text. + :param finish: Callback for doing something with the completely rendered + output. Is called: `return callback_finish()` and must return the + rendered output. """ + _callbacks: Callbacks + def __init__( self, - initialize=None, - paint_module=None, - paint_text=None, - finish=None, + initialize: Callable | None, + paint_module: Callable, + paint_text: Callable | None, + finish: Callable, ) -> None: self._callbacks = { "initialize": initialize, @@ -218,7 +235,7 @@ def render(self, code): # Left quiet zone is x startposition xpos = self.quiet_zone bxs = xpos # x start of barcode - text = { + 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 @@ -281,12 +298,10 @@ def render(self, code): # The last text block is always put after the last guard end text["xpos"].append(text["end"][-1] + 4 * self.module_width) - # Split the ean into its blocks - self.text = self.text.split(" ") - ypos += pt2mm(self.font_size) - blocks = self.text + # Split the ean into its blocks + blocks = self.text.split(" ") for text_, xpos in zip(blocks, text["xpos"]): self.text = text_ self._callbacks["paint_text"](xpos, ypos) From ba08a1ee304d7187b029d14e70e7445eecb6c479 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:19:09 +0200 Subject: [PATCH 129/192] Enforce lazy evaluation of annotations They're only used for type hints in this code base. --- barcode/__init__.py | 20 +++++++++++--------- barcode/base.py | 10 +++++----- barcode/charsets/codabar.py | 2 ++ barcode/charsets/code128.py | 2 ++ barcode/charsets/code39.py | 2 ++ barcode/charsets/ean.py | 2 ++ barcode/charsets/itf.py | 2 ++ barcode/charsets/upc.py | 2 ++ barcode/codabar.py | 2 ++ barcode/codex.py | 1 + barcode/ean.py | 2 ++ barcode/errors.py | 2 ++ barcode/isxn.py | 2 ++ barcode/itf.py | 2 ++ barcode/pybarcode.py | 2 ++ barcode/upc.py | 2 ++ docs/conf.py | 2 ++ pyproject.toml | 1 + setup.py | 2 ++ tests/test_builds.py | 2 ++ tests/test_checksums.py | 2 ++ tests/test_init.py | 2 ++ tests/test_manually.py | 2 ++ tests/test_writers.py | 2 ++ 24 files changed, 58 insertions(+), 14 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 1879742..5568d04 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -3,11 +3,10 @@ created as SVG objects. If Pillow is installed, the barcodes can also be rendered as images (all formats supported by Pillow). """ -import os +from __future__ import annotations + +from typing import TYPE_CHECKING from typing import BinaryIO -from typing import Dict -from typing import Optional -from typing import Union from barcode.codabar import CODABAR from barcode.codex import PZN @@ -28,6 +27,9 @@ from barcode.upc import UPCA from barcode.version import version # noqa: F401 +if TYPE_CHECKING: + import os + __BARCODE_MAP = { "ean8": EAN8, "ean8-guard": EAN8_GUARD, @@ -59,9 +61,9 @@ def get( name: str, - code: Optional[str] = None, + code: str | None = None, writer=None, - options: Optional[dict] = None, + options: dict | None = None, ): """Helper method for getting a generator or even a generated code. @@ -93,9 +95,9 @@ def generate( name: str, code: str, writer=None, - output: Union[str, os.PathLike, BinaryIO, None] = None, - writer_options: Union[Dict, None] = None, - text: Union[str, None] = None, + output: str | (os.PathLike | (BinaryIO | None)) = None, + writer_options: dict | None = None, + text: str | None = None, ): """Shortcut to generate a barcode in one line. diff --git a/barcode/base.py b/barcode/base.py index 35caece..595c7ff 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -1,9 +1,9 @@ """barcode.base """ +from __future__ import annotations + from typing import ClassVar -from typing import List -from typing import Optional from barcode.writer import BaseWriter from barcode.writer import SVGWriter @@ -39,7 +39,7 @@ def to_ascii(self) -> str: def __repr__(self) -> str: return f"<{self.__class__.__name__}({self.get_fullcode()!r})>" - def build(self) -> List[str]: + def build(self) -> list[str]: raise NotImplementedError def get_fullcode(self): @@ -51,7 +51,7 @@ def get_fullcode(self): raise NotImplementedError def save( - self, filename: str, options: Optional[dict] = None, text: Optional[str] = None + self, filename: str, options: dict | None = None, text: str | None = None ) -> str: """Renders the barcode and saves it in `filename`. @@ -80,7 +80,7 @@ def write(self, fp, options=None, text=None): output = self.render(options, text) self.writer.write(output, fp) - def render(self, writer_options: Optional[dict] = None, text: Optional[str] = None): + def render(self, writer_options: dict | None = None, text: str | None = None): """Renders the barcode using `self.writer`. :param writer_options: Options for `self.writer`, see writer docs for details. diff --git a/barcode/charsets/codabar.py b/barcode/charsets/codabar.py index d0ba077..429b3ce 100644 --- a/barcode/charsets/codabar.py +++ b/barcode/charsets/codabar.py @@ -1,3 +1,5 @@ +from __future__ import annotations + # W = Wide bar # w = wide space # N = Narrow bar diff --git a/barcode/charsets/code128.py b/barcode/charsets/code128.py index 26fd837..c631f3b 100644 --- a/barcode/charsets/code128.py +++ b/barcode/charsets/code128.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import string # Charsets for code 128 diff --git a/barcode/charsets/code39.py b/barcode/charsets/code39.py index d9f679d..dcd80e7 100644 --- a/barcode/charsets/code39.py +++ b/barcode/charsets/code39.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import string # Charsets for code 39 diff --git a/barcode/charsets/ean.py b/barcode/charsets/ean.py index 199142d..afc94c1 100644 --- a/barcode/charsets/ean.py +++ b/barcode/charsets/ean.py @@ -1,3 +1,5 @@ +from __future__ import annotations + EDGE = "101" MIDDLE = "01010" CODES = { diff --git a/barcode/charsets/itf.py b/barcode/charsets/itf.py index 24eb02f..6965bae 100644 --- a/barcode/charsets/itf.py +++ b/barcode/charsets/itf.py @@ -1,3 +1,5 @@ +from __future__ import annotations + # W = Wide bar # w = wide space # N = Narrow bar diff --git a/barcode/charsets/upc.py b/barcode/charsets/upc.py index df90deb..cb49510 100644 --- a/barcode/charsets/upc.py +++ b/barcode/charsets/upc.py @@ -1,3 +1,5 @@ +from __future__ import annotations + EDGE = "101" MIDDLE = "01010" CODES = { diff --git a/barcode/codabar.py b/barcode/codabar.py index e080324..6395459 100644 --- a/barcode/codabar.py +++ b/barcode/codabar.py @@ -2,6 +2,8 @@ :Provided barcodes: Codabar (NW-7) """ +from __future__ import annotations + __docformat__ = "restructuredtext en" from barcode.base import Barcode diff --git a/barcode/codex.py b/barcode/codex.py index 152f572..ef72187 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -2,6 +2,7 @@ :Provided barcodes: Code 39, Code 128, PZN """ +from __future__ import annotations from typing import Collection diff --git a/barcode/ean.py b/barcode/ean.py index bf47765..94420d2 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -2,6 +2,8 @@ :Provided barcodes: EAN-14, EAN-13, EAN-8, JAN """ +from __future__ import annotations + __docformat__ = "restructuredtext en" from functools import reduce diff --git a/barcode/errors.py b/barcode/errors.py index 2a55791..2da49d6 100755 --- a/barcode/errors.py +++ b/barcode/errors.py @@ -1,4 +1,6 @@ """barcode.errors""" +from __future__ import annotations + __docformat__ = "restructuredtext en" diff --git a/barcode/isxn.py b/barcode/isxn.py index 7438208..bcb56ba 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -21,6 +21,8 @@ '0132354187' """ +from __future__ import annotations + from barcode.ean import EuropeanArticleNumber13 from barcode.errors import BarcodeError from barcode.errors import WrongCountryCodeError diff --git a/barcode/itf.py b/barcode/itf.py index dc1de0e..428dd4d 100644 --- a/barcode/itf.py +++ b/barcode/itf.py @@ -2,6 +2,8 @@ :Provided barcodes: Interleaved 2 of 5 """ +from __future__ import annotations + __docformat__ = "restructuredtext en" from barcode.base import Barcode diff --git a/barcode/pybarcode.py b/barcode/pybarcode.py index b18ae92..c20223c 100644 --- a/barcode/pybarcode.py +++ b/barcode/pybarcode.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from argparse import ArgumentParser diff --git a/barcode/upc.py b/barcode/upc.py index 0e17c37..37b9f76 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -2,6 +2,8 @@ :Provided barcodes: UPC-A """ +from __future__ import annotations + __docformat__ = "restructuredtext en" from functools import reduce diff --git a/docs/conf.py b/docs/conf.py index 788677e..e517fed 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,6 +9,8 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # +from __future__ import annotations + import barcode # -- Project information ----------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 525a3c5..7487714 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,3 +43,4 @@ target-version = "py38" [tool.ruff.isort] force-single-line = true +required-imports = ["from __future__ import annotations"] diff --git a/setup.py b/setup.py index 853961b..697ccb6 100755 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pathlib import Path from setuptools import find_packages diff --git a/tests/test_builds.py b/tests/test_builds.py index 512c2c5..04365fc 100755 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from barcode import get_barcode diff --git a/tests/test_checksums.py b/tests/test_checksums.py index 513ac1f..19d7c92 100755 --- a/tests/test_checksums.py +++ b/tests/test_checksums.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from barcode import get_barcode diff --git a/tests/test_init.py b/tests/test_init.py index 846da26..e031e85 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from io import BytesIO diff --git a/tests/test_manually.py b/tests/test_manually.py index f396be0..7759c84 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -1,4 +1,6 @@ """Generates barcodes for visually inspecting the results.""" +from __future__ import annotations + import codecs import os diff --git a/tests/test_writers.py b/tests/test_writers.py index 894184d..0e49f04 100644 --- a/tests/test_writers.py +++ b/tests/test_writers.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from io import BytesIO From a1f2cb6bc3b9ecb728b20893fc705899ac1d0b6d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:23:39 +0200 Subject: [PATCH 130/192] Drop support for Python 3.7 --- .github/workflows/tests.yml | 2 +- docs/changelog.rst | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 47f7018..16a66eb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ ubuntu-20.04 ] - python: [ '3.7', '3.8', '3.9', '3.10', '3.11' ] + python: [ '3.8', '3.9', '3.10', '3.11' ] variant: [ "py", "py-images" ] include: - os: macOS-11 diff --git a/docs/changelog.rst b/docs/changelog.rst index c384f4d..a750804 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog --------- +v0.16.0 +~~~~~~~ + +* **Breaking** Drop support for Python 3.7. + v0.15.1 ~~~~~~~~~~ From 82045b441752442400ec82eede049c1cba1b4b8d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:23:50 +0200 Subject: [PATCH 131/192] Formatting --- docs/changelog.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index a750804..caa3827 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,12 +7,12 @@ v0.16.0 * **Breaking** Drop support for Python 3.7. v0.15.1 -~~~~~~~~~~ +~~~~~~~ * Add missing dependency to release script. v0.15.0 -~~~~~~~~~~ +~~~~~~~ * **Breaking** Dropped support for Python 3.6 and 3.7. * Added support for Python 3.11. From ec3ae0901fa40ff9e8252633f7d0253ff2353f69 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:24:40 +0200 Subject: [PATCH 132/192] Remove meaningless comments --- barcode/writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index d6cc731..947e9ce 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -31,7 +31,7 @@ class Callbacks(TypedDict): import ImageFont except ImportError: try: - from PIL import Image # lint:ok + from PIL import Image from PIL import ImageDraw from PIL import ImageFont except ImportError: @@ -39,7 +39,7 @@ class Callbacks(TypedDict): log = logging.getLogger("pyBarcode") log.info("Pillow not found. Image output disabled") - Image = ImageDraw = ImageFont = None # lint:ok + Image = ImageDraw = ImageFont = None def mm2px(mm, dpi=300): From 909f03751f9aa624ecd3979eb8121a7715b7d7a5 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:43:58 +0200 Subject: [PATCH 133/192] Add a lot more type hints --- barcode/__init__.py | 10 ++++++---- barcode/base.py | 21 +++++++++++++-------- barcode/pybarcode.py | 10 ++++++---- barcode/writer.py | 13 ++++++++----- tests/test_builds.py | 4 ++-- tests/test_checksums.py | 18 +++++++++--------- tests/test_init.py | 10 +++++----- tests/test_manually.py | 6 +++--- tests/test_writers.py | 14 +++++++++----- 9 files changed, 61 insertions(+), 45 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 5568d04..602b61d 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -30,6 +30,8 @@ if TYPE_CHECKING: import os + from barcode.writer import BaseWriter + __BARCODE_MAP = { "ean8": EAN8, "ean8-guard": EAN8_GUARD, @@ -62,7 +64,7 @@ def get( name: str, code: str | None = None, - writer=None, + writer: BaseWriter | None = None, options: dict | None = None, ): """Helper method for getting a generator or even a generated code. @@ -87,18 +89,18 @@ def get( return barcode -def get_class(name): +def get_class(name: str): return get_barcode(name) def generate( name: str, code: str, - writer=None, + writer: BaseWriter | None = None, output: str | (os.PathLike | (BinaryIO | None)) = None, writer_options: dict | None = None, text: str | None = None, -): +) -> str | None: """Shortcut to generate a barcode in one line. :param name: Name of the type of barcode to use. diff --git a/barcode/base.py b/barcode/base.py index 595c7ff..d5296ae 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -3,11 +3,15 @@ """ from __future__ import annotations +from typing import TYPE_CHECKING from typing import ClassVar from barcode.writer import BaseWriter from barcode.writer import SVGWriter +if TYPE_CHECKING: + from typing import BinaryIO + class Barcode: name = "" @@ -65,17 +69,18 @@ def save( return self.writer.save(filename, output) - def write(self, fp, options=None, text=None): + def write( + self, + fp: BinaryIO, + options: dict | None = None, + text: str | None = None, + ) -> None: """Renders the barcode and writes it to the file like object `fp`. - :parameters: - fp : File like object - Object to write the raw data in. - options : Dict - The same as in `self.render`. - text : str - Text to render under the barcode. + :param fp: Object to write the raw data in. + :param options: The same as in `self.render`. + :param text: Text to render under the barcode. """ output = self.render(options, text) self.writer.write(output, fp) diff --git a/barcode/pybarcode.py b/barcode/pybarcode.py index c20223c..35d69b0 100644 --- a/barcode/pybarcode.py +++ b/barcode/pybarcode.py @@ -5,13 +5,14 @@ import barcode from barcode.version import version +from barcode.writer import BaseWriter from barcode.writer import ImageWriter from barcode.writer import SVGWriter IMG_FORMATS = ("BMP", "GIF", "JPEG", "MSP", "PCX", "PNG", "TIFF", "XBM") -def list_types(args, parser=None): +def list_types(args, parser=None) -> None: print("\npython-barcode available barcode formats:") print(", ".join(barcode.PROVIDED_BARCODES)) print("\n") @@ -24,7 +25,7 @@ def list_types(args, parser=None): print("\n") -def create_barcode(args, parser): +def create_barcode(args, parser) -> None: args.type = args.type.upper() if args.type != "SVG" and args.type not in IMG_FORMATS: parser.error(f"Unknown type {args.type}. Try list action for available types.") @@ -34,8 +35,9 @@ def create_barcode(args, parser): f"Unknown barcode {args.barcode}. Try list action for available barcodes." ) if args.type != "SVG": + assert ImageWriter is not None opts = {"format": args.type} - writer = ImageWriter() + writer: BaseWriter = ImageWriter() else: opts = {"compress": args.compress} writer = SVGWriter() @@ -44,7 +46,7 @@ def create_barcode(args, parser): print(f"New barcode saved as {name}.") -def main(): +def main() -> None: msg = [] if ImageWriter is None: msg.append("Image output disabled (Pillow not found), --type option disabled.") diff --git a/barcode/writer.py b/barcode/writer.py index 947e9ce..4690c8c 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -308,6 +308,9 @@ def render(self, code): return self._callbacks["finish"]() + def write(self, content, fp: BinaryIO) -> None: + raise NotImplementedError + class SVGWriter(BaseWriter): def __init__(self) -> None: @@ -400,7 +403,7 @@ def save(self, filename, output): f.write(output) return _filename - def write(self, content, fp: BinaryIO): + def write(self, content, fp: BinaryIO) -> None: """Write `content` into a file-like object. Content should be a barcode rendered by this writer. @@ -409,7 +412,7 @@ def write(self, content, fp: BinaryIO): if Image is None: - ImageWriter = None + ImageWriter: type | None = None else: class ImageWriter(BaseWriter): # type: ignore[no-redef] @@ -463,15 +466,15 @@ def _paint_text(self, xpos, ypos): ) ypos += pt2mm(self.font_size) / 2 + self.text_line_distance - def _finish(self): + def _finish(self) -> Image: return self._image - def save(self, filename, output): + def save(self, filename: str, output) -> str: filename = f"{filename}.{self.format.lower()}" output.save(filename, self.format.upper()) return filename - def write(self, content, fp: BinaryIO): + def write(self, content, fp: BinaryIO) -> None: """Write `content` into a file-like object. Content should be a barcode rendered by this writer. diff --git a/tests/test_builds.py b/tests/test_builds.py index 04365fc..aac11eb 100755 --- a/tests/test_builds.py +++ b/tests/test_builds.py @@ -3,14 +3,14 @@ from barcode import get_barcode -def test_ean8_builds(): +def test_ean8_builds() -> None: ref = "1010100011000110100100110101111010101000100100010011100101001000101" ean = get_barcode("ean8", "40267708") bc = ean.build() assert ref == bc[0] -def test_ean8_builds_with_longer_bars(): +def test_ean8_builds_with_longer_bars() -> None: ref = "G0G01000110001101001001101011110G0G01000100100010011100101001000G0G" ean = get_barcode("ean8", "40267708", options={"guardbar": True}) bc = ean.build() diff --git a/tests/test_checksums.py b/tests/test_checksums.py index 19d7c92..d79c206 100755 --- a/tests/test_checksums.py +++ b/tests/test_checksums.py @@ -3,46 +3,46 @@ from barcode import get_barcode -def test_code39_checksum(): +def test_code39_checksum() -> None: code39 = get_barcode("code39", "Code39") assert code39.get_fullcode() == "CODE39W" -def test_pzn_checksum(): +def test_pzn_checksum() -> None: pzn = get_barcode("pzn", "103940") assert pzn.get_fullcode() == "PZN-1039406" -def test_ean13_checksum(): +def test_ean13_checksum() -> None: ean = get_barcode("ean13", "400614457735") assert ean.get_fullcode() == "4006144577350" -def test_ean8_checksum(): +def test_ean8_checksum() -> None: ean = get_barcode("ean8", "6032299") assert ean.get_fullcode() == "60322999" -def test_jan_checksum(): +def test_jan_checksum() -> None: jan = get_barcode("jan", "491400614457") assert jan.get_fullcode() == "4914006144575" -def test_ean14_checksum(): +def test_ean14_checksum() -> None: ean = get_barcode("ean14", "1234567891258") assert ean.get_fullcode() == "12345678912589" -def test_isbn10_checksum(): +def test_isbn10_checksum() -> None: isbn = get_barcode("isbn10", "376926085") assert isbn.isbn10 == "3769260856" -def test_isbn13_checksum(): +def test_isbn13_checksum() -> None: isbn = get_barcode("isbn13", "978376926085") assert isbn.get_fullcode() == "9783769260854" -def test_gs1_128_checksum(): +def test_gs1_128_checksum() -> None: gs1_128 = get_barcode("gs1_128", "00376401856400470087") assert gs1_128.get_fullcode() == "00376401856400470087" diff --git a/tests/test_init.py b/tests/test_init.py index e031e85..b78703a 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -12,17 +12,17 @@ TESTPATH = os.path.join(PATH, "test_outputs") -def test_generate_without_output(): +def test_generate_without_output() -> None: with pytest.raises(TypeError, match="'output' cannot be None"): barcode.generate("ean13", "123455559121112") -def test_generate_with_file(): +def test_generate_with_file() -> None: with open(os.path.join(TESTPATH, "generate_with_file.jpeg"), "wb") as f: barcode.generate("ean13", "123455559121112", output=f) -def test_generate_with_filepath(): +def test_generate_with_filepath() -> None: # FIXME: extension is added to the filepath even if you include it. rv = barcode.generate( "ean13", @@ -32,12 +32,12 @@ def test_generate_with_filepath(): assert rv == os.path.abspath(os.path.join(TESTPATH, "generate_with_filepath.svg")) -def test_generate_with_file_and_writer(): +def test_generate_with_file_and_writer() -> None: with open(os.path.join(TESTPATH, "generate_with_file_and_writer.jpeg"), "wb") as f: barcode.generate("ean13", "123455559121112", output=f, writer=SVGWriter()) -def test_generate_with_bytesio(): +def test_generate_with_bytesio() -> None: bio = BytesIO() barcode.generate("ean13", "123455559121112", output=bio) # XXX: File is not 100% deterministic; needs to be addressed at some point. diff --git a/tests/test_manually.py b/tests/test_manually.py index 7759c84..323431e 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -50,15 +50,15 @@ ) -def test_generating_barcodes(): +def test_generating_barcodes() -> None: os.makedirs(TESTPATH, exist_ok=True) objects = [] - def append(x, y): + def append(x, y) -> None: objects.append(OBJECTS.format(filename=x, name=y)) - def append_img(x, y): + def append_img(x, y) -> None: objects.append(IMAGES.format(filename=x, name=y)) options = {} diff --git a/tests/test_writers.py b/tests/test_writers.py index 0e49f04..27454bb 100644 --- a/tests/test_writers.py +++ b/tests/test_writers.py @@ -10,16 +10,20 @@ PATH = os.path.dirname(os.path.abspath(__file__)) TESTPATH = os.path.join(PATH, "test_outputs") -if ImageWriter: +if ImageWriter is not None: + + def test_saving_image_to_byteio() -> None: + assert ImageWriter is not None # workaround for mypy - def test_saving_image_to_byteio(): rv = BytesIO() EAN13(str(100000902922), writer=ImageWriter()).write(rv) with open(f"{TESTPATH}/somefile.jpeg", "wb") as f: EAN13("100000011111", writer=ImageWriter()).write(f) - def test_saving_rgba_image(): + def test_saving_rgba_image() -> None: + assert ImageWriter is not None # workaround for mypy + rv = BytesIO() EAN13(str(100000902922), writer=ImageWriter()).write(rv) @@ -31,7 +35,7 @@ def test_saving_rgba_image(): ) -def test_saving_svg_to_byteio(): +def test_saving_svg_to_byteio() -> None: rv = BytesIO() EAN13(str(100000902922), writer=SVGWriter()).write(rv) @@ -39,7 +43,7 @@ def test_saving_svg_to_byteio(): EAN13("100000011111", writer=SVGWriter()).write(f) -def test_saving_svg_to_byteio_with_guardbar(): +def test_saving_svg_to_byteio_with_guardbar() -> None: rv = BytesIO() EAN13(str(100000902922), writer=SVGWriter(), guardbar=True).write(rv) From 2bd614d65d4d2e2794b6004849cd03c55edc1725 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:56:35 +0200 Subject: [PATCH 134/192] Prefer unaliased name for PZN --- docs/supported-formats.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/supported-formats.rst b/docs/supported-formats.rst index 5dc8548..d0fe1d4 100644 --- a/docs/supported-formats.rst +++ b/docs/supported-formats.rst @@ -24,13 +24,13 @@ Code 128 .. autoclass:: barcode.codex.Code128 :members: -PZN7 (aka: PZN) +PZN (aka: PZN7) --------------- -.. inheritance-diagram:: barcode.codex.PZN7 +.. inheritance-diagram:: barcode.codex.PZN :parts: 1 -.. autoclass:: barcode.codex.PZN7 +.. autoclass:: barcode.codex.PZN :members: From 25677ec28936c9febf2d90d7cd0e27f43c4a80f2 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 14:57:18 +0200 Subject: [PATCH 135/192] Sort barcodes --- barcode/__init__.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 602b61d..8c54f8c 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -33,28 +33,28 @@ from barcode.writer import BaseWriter __BARCODE_MAP = { - "ean8": EAN8, - "ean8-guard": EAN8_GUARD, + "codabar": CODABAR, + "code128": Code128, + "code39": Code39, + "ean": EAN13, "ean13": EAN13, "ean13-guard": EAN13_GUARD, - "ean": EAN13, - "gtin": EAN14, "ean14": EAN14, - "jan": JAN, - "upc": UPCA, - "upca": UPCA, - "isbn": ISBN13, - "isbn13": ISBN13, + "ean8": EAN8, + "ean8-guard": EAN8_GUARD, "gs1": ISBN13, + "gs1_128": Gs1_128, + "gtin": EAN14, + "isbn": ISBN13, "isbn10": ISBN10, + "isbn13": ISBN13, "issn": ISSN, - "code39": Code39, - "pzn": PZN, - "code128": Code128, "itf": ITF, - "gs1_128": Gs1_128, - "codabar": CODABAR, + "jan": JAN, "nw-7": CODABAR, + "pzn": PZN, + "upc": UPCA, + "upca": UPCA, } PROVIDED_BARCODES = list(__BARCODE_MAP) From b587602807ea572782b1189187e911e10e8a7068 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 15:04:37 +0200 Subject: [PATCH 136/192] Add some missing barcodes to the documentation Fixes: https://github.com/WhyNotHugo/python-barcode/issues/154 Fixes: https://github.com/WhyNotHugo/python-barcode/issues/2 --- barcode/ean.py | 4 +++ docs/supported-formats.rst | 57 +++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index 94420d2..fbfd79c 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -124,6 +124,8 @@ def render(self, writer_options=None, text=None): class EuropeanArticleNumber13WithGuard(EuropeanArticleNumber13): + """A shortcut to EAN-13 with ``guardbar=True``.""" + name = "EAN-13 with guards" def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None: @@ -188,6 +190,8 @@ def get_fullcode(self): class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8): + """A shortcut to EAN-8 with ``guardbar=True``.""" + name = "EAN-8 with guards" def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None: diff --git a/docs/supported-formats.rst b/docs/supported-formats.rst index d0fe1d4..20d8601 100644 --- a/docs/supported-formats.rst +++ b/docs/supported-formats.rst @@ -4,6 +4,15 @@ Supported Formats The following are the supported barcode formats. PRs for other code formats are welcome! +Codabar +------- + +.. inheritance-diagram:: barcode.codabar.CODABAR + :parts: 1 + +.. autoclass:: barcode.codabar.CODABAR + :members: + Code 39 ------- @@ -33,25 +42,39 @@ PZN (aka: PZN7) .. autoclass:: barcode.codex.PZN :members: - EAN-13 ------ -.. inheritance-diagram:: barcode.ean.EuropeanArticleNumber13 +.. inheritance-diagram:: barcode.ean.EuropeanArticleNumber13WithGuard :parts: 1 .. autoclass:: barcode.ean.EuropeanArticleNumber13 :members: +.. autoclass:: barcode.ean.EuropeanArticleNumber13WithGuard + :members: + EAN-8 ----- -.. inheritance-diagram:: barcode.ean.EuropeanArticleNumber8 +.. inheritance-diagram:: barcode.ean.EuropeanArticleNumber8WithGuard :parts: 1 .. autoclass:: barcode.ean.EuropeanArticleNumber8 :members: +.. autoclass:: barcode.ean.EuropeanArticleNumber8WithGuard + :members: + +EAN-14 +------ + +.. inheritance-diagram:: barcode.ean.EuropeanArticleNumber14 + :parts: 1 + +.. autoclass:: barcode.ean.EuropeanArticleNumber14 + :members: + JAN --- @@ -61,8 +84,8 @@ JAN .. autoclass:: barcode.ean.JapanArticleNumber :members: -ISBN-13 -------- +ISBN-13 (aka: GS1, ISBN) +------------------------ .. inheritance-diagram:: barcode.isxn.InternationalStandardBookNumber13 :parts: 1 @@ -97,8 +120,8 @@ UPC-A .. autoclass:: barcode.upc.UniversalProductCodeA :members: -EAN14 ------ +EAN14 (aka: GTIN) +----------------- .. inheritance-diagram:: barcode.ean.EuropeanArticleNumber14 :parts: 1 @@ -116,3 +139,23 @@ GS1-128 .. autoclass:: barcode.codex.Gs1_128 :members: + +ITF +--- + +.. versionadded:: 0.8.0 + +.. inheritance-diagram:: barcode.itf.ITF + :parts: 1 + +.. autoclass:: barcode.itf.ITF + :members: + +UPCA (aka UPC) +-------------- + +.. inheritance-diagram:: barcode.upc.UPCA + :parts: 1 + +.. autoclass:: barcode.upc.UPCA + :members: From 73afc4a97a9299c88f11be622955ef246a109e49 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 15:22:06 +0200 Subject: [PATCH 137/192] Add some more type hints --- barcode/writer.py | 83 ++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 4690c8c..613c394 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -11,6 +11,8 @@ from barcode.version import version if TYPE_CHECKING: + from typing import Generator + from typing import Literal class InternalText(TypedDict): start: list @@ -77,20 +79,19 @@ def create_svg_object(with_doctype=False): class BaseWriter: """Baseclass for all writers. - Initializes the basic writer options. Childclasses can add more - attributes and can set them directly or using - `self.set_options(option=value)`. + Initializes the basic writer options. Child classes can add more attributes and can + set them directly or using ``self.set_options(option=value)``. :param initialize: Callback for initializing the inheriting writer. - Is called: `callback_initialize(raw_code)` + Is called: ``callback_initialize(raw_code)`` :param paint_module: Callback for painting one barcode module. - Is called: `callback_paint_module(xpos, ypos, width, color)` + Is called: ``callback_paint_module(xpos, ypos, width, color)`` :param paint_text: Callback for painting the text under the barcode. - Is called: `callback_paint_text(xpos, ypos)` using `self.text` + Is called: ``callback_paint_text(xpos, ypos)`` using `self.text` as text. :param finish: Callback for doing something with the completely rendered - output. Is called: `return callback_finish()` and must return the + output. Is called: ``return callback_finish()`` and must return the rendered output. """ @@ -125,17 +126,13 @@ def __init__( self.margin_top = 1 self.margin_bottom = 1 - def calculate_size(self, modules_per_line, number_of_lines): + def calculate_size(self, modules_per_line: int, number_of_lines: int) -> tuple: """Calculates the size of the barcode in pixel. - :parameters: - modules_per_line : Integer - Number of modules in one line. - number_of_lines : Integer - Number of lines of the barcode. + :param modules_per_line: Number of modules in one line. + :param number_of_lines: Number of lines of the barcode. :returns: Width and height of the barcode in pixel. - :rtype: Tuple """ width = 2 * self.quiet_zone + modules_per_line * self.module_width height = ( @@ -149,49 +146,41 @@ def calculate_size(self, modules_per_line, number_of_lines): height += self.text_line_distance * (number_of_text_lines - 1) return width, height - def save(self, filename, output): + def save(self, filename: str, output) -> str: """Saves the rendered output to `filename`. - :parameters: - filename : String - Filename without extension. - output : String - The rendered output. + :param filename: Filename without extension. + :param output: The rendered output. :returns: The full filename with extension. - :rtype: String """ raise NotImplementedError - def register_callback(self, action, callback): - """Register one of the three callbacks if not given at instance - creation. + def register_callback( + self, + action: Literal["initialize", "paint_module", "paint_text", "finish"], + callback: Callable, + ) -> None: + """Register one of the three callbacks if not given at instance creation. - :parameters: - action : String - One of 'initialize', 'paint_module', 'paint_text', 'finish'. - callback : Function - The callback function for the given action. + :param action: One of 'initialize', 'paint_module', 'paint_text', 'finish'. + :param callback: The callback function for the given action. """ self._callbacks[action] = callback - def set_options(self, options): + def set_options(self, options: dict) -> None: """Sets the given options as instance attributes (only if they are known). - :parameters: - options : Dict - All known instance attributes and more if the childclass - has defined them before this call. - - :rtype: None + :param options: All known instance attributes and more if the child class + has defined them before this call. """ for key, val in options.items(): key = key.lstrip("_") if hasattr(self, key): setattr(self, key, val) - def packed(self, line): + def packed(self, line: str) -> Generator[tuple[int, float], str, None]: """ Pack line to list give better gfx result, otherwise in can result in aliasing gaps @@ -199,10 +188,7 @@ def packed(self, line): This method will yield a sequence of pairs (width, height_factor). - :parameters: - line: String - A string matching the writer spec - (only contain 0 or 1 or G). + :param line: A string matching the writer spec (only contain 0 or 1 or G). """ line += " " c = 1 @@ -314,8 +300,11 @@ def write(self, content, fp: BinaryIO) -> None: class SVGWriter(BaseWriter): def __init__(self) -> None: - BaseWriter.__init__( - self, self._init, self._create_module, self._create_text, self._finish + super().__init__( + self._init, + self._create_module, + self._create_text, + self._finish, ) self.compress = False self.with_doctype = True @@ -391,12 +380,12 @@ def _finish(self): indent=4 * " ", newl=os.linesep, encoding="UTF-8" ) - def save(self, filename, output): + def save(self, filename: str, output) -> str: if self.compress: _filename = f"{filename}.svgz" - f = gzip.open(_filename, "wb") - f.write(output) - f.close() + with gzip.open(_filename, "wb") as f: + f.write(output) + f.close() else: _filename = f"{filename}.svg" with open(_filename, "wb") as f: From 8ed1aa4c6bfc67c00fcd9b72051920f949fcb04c Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 15:22:18 +0200 Subject: [PATCH 138/192] Minor documentation improvements --- docs/writers.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/writers.rst b/docs/writers.rst index 08ca570..c5399c1 100644 --- a/docs/writers.rst +++ b/docs/writers.rst @@ -57,6 +57,14 @@ be set). Some barcode classes change the above defaults to fit in some kind of specification. +BaseWriter +---------- + +Both ``ImageWriter`` and ``SVGWriter`` are subclasses of ``BaseWriter``: + +.. autoclass:: barcode.writer.BaseWriter + :members: + SVGWriter --------- @@ -90,7 +98,7 @@ In addition to the common writer options you can give the following special opti Custom writers -------------- -It's possible to create your own writer by inheriting from `barcode.writer.BaseWriter`. +It's possible to create your own writer by inheriting from ``barcode.writer.BaseWriter``. In your ``__init__`` method call BaseWriter's ``__init__`` and give your callbacks for: @@ -122,5 +130,5 @@ Saving a compressed SVG (SVGZ): >>> filename 'ean13.svgz' -Now you have ean13.svg and the compressed ean13.svgz in your current +Now you have ``ean13.svg`` and the compressed ``ean13.svgz`` in your current working directory. Open it and see the result. From 8e09fc73bd313018de3b42167874b5e7be3078f3 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 15:27:43 +0200 Subject: [PATCH 139/192] Make image DPI configurable Fixes: https://github.com/WhyNotHugo/python-barcode/issues/206 --- barcode/writer.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index 613c394..ae5bb54 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -44,7 +44,7 @@ class Callbacks(TypedDict): Image = ImageDraw = ImageFont = None -def mm2px(mm, dpi=300): +def mm2px(mm, dpi: int): return (mm * dpi) / 25.4 @@ -409,7 +409,7 @@ class ImageWriter(BaseWriter): # type: ignore[no-redef] mode: str dpi: int - def __init__(self, format="PNG", mode="RGB") -> None: + def __init__(self, format="PNG", mode="RGB", dpi=300) -> None: """Initialise a new write instance. :params format: The file format for the generated image. This parameter can @@ -417,12 +417,15 @@ def __init__(self, format="PNG", mode="RGB") -> None: :params mode: The colour-mode for the generated image. Set this to RGBA if you wish to use colours with transparency. """ - BaseWriter.__init__( - self, self._init, self._paint_module, self._paint_text, self._finish + super().__init__( + self._init, + self._paint_module, + self._paint_text, + self._finish, ) self.format = format self.mode = mode - self.dpi = 300 + self.dpi = dpi self._image = None self._draw = None From 35e4343a38b8111983292b71c299b73776316cb1 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 16:41:14 +0200 Subject: [PATCH 140/192] Include instructions on how to build the docs --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 8e1eb6c..c7f61bb 100644 --- a/README.rst +++ b/README.rst @@ -37,6 +37,8 @@ Documentation Full documentation is published at http://python-barcode.rtfd.io/ +You can build the documentation locally using ``make -C docs html``. + Licence ------- From c5fac0acc0336722bf5cf9822a4a9c79b8ef3573 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 20 Sep 2023 16:41:25 +0200 Subject: [PATCH 141/192] A few more type hints --- barcode/ean.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index fbfd79c..c532684 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -72,16 +72,15 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=False) -> None: def __str__(self) -> str: return self.ean - def get_fullcode(self): + def get_fullcode(self) -> str: if self.guardbar: return self.ean[0] + " " + self.ean[1:7] + " " + self.ean[7:] + " >" return self.ean - def calculate_checksum(self): + def calculate_checksum(self) -> int: """Calculates the checksum for EAN13-Code. - :returns: The checksum for `self.ean`. - :rtype: Integer + :returns: The checksum for ``self.ean``. """ def sum_(x, y): From da2c542ac1e3ab632d03cdf47d132934ab6b2d03 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 21 Sep 2023 17:32:53 +0200 Subject: [PATCH 142/192] Add tests for EAN checksum calculation Closes: https://github.com/WhyNotHugo/python-barcode/pull/215 --- tests/test_ean.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/test_ean.py diff --git a/tests/test_ean.py b/tests/test_ean.py new file mode 100644 index 0000000..817b5bd --- /dev/null +++ b/tests/test_ean.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from barcode.ean import EAN13 + + +def test_ean_checksum() -> None: + ean = EAN13("842169142322") # input has 12 digits + assert ean.calculate_checksum() == 0 + + ean = EAN13("8421691423220") # input has 13 digits + assert ean.calculate_checksum() == 0 From f2429610bb43fc64bdcd043937f0fd0160f9be51 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 21 Sep 2023 17:36:53 +0200 Subject: [PATCH 143/192] Fix inconsistent checksum calculation for EAN14 --- barcode/ean.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index afae385..a4547cf 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -212,18 +212,19 @@ class EuropeanArticleNumber14(EuropeanArticleNumber13): name = "EAN-14" digits = 13 - def calculate_checksum(self): + def calculate_checksum(self) -> int: """Calculates the checksum for EAN13-Code. - :returns: The checksum for `self.ean`. - :rtype: Integer + :returns: The checksum for ``self.ean``. """ def sum_(x, y): return int(x) + int(y) - evensum = reduce(sum_, self.ean[::2]) - oddsum = reduce(sum_, self.ean[1::2]) + ean_without_checksum = self.ean[: self.digits] + + evensum = reduce(sum_, ean_without_checksum[::2]) + oddsum = reduce(sum_, ean_without_checksum[1::2]) return (10 - (((evensum * 3) + oddsum) % 10)) % 10 From d9a23a9110d0d4130753c5fcda78f090b7b72ee5 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 21 Sep 2023 17:39:35 +0200 Subject: [PATCH 144/192] Add missing changelog entries --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index caa3827..37ce142 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,11 @@ v0.16.0 ~~~~~~~ * **Breaking** Drop support for Python 3.7. +* Make image DPI configurable. +* Fixed inconsistent checksum calculation when calculating the checksum + multiple times for EAN barcodes. +* Update the documentation with some barcodes that were not previously + documented. v0.15.1 ~~~~~~~ From 8def72890ff9fe5f887ebbba458a9a5787b97f8b Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 25 Sep 2023 14:12:02 +0200 Subject: [PATCH 145/192] Update copyright year --- LICENCE | 2 +- docs/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENCE b/LICENCE index 159bbe8..dc22edf 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2017-2020 Hugo Osvaldo Barrera , et al +Copyright (c) 2017-2023 Hugo Osvaldo Barrera , et al Copyright (c) 2010-2013 Thorsten Weimann Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/docs/conf.py b/docs/conf.py index e517fed..81e3338 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ # -- Project information ----------------------------------------------------- project = "python-barcode" -copyright = "2020, Hugo Osvaldo Barrera, et al" +copyright = "2017-2023, Hugo Osvaldo Barrera, et al" author = "Hugo Osvaldo Barrera, et al" # The short X.Y version. From 23e4a67986db0d31acc0c0c2b35b0b92b5b9a250 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 25 Sep 2023 16:23:49 +0200 Subject: [PATCH 146/192] Ignore type checking lines in coverage report --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7487714..d3130f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,3 +44,8 @@ target-version = "py38" [tool.ruff.isort] force-single-line = true required-imports = ["from __future__ import annotations"] + +[tool.coverage.report] +exclude_lines = [ + "if TYPE_CHECKING:", +] From 3a820361bf993306cb6d4c9ed11f7ed342263bdf Mon Sep 17 00:00:00 2001 From: fra87 <5077242+fra87@users.noreply.github.com> Date: Tue, 3 Oct 2023 12:16:27 +0200 Subject: [PATCH 147/192] Allow SVGWriter to avoid having the background element (#211) Modify SVGWriter to be able to avoid having the background element on SVG images If you set the "background" to None or (255,255,255,0) (i.e. transparent) in the writer_options parameter when calling render then the background will not be written in the SVG (instead of having a rectangle). This is because transparent background does not seem to work in the current library (it is black) --- barcode/writer.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/barcode/writer.py b/barcode/writer.py index ae5bb54..e591cd3 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -329,14 +329,15 @@ def _init(self, code): attributes = {"id": "barcode_group"} _set_attributes(group, **attributes) self._group = self._root.appendChild(group) - background = self._document.createElement("rect") - attributes = { - "width": "100%", - "height": "100%", - "style": f"fill:{self.background}", - } - _set_attributes(background, **attributes) - self._group.appendChild(background) + if self.background is not None: + background = self._document.createElement("rect") + attributes = { + "width": "100%", + "height": "100%", + "style": f"fill:{self.background}", + } + _set_attributes(background, **attributes) + self._group.appendChild(background) def _create_module(self, xpos, ypos, width, color): # Background rect has been provided already, so skipping "spaces" From bf7f1b67cae7f5c7748ff33a2023eba6ef42123e Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 3 Oct 2023 12:18:00 +0200 Subject: [PATCH 148/192] Add changelog entry for latest changes See: https://github.com/WhyNotHugo/python-barcode/pull/211 --- docs/changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 37ce142..9f7e7f2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -10,6 +10,8 @@ v0.16.0 multiple times for EAN barcodes. * Update the documentation with some barcodes that were not previously documented. +* Specifying ``None`` as a background for the ``SVGWriter``, no background is + included resulting in a transparent background. v0.15.1 ~~~~~~~ From 6591846dae95870bb4542a397a34bb74fa18ea89 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 12 Oct 2023 13:20:43 +0200 Subject: [PATCH 149/192] Update funding link --- docs/contents.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contents.rst b/docs/contents.rst index 1ddf7c4..de71562 100644 --- a/docs/contents.rst +++ b/docs/contents.rst @@ -36,7 +36,7 @@ Issues and source code are all in `GitHub `_ for further +Donations are welcome. See `here `_ for further details. Licence From 12148e1d603a5c51a1925cf9db53aa5dfff95840 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 12:42:55 +0200 Subject: [PATCH 150/192] Skip rendering text when font size would be zero (#218) Refs #217 --- barcode/writer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/barcode/writer.py b/barcode/writer.py index e591cd3..27ca933 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -448,6 +448,8 @@ def _paint_module(self, xpos, ypos, width, color): def _paint_text(self, xpos, ypos): font_size = int(mm2px(pt2mm(self.font_size), self.dpi)) + if font_size <= 0: + return font = ImageFont.truetype(self.font_path, font_size) for subtext in self.text.split("\n"): pos = ( From c96ce2bea816e4ce15175a93d955638e1039899f Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:02:39 +0200 Subject: [PATCH 151/192] Renovate pre-commit tools --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9ec28d..845199c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,26 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/psf/black - rev: "23.7.0" + rev: "23.11.0" hooks: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.5.1' + rev: 'v1.7.1' hooks: - id: mypy - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.0.287' + rev: 'v0.1.6' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/netromdk/vermin - rev: v1.5.2 + rev: v1.6.0 hooks: - id: vermin args: ['-t=3.8-', '--violations'] From f3f2b3abd4d6b623397185f829a7f535e089ccef Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:04:10 +0200 Subject: [PATCH 152/192] Renovate GitHub Actions and use cache --- .github/workflows/publish.yml | 4 ++-- .github/workflows/tests.yml | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index eec74f7..baae952 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,8 +10,8 @@ jobs: runs-on: ubuntu-22.04 name: Publish package on PyPI steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: python-version: 3.7 - run: pip install build setuptools wheel build setuptools_scm diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 16a66eb..8b22ce8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,10 +20,14 @@ jobs: - os: windows-2019 name: python${{ matrix.python }} on ${{ matrix.os }} ${{ matrix.variant }} steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} + cache: pip + cache-dependency-path: | + pyproject.toml + setup.py - name: Install test dependency run: pip install tox - name: Run tests From 713c3c6bfa152788e50d8e75beb37d3b53ec6911 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:05:31 +0200 Subject: [PATCH 153/192] CI: renovate OS versions, test on Python 3.12 --- .github/workflows/tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8b22ce8..ab81429 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,17 +7,17 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04 ] - python: [ '3.8', '3.9', '3.10', '3.11' ] + os: [ ubuntu-22.04 ] + python: [ '3.8', '3.9', '3.10', '3.11', '3.12' ] variant: [ "py", "py-images" ] include: - - os: macOS-11 - python: "3.10" + - os: macOS-12 + python: "3.12" variant: py-images - - os: windows-2019 - python: "3.10" + - os: windows-2022 + python: "3.12" variant: py-images - - os: windows-2019 + - os: windows-2022 name: python${{ matrix.python }} on ${{ matrix.os }} ${{ matrix.variant }} steps: - uses: actions/checkout@v4 From 81563369fd66c7485ea1c3d244f0a3f2276c75af Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:06:03 +0200 Subject: [PATCH 154/192] Mark as officially supported on Python 3.12 --- README.rst | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c7f61bb..1901972 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.8 to 3.11. +Support Python 3.8 to 3.12. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/setup.py b/setup.py index 697ccb6..ef72f38 100755 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Multimedia :: Graphics", "Topic :: Software Development :: Libraries :: Python Modules", ], From 4ffc1b50ac29c8fb0ea1c0a0c711af0d73c19702 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:06:39 +0200 Subject: [PATCH 155/192] Fix CI status badge --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1901972..7231572 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ python-barcode ============== -.. image:: https://action-badges.now.sh/WhyNotHugo/python-barcode +.. image:: https://github.com/WhyNotHugo/python-barcode/actions/workflows/tests.yml/badge.svg :target: https://github.com/WhyNotHugo/python-barcode/actions :alt: CI status From b7d063c9344f5a4c5ec5efdaa22c1ddeb110b672 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:07:38 +0200 Subject: [PATCH 156/192] Use ruff-format for formatting --- .pre-commit-config.yaml | 5 +---- pyproject.toml | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 845199c..ec56193 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,10 +6,6 @@ repos: args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - - repo: https://github.com/psf/black - rev: "23.11.0" - hooks: - - id: black - repo: https://github.com/pre-commit/mirrors-mypy rev: 'v1.7.1' hooks: @@ -19,6 +15,7 @@ repos: hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format - repo: https://github.com/netromdk/vermin rev: v1.6.0 hooks: diff --git a/pyproject.toml b/pyproject.toml index d3130f8..3eaa7ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,9 +5,6 @@ requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] write_to = "barcode/version.py" version_scheme = "post-release" -[tool.black] -target-version = ['py38'] - [tool.ruff] select = [ "F", From c00208824d8cf370f3e84407d477af663648fa0e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 28 Nov 2023 13:59:34 +0200 Subject: [PATCH 157/192] Add changelog entry for #218 --- docs/changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9f7e7f2..12a1b56 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -12,6 +12,8 @@ v0.16.0 documented. * Specifying ``None`` as a background for the ``SVGWriter``, no background is included resulting in a transparent background. +* Do not paint text if its size would be zero, to avoid an "invalid ppem value" + error with newer versions of Pillow. v0.15.1 ~~~~~~~ From 841c8d36269bf6800ec3baa4d44c7ecb065146f7 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Sat, 9 Mar 2024 14:17:13 +0100 Subject: [PATCH 158/192] Update my email address --- LICENCE | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENCE b/LICENCE index dc22edf..92decff 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -Copyright (c) 2017-2023 Hugo Osvaldo Barrera , et al +Copyright (c) 2017-2023 Hugo Osvaldo Barrera , et al Copyright (c) 2010-2013 Thorsten Weimann Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/setup.py b/setup.py index ef72f38..6605f21 100755 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ url="https://github.com/WhyNotHugo/python-barcode", license="MIT", author="Hugo Osvaldo Barrera et al", - author_email="hugo@barrera.io", + author_email="hugo@whynothugo.nl", description=( "Create standard barcodes with Python. No external modules needed. " "(optional Pillow support included)." From 32b658b6179a307494035dfa582f8f54868cce2c Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Fri, 5 Apr 2024 13:58:41 +0200 Subject: [PATCH 159/192] Explain how to report issues --- CONTRIBUTING.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..69e83c2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Contributing to python-barcode + +## Bug reports + +Please make sure that you take the following steps when reporting issues: + +Ensure that you are using the latest version. You bug may already have been +fixed. + +Search existing issues to ensure that it was not previously included. Be sure to +search closed issues too, your issue may have been fixed recently. + +In case that the generated output is not correct, please mention: + +- What input did you provide? +- What output did you get? +- What output do you think you should have obtained? + +In case of crashes, please copy the full stack trace and **at least** the line +from your code that reproduces the crash. + +Trying to understand and debug any error is a lot easier if you provide a small +portion of code that I can run myself to reproduce the error. From 9a5d6882814a66a3d538769045d2e5d3484de7e5 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 8 Apr 2024 15:42:34 +0200 Subject: [PATCH 160/192] Tidy up docstring format --- barcode/ean.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index a4547cf..8158413 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -32,18 +32,18 @@ class EuropeanArticleNumber13(Barcode): """Initializes EAN13 object. - :parameters: - ean : String - The ean number as string. - writer : barcode.writer Instance - The writer to render the barcode (default: SVGWriter). + :param ean: The ean number as string. + :param writer: The writer to render the barcode (default: SVGWriter). + :param no_checksum: Don't calculate the checksum. Use the provided input instead. """ name = "EAN-13" digits = 12 - def __init__(self, ean, writer=None, no_checksum=False, guardbar=False) -> None: + def __init__( + self, ean: str, writer=None, no_checksum=False, guardbar=False + ) -> None: ean = ean[: self.digits] if not ean.isdigit(): raise IllegalCharacterError("EAN code can only contain numbers.") @@ -202,11 +202,9 @@ def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None: class EuropeanArticleNumber14(EuropeanArticleNumber13): """Represents an EAN-14 barcode. See EAN13's __init__ for details. - :parameters: - ean : String - The ean number as string. - writer : barcode.writer Instance - The writer to render the barcode (default: SVGWriter). + :param ean: The ean number as string. + :param writer: The writer to render the barcode (default: SVGWriter). + :param no_checksum: Don't calculate the checksum. Use the provided input instead. """ name = "EAN-14" From bb2175ae43ff5549e73e891c9f8acb41d97466e4 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 8 Apr 2024 15:55:43 +0200 Subject: [PATCH 161/192] Improve test for EAN13 --- tests/test_ean.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/test_ean.py b/tests/test_ean.py index 817b5bd..0825c82 100644 --- a/tests/test_ean.py +++ b/tests/test_ean.py @@ -3,9 +3,19 @@ from barcode.ean import EAN13 -def test_ean_checksum() -> None: - ean = EAN13("842169142322") # input has 12 digits - assert ean.calculate_checksum() == 0 +def test_ean_checksum_generated() -> None: + ean = EAN13("842167143322") # input has 12 digits + assert ean.calculate_checksum() == 5 + assert ean.ean == "8421671433225" - ean = EAN13("8421691423220") # input has 13 digits - assert ean.calculate_checksum() == 0 + +def test_ean_checksum_zeroed() -> None: + ean = EAN13("842167143322", no_checksum=True) # input has 12 digits + assert ean.calculate_checksum() == 5 + assert ean.ean == "8421671433220" + + +def test_ean_checksum_supplied_and_generated() -> None: + ean = EAN13("8421671433225") # input has 13 digits + assert ean.calculate_checksum() == 5 + assert ean.ean == "8421671433225" From 4c0c3a41c5368392bb8cdb5549d71285172c6114 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 8 Apr 2024 16:11:44 +0200 Subject: [PATCH 162/192] Fix type-soup when calculating checksums --- barcode/ean.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index 8158413..5c106fa 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -6,7 +6,6 @@ __docformat__ = "restructuredtext en" -from functools import reduce from barcode.base import Barcode from barcode.charsets import ean as _ean @@ -83,13 +82,10 @@ def calculate_checksum(self) -> int: :returns: The checksum for ``self.ean``. """ - def sum_(x, y): - return int(x) + int(y) - ean_without_checksum = self.ean[: self.digits] - evensum = reduce(sum_, ean_without_checksum[-2::-2]) - oddsum = reduce(sum_, ean_without_checksum[-1::-2]) + evensum = sum(int(x) for x in ean_without_checksum[-2::-2]) + oddsum = sum(int(x) for x in ean_without_checksum[-1::-2]) return (10 - ((evensum + oddsum * 3) % 10)) % 10 def build(self): @@ -216,13 +212,10 @@ def calculate_checksum(self) -> int: :returns: The checksum for ``self.ean``. """ - def sum_(x, y): - return int(x) + int(y) - ean_without_checksum = self.ean[: self.digits] - evensum = reduce(sum_, ean_without_checksum[::2]) - oddsum = reduce(sum_, ean_without_checksum[1::2]) + evensum = sum(int(x) for x in ean_without_checksum[::2]) + oddsum = sum(int(x) for x in ean_without_checksum[1::2]) return (10 - (((evensum * 3) + oddsum) % 10)) % 10 From 06eb1a7b78212b5318f165bff85245718531c4e2 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 8 Apr 2024 16:26:52 +0200 Subject: [PATCH 163/192] Fix no_checksum for EAN13 The `ean` variable was shadowed early, so a custom 13th digit was lost when ignoring checksums. Fixes: https://github.com/WhyNotHugo/python-barcode/issues/43 --- barcode/ean.py | 47 +++++++++++++++++++++++++++-------------------- tests/test_ean.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index 5c106fa..028d20a 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -2,6 +2,7 @@ :Provided barcodes: EAN-14, EAN-13, EAN-8, JAN """ + from __future__ import annotations __docformat__ = "restructuredtext en" @@ -31,7 +32,7 @@ class EuropeanArticleNumber13(Barcode): """Initializes EAN13 object. - :param ean: The ean number as string. + :param ean: The ean number as string. If the value is too long, it is trimmed. :param writer: The writer to render the barcode (default: SVGWriter). :param no_checksum: Don't calculate the checksum. Use the provided input instead. """ @@ -43,21 +44,25 @@ class EuropeanArticleNumber13(Barcode): def __init__( self, ean: str, writer=None, no_checksum=False, guardbar=False ) -> None: - ean = ean[: self.digits] - if not ean.isdigit(): - raise IllegalCharacterError("EAN code can only contain numbers.") - if len(ean) != self.digits: + if not ean[: self.digits].isdigit(): + raise IllegalCharacterError(f"EAN code can only contain numbers {ean}.") + + if len(ean) < self.digits: raise NumberOfDigitsError( - f"EAN must have {self.digits} digits, not {len(ean)}." + f"EAN must have {self.digits} digits, received {len(ean)}." ) - self.ean = ean - # If no checksum + + base = ean[: self.digits] if no_checksum: - # Add a thirteen char if given in parameter, - # otherwise pad with zero - self.ean = f"{ean}{ean[self.digits] if len(ean) > self.digits else 0}" + # Use the thirteenth digit if given in parameter, otherwise pad with zero + if len(ean) > self.digits and ean[self.digits].isdigit(): + last = int(ean[self.digits]) + else: + last = 0 else: - self.ean = f"{ean}{self.calculate_checksum()}" + last = self.calculate_checksum(base) + + self.ean = f"{base}{last}" self.guardbar = guardbar if guardbar: @@ -76,13 +81,14 @@ def get_fullcode(self) -> str: return self.ean[0] + " " + self.ean[1:7] + " " + self.ean[7:] + " >" return self.ean - def calculate_checksum(self) -> int: - """Calculates the checksum for EAN13-Code. + def calculate_checksum(self, value: str | None = None) -> int: + """Calculates and returns the checksum for EAN13-Code. - :returns: The checksum for ``self.ean``. + Calculates the checksum for the supplied `value` (if any) or for this barcode's + internal ``self.ean`` property. """ - ean_without_checksum = self.ean[: self.digits] + ean_without_checksum = value or self.ean[: self.digits] evensum = sum(int(x) for x in ean_without_checksum[-2::-2]) oddsum = sum(int(x) for x in ean_without_checksum[-1::-2]) @@ -206,13 +212,14 @@ class EuropeanArticleNumber14(EuropeanArticleNumber13): name = "EAN-14" digits = 13 - def calculate_checksum(self) -> int: - """Calculates the checksum for EAN13-Code. + def calculate_checksum(self, value: str | None = None) -> int: + """Calculates and returns the checksum for EAN14-Code. - :returns: The checksum for ``self.ean``. + Calculates the checksum for the supplied `value` (if any) or for this barcode's + internal ``self.ean`` property. """ - ean_without_checksum = self.ean[: self.digits] + ean_without_checksum = value or self.ean[: self.digits] evensum = sum(int(x) for x in ean_without_checksum[::2]) oddsum = sum(int(x) for x in ean_without_checksum[1::2]) diff --git a/tests/test_ean.py b/tests/test_ean.py index 0825c82..95097de 100644 --- a/tests/test_ean.py +++ b/tests/test_ean.py @@ -1,5 +1,9 @@ from __future__ import annotations +import sys + +import pytest + from barcode.ean import EAN13 @@ -19,3 +23,31 @@ def test_ean_checksum_supplied_and_generated() -> None: ean = EAN13("8421671433225") # input has 13 digits assert ean.calculate_checksum() == 5 assert ean.ean == "8421671433225" + + +def test_ean_checksum_supplied_and_matching() -> None: + ean = EAN13("8421671433225", no_checksum=True) # input has 13 digits + assert ean.calculate_checksum() == 5 + assert ean.ean == "8421671433225" + + +def test_ean_checksum_supplied_and_different() -> None: + ean = EAN13("8421671433229", no_checksum=True) # input has 13 digits + assert ean.calculate_checksum() == 5 + assert ean.ean == "8421671433229" + + +def test_ean_checksum_generated_placeholder() -> None: + ean = EAN13("977114487500X") # input has 13 digits + assert ean.calculate_checksum() == 7 + assert ean.ean == "9771144875007" + + +@pytest.mark.skipif(sys.platform == "win32", reason="no /dev/null") +def test_ean_checksum_supplied_placeholder() -> None: + ean = EAN13("977114487500X", no_checksum=True) # input has 13 digits + assert ean.calculate_checksum() == 7 + assert ean.ean == "9771144875000" + + with open("/dev/null", "wb") as f: + ean.write(f) From af2f00ddebf3b8612a9cb42392e51bdbde094dbc Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 20:49:52 +0200 Subject: [PATCH 164/192] Parametrize test_generating_barcodes --- tests/test_manually.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/test_manually.py b/tests/test_manually.py index 323431e..9a877dc 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -3,6 +3,9 @@ import codecs import os +from typing import Iterator + +import pytest from barcode import get_barcode from barcode import get_barcode_class @@ -50,10 +53,13 @@ ) -def test_generating_barcodes() -> None: +@pytest.mark.parametrize(("codename", "code"), TESTCODES) +def test_generating_barcodes( + codename: str, code: str, gather_image_elements_into_html: list[str] +) -> None: os.makedirs(TESTPATH, exist_ok=True) - objects = [] + objects = gather_image_elements_into_html def append(x, y) -> None: objects.append(OBJECTS.format(filename=x, name=y)) @@ -62,7 +68,7 @@ def append_img(x, y) -> None: objects.append(IMAGES.format(filename=x, name=y)) options = {} - for codename, code in TESTCODES: + if True: bcode = get_barcode(codename, code) if codename.startswith("i"): options["center_text"] = False @@ -83,9 +89,16 @@ def append_img(x, y) -> None: append_img(os.path.basename(filename), bcode.name) else: objects.append(NO_PIL) + + +@pytest.fixture(scope="module") +def gather_image_elements_into_html() -> Iterator[list[str]]: + image_elements: list[str] = [] + yield image_elements + # Save htmlfile with all objects with codecs.open(HTMLFILE, "w", encoding="utf-8") as f: - obj = "\n".join(objects) + obj = "\n".join(image_elements) f.write(HTML.format(version=version, body=obj)) print(f"\nNow open {HTMLFILE} in your browser.") From 5d57843e7078f3b613b42bc35c3805a5b66c7bb3 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 20:50:43 +0200 Subject: [PATCH 165/192] Dedent --- tests/test_manually.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/test_manually.py b/tests/test_manually.py index 9a877dc..18876ce 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -68,27 +68,26 @@ def append_img(x, y) -> None: objects.append(IMAGES.format(filename=x, name=y)) options = {} - if True: - bcode = get_barcode(codename, code) + bcode = get_barcode(codename, code) + if codename.startswith("i"): + options["center_text"] = False + else: + options["center_text"] = True + filename = bcode.save(os.path.join(TESTPATH, codename), options=options) + print(f"Code: {bcode.name}, Input: {code}, Output: {bcode.get_fullcode()}") + append(os.path.basename(filename), bcode.name) + if ImageWriter is not None: + bcodec = get_barcode_class(codename) + bcode = bcodec(code, writer=ImageWriter()) + opts = {} if codename.startswith("i"): - options["center_text"] = False + opts["center_text"] = False else: - options["center_text"] = True - filename = bcode.save(os.path.join(TESTPATH, codename), options=options) - print(f"Code: {bcode.name}, Input: {code}, Output: {bcode.get_fullcode()}") - append(os.path.basename(filename), bcode.name) - if ImageWriter is not None: - bcodec = get_barcode_class(codename) - bcode = bcodec(code, writer=ImageWriter()) - opts = {} - if codename.startswith("i"): - opts["center_text"] = False - else: - opts["center_text"] = True - filename = bcode.save(os.path.join(TESTPATH, codename), options=opts) - append_img(os.path.basename(filename), bcode.name) - else: - objects.append(NO_PIL) + opts["center_text"] = True + filename = bcode.save(os.path.join(TESTPATH, codename), options=opts) + append_img(os.path.basename(filename), bcode.name) + else: + objects.append(NO_PIL) @pytest.fixture(scope="module") From 387175971598c6d7107375339540461fba0185a9 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 20:51:08 +0200 Subject: [PATCH 166/192] Improve variable name --- tests/test_manually.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_manually.py b/tests/test_manually.py index 18876ce..1677792 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -59,13 +59,13 @@ def test_generating_barcodes( ) -> None: os.makedirs(TESTPATH, exist_ok=True) - objects = gather_image_elements_into_html + image_elements = gather_image_elements_into_html def append(x, y) -> None: - objects.append(OBJECTS.format(filename=x, name=y)) + image_elements.append(OBJECTS.format(filename=x, name=y)) def append_img(x, y) -> None: - objects.append(IMAGES.format(filename=x, name=y)) + image_elements.append(IMAGES.format(filename=x, name=y)) options = {} bcode = get_barcode(codename, code) @@ -87,7 +87,7 @@ def append_img(x, y) -> None: filename = bcode.save(os.path.join(TESTPATH, codename), options=opts) append_img(os.path.basename(filename), bcode.name) else: - objects.append(NO_PIL) + image_elements.append(NO_PIL) @pytest.fixture(scope="module") From 6e14fe700eee107548dbb99ec6b254aed1eaec34 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 20:48:14 +0200 Subject: [PATCH 167/192] Ignore mypy undefined attribute isbn10 in test --- tests/test_checksums.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_checksums.py b/tests/test_checksums.py index d79c206..b97fef7 100755 --- a/tests/test_checksums.py +++ b/tests/test_checksums.py @@ -35,7 +35,7 @@ def test_ean14_checksum() -> None: def test_isbn10_checksum() -> None: isbn = get_barcode("isbn10", "376926085") - assert isbn.isbn10 == "3769260856" + assert isbn.isbn10 == "3769260856" # type: ignore[attr-defined] def test_isbn13_checksum() -> None: From 980f3ffc98c47844617f03789016f743e4c42bd3 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Tue, 30 Jul 2024 10:03:37 +0200 Subject: [PATCH 168/192] Type hints and cleanup of Barcode.build() and surrounding code (#230) * Raise a runtime error when converting missing character * Enforce that Barcode.build() returns a single-item list * Fix SVG DOM implementation to minidom * Enforce that code is a singleton list for BaseWriter.render * Assume Pillow * Enforce singletons in ImageWriter and SVGWriter * Fix bad info in docstring * Make writer and output optional params of generate * More type annotations --- barcode/__init__.py | 43 ++++++++--- barcode/base.py | 23 ++++-- barcode/codabar.py | 2 +- barcode/codex.py | 47 +++++++----- barcode/ean.py | 20 ++--- barcode/itf.py | 2 +- barcode/upc.py | 15 ++-- barcode/writer.py | 180 ++++++++++++++++++++++++-------------------- 8 files changed, 199 insertions(+), 133 deletions(-) diff --git a/barcode/__init__.py b/barcode/__init__.py index 8c54f8c..1a16b4e 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,7 +108,7 @@ def get( return barcode -def get_class(name: str): +def get_class(name: str) -> type[Barcode]: return get_barcode(name) @@ -97,7 +116,7 @@ def generate( name: str, code: str, writer: BaseWriter | None = None, - output: str | (os.PathLike | (BinaryIO | None)) = None, + output: str | os.PathLike | BinaryIO | None = None, 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 d5296ae..95223fb 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -34,16 +34,24 @@ class Barcode: writer: BaseWriter + def __init__(self, code: str, writer: BaseWriter | None = None, **options) -> None: + raise NotImplementedError + 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 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): @@ -101,5 +109,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 ef72187..b82c020 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): if self._charset == "A": return code128.A[char] if self._charset == "B": @@ -212,22 +224,23 @@ def _convert(self, char): 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): + 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 +253,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..b3d32fd 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 @@ -14,6 +14,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 @@ -28,27 +31,22 @@ 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 + import logging - log = logging.getLogger("pyBarcode") - log.info("Pillow not found. Image output disabled") - Image = ImageDraw = ImageFont = None + log = logging.getLogger("pyBarcode") + log.info("Pillow not found. Image output disabled") + 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 @@ -57,8 +55,9 @@ def _set_attributes(element, **attributes): element.setAttribute(key, value) -def create_svg_object(with_doctype=False): - imp = xml.dom.getDOMImplementation() +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", @@ -96,6 +95,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 +218,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]): """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"]: @@ -306,14 +315,17 @@ 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 - - def _init(self, code): - width, height = self.calculate_size(len(code[0]), len(code)) + 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: list[str]): + 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 = { @@ -362,9 +374,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) @@ -427,16 +439,21 @@ 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 - - def _init(self, code): - width, height = self.calculate_size(len(code[0]), len(code)) + self._image: T_Image + self._draw: T_ImageDraw + + def _init(self, code: list[str]) -> None: + if ImageDraw is None: + raise RuntimeError("Pillow not found. Cannot create image.") + 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) - 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)), ( @@ -447,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 @@ -461,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 b767a4c73ea705b2b34df32ac4497345c7b8c6ee Mon Sep 17 00:00:00 2001 From: "Tony M.Jenkins" Date: Tue, 23 Apr 2024 10:32:47 +0200 Subject: [PATCH 169/192] Update writer.py Added human text option to ImageWriter --- barcode/writer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index b3d32fd..77c5c8d 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -465,11 +465,16 @@ def _paint_module(self, xpos: float, ypos: float, width: float, color): def _paint_text(self, xpos, ypos): assert ImageFont is not None + + # check option to override self.text with self.human (barcode as + # human readable data, can be used to print own formats) + barcodetext = self.human if self.human != "" else self.text + font_size = int(mm2px(pt2mm(self.font_size), self.dpi)) if font_size <= 0: return font = ImageFont.truetype(self.font_path, font_size) - for subtext in self.text.split("\n"): + for subtext in barcodetext.split("\n"): pos = ( mm2px(xpos, self.dpi), mm2px(ypos, self.dpi), From 389dc5fac558c89f189cef850febf56209f72b9d Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Fri, 20 Sep 2024 11:02:00 +0200 Subject: [PATCH 170/192] Configure tox to use system Python This makes it usable by contributors by default. CI pipelines explicitly override the version of Python used. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 3d1f037..632f990 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py38,py39,py310,py311}{,-images} +envlist = py,py-images skip_missing_interpreters = True [testenv] From e2e626b9013c9b9e20590325605d1c2fa4a97224 Mon Sep 17 00:00:00 2001 From: Piotr Wawrzyniak Date: Sat, 6 Jul 2024 21:50:02 +0200 Subject: [PATCH 171/192] Optimization of code creation, avoiding to many charset switch. This results in shorter code which according to GS1 spec should not be longer than 165 mm (6.5") 1) For code128, starding encoding set to C 2) Added function that determines whether the char is FNC1 char - defined the same in all three encodings 3) _maybe_switch_charset recognizes that FNC1 char is defined in all three encodings, and avoids changing encodings 4) added testcases for new functionality. --- .gitignore | 1 + barcode/codex.py | 30 ++++++++++++++++++-------- tests/test_Gs1_128.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 tests/test_Gs1_128.py diff --git a/.gitignore b/.gitignore index 33f7bda..844a299 100755 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.kpf *.svg *.db +.idea/* barcode/__pycache* build/* dist/* diff --git a/barcode/codex.py b/barcode/codex.py index b82c020..874f451 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -149,7 +149,7 @@ class Code128(Barcode): def __init__(self, code: str, writer=None) -> None: self.code = code self.writer = writer or self.default_writer() - self._charset = "B" + self._charset = "C" self._buffer = "" check_code(self.code, self.name, code128.ALL) @@ -175,9 +175,15 @@ def _new_charset(self, which: Literal["A", "B", "C"]) -> list[int]: self._charset = which return [code] + # to be redefined in subclass if required + def _is_char_FNC1_CHAR(self, char): + # FNC1 char is defined in GS1-128 specification and it is defined just the same for all encodings + # therefore this sing should be treated in a special way. + return False + def _maybe_switch_charset(self, pos: int) -> list[int]: char = self.code[pos] - next_ = self.code[pos : pos + 10] + next_ = self.code[pos: pos + 10] def look_next() -> bool: digits = 0 @@ -190,13 +196,16 @@ def look_next() -> bool: codes: list[int] = [] if self._charset == "C" and not char.isdigit(): - if char in code128.B: - codes = self._new_charset("B") - elif char in code128.A: - codes = self._new_charset("A") - if len(self._buffer) == 1: - codes.append(self._convert(self._buffer[0])) - self._buffer = "" + if self._is_char_FNC1_CHAR(char) and not self._buffer: + return codes + else: + if char in code128.B: + codes = self._new_charset("B") + elif char in code128.A: + codes = self._new_charset("A") + if len(self._buffer) == 1: + codes.append(self._convert(self._buffer[0])) + self._buffer = "" elif self._charset == "B": if look_next(): codes = self._new_charset("C") @@ -288,6 +297,9 @@ def __init__(self, code, writer=None) -> None: def get_fullcode(self): return super().get_fullcode()[1:] + def _is_char_FNC1_CHAR(self, char): + return char == self.FNC1_CHAR + # For pre 0.8 compatibility PZN = PZN7 diff --git a/tests/test_Gs1_128.py b/tests/test_Gs1_128.py new file mode 100644 index 0000000..25e0ac8 --- /dev/null +++ b/tests/test_Gs1_128.py @@ -0,0 +1,50 @@ +import pytest +import sys +import os.path +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from barcode.codex import Gs1_128 + +FNC1_CHAR = "\xf1" +FNC1 = 102 +START_B = 104 +START_C = 105 +FROM_AC_TO_B = 100 +FROM_AB_TO_C = 99 +FROM_BC_TO_A = 101 +CODE_BUILD_TEST = ( + # '(01)01234567891011(11)200622(17)240622(21)88888888' + ('010123456789101111200622172406222188888888', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, + 11, 20, 6, 22, + 17, 24, 6, 22, + 21, 88, 88, 88, 88],), + # '(01)01234567891011(11)200622(17)240622(21)888888888' + ('0101234567891011112006221724062221888888888', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, + 11, 20, 6, 22, + 17, 24, 6, 22, + 21, 88, 88, 88, 88, 100, 24],), + # '(01)01234567891011(11)200622(10)12345(21)1234' + ('0101234567891011112006221012345' + FNC1_CHAR + '211234', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, + 11, 20, 6, 22, + 10, 12, 34, FROM_AC_TO_B, 21, FNC1, + FROM_AB_TO_C, 21, 12, 34],), + # '(01)01234567891011(11)200622(10)1234(21)1234' + ('010123456789101111200622101234' + FNC1_CHAR + '211234', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, + 11, 20, 6, 22, + 10, 12, 34, FNC1, + 21, 12, 34],), + # '(01)01234567891011(11)200622(10)240622(21)888888888' + ('01012345678910111120062210240622' + FNC1_CHAR + '21888888888', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, + 11, 20, 6, 22, + 10, 24, 6, 22, FNC1, + 21, 88, 88, 88, 88, 100, 24],), + # '(01)08720299927469(11)240621(17)250621(10)20240621/0001(21)xyz' + ('010872029992746911240621172506211020240621/0001' + FNC1_CHAR + '21xyz', [105, 102, 1, 8, 72, 2, 99, 92, 74, 69, + 11, 24, 6, 21, + 17, 25, 6, 21, + 10, 20, 24, 6, 21, 100, 15, 99, 0, 1, + 102, 21, 100, 88, 89, 90]), +) +@pytest.mark.parametrize('target, answer', CODE_BUILD_TEST) +def test_code_build(target, answer): + gs1_128 = Gs1_128(target) + assert gs1_128._build() == answer From 2f96373fb25939b8fa98e1a9537a0ecfe26eb708 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 23 Jan 2025 17:41:04 +0100 Subject: [PATCH 172/192] Fix various linting and style checks - Functions names must be lowercase. - Needless `else` after `return`. - Invalid module name for tests file. --- barcode/codex.py | 32 ++++--- tests/test_Gs1_128.py | 50 ---------- tests/test_gs1_128.py | 210 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+), 64 deletions(-) delete mode 100644 tests/test_Gs1_128.py create mode 100644 tests/test_gs1_128.py diff --git a/barcode/codex.py b/barcode/codex.py index 874f451..0eda13a 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -2,6 +2,7 @@ :Provided barcodes: Code 39, Code 128, PZN """ + from __future__ import annotations from typing import TYPE_CHECKING @@ -176,14 +177,18 @@ def _new_charset(self, which: Literal["A", "B", "C"]) -> list[int]: return [code] # to be redefined in subclass if required - def _is_char_FNC1_CHAR(self, char): - # FNC1 char is defined in GS1-128 specification and it is defined just the same for all encodings - # therefore this sing should be treated in a special way. + def _is_char_fnc1_char(self, char): + """Whether a character is the FNC1 character. + + May be redefined by subclasses if required. FNC1 char is defined in GS1-128 + specification and it is defined just the same for all encodings therefore this + sign should be treated in a special way. + """ return False def _maybe_switch_charset(self, pos: int) -> list[int]: char = self.code[pos] - next_ = self.code[pos: pos + 10] + next_ = self.code[pos : pos + 10] def look_next() -> bool: digits = 0 @@ -196,16 +201,15 @@ def look_next() -> bool: codes: list[int] = [] if self._charset == "C" and not char.isdigit(): - if self._is_char_FNC1_CHAR(char) and not self._buffer: + if self._is_char_fnc1_char(char) and not self._buffer: return codes - else: - if char in code128.B: - codes = self._new_charset("B") - elif char in code128.A: - codes = self._new_charset("A") - if len(self._buffer) == 1: - codes.append(self._convert(self._buffer[0])) - self._buffer = "" + if char in code128.B: + codes = self._new_charset("B") + elif char in code128.A: + codes = self._new_charset("A") + if len(self._buffer) == 1: + codes.append(self._convert(self._buffer[0])) + self._buffer = "" elif self._charset == "B": if look_next(): codes = self._new_charset("C") @@ -297,7 +301,7 @@ def __init__(self, code, writer=None) -> None: def get_fullcode(self): return super().get_fullcode()[1:] - def _is_char_FNC1_CHAR(self, char): + def _is_char_fnc1_char(self, char): return char == self.FNC1_CHAR diff --git a/tests/test_Gs1_128.py b/tests/test_Gs1_128.py deleted file mode 100644 index 25e0ac8..0000000 --- a/tests/test_Gs1_128.py +++ /dev/null @@ -1,50 +0,0 @@ -import pytest -import sys -import os.path -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -from barcode.codex import Gs1_128 - -FNC1_CHAR = "\xf1" -FNC1 = 102 -START_B = 104 -START_C = 105 -FROM_AC_TO_B = 100 -FROM_AB_TO_C = 99 -FROM_BC_TO_A = 101 -CODE_BUILD_TEST = ( - # '(01)01234567891011(11)200622(17)240622(21)88888888' - ('010123456789101111200622172406222188888888', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, - 11, 20, 6, 22, - 17, 24, 6, 22, - 21, 88, 88, 88, 88],), - # '(01)01234567891011(11)200622(17)240622(21)888888888' - ('0101234567891011112006221724062221888888888', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, - 11, 20, 6, 22, - 17, 24, 6, 22, - 21, 88, 88, 88, 88, 100, 24],), - # '(01)01234567891011(11)200622(10)12345(21)1234' - ('0101234567891011112006221012345' + FNC1_CHAR + '211234', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, - 11, 20, 6, 22, - 10, 12, 34, FROM_AC_TO_B, 21, FNC1, - FROM_AB_TO_C, 21, 12, 34],), - # '(01)01234567891011(11)200622(10)1234(21)1234' - ('010123456789101111200622101234' + FNC1_CHAR + '211234', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, - 11, 20, 6, 22, - 10, 12, 34, FNC1, - 21, 12, 34],), - # '(01)01234567891011(11)200622(10)240622(21)888888888' - ('01012345678910111120062210240622' + FNC1_CHAR + '21888888888', [START_C, FNC1, 1, 1, 23, 45, 67, 89, 10, 11, - 11, 20, 6, 22, - 10, 24, 6, 22, FNC1, - 21, 88, 88, 88, 88, 100, 24],), - # '(01)08720299927469(11)240621(17)250621(10)20240621/0001(21)xyz' - ('010872029992746911240621172506211020240621/0001' + FNC1_CHAR + '21xyz', [105, 102, 1, 8, 72, 2, 99, 92, 74, 69, - 11, 24, 6, 21, - 17, 25, 6, 21, - 10, 20, 24, 6, 21, 100, 15, 99, 0, 1, - 102, 21, 100, 88, 89, 90]), -) -@pytest.mark.parametrize('target, answer', CODE_BUILD_TEST) -def test_code_build(target, answer): - gs1_128 = Gs1_128(target) - assert gs1_128._build() == answer diff --git a/tests/test_gs1_128.py b/tests/test_gs1_128.py new file mode 100644 index 0000000..3352bf0 --- /dev/null +++ b/tests/test_gs1_128.py @@ -0,0 +1,210 @@ +from __future__ import annotations + +import pytest + +from barcode.codex import Gs1_128 + +FNC1_CHAR = "\xf1" +FNC1 = 102 +START_B = 104 +START_C = 105 +FROM_AC_TO_B = 100 +FROM_AB_TO_C = 99 +FROM_BC_TO_A = 101 +CODE_BUILD_TEST = ( + # '(01)01234567891011(11)200622(17)240622(21)88888888' # noqa: ERA001 + ( + "010123456789101111200622172406222188888888", + [ + START_C, + FNC1, + 1, + 1, + 23, + 45, + 67, + 89, + 10, + 11, + 11, + 20, + 6, + 22, + 17, + 24, + 6, + 22, + 21, + 88, + 88, + 88, + 88, + ], + ), + # '(01)01234567891011(11)200622(17)240622(21)888888888' # noqa: ERA001 + ( + "0101234567891011112006221724062221888888888", + [ + START_C, + FNC1, + 1, + 1, + 23, + 45, + 67, + 89, + 10, + 11, + 11, + 20, + 6, + 22, + 17, + 24, + 6, + 22, + 21, + 88, + 88, + 88, + 88, + 100, + 24, + ], + ), + # '(01)01234567891011(11)200622(10)12345(21)1234' # noqa: ERA001 + ( + "0101234567891011112006221012345" + FNC1_CHAR + "211234", + [ + START_C, + FNC1, + 1, + 1, + 23, + 45, + 67, + 89, + 10, + 11, + 11, + 20, + 6, + 22, + 10, + 12, + 34, + FROM_AC_TO_B, + 21, + FNC1, + FROM_AB_TO_C, + 21, + 12, + 34, + ], + ), + # '(01)01234567891011(11)200622(10)1234(21)1234' # noqa: ERA001 + ( + "010123456789101111200622101234" + FNC1_CHAR + "211234", + [ + START_C, + FNC1, + 1, + 1, + 23, + 45, + 67, + 89, + 10, + 11, + 11, + 20, + 6, + 22, + 10, + 12, + 34, + FNC1, + 21, + 12, + 34, + ], + ), + # '(01)01234567891011(11)200622(10)240622(21)888888888' # noqa: ERA001 + ( + "01012345678910111120062210240622" + FNC1_CHAR + "21888888888", + [ + START_C, + FNC1, + 1, + 1, + 23, + 45, + 67, + 89, + 10, + 11, + 11, + 20, + 6, + 22, + 10, + 24, + 6, + 22, + FNC1, + 21, + 88, + 88, + 88, + 88, + 100, + 24, + ], + ), + # '(01)08720299927469(11)240621(17)250621(10)20240621/0001(21)xyz' # noqa: ERA001 + ( + "010872029992746911240621172506211020240621/0001" + FNC1_CHAR + "21xyz", + [ + 105, + 102, + 1, + 8, + 72, + 2, + 99, + 92, + 74, + 69, + 11, + 24, + 6, + 21, + 17, + 25, + 6, + 21, + 10, + 20, + 24, + 6, + 21, + 100, + 15, + 99, + 0, + 1, + 102, + 21, + 100, + 88, + 89, + 90, + ], + ), +) + + +@pytest.mark.parametrize(("target", "answer"), CODE_BUILD_TEST) +def test_code_build(target, answer): + gs1_128 = Gs1_128(target) + assert gs1_128._build() == answer From f013b69dc63874924a2b88b90e5028be3e7bdc11 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 27 Aug 2025 12:26:32 +0200 Subject: [PATCH 173/192] Drop support for Python 3.8 --- .github/workflows/tests.yml | 2 +- .pre-commit-config.yaml | 2 +- README.rst | 2 +- docs/changelog.rst | 2 +- pyproject.toml | 2 +- setup.py | 1 - 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ab81429..7d6a967 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ ubuntu-22.04 ] - python: [ '3.8', '3.9', '3.10', '3.11', '3.12' ] + python: [ '3.9', '3.10', '3.11', '3.12' ] variant: [ "py", "py-images" ] include: - os: macOS-12 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ec56193..a330b36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,4 +20,4 @@ repos: rev: v1.6.0 hooks: - id: vermin - args: ['-t=3.8-', '--violations'] + args: ['-t=3.9-', '--violations'] diff --git a/README.rst b/README.rst index 7231572..9587f0b 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.8 to 3.12. +Support Python 3.9 to 3.12. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/docs/changelog.rst b/docs/changelog.rst index 12a1b56..7e62cc3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,7 @@ Changelog v0.16.0 ~~~~~~~ -* **Breaking** Drop support for Python 3.7. +* **Breaking** Drop support for Python 3.7 and 3.8. * Make image DPI configurable. * Fixed inconsistent checksum calculation when calculating the checksum multiple times for EAN barcodes. diff --git a/pyproject.toml b/pyproject.toml index 3eaa7ca..4f40731 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ select = [ "PLE", "RUF", ] -target-version = "py38" +target-version = "py39" [tool.ruff.isort] force-single-line = true diff --git a/setup.py b/setup.py index 6605f21..9b01447 100755 --- a/setup.py +++ b/setup.py @@ -25,7 +25,6 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", From cf58852b02a4c30c84d4678a357e83bd71374adc Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 27 Aug 2025 12:33:19 +0200 Subject: [PATCH 174/192] Switch from setup.py to pyproject.toml The former is deprecated. Also fixes issues rendering README on PyPI. --- .github/workflows/tests.yml | 1 - docs/changelog.rst | 5 ++++ pyproject.toml | 47 ++++++++++++++++++++++++++++++++++++- setup.py | 39 ------------------------------ 4 files changed, 51 insertions(+), 41 deletions(-) delete mode 100755 setup.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7d6a967..fc64e42 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,7 +27,6 @@ jobs: cache: pip cache-dependency-path: | pyproject.toml - setup.py - name: Install test dependency run: pip install tox - name: Run tests diff --git a/docs/changelog.rst b/docs/changelog.rst index 7e62cc3..051d8e4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog --------- +v0.16.1 +~~~~~~~ +* Switch from ``setup.py`` to ``pyproject.toml``. Only affects how installation + from source is performed, and has no runtime impact. + v0.16.0 ~~~~~~~ diff --git a/pyproject.toml b/pyproject.toml index 4f40731..a06c879 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,50 @@ [build-system] -requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] +requires = ["setuptools>=61", "wheel", "setuptools_scm>=6.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "python-barcode" +description = "Create standard barcodes with Python. No external modules needed. (optional Pillow support included)." +readme = "README.rst" +requires-python = ">=3.9" +license = { text = "MIT" } +authors = [ + { name = "Hugo Osvaldo Barrera et al", email = "hugo@whynothugo.nl" } +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Multimedia :: Graphics", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dynamic = ["version"] + +[project.optional-dependencies] +images = ["pillow"] + +[project.scripts] +python-barcode = "barcode.pybarcode:main" + +[project.urls] +documentation = "https://python-barcode.readthedocs.io/" +repository = "https://github.com/WhyNotHugo/python-barcode" +issues = "https://github.com/WhyNotHugo/python-barcode/issues" +funding= "https://whynothugo.nl/sponsor/" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages.find] +exclude = ["tests"] [tool.setuptools_scm] write_to = "barcode/version.py" diff --git a/setup.py b/setup.py deleted file mode 100755 index 9b01447..0000000 --- a/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from setuptools import find_packages -from setuptools import setup - -setup( - name="python-barcode", - packages=find_packages(exclude=["tests"]), - url="https://github.com/WhyNotHugo/python-barcode", - license="MIT", - author="Hugo Osvaldo Barrera et al", - author_email="hugo@whynothugo.nl", - description=( - "Create standard barcodes with Python. No external modules needed. " - "(optional Pillow support included)." - ), - long_description=Path("README.rst").read_text(), - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Multimedia :: Graphics", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - entry_points={"console_scripts": ["python-barcode = barcode.pybarcode:main"]}, - setup_requires=["setuptools_scm"], - extras_require={"images": ["pillow"]}, - include_package_data=True, -) From 29a3f5c65f148afd2803a05f5772b7233b41ce39 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 27 Aug 2025 13:06:02 +0200 Subject: [PATCH 175/192] Add manifest to manually publish releases GitHub's CI setup is too flaky, and there's not even a mechanism for retrying an older release. --- publish-release.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 publish-release.yaml diff --git a/publish-release.yaml b/publish-release.yaml new file mode 100644 index 0000000..3555260 --- /dev/null +++ b/publish-release.yaml @@ -0,0 +1,25 @@ +# Run this with: +# hut builds submit -f publish-release.yaml +image: archlinux +packages: + - python-build + - python-setuptools-scm + - python-wheel + - twine +sources: + - https://github.com/WhyNotHugo/python-barcode/ +secrets: + - 0dd39b49-3530-4002-a197-e0ca7fc3fde7 # PyPI token. +tasks: + - check: | + cd python-barcode + git fetch --tags + + # Stop here unless this is a tag. + git describe --exact-match --tags || complete-build + - build: | + cd python-barcode + python -m build --no-isolation + - publish: | + cd python-barcode + twine upload --non-interactive dist/* From b7f17aab6c96bebf8fde8d4c86182903dc8754fb Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 27 Aug 2025 12:58:53 +0200 Subject: [PATCH 176/192] ruff: remove deprecated setting Ruff now uses the value from requires-python. --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a06c879..12dbd50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,6 @@ select = [ "PLE", "RUF", ] -target-version = "py39" [tool.ruff.isort] force-single-line = true From 34196aa12d73393c30a5b2d6349152337505db28 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 10 May 2025 14:02:45 +0300 Subject: [PATCH 177/192] Move pytest configuration to pyproject.toml --- pyproject.toml | 8 ++++++++ setup.cfg | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) delete mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index 12dbd50..9181af3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,3 +90,11 @@ required-imports = ["from __future__ import annotations"] exclude_lines = [ "if TYPE_CHECKING:", ] + +[tool.pytest.ini_options] +addopts = [ + "-vv", + "--cov=barcode", + "--cov-report=term-missing:skip-covered", + "--no-cov-on-fail", +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ed5ed80..0000000 --- a/setup.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[tool:pytest] -addopts = - -vv - --cov=barcode - --cov-report=term-missing:skip-covered - --no-cov-on-fail From cc56cbbb2c1371f7d1ae3999c4f15e950e7625f6 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 10 May 2025 14:03:34 +0300 Subject: [PATCH 178/192] Declare support for and test on Python 3.13 --- .github/workflows/tests.yml | 2 +- README.rst | 2 +- docs/changelog.rst | 4 ++++ pyproject.toml | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fc64e42..31375c4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ ubuntu-22.04 ] - python: [ '3.9', '3.10', '3.11', '3.12' ] + python: [ '3.9', '3.10', '3.11', '3.12', '3.13' ] variant: [ "py", "py-images" ] include: - os: macOS-12 diff --git a/README.rst b/README.rst index 9587f0b..44b25a2 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ python-barcode There are no external dependencies when generating SVG files. Pillow is required for generating images (e.g.: PNGs). -Support Python 3.9 to 3.12. +Support Python 3.9 to 3.13. .. image:: example-ean13.png :target: https://github.com/WhyNotHugo/python-barcode diff --git a/docs/changelog.rst b/docs/changelog.rst index 051d8e4..d2c295c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,10 @@ Changelog --------- +v0.16.2 +~~~~~~~ +* Add support for Python 3.13. + v0.16.1 ~~~~~~~ * Switch from ``setup.py`` to ``pyproject.toml``. Only affects how installation diff --git a/pyproject.toml b/pyproject.toml index 9181af3..07e2503 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Multimedia :: Graphics", "Topic :: Software Development :: Libraries :: Python Modules", ] From 37b1b9591970c51c4875123bc4606c28ee7ae583 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 10 May 2025 14:05:10 +0300 Subject: [PATCH 179/192] Update pre-commit tools; apply ruff format --- .pre-commit-config.yaml | 6 +++--- barcode/__init__.py | 7 +++---- barcode/base.py | 3 +-- barcode/codabar.py | 1 + barcode/errors.py | 1 + barcode/isxn.py | 1 + barcode/itf.py | 1 + barcode/upc.py | 1 + pyproject.toml | 4 ++-- tests/test_manually.py | 1 + 10 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a330b36..edbab57 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,17 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.7.1' + rev: 'v1.15.0' hooks: - id: mypy - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.1.6' + rev: 'v0.11.9' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/barcode/__init__.py b/barcode/__init__.py index 1a16b4e..6a4d481 100755 --- a/barcode/__init__.py +++ b/barcode/__init__.py @@ -3,6 +3,7 @@ created as SVG objects. If Pillow is installed, the barcodes can also be rendered as images (all formats supported by Pillow). """ + from __future__ import annotations import os @@ -65,8 +66,7 @@ @overload def get( name: str, code: str, writer: BaseWriter | None = None, options: dict | None = None -) -> Barcode: - ... +) -> Barcode: ... @overload @@ -75,8 +75,7 @@ def get( code: None = None, writer: BaseWriter | None = None, options: dict | None = None, -) -> type[Barcode]: - ... +) -> type[Barcode]: ... def get( diff --git a/barcode/base.py b/barcode/base.py index 95223fb..cfcbe36 100755 --- a/barcode/base.py +++ b/barcode/base.py @@ -1,6 +1,5 @@ -"""barcode.base +"""barcode.base""" -""" from __future__ import annotations from typing import TYPE_CHECKING diff --git a/barcode/codabar.py b/barcode/codabar.py index aecd8ab..a70e3dd 100644 --- a/barcode/codabar.py +++ b/barcode/codabar.py @@ -2,6 +2,7 @@ :Provided barcodes: Codabar (NW-7) """ + from __future__ import annotations __docformat__ = "restructuredtext en" diff --git a/barcode/errors.py b/barcode/errors.py index 2da49d6..4725187 100755 --- a/barcode/errors.py +++ b/barcode/errors.py @@ -1,4 +1,5 @@ """barcode.errors""" + from __future__ import annotations __docformat__ = "restructuredtext en" diff --git a/barcode/isxn.py b/barcode/isxn.py index bcb56ba..e74ea58 100755 --- a/barcode/isxn.py +++ b/barcode/isxn.py @@ -21,6 +21,7 @@ '0132354187' """ + from __future__ import annotations from barcode.ean import EuropeanArticleNumber13 diff --git a/barcode/itf.py b/barcode/itf.py index 7d9c2fe..f40d9cb 100644 --- a/barcode/itf.py +++ b/barcode/itf.py @@ -2,6 +2,7 @@ :Provided barcodes: Interleaved 2 of 5 """ + from __future__ import annotations __docformat__ = "restructuredtext en" diff --git a/barcode/upc.py b/barcode/upc.py index 9093b8f..060f19f 100755 --- a/barcode/upc.py +++ b/barcode/upc.py @@ -2,6 +2,7 @@ :Provided barcodes: UPC-A """ + from __future__ import annotations __docformat__ = "restructuredtext en" diff --git a/pyproject.toml b/pyproject.toml index 07e2503..cbfd716 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ exclude = ["tests"] write_to = "barcode/version.py" version_scheme = "post-release" -[tool.ruff] +[tool.ruff.lint] select = [ "F", "E", @@ -83,7 +83,7 @@ select = [ "RUF", ] -[tool.ruff.isort] +[tool.ruff.lint.isort] force-single-line = true required-imports = ["from __future__ import annotations"] diff --git a/tests/test_manually.py b/tests/test_manually.py index 1677792..12f4d15 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -1,4 +1,5 @@ """Generates barcodes for visually inspecting the results.""" + from __future__ import annotations import codecs From 45bc47a324537793a266231a545b29b17d684142 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 10 May 2025 14:09:58 +0300 Subject: [PATCH 180/192] Add missing changelog for #232 --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index d2c295c..6309ee6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -23,6 +23,9 @@ v0.16.0 included resulting in a transparent background. * Do not paint text if its size would be zero, to avoid an "invalid ppem value" error with newer versions of Pillow. +* Optimization of code creation, avoiding to many charset switch. + This results in shorter codes; according to GS1 codes should not + be longer than 165 mm (6.5"). (#232) v0.15.1 ~~~~~~~ From 024dcab12c6a9f5948f49bb13ff0f9a93298f12a Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 27 Aug 2025 12:58:21 +0200 Subject: [PATCH 181/192] Fix some ruff lint warnings --- barcode/codex.py | 3 ++- barcode/writer.py | 2 +- tests/test_manually.py | 5 ++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index 0eda13a..f296855 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -6,7 +6,6 @@ from __future__ import annotations from typing import TYPE_CHECKING -from typing import Collection from typing import Literal from barcode.base import Barcode @@ -17,6 +16,8 @@ from barcode.errors import NumberOfDigitsError if TYPE_CHECKING: + from collections.abc import Collection + from barcode.writer import BaseWriter __docformat__ = "restructuredtext en" diff --git a/barcode/writer.py b/barcode/writer.py index 77c5c8d..9f64835 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -11,7 +11,7 @@ from barcode.version import version if TYPE_CHECKING: - from typing import Generator + from collections.abc import Generator from typing import Literal from PIL.Image import Image as T_Image diff --git a/tests/test_manually.py b/tests/test_manually.py index 12f4d15..802fb03 100755 --- a/tests/test_manually.py +++ b/tests/test_manually.py @@ -4,7 +4,7 @@ import codecs import os -from typing import Iterator +from typing import TYPE_CHECKING import pytest @@ -13,6 +13,9 @@ from barcode import version from barcode.writer import ImageWriter +if TYPE_CHECKING: + from collections.abc import Iterator + PATH = os.path.dirname(os.path.abspath(__file__)) TESTPATH = os.path.join(PATH, "test_outputs") HTMLFILE = os.path.join(TESTPATH, "index.html") From 2e765bd9cc2b367f9e2c8ba3f6666a51591befa3 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 28 Aug 2025 11:41:27 +0200 Subject: [PATCH 182/192] ruff: use extend-select Ensure that we don't disable any default rules. --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cbfd716..891d6c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,8 +52,7 @@ write_to = "barcode/version.py" version_scheme = "post-release" [tool.ruff.lint] -select = [ - "F", +extend-select = [ "E", "W", "I", From a31d1bdfd4b2721922d8cf86bbd623b86c90688f Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 28 Aug 2025 12:22:26 +0200 Subject: [PATCH 183/192] Add a few type hints --- barcode/ean.py | 23 +++++++++++++++-------- barcode/writer.py | 17 +++++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/barcode/ean.py b/barcode/ean.py index c65d17c..e001ccc 100755 --- a/barcode/ean.py +++ b/barcode/ean.py @@ -42,7 +42,11 @@ class EuropeanArticleNumber13(Barcode): digits = 12 def __init__( - self, ean: str, writer=None, no_checksum=False, guardbar=False + self, + ean: str, + writer=None, + no_checksum: bool = False, + guardbar: bool = False, ) -> None: if not ean[: self.digits].isdigit(): raise IllegalCharacterError(f"EAN code can only contain numbers {ean}.") @@ -121,7 +125,7 @@ def to_ascii(self) -> str: code = code_list[0] return code.replace("G", "|").replace("1", "|").replace("0", " ") - def render(self, writer_options=None, text=None): + def render(self, writer_options: dict | None = None, text: str | None = None): options = {"module_width": SIZES["SC2"]} options.update(writer_options or {}) return super().render(options, text) @@ -161,11 +165,8 @@ def __init__(self, jan, *args, **kwargs) -> None: class EuropeanArticleNumber8(EuropeanArticleNumber13): """Represents an EAN-8 barcode. See EAN13's __init__ for details. - :parameters: - ean : String - The ean number as string. - writer : barcode.writer Instance - The writer to render the barcode (default: SVGWriter). + :param ean: The ean number as string. + :param writer: The writer to render the barcode (default: SVGWriter). """ name = "EAN-8" @@ -197,7 +198,13 @@ class EuropeanArticleNumber8WithGuard(EuropeanArticleNumber8): name = "EAN-8 with guards" - def __init__(self, ean, writer=None, no_checksum=False, guardbar=True) -> None: + def __init__( + self, + ean: str, + writer=None, + no_checksum: bool = False, + guardbar: bool = True, + ) -> None: super().__init__(ean, writer, no_checksum, guardbar) diff --git a/barcode/writer.py b/barcode/writer.py index 9f64835..24b3a56 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -50,12 +50,15 @@ def pt2mm(pt: float) -> float: return pt * 0.352777778 -def _set_attributes(element, **attributes): +def _set_attributes( + element: xml.dom.minidom.Element, + **attributes: str, +) -> None: for key, value in attributes.items(): element.setAttribute(key, value) -def create_svg_object(with_doctype=False) -> xml.dom.minidom.Document: +def create_svg_object(with_doctype: bool = False) -> xml.dom.minidom.Document: imp = xml.dom.minidom.getDOMImplementation() assert imp is not None doctype = imp.createDocumentType( @@ -64,6 +67,7 @@ def create_svg_object(with_doctype=False) -> xml.dom.minidom.Document: "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", ) document = imp.createDocument(None, "svg", doctype if with_doctype else None) + assert document.documentElement is not None _set_attributes( document.documentElement, version="1.1", xmlns="http://www.w3.org/2000/svg" ) @@ -385,7 +389,7 @@ def _create_text(self, xpos, ypos): self._group.appendChild(element) ypos += pt2mm(self.font_size) + self.text_line_distance - def _finish(self): + def _finish(self) -> bytes: if self.compress: return self._document.toxml(encoding="UTF-8") @@ -422,7 +426,12 @@ class ImageWriter(BaseWriter): # type: ignore[no-redef] mode: str dpi: int - def __init__(self, format="PNG", mode="RGB", dpi=300) -> None: + def __init__( + self, + format: str = "PNG", + mode: str = "RGB", + dpi: int = 300, + ) -> None: """Initialise a new write instance. :params format: The file format for the generated image. This parameter can From 7ca31f106eb35ed0f309169b42fd1e81b0963dce Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 28 Aug 2025 12:32:29 +0200 Subject: [PATCH 184/192] Squelch mypy warning I don't think mypy can handle this. --- barcode/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barcode/writer.py b/barcode/writer.py index 24b3a56..1e17f1f 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -39,7 +39,7 @@ class Callbacks(TypedDict): log = logging.getLogger("pyBarcode") log.info("Pillow not found. Image output disabled") - Image = ImageDraw = ImageFont = None + Image = ImageDraw = ImageFont = None # type: ignore[assignment] def mm2px(mm: float, dpi: int) -> float: From 0ad4e5bacead17cdc0e363a2d63f434909b7ab8c Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 28 Aug 2025 12:32:45 +0200 Subject: [PATCH 185/192] Assert type Works around mypy warning; this should always be true anyway. --- barcode/writer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/barcode/writer.py b/barcode/writer.py index 1e17f1f..1bed3fc 100755 --- a/barcode/writer.py +++ b/barcode/writer.py @@ -331,6 +331,7 @@ def _init(self, code: list[str]): line = code[0] width, height = self.calculate_size(len(line), 1) self._document = create_svg_object(self.with_doctype) + assert self._document.documentElement is not None self._root = self._document.documentElement attributes = { "width": SIZE.format(width), From 1e14c626515e6b5ba1eed17ab1cd6d9008fe5925 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 28 Aug 2025 13:45:58 +0200 Subject: [PATCH 186/192] Run mypy with both possible setups --- .pre-commit-config.yaml | 4 ---- tox.ini | 9 ++++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index edbab57..2b932c2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,10 +6,6 @@ repos: args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: debug-statements - - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v1.15.0' - hooks: - - id: mypy - repo: https://github.com/astral-sh/ruff-pre-commit rev: 'v0.11.9' hooks: diff --git a/tox.ini b/tox.ini index 632f990..ba2c87a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py,py-images +envlist = py,py-images,mypy,mypy-images skip_missing_interpreters = True [testenv] @@ -9,3 +9,10 @@ deps = images: Pillow commands = pytest --cov barcode usedevelop = True + +[testenv:mypy] +deps = + mypy + images: Pillow +commands = mypy . +usedevelop = True From a5917f1137916c59aa3da1c398bf8dd8442af1c2 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 23:32:50 +0200 Subject: [PATCH 187/192] Distinguish between buffered and unbuffered code128 conversion --- barcode/codex.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index f296855..78587cb 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -223,12 +223,33 @@ def look_next() -> bool: codes = self._new_charset("B") return codes - def _convert(self, char: str): + def _convert(self, char: str) -> int: + """Convert a character to a code number for the current charset. + + NOTE: encoding digits with charset C requires buffering and is not supported + here. Use _convert_or_buffer instead. + """ if self._charset == "A": return code128.A[char] if self._charset == "B": return code128.B[char] if self._charset == "C": + if char in ["TO_A", "TO_B"]: + return code128.C[char] + raise RuntimeError("Use _convert_or_buffer for charset C.") + raise RuntimeError( + f"Character {char} could not be converted in charset {self._charset}." + ) + + def _convert_or_buffer(self, char: str) -> int | None: + """Convert a character to a code number for the current charset. + + If charset C is active then digits are encoded in pairs. When the first digit + is encountered, it is buffered and None is returned. + """ + if self._charset != "C": + return self._convert(char) + else: if char in code128.C: return code128.C[char] if char.isdigit(): @@ -257,7 +278,7 @@ 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) + code_num = self._convert_or_buffer(char) if code_num is not None: encoded.append(code_num) # Finally look in the buffer From 65914f8cb72ffd9bf2eb4632d5a9e3d8f28e83bc Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 23:39:18 +0200 Subject: [PATCH 188/192] Be more explicit about the cases --- barcode/codex.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index 78587cb..7ebebbc 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -254,14 +254,14 @@ def _convert_or_buffer(self, char: str) -> int | None: return code128.C[char] if char.isdigit(): self._buffer += char - if len(self._buffer) == 2: - value = int(self._buffer) - self._buffer = "" - return value - return None - raise RuntimeError( - f"Character {char} could not be converted in charset {self._charset}." - ) + if len(self._buffer) == 1: + # Wait for the second digit to group in pairs + return None + assert len(self._buffer) == 2 + value = int(self._buffer) + self._buffer = "" + return value + raise RuntimeError(f"Character {char} could not be converted in charset C.") def _try_to_optimize(self, encoded: list[int]) -> list[int]: if encoded[1] in code128.TO: From 610948407496dd732cb38e020f332e104ee92046 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 23:41:57 +0200 Subject: [PATCH 189/192] Dedent --- barcode/codex.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index 7ebebbc..cbd91c3 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -249,18 +249,17 @@ def _convert_or_buffer(self, char: str) -> int | None: """ if self._charset != "C": return self._convert(char) - else: - if char in code128.C: - return code128.C[char] - if char.isdigit(): - self._buffer += char - if len(self._buffer) == 1: - # Wait for the second digit to group in pairs - return None - assert len(self._buffer) == 2 - value = int(self._buffer) - self._buffer = "" - return value + if char in code128.C: + return code128.C[char] + if char.isdigit(): + self._buffer += char + if len(self._buffer) == 1: + # Wait for the second digit to group in pairs + return None + assert len(self._buffer) == 2 + value = int(self._buffer) + self._buffer = "" + return value raise RuntimeError(f"Character {char} could not be converted in charset C.") def _try_to_optimize(self, encoded: list[int]) -> list[int]: From 1aa5068fb8992c796e4245b9ab04f472aa5f2f46 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 8 Jul 2024 23:54:47 +0200 Subject: [PATCH 190/192] Don't return None in Code 39 calculate_checksum --- barcode/codex.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index cbd91c3..e782932 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -66,12 +66,14 @@ def get_fullcode(self) -> str: """:returns: The full code as it will be encoded.""" return self.code - def calculate_checksum(self): + def calculate_checksum(self) -> str: check = sum(code39.MAP[x][0] for x in self.code) % 43 for k, v in code39.MAP.items(): if check == v[0]: return k - return None + raise RuntimeError( + "All possible values for the checksum should have been included in the map." + ) def build(self) -> list[str]: chars = [code39.EDGE] From 2814211105c4431b0fba1356b80574739c8346a1 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Tue, 9 Jul 2024 00:22:36 +0200 Subject: [PATCH 191/192] =?UTF-8?q?Improve=20the=20name=20=5Fbuffer=20?= =?UTF-8?q?=E2=86=92=20=5Fdigit=5Fbuffer=20for=20Code128=20charset=20C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- barcode/codex.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/barcode/codex.py b/barcode/codex.py index e782932..a0a5234 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -154,7 +154,7 @@ def __init__(self, code: str, writer=None) -> None: self.code = code self.writer = writer or self.default_writer() self._charset = "C" - self._buffer = "" + self._digit_buffer = "" # Accumulate pairs of digits for charset C check_code(self.code, self.name, code128.ALL) def __str__(self) -> str: @@ -204,15 +204,15 @@ def look_next() -> bool: codes: list[int] = [] if self._charset == "C" and not char.isdigit(): - if self._is_char_fnc1_char(char) and not self._buffer: + if self._is_char_fnc1_char(char) and not self._digit_buffer: return codes if char in code128.B: codes = self._new_charset("B") elif char in code128.A: codes = self._new_charset("A") - if len(self._buffer) == 1: - codes.append(self._convert(self._buffer[0])) - self._buffer = "" + if len(self._digit_buffer) == 1: + codes.append(self._convert(self._digit_buffer[0])) + self._digit_buffer = "" elif self._charset == "B": if look_next(): codes = self._new_charset("C") @@ -254,13 +254,13 @@ def _convert_or_buffer(self, char: str) -> int | None: if char in code128.C: return code128.C[char] if char.isdigit(): - self._buffer += char - if len(self._buffer) == 1: + self._digit_buffer += char + if len(self._digit_buffer) == 1: # Wait for the second digit to group in pairs return None - assert len(self._buffer) == 2 - value = int(self._buffer) - self._buffer = "" + assert len(self._digit_buffer) == 2 + value = int(self._digit_buffer) + self._digit_buffer = "" return value raise RuntimeError(f"Character {char} could not be converted in charset C.") @@ -283,10 +283,10 @@ def _build(self) -> list[int]: if code_num is not None: encoded.append(code_num) # Finally look in the buffer - if len(self._buffer) == 1: + if len(self._digit_buffer) == 1: encoded.extend(self._new_charset("B")) - encoded.append(self._convert(self._buffer[0])) - self._buffer = "" + encoded.append(self._convert(self._digit_buffer[0])) + self._digit_buffer = "" return self._try_to_optimize(encoded) def build(self) -> list[str]: From 85acbda24151d5ce155b3299077a87b25fcd2cc2 Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Tue, 9 Jul 2024 16:36:38 +0200 Subject: [PATCH 192/192] Add some comments explaining buffer flush --- barcode/codex.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/barcode/codex.py b/barcode/codex.py index a0a5234..9a60c31 100755 --- a/barcode/codex.py +++ b/barcode/codex.py @@ -210,7 +210,9 @@ def look_next() -> bool: codes = self._new_charset("B") elif char in code128.A: codes = self._new_charset("A") + assert self._charset != "C" if len(self._digit_buffer) == 1: + # Flush the remaining single digit from the buffer codes.append(self._convert(self._digit_buffer[0])) self._digit_buffer = "" elif self._charset == "B": @@ -282,7 +284,8 @@ def _build(self) -> list[int]: code_num = self._convert_or_buffer(char) if code_num is not None: encoded.append(code_num) - # Finally look in the buffer + # If we finish in charset C with a single digit remaining in the buffer, + # switch to charset B and flush out the buffer. if len(self._digit_buffer) == 1: encoded.extend(self._new_charset("B")) encoded.append(self._convert(self._digit_buffer[0]))