From bea4f29d28b3a93e17102b838a0e26539fd60cdf Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Sun, 10 Dec 2023 01:54:48 +0100 Subject: [PATCH 01/27] fix unfinished Python2 -> 3 translation found by mypy --- examples/weather.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/weather.py b/examples/weather.py index 23a06198..795d092b 100644 --- a/examples/weather.py +++ b/examples/weather.py @@ -18,7 +18,7 @@ import json import os import time -import urllib +from urllib.request import urlopen from datetime import datetime from escpos.printer import Usb @@ -93,7 +93,7 @@ def icon(): + LONG + "?exclude=[alerts,minutely,hourly,flags]&units=si" ) # change last bit to 'us' for Fahrenheit -response = urllib.urlopen(url) +response = urlopen(url) data = json.loads(response.read()) printer.print_and_feed(n=1) From 35565d556431c957a123bc5c2215fc37bffd4042 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Sun, 10 Dec 2023 01:56:31 +0100 Subject: [PATCH 02/27] remove some six usage --- src/escpos/capabilities.py | 5 ++--- src/escpos/cli.py | 4 +--- src/escpos/escpos.py | 10 +++------- src/escpos/magicencode.py | 2 +- test/test_function_cut.py | 4 +--- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index d8a4b58a..ae64f32a 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -11,7 +11,6 @@ from typing import Any, Dict, Optional, Type import importlib_resources -import six import yaml logging.basicConfig() @@ -113,7 +112,7 @@ def get_font(self, font) -> int: 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( '"{}" is not a valid font in the current profile'.format(font) ) @@ -122,7 +121,7 @@ def get_font(self, font) -> int: def get_columns(self, font): """Return the number of columns for the given font.""" font = self.get_font(font) - return self.fonts[six.text_type(font)]["columns"] + return self.fonts[str(font)]["columns"] def supports(self, feature): """Return true/false for the given feature.""" diff --git a/src/escpos/cli.py b/src/escpos/cli.py index 6d99b90a..2f79507b 100644 --- a/src/escpos/cli.py +++ b/src/escpos/cli.py @@ -20,8 +20,6 @@ pass # noqa import sys -import six - from . import config from . import printer as escpos_printer_module from . import version @@ -581,7 +579,7 @@ def main(): parser.print_help() sys.exit() command_arguments = dict( - [k, v] for k, v in six.iteritems(args_dict) if v is not None + [k, v] for k, v in args_dict.items() if v is not None ) # If version should be printed, do this, then exit diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 9c8f35de..64bc4520 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -866,8 +866,7 @@ def text(self, txt): :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=""): """Print alpha-numeric text with a newline. @@ -1420,7 +1419,7 @@ def writelines(self, text, **kwargs): 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 @@ -1433,10 +1432,7 @@ def writelines(self, text, **kwargs): # 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("{0}\n".format(line)) - else: - self.printer.text("{0}\n".format(line)) + self.printer.text("{0}\n".format(line)) def close(self): """Close printer. diff --git a/src/escpos/magicencode.py b/src/escpos/magicencode.py index 7cb17be2..5e79100b 100644 --- a/src/escpos/magicencode.py +++ b/src/escpos/magicencode.py @@ -292,7 +292,7 @@ def _handle_character_failed(self, char): 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( "The supplied text has to be unicode, but is of type {type}.".format( type=type(text) diff --git a/test/test_function_cut.py b/test/test_function_cut.py index 75333c22..b92c0201 100644 --- a/test/test_function_cut.py +++ b/test/test_function_cut.py @@ -1,5 +1,3 @@ -import six - import escpos.printer as printer from escpos.constants import GS @@ -8,5 +6,5 @@ def test_cut_without_feed(): """Test cut without feeding paper""" instance = printer.Dummy() instance.cut(feed=False) - expected = GS + b"V" + six.int2byte(66) + b"\x00" + expected = GS + b"V" + bytes((66,)) + b"\x00" assert instance.output == expected From 6aec41fbd85451a302fef477df5339a411d6a35d Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Sun, 10 Dec 2023 02:49:26 +0100 Subject: [PATCH 03/27] annotate --- src/escpos/capabilities.py | 18 +++++----- src/escpos/cli.py | 6 ++-- src/escpos/config.py | 9 ++--- src/escpos/constants.py | 2 +- src/escpos/escpos.py | 41 ++++++++++++--------- src/escpos/exceptions.py | 66 +++++++++++++++++----------------- src/escpos/image.py | 10 +++--- src/escpos/magicencode.py | 4 +-- src/escpos/printer/cups.py | 15 ++++---- src/escpos/printer/dummy.py | 14 ++++---- src/escpos/printer/file.py | 4 +-- src/escpos/printer/lp.py | 2 +- src/escpos/printer/network.py | 6 ++-- src/escpos/printer/serial.py | 6 ++-- src/escpos/printer/usb.py | 6 ++-- src/escpos/printer/win32raw.py | 4 +-- 16 files changed, 116 insertions(+), 97 deletions(-) diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index ae64f32a..e58ef474 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -93,7 +93,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 @@ -118,16 +118,18 @@ def get_font(self, font) -> int: ) 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[str(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()} @@ -164,7 +166,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) @@ -183,14 +185,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 diff --git a/src/escpos/cli.py b/src/escpos/cli.py index 2f79507b..2b916346 100644 --- a/src/escpos/cli.py +++ b/src/escpos/cli.py @@ -26,7 +26,7 @@ # 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 @@ -561,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 @@ -619,7 +619,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 diff --git a/src/escpos/config.py b/src/escpos/config.py index f0d249b6..3682c231 100644 --- a/src/escpos/config.py +++ b/src/escpos/config.py @@ -4,6 +4,7 @@ """ import os import pathlib +from typing import Optional import appdirs import yaml @@ -11,7 +12,7 @@ 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 +22,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 +34,7 @@ def __init__(self): 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 @@ -45,7 +46,7 @@ def _reset_config(self): self._printer_name = None self._printer_config = None - def load(self, config_path=None): + def load(self, config_path = None): """Load and parse the configuration file using pyyaml. :param config_path: An optional file path, file handle, or byte string diff --git a/src/escpos/constants.py b/src/escpos/constants.py index 5328f4e6..fb055021 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -47,7 +47,7 @@ # Cash Drawer (ESC p ) _CASH_DRAWER = ( - lambda m, t1="", t2="": ESC + b"p" + m + six.int2byte(t1) + six.int2byte(t2) + lambda m, t1="", t2="": ESC + b"p" + m + bytes((t1, t2)) ) #: decimal cash drawer kick sequence diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 64bc4520..8816c987 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -15,7 +15,8 @@ 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 List, Literal, Optional, Union, Any import barcode import qrcode @@ -107,8 +108,7 @@ } -@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 @@ -164,7 +164,7 @@ def _raw(self, msg: bytes) -> None: """ 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 +250,7 @@ def image( 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 +263,8 @@ def image( ) 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"1" if high_density_vertical else b"2" + xm = b"1" if high_density_horizontal else b"2" 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) @@ -857,7 +857,7 @@ def _sw_barcode( 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. @@ -868,7 +868,7 @@ def text(self, 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. @@ -879,7 +879,7 @@ def textln(self, txt=""): """ self.text("{}\n".format(txt)) - def ln(self, count=1): + def ln(self, count: int = 1) -> None: """Print a newline or more. :param count: number of newlines to print @@ -890,7 +890,7 @@ def ln(self, count=1): 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. @@ -1297,7 +1297,7 @@ def is_online(self) -> bool: 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 @@ -1314,6 +1314,8 @@ def paper_status(self): 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. @@ -1368,7 +1370,7 @@ def buzzer(self, times: int = 2, duration: int = 4) -> None: 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 @@ -1414,7 +1416,7 @@ def set(self, **kwargs) -> None: """ 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) @@ -1434,18 +1436,23 @@ def writelines(self, text, **kwargs): self.printer.set(**params) self.printer.text("{0}\n".format(line)) - 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 ` is `True` (set by this class' constructor), diff --git a/src/escpos/exceptions.py b/src/escpos/exceptions.py index a5834677..d8e28cc9 100644 --- a/src/escpos/exceptions.py +++ b/src/escpos/exceptions.py @@ -26,6 +26,8 @@ :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 @@ def __init__(self, msg, status=None): 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 "No Barcode type is defined ({msg})".format(msg=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 "Barcode size is out of range ({msg})".format(msg=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 "No Barcode code was supplied ({msg})".format(msg=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 "Image height is longer than 255px and can't be printed ({msg})".format( msg=self.msg @@ -162,13 +164,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 "Image width is too large ({msg})".format(msg=self.msg) @@ -186,13 +188,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 "Text string must be supplied to the text() method ({msg})".format( msg=self.msg @@ -212,13 +214,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 "Valid pin must be set to send pulse ({msg})".format(msg=self.msg) @@ -239,13 +241,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 "Valid tab positions must be in the range 0 to 16 ({msg})".format( msg=self.msg @@ -265,13 +267,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 "Valid char code must be set ({msg})".format(msg=self.msg) @@ -289,13 +291,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})" @@ -313,13 +315,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})" @@ -337,13 +339,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 "Set variable out of range ({msg})".format(msg=self.msg) @@ -364,13 +366,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 "Configuration not found ({msg})".format(msg=self.msg) @@ -388,13 +390,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 "Configuration syntax is invalid ({msg})".format(msg=self.msg) @@ -412,12 +414,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 "Configuration section is missing ({msg})".format(msg=self.msg) diff --git a/src/escpos/image.py b/src/escpos/image.py index 70fae528..69070787 100644 --- a/src/escpos/image.py +++ b/src/escpos/image.py @@ -10,12 +10,12 @@ import math -from typing import Union +from typing import Union, Iterator 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 @@ def height(self) -> int: _, 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 @@ def to_column_format(self, high_density_vertical: bool = True): 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() diff --git a/src/escpos/magicencode.py b/src/escpos/magicencode.py index 5e79100b..1af31318 100644 --- a/src/escpos/magicencode.py +++ b/src/escpos/magicencode.py @@ -23,7 +23,7 @@ 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 diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index b4d430e6..faae1616 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -11,7 +11,7 @@ import functools import logging import tempfile -from typing import Literal, Optional, Type, Union +from typing import Literal, Optional, Type, Union, List from ..escpos import Escpos from ..exceptions import DeviceNotFoundError @@ -84,7 +84,7 @@ def is_usable() -> bool: 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,7 +163,7 @@ def open( 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 @@ -176,8 +176,9 @@ def _raw(self, msg): 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 +191,7 @@ def send(self): ) self._clear() - def _clear(self): + def _clear(self) -> None: """Finish the print job. Remove temporary file. @@ -198,7 +199,7 @@ def _clear(self): self.tmpfile.close() self.pending_job = False - def _read(self): + def _read(self) -> List[int]: """Return a single-item array with the accepting state of the print queue. states: idle = [3], printing a job = [4], stopped = [5] @@ -209,7 +210,7 @@ def _read(self): return [] return [state] - def close(self): + def close(self) -> None: """Close CUPS connection. Send pending job to the printer if needed. diff --git a/src/escpos/printer/dummy.py b/src/escpos/printer/dummy.py index 36601baa..a712016e 100644 --- a/src/escpos/printer/dummy.py +++ b/src/escpos/printer/dummy.py @@ -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 @@ def is_usable() -> bool: """ 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 @@ def clear(self): """ del self._output_list[:] - def close(self): + def close(self) -> None: """Close not implemented for Dummy printer.""" pass diff --git a/src/escpos/printer/file.py b/src/escpos/printer/file.py index 67cbe4f8..9082265d 100644 --- a/src/escpos/printer/file.py +++ b/src/escpos/printer/file.py @@ -88,12 +88,12 @@ def flush(self) -> None: 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() diff --git a/src/escpos/printer/lp.py b/src/escpos/printer/lp.py index 951df66e..f928d382 100644 --- a/src/escpos/printer/lp.py +++ b/src/escpos/printer/lp.py @@ -182,7 +182,7 @@ def flush(self) -> None: 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 diff --git a/src/escpos/printer/network.py b/src/escpos/printer/network.py index 5b0d0fae..7a2d5c1e 100644 --- a/src/escpos/printer/network.py +++ b/src/escpos/printer/network.py @@ -110,16 +110,18 @@ def open(self, raise_not_found: bool = True) -> None: 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: diff --git a/src/escpos/printer/serial.py b/src/escpos/printer/serial.py index bbe1d25b..66592af9 100644 --- a/src/escpos/printer/serial.py +++ b/src/escpos/printer/serial.py @@ -155,16 +155,18 @@ def open(self, raise_not_found: bool = True) -> None: return logging.info("Serial printer enabled") - def _raw(self, msg): + def _raw(self, msg) -> 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: diff --git a/src/escpos/printer/usb.py b/src/escpos/printer/usb.py index d07df7b6..9f7dde7f 100644 --- a/src/escpos/printer/usb.py +++ b/src/escpos/printer/usb.py @@ -181,16 +181,18 @@ def _configure_usb(self) -> None: 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 diff --git a/src/escpos/printer/win32raw.py b/src/escpos/printer/win32raw.py index 2b267328..c4aed39d 100644 --- a/src/escpos/printer/win32raw.py +++ b/src/escpos/printer/win32raw.py @@ -76,7 +76,7 @@ def is_usable() -> bool: 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,7 +148,7 @@ def close(self) -> None: 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 From 5bd936b585d9a6c6666d21f444ffdb3f5e09837f Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Sun, 10 Dec 2023 22:47:32 +0100 Subject: [PATCH 04/27] run isort & black --- examples/weather.py | 2 +- src/escpos/cli.py | 4 +--- src/escpos/config.py | 2 +- src/escpos/constants.py | 4 +--- src/escpos/escpos.py | 13 +++++-------- src/escpos/exceptions.py | 2 +- src/escpos/image.py | 2 +- src/escpos/printer/cups.py | 2 +- 8 files changed, 12 insertions(+), 19 deletions(-) diff --git a/examples/weather.py b/examples/weather.py index 795d092b..f9203059 100644 --- a/examples/weather.py +++ b/examples/weather.py @@ -18,8 +18,8 @@ import json import os import time -from urllib.request import urlopen from datetime import datetime +from urllib.request import urlopen from escpos.printer import Usb diff --git a/src/escpos/cli.py b/src/escpos/cli.py index 2b916346..e7cc826a 100644 --- a/src/escpos/cli.py +++ b/src/escpos/cli.py @@ -578,9 +578,7 @@ def main() -> None: if not args_dict: parser.print_help() sys.exit() - command_arguments = dict( - [k, v] for k, v in args_dict.items() 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) diff --git a/src/escpos/config.py b/src/escpos/config.py index 3682c231..5b72764e 100644 --- a/src/escpos/config.py +++ b/src/escpos/config.py @@ -46,7 +46,7 @@ def _reset_config(self) -> None: self._printer_name = None self._printer_config = None - def load(self, config_path = None): + def load(self, config_path=None): """Load and parse the configuration file using pyyaml. :param config_path: An optional file path, file handle, or byte string diff --git a/src/escpos/constants.py b/src/escpos/constants.py index fb055021..c435bcc7 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -46,9 +46,7 @@ # (TODO: Where is this specified?) # Cash Drawer (ESC p ) -_CASH_DRAWER = ( - lambda m, t1="", t2="": ESC + b"p" + m + bytes((t1, t2)) -) +_CASH_DRAWER = lambda m, t1="", t2="": ESC + b"p" + m + bytes((t1, t2)) #: decimal cash drawer kick sequence CD_KICK_DEC_SEQUENCE = ( diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 8816c987..36d43033 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -16,7 +16,7 @@ from abc import ABCMeta, abstractmethod # abstract base class support from re import match as re_match from types import TracebackType -from typing import List, Literal, Optional, Union, Any +from typing import Any, List, Literal, Optional, Union import barcode import qrcode @@ -857,7 +857,7 @@ def _sw_barcode( image = my_code.writer._image self.image(image, impl=impl, center=center) - def text(self, txt:str) -> None: + def text(self, txt: str) -> None: """Print alpha-numeric text. The text has to be encoded in the currently selected codepage. @@ -1297,7 +1297,7 @@ def is_online(self) -> bool: return False return not (status[0] & RT_MASK_ONLINE) - def paper_status(self) -> int: # could be IntEnum + 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 @@ -1443,15 +1443,12 @@ def close(self) -> None: """ self.printer.close() - def __enter__(self, **kwargs: Any) -> 'EscposIO': + def __enter__(self, **kwargs: Any) -> "EscposIO": """Enter context.""" return self def __exit__( - self, - type: type[BaseException], - value: BaseException, - traceback: TracebackType + self, type: type[BaseException], value: BaseException, traceback: TracebackType ) -> None: """Cut and close if configured. diff --git a/src/escpos/exceptions.py b/src/escpos/exceptions.py index d8e28cc9..2b3e98c2 100644 --- a/src/escpos/exceptions.py +++ b/src/escpos/exceptions.py @@ -39,7 +39,7 @@ class Error(Exception): """ - def __init__(self, msg:str, status: Optional[int] = None) -> None: + def __init__(self, msg: str, status: Optional[int] = None) -> None: """Initialize Error object.""" Exception.__init__(self) self.msg = msg diff --git a/src/escpos/image.py b/src/escpos/image.py index 69070787..6be9f0f1 100644 --- a/src/escpos/image.py +++ b/src/escpos/image.py @@ -10,7 +10,7 @@ import math -from typing import Union, Iterator +from typing import Iterator, Union from PIL import Image, ImageOps diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index faae1616..527523a6 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -11,7 +11,7 @@ import functools import logging import tempfile -from typing import Literal, Optional, Type, Union, List +from typing import List, Literal, Optional, Type, Union from ..escpos import Escpos from ..exceptions import DeviceNotFoundError From 324a236ae64b341ff2c106d2275090316a21f25f Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Sun, 10 Dec 2023 22:59:37 +0100 Subject: [PATCH 05/27] pyflakes3 --- src/escpos/__init__.py | 2 +- src/escpos/config.py | 1 - src/escpos/katakana.py | 2 +- test/test_cli.py | 19 +++++++++---------- test/test_function_text.py | 15 +++++++-------- test/test_magicencode.py | 14 +++++++------- 6 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/escpos/__init__.py b/src/escpos/__init__.py index 286a6e65..94afabbd 100644 --- a/src/escpos/__init__.py +++ b/src/escpos/__init__.py @@ -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 diff --git a/src/escpos/config.py b/src/escpos/config.py index 5b72764e..ecdbee68 100644 --- a/src/escpos/config.py +++ b/src/escpos/config.py @@ -4,7 +4,6 @@ """ import os import pathlib -from typing import Optional import appdirs import yaml diff --git a/src/escpos/katakana.py b/src/escpos/katakana.py index 2eb99f7a..b20ac677 100644 --- a/src/escpos/katakana.py +++ b/src/escpos/katakana.py @@ -11,7 +11,7 @@ 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: diff --git a/test/test_cli.py b/test/test_cli.py index c40f8cc2..c09cdda4 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -7,7 +7,6 @@ import shutil import tempfile -import pytest from scripttest import TestFileEnvironment as TFE import escpos @@ -31,18 +30,18 @@ class TestCLI: """Contains setups, teardowns, and tests for CLI""" @classmethod - def setup_class(cls): + def setup_class(cls) -> None: """Create a config file to read from""" with open(CONFIGFILE, "w") as config: config.write(CONFIG_YAML) @classmethod - def teardown_class(cls): + def teardown_class(cls) -> None: """Remove config file""" os.remove(CONFIGFILE) shutil.rmtree(TEST_DIR) - def setup_method(self): + def setup_method(self) -> None: """Create a file to print to and set up env""" self.env = None self.default_args = None @@ -64,24 +63,24 @@ def setup_method(self): finally: fhandle.close() - def teardown_method(self): + def teardown_method(self) -> None: """Destroy printer file and env""" os.remove(DEVFILE) self.env.clear() - def test_cli_help(self): + def test_cli_help(self) -> None: """Test getting help from cli""" result = self.env.run("python-escpos", "-h") assert not result.stderr assert "usage" in result.stdout - def test_cli_version(self): + def test_cli_version(self) -> None: """Test the version string""" result = self.env.run("python-escpos", "version") assert not result.stderr assert escpos.__version__ == result.stdout.strip() - def test_cli_version_extended(self): + def test_cli_version_extended(self) -> None: """Test the extended version information""" result = self.env.run("python-escpos", "version_extended") assert not result.stderr @@ -89,7 +88,7 @@ def test_cli_version_extended(self): # test that additional information on e.g. Serial is printed assert "Serial" in result.stdout - def test_cli_text(self): + def test_cli_text(self) -> None: """Make sure text returns what we sent it""" test_text = "this is some text" result = self.env.run( @@ -108,7 +107,7 @@ def test_cli_text(self): result.files_updated[DEVFILE_NAME].bytes == "\x1bt\x00" + test_text + "\n" ) - def test_cli_text_invalid_args(self): + def test_cli_text_invalid_args(self) -> None: """Test a failure to send valid arguments""" result = self.env.run( *(self.default_args + ("text", "--invalid-param", "some data")), diff --git a/test/test_function_text.py b/test/test_function_text.py index 79a2b238..7a72f34e 100644 --- a/test/test_function_text.py +++ b/test/test_function_text.py @@ -10,8 +10,7 @@ import hypothesis.strategies as st import mock -import pytest -from hypothesis import assume, given +from hypothesis import given from escpos.printer import Dummy @@ -21,7 +20,7 @@ def get_printer(): @given(text=st.text()) -def test_text(text): +def test_text(text: str) -> None: """Test that text() calls the MagicEncode object.""" instance = get_printer() instance.magic.write = mock.Mock() @@ -29,7 +28,7 @@ def test_text(text): instance.magic.write.assert_called_with(text) -def test_block_text(): +def test_block_text() -> None: printer = get_printer() printer.block_text( "All the presidents men were eating falafel for breakfast.", font="a" @@ -39,25 +38,25 @@ def test_block_text(): ) -def test_textln(): +def test_textln() -> None: printer = get_printer() printer.textln("hello, world") assert printer.output == b"hello, world\n" -def test_textln_empty(): +def test_textln_empty() -> None: printer = get_printer() printer.textln() assert printer.output == b"\n" -def test_ln(): +def test_ln() -> None: printer = get_printer() printer.ln() assert printer.output == b"\n" -def test_multiple_ln(): +def test_multiple_ln() -> None: printer = get_printer() printer.ln(3) assert printer.output == b"\n\n\n" diff --git a/test/test_magicencode.py b/test/test_magicencode.py index da457188..dc3729f7 100644 --- a/test/test_magicencode.py +++ b/test/test_magicencode.py @@ -13,7 +13,7 @@ import pytest from hypothesis import example, given -from escpos.exceptions import CharCodeError, Error +from escpos.exceptions import Error from escpos.katakana import encode_katakana from escpos.magicencode import Encoder, MagicEncode @@ -23,23 +23,23 @@ class TestEncoder: Tests the single encoders. """ - def test_can_encode(self): + def test_can_encode(self) -> None: assert not Encoder({"CP437": 1}).can_encode("CP437", "€") assert Encoder({"CP437": 1}).can_encode("CP437", "á") assert not Encoder({"foobar": 1}).can_encode("foobar", "a") - def test_find_suitable_encoding(self): + def test_find_suitable_encoding(self) -> None: assert not Encoder({"CP437": 1}).find_suitable_encoding("€") assert Encoder({"CP858": 1}).find_suitable_encoding("€") == "CP858" - def test_find_suitable_encoding_unnecessary_codepage_swap(self): + def test_find_suitable_encoding_unnecessary_codepage_swap(self) -> None: enc = Encoder({"CP857": 1, "CP437": 2, "CP1252": 3, "CP852": 4, "CP858": 5}) # desired behavior would be that the encoder always stays in the lower # available codepages if possible for character in ("Á", "É", "Í", "Ó", "Ú"): assert enc.find_suitable_encoding(character) == "CP857" - def test_get_encoding(self): + def test_get_encoding(self) -> None: with pytest.raises(ValueError): Encoder({}).get_encoding_name("latin1") @@ -122,9 +122,9 @@ class TestKatakana: @example("カタカナ") @example("あいうえお") @example("ハンカクカタカナ") - def test_accept(self, text): + def test_accept(self, text: str) -> None: encode_katakana(text) - def test_result(self): + def test_result(self) -> None: assert encode_katakana("カタカナ") == b"\xb6\xc0\xb6\xc5" assert encode_katakana("あいうえお") == b"\xb1\xb2\xb3\xb4\xb5" From 8c8660bb8f9ebfac95d63978e27ba8251f0e7aa7 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Sun, 10 Dec 2023 23:46:24 +0100 Subject: [PATCH 06/27] fix regression in Six removal --- src/escpos/escpos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 36d43033..f5500f62 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -263,8 +263,8 @@ def image( ) tone = b"0" colors = b"1" - ym = b"1" if high_density_vertical else b"2" - xm = b"1" if high_density_horizontal else b"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) From 369e709fc29d82b0d7d92009dc9c6f37c1190e65 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 Dec 2023 00:42:38 +0100 Subject: [PATCH 07/27] use Type instead of type --- src/escpos/escpos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 742e6025..5a5e2f8b 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -16,7 +16,7 @@ from abc import ABCMeta, abstractmethod # abstract base class support from re import match as re_match from types import TracebackType -from typing import Any, List, Literal, Optional, Union +from typing import Any, List, Literal, Optional, Type, Union import barcode import qrcode @@ -1438,7 +1438,7 @@ def __enter__(self, **kwargs: Any) -> "EscposIO": return self def __exit__( - self, type: type[BaseException], value: BaseException, traceback: TracebackType + self, type: Type[BaseException], value: BaseException, traceback: TracebackType ) -> None: """Cut and close if configured. From 9a941e87469c0451c72fed3a7be8260d6ce450be Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Mon, 11 Dec 2023 00:44:19 +0100 Subject: [PATCH 08/27] from __future__ import annotations --- src/escpos/escpos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 5a5e2f8b..11efdf66 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -9,7 +9,7 @@ :copyright: Copyright (c) 2012-2017 Bashlinux and python-escpos :license: MIT """ - +from __future__ import annotations import textwrap import warnings From e6380d1fc69fd697375d746d01aed396e5560074 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 Dec 2023 00:48:17 +0100 Subject: [PATCH 09/27] Revert "use Type instead of type" This reverts commit 369e709fc29d82b0d7d92009dc9c6f37c1190e65. --- src/escpos/escpos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 11efdf66..1c87b66f 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -16,7 +16,7 @@ from abc import ABCMeta, abstractmethod # abstract base class support from re import match as re_match from types import TracebackType -from typing import Any, List, Literal, Optional, Type, Union +from typing import Any, List, Literal, Optional, Union import barcode import qrcode @@ -1438,7 +1438,7 @@ def __enter__(self, **kwargs: Any) -> "EscposIO": return self def __exit__( - self, type: Type[BaseException], value: BaseException, traceback: TracebackType + self, type: type[BaseException], value: BaseException, traceback: TracebackType ) -> None: """Cut and close if configured. From 56cabcedd73d00ea6076c190ca05fb7db270962b Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Mon, 11 Dec 2023 01:04:02 +0100 Subject: [PATCH 10/27] mypy: self.enf is always defined --- test/test_cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_cli.py b/test/test_cli.py index c09cdda4..adbeb9d1 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -43,7 +43,6 @@ def teardown_class(cls) -> None: def setup_method(self) -> None: """Create a file to print to and set up env""" - self.env = None self.default_args = None self.env = TFE( From a8e38bed9a3d81714047cfab92cb2796d2ff3b8b Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:18:15 +0100 Subject: [PATCH 11/27] fix return type of cups.py --- src/escpos/printer/cups.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index 527523a6..0236dd69 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -199,7 +199,7 @@ def _clear(self) -> None: self.tmpfile.close() self.pending_job = False - def _read(self) -> List[int]: + 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] @@ -207,8 +207,8 @@ def _read(self) -> List[int]: printer = self.printers.get(self.printer_name, {}) state = printer.get("printer-state") if not state: - return [] - return [state] + return b'' + return bytes((state,)) def close(self) -> None: """Close CUPS connection. From a0ec58d21301fe0a4b1c7627eff1b0355d531f75 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:21:44 +0100 Subject: [PATCH 12/27] Usb idVendor/idProduct are integers --- src/escpos/printer/usb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/escpos/printer/usb.py b/src/escpos/printer/usb.py index 9f7dde7f..0d12df42 100644 --- a/src/escpos/printer/usb.py +++ b/src/escpos/printer/usb.py @@ -74,8 +74,8 @@ def is_usable() -> bool: def __init__( self, - idVendor: str = "", - idProduct: str = "", + idVendor: Optional[int] = None, + idProduct: Optional[int] = None, usb_args: Dict[str, str] = {}, timeout: Union[int, float] = 0, in_ep: int = 0x82, From 44893f95cf36f9d0e5f71e4788eb12a97b332726 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:23:49 +0100 Subject: [PATCH 13/27] self.default_args is always defined --- test/test_cli.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_cli.py b/test/test_cli.py index adbeb9d1..2c55743d 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -43,8 +43,6 @@ def teardown_class(cls) -> None: def setup_method(self) -> None: """Create a file to print to and set up env""" - self.default_args = None - self.env = TFE( base_path=TEST_DIR, cwd=os.getcwd(), From fb6b9d94a253a4fb8e020baefc6d6782d6538013 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:27:03 +0100 Subject: [PATCH 14/27] tweak usb_args, PEP589 is better --- src/escpos/printer/usb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/escpos/printer/usb.py b/src/escpos/printer/usb.py index 0d12df42..20743ad6 100644 --- a/src/escpos/printer/usb.py +++ b/src/escpos/printer/usb.py @@ -76,7 +76,7 @@ def __init__( self, idVendor: Optional[int] = None, idProduct: Optional[int] = None, - usb_args: Dict[str, str] = {}, + usb_args: Dict[str, Union[str, int]] = {}, timeout: Union[int, float] = 0, in_ep: int = 0x82, out_ep: int = 0x01, From 11474723e22bc6b93861579381d8d2623c1041d1 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:29:25 +0100 Subject: [PATCH 15/27] lp.py: reassure mypy --- src/escpos/printer/lp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/escpos/printer/lp.py b/src/escpos/printer/lp.py index f928d382..e8b7bcf0 100644 --- a/src/escpos/printer/lp.py +++ b/src/escpos/printer/lp.py @@ -188,6 +188,8 @@ def _raw(self, msg: bytes) -> None: :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: From e7db5a9b4e831be9b708ebba72fb2d5ebb9416c3 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:39:31 +0100 Subject: [PATCH 16/27] run black --- src/escpos/printer/cups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index 0236dd69..2ac3fff0 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -207,7 +207,7 @@ def _read(self) -> bytes: printer = self.printers.get(self.printer_name, {}) state = printer.get("printer-state") if not state: - return b'' + return b"" return bytes((state,)) def close(self) -> None: From 512a5ff24abe59c999bdb7916d0bb4275160feaa Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:44:10 +0100 Subject: [PATCH 17/27] correctly cast call to CashDrawerError() --- src/escpos/escpos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 1c87b66f..8cab39f1 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -1131,7 +1131,7 @@ def cashdraw(self, pin) -> None: 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. From 45829b8ecb9d6ec2f33336da5b572801a49be233 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:47:01 +0100 Subject: [PATCH 18/27] pyflakes3 --- src/escpos/printer/cups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index 2ac3fff0..a7ee2c8f 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -11,7 +11,7 @@ import functools import logging import tempfile -from typing import List, Literal, Optional, Type, Union +from typing import Literal, Optional, Type, Union from ..escpos import Escpos from ..exceptions import DeviceNotFoundError From 254eae07ef345bef788e108176b659c2353e8449 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 21:59:48 +0100 Subject: [PATCH 19/27] update CUPS test --- test/test_printers/test_printer_cups.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/test_printers/test_printer_cups.py b/test/test_printers/test_printer_cups.py index e39b851e..1b9cb431 100644 --- a/test/test_printers/test_printer_cups.py +++ b/test/test_printers/test_printer_cups.py @@ -19,7 +19,7 @@ ) -def test_device_not_initialized(cupsprinter): +def test_device_not_initialized(cupsprinter) -> None: """ GIVEN a cups printer object WHEN it is not initialized @@ -28,7 +28,7 @@ def test_device_not_initialized(cupsprinter): assert cupsprinter._device is False -def test_open_raise_exception(cupsprinter, devicenotfounderror): +def test_open_raise_exception(cupsprinter, devicenotfounderror) -> None: """ GIVEN a cups printer object WHEN open() is set to raise a DeviceNotFoundError on error @@ -40,7 +40,7 @@ def test_open_raise_exception(cupsprinter, devicenotfounderror): cupsprinter.open(raise_not_found=True) -def test_open_not_raise_exception(cupsprinter, caplog): +def test_open_not_raise_exception(cupsprinter, caplog) -> None: """ GIVEN a cups printer object WHEN open() is set to not raise on error but simply cancel @@ -55,7 +55,7 @@ def test_open_not_raise_exception(cupsprinter, caplog): assert cupsprinter.device is None -def test_open(cupsprinter, caplog, mocker): +def test_open(cupsprinter, caplog, mocker) -> None: """ GIVEN a cups printer object and a mocked pycups device WHEN a valid connection to a device is opened @@ -74,7 +74,7 @@ def test_open(cupsprinter, caplog, mocker): assert cupsprinter.device -def test_close_on_reopen(cupsprinter, mocker): +def test_close_on_reopen(cupsprinter, mocker) -> None: """ GIVEN a cups printer object and a mocked connection WHEN a valid connection to a device is reopened before close @@ -112,7 +112,7 @@ def test_close(cupsprinter, caplog, mocker): assert cupsprinter._device is False -def test_send_on_close(cupsprinter, mocker): +def test_send_on_close(cupsprinter, mocker) -> None: """ GIVEN a cups printer object and a mocked pycups device WHEN closing connection before send the buffer @@ -133,7 +133,7 @@ def test_send_on_close(cupsprinter, mocker): assert cupsprinter.pending_job is False -def test_raw_raise_exception(cupsprinter): +def test_raw_raise_exception(cupsprinter) -> None: """ GIVEN a cups printer object WHEN passing a non byte string to _raw() @@ -145,7 +145,7 @@ def test_raw_raise_exception(cupsprinter): assert cupsprinter.pending_job is False -def test_raw(cupsprinter): +def test_raw(cupsprinter) -> None: """ GIVEN a cups printer object WHEN passing a byte string to _raw() @@ -156,7 +156,7 @@ def test_raw(cupsprinter): assert cupsprinter.tmpfile.read() == b"Test" -def test_printers_no_device(cupsprinter): +def test_printers_no_device(cupsprinter) -> None: """ GIVEN a cups printer object WHEN device is None @@ -166,11 +166,11 @@ def test_printers_no_device(cupsprinter): assert cupsprinter.printers == {} -def test_read_no_device(cupsprinter): +def test_read_no_device(cupsprinter) -> None: """ GIVEN a cups printer object WHEN device is None - THEN check the return value is [] + THEN check the return value is b'' """ cupsprinter.device = None - assert cupsprinter._read() == [] + assert cupsprinter._read() == b'' From aa2e5cd819207d2db612dbde2f5952354864933f Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 22:08:47 +0100 Subject: [PATCH 20/27] add missing close() method in metaclass --- src/escpos/escpos.py | 4 ++++ test/test_printers/test_printer_cups.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 8cab39f1..31e0b9a7 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -154,6 +154,10 @@ def open(self): """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. diff --git a/test/test_printers/test_printer_cups.py b/test/test_printers/test_printer_cups.py index 1b9cb431..eb4a6f51 100644 --- a/test/test_printers/test_printer_cups.py +++ b/test/test_printers/test_printer_cups.py @@ -173,4 +173,4 @@ def test_read_no_device(cupsprinter) -> None: THEN check the return value is b'' """ cupsprinter.device = None - assert cupsprinter._read() == b'' + assert cupsprinter._read() == b"" From 180721c03cc13e3f335398289db6c08395b3369b Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 22:55:30 +0100 Subject: [PATCH 21/27] document a bug in typeshed --- src/escpos/printer/win32raw.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/escpos/printer/win32raw.py b/src/escpos/printer/win32raw.py index c4aed39d..8de56ebf 100644 --- a/src/escpos/printer/win32raw.py +++ b/src/escpos/printer/win32raw.py @@ -158,4 +158,8 @@ def _raw(self, msg: bytes) -> 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 From 0f2ee2b8b5c5fd63ab4f16b29933f238639602f8 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Wed, 13 Dec 2023 23:24:20 +0100 Subject: [PATCH 22/27] query_status() returns bytes as seen in constants.py --- src/escpos/escpos.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 31e0b9a7..ada52ec8 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -16,7 +16,7 @@ from abc import ABCMeta, abstractmethod # abstract base class support from re import match as re_match from types import TracebackType -from typing import Any, List, Literal, Optional, Union +from typing import Any, Literal, Optional, Union import barcode import qrcode @@ -1268,10 +1268,10 @@ def panel_buttons(self, enable: bool = True) -> None: 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. From 99b9484c4be45a3db776dc93e901977f6e9cf14b Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Thu, 14 Dec 2023 00:26:30 +0100 Subject: [PATCH 23/27] remove more SIX usage --- examples/codepage_tables.py | 8 +++--- test/test_function_buzzer.py | 13 ++++----- test/test_function_image.py | 30 ++++++++++----------- test/test_function_linedisplay.py | 6 ++--- test/test_function_set.py | 45 ++++++++++++++++--------------- test/test_image.py | 23 ++++++++-------- 6 files changed, 65 insertions(+), 60 deletions(-) diff --git a/examples/codepage_tables.py b/examples/codepage_tables.py index 94369107..3053681c 100644 --- a/examples/codepage_tables.py +++ b/examples/codepage_tables.py @@ -3,8 +3,6 @@ import sys -import six - from escpos import printer from escpos.constants import ( CODEPAGE_CHANGE, @@ -38,7 +36,7 @@ def print_codepage(printer, codepage): """Print a codepage.""" if codepage.isdigit(): codepage = int(codepage) - printer._raw(CODEPAGE_CHANGE + six.int2byte(codepage)) + printer._raw(CODEPAGE_CHANGE + bytes((codepage,))) printer._raw("after") else: printer.charcode(codepage) @@ -58,7 +56,9 @@ def print_codepage(printer, codepage): printer.set() for y in range(0, 16): - byte = six.int2byte(x * 16 + y) + byte = bytes( + (x * 16 + y), + ) if byte in (ESC, CTL_LF, CTL_FF, CTL_CR, CTL_HT, CTL_VT): byte = " " diff --git a/test/test_function_buzzer.py b/test/test_function_buzzer.py index 85d5cd73..2118cc0a 100644 --- a/test/test_function_buzzer.py +++ b/test/test_function_buzzer.py @@ -1,14 +1,13 @@ import pytest -import six from escpos import printer from escpos.constants import BUZZER -def test_buzzer_function_with_default_params(): +def test_buzzer_function_with_default_params() -> None: instance = printer.Dummy() instance.buzzer() - expected = BUZZER + six.int2byte(2) + six.int2byte(4) + expected = BUZZER + bytes((2, 4)) assert instance.output == expected @@ -26,10 +25,10 @@ def test_buzzer_function_with_default_params(): [9, 9], ], ) -def test_buzzer_function(times, duration): +def test_buzzer_function(times: int, duration: int) -> None: instance = printer.Dummy() instance.buzzer(times, duration) - expected = BUZZER + six.int2byte(times) + six.int2byte(duration) + expected = BUZZER + bytes((times, duration)) assert instance.output == expected @@ -46,7 +45,9 @@ def test_buzzer_function(times, duration): [3, 11, "duration must be between 1 and 9"], ], ) -def test_buzzer_fuction_with_outrange_values(times, duration, expected_message): +def test_buzzer_fuction_with_outrange_values( + times: int, duration: int, expected_message: str +) -> None: instance = printer.Dummy() with pytest.raises(ValueError) as e: instance.buzzer(times, duration) diff --git a/test/test_function_image.py b/test/test_function_image.py index 981d5362..c4b31a55 100644 --- a/test/test_function_image.py +++ b/test/test_function_image.py @@ -16,7 +16,7 @@ # Raster format print -def test_bit_image_black(): +def test_bit_image_black() -> None: """ Test printing solid black bit image (raster) """ @@ -30,7 +30,7 @@ def test_bit_image_black(): assert instance.output == b"\x1dv0\x00\x01\x00\x01\x00\x80" -def test_bit_image_white(): +def test_bit_image_white() -> None: """ Test printing solid white bit image (raster) """ @@ -39,7 +39,7 @@ def test_bit_image_white(): assert instance.output == b"\x1dv0\x00\x01\x00\x01\x00\x00" -def test_bit_image_both(): +def test_bit_image_both() -> None: """ Test printing black/white bit image (raster) """ @@ -58,7 +58,7 @@ def test_bit_image_transparent(): # Column format print -def test_bit_image_colfmt_black(): +def test_bit_image_colfmt_black() -> None: """ Test printing solid black bit image (column format) """ @@ -67,7 +67,7 @@ def test_bit_image_colfmt_black(): assert instance.output == b"\x1b3\x10\x1b*!\x01\x00\x80\x00\x00\x0a\x1b2" -def test_bit_image_colfmt_white(): +def test_bit_image_colfmt_white() -> None: """ Test printing solid white bit image (column format) """ @@ -76,7 +76,7 @@ def test_bit_image_colfmt_white(): assert instance.output == b"\x1b3\x10\x1b*!\x01\x00\x00\x00\x00\x0a\x1b2" -def test_bit_image_colfmt_both(): +def test_bit_image_colfmt_both() -> None: """ Test printing black/white bit image (column format) """ @@ -87,7 +87,7 @@ def test_bit_image_colfmt_both(): ) -def test_bit_image_colfmt_transparent(): +def test_bit_image_colfmt_transparent() -> None: """ Test printing black/transparent bit image (column format) """ @@ -99,7 +99,7 @@ def test_bit_image_colfmt_transparent(): # Graphics print -def test_graphics_black(): +def test_graphics_black() -> None: """ Test printing solid black graphics """ @@ -111,7 +111,7 @@ def test_graphics_black(): ) -def test_graphics_white(): +def test_graphics_white() -> None: """ Test printing solid white graphics """ @@ -123,7 +123,7 @@ def test_graphics_white(): ) -def test_graphics_both(): +def test_graphics_both() -> None: """ Test printing black/white graphics """ @@ -135,7 +135,7 @@ def test_graphics_both(): ) -def test_graphics_transparent(): +def test_graphics_transparent() -> None: """ Test printing black/transparent graphics """ @@ -147,7 +147,7 @@ def test_graphics_transparent(): ) -def test_large_graphics(): +def test_large_graphics() -> None: """ Test whether 'large' graphics that induce a fragmentation are handled correctly. """ @@ -162,13 +162,13 @@ def test_large_graphics(): @pytest.fixture -def dummy_with_width(): +def dummy_with_width() -> printer.Dummy: instance = printer.Dummy() instance.profile.profile_data = {"media": {"width": {"pixels": 384}}} return instance -def test_width_too_large(dummy_with_width): +def test_width_too_large(dummy_with_width: printer.Dummy) -> None: """ Test printing an image that is too large in width. """ @@ -180,7 +180,7 @@ def test_width_too_large(dummy_with_width): instance.image(Image.new("RGB", (384, 200))) -def test_center_image(dummy_with_width): +def test_center_image(dummy_with_width: printer.Dummy) -> None: instance = dummy_with_width with pytest.raises(ImageWidthError): diff --git a/test/test_function_linedisplay.py b/test/test_function_linedisplay.py index 693706ce..4b057612 100644 --- a/test/test_function_linedisplay.py +++ b/test/test_function_linedisplay.py @@ -11,21 +11,21 @@ import escpos.printer as printer -def test_function_linedisplay_select_on(): +def test_function_linedisplay_select_on() -> None: """test the linedisplay_select function (activate)""" instance = printer.Dummy() instance.linedisplay_select(select_display=True) assert instance.output == b"\x1B\x3D\x02" -def test_function_linedisplay_select_off(): +def test_function_linedisplay_select_off() -> None: """test the linedisplay_select function (deactivate)""" instance = printer.Dummy() instance.linedisplay_select(select_display=False) assert instance.output == b"\x1B\x3D\x01" -def test_function_linedisplay_clear(): +def test_function_linedisplay_clear() -> None: """test the linedisplay_clear function""" instance = printer.Dummy() instance.linedisplay_clear() diff --git a/test/test_function_set.py b/test/test_function_set.py index 90458f37..482631b5 100644 --- a/test/test_function_set.py +++ b/test/test_function_set.py @@ -1,12 +1,13 @@ import pytest import six +from typing import Optional import escpos.printer as printer from escpos.constants import SET_FONT, TXT_NORMAL, TXT_SIZE, TXT_STYLE from escpos.exceptions import SetVariableError -def test_default_values_with_default(): +def test_default_values_with_default() -> None: """Default test, please copy and paste this block to test set method calls""" instance = printer.Dummy() instance.set_with_default() @@ -26,7 +27,7 @@ def test_default_values_with_default(): assert instance.output == b"".join(expected_sequence) -def test_default_values(): +def test_default_values() -> None: """Default test""" instance = printer.Dummy() instance.set() @@ -37,7 +38,7 @@ def test_default_values(): # Size tests -def test_set_size_2h(): +def test_set_size_2h() -> None: instance = printer.Dummy() instance.set_with_default(double_height=True) @@ -56,7 +57,7 @@ def test_set_size_2h(): assert instance.output == b"".join(expected_sequence) -def test_set_size_2h_no_default(): +def test_set_size_2h_no_default() -> None: instance = printer.Dummy() instance.set(double_height=True) @@ -68,7 +69,7 @@ def test_set_size_2h_no_default(): assert instance.output == b"".join(expected_sequence) -def test_set_size_2w(): +def test_set_size_2w() -> None: instance = printer.Dummy() instance.set_with_default(double_width=True) @@ -87,7 +88,7 @@ def test_set_size_2w(): assert instance.output == b"".join(expected_sequence) -def test_set_size_2w_no_default(): +def test_set_size_2w_no_default() -> None: instance = printer.Dummy() instance.set(double_width=True) @@ -99,7 +100,7 @@ def test_set_size_2w_no_default(): assert instance.output == b"".join(expected_sequence) -def test_set_size_2x(): +def test_set_size_2x() -> None: instance = printer.Dummy() instance.set_with_default(double_height=True, double_width=True) @@ -118,7 +119,7 @@ def test_set_size_2x(): assert instance.output == b"".join(expected_sequence) -def test_set_size_2x_no_default(): +def test_set_size_2x_no_default() -> None: instance = printer.Dummy() instance.set(double_width=True, double_height=True) @@ -130,7 +131,7 @@ def test_set_size_2x_no_default(): assert instance.output == b"".join(expected_sequence) -def test_set_size_custom(): +def test_set_size_custom() -> None: instance = printer.Dummy() instance.set_with_default(custom_size=True, width=8, height=7) @@ -151,7 +152,7 @@ def test_set_size_custom(): @pytest.mark.parametrize("width", [1, 8]) @pytest.mark.parametrize("height", [1, 8]) -def test_set_size_custom_no_default(width, height): +def test_set_size_custom_no_default(width: int, height: int) -> None: instance = printer.Dummy() instance.set(custom_size=True, width=width, height=height) @@ -165,7 +166,9 @@ def test_set_size_custom_no_default(width, height): @pytest.mark.parametrize("width", [None, 0, 9, 10, 4444]) @pytest.mark.parametrize("height", [None, 0, 9, 10, 4444]) -def test_set_size_custom_invalid_input(width, height): +def test_set_size_custom_invalid_input( + width: Optional[int], height: Optional[int] +) -> None: instance = printer.Dummy() with pytest.raises(SetVariableError): instance.set(custom_size=True, width=width, height=height) @@ -174,7 +177,7 @@ def test_set_size_custom_invalid_input(width, height): # Flip -def test_set_flip(): +def test_set_flip() -> None: instance = printer.Dummy() instance.set_with_default(flip=True) @@ -193,7 +196,7 @@ def test_set_flip(): assert instance.output == b"".join(expected_sequence) -def test_set_flip_no_default(): +def test_set_flip_no_default() -> None: instance = printer.Dummy() instance.set(flip=True) @@ -205,7 +208,7 @@ def test_set_flip_no_default(): # Smooth -def test_smooth(): +def test_smooth() -> None: instance = printer.Dummy() instance.set_with_default(smooth=True) @@ -227,7 +230,7 @@ def test_smooth(): # Type -def test_set_bold(): +def test_set_bold() -> None: instance = printer.Dummy() instance.set_with_default(bold=True) @@ -246,7 +249,7 @@ def test_set_bold(): assert instance.output == b"".join(expected_sequence) -def test_set_underline(): +def test_set_underline() -> None: instance = printer.Dummy() instance.set_with_default(underline=1) @@ -265,7 +268,7 @@ def test_set_underline(): assert instance.output == b"".join(expected_sequence) -def test_set_underline2(): +def test_set_underline2() -> None: instance = printer.Dummy() instance.set_with_default(underline=2) @@ -287,7 +290,7 @@ def test_set_underline2(): # Align -def test_align_center(): +def test_align_center() -> None: instance = printer.Dummy() instance.set_with_default(align="center") @@ -306,7 +309,7 @@ def test_align_center(): assert instance.output == b"".join(expected_sequence) -def test_align_right(): +def test_align_right() -> None: instance = printer.Dummy() instance.set_with_default(align="right") @@ -328,7 +331,7 @@ def test_align_right(): # Densities -def test_densities(): +def test_densities() -> None: for density in range(8): instance = printer.Dummy() instance.set_with_default(density=density) @@ -352,7 +355,7 @@ def test_densities(): # Invert -def test_invert(): +def test_invert() -> None: instance = printer.Dummy() instance.set_with_default(invert=True) diff --git a/test/test_image.py b/test/test_image.py index 258e4f14..7f121f7c 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -7,11 +7,12 @@ :copyright: Copyright (c) 2016 `Michael Billington `_ :license: MIT """ +from typing import List from escpos.image import EscposImage -def test_image_black(): +def test_image_black() -> None: """ Test rendering solid black image """ @@ -19,7 +20,7 @@ def test_image_black(): _load_and_check_img("canvas_black." + img_format, 1, 1, b"\x80", [b"\x80"]) -def test_image_black_transparent(): +def test_image_black_transparent() -> None: """ Test rendering black/transparent image """ @@ -29,7 +30,7 @@ def test_image_black_transparent(): ) -def test_image_black_white(): +def test_image_black_white() -> None: """ Test rendering black/white image """ @@ -39,7 +40,7 @@ def test_image_black_white(): ) -def test_image_white(): +def test_image_white() -> None: """ Test rendering solid white image """ @@ -47,7 +48,7 @@ def test_image_white(): _load_and_check_img("canvas_white." + img_format, 1, 1, b"\x00", [b"\x00"]) -def test_split(): +def test_split() -> None: """ test whether the split-function works as expected """ @@ -62,12 +63,12 @@ def test_split(): def _load_and_check_img( - filename, - width_expected, - height_expected, - raster_format_expected, - column_format_expected, -): + filename: str, + width_expected: int, + height_expected: int, + raster_format_expected: bytes, + column_format_expected: List[bytes], +) -> None: """ Load an image, and test whether raster & column formatted output, sizes, etc match expectations. """ From 7ad4396b48b02eaba178322ec28a1067b3c9a103 Mon Sep 17 00:00:00 2001 From: Alexandre Detiste Date: Thu, 14 Dec 2023 00:53:24 +0100 Subject: [PATCH 24/27] isort --- test/test_function_set.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_function_set.py b/test/test_function_set.py index 482631b5..93c8f5d5 100644 --- a/test/test_function_set.py +++ b/test/test_function_set.py @@ -1,6 +1,7 @@ +from typing import Optional + import pytest import six -from typing import Optional import escpos.printer as printer from escpos.constants import SET_FONT, TXT_NORMAL, TXT_SIZE, TXT_STYLE From 3fb52f4d9d9a55dfe741c92d3efbbf8ce0c5c584 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Fri, 15 Dec 2023 14:13:16 +0100 Subject: [PATCH 25/27] test examples too --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0d458a4a..b058d514 100644 --- a/tox.ini +++ b/tox.ini @@ -58,6 +58,7 @@ deps = mypy types-Pillow types-pyserial types-pywin32>=306.0.0.6 + types-flask hypothesis>=6.83 jaconv -commands = mypy src test +commands = mypy src test examples From 5c5acff16cc9ead25af37d61c04cee25cc65f81a Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Fri, 15 Dec 2023 15:26:54 +0100 Subject: [PATCH 26/27] remove type comment where type is annotated --- src/escpos/printer/cups.py | 1 - src/escpos/printer/lp.py | 1 - src/escpos/printer/network.py | 1 - src/escpos/printer/serial.py | 3 +-- src/escpos/printer/usb.py | 1 - src/escpos/printer/win32raw.py | 1 - 6 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index a7ee2c8f..e2eea8dc 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -167,7 +167,6 @@ 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: diff --git a/src/escpos/printer/lp.py b/src/escpos/printer/lp.py index e8b7bcf0..ff22e059 100644 --- a/src/escpos/printer/lp.py +++ b/src/escpos/printer/lp.py @@ -186,7 +186,6 @@ 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 diff --git a/src/escpos/printer/network.py b/src/escpos/printer/network.py index 7a2d5c1e..d0338563 100644 --- a/src/escpos/printer/network.py +++ b/src/escpos/printer/network.py @@ -114,7 +114,6 @@ 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) diff --git a/src/escpos/printer/serial.py b/src/escpos/printer/serial.py index 66592af9..28ada62a 100644 --- a/src/escpos/printer/serial.py +++ b/src/escpos/printer/serial.py @@ -155,11 +155,10 @@ def open(self, raise_not_found: bool = True) -> None: return logging.info("Serial printer enabled") - 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 """ assert self.device self.device.write(msg) diff --git a/src/escpos/printer/usb.py b/src/escpos/printer/usb.py index 20743ad6..e5d2dbe4 100644 --- a/src/escpos/printer/usb.py +++ b/src/escpos/printer/usb.py @@ -185,7 +185,6 @@ 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) diff --git a/src/escpos/printer/win32raw.py b/src/escpos/printer/win32raw.py index 8de56ebf..d0844366 100644 --- a/src/escpos/printer/win32raw.py +++ b/src/escpos/printer/win32raw.py @@ -152,7 +152,6 @@ 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") From b634354c9903f12ac5c71264c103e47676410b8c Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sat, 16 Dec 2023 21:57:36 +0100 Subject: [PATCH 27/27] adapt behavior of cups printer to match other implementations --- src/escpos/printer/cups.py | 6 +++--- test/test_printers/test_printer_cups.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/escpos/printer/cups.py b/src/escpos/printer/cups.py index e2eea8dc..f9634c26 100644 --- a/src/escpos/printer/cups.py +++ b/src/escpos/printer/cups.py @@ -205,9 +205,9 @@ def _read(self) -> bytes: """ printer = self.printers.get(self.printer_name, {}) state = printer.get("printer-state") - if not state: - return b"" - return bytes((state,)) + if not state or state in [4, 5]: + return b"8" # offline + return b"0" # online def close(self) -> None: """Close CUPS connection. diff --git a/test/test_printers/test_printer_cups.py b/test/test_printers/test_printer_cups.py index eb4a6f51..85edfe32 100644 --- a/test/test_printers/test_printer_cups.py +++ b/test/test_printers/test_printer_cups.py @@ -170,7 +170,7 @@ def test_read_no_device(cupsprinter) -> None: """ GIVEN a cups printer object WHEN device is None - THEN check the return value is b'' + THEN check the return value is b'8' """ cupsprinter.device = None - assert cupsprinter._read() == b"" + assert cupsprinter._read() == b"8"