1
0
mirror of https://github.com/python-escpos/python-escpos synced 2025-09-13 09:09:58 +00:00

start removal of six and improve type annotation (#607)

* fix unfinished Python2 -> 3 translation
* remove some six usage
* annotate
* fix regression in Six removal
* mypy: self.enf is always defined
* fix return type of cups.py
* Usb idVendor/idProduct are integers
* self.default_args is always defined
* tweak usb_args, PEP589 is better
* lp.py: reassure mypy
* correctly cast call to CashDrawerError()
* update CUPS test
* add missing close() method in metaclass
* document a bug in typeshed
* query_status() returns bytes as seen in constants.py
* remove more SIX usage
* test examples too
* remove type comment where type is annotated
* adapt behavior of cups printer to match other implementations

---------

Co-authored-by: Patrick Kanzler <dev@pkanzler.de>
Co-authored-by: Patrick Kanzler <4189642+patkan@users.noreply.github.com>
This commit is contained in:
Alexandre Detiste
2023-12-16 22:02:24 +01:00
committed by GitHub
parent 06bdb56937
commit 66a2e78e16
31 changed files with 245 additions and 237 deletions

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""python-escpos enables you to manipulate escpos-printers."""
__all__ = ["constants", "escpos", "exceptions", "printer"]
__all__ = ["constants", "escpos", "exceptions", "printer", "__version__"]
try:
from .version import version as __version__ # noqa

View File

@@ -11,7 +11,6 @@ from tempfile import mkdtemp
from typing import Any, Dict, Optional, Type
import importlib_resources
import six
import yaml
logging.basicConfig()
@@ -92,7 +91,7 @@ class NotSupported(Exception):
BARCODE_B = "barcodeB"
class BaseProfile(object):
class BaseProfile:
"""This represents a printer profile.
A printer profile knows about the number of columns, supported
@@ -111,20 +110,22 @@ class BaseProfile(object):
Makes sure that the requested `font` is valid.
"""
font = {"a": 0, "b": 1}.get(font, font)
if not six.text_type(font) in self.fonts:
if not str(font) in self.fonts:
raise NotSupported(f'"{font}" is not a valid font in the current profile')
return font
def get_columns(self, font):
def get_columns(self, font) -> int:
"""Return the number of columns for the given font."""
font = self.get_font(font)
return self.fonts[six.text_type(font)]["columns"]
columns = self.fonts[str(font)]["columns"]
assert type(columns) is int
return columns
def supports(self, feature):
def supports(self, feature) -> bool:
"""Return true/false for the given feature."""
return self.features.get(feature)
def get_code_pages(self):
def get_code_pages(self) -> Dict[str, int]:
"""Return the support code pages as a ``{name: index}`` dict."""
return {v: k for k, v in self.codePages.items()}
@@ -161,7 +162,7 @@ def get_profile_class(name: str) -> Type[BaseProfile]:
return CLASS_CACHE[name]
def clean(s):
def clean(s: str) -> str:
"""Clean profile name."""
# Remove invalid characters
s = re.sub("[^0-9a-zA-Z_]", "", s)
@@ -180,14 +181,14 @@ class Profile(ProfileBaseClass):
For users, who want to provide their own profile.
"""
def __init__(self, columns=None, features=None):
def __init__(self, columns: Optional[int] = None, features=None) -> None:
"""Initialize profile."""
super(Profile, self).__init__()
self.columns = columns
self.features = features or {}
def get_columns(self, font):
def get_columns(self, font) -> int:
"""Get column count of printer."""
if self.columns is not None:
return self.columns

View File

@@ -20,15 +20,13 @@ except ImportError:
pass # noqa
import sys
import six
from . import config
from . import printer as escpos_printer_module
from . import version
# Must be defined before it's used in DEMO_FUNCTIONS
def str_to_bool(string):
def str_to_bool(string: str) -> bool:
"""Convert string to bool.
Used as a type in argparse so that we get back a proper
@@ -563,7 +561,7 @@ def generate_parser() -> argparse.ArgumentParser:
return parser
def main():
def main() -> None:
"""Handle main entry point of CLI script.
Handles loading of configuration and creating and processing of command
@@ -580,9 +578,7 @@ def main():
if not args_dict:
parser.print_help()
sys.exit()
command_arguments = dict(
[k, v] for k, v in six.iteritems(args_dict) if v is not None
)
command_arguments = dict([k, v] for k, v in args_dict.items() if v is not None)
# If version should be printed, do this, then exit
print_version = command_arguments.pop("version", None)
@@ -621,7 +617,7 @@ def main():
globals()[target_command](**command_arguments)
def demo(printer, **kwargs):
def demo(printer, **kwargs) -> None:
"""Print demos.
Called when CLI is passed `demo`. This function

View File

@@ -11,7 +11,7 @@ import yaml
from . import exceptions, printer
class Config(object):
class Config:
"""Configuration handler class.
This class loads configuration from a default or specified directory. It
@@ -21,7 +21,7 @@ class Config(object):
_app_name = "python-escpos"
_config_file = "config.yaml"
def __init__(self):
def __init__(self) -> None:
"""Initialize configuration.
Remember to add anything that needs to be reset between configurations
@@ -33,7 +33,7 @@ class Config(object):
self._printer_name = None
self._printer_config = None
def _reset_config(self):
def _reset_config(self) -> None:
"""Clear the loaded configuration.
If we are loading a changed config, we don't want to have leftover

View File

@@ -46,9 +46,7 @@ HW_RESET: bytes = ESC + b"\x3f\x0a\x00" # Reset printer hardware
# (TODO: Where is this specified?)
# Cash Drawer (ESC p <pin> <on time: 2*ms> <off time: 2*ms>)
_CASH_DRAWER = (
lambda m, t1="", t2="": ESC + b"p" + m + six.int2byte(t1) + six.int2byte(t2)
)
_CASH_DRAWER = lambda m, t1="", t2="": ESC + b"p" + m + bytes((t1, t2))
#: decimal cash drawer kick sequence
CD_KICK_DEC_SEQUENCE = (

View File

@@ -9,13 +9,14 @@ This module contains the abstract base class :py:class:`Escpos`.
:copyright: Copyright (c) 2012-2017 Bashlinux and python-escpos
:license: MIT
"""
from __future__ import annotations
import textwrap
import warnings
from abc import ABCMeta, abstractmethod # abstract base class support
from re import match as re_match
from typing import List, Literal, Optional, Union
from types import TracebackType
from typing import Any, Literal, Optional, Union
import barcode
import qrcode
@@ -107,8 +108,7 @@ SW_BARCODE_NAMES = {
}
@six.add_metaclass(ABCMeta)
class Escpos(object):
class Escpos(object, metaclass=ABCMeta):
"""ESC/POS Printer object.
This class is the abstract base class for an Esc/Pos-printer. The printer implementations are children of this
@@ -154,6 +154,10 @@ class Escpos(object):
"""Open a printer device/connection."""
pass
def close(self):
"""Close a printer device/connection."""
pass
@abstractmethod
def _raw(self, msg: bytes) -> None:
"""Send raw data to the printer.
@@ -164,7 +168,7 @@ class Escpos(object):
"""
pass
def _read(self):
def _read(self) -> bytes:
"""Read from printer.
Returns a NotImplementedError if the instance of the class doesn't override this method.
@@ -250,7 +254,7 @@ class Escpos(object):
header = (
GS
+ b"v0"
+ six.int2byte(density_byte)
+ bytes((density_byte,))
+ self._int_low_high(im.width_bytes, 2)
+ self._int_low_high(im.height, 2)
)
@@ -263,8 +267,8 @@ class Escpos(object):
)
tone = b"0"
colors = b"1"
ym = six.int2byte(1 if high_density_vertical else 2)
xm = six.int2byte(1 if high_density_horizontal else 2)
ym = b"\x01" if high_density_vertical else b"\x02"
xm = b"\x01" if high_density_horizontal else b"\x02"
header = tone + xm + ym + colors + img_header
raster_data = im.to_raster_format()
self._image_send_graphics_data(b"0", b"p", header + raster_data)
@@ -847,7 +851,7 @@ class Escpos(object):
image = my_code.writer._image
self.image(image, impl=impl, center=center)
def text(self, txt):
def text(self, txt: str) -> None:
"""Print alpha-numeric text.
The text has to be encoded in the currently selected codepage.
@@ -856,10 +860,9 @@ class Escpos(object):
:param txt: text to be printed
:raises: :py:exc:`~escpos.exceptions.TextError`
"""
txt = six.text_type(txt)
self.magic.write(txt)
self.magic.write(str(txt))
def textln(self, txt=""):
def textln(self, txt: str = "") -> None:
"""Print alpha-numeric text with a newline.
The text has to be encoded in the currently selected codepage.
@@ -870,7 +873,7 @@ class Escpos(object):
"""
self.text(f"{txt}\n")
def ln(self, count=1):
def ln(self, count: int = 1) -> None:
"""Print a newline or more.
:param count: number of newlines to print
@@ -881,7 +884,7 @@ class Escpos(object):
if count > 0:
self.text("\n" * count)
def block_text(self, txt, font="0", columns=None):
def block_text(self, txt, font="0", columns=None) -> None:
"""Print text wrapped to specific columns.
Text has to be encoded in unicode.
@@ -1132,7 +1135,7 @@ class Escpos(object):
try:
self._raw(CD_KICK_DEC_SEQUENCE(*pin))
except TypeError as err:
raise CashDrawerError(err)
raise CashDrawerError(str(err))
def linedisplay_select(self, select_display: bool = False) -> None:
"""Select the line display or the printer.
@@ -1265,10 +1268,10 @@ class Escpos(object):
else:
self._raw(PANEL_BUTTON_OFF)
def query_status(self, mode: bytes) -> List[int]:
def query_status(self, mode: bytes) -> bytes:
"""Query the printer for its status.
Returns an array of integers containing it.
Returns byte array containing it.
:param mode: Integer that sets the status mode queried to the printer.
- RT_STATUS_ONLINE: Printer status.
@@ -1288,7 +1291,7 @@ class Escpos(object):
return False
return not (status[0] & RT_MASK_ONLINE)
def paper_status(self):
def paper_status(self) -> int: # could be IntEnum
"""Query the paper status of the printer.
Returns 2 if there is plenty of paper, 1 if the paper has arrived to
@@ -1305,6 +1308,8 @@ class Escpos(object):
return 1
if status[0] & RT_MASK_PAPER == RT_MASK_PAPER:
return 2
# not reached
return 0
def target(self, type: str = "ROLL") -> None:
"""Select where to print to.
@@ -1359,7 +1364,7 @@ class Escpos(object):
self._raw(BUZZER + six.int2byte(times) + six.int2byte(duration))
class EscposIO(object):
class EscposIO:
r"""ESC/POS Printer IO object.
Allows the class to be used together with the `with`-statement. You have to define a printer instance
@@ -1405,12 +1410,12 @@ class EscposIO(object):
"""
self.params.update(kwargs)
def writelines(self, text, **kwargs):
def writelines(self, text: str, **kwargs) -> None:
"""Print text."""
params = dict(self.params)
params.update(kwargs)
if isinstance(text, six.text_type):
if isinstance(text, str):
lines = text.split("\n")
elif isinstance(text, list) or isinstance(text, tuple):
lines = text
@@ -1423,23 +1428,22 @@ class EscposIO(object):
# TODO flush? or on print? (this should prob rather be handled by the _raw-method)
for line in lines:
self.printer.set(**params)
if isinstance(text, six.text_type):
self.printer.text(f"{line}\n")
else:
self.printer.text(f"{line}\n")
self.printer.text(f"{line}\n")
def close(self):
def close(self) -> None:
"""Close printer.
Called upon closing the `with`-statement.
"""
self.printer.close()
def __enter__(self, **kwargs):
def __enter__(self, **kwargs: Any) -> "EscposIO":
"""Enter context."""
return self
def __exit__(self, type, value, traceback):
def __exit__(
self, type: type[BaseException], value: BaseException, traceback: TracebackType
) -> None:
"""Cut and close if configured.
If :py:attr:`autocut <escpos.escpos.EscposIO.autocut>` is `True` (set by this class' constructor),

View File

@@ -26,6 +26,8 @@ Result/Exit codes:
:license: MIT
"""
from typing import Optional
class Error(Exception):
"""Base class for ESC/POS errors.
@@ -37,7 +39,7 @@ class Error(Exception):
"""
def __init__(self, msg, status=None):
def __init__(self, msg: str, status: Optional[int] = None) -> None:
"""Initialize Error object."""
Exception.__init__(self)
self.msg = msg
@@ -45,7 +47,7 @@ class Error(Exception):
if status is not None:
self.resultcode = status
def __str__(self):
def __str__(self) -> str:
"""Return string representation of Error."""
return self.msg
@@ -64,13 +66,13 @@ class BarcodeTypeError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize BarcodeTypeError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 10
def __str__(self):
def __str__(self) -> str:
"""Return string representation of BarcodeTypeError."""
return f"No Barcode type is defined ({self.msg})"
@@ -89,13 +91,13 @@ class BarcodeSizeError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize BarcodeSizeError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 20
def __str__(self):
def __str__(self) -> str:
"""Return string representation of BarcodeSizeError."""
return f"Barcode size is out of range ({self.msg})"
@@ -114,13 +116,13 @@ class BarcodeCodeError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize BarcodeCodeError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 30
def __str__(self):
def __str__(self) -> str:
"""Return string representation of BarcodeCodeError."""
return f"No Barcode code was supplied ({self.msg})"
@@ -137,13 +139,13 @@ class ImageSizeError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize ImageSizeError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 40
def __str__(self):
def __str__(self) -> str:
"""Return string representation of ImageSizeError."""
return f"Image height is longer than 255px and can't be printed ({self.msg})"
@@ -160,13 +162,13 @@ class ImageWidthError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize ImageWidthError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 41
def __str__(self):
def __str__(self) -> str:
"""Return string representation of ImageWidthError."""
return f"Image width is too large ({self.msg})"
@@ -184,13 +186,13 @@ class TextError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize TextError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 50
def __str__(self):
def __str__(self) -> str:
"""Return string representation of TextError."""
return f"Text string must be supplied to the text() method ({self.msg})"
@@ -208,13 +210,13 @@ class CashDrawerError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize CashDrawerError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 60
def __str__(self):
def __str__(self) -> str:
"""Return string representation of CashDrawerError."""
return f"Valid pin must be set to send pulse ({self.msg})"
@@ -235,13 +237,13 @@ class TabPosError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize TabPosError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 70
def __str__(self):
def __str__(self) -> str:
"""Return string representation of TabPosError."""
return f"Valid tab positions must be in the range 0 to 16 ({self.msg})"
@@ -259,13 +261,13 @@ class CharCodeError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize CharCodeError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 80
def __str__(self):
def __str__(self) -> str:
"""Return string representation of CharCodeError."""
return f"Valid char code must be set ({self.msg})"
@@ -283,13 +285,13 @@ class DeviceNotFoundError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize DeviceNotFoundError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 90
def __str__(self):
def __str__(self) -> str:
"""Return string representation of DeviceNotFoundError."""
return f"Device not found ({self.msg})"
@@ -307,13 +309,13 @@ class USBNotFoundError(DeviceNotFoundError):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize USBNotFoundError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 91
def __str__(self):
def __str__(self) -> str:
"""Return string representation of USBNotFoundError."""
return f"USB device not found ({self.msg})"
@@ -331,13 +333,13 @@ class SetVariableError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize SetVariableError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 100
def __str__(self):
def __str__(self) -> str:
"""Return string representation of SetVariableError."""
return f"Set variable out of range ({self.msg})"
@@ -358,13 +360,13 @@ class ConfigNotFoundError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize ConfigNotFoundError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 200
def __str__(self):
def __str__(self) -> str:
"""Return string representation of ConfigNotFoundError."""
return f"Configuration not found ({self.msg})"
@@ -382,13 +384,13 @@ class ConfigSyntaxError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize ConfigSyntaxError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 210
def __str__(self):
def __str__(self) -> str:
"""Return string representation of ConfigSyntaxError."""
return f"Configuration syntax is invalid ({self.msg})"
@@ -406,12 +408,12 @@ class ConfigSectionMissingError(Error):
"""
def __init__(self, msg=""):
def __init__(self, msg: str = "") -> None:
"""Initialize ConfigSectionMissingError object."""
Error.__init__(self, msg)
self.msg = msg
self.resultcode = 220
def __str__(self):
def __str__(self) -> str:
"""Return string representation of ConfigSectionMissingError."""
return f"Configuration section is missing ({self.msg})"

View File

@@ -10,12 +10,12 @@ This module contains the image format handler :py:class:`EscposImage`.
import math
from typing import Union
from typing import Iterator, Union
from PIL import Image, ImageOps
class EscposImage(object):
class EscposImage:
"""
Load images in, and output ESC/POS formats.
@@ -23,7 +23,7 @@ class EscposImage(object):
PIL, rather than spend CPU cycles looping over pixels.
"""
def __init__(self, img_source: Union[Image.Image, str]):
def __init__(self, img_source: Union[Image.Image, str]) -> None:
"""Load in an image.
:param img_source: PIL.Image, or filename to load one from.
@@ -65,7 +65,7 @@ class EscposImage(object):
_, height_pixels = self._im.size
return height_pixels
def to_column_format(self, high_density_vertical: bool = True):
def to_column_format(self, high_density_vertical: bool = True) -> Iterator[bytes]:
"""Extract slices of an image as equal-sized blobs of column-format data.
:param high_density_vertical: Printed line height in dots
@@ -82,7 +82,7 @@ class EscposImage(object):
yield (im_bytes)
left += line_height
def to_raster_format(self):
def to_raster_format(self) -> bytes:
"""Convert image to raster-format binary."""
return self._im.tobytes()

View File

@@ -11,7 +11,7 @@ except ImportError:
jaconv = None
def encode_katakana(text):
def encode_katakana(text: str) -> bytes:
"""I don't think this quite works yet."""
encoded = []
for char in text:

View File

@@ -23,7 +23,7 @@ from .constants import CODEPAGE_CHANGE
from .exceptions import Error
class Encoder(object):
class Encoder:
"""Take available code spaces and pick the right one for a given character.
Note: To determine the code page, it needs to do the conversion, and
@@ -202,7 +202,7 @@ def split_writable_text(encoder, text, encoding):
return text, None
class MagicEncode(object):
class MagicEncode:
"""Help switching to the right code page.
A helper that helps us to automatically switch to the right
@@ -292,7 +292,7 @@ class MagicEncode(object):
def write_with_encoding(self, encoding, text):
"""Write the text and inject necessary codepage switches."""
if text is not None and type(text) is not six.text_type:
if text is not None and type(text) is not str:
raise Error(
f"The supplied text has to be unicode, but is of type {type(text)}."
)

View File

@@ -84,7 +84,7 @@ class CupsPrinter(Escpos):
return is_usable()
@dependency_pycups
def __init__(self, printer_name: str = "", *args, **kwargs):
def __init__(self, printer_name: str = "", *args, **kwargs) -> None:
"""Class constructor for CupsPrinter.
:param printer_name: CUPS printer name (Optional)
@@ -163,11 +163,10 @@ class CupsPrinter(Escpos):
return
logging.info("CupsPrinter printer enabled")
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Append any command sent in raw format to temporary file.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
self.pending_job = True
try:
@@ -176,8 +175,9 @@ class CupsPrinter(Escpos):
self.pending_job = False
raise TypeError("Bytes required. Printer job not opened")
def send(self):
def send(self) -> None:
"""Send the print job to the printer."""
assert self.device
if self.pending_job:
# Rewind tempfile
self.tmpfile.seek(0)
@@ -190,7 +190,7 @@ class CupsPrinter(Escpos):
)
self._clear()
def _clear(self):
def _clear(self) -> None:
"""Finish the print job.
Remove temporary file.
@@ -198,18 +198,18 @@ class CupsPrinter(Escpos):
self.tmpfile.close()
self.pending_job = False
def _read(self):
def _read(self) -> bytes:
"""Return a single-item array with the accepting state of the print queue.
states: idle = [3], printing a job = [4], stopped = [5]
"""
printer = self.printers.get(self.printer_name, {})
state = printer.get("printer-state")
if not state:
return []
return [state]
if not state or state in [4, 5]:
return b"8" # offline
return b"0" # online
def close(self):
def close(self) -> None:
"""Close CUPS connection.
Send pending job to the printer if needed.

View File

@@ -7,6 +7,7 @@
:copyright: Copyright (c) 2012-2023 Bashlinux and python-escpos
:license: MIT
"""
from typing import List
from ..escpos import Escpos
@@ -39,25 +40,24 @@ class Dummy(Escpos):
"""
return is_usable()
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
"""Init with empty output list."""
Escpos.__init__(self, *args, **kwargs)
self._output_list = []
self._output_list: List[bytes] = []
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
self._output_list.append(msg)
@property
def output(self):
def output(self) -> bytes:
"""Get the data that was sent to this printer."""
return b"".join(self._output_list)
def clear(self):
def clear(self) -> None:
"""Clear the buffer of the printer.
This method can be called if you send the contents to a physical printer
@@ -65,6 +65,6 @@ class Dummy(Escpos):
"""
del self._output_list[:]
def close(self):
def close(self) -> None:
"""Close not implemented for Dummy printer."""
pass

View File

@@ -88,12 +88,12 @@ class File(Escpos):
if self.device:
self.device.flush()
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
assert self.device
self.device.write(msg)
if self.auto_flush:
self.flush()

View File

@@ -182,12 +182,13 @@ class LP(Escpos):
if not self._is_closing:
self.open(_close_opened=False)
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Write raw command(s) to the printer.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
assert self.device is not None
assert self.device.stdin is not None
if self.device.stdin.writable():
self.device.stdin.write(msg)
else:

View File

@@ -110,16 +110,17 @@ class Network(Escpos):
return
logging.info("Network printer enabled")
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
assert self.device
self.device.sendall(msg)
def _read(self):
def _read(self) -> bytes:
"""Read data from the TCP socket."""
assert self.device
return self.device.recv(16)
def close(self) -> None:

View File

@@ -155,16 +155,17 @@ class Serial(Escpos):
return
logging.info("Serial printer enabled")
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
assert self.device
self.device.write(msg)
def _read(self):
def _read(self) -> bytes:
"""Read the data buffer and return it to the caller."""
assert self.device
return self.device.read(16)
def close(self) -> None:

View File

@@ -74,9 +74,9 @@ class Usb(Escpos):
def __init__(
self,
idVendor: str = "",
idProduct: str = "",
usb_args: Dict[str, str] = {},
idVendor: Optional[int] = None,
idProduct: Optional[int] = None,
usb_args: Dict[str, Union[str, int]] = {},
timeout: Union[int, float] = 0,
in_ep: int = 0x82,
out_ep: int = 0x01,
@@ -181,16 +181,17 @@ class Usb(Escpos):
except usb.core.USBError as e:
logging.error("Could not set configuration: %s", str(e))
def _raw(self, msg):
def _raw(self, msg: bytes) -> None:
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
assert self.device
self.device.write(self.out_ep, msg, self.timeout)
def _read(self):
def _read(self) -> bytes:
"""Read a data buffer and return it to the caller."""
assert self.device
return self.device.read(self.in_ep, 16)
@dependency_usb

View File

@@ -76,7 +76,7 @@ class Win32Raw(Escpos):
return is_usable()
@dependency_win32print
def __init__(self, printer_name: str = "", *args, **kwargs):
def __init__(self, printer_name: str = "", *args, **kwargs) -> None:
"""Initialize default printer."""
Escpos.__init__(self, *args, **kwargs)
self.printer_name = printer_name
@@ -148,14 +148,17 @@ class Win32Raw(Escpos):
win32print.ClosePrinter(self._device)
self._device = False
def _raw(self, msg) -> None:
def _raw(self, msg: bytes) -> None:
"""Print any command sent in raw format.
:param msg: arbitrary code to be printed
:type msg: bytes
"""
if self.printer_name is None:
raise DeviceNotFoundError("Printer not found")
if not self.device:
raise DeviceNotFoundError("Printer job not opened")
win32print.WritePrinter(self.device, msg)
win32print.WritePrinter(self.device, msg) # type: ignore
# there is a bug in the typeshed
# https://github.com/mhammond/pywin32/blob/main/win32/src/win32print/win32print.cpp#L976
# https://github.com/python/typeshed/blob/main/stubs/pywin32/win32/win32print.pyi#L27C4-L27C4