diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index 472ee54..5f7937c 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -26,6 +26,13 @@ except ImportError: # TODO: dev build mode that let's the wrapper bypass? +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + usable = False + if not _DEP_PYCUPS: + usable = True + return usable + def dependency_pycups(func): """Indicate dependency on pycups.""" @@ -33,7 +40,7 @@ def dependency_pycups(func): @functools.wraps(func) def wrapper(*args, **kwargs): """Throw a RuntimeError if pycups is not imported.""" - if not _DEP_PYCUPS: + if is_usable(): raise RuntimeError( "Printing with PyCups requires the pycups library to" "be installed. Please refer to the documentation on" diff --git a/src/escpos/printer/dummy.py b/src/escpos/printer/dummy.py index 8a0f28d..297c488 100644 --- a/src/escpos/printer/dummy.py +++ b/src/escpos/printer/dummy.py @@ -11,6 +11,11 @@ from ..escpos import Escpos +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + return True + + class Dummy(Escpos): """Dummy printer. diff --git a/src/escpos/printer/file.py b/src/escpos/printer/file.py index ae5cbbd..d1c970c 100644 --- a/src/escpos/printer/file.py +++ b/src/escpos/printer/file.py @@ -11,6 +11,11 @@ from ..escpos import Escpos +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + return True + + class File(Escpos): """Generic file printer. diff --git a/src/escpos/printer/lp.py b/src/escpos/printer/lp.py index 107d525..d091032 100644 --- a/src/escpos/printer/lp.py +++ b/src/escpos/printer/lp.py @@ -16,13 +16,21 @@ import sys from ..escpos import Escpos +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + usable = False + if not sys.platform.startswith("win"): + usable = True + return usable + + def dependency_linux_lp(func): """Indicate dependency on non Windows.""" @functools.wraps(func) def wrapper(*args, **kwargs): """Throw a RuntimeError if not on a non-Windows system.""" - if not sys.platform.startswith("win"): + if is_usable(): raise RuntimeError( "This printer driver depends on LP which is not" "available on Windows systems." diff --git a/src/escpos/printer/network.py b/src/escpos/printer/network.py index 2dadc2d..80ff8b1 100644 --- a/src/escpos/printer/network.py +++ b/src/escpos/printer/network.py @@ -13,6 +13,11 @@ import socket from ..escpos import Escpos +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + return True + + class Network(Escpos): """Network printer. diff --git a/src/escpos/printer/serial.py b/src/escpos/printer/serial.py index eb1d4ff..5905feb 100644 --- a/src/escpos/printer/serial.py +++ b/src/escpos/printer/serial.py @@ -24,13 +24,21 @@ except ImportError: pass +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + usable = False + if not _DEP_PYSERIAL: + usable = True + return usable + + def dependency_pyserial(func): """Indicate dependency on pyserial.""" @functools.wraps(func) def wrapper(*args, **kwargs): """Throw a RuntimeError if pyserial not installed.""" - if not _DEP_PYSERIAL: + if is_usable(): raise RuntimeError( "Printing with Serial requires the pyserial library to" "be installed. Please refer to the documentation on" diff --git a/src/escpos/printer/usb.py b/src/escpos/printer/usb.py index d1bd4cc..d0f7643 100644 --- a/src/escpos/printer/usb.py +++ b/src/escpos/printer/usb.py @@ -7,13 +7,47 @@ :copyright: Copyright (c) 2012-2023 Bashlinux and python-escpos :license: MIT """ - -import usb.core -import usb.util +import functools from ..escpos import Escpos from ..exceptions import USBNotFoundError +#: keeps track if the usb dependency could be loaded (:py:class:`escpos.printer.Usb`) +_DEP_USB = False + +try: + import usb.core + import usb.util + + _DEP_USB = True +except ImportError: + pass + + +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + usable = False + if not _DEP_USB: + usable = True + return usable + + +def dependency_usb(func): + """Indicate dependency on usb.""" + + @functools.wraps(func) + def wrapper(*args, **kwargs): + """Throw a RuntimeError if usb not installed.""" + if is_usable(): + raise RuntimeError( + "Printing with USB connection requires a usb library to" + "be installed. Please refer to the documentation on" + "what to install and install the dependencies for USB." + ) + return func(*args, **kwargs) + + return wrapper + class Usb(Escpos): """USB printer. @@ -37,7 +71,7 @@ class Usb(Escpos): out_ep=0x01, *args, **kwargs - ): # noqa: N803 + ): """Initialize USB printer. :param idVendor: Vendor ID @@ -59,6 +93,7 @@ class Usb(Escpos): usb_args["idProduct"] = idProduct self.open(usb_args) + @dependency_usb def open(self, usb_args): """Search device on USB tree and set it as escpos device. @@ -110,6 +145,7 @@ class Usb(Escpos): """Read a data buffer and return it to the caller.""" return self.device.read(self.in_ep, 16) + @dependency_usb def close(self): """Release USB interface.""" if self.device: diff --git a/src/escpos/printer/win32raw.py b/src/escpos/printer/win32raw.py index 72dcb6f..d02ebf1 100644 --- a/src/escpos/printer/win32raw.py +++ b/src/escpos/printer/win32raw.py @@ -8,68 +8,99 @@ :license: MIT """ +import functools + from ..escpos import Escpos -_WIN32PRINT = False +#: keeps track if the win32print dependency could be loaded (:py:class:`escpos.printer.Win32Raw`) +_DEP_WIN32PRINT = False + try: import win32print - _WIN32PRINT = True + _DEP_WIN32PRINT = True except ImportError: pass -if _WIN32PRINT: +def is_usable(): + """Indicate whether this component can be used due to dependencies.""" + usable = False + if not _DEP_WIN32PRINT: + usable = True + return usable - class Win32Raw(Escpos): - """Printer binding for win32 API. - Uses the module pywin32 for printing. +def dependency_win32print(func): + """Indicate dependency on win32print.""" - inheritance: - - .. inheritance-diagram:: escpos.printer.Win32Raw - :parts: 1 - - """ - - def __init__(self, printer_name=None, *args, **kwargs): - """Initialize default printer.""" - Escpos.__init__(self, *args, **kwargs) - if printer_name is not None: - self.printer_name = printer_name - else: - self.printer_name = win32print.GetDefaultPrinter() - self.hPrinter = None - self.open() - - def open(self, job_name="python-escpos"): - """Open connection to default printer.""" - if self.printer_name is None: - raise Exception("Printer not found") - self.hPrinter = win32print.OpenPrinter(self.printer_name) - self.current_job = win32print.StartDocPrinter( - self.hPrinter, 1, (job_name, None, "RAW") + @functools.wraps(func) + def wrapper(*args, **kwargs): + """Throw a RuntimeError if win32print not installed.""" + if is_usable(): + raise RuntimeError( + "Printing with Win32Raw requires a win32print library to" + "be installed. Please refer to the documentation on" + "what to install and install the dependencies for win32print." ) - win32print.StartPagePrinter(self.hPrinter) + return func(*args, **kwargs) - def close(self): - """Close connection to default printer.""" - if not self.hPrinter: - return - win32print.EndPagePrinter(self.hPrinter) - win32print.EndDocPrinter(self.hPrinter) - win32print.ClosePrinter(self.hPrinter) - self.hPrinter = None + return wrapper - def _raw(self, msg): - """Print any command sent in raw format. - :param msg: arbitrary code to be printed - :type msg: bytes - """ - if self.printer_name is None: - raise Exception("Printer not found") - if self.hPrinter is None: - raise Exception("Printer job not opened") - win32print.WritePrinter(self.hPrinter, msg) +class Win32Raw(Escpos): + """Printer binding for win32 API. + + Uses the module pywin32 for printing. + + inheritance: + + .. inheritance-diagram:: escpos.printer.Win32Raw + :parts: 1 + + """ + + @dependency_win32print + def __init__(self, printer_name=None, *args, **kwargs): + """Initialize default printer.""" + Escpos.__init__(self, *args, **kwargs) + if printer_name is not None: + self.printer_name = printer_name + else: + self.printer_name = win32print.GetDefaultPrinter() + self.hPrinter = None + self.open() + + @dependency_win32print + def open(self, job_name="python-escpos"): + """Open connection to default printer.""" + if self.printer_name is None: + raise Exception("Printer not found") + self.hPrinter = win32print.OpenPrinter(self.printer_name) + self.current_job = win32print.StartDocPrinter( + self.hPrinter, 1, (job_name, None, "RAW") + ) + win32print.StartPagePrinter(self.hPrinter) + + @dependency_win32print + def close(self): + """Close connection to default printer.""" + if not self.hPrinter: + return + win32print.EndPagePrinter(self.hPrinter) + win32print.EndDocPrinter(self.hPrinter) + win32print.ClosePrinter(self.hPrinter) + self.hPrinter = None + + @dependency_win32print + def _raw(self, msg): + """Print any command sent in raw format. + + :param msg: arbitrary code to be printed + :type msg: bytes + """ + if self.printer_name is None: + raise Exception("Printer not found") + if self.hPrinter is None: + raise Exception("Printer job not opened") + win32print.WritePrinter(self.hPrinter, msg)