Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions barcode/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@
from barcode.version import version # noqa: F401

if TYPE_CHECKING:
from typing import Any

from barcode.base import Barcode
from barcode.writer import BaseWriter
from barcode.writer import T_Output

__BARCODE_MAP: dict[str, type[Barcode]] = {
__BARCODE_MAP: dict[str, type[Barcode[Any]]] = {
"codabar": CODABAR,
"code128": Code128,
"code39": Code39,
Expand All @@ -59,31 +62,33 @@
"upca": UPCA,
}

PROVIDED_BARCODES = list(__BARCODE_MAP)
PROVIDED_BARCODES.sort()
PROVIDED_BARCODES = sorted(__BARCODE_MAP)


@overload
def get(
name: str, code: str, writer: BaseWriter | None = None, options: dict | None = None
) -> Barcode: ...
name: str,
code: str,
writer: BaseWriter[T_Output] | None = None,
options: dict | None = None,
) -> Barcode[T_Output]: ...


@overload
def get(
name: str,
code: None = None,
writer: BaseWriter | None = None,
writer: BaseWriter[Any] | None = None,
options: dict | None = None,
) -> type[Barcode]: ...
) -> type[Barcode[Any]]: ...


def get(
name: str,
code: str | None = None,
writer: BaseWriter | None = None,
writer: BaseWriter[Any] | None = None,
options: dict | None = None,
) -> Barcode | type[Barcode]:
) -> Barcode[Any] | type[Barcode[Any]]:
"""Helper method for getting a generator or even a generated code.

:param name: The name of the type of barcode desired.
Expand All @@ -96,7 +101,6 @@ def get(
generating.
"""
options = options or {}
barcode: type[Barcode]
try:
barcode = __BARCODE_MAP[name.lower()]
except KeyError as e:
Expand All @@ -107,14 +111,14 @@ def get(
return barcode


def get_class(name: str) -> type[Barcode]:
def get_class(name: str) -> type[Barcode[Any]]:
return get_barcode(name)


def generate(
name: str,
code: str,
writer: BaseWriter | None = None,
writer: BaseWriter[Any] | None = None,
output: str | os.PathLike | BinaryIO | None = None,
writer_options: dict | None = None,
text: str | None = None,
Expand Down
55 changes: 47 additions & 8 deletions barcode/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,25 @@

from typing import TYPE_CHECKING
from typing import ClassVar
from typing import Generic
from typing import overload

from barcode.writer import BaseWriter
from barcode.writer import SVGWriter
from barcode.writer import T_Output

if TYPE_CHECKING:
from collections.abc import Callable
from typing import Any
from typing import BinaryIO

from PIL.Image import Image as T_Image


class Barcode:
class Barcode(Generic[T_Output]):
name = ""

digits = 0

default_writer = SVGWriter
default_writer: ClassVar[Callable[[], BaseWriter[Any]]] = SVGWriter

default_writer_options: ClassVar[dict] = {
"module_width": 0.2,
Expand All @@ -33,11 +36,44 @@ class Barcode:
"text": "",
}

writer: BaseWriter
writer: BaseWriter[T_Output]

@overload
def __init__(
self,
code: str,
writer: None = None,
**options,
) -> None: ...

@overload
def __init__(
self,
code: str,
writer: BaseWriter[T_Output],
**options,
) -> None: ...

def __init__(self, code: str, writer: BaseWriter | None = None, **options) -> None:
def __init__(
self,
code: str,
writer: BaseWriter[T_Output] | None = None,
**options,
) -> None:
raise NotImplementedError

def _resolve_writer(
self,
writer: BaseWriter[T_Output] | None,
) -> BaseWriter[T_Output]:
if writer is not None:
return writer
# When no writer is given, T_Output falls back to its default (bytes),
# which matches what the default writer (SVGWriter) renders. Accessing
# default_writer through the class keeps mypy from binding it like a
# method.
return type(self).default_writer()

def to_ascii(self) -> str:
code_list = self.build()
if not len(code_list) == 1:
Expand All @@ -64,7 +100,10 @@ def get_fullcode(self) -> str:
raise NotImplementedError

def save(
self, filename: str, options: dict | None = None, text: str | None = None
self,
filename: str,
options: dict | None = None,
text: str | None = None,
) -> str:
"""Renders the barcode and saves it in `filename`.

Expand Down Expand Up @@ -98,7 +137,7 @@ def render(
self,
writer_options: dict | None = None,
text: str | None = None,
) -> bytes | T_Image:
) -> T_Output:
"""Renders the barcode using `self.writer`.

:param writer_options: Options for `self.writer`, see writer docs for details.
Expand Down
26 changes: 23 additions & 3 deletions barcode/codabar.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
__docformat__ = "restructuredtext en"

from typing import TYPE_CHECKING
from typing import overload

from barcode.base import Barcode
from barcode.charsets import codabar
from barcode.errors import BarcodeError
from barcode.errors import IllegalCharacterError
from barcode.writer import T_Output

if TYPE_CHECKING:
from barcode.writer import BaseWriter


class CODABAR(Barcode):
class CODABAR(Barcode[T_Output]):
"""Initializes a new CODABAR instance.

:param code: Codabar (NW-7) string that matches [ABCD][0-9$:/.+-]+[ABCD]
Expand All @@ -30,15 +32,33 @@ class CODABAR(Barcode):

name = "Codabar (NW-7)"

@overload
def __init__(
self,
code: str,
writer: BaseWriter | None = None,
writer: None = None,
narrow: int = 2,
wide: int = 5,
) -> None: ...

@overload
def __init__(
self,
code: str,
writer: BaseWriter[T_Output],
narrow: int = 2,
wide: int = 5,
) -> None: ...

def __init__(
self,
code: str,
writer: BaseWriter[T_Output] | None = None,
narrow: int = 2,
wide: int = 5,
) -> None:
self.code = code
self.writer = writer or self.default_writer()
self.writer = self._resolve_writer(writer)
self.narrow = narrow
self.wide = wide

Expand Down
Loading
Loading