From 8f71372bb006027ededf60ab2f5545b0344e31db Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sat, 9 May 2020 03:02:35 +0200 Subject: [PATCH 01/68] remove landscape io badge --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index bd36cbe..ef8c55f 100644 --- a/README.rst +++ b/README.rst @@ -6,10 +6,6 @@ python-escpos - Python library to manipulate ESC/POS Printers :target: https://travis-ci.org/python-escpos/python-escpos :alt: Continous Integration -.. image:: https://landscape.io/github/python-escpos/python-escpos/master/landscape.svg?style=flat - :target: https://landscape.io/github/python-escpos/python-escpos/master - :alt: Code Health - .. image:: https://codecov.io/github/python-escpos/python-escpos/coverage.svg?branch=master :target: https://codecov.io/github/python-escpos/python-escpos?branch=master :alt: Code Coverage From 142fc4af712837f674ad2757bad113b31e7bd3d6 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sat, 9 May 2020 03:05:58 +0200 Subject: [PATCH 02/68] fix readthedocs badge --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ef8c55f..cc1bc7b 100644 --- a/README.rst +++ b/README.rst @@ -10,8 +10,8 @@ python-escpos - Python library to manipulate ESC/POS Printers :target: https://codecov.io/github/python-escpos/python-escpos?branch=master :alt: Code Coverage -.. image:: https://readthedocs.org/projects/python-escpos/badge/?version=stable - :target: http://python-escpos.readthedocs.io/en/latest/?badge=stable +.. image:: https://readthedocs.org/projects/python-escpos/badge/?version=latest + :target: http://python-escpos.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status From cb30d7a8819abc258b2340dcf141e04798e3a3d0 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Mon, 7 Oct 2019 07:43:23 +0200 Subject: [PATCH 03/68] drop Python 2.7 support Python 2.7 EOL is arriving on 2020-01-01: https://pythonclock.org/ This will allow us to use Python 3 only libraries, like python-barcode, which can maintain a reduced, simpler codebase, due to only one version to support. Closes #371. Signed-off-by: Romain Porte --- CONTRIBUTING.rst | 19 ------------------- doc/user/usage.rst | 6 +++--- examples/codepage_tables.py | 1 - examples/weather.py | 1 - setup.cfg | 4 ---- setup.py | 4 ---- src/escpos/__init__.py | 4 ---- src/escpos/cli.py | 4 ---- src/escpos/config.py | 4 ---- src/escpos/constants.py | 4 ---- src/escpos/escpos.py | 4 ---- src/escpos/exceptions.py | 5 ----- src/escpos/image.py | 4 ---- src/escpos/katakana.py | 5 ----- src/escpos/magicencode.py | 4 ---- src/escpos/printer.py | 1 - test/test_abstract_base_class.py | 4 ---- test/test_cli.py | 4 ---- test/test_function_barcode.py | 4 ---- test/test_function_cashdraw.py | 4 ---- test/test_function_check_barcode.py | 4 ---- test/test_function_cut.py | 4 ---- test/test_function_image.py | 4 ---- test/test_function_linedisplay.py | 4 ---- test/test_function_panel_button.py | 4 ---- test/test_function_qr_native.py | 4 ---- test/test_function_qr_non-native.py | 4 ---- test/test_function_set.py | 4 ---- test/test_function_softbarcode.py | 4 ---- test/test_function_text.py | 4 ---- test/test_load_module.py | 4 ---- test/test_magicencode.py | 4 ---- test/test_printer_file.py | 4 ---- test/test_raise_arbitrary_error.py | 4 ---- test/test_with_statement.py | 4 ---- 35 files changed, 3 insertions(+), 147 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f14bc2b..dd0bb9b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -27,25 +27,6 @@ Style-Guide When writing code please try to stick to these rules. -Python 2 and 3 -^^^^^^^^^^^^^^ -We have rewritten the code in order to maintain compatibility with both Python 2 and Python 3. -In order to ensure that we do not miss any accidental degradation, please add these imports to the top -of every file of code: - -.. code-block:: Python - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - -Furthermore please be aware of the differences between Python 2 and 3. For -example `this guide `_ is helpful. -Special care has to be taken when dealing with strings and byte-strings. Please note -that the :py:meth:`~escpos.escpos.Escpos._raw`-method only accepts byte-strings. -Often you can achieve compatibility quite easily with a tool from the `six`-package. - PEP8 ^^^^ The entire codebase adheres to the rules of PEP8. diff --git a/doc/user/usage.rst b/doc/user/usage.rst index 853484f..21537a8 100644 --- a/doc/user/usage.rst +++ b/doc/user/usage.rst @@ -84,7 +84,7 @@ to. :: p = printer.Serial("/dev/tty0") - + # on a Windows OS serial devices are typically accessible as COM p = printer.Serial("COM1") @@ -194,8 +194,8 @@ An USB-printer could be defined by:: Printing text right ------------------- -Python-escpos is designed to accept unicode. So make sure that you use ``u'strings'`` or import ``unicode_literals`` -from ``__future__`` if you are on Python 2. On Python 3 you should be fine. + +Python-escpos is designed to accept unicode. For normal usage you can simply pass your text to the printers ``text()``-function. It will automatically guess the right codepage and then send the encoded data to the printer. If this feature does not work, please try to diff --git a/examples/codepage_tables.py b/examples/codepage_tables.py index a071bf5..5a10ee4 100644 --- a/examples/codepage_tables.py +++ b/examples/codepage_tables.py @@ -1,7 +1,6 @@ """Prints code page tables. """ -from __future__ import print_function import six import sys diff --git a/examples/weather.py b/examples/weather.py index 31ef488..e03b19f 100644 --- a/examples/weather.py +++ b/examples/weather.py @@ -13,7 +13,6 @@ # Check out his github: https://github.com/AdamWhitcroft/climacons -from __future__ import print_function from datetime import datetime import calendar import urllib diff --git a/setup.cfg b/setup.cfg index bf55928..a2dcf83 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,10 +63,6 @@ tests_require = verbosity=3 with-doctest=1 -[bdist_wheel] -# This flag says that the code is written to work on both Python 2 and Python 3. -universal=1 - [flake8] exclude = .git,.tox,.github,.eggs,__pycache__,doc/conf.py,build,dist,capabilities-data,test,src/escpos/constants.py max-line-length = 120 diff --git a/setup.py b/setup.py index 0d98b58..898a51b 100755 --- a/setup.py +++ b/setup.py @@ -22,10 +22,6 @@ setuptools_scm_template = """\ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals version = '{version}' """ diff --git a/src/escpos/__init__.py b/src/escpos/__init__.py index 3fbc808..73e7644 100644 --- a/src/escpos/__init__.py +++ b/src/escpos/__init__.py @@ -2,10 +2,6 @@ """ python-escpos enables you to manipulate escpos-printers """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals __all__ = ["constants", "escpos", "exceptions", "printer"] diff --git a/src/escpos/cli.py b/src/escpos/cli.py index 665e3b8..1b0db9e 100644 --- a/src/escpos/cli.py +++ b/src/escpos/cli.py @@ -9,10 +9,6 @@ It requires you to have a configuration file. See documentation for details. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals import argparse try: diff --git a/src/escpos/config.py b/src/escpos/config.py index 7528934..e2dd789 100644 --- a/src/escpos/config.py +++ b/src/escpos/config.py @@ -4,10 +4,6 @@ This module contains the implentations of abstract base class :py:class:`Config` """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals import os import appdirs diff --git a/src/escpos/constants.py b/src/escpos/constants.py index 88b91f5..fc30bce 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -11,10 +11,6 @@ moved to `capabilities` as in `escpos-php by @mike42 Date: Sun, 10 May 2020 12:35:36 +0200 Subject: [PATCH 04/68] disable codecov status --- codecov.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/codecov.yml b/codecov.yml index 7cb16a0..e236e8c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,10 +3,9 @@ codecov: coverage: status: - project: - default: # status context - target: auto - threshold: "1%" + project: off + patch: off + changes: off range: "60...100" comment: off From b0ea9aec41d2347b88ee7332260e9d722d96acef Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Mon, 7 Oct 2019 07:25:28 +0200 Subject: [PATCH 05/68] barcodes: replace viivakoodi with python-barcode python-barcode is yet another clone of the PyPI barcode library, but which is still developped compared to viivakoodi. Signed-off-by: Romain Porte --- README.rst | 2 +- doc/requirements.txt | 2 +- setup.cfg | 1 + tox.ini | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index cc1bc7b..86683b0 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ This library makes use of: * `Pillow `_ for image printing * `qrcode `_ for the generation of QR-codes * `pyserial `_ for serial printers -* `viivakoodi `_ for the generation of barcodes +* `python-barcode `_ for the generation of barcodes Documentation and Usage ----------------------- diff --git a/doc/requirements.txt b/doc/requirements.txt index 9f38356..89cd833 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -5,4 +5,4 @@ pyserial sphinx-rtd-theme setuptools-scm docutils>=0.12 -viivakoodi +python-barcode>=0.11.0,<1 diff --git a/setup.cfg b/setup.cfg index a2dcf83..10e0396 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,6 +39,7 @@ install_requires = Pillow>=2.0 qrcode>=4.0 pyserial + python-barcode>=0.9.1,<1 six appdirs PyYAML diff --git a/tox.ini b/tox.ini index 53211f3..d0f9361 100644 --- a/tox.ini +++ b/tox.ini @@ -18,6 +18,7 @@ deps = nose pytest-cov pytest-mock hypothesis>4 + python-barcode viivakoodi commands = pytest --cov escpos passenv = ESCPOS_CAPABILITIES_PICKLE_DIR ESCPOS_CAPABILITIES_FILE CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* CODECOV_* @@ -27,7 +28,7 @@ basepython = python changedir = doc deps = sphinx>=1.5.1 setuptools_scm - viivakoodi + python-barcode commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html [testenv:flake8] From 1e313cefc6a7c8e9a4ee0a29beae5c214307ab78 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Sun, 10 May 2020 14:03:43 +0200 Subject: [PATCH 06/68] test_function_barcode.py: remove unused imports --- test/test_function_barcode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_function_barcode.py b/test/test_function_barcode.py index c3a0c7f..a2db09c 100644 --- a/test/test_function_barcode.py +++ b/test/test_function_barcode.py @@ -1,7 +1,6 @@ #!/usr/bin/python import escpos.printer as printer -from escpos.constants import BARCODE_TYPE_A, BARCODE_TYPE_B from escpos.capabilities import Profile, BARCODE_B from escpos.exceptions import BarcodeTypeError, BarcodeCodeError import pytest From 725f1254aa3f2fc79d9ccf8ec5aafbe6785be8ab Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Sun, 10 May 2020 14:04:25 +0200 Subject: [PATCH 07/68] examples: software_barcode: fix too long code39 for 5890 printer --- examples/software_barcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/software_barcode.py b/examples/software_barcode.py index 2fd3c18..9bbcc09 100644 --- a/examples/software_barcode.py +++ b/examples/software_barcode.py @@ -6,4 +6,4 @@ p = Usb(0x0416, 0x5011, profile="POS-5890") # Some software barcodes p.soft_barcode('code128', 'Hello') -p.soft_barcode('code39', '123456') +p.soft_barcode('code39', '1234') From ab30ef4a8c98a9e1c5cfaf264fc437bcc82d1859 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Sun, 10 May 2020 14:05:33 +0200 Subject: [PATCH 08/68] test_function_softbarcode: use pytest fixture --- test/test_function_softbarcode.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/test_function_softbarcode.py b/test/test_function_softbarcode.py index 1bef1cd..f9288f7 100644 --- a/test/test_function_softbarcode.py +++ b/test/test_function_softbarcode.py @@ -4,9 +4,11 @@ import escpos.printer as printer import pytest -def test_soft_barcode(): - """just execute soft_barcode - """ - instance = printer.Dummy() +@pytest.fixture +def instance(): + return printer.Dummy() + + +def test_soft_barcode_ean8(instance): instance.soft_barcode("ean8", "1234") From 8ca682e3acf19f0b599235b22d9fb711c368e047 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Sun, 10 May 2020 14:06:00 +0200 Subject: [PATCH 09/68] soft_barcode: add new center=True option --- src/escpos/escpos.py | 5 +++-- test/test_function_softbarcode.py | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 17e0d12..24f6475 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -479,7 +479,8 @@ class Escpos(object): self._raw(NUL) def soft_barcode(self, barcode_type, data, impl='bitImageColumn', - module_height=5, module_width=0.2, text_distance=1): + module_height=5, module_width=0.2, text_distance=1, + center=True): image_writer = ImageWriter() @@ -502,7 +503,7 @@ class Escpos(object): # Retrieve the Pillow image and print it image = my_code.writer._image - self.image(image, impl=impl) + self.image(image, impl=impl, center=center) def text(self, txt): """ Print alpha-numeric text diff --git a/test/test_function_softbarcode.py b/test/test_function_softbarcode.py index f9288f7..fc702d7 100644 --- a/test/test_function_softbarcode.py +++ b/test/test_function_softbarcode.py @@ -12,3 +12,6 @@ def instance(): def test_soft_barcode_ean8(instance): instance.soft_barcode("ean8", "1234") + +def test_soft_barcode_ean8_nocenter(instance): + instance.soft_barcode("ean8", "1234", center=False) From cbe412cfdb0b75eb9b7e8b1cd6f035c4c34ed15d Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 May 2020 01:15:41 +0200 Subject: [PATCH 10/68] fix gitignore for vscode --- .gitignore | 6 +++++- .vscode/settings.json | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index ba41934..6f33e94 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,8 @@ test/test-cli-output/ *.swo # vscode -.vscode/settings.json +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..32ab8c0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "restructuredtext.confPath": "${workspaceFolder}/doc", + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/*/**": true, + "**/.eggs/**": true, + "**/.hypothesis/**": true, + "**/.tox/**": true, + } +} \ No newline at end of file From b9e38278671f04b8840b8749194af56b05cba660 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 May 2020 22:43:44 +0200 Subject: [PATCH 11/68] use pkg_resources This change uses if no path for e capabilities-file is supplied a temporary file created by pkg_resources, which should be more robust than directly accessing the file. (This failed sometimes, for example in zipped distributions or uncommon structures) --- doc/requirements.txt | 1 + setup.cfg | 1 + src/escpos/capabilities.py | 7 ++++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 89cd833..0908f3d 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -3,6 +3,7 @@ Pillow>=2.0 qrcode>=4.0 pyserial sphinx-rtd-theme +setuptools setuptools-scm docutils>=0.12 python-barcode>=0.11.0,<1 diff --git a/setup.cfg b/setup.cfg index 10e0396..6d6ff7f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,7 @@ install_requires = qrcode>=4.0 pyserial python-barcode>=0.9.1,<1 + setuptools six appdirs PyYAML diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index c4b6ab5..2c9c909 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -1,5 +1,6 @@ import re from os import environ, path +import pkg_resources import pickle import logging import time @@ -15,9 +16,9 @@ logger = logging.getLogger(__name__) pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', gettempdir()) pickle_path = path.join(pickle_dir, '{v}.capabilities.pickle'.format(v=platform.python_version())) -capabilities_path = environ.get( - 'ESCPOS_CAPABILITIES_FILE', - path.join(path.dirname(__file__), 'capabilities.json')) +# get a temporary file from pkg_resources if no file is specified in env +capabilities_path = environ.get('ESCPOS_CAPABILITIES_FILE', + pkg_resources.resource_filename(__name__, 'capabilities.json')) # Load external printer database t0 = time.time() From 9e406efc86edf9b57f2907984f2c4fedb0b41668 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 May 2020 23:22:40 +0200 Subject: [PATCH 12/68] add sphinxcontrib-spelling --- doc/Makefile | 8 +++++++- doc/conf.py | 1 + doc/requirements.txt | 1 + doc/spelling_wordlist.txt | 0 setup.cfg | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 doc/spelling_wordlist.txt diff --git a/doc/Makefile b/doc/Makefile index f423c2f..535ebbd 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -19,7 +19,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext spelling help: @echo "Please use \`make ' where is one of" @@ -45,6 +45,7 @@ help: @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " spelling to run the spellchecker" clean: rm -rf $(BUILDDIR)/* @@ -175,3 +176,8 @@ pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +spelling: + $(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) $(BUILDDIR)/spelling + @echo + @echo "Spellchecker finished." diff --git a/doc/conf.py b/doc/conf.py index 2cbe0cf..b83629e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -43,6 +43,7 @@ extensions = [ 'sphinx.ext.todo', 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', + 'sphinxcontrib.spelling', ] # supress warnings for external images diff --git a/doc/requirements.txt b/doc/requirements.txt index 89cd833..c0050c9 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -5,4 +5,5 @@ pyserial sphinx-rtd-theme setuptools-scm docutils>=0.12 +sphinxcontrib-spelling>=5 python-barcode>=0.11.0,<1 diff --git a/doc/spelling_wordlist.txt b/doc/spelling_wordlist.txt new file mode 100644 index 0000000..e69de29 diff --git a/setup.cfg b/setup.cfg index 10e0396..567e548 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,6 +59,7 @@ tests_require = mock hypothesis>4 flake8 + sphinxcontrib-spelling>=5 [nosetests] verbosity=3 From f4e214ad17d42a0da7370ca4e17dfaf99a990050 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 May 2020 23:39:56 +0200 Subject: [PATCH 13/68] add spellchecker with first conf, fix some errors --- doc/conf.py | 6 ++++++ doc/spelling_wordlist.txt | 7 +++++++ doc/user/usage.rst | 10 +++++----- src/escpos/capabilities.py | 2 +- src/escpos/cli.py | 4 ++-- src/escpos/config.py | 2 +- src/escpos/escpos.py | 2 +- test/test_abstract_base_class.py | 4 ++-- 8 files changed, 25 insertions(+), 12 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index b83629e..db40f67 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -293,3 +293,9 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False + +# spellchecker +spelling_ignore_pypi_package_names = True +spelling_ignore_wiki_words = True +spelling_ignore_python_builtins = True +spelling_ignore_importable_modules = True diff --git a/doc/spelling_wordlist.txt b/doc/spelling_wordlist.txt index e69de29..e037ead 100644 --- a/doc/spelling_wordlist.txt +++ b/doc/spelling_wordlist.txt @@ -0,0 +1,7 @@ +Raspbian +ESC +POS +Escpos +Escpos +baudrate +lsusb \ No newline at end of file diff --git a/doc/user/usage.rst b/doc/user/usage.rst index 21537a8..947a521 100644 --- a/doc/user/usage.rst +++ b/doc/user/usage.rst @@ -48,9 +48,9 @@ to have and the second yields the "Output Endpoint" address. By default the "Interface" number is "0" and the "Output Endpoint" address is "0x01". If you have other values then you can define them on -your instance. So, assuming that we have another printer, CT-S2000, -manufactured by Citizen (with "Vendor ID" of 2730 and "Product ID" of 0fff) -where in\_ep is on 0x81 and out\_ep=0x02, then the printer definition should +your instance. So, assuming that we have another printer, CT-S2000, +manufactured by Citizen (with "Vendor ID" of 2730 and "Product ID" of 0fff) +where in\_ep is on 0x81 and out\_ep=0x02, then the printer definition should look like: **Generic USB Printer initialization** @@ -163,7 +163,7 @@ The printer section The ``printer`` configuration section defines a default printer to create. -The only required paramter is ``type``. The value of this has to be one of the +The only required parameter is ``type``. The value of this has to be one of the printers defined in :doc:`/user/printers`. The rest of the given parameters will be passed on to the initialization of the printer class. @@ -199,7 +199,7 @@ Python-escpos is designed to accept unicode. For normal usage you can simply pass your text to the printers ``text()``-function. It will automatically guess the right codepage and then send the encoded data to the printer. If this feature does not work, please try to -isolate the error and then create an issue on the Github project page. +isolate the error and then create an issue on the GitHub project page. If you want or need to you can manually set the codepage. For this please use the ``charcode()``-function. You can set any key-value that is in ``CHARCODE``. If something is wrong, an ``CharCodeError`` will be raised. diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index c4b6ab5..65c2666 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -58,7 +58,7 @@ BARCODE_B = 'barcodeB' class BaseProfile(object): - """This respresents a printer profile. + """This represents a printer profile. A printer profile knows about the number of columns, supported features, colors and more. diff --git a/src/escpos/cli.py b/src/escpos/cli.py index 1b0db9e..c9f9125 100644 --- a/src/escpos/cli.py +++ b/src/escpos/cli.py @@ -3,7 +3,7 @@ """ CLI This module acts as a command line interface for python-escpos. It mirrors -closely the available ESCPOS commands while adding a couple extra ones for convience. +closely the available ESCPOS commands while adding a couple extra ones for convenience. It requires you to have a configuration file. See documentation for details. @@ -554,7 +554,7 @@ def main(): def demo(printer, **kwargs): """ - Prints specificed demos. Called when CLI is passed `demo`. This function + Prints demos. Called when CLI is passed `demo`. This function uses the DEMO_FUNCTIONS dictionary. :param printer: A printer from escpos.printer diff --git a/src/escpos/config.py b/src/escpos/config.py index e2dd789..b4cdb33 100644 --- a/src/escpos/config.py +++ b/src/escpos/config.py @@ -1,6 +1,6 @@ """ ESC/POS configuration manager. -This module contains the implentations of abstract base class :py:class:`Config`. +This module contains the implementations of abstract base class :py:class:`Config`. """ diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 24f6475..05a763d 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -398,7 +398,7 @@ class Escpos(object): *default*: A :param check: If this parameter is True, the barcode format will be checked to ensure it meets the bc - requirements as defigned in the esc/pos documentation. See :py:meth:`.check_barcode()` + requirements as definged in the ESC/POS documentation. See :py:meth:`.check_barcode()` for more information. *default*: True. :raises: :py:exc:`~escpos.exceptions.BarcodeSizeError`, diff --git a/test/test_abstract_base_class.py b/test/test_abstract_base_class.py index cc09ee1..5edc8a2 100644 --- a/test/test_abstract_base_class.py +++ b/test/test_abstract_base_class.py @@ -1,5 +1,5 @@ #!/usr/bin/python -"""verifies that the metaclass abc is properly used by Escpos +"""verifies that the metaclass abc is properly used by ESC/POS :author: `Patrick Kanzler `_ :organization: `python-escpos `_ @@ -16,7 +16,7 @@ from abc import ABCMeta @raises(TypeError) def test_abstract_base_class_raises(): - """test whether the abstract base class raises an exception for Escpos""" + """test whether the abstract base class raises an exception for ESC/POS""" escpos.Escpos() # This call should raise TypeError because of abstractmethod _raw() From f7962576b4192c16b39d4d6ba4326376f459d06c Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 11 May 2020 23:44:10 +0200 Subject: [PATCH 14/68] fix tox config --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d0f9361..1501eb8 100644 --- a/tox.ini +++ b/tox.ini @@ -19,7 +19,6 @@ deps = nose pytest-mock hypothesis>4 python-barcode - viivakoodi commands = pytest --cov escpos passenv = ESCPOS_CAPABILITIES_PICKLE_DIR ESCPOS_CAPABILITIES_FILE CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* CODECOV_* @@ -29,6 +28,7 @@ changedir = doc deps = sphinx>=1.5.1 setuptools_scm python-barcode + sphinxcontrib-spelling>=5 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html [testenv:flake8] From aa7f2773eb5733726e9980b713a2f7e506cd852e Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Tue, 12 May 2020 00:04:53 +0200 Subject: [PATCH 15/68] install spelling on travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 39b238e..4758f82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ matrix: language: shell before_install: - choco install python - - pip install tox codecov 'sphinx>=1.5.1' + - pip install tox codecov 'sphinx>=1.5.1' 'sphinxcontrib-spelling>=5' env: - TOXENV=py37 - PATH=/c/Python37:/c/Python37/Scripts:$PATH @@ -57,7 +57,7 @@ matrix: - os: windows - os: osx before_install: - - pip install tox codecov 'sphinx>=1.5.1' + - pip install tox codecov 'sphinx>=1.5.1' 'sphinxcontrib-spelling>=5' - ./doc/generate_authors.sh --check script: - tox From 570661b3c81f10caf75193f65c930e6b1d272850 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Tue, 12 May 2020 18:17:22 +0200 Subject: [PATCH 16/68] install libenchant1c2a on travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4758f82..e027551 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ addons: apt: packages: - graphviz + - libenchant1c2a env: global: - ESCPOS_CAPABILITIES_FILE=/home/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json From 544bb4f904204892ea307858129a8c5c003715d6 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Tue, 12 May 2020 18:29:14 +0200 Subject: [PATCH 17/68] omit env for capabilities on travis --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e027551..f417023 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,6 @@ addons: packages: - graphviz - libenchant1c2a -env: - global: - - ESCPOS_CAPABILITIES_FILE=/home/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json matrix: fast_finish: true include: From ef34cca4633517f83408a6d75e136c0306107ee9 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Tue, 12 May 2020 19:32:22 +0200 Subject: [PATCH 18/68] update changelog --- CHANGELOG.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6360a69..dffb008 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,28 @@ ********* Changelog ********* +2020-05-12 - Version 3.0a8 - "Only Slightly Bent" +------------------------------------------------- +This release is the ninth alpha release of the new version 3.0. +Please be aware that the API is subject to change until v3.0 is +released. + +This release drops support for Python 2, requiring at least +version 3.5 of Python. + +changes +^^^^^^^ +- Drop support for Python 2 and mark in setuptools as only supporting 3.5 and upwards +- remove landscape.io badge +- replace viivakoodi with python-barcode which is maintained +- add configuration for Visual Studio Code +- use pkg_resources for the retrieval of the capabilities.json + +contributors +^^^^^^^^^^^^ +- Romain Porte +- Patrick Kanzler + 2020-05-09 - Version 3.0a7 - "No Fixed Abode" --------------------------------------------- This release is the eight alpha release of the new version 3.0. From ee6eef6db3aa57ed78020b3ca63b3b5b02ce7ecb Mon Sep 17 00:00:00 2001 From: Maximilian Wagenbach Date: Tue, 26 May 2020 01:38:57 +0200 Subject: [PATCH 19/68] I verified that this works. Both modes cut the paper 95% of the way through. --- src/escpos/escpos.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 05a763d..de4ee09 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -661,8 +661,6 @@ class Escpos(object): be attempted. Note however, that not all models can do a partial cut. See the documentation of your printer for details. - .. todo:: Check this function on TM-T88II. - :param mode: set to 'PART' for a partial cut. default: 'FULL' :param feed: print and feed before cutting. default: true :raises ValueError: if mode not in ('FULL', 'PART') From 323db98318c38e02c4e82a6a309b7adda3f9a129 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Tue, 26 May 2020 20:43:24 +0200 Subject: [PATCH 20/68] make gen authors more robust --- .mailmap | 1 + doc/generate_authors.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 4540049..5adfefb 100644 --- a/.mailmap +++ b/.mailmap @@ -13,3 +13,4 @@ csoft2k Sergio Pulgarin reck31 Alex Debiasio +Maximilian Wagenbach diff --git a/doc/generate_authors.sh b/doc/generate_authors.sh index aefcb6e..1fcda40 100755 --- a/doc/generate_authors.sh +++ b/doc/generate_authors.sh @@ -12,7 +12,7 @@ if [ "$#" -eq 1 ] echo "\nNew authorsfile:\n" cat $TEMPAUTHORSFILE echo "\nUsing diff on files...\n" - diff -q --from-file $AUTHORSFILE $TEMPAUTHORSFILE + diff --suppress-common-lines -b --from-file $AUTHORSFILE $TEMPAUTHORSFILE else echo "$GENLIST">$AUTHORSFILE fi From e3cf088d69c6a4861a9fb5537ce099a12b3b33ca Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Tue, 26 May 2020 21:29:16 +0200 Subject: [PATCH 21/68] improve output of authors script --- .mailmap | 2 +- doc/generate_authors.sh | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 5adfefb..825019f 100644 --- a/.mailmap +++ b/.mailmap @@ -13,4 +13,4 @@ csoft2k Sergio Pulgarin reck31 Alex Debiasio -Maximilian Wagenbach +Maximilian Wagenbach diff --git a/doc/generate_authors.sh b/doc/generate_authors.sh index 1fcda40..bf35156 100755 --- a/doc/generate_authors.sh +++ b/doc/generate_authors.sh @@ -1,6 +1,7 @@ #!/bin/sh GENLIST=$(git shortlog -s -n | cut -f2 | sort -f) +GENLIST_W_MAIL=$(git shortlog -s -e -n | cut -f2 | sort -f) AUTHORSFILE="$(dirname $0)/../AUTHORS" TEMPAUTHORSFILE="/tmp/python-escpos-authorsfile" @@ -12,7 +13,9 @@ if [ "$#" -eq 1 ] echo "\nNew authorsfile:\n" cat $TEMPAUTHORSFILE echo "\nUsing diff on files...\n" - diff --suppress-common-lines -b --from-file $AUTHORSFILE $TEMPAUTHORSFILE + diff --suppress-common-lines -b --from-file $AUTHORSFILE $TEMPAUTHORSFILE + echo "Authors with mail addresses:\n" + echo "$GENLIST_W_MAIL" else echo "$GENLIST">$AUTHORSFILE fi From c57b9c35dc8e5479d561239874457526ed394a96 Mon Sep 17 00:00:00 2001 From: Maximilian Wagenbach Date: Sun, 31 May 2020 16:21:42 +0200 Subject: [PATCH 22/68] Use https links. --- CONTRIBUTING.rst | 4 ++-- README.rst | 12 +++++------- doc/Makefile | 2 +- doc/make.bat | 2 +- examples/graphics/climacons/readme.md | 6 +++--- examples/weather.py | 2 +- src/escpos/constants.py | 2 +- 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index dd0bb9b..7f6b40f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -9,7 +9,7 @@ In order to reduce the amount of work for everyone please try to adhere to good The pull requests and issues will be prefilled with templates. Please fill in your information where applicable. -This project uses `semantic versioning `_ and tries to adhere to the proposed rules as +This project uses `semantic versioning `_ and tries to adhere to the proposed rules as well as possible. Author-list @@ -62,7 +62,7 @@ You can copy the structure from other testcases. Please remember to adapt the do Further reading ^^^^^^^^^^^^^^^ For further best practices and hints on contributing please see the -`contribution-guide `_. Should there be any contradictions between this guide +`contribution-guide `_. Should there be any contradictions between this guide and the linked one, please stick to this text. Aside from that feel free to create an issue or write an email if anything is unclear. diff --git a/README.rst b/README.rst index 86683b0..bd545aa 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ python-escpos - Python library to manipulate ESC/POS Printers :alt: Code Coverage .. image:: https://readthedocs.org/projects/python-escpos/badge/?version=latest - :target: http://python-escpos.readthedocs.io/en/latest/?badge=latest + :target: https://python-escpos.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status @@ -67,18 +67,18 @@ Another example based on the Network printer class: .. code:: python from escpos.printer import Network - + kitchen = Network("192.168.1.100") #Printer IP Address kitchen.text("Hello World\n") kitchen.barcode('1324354657687', 'EAN13', 64, 2, '', '') kitchen.cut() - + Another example based on the Serial printer class: .. code:: python from escpos.printer import Serial - + """ 9600 Baud, 8N1, Flow Control Enabled """ p = Serial(devfile='/dev/tty.usbserial', baudrate=9600, @@ -98,7 +98,7 @@ The full project-documentation is available on `Read the Docs `_ for more information. +This project is open for any contribution! Please see `CONTRIBUTING.rst `_ for more information. Disclaimer @@ -106,5 +106,3 @@ Disclaimer None of the vendors cited in this project agree or endorse any of the patterns or implementations. Its names are used only to maintain context. - - diff --git a/doc/Makefile b/doc/Makefile index 535ebbd..bf68b2a 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,7 +9,7 @@ BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/) endif # Internal variables. diff --git a/doc/make.bat b/doc/make.bat index 8e2ddc9..dbee13a 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -56,7 +56,7 @@ if errorlevel 9009 ( echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ + echo.https://sphinx-doc.org/ exit /b 1 ) diff --git a/examples/graphics/climacons/readme.md b/examples/graphics/climacons/readme.md index d610a00..99a0ca9 100644 --- a/examples/graphics/climacons/readme.md +++ b/examples/graphics/climacons/readme.md @@ -1,10 +1,10 @@ # Climacons by Adam Whitcroft -75 climatically categorised pictographs for web and UI design by [@adamwhitcroft](http://www.twitter.com/#!/adamwhitcroft). +75 climatically categorised pictographs for web and UI design by [@adamwhitcroft](https://www.twitter.com/#!/adamwhitcroft). -Visit the [Climacons](http://adamwhitcroft.com/climacons/) website for more information. +Visit the [Climacons](https://adamwhitcroft.com/climacons/) website for more information. Visit [Adam Whitcroft on GitHub](https://github.com/AdamWhitcroft) ## License -You are free to use any of the Climacons Icons (the "icons") in any personal or commercial work without obligation of payment (monetary or otherwise) or attribution, however a credit for the work would be appreciated. **Do not** redistribute or sell and **do not** claim creative credit. Intellectual property rights are not transferred with the download of the icons. \ No newline at end of file +You are free to use any of the Climacons Icons (the "icons") in any personal or commercial work without obligation of payment (monetary or otherwise) or attribution, however a credit for the work would be appreciated. **Do not** redistribute or sell and **do not** claim creative credit. Intellectual property rights are not transferred with the download of the icons. diff --git a/examples/weather.py b/examples/weather.py index e03b19f..2ba457b 100644 --- a/examples/weather.py +++ b/examples/weather.py @@ -9,7 +9,7 @@ # Written by Adafruit Industries. MIT license. # Adapted and enhanced for escpos library by MrWunderbar666 -# Icons taken from http://adamwhitcroft.com/climacons/ +# Icons taken from https://adamwhitcroft.com/climacons/ # Check out his github: https://github.com/AdamWhitcroft/climacons diff --git a/src/escpos/constants.py b/src/escpos/constants.py index fc30bce..6ebfc91 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -15,7 +15,7 @@ moved to `capabilities` as in `escpos-php by @mike42 Date: Mon, 28 Sep 2020 19:51:49 +0200 Subject: [PATCH 23/68] add hint on proper script names fixes #404 --- doc/user/usage.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/user/usage.rst b/doc/user/usage.rst index 947a521..47ef916 100644 --- a/doc/user/usage.rst +++ b/doc/user/usage.rst @@ -125,6 +125,11 @@ on a USB interface. # Cut paper p.cut() +Standard python constraints on libraries apply. This means especially +that you should not name the script in which you implement these lines +should not be named ``escpos`` as this would collide with the name of +the library. + Configuration File ------------------ From 596787554b3c72f1cc6d8309f4d0e05e80d02038 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 20:44:22 +0200 Subject: [PATCH 24/68] add disclaimer to beep sound fixes #365 --- src/escpos/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/escpos/constants.py b/src/escpos/constants.py index 6ebfc91..5e5b38c 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -52,7 +52,7 @@ _CUT_PAPER = lambda m: GS + b'V' + m PAPER_FULL_CUT = _CUT_PAPER(b'\x00') # Full cut paper PAPER_PART_CUT = _CUT_PAPER(b'\x01') # Partial cut paper -# Beep +# Beep (please note that the actual beep sequence may differ between devices) BEEP = b'\x07' # Panel buttons (e.g. the FEED button) From 818c1313e84ffb67375d7fb13527ab5867856c4c Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Mon, 28 Sep 2020 20:55:13 +0200 Subject: [PATCH 25/68] Build documentation in Github action --- .github/workflows/documentation.yml | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/documentation.yml diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..fd85eaa --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,33 @@ +# This is a basic workflow to help you get started with Actions + +name: Documentation build + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + - name: Sphinx Build + # You may pin to the exact commit or the version. + # uses: ammaraskar/sphinx-action@8b4f60114d7fd1faeba1a712269168508d4750d2 + uses: ammaraskar/sphinx-action@0.4 + with: + # The folder containing your sphinx docs. + docs-folder: doc/ + # The command used to build your documentation. + build-command: make html From 8488fafd757ebd89c5630b0706d28f04581c4fba Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 20:59:36 +0200 Subject: [PATCH 26/68] remove travis config fixes #389 --- .travis.yml | 79 ----------------------------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f417023..0000000 --- a/.travis.yml +++ /dev/null @@ -1,79 +0,0 @@ -language: python -sudo: false -cache: pip -dist: bionic -git: - depth: 100000 -addons: - apt: - packages: - - graphviz - - libenchant1c2a -matrix: - fast_finish: true - include: - - name: "Python 3.7 on Windows" - os: windows - language: shell - before_install: - - choco install python - - pip install tox codecov 'sphinx>=1.5.1' 'sphinxcontrib-spelling>=5' - env: - - TOXENV=py37 - - PATH=/c/Python37:/c/Python37/Scripts:$PATH - - ESCPOS_CAPABILITIES_FILE=C:/Users/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json - - name: "Python 3.7 on macOS" - os: osx - osx_image: xcode10.2 - language: shell - env: TOXENV=py37 ESCPOS_CAPABILITIES_FILE=/Users/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json - - python: 3.5 - env: TOXENV=py35 - - python: 3.6 - env: TOXENV=py36 - - python: 3.7 - env: TOXENV=py37 - - python: 3.7-dev - env: TOXENV=py37 - - python: 3.8 - env: TOXENV=py38 - - python: 3.8-dev - env: TOXENV=py38 - - python: nightly - env: TOXENV=py38 - - python: pypy3 - env: TOXENV=pypy3 - - python: 3.8 - env: TOXENV=docs - - python: 3.8 - env: TOXENV=flake8 - allow_failures: - - python: 3.7-dev - - python: 3.8-dev - - python: nightly - - python: pypy3 - - os: windows - - os: osx -before_install: - - pip install tox codecov 'sphinx>=1.5.1' 'sphinxcontrib-spelling>=5' - - ./doc/generate_authors.sh --check -script: - - tox - - codecov -notifications: - email: - on_success: never - on_failure: change -deploy: -# Github deployment - - provider: releases - api_key: - secure: oiR3r5AIx9ENIRtbUKIxorRx8GMv4BxgVIZcieXbgSTN4DBZdRWdzs1Xxngu/90Xf79G0X+XGxZyXrYN7eFFNp0kUYj8kwZ1aS/dyR88scskumERWi1Hv5WUJrYGrDe7PcjNGsJ2jw0nNnRPKG87Y84aR4lQygyGBSlDcdrOBnBv0sHYJMxRvHSRkGgWpur06QIOGOk4oOipTXR/7E9cg3YQC5nvZAf2QiprwTa8IcOSFlZQPykEVRYSiAgXrgqBYcZzpX0hAGuIBv7DmPI2ORTF+t79Wbhxhnho3gGJleDv7Z96//sf1vQNCG6qOgeIc9ZY08Jm1AwXQoW0p6F1/XcEPxeyPDkXJzlojE9rjYNLCPL4gxb/LESEuUafm0U4JGMsZ6hnsBOw583yTuAdfQuJ9M+QaSyem6OVNkky3+DKAD3z0WJnl9jmGXIXigNSIxD25XhpvY+j9P0XTLBG1GT2Q+wXCIjSYJc2XnYcdgVJcLoxSWk1fKj/KPi7buAWtqwnL3tjeldpMMOZMliPUTWMM14zoGskHztt0JCkAtcotm9AQtvL8eZ2LHLDK/jyLzjv0wAwU5vzSVp14XHLZl7Q0AIoNc20p1EYGa9C/gSPd9CkrWZoG4lMOiAu3tp2PRLVrdXH3ZWSPQq4Ek5MczrUTkmB82XErNbOa8QB1Dw= - file: .tox/dist/python-escpos*.zip - file_glob: true - skip_cleanup: true - on: - tags: true - repo: python-escpos/python-escpos - branch: master - condition: $TRAVIS_PYTHON_VERSION = "3.8" From d390449400e1f6914c0ed7fecb8b4ccbe7a4a273 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:06:45 +0200 Subject: [PATCH 27/68] change documenatation action --- .github/workflows/documentation.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index fd85eaa..8061c58 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,15 +19,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - - - name: Sphinx Build - # You may pin to the exact commit or the version. - # uses: ammaraskar/sphinx-action@8b4f60114d7fd1faeba1a712269168508d4750d2 - uses: ammaraskar/sphinx-action@0.4 + - uses: ammaraskar/sphinx-action@0.4 with: - # The folder containing your sphinx docs. - docs-folder: doc/ - # The command used to build your documentation. - build-command: make html + docs-folder: "doc/" \ No newline at end of file From 8ece137789e5c676ebcfc7133eaeeaa5a8dc04a1 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:12:44 +0200 Subject: [PATCH 28/68] tr< checkout v1 --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 8061c58..04e2f54 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,7 +19,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v1 - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" \ No newline at end of file From 04e0a0ce4713fc21125b3ce87027c967143ea237 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:14:56 +0200 Subject: [PATCH 29/68] Revert "tr< checkout v1" This reverts commit 8ece137789e5c676ebcfc7133eaeeaa5a8dc04a1. --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 04e2f54..8061c58 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,7 +19,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" \ No newline at end of file From a8941b4a3ed24f8f22db0baec4d6ae49b6cea187 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:15:33 +0200 Subject: [PATCH 30/68] check out submodules --- .github/workflows/documentation.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 8061c58..cfa0a2f 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -20,6 +20,8 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - uses: actions/checkout@v2 + with: + submodules: 'recursive' - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" \ No newline at end of file From 21b9dba34586b8d1e638459f41d5044dbcc2957b Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:22:54 +0200 Subject: [PATCH 31/68] have a look around before building doc --- .github/workflows/documentation.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index cfa0a2f..8c6e3fb 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -24,4 +24,5 @@ jobs: submodules: 'recursive' - uses: ammaraskar/sphinx-action@0.4 with: - docs-folder: "doc/" \ No newline at end of file + docs-folder: "doc/" + pre-build-command: "ls -al" \ No newline at end of file From 243eb48b387e09d7d9796042a823e72a1c6a7204 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:26:42 +0200 Subject: [PATCH 32/68] install git in container --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 8c6e3fb..bb69bf3 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,4 +25,4 @@ jobs: - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" - pre-build-command: "ls -al" \ No newline at end of file + pre-build-command: "apt-get update -y && apt-get install -y git" \ No newline at end of file From 247211d0cedc97c706ca65e274e20786771757b5 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:31:29 +0200 Subject: [PATCH 33/68] install sphinx --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index bb69bf3..e472df9 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,4 +25,4 @@ jobs: - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" - pre-build-command: "apt-get update -y && apt-get install -y git" \ No newline at end of file + pre-build-command: "apt-get update -y && apt-get install -y git sphinx" \ No newline at end of file From 1dc79baf82e0cc26d7134f4f66b48aecf41af9d0 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 21:33:16 +0200 Subject: [PATCH 34/68] rename job --- .github/workflows/documentation.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index e472df9..75deb87 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -12,8 +12,8 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "build" - build: + # This workflow contains a single job called "docs" + docs: # The type of runner that the job will run on runs-on: ubuntu-latest From 5d62be2be97822a211f39e72c8ffaefb0aeaebdd Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 22:28:27 +0200 Subject: [PATCH 35/68] install graphviz and libenchant --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 75deb87..c855eb8 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,4 +25,4 @@ jobs: - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" - pre-build-command: "apt-get update -y && apt-get install -y git sphinx" \ No newline at end of file + pre-build-command: "apt-get update -y && apt-get install -y git sphinx graphviz libenchant1c2a" \ No newline at end of file From 18cc1a268a29f857d325bf1358063b3c07270c2c Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Mon, 28 Sep 2020 22:31:48 +0200 Subject: [PATCH 36/68] use corrct name for sphinx package --- .github/workflows/documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index c855eb8..6696c6d 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -25,4 +25,4 @@ jobs: - uses: ammaraskar/sphinx-action@0.4 with: docs-folder: "doc/" - pre-build-command: "apt-get update -y && apt-get install -y git sphinx graphviz libenchant1c2a" \ No newline at end of file + pre-build-command: "apt-get update -y && apt-get install -y git python3-sphinx graphviz libenchant1c2a" \ No newline at end of file From a2d553efb10caacd6f678a17de5554007aaa632c Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Thu, 1 Oct 2020 13:46:23 +0200 Subject: [PATCH 37/68] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..2dd483a --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +name: "CodeQL" + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 1 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['python'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 40c8399a68d82834effea95ae2595118cd829e3a Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 4 Oct 2020 14:28:09 +0200 Subject: [PATCH 38/68] update documentation on STAR TSP100 fixes #410 and #403 --- doc/user/usage.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/user/usage.rst b/doc/user/usage.rst index 47ef916..3a30c8c 100644 --- a/doc/user/usage.rst +++ b/doc/user/usage.rst @@ -294,4 +294,18 @@ This way you could also store the code in a file and print it later. You could then for example print the code from another process than your main-program and thus reduce the waiting time. (Of course this will not make the printer print faster.) +Troubleshooting +--------------- + +This section gathers varios hint on troubleshooting. + +Print with STAR TSP100 family +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Printer of the STAR TSP100 family do not have a native ESC/POS mode, which +is why you will not be able to directly print with this library to the printer. + +More information on this topic can be found in the online documentation of +`Star Micronics `_ +and the `discussion in the python-escpos project `_. + From a2e348d04a0cab13dd8326b8385e74fd2b0b87c8 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 4 Oct 2020 14:30:03 +0200 Subject: [PATCH 39/68] fix typos --- doc/user/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/usage.rst b/doc/user/usage.rst index 3a30c8c..9124c1b 100644 --- a/doc/user/usage.rst +++ b/doc/user/usage.rst @@ -297,7 +297,7 @@ You could then for example print the code from another process than your main-pr Troubleshooting --------------- -This section gathers varios hint on troubleshooting. +This section gathers various hints on troubleshooting. Print with STAR TSP100 family ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 6c89e93c22c6038083e27eafadf9abba5b48388d Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Mon, 5 Oct 2020 23:51:27 +0200 Subject: [PATCH 40/68] create dependabot.yml --- .github/dependabot.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0a0aa93 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" + - package-ecosystem: "gitsubmodule" + directory: "capabilities-data" + schedule: + interval: "daily" + - package-ecosystem: "githubactions" + directory: ".github" + schedule: + interval: "daily" From 3c479407dcfac425a0d6e116ec7c57e8595d7084 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Mon, 5 Oct 2020 23:58:35 +0200 Subject: [PATCH 41/68] fix github-actions config --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0a0aa93..65ce690 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,7 +13,7 @@ updates: directory: "capabilities-data" schedule: interval: "daily" - - package-ecosystem: "githubactions" - directory: ".github" + - package-ecosystem: "github-actions" + directory: ".github/workflows" schedule: interval: "daily" From 21f19b7f7e04d62d3b018fa5a73370c1e215ac2d Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Tue, 6 Oct 2020 00:01:59 +0200 Subject: [PATCH 42/68] use standard directories --- .github/dependabot.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 65ce690..8cfc4c3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,10 +10,8 @@ updates: schedule: interval: "daily" - package-ecosystem: "gitsubmodule" - directory: "capabilities-data" schedule: interval: "daily" - package-ecosystem: "github-actions" - directory: ".github/workflows" schedule: interval: "daily" From d6639d37f3c15f8a66330b06b0a9f802ab1067fa Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Tue, 6 Oct 2020 00:04:11 +0200 Subject: [PATCH 43/68] set directories --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8cfc4c3..932d224 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,9 +9,15 @@ updates: directory: "/" # Location of package manifests schedule: interval: "daily" + - package-ecosystem: "pip" + directory: "/doc" + schedule: + interval: "daily" - package-ecosystem: "gitsubmodule" + directory: "/" schedule: interval: "daily" - package-ecosystem: "github-actions" + directory: "/" schedule: interval: "daily" From 1275bf90d18b1981c5c36198a42d44bc4e833d60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2020 22:10:17 +0000 Subject: [PATCH 44/68] Bump actions/setup-python from v1 to v2.1.3 Bumps [actions/setup-python](https://github.com/actions/setup-python) from v1 to v2.1.3. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v1...c181ffa198a1248f902bc2f7965d2f9a36c2d7f6) Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 2c2e3eb..a9e6944 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2.1.3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From b6516e4b42acaf5c96ab69635bbbb1a75b95790c Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Thu, 8 Oct 2020 23:56:05 +0200 Subject: [PATCH 45/68] Test for python 3.9 #421 --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index a9e6944..27e629d 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8] + python-version: [3.5, 3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 From 809b4f47aaf19c872421de7a12647e762b32b8d9 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 19:45:08 +0100 Subject: [PATCH 46/68] update capabilities-data --- capabilities-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capabilities-data b/capabilities-data index 3b5b35c..3612db4 160000 --- a/capabilities-data +++ b/capabilities-data @@ -1 +1 @@ -Subproject commit 3b5b35cfd35d297f9085ec16d47d1f77c3f1e768 +Subproject commit 3612db407d02a08acd93a1540f2b4823be3f020e From e898413464934bf4059553b883f846b54c96bb43 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 21:29:18 +0100 Subject: [PATCH 47/68] change useage of assert_equal(s) --- test/test_cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_cli.py b/test/test_cli.py index 1f41095..eba5b76 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -6,7 +6,7 @@ import os import sys from scripttest import TestFileEnvironment -from nose.tools import assert_equals, nottest +from nose.tools import assert_equal, nottest import escpos TEST_DIR = os.path.abspath('test/test-cli-output') @@ -78,7 +78,7 @@ class TestCLI: """ Test the version string """ result = self.env.run('python-escpos', 'version') assert not result.stderr - assert_equals(escpos.__version__, result.stdout.strip()) + assert_equal(escpos.__version__, result.stdout.strip()) @nottest # disable this test as it is not that easy anymore to predict the outcome of this call def test_cli_text(self): @@ -109,6 +109,6 @@ class TestCLI: expect_error=True, expect_stderr=True ) - assert_equals(result.returncode, 2) + assert_equal(result.returncode, 2) assert 'error:' in result.stderr assert not result.files_updated From b608d599420e8371fb2898b342adc6bc7d261ab3 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 21:34:07 +0100 Subject: [PATCH 48/68] remove viivakoodi from dependencies --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 8052855..82bb443 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,6 @@ install_requires = argparse argcomplete future - viivakoodi>=0.8 setup_requires = setuptools_scm tests_require = jaconv From 3962bc991f5073e2a73ba06692d490be0621c1f7 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 22:04:27 +0100 Subject: [PATCH 49/68] improve test for soft barcode --- test/test_function_softbarcode.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/test_function_softbarcode.py b/test/test_function_softbarcode.py index fc702d7..78ae5bc 100644 --- a/test/test_function_softbarcode.py +++ b/test/test_function_softbarcode.py @@ -1,6 +1,7 @@ #!/usr/bin/python import escpos.printer as printer +import barcode.errors import pytest @@ -8,10 +9,15 @@ import pytest def instance(): return printer.Dummy() +def test_soft_barcode_ean8_invalid(instance): + """test with an invalid barcode""" + with pytest.raises(barcode.errors.BarcodeError): + instance.soft_barcode("ean8", "1234") def test_soft_barcode_ean8(instance): - instance.soft_barcode("ean8", "1234") + """test with a valid ean8 barcode""" + instance.soft_barcode("ean8", "1234567") def test_soft_barcode_ean8_nocenter(instance): - instance.soft_barcode("ean8", "1234", center=False) + instance.soft_barcode("ean8", "1234567", center=False) From 0057a498bbb877e2cd14261ffeb23e53438652e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Nov 2020 21:08:33 +0000 Subject: [PATCH 50/68] Bump actions/setup-python from v2.1.3 to v2.1.4 Bumps [actions/setup-python](https://github.com/actions/setup-python) from v2.1.3 to v2.1.4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.1.3...41b7212b1668f5de9d65e9c82aa777e6bbedb3a8) Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index a9e6944..78de3a5 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.1.3 + uses: actions/setup-python@v2.1.4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 0758a79e64e377329810ec15f4ecd4413e935b2f Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 22:33:51 +0100 Subject: [PATCH 51/68] test for graceful closing of socket --- test/test_printer_network.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 test/test_printer_network.py diff --git a/test/test_printer_network.py b/test/test_printer_network.py new file mode 100644 index 0000000..0669653 --- /dev/null +++ b/test/test_printer_network.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import escpos.printer as printer +import pytest +import mock +import socket + + +@pytest.fixture +def instance(): + socket.socket.connect = mock.Mock() + return printer.Network("localhost") + +def test_close_without_open(instance): + """try to close without opening (should fail gracefully) + + Currently we never open from our fixture, so calling close once + should be enough. In the future this might not be enough, + therefore we have to close twice in order to provoke an error + (if possible, this should not raise) + """ + instance.close() + instance.close() \ No newline at end of file From 36bbb6690f6a5327c537f12d7ec4bb74b682d4fe Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 22:34:57 +0100 Subject: [PATCH 52/68] handle socket error when closing --- src/escpos/printer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/escpos/printer.py b/src/escpos/printer.py index 59bacad..47834f6 100644 --- a/src/escpos/printer.py +++ b/src/escpos/printer.py @@ -242,7 +242,10 @@ class Network(Escpos): def close(self): """ Close TCP connection """ if self.device is not None: - self.device.shutdown(socket.SHUT_RDWR) + try: + self.device.shutdown(socket.SHUT_RDWR) + except socket.error: + pass self.device.close() From ed8ec0788ae98f27cdc44beb18e33ae72a3bb3a5 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 23:45:25 +0100 Subject: [PATCH 53/68] start with type annotation --- src/escpos/capabilities.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index 80f88a1..5ef1f1e 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -11,6 +11,8 @@ import yaml from tempfile import gettempdir import platform +from typing import Any, Dict + logging.basicConfig() logger = logging.getLogger(__name__) @@ -45,7 +47,7 @@ if full_load: logger.debug('Finished loading capabilities took %.2fs', time.time() - t0) -PROFILES = CAPABILITIES['profiles'] +PROFILES: Dict[str, Any] = CAPABILITIES['profiles'] class NotSupported(Exception): @@ -65,12 +67,12 @@ class BaseProfile(object): features, colors and more. """ - profile_data = {} + profile_data: Dict[str, Any] = {} def __getattr__(self, name): return self.profile_data[name] - def get_font(self, font): + def get_font(self, font) -> int: """Return the escpos index for `font`. Makes sure that the requested `font` is valid. """ @@ -97,7 +99,7 @@ class BaseProfile(object): return {v: k for k, v in self.codePages.items()} -def get_profile(name=None, **kwargs): +def get_profile(name:str=None, **kwargs): """Get the profile by name; if no name is given, return the default profile. """ @@ -111,7 +113,7 @@ def get_profile(name=None, **kwargs): CLASS_CACHE = {} -def get_profile_class(name): +def get_profile_class(name: str): """For the given profile name, load the data from the external database, then generate dynamically a class. """ From 1ad53eb642c8cdc29be7a38ba6a0822ea22fa325 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sun, 8 Nov 2020 23:51:57 +0100 Subject: [PATCH 54/68] drop support for python 3.5 --- .github/workflows/pythonpackage.yml | 2 +- setup.cfg | 4 ++-- src/escpos/capabilities.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 55b9ab0..219476f 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 diff --git a/setup.cfg b/setup.cfg index 82bb443..3423563 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,10 +18,10 @@ classifiers = Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython Topic :: Software Development :: Libraries :: Python Modules Topic :: Office/Business :: Financial :: Point-Of-Sale @@ -31,7 +31,7 @@ project_urls = Release Notes = https://github.com/python-escpos/python-escpos/releases [options] -python_requires = >=3.5 +python_requires = >=3.6 zip_safe = false include_package_data = true install_requires = diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index 5ef1f1e..c57acfa 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -99,7 +99,7 @@ class BaseProfile(object): return {v: k for k, v in self.codePages.items()} -def get_profile(name:str=None, **kwargs): +def get_profile(name: str=None, **kwargs): """Get the profile by name; if no name is given, return the default profile. """ From 69e9b9103ecf1076680f6896fbc597cb568602b7 Mon Sep 17 00:00:00 2001 From: "B. Howell" Date: Sat, 28 Nov 2020 00:41:11 +0100 Subject: [PATCH 55/68] Update escpos.py Set block_text to use the default font. This means calls will wrap to full paper width. --- 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 de4ee09..012bb17 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -539,7 +539,7 @@ class Escpos(object): if count > 0: self.text('\n' * count) - def block_text(self, txt, font=None, columns=None): + def block_text(self, txt, font="0", columns=None): """ Text is printed wrapped to specified columns Text has to be encoded in unicode. From 0b2118e146e2c266efea5f6f1348e11d5d000298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Dec 2020 06:30:08 +0000 Subject: [PATCH 56/68] Bump actions/setup-python from v2.1.4 to v2.2.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from v2.1.4 to v2.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.1.4...8c5ea631b2b2d5d8840cf4a2b183a8a0edc1e40d) Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 219476f..42d45f1 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.1.4 + uses: actions/setup-python@v2.2.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From c2e3b79e5ae336b93f2226c002f33a48891088b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Dec 2020 07:27:38 +0000 Subject: [PATCH 57/68] Bump actions/setup-python from v2.2.0 to v2.2.1 Bumps [actions/setup-python](https://github.com/actions/setup-python) from v2.2.0 to v2.2.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.2.0...3105fb18c05ddd93efea5f9e0bef7a03a6e9e7df) Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 42d45f1..d227049 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.2.0 + uses: actions/setup-python@v2.2.1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 7c82881f658c600018ef51ca2504eb8c59ef0dd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 May 2021 05:16:05 +0000 Subject: [PATCH 58/68] Bump actions/checkout from 2 to 2.3.4 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 2.3.4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v2.3.4) Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/documentation.yml | 2 +- .github/workflows/pythonpackage.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2dd483a..53343f7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v2.3.4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 6696c6d..4f9e8ce 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,7 +19,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 with: submodules: 'recursive' - uses: ammaraskar/sphinx-action@0.4 diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index d227049..d4f5edd 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -18,7 +18,7 @@ jobs: python-version: [3.6, 3.7, 3.8, 3.9] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2.3.4 with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} From 92fbcabdd2298761e4431c3106919fe5a1cd25df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Sep 2021 09:45:29 +0000 Subject: [PATCH 59/68] Bump actions/setup-python from 2.2.1 to 2.2.2 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.2.1 to 2.2.2. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.2.1...v2.2.2) Signed-off-by: dependabot[bot] --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index d4f5edd..3341ccb 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.2.1 + uses: actions/setup-python@v2.2.2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From a526be5ddfe2f42ea1306dfa712d2cb83415a1ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 10:04:25 +0000 Subject: [PATCH 60/68] Bump actions/checkout from 2.3.4 to 2.3.5 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.4 to 2.3.5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.3.4...v2.3.5) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/documentation.yml | 2 +- .github/workflows/pythonpackage.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 53343f7..c390002 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2.3.5 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 4f9e8ce..81be6a9 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,7 +19,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v2.3.5 with: submodules: 'recursive' - uses: ammaraskar/sphinx-action@0.4 diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index d4f5edd..30cef4d 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -18,7 +18,7 @@ jobs: python-version: [3.6, 3.7, 3.8, 3.9] steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v2.3.5 with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} From 15c18f225e4c9815da499a1b7e9749e191b5c58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Hern=C3=A1ndez?= Date: Wed, 27 Oct 2021 17:35:53 -0500 Subject: [PATCH 61/68] Update printer.py without the call to open function, the printer isn't initialize to print, i tested on a generic and epson physical printers. --- src/escpos/printer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/escpos/printer.py b/src/escpos/printer.py index 47834f6..7a0f7f3 100644 --- a/src/escpos/printer.py +++ b/src/escpos/printer.py @@ -364,6 +364,7 @@ if _WIN32PRINT: else: self.printer_name = win32print.GetDefaultPrinter() self.hPrinter = None + self.open() def open(self, job_name="python-escpos"): if self.printer_name is None: From 4a88bacd3f4bd8ea0f12058b76d04c55982312e1 Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sat, 30 Oct 2021 18:07:21 +0200 Subject: [PATCH 62/68] add black as lint step --- .github/workflows/black.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..b04fb15 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,10 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: psf/black@stable From 109a5d8a92501e1c64dc52b43e2a458d13bd510d Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sat, 30 Oct 2021 18:14:59 +0200 Subject: [PATCH 63/68] add config for black --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d9101a0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +extend-exclude = 'capabilities-data' + From 435f2bba24983f258eea6b74b5fbb2960988843b Mon Sep 17 00:00:00 2001 From: Patrick Kanzler Date: Sat, 30 Oct 2021 18:15:22 +0200 Subject: [PATCH 64/68] reformat codebase --- doc/conf.py | 180 ++++---- examples/barcodes.py | 8 +- examples/codepage_tables.py | 24 +- examples/qr_code.py | 2 +- examples/software_barcode.py | 4 +- examples/weather.py | 84 ++-- src/escpos/__init__.py | 6 +- src/escpos/capabilities.py | 64 +-- src/escpos/cli.py | 643 ++++++++++++++-------------- src/escpos/codepages.py | 2 +- src/escpos/config.py | 40 +- src/escpos/constants.py | 306 ++++++------- src/escpos/escpos.py | 437 ++++++++++++------- src/escpos/exceptions.py | 58 ++- src/escpos/image.py | 6 +- src/escpos/katakana.py | 127 +++--- src/escpos/magicencode.py | 73 ++-- src/escpos/printer.py | 107 +++-- test/test_abstract_base_class.py | 2 +- test/test_cli.py | 71 ++- test/test_function_barcode.py | 51 ++- test/test_function_cashdraw.py | 6 +- test/test_function_check_barcode.py | 182 ++++---- test/test_function_cut.py | 5 +- test/test_function_dummy_clear.py | 3 +- test/test_function_image.py | 83 ++-- test/test_function_linedisplay.py | 9 +- test/test_function_panel_button.py | 4 +- test/test_function_qr_native.py | 49 ++- test/test_function_qr_non-native.py | 2 +- test/test_function_set.py | 282 ++++++------ test/test_function_softbarcode.py | 2 + test/test_function_text.py | 23 +- test/test_functions.py | 4 +- test/test_image.py | 50 ++- test/test_load_module.py | 2 +- test/test_magicencode.py | 64 +-- test/test_printer_file.py | 12 +- test/test_printer_network.py | 3 +- test/test_profile.py | 22 +- test/test_with_statement.py | 2 +- 41 files changed, 1706 insertions(+), 1398 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index db40f67..e81e20b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,7 +14,8 @@ import sys import os -on_rtd = os.getenv('READTHEDOCS') == 'True' + +on_rtd = os.getenv("READTHEDOCS") == "True" if on_rtd: import escpos else: @@ -23,52 +24,52 @@ else: # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../src')) -root = os.path.relpath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.insert(0, os.path.abspath("../src")) +root = os.path.relpath(os.path.join(os.path.dirname(__file__), "..")) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.viewcode', - 'sphinx.ext.todo', - 'sphinx.ext.graphviz', - 'sphinx.ext.inheritance_diagram', - 'sphinxcontrib.spelling', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx.ext.todo", + "sphinx.ext.graphviz", + "sphinx.ext.inheritance_diagram", + "sphinxcontrib.spelling", ] # supress warnings for external images suppress_warnings = [ - 'image.nonlocal_uri', + "image.nonlocal_uri", ] # enable todos todo_include_todos = True # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'python-escpos' -copyright = u'2016, Manuel F Martinez and others' +project = u"python-escpos" +copyright = u"2016, Manuel F Martinez and others" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -81,45 +82,45 @@ else: # locally setuptools_scm should work release = get_version(root=root) # The short X.Y version. -version = '.'.join(release.split('.')[:2]) # The short X.Y version. +version = ".".join(release.split(".")[:2]) # The short X.Y version. # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- @@ -127,135 +128,139 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. if on_rtd: - html_theme = 'default' + html_theme = "default" else: try: import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' + + html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except ImportError: print("no sphinx_rtd_theme found, switching to nature") - html_theme = 'default' + html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = 'pyescpos.ico' +html_favicon = "pyescpos.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'python-escposdoc' +htmlhelp_basename = "python-escposdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'python-escpos.tex', u'python-escpos Documentation', - u'Manuel F Martinez and others', 'manual'), + ( + "index", + "python-escpos.tex", + u"python-escpos Documentation", + u"Manuel F Martinez and others", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -263,12 +268,17 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'python-escpos', u'python-escpos Documentation', - [u'Manuel F Martinez and others'], 1) + ( + "index", + "python-escpos", + u"python-escpos Documentation", + [u"Manuel F Martinez and others"], + 1, + ) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -277,22 +287,28 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'python-escpos', u'python-escpos Documentation', - u'Manuel F Martinez and others', 'python-escpos', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "python-escpos", + u"python-escpos Documentation", + u"Manuel F Martinez and others", + "python-escpos", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False # spellchecker spelling_ignore_pypi_package_names = True diff --git a/examples/barcodes.py b/examples/barcodes.py index 7e9c242..ef7944b 100644 --- a/examples/barcodes.py +++ b/examples/barcodes.py @@ -5,7 +5,7 @@ from escpos.printer import Usb p = Usb(0x0416, 0x5011, profile="POS-5890") # Print software and then hardware barcode with the same content -p.soft_barcode('code39', '123456') -p.text('\n') -p.text('\n') -p.barcode('123456', 'CODE39') +p.soft_barcode("code39", "123456") +p.text("\n") +p.text("\n") +p.barcode("123456", "CODE39") diff --git a/examples/codepage_tables.py b/examples/codepage_tables.py index 5a10ee4..dcec2a3 100644 --- a/examples/codepage_tables.py +++ b/examples/codepage_tables.py @@ -6,15 +6,23 @@ import six import sys from escpos import printer -from escpos.constants import CODEPAGE_CHANGE, ESC, CTL_LF, CTL_FF, CTL_CR, CTL_HT, CTL_VT +from escpos.constants import ( + CODEPAGE_CHANGE, + ESC, + CTL_LF, + CTL_FF, + CTL_CR, + CTL_HT, + CTL_VT, +) def main(): dummy = printer.Dummy() - dummy.hw('init') + dummy.hw("init") - for codepage in sys.argv[1:] or ['USA']: + for codepage in sys.argv[1:] or ["USA"]: dummy.set(height=2, width=2) dummy._raw(codepage + "\n\n\n") print_codepage(dummy, codepage) @@ -36,14 +44,14 @@ def print_codepage(printer, codepage): sep = "" # Table header - printer.set(font='b') + printer.set(font="b") printer._raw(" {}\n".format(sep.join(map(lambda s: hex(s)[2:], range(0, 16))))) printer.set() # The table for x in range(0, 16): # First column - printer.set(font='b') + printer.set(font="b") printer._raw("{} ".format(hex(x)[2:])) printer.set() @@ -51,12 +59,12 @@ def print_codepage(printer, codepage): byte = six.int2byte(x * 16 + y) if byte in (ESC, CTL_LF, CTL_FF, CTL_CR, CTL_HT, CTL_VT): - byte = ' ' + byte = " " printer._raw(byte) printer._raw(sep) - printer._raw('\n') + printer._raw("\n") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/qr_code.py b/examples/qr_code.py index 325f845..24b9d60 100644 --- a/examples/qr_code.py +++ b/examples/qr_code.py @@ -7,7 +7,7 @@ def usage(): print("usage: qr_code.py ") -if __name__ == '__main__': +if __name__ == "__main__": if len(sys.argv) != 2: usage() sys.exit(1) diff --git a/examples/software_barcode.py b/examples/software_barcode.py index 9bbcc09..0fdaaee 100644 --- a/examples/software_barcode.py +++ b/examples/software_barcode.py @@ -5,5 +5,5 @@ from escpos.printer import Usb p = Usb(0x0416, 0x5011, profile="POS-5890") # Some software barcodes -p.soft_barcode('code128', 'Hello') -p.soft_barcode('code39', '1234') +p.soft_barcode("code128", "Hello") +p.soft_barcode("code39", "1234") diff --git a/examples/weather.py b/examples/weather.py index 2ba457b..2584a4d 100644 --- a/examples/weather.py +++ b/examples/weather.py @@ -33,93 +33,95 @@ printer = Usb(0x0416, 0x5011, profile="POS-5890") # Technically you can use any other weather service, of course :) API_KEY = "YOUR API KEY" -LAT = "22.345490" # Your Location -LONG = "114.189945" # Your Location +LAT = "22.345490" # Your Location +LONG = "114.189945" # Your Location def forecast_icon(idx): - icon = data['daily']['data'][idx]['icon'] + icon = data["daily"]["data"][idx]["icon"] image = GRAPHICS_PATH + icon + ".png" return image # Dumps one forecast line to the printer def forecast(idx): - date = datetime.fromtimestamp(int(data['daily']['data'][idx]['time'])) + date = datetime.fromtimestamp(int(data["daily"]["data"][idx]["time"])) day = calendar.day_name[date.weekday()] - lo = data['daily']['data'][idx]['temperatureMin'] - hi = data['daily']['data'][idx]['temperatureMax'] - cond = data['daily']['data'][idx]['summary'] + lo = data["daily"]["data"][idx]["temperatureMin"] + hi = data["daily"]["data"][idx]["temperatureMax"] + cond = data["daily"]["data"][idx]["summary"] print(date) print(day) print(lo) print(hi) print(cond) time.sleep(1) - printer.set( - font='a', - height=2, - align='left', - bold=False, - double_height=False) - printer.text(day + ' \n ') - time.sleep(5) # Sleep to prevent printer buffer overflow - printer.text('\n') + printer.set(font="a", height=2, align="left", bold=False, double_height=False) + printer.text(day + " \n ") + time.sleep(5) # Sleep to prevent printer buffer overflow + printer.text("\n") printer.image(forecast_icon(idx)) - printer.text('low ' + str(lo)) + printer.text("low " + str(lo)) printer.text(deg) - printer.text('\n') - printer.text(' high ' + str(hi)) + printer.text("\n") + printer.text(" high " + str(hi)) printer.text(deg) - printer.text('\n') + printer.text("\n") # take care of pesky unicode dash - printer.text(cond.replace(u'\u2013', '-').encode('utf-8')) - printer.text('\n \n') + printer.text(cond.replace(u"\u2013", "-").encode("utf-8")) + printer.text("\n \n") def icon(): - icon = data['currently']['icon'] + icon = data["currently"]["icon"] image = GRAPHICS_PATH + icon + ".png" return image -deg = ' C' # Degree symbol on thermal printer, need to find a better way to use a proper degree symbol +deg = " C" # Degree symbol on thermal printer, need to find a better way to use a proper degree symbol # if you want Fahrenheit change units= to 'us' -url = "https://api.darksky.net/forecast/" + API_KEY + "/" + LAT + "," + LONG + \ - "?exclude=[alerts,minutely,hourly,flags]&units=si" # change last bit to 'us' for Fahrenheit +url = ( + "https://api.darksky.net/forecast/" + + API_KEY + + "/" + + LAT + + "," + + LONG + + "?exclude=[alerts,minutely,hourly,flags]&units=si" +) # change last bit to 'us' for Fahrenheit response = urllib.urlopen(url) data = json.loads(response.read()) printer.print_and_feed(n=1) printer.control("LF") -printer.set(font='a', height=2, align='center', bold=True, double_height=True) +printer.set(font="a", height=2, align="center", bold=True, double_height=True) printer.text("Weather Forecast") printer.text("\n") -printer.set(align='center') +printer.set(align="center") # Print current conditions -printer.set(font='a', height=2, align='center', bold=True, double_height=False) -printer.text('Current conditions: \n') +printer.set(font="a", height=2, align="center", bold=True, double_height=False) +printer.text("Current conditions: \n") printer.image(icon()) printer.text("\n") -printer.set(font='a', height=2, align='left', bold=False, double_height=False) -temp = data['currently']['temperature'] -cond = data['currently']['summary'] +printer.set(font="a", height=2, align="left", bold=False, double_height=False) +temp = data["currently"]["temperature"] +cond = data["currently"]["summary"] printer.text(temp) -printer.text(' ') +printer.text(" ") printer.text(deg) -printer.text(' ') -printer.text('\n') -printer.text('Sky: ' + cond) -printer.text('\n') -printer.text('\n') +printer.text(" ") +printer.text("\n") +printer.text("Sky: " + cond) +printer.text("\n") +printer.text("\n") # Print forecast -printer.set(font='a', height=2, align='center', bold=True, double_height=False) -printer.text('Forecast: \n') +printer.set(font="a", height=2, align="center", bold=True, double_height=False) +printer.text("Forecast: \n") forecast(0) forecast(1) printer.cut() diff --git a/src/escpos/__init__.py b/src/escpos/__init__.py index 73e7644..bf739e5 100644 --- a/src/escpos/__init__.py +++ b/src/escpos/__init__.py @@ -9,7 +9,7 @@ try: from .version import version as __version__ # noqa except ImportError: # pragma: no cover raise ImportError( - 'Failed to find (autogenerated) version.py. ' - 'This might be because you are installing from GitHub\'s tarballs, ' - 'use the PyPI ones.' + "Failed to find (autogenerated) version.py. " + "This might be because you are installing from GitHub's tarballs, " + "use the PyPI ones." ) diff --git a/src/escpos/capabilities.py b/src/escpos/capabilities.py index c57acfa..02ae57b 100644 --- a/src/escpos/capabilities.py +++ b/src/escpos/capabilities.py @@ -16,48 +16,53 @@ from typing import Any, Dict logging.basicConfig() logger = logging.getLogger(__name__) -pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', gettempdir()) -pickle_path = path.join(pickle_dir, '{v}.capabilities.pickle'.format(v=platform.python_version())) +pickle_dir = environ.get("ESCPOS_CAPABILITIES_PICKLE_DIR", gettempdir()) +pickle_path = path.join( + pickle_dir, "{v}.capabilities.pickle".format(v=platform.python_version()) +) # get a temporary file from pkg_resources if no file is specified in env -capabilities_path = environ.get('ESCPOS_CAPABILITIES_FILE', - pkg_resources.resource_filename(__name__, 'capabilities.json')) +capabilities_path = environ.get( + "ESCPOS_CAPABILITIES_FILE", + pkg_resources.resource_filename(__name__, "capabilities.json"), +) # Load external printer database t0 = time.time() -logger.debug('Using capabilities from file: %s', capabilities_path) +logger.debug("Using capabilities from file: %s", capabilities_path) if path.exists(pickle_path): if path.getmtime(capabilities_path) > path.getmtime(pickle_path): - logger.debug('Found a more recent capabilities file') + logger.debug("Found a more recent capabilities file") full_load = True else: full_load = False - logger.debug('Loading capabilities from pickle in %s', pickle_path) - with open(pickle_path, 'rb') as cf: + logger.debug("Loading capabilities from pickle in %s", pickle_path) + with open(pickle_path, "rb") as cf: CAPABILITIES = pickle.load(cf) else: - logger.debug('Capabilities pickle file not found: %s', pickle_path) + logger.debug("Capabilities pickle file not found: %s", pickle_path) full_load = True if full_load: - logger.debug('Loading and pickling capabilities') - with open(capabilities_path) as cp, open(pickle_path, 'wb') as pp: + logger.debug("Loading and pickling capabilities") + with open(capabilities_path) as cp, open(pickle_path, "wb") as pp: CAPABILITIES = yaml.safe_load(cp) pickle.dump(CAPABILITIES, pp, protocol=2) -logger.debug('Finished loading capabilities took %.2fs', time.time() - t0) +logger.debug("Finished loading capabilities took %.2fs", time.time() - t0) -PROFILES: Dict[str, Any] = CAPABILITIES['profiles'] +PROFILES: Dict[str, Any] = CAPABILITIES["profiles"] class NotSupported(Exception): """Raised if a requested feature is not supported by the printer profile. """ + pass -BARCODE_B = 'barcodeB' +BARCODE_B = "barcodeB" class BaseProfile(object): @@ -76,37 +81,35 @@ class BaseProfile(object): """Return the escpos index for `font`. Makes sure that the requested `font` is valid. """ - font = {'a': 0, 'b': 1}.get(font, font) + font = {"a": 0, "b": 1}.get(font, font) if not six.text_type(font) in self.fonts: raise NotSupported( - '"{}" is not a valid font in the current profile'.format(font)) + '"{}" is not a valid font in the current profile'.format(font) + ) return font def get_columns(self, font): - """ Return the number of columns for the given 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[six.text_type(font)]["columns"] def supports(self, feature): - """Return true/false for the given feature. - """ + """Return true/false for the given feature.""" return self.features.get(feature) def get_code_pages(self): - """Return the support code pages as a ``{name: index}`` dict. - """ + """Return the support code pages as a ``{name: index}`` dict.""" return {v: k for k, v in self.codePages.items()} -def get_profile(name: str=None, **kwargs): +def get_profile(name: str = None, **kwargs): """Get the profile by name; if no name is given, return the default profile. """ if isinstance(name, Profile): return name - clazz = get_profile_class(name or 'default') + clazz = get_profile_class(name or "default") return clazz(**kwargs) @@ -120,9 +123,8 @@ def get_profile_class(name: str): if name not in CLASS_CACHE: profile_data = PROFILES[name] profile_name = clean(name) - class_name = '{}{}Profile'.format( - profile_name[0].upper(), profile_name[1:]) - new_class = type(class_name, (BaseProfile,), {'profile_data': profile_data}) + class_name = "{}{}Profile".format(profile_name[0].upper(), profile_name[1:]) + new_class = type(class_name, (BaseProfile,), {"profile_data": profile_data}) CLASS_CACHE[name] = new_class return CLASS_CACHE[name] @@ -130,13 +132,13 @@ def get_profile_class(name: str): def clean(s): # Remove invalid characters - s = re.sub('[^0-9a-zA-Z_]', '', s) + s = re.sub("[^0-9a-zA-Z_]", "", s) # Remove leading characters until we find a letter or underscore - s = re.sub('^[^a-zA-Z_]+', '', s) + s = re.sub("^[^a-zA-Z_]+", "", s) return str(s) -class Profile(get_profile_class('default')): +class Profile(get_profile_class("default")): """ For users, who want to provide their profile """ diff --git a/src/escpos/cli.py b/src/escpos/cli.py index c9f9125..98487c0 100644 --- a/src/escpos/cli.py +++ b/src/escpos/cli.py @@ -11,6 +11,7 @@ It requires you to have a configuration file. See documentation for details. import argparse + try: import argcomplete except ImportError: @@ -24,14 +25,14 @@ from . import version # Must be defined before it's used in DEMO_FUNCTIONS def str_to_bool(string): - """ Used as a type in argparse so that we get back a proper + """Used as a type in argparse so that we get back a proper bool instead of always True """ - return string.lower() in ('y', 'yes', '1', 'true') + return string.lower() in ("y", "yes", "1", "true") # A list of functions that work better with a newline to be sent after them. -REQUIRES_NEWLINE = ('qr', 'barcode', 'text', 'block_text') +REQUIRES_NEWLINE = ("qr", "barcode", "text", "block_text") # Used in demo method @@ -39,40 +40,46 @@ REQUIRES_NEWLINE = ('qr', 'barcode', 'text', 'block_text') # manual translation is done in the case of barcodes_a -> barcode. # Value: A list of dictionaries to pass to the escpos function as arguments. DEMO_FUNCTIONS = { - 'text': [ - {'txt': 'Hello, World!\n', } + "text": [ + { + "txt": "Hello, World!\n", + } ], - 'qr': [ - {'content': 'This tests a QR code'}, - {'content': 'https://en.wikipedia.org/'} + "qr": [ + {"content": "This tests a QR code"}, + {"content": "https://en.wikipedia.org/"}, ], - 'barcodes_a': [ - {'bc': 'UPC-A', 'code': '13243546576'}, - {'bc': 'UPC-E', 'code': '132435'}, - {'bc': 'EAN13', 'code': '1324354657687'}, - {'bc': 'EAN8', 'code': '1324354'}, - {'bc': 'CODE39', 'code': 'TEST'}, - {'bc': 'ITF', 'code': '55867492279103'}, - {'bc': 'NW7', 'code': 'A00000000A'}, + "barcodes_a": [ + {"bc": "UPC-A", "code": "13243546576"}, + {"bc": "UPC-E", "code": "132435"}, + {"bc": "EAN13", "code": "1324354657687"}, + {"bc": "EAN8", "code": "1324354"}, + {"bc": "CODE39", "code": "TEST"}, + {"bc": "ITF", "code": "55867492279103"}, + {"bc": "NW7", "code": "A00000000A"}, ], - 'barcodes_b': [ - {'bc': 'UPC-A', 'code': '13243546576', 'function_type': 'B'}, - {'bc': 'UPC-E', 'code': '132435', 'function_type': 'B'}, - {'bc': 'EAN13', 'code': '1324354657687', 'function_type': 'B'}, - {'bc': 'EAN8', 'code': '1324354', 'function_type': 'B'}, - {'bc': 'CODE39', 'code': 'TEST', 'function_type': 'B'}, - {'bc': 'ITF', 'code': '55867492279103', 'function_type': 'B'}, - {'bc': 'NW7', 'code': 'A00000000A', 'function_type': 'B'}, - {'bc': 'CODE93', 'code': 'A00000000A', 'function_type': 'B'}, - {'bc': 'CODE93', 'code': '1324354657687', 'function_type': 'B'}, - {'bc': 'CODE128A', 'code': 'TEST', 'function_type': 'B'}, - {'bc': 'CODE128B', 'code': 'TEST', 'function_type': 'B'}, - {'bc': 'CODE128C', 'code': 'TEST', 'function_type': 'B'}, - {'bc': 'GS1-128', 'code': '00123456780000000001', 'function_type': 'B'}, - {'bc': 'GS1 DataBar Omnidirectional', 'code': '0000000000000', 'function_type': 'B'}, - {'bc': 'GS1 DataBar Truncated', 'code': '0000000000000', 'function_type': 'B'}, - {'bc': 'GS1 DataBar Limited', 'code': '0000000000000', 'function_type': 'B'}, - {'bc': 'GS1 DataBar Expanded', 'code': '00AAAAAAA', 'function_type': 'B'}, + "barcodes_b": [ + {"bc": "UPC-A", "code": "13243546576", "function_type": "B"}, + {"bc": "UPC-E", "code": "132435", "function_type": "B"}, + {"bc": "EAN13", "code": "1324354657687", "function_type": "B"}, + {"bc": "EAN8", "code": "1324354", "function_type": "B"}, + {"bc": "CODE39", "code": "TEST", "function_type": "B"}, + {"bc": "ITF", "code": "55867492279103", "function_type": "B"}, + {"bc": "NW7", "code": "A00000000A", "function_type": "B"}, + {"bc": "CODE93", "code": "A00000000A", "function_type": "B"}, + {"bc": "CODE93", "code": "1324354657687", "function_type": "B"}, + {"bc": "CODE128A", "code": "TEST", "function_type": "B"}, + {"bc": "CODE128B", "code": "TEST", "function_type": "B"}, + {"bc": "CODE128C", "code": "TEST", "function_type": "B"}, + {"bc": "GS1-128", "code": "00123456780000000001", "function_type": "B"}, + { + "bc": "GS1 DataBar Omnidirectional", + "code": "0000000000000", + "function_type": "B", + }, + {"bc": "GS1 DataBar Truncated", "code": "0000000000000", "function_type": "B"}, + {"bc": "GS1 DataBar Limited", "code": "0000000000000", "function_type": "B"}, + {"bc": "GS1 DataBar Expanded", "code": "00AAAAAAA", "function_type": "B"}, ], } @@ -84,356 +91,355 @@ DEMO_FUNCTIONS = { # arguments: A list of dicts of args for subparser.add_argument ESCPOS_COMMANDS = [ { - 'parser': { - 'name': 'qr', - 'help': 'Print a QR code', + "parser": { + "name": "qr", + "help": "Print a QR code", }, - 'defaults': { - 'func': 'qr', + "defaults": { + "func": "qr", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--content',), - 'help': 'Text to print as a qr code', - 'required': True, + "option_strings": ("--content",), + "help": "Text to print as a qr code", + "required": True, }, { - 'option_strings': ('--size',), - 'help': 'QR code size (1-16) [default:3]', - 'required': False, - 'type': int, + "option_strings": ("--size",), + "help": "QR code size (1-16) [default:3]", + "required": False, + "type": int, + }, + ], + }, + { + "parser": { + "name": "barcode", + "help": "Print a barcode", + }, + "defaults": { + "func": "barcode", + }, + "arguments": [ + { + "option_strings": ("--code",), + "help": "Barcode data to print", + "required": True, + }, + { + "option_strings": ("--bc",), + "help": "Barcode format", + "required": True, + }, + { + "option_strings": ("--height",), + "help": "Barcode height in px", + "type": int, + }, + { + "option_strings": ("--width",), + "help": "Barcode width", + "type": int, + }, + { + "option_strings": ("--pos",), + "help": "Label position", + "choices": ["BELOW", "ABOVE", "BOTH", "OFF"], + }, + { + "option_strings": ("--font",), + "help": "Label font", + "choices": ["A", "B"], + }, + { + "option_strings": ("--align_ct",), + "help": "Align barcode center", + "type": str_to_bool, + }, + { + "option_strings": ("--function_type",), + "help": "ESCPOS function type", + "choices": ["A", "B"], + }, + ], + }, + { + "parser": { + "name": "text", + "help": "Print plain text", + }, + "defaults": { + "func": "text", + }, + "arguments": [ + { + "option_strings": ("--txt",), + "help": "Plain text to print", + "required": True, } ], }, { - 'parser': { - 'name': 'barcode', - 'help': 'Print a barcode', + "parser": { + "name": "block_text", + "help": "Print wrapped text", }, - 'defaults': { - 'func': 'barcode', + "defaults": { + "func": "block_text", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--code',), - 'help': 'Barcode data to print', - 'required': True, + "option_strings": ("--txt",), + "help": "block_text to print", + "required": True, }, { - 'option_strings': ('--bc',), - 'help': 'Barcode format', - 'required': True, - }, - { - 'option_strings': ('--height',), - 'help': 'Barcode height in px', - 'type': int, - }, - { - 'option_strings': ('--width',), - 'help': 'Barcode width', - 'type': int, - }, - { - 'option_strings': ('--pos',), - 'help': 'Label position', - 'choices': ['BELOW', 'ABOVE', 'BOTH', 'OFF'], - }, - { - 'option_strings': ('--font',), - 'help': 'Label font', - 'choices': ['A', 'B'], - }, - { - 'option_strings': ('--align_ct',), - 'help': 'Align barcode center', - 'type': str_to_bool, - }, - { - 'option_strings': ('--function_type',), - 'help': 'ESCPOS function type', - 'choices': ['A', 'B'], + "option_strings": ("--columns",), + "help": "Number of columns", + "type": int, }, ], }, { - 'parser': { - 'name': 'text', - 'help': 'Print plain text', + "parser": { + "name": "cut", + "help": "Cut the paper", }, - 'defaults': { - 'func': 'text', + "defaults": { + "func": "cut", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--txt',), - 'help': 'Plain text to print', - 'required': True, - } - ], - }, - { - 'parser': { - 'name': 'block_text', - 'help': 'Print wrapped text', - }, - 'defaults': { - 'func': 'block_text', - }, - 'arguments': [ - { - 'option_strings': ('--txt',), - 'help': 'block_text to print', - 'required': True, - }, - { - 'option_strings': ('--columns',), - 'help': 'Number of columns', - 'type': int, + "option_strings": ("--mode",), + "help": "Type of cut", + "choices": ["FULL", "PART"], }, ], }, { - 'parser': { - 'name': 'cut', - 'help': 'Cut the paper', + "parser": { + "name": "cashdraw", + "help": "Kick the cash drawer", }, - 'defaults': { - 'func': 'cut', + "defaults": { + "func": "cashdraw", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--mode',), - 'help': 'Type of cut', - 'choices': ['FULL', 'PART'], + "option_strings": ("--pin",), + "help": "Which PIN to kick", + "choices": [2, 5], }, ], }, { - 'parser': { - 'name': 'cashdraw', - 'help': 'Kick the cash drawer', + "parser": { + "name": "image", + "help": "Print an image", }, - 'defaults': { - 'func': 'cashdraw', + "defaults": { + "func": "image", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--pin',), - 'help': 'Which PIN to kick', - 'choices': [2, 5], + "option_strings": ("--img_source",), + "help": "Path to image", + "required": True, + }, + { + "option_strings": ("--impl",), + "help": "Implementation to use", + "choices": ["bitImageRaster", "bitImageColumn", "graphics"], + }, + { + "option_strings": ("--high_density_horizontal",), + "help": "Image density (horizontal)", + "type": str_to_bool, + }, + { + "option_strings": ("--high_density_vertical",), + "help": "Image density (vertical)", + "type": str_to_bool, }, ], }, { - 'parser': { - 'name': 'image', - 'help': 'Print an image', + "parser": { + "name": "fullimage", + "help": "Print a fullimage", }, - 'defaults': { - 'func': 'image', + "defaults": { + "func": "fullimage", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--img_source',), - 'help': 'Path to image', - 'required': True, + "option_strings": ("--img",), + "help": "Path to img", + "required": True, }, { - 'option_strings': ('--impl',), - 'help': 'Implementation to use', - 'choices': ['bitImageRaster', 'bitImageColumn', 'graphics'], + "option_strings": ("--max_height",), + "help": "Max height of image in px", + "type": int, }, { - 'option_strings': ('--high_density_horizontal',), - 'help': 'Image density (horizontal)', - 'type': str_to_bool, + "option_strings": ("--width",), + "help": "Max width of image in px", + "type": int, }, { - 'option_strings': ('--high_density_vertical',), - 'help': 'Image density (vertical)', - 'type': str_to_bool, - }, - - ], - }, - { - 'parser': { - 'name': 'fullimage', - 'help': 'Print a fullimage', - }, - 'defaults': { - 'func': 'fullimage', - }, - 'arguments': [ - { - 'option_strings': ('--img',), - 'help': 'Path to img', - 'required': True, + "option_strings": ("--histeq",), + "help": "Equalize the histrogram", + "type": str_to_bool, }, { - 'option_strings': ('--max_height',), - 'help': 'Max height of image in px', - 'type': int, - }, - { - 'option_strings': ('--width',), - 'help': 'Max width of image in px', - 'type': int, - }, - { - 'option_strings': ('--histeq',), - 'help': 'Equalize the histrogram', - 'type': str_to_bool, - }, - { - 'option_strings': ('--bandsize',), - 'help': 'Size of bands to divide into when printing', - 'type': int, + "option_strings": ("--bandsize",), + "help": "Size of bands to divide into when printing", + "type": int, }, ], }, { - 'parser': { - 'name': 'charcode', - 'help': 'Set character code table', + "parser": { + "name": "charcode", + "help": "Set character code table", }, - 'defaults': { - 'func': 'charcode', + "defaults": { + "func": "charcode", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--code',), - 'help': 'Character code', - 'required': True, + "option_strings": ("--code",), + "help": "Character code", + "required": True, }, ], }, { - 'parser': { - 'name': 'set', - 'help': 'Set text properties', + "parser": { + "name": "set", + "help": "Set text properties", }, - 'defaults': { - 'func': 'set', + "defaults": { + "func": "set", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--align',), - 'help': 'Horizontal alignment', - 'choices': ['left', 'center', 'right'], + "option_strings": ("--align",), + "help": "Horizontal alignment", + "choices": ["left", "center", "right"], }, { - 'option_strings': ('--font',), - 'help': 'Font choice', - 'choices': ['left', 'center', 'right'], + "option_strings": ("--font",), + "help": "Font choice", + "choices": ["left", "center", "right"], }, { - 'option_strings': ('--text_type',), - 'help': 'Text properties', - 'choices': ['B', 'U', 'U2', 'BU', 'BU2', 'NORMAL'], + "option_strings": ("--text_type",), + "help": "Text properties", + "choices": ["B", "U", "U2", "BU", "BU2", "NORMAL"], }, { - 'option_strings': ('--width',), - 'help': 'Width multiplier', - 'type': int, + "option_strings": ("--width",), + "help": "Width multiplier", + "type": int, }, { - 'option_strings': ('--height',), - 'help': 'Height multiplier', - 'type': int, + "option_strings": ("--height",), + "help": "Height multiplier", + "type": int, }, { - 'option_strings': ('--density',), - 'help': 'Print density', - 'type': int, + "option_strings": ("--density",), + "help": "Print density", + "type": int, }, { - 'option_strings': ('--invert',), - 'help': 'White on black printing', - 'type': str_to_bool, + "option_strings": ("--invert",), + "help": "White on black printing", + "type": str_to_bool, }, { - 'option_strings': ('--smooth',), - 'help': 'Text smoothing. Effective on >: 4x4 text', - 'type': str_to_bool, + "option_strings": ("--smooth",), + "help": "Text smoothing. Effective on >: 4x4 text", + "type": str_to_bool, }, { - 'option_strings': ('--flip',), - 'help': 'Text smoothing. Effective on >: 4x4 text', - 'type': str_to_bool, + "option_strings": ("--flip",), + "help": "Text smoothing. Effective on >: 4x4 text", + "type": str_to_bool, }, ], }, { - 'parser': { - 'name': 'hw', - 'help': 'Hardware operations', + "parser": { + "name": "hw", + "help": "Hardware operations", }, - 'defaults': { - 'func': 'hw', + "defaults": { + "func": "hw", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--hw',), - 'help': 'Operation', - 'choices': ['INIT', 'SELECT', 'RESET'], - 'required': True, + "option_strings": ("--hw",), + "help": "Operation", + "choices": ["INIT", "SELECT", "RESET"], + "required": True, }, ], }, { - 'parser': { - 'name': 'control', - 'help': 'Control sequences', + "parser": { + "name": "control", + "help": "Control sequences", }, - 'defaults': { - 'func': 'control', + "defaults": { + "func": "control", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--ctl',), - 'help': 'Control sequence', - 'choices': ['LF', 'FF', 'CR', 'HT', 'VT'], - 'required': True, + "option_strings": ("--ctl",), + "help": "Control sequence", + "choices": ["LF", "FF", "CR", "HT", "VT"], + "required": True, }, { - 'option_strings': ('--pos',), - 'help': 'Horizontal tab position (1-4)', - 'type': int, + "option_strings": ("--pos",), + "help": "Horizontal tab position (1-4)", + "type": int, }, ], }, { - 'parser': { - 'name': 'panel_buttons', - 'help': 'Controls panel buttons', + "parser": { + "name": "panel_buttons", + "help": "Controls panel buttons", }, - 'defaults': { - 'func': 'panel_buttons', + "defaults": { + "func": "panel_buttons", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--enable',), - 'help': 'Feed button enabled', - 'type': str_to_bool, - 'required': True, + "option_strings": ("--enable",), + "help": "Feed button enabled", + "type": str_to_bool, + "required": True, }, ], }, { - 'parser': { - 'name': 'raw', - 'help': 'Raw data', + "parser": { + "name": "raw", + "help": "Raw data", }, - 'defaults': { - 'func': '_raw', + "defaults": { + "func": "_raw", }, - 'arguments': [ + "arguments": [ { - 'option_strings': ('--msg',), - 'help': 'Raw data to send', - 'required': True, + "option_strings": ("--msg",), + "help": "Raw data to send", + "required": True, }, ], }, @@ -449,68 +455,71 @@ def main(): """ parser = argparse.ArgumentParser( - description='CLI for python-escpos', - epilog='Printer configuration is defined in the python-escpos config' - 'file. See documentation for details.', + description="CLI for python-escpos", + epilog="Printer configuration is defined in the python-escpos config" + "file. See documentation for details.", ) - parser.register('type', 'bool', str_to_bool) + parser.register("type", "bool", str_to_bool) # Allow config file location to be passed parser.add_argument( - '-c', '--config', - help='Alternate path to the configuration file', + "-c", + "--config", + help="Alternate path to the configuration file", ) # Everything interesting runs off of a subparser so we can use the format # cli [subparser] -args command_subparsers = parser.add_subparsers( - title='ESCPOS Command', - dest='parser', + title="ESCPOS Command", + dest="parser", ) # fix inconsistencies in the behaviour of some versions of argparse - command_subparsers.required = False # force 'required' testing + command_subparsers.required = False # force 'required' testing # Build the ESCPOS command arguments for command in ESCPOS_COMMANDS: - parser_command = command_subparsers.add_parser(**command['parser']) - parser_command.set_defaults(**command['defaults']) - for argument in command['arguments']: - option_strings = argument.pop('option_strings') + parser_command = command_subparsers.add_parser(**command["parser"]) + parser_command.set_defaults(**command["defaults"]) + for argument in command["arguments"]: + option_strings = argument.pop("option_strings") parser_command.add_argument(*option_strings, **argument) # Build any custom arguments - parser_command_demo = command_subparsers.add_parser('demo', - help='Demonstrates various functions') - parser_command_demo.set_defaults(func='demo') + parser_command_demo = command_subparsers.add_parser( + "demo", help="Demonstrates various functions" + ) + parser_command_demo.set_defaults(func="demo") demo_group = parser_command_demo.add_mutually_exclusive_group() demo_group.add_argument( - '--barcodes-a', - help='Print demo barcodes for function type A', - action='store_true', + "--barcodes-a", + help="Print demo barcodes for function type A", + action="store_true", ) demo_group.add_argument( - '--barcodes-b', - help='Print demo barcodes for function type B', - action='store_true', + "--barcodes-b", + help="Print demo barcodes for function type B", + action="store_true", ) demo_group.add_argument( - '--qr', - help='Print some demo QR codes', - action='store_true', + "--qr", + help="Print some demo QR codes", + action="store_true", ) demo_group.add_argument( - '--text', - help='Print some demo text', - action='store_true', + "--text", + help="Print some demo text", + action="store_true", ) - parser_command_version = command_subparsers.add_parser('version', - help='Print the version of python-escpos') + parser_command_version = command_subparsers.add_parser( + "version", help="Print the version of python-escpos" + ) parser_command_version.set_defaults(version=True) # hook in argcomplete - if 'argcomplete' in globals(): + if "argcomplete" in globals(): argcomplete.autocomplete(parser) # Get only arguments actually passed @@ -518,16 +527,18 @@ 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 six.iteritems(args_dict) if v is not None + ) # If version should be printed, do this, then exit - print_version = command_arguments.pop('version', None) + print_version = command_arguments.pop("version", None) if print_version: print(version.version) sys.exit() # If there was a config path passed, grab it - config_path = command_arguments.pop('config', None) + config_path = command_arguments.pop("config", None) # Load the configuration and defined printer saved_config = config.Config() @@ -535,12 +546,12 @@ def main(): printer = saved_config.printer() if not printer: - raise Exception('No printers loaded from config') + raise Exception("No printers loaded from config") - target_command = command_arguments.pop('func') + target_command = command_arguments.pop("func") # remove helper-argument 'parser' from dict - command_arguments.pop('parser', None) + command_arguments.pop("parser", None) if hasattr(printer, target_command): # print command with args @@ -548,7 +559,7 @@ def main(): if target_command in REQUIRES_NEWLINE: printer.text("\n") else: - command_arguments['printer'] = printer + command_arguments["printer"] = printer globals()[target_command](**command_arguments) @@ -564,14 +575,14 @@ def demo(printer, **kwargs): for demo_choice in kwargs.keys(): command = getattr( printer, - demo_choice - .replace('barcodes_a', 'barcode') - .replace('barcodes_b', 'barcode') + demo_choice.replace("barcodes_a", "barcode").replace( + "barcodes_b", "barcode" + ), ) for params in DEMO_FUNCTIONS[demo_choice]: command(**params) printer.cut() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/escpos/codepages.py b/src/escpos/codepages.py index ca63ef3..f858b08 100644 --- a/src/escpos/codepages.py +++ b/src/escpos/codepages.py @@ -21,4 +21,4 @@ class CodePageManager: return self.data[encoding] -CodePages = CodePageManager(CAPABILITIES['encodings']) +CodePages = CodePageManager(CAPABILITIES["encodings"]) diff --git a/src/escpos/config.py b/src/escpos/config.py index b4cdb33..8fdafb0 100644 --- a/src/escpos/config.py +++ b/src/escpos/config.py @@ -14,16 +14,17 @@ from . import exceptions class Config(object): - """ Configuration handler class. + """Configuration handler class. This class loads configuration from a default or specificed directory. It can create your defined printer and return it to you. """ - _app_name = 'python-escpos' - _config_file = 'config.yaml' + + _app_name = "python-escpos" + _config_file = "config.yaml" def __init__(self): - """ Initialize configuration. + """Initialize configuration. Remember to add anything that needs to be reset between configurations to self._reset_config @@ -35,7 +36,7 @@ class Config(object): self._printer_config = None def _reset_config(self): - """ Clear the loaded configuration. + """Clear the loaded configuration. If we are loading a changed config, we don't want to have leftover data. @@ -47,7 +48,7 @@ class Config(object): self._printer_config = None def load(self, config_path=None): - """ Load and parse the configuration file using pyyaml + """Load and parse the configuration file using pyyaml :param config_path: An optional file path, file handle, or byte string for the configuration file. @@ -58,31 +59,32 @@ class Config(object): if not config_path: config_path = os.path.join( - appdirs.user_config_dir(self._app_name), - self._config_file + appdirs.user_config_dir(self._app_name), self._config_file ) try: # First check if it's file like. If it is, pyyaml can load it. # I'm checking type instead of catching exceptions to keep the # exception handling simple - if hasattr(config_path, 'read'): + if hasattr(config_path, "read"): config = yaml.safe_load(config_path) else: # If it isn't, it's a path. We have to open it first, otherwise # pyyaml will try to read it as yaml - with open(config_path, 'rb') as config_file: + with open(config_path, "rb") as config_file: config = yaml.safe_load(config_file) except EnvironmentError: - raise exceptions.ConfigNotFoundError('Couldn\'t read config at {config_path}'.format( - config_path=str(config_path), - )) + raise exceptions.ConfigNotFoundError( + "Couldn't read config at {config_path}".format( + config_path=str(config_path), + ) + ) except yaml.YAMLError: - raise exceptions.ConfigSyntaxError('Error parsing YAML') + raise exceptions.ConfigSyntaxError("Error parsing YAML") - if 'printer' in config: - self._printer_config = config['printer'] - self._printer_name = self._printer_config.pop('type').title() + if "printer" in config: + self._printer_config = config["printer"] + self._printer_name = self._printer_config.pop("type").title() if not self._printer_name or not hasattr(printer, self._printer_name): raise exceptions.ConfigSyntaxError( @@ -94,7 +96,7 @@ class Config(object): self._has_loaded = True def printer(self): - """ Returns a printer that was defined in the config, or throws an + """Returns a printer that was defined in the config, or throws an exception. This method loads the default config if one hasn't beeen already loaded. @@ -104,7 +106,7 @@ class Config(object): self.load() if not self._printer_name: - raise exceptions.ConfigSectionMissingError('printer') + raise exceptions.ConfigSectionMissingError("printer") if not self._printer: # We could catch init errors and make them a ConfigSyntaxError, diff --git a/src/escpos/constants.py b/src/escpos/constants.py index 5e5b38c..80c921c 100644 --- a/src/escpos/constants.py +++ b/src/escpos/constants.py @@ -16,120 +16,125 @@ import six # Control characters # as labelled in https://www.novopos.ch/client/EPSON/TM-T20/TM-T20_eng_qr.pdf -NUL = b'\x00' -EOT = b'\x04' -ENQ = b'\x05' -DLE = b'\x10' -DC4 = b'\x14' -CAN = b'\x18' -ESC = b'\x1b' -FS = b'\x1c' -GS = b'\x1d' +NUL = b"\x00" +EOT = b"\x04" +ENQ = b"\x05" +DLE = b"\x10" +DC4 = b"\x14" +CAN = b"\x18" +ESC = b"\x1b" +FS = b"\x1c" +GS = b"\x1d" # Feed control sequences -CTL_LF = b'\n' # Print and line feed -CTL_FF = b'\f' # Form feed -CTL_CR = b'\r' # Carriage return -CTL_HT = b'\t' # Horizontal tab -CTL_SET_HT = ESC + b'\x44' # Set horizontal tab positions -CTL_VT = b'\v' # Vertical tab +CTL_LF = b"\n" # Print and line feed +CTL_FF = b"\f" # Form feed +CTL_CR = b"\r" # Carriage return +CTL_HT = b"\t" # Horizontal tab +CTL_SET_HT = ESC + b"\x44" # Set horizontal tab positions +CTL_VT = b"\v" # Vertical tab # Printer hardware -HW_INIT = ESC + b'@' # Clear data in buffer and reset modes -HW_SELECT = ESC + b'=\x01' # Printer select +HW_INIT = ESC + b"@" # Clear data in buffer and reset modes +HW_SELECT = ESC + b"=\x01" # Printer select -HW_RESET = ESC + b'\x3f\x0a\x00' # Reset printer hardware - # (TODO: Where is this specified?) +HW_RESET = ESC + b"\x3f\x0a\x00" # Reset printer hardware +# (TODO: Where is this specified?) # Cash Drawer (ESC p ) -_CASH_DRAWER = lambda m, t1='', t2='': ESC + b'p' + m + six.int2byte(t1) + six.int2byte(t2) -CD_KICK_DEC_SEQUENCE = lambda esc, p, m, t1=50, t2=50: six.int2byte(esc) + six.int2byte(p) + six.int2byte(m) + six.int2byte(t1) + six.int2byte(t2) -CD_KICK_2 = _CASH_DRAWER(b'\x00', 50, 50) # Sends a pulse to pin 2 [] -CD_KICK_5 = _CASH_DRAWER(b'\x01', 50, 50) # Sends a pulse to pin 5 [] +_CASH_DRAWER = ( + lambda m, t1="", t2="": ESC + b"p" + m + six.int2byte(t1) + six.int2byte(t2) +) +CD_KICK_DEC_SEQUENCE = ( + lambda esc, p, m, t1=50, t2=50: six.int2byte(esc) + + six.int2byte(p) + + six.int2byte(m) + + six.int2byte(t1) + + six.int2byte(t2) +) +CD_KICK_2 = _CASH_DRAWER(b"\x00", 50, 50) # Sends a pulse to pin 2 [] +CD_KICK_5 = _CASH_DRAWER(b"\x01", 50, 50) # Sends a pulse to pin 5 [] # Paper Cutter -_CUT_PAPER = lambda m: GS + b'V' + m -PAPER_FULL_CUT = _CUT_PAPER(b'\x00') # Full cut paper -PAPER_PART_CUT = _CUT_PAPER(b'\x01') # Partial cut paper +_CUT_PAPER = lambda m: GS + b"V" + m +PAPER_FULL_CUT = _CUT_PAPER(b"\x00") # Full cut paper +PAPER_PART_CUT = _CUT_PAPER(b"\x01") # Partial cut paper # Beep (please note that the actual beep sequence may differ between devices) -BEEP = b'\x07' +BEEP = b"\x07" # Panel buttons (e.g. the FEED button) -_PANEL_BUTTON = lambda n: ESC + b'c5' + six.int2byte(n) +_PANEL_BUTTON = lambda n: ESC + b"c5" + six.int2byte(n) PANEL_BUTTON_ON = _PANEL_BUTTON(0) # enable all panel buttons PANEL_BUTTON_OFF = _PANEL_BUTTON(1) # disable all panel buttons # Line display printing -LINE_DISPLAY_OPEN = ESC + b'\x3d\x02' -LINE_DISPLAY_CLEAR = ESC + b'\x40' -LINE_DISPLAY_CLOSE = ESC + b'\x3d\x01' +LINE_DISPLAY_OPEN = ESC + b"\x3d\x02" +LINE_DISPLAY_CLEAR = ESC + b"\x40" +LINE_DISPLAY_CLOSE = ESC + b"\x3d\x01" # Sheet modes -SHEET_SLIP_MODE = ESC + b'\x63\x30\x04' # slip paper -SHEET_ROLL_MODE = ESC + b'\x63\x30\x01' # paper roll +SHEET_SLIP_MODE = ESC + b"\x63\x30\x04" # slip paper +SHEET_ROLL_MODE = ESC + b"\x63\x30\x01" # paper roll # Text format # TODO: Acquire the "ESC/POS Application Programming Guide for Paper Roll # Printers" and tidy up this stuff too. -TXT_SIZE = GS + b'!' +TXT_SIZE = GS + b"!" -TXT_NORMAL = ESC + b'!\x00' # Normal text +TXT_NORMAL = ESC + b"!\x00" # Normal text TXT_STYLE = { - 'bold': { - False: ESC + b'\x45\x00', # Bold font OFF - True: ESC + b'\x45\x01' # Bold font ON + "bold": { + False: ESC + b"\x45\x00", # Bold font OFF + True: ESC + b"\x45\x01", # Bold font ON }, - 'underline': { - 0: ESC + b'\x2d\x00', # Underline font OFF - 1: ESC + b'\x2d\x01', # Underline font 1-dot ON - 2: ESC + b'\x2d\x02' # Underline font 2-dot ON + "underline": { + 0: ESC + b"\x2d\x00", # Underline font OFF + 1: ESC + b"\x2d\x01", # Underline font 1-dot ON + 2: ESC + b"\x2d\x02", # Underline font 2-dot ON }, - 'size': { - 'normal': TXT_NORMAL + ESC + b'!\x00', # Normal text - '2h': TXT_NORMAL + ESC + b'!\x10', # Double height text - '2w': TXT_NORMAL + ESC + b'!\x20', # Double width text - '2x': TXT_NORMAL + ESC + b'!\x30' # Quad area text + "size": { + "normal": TXT_NORMAL + ESC + b"!\x00", # Normal text + "2h": TXT_NORMAL + ESC + b"!\x10", # Double height text + "2w": TXT_NORMAL + ESC + b"!\x20", # Double width text + "2x": TXT_NORMAL + ESC + b"!\x30", # Quad area text }, - 'font': { - 'a': ESC + b'\x4d\x00', # Font type A - 'b': ESC + b'\x4d\x00' # Font type B + "font": { + "a": ESC + b"\x4d\x00", # Font type A + "b": ESC + b"\x4d\x00", # Font type B }, - 'align': { - 'left': ESC + b'\x61\x00', # Left justification - 'center': ESC + b'\x61\x01', # Centering - 'right': ESC + b'\x61\x02' # Right justification + "align": { + "left": ESC + b"\x61\x00", # Left justification + "center": ESC + b"\x61\x01", # Centering + "right": ESC + b"\x61\x02", # Right justification }, - 'invert': { - True: GS + b'\x42\x01', # Inverse Printing ON - False: GS + b'\x42\x00' # Inverse Printing OFF + "invert": { + True: GS + b"\x42\x01", # Inverse Printing ON + False: GS + b"\x42\x00", # Inverse Printing OFF }, - 'color': { - 'black': ESC + b'\x72\x00', # Default Color - 'red': ESC + b'\x72\x01' # Alternative Color, Usually Red + "color": { + "black": ESC + b"\x72\x00", # Default Color + "red": ESC + b"\x72\x01", # Alternative Color, Usually Red }, - 'flip': { - True: ESC + b'\x7b\x01', # Flip ON - False: ESC + b'\x7b\x00' # Flip OFF + "flip": {True: ESC + b"\x7b\x01", False: ESC + b"\x7b\x00"}, # Flip ON # Flip OFF + "density": { + 0: GS + b"\x7c\x00", # Printing Density -50% + 1: GS + b"\x7c\x01", # Printing Density -37.5% + 2: GS + b"\x7c\x02", # Printing Density -25% + 3: GS + b"\x7c\x03", # Printing Density -12.5% + 4: GS + b"\x7c\x04", # Printing Density 0% + 5: GS + b"\x7c\x08", # Printing Density +50% + 6: GS + b"\x7c\x07", # Printing Density +37.5% + 7: GS + b"\x7c\x06", # Printing Density +25% + 8: GS + b"\x7c\x05", # Printing Density +12.5% }, - 'density': { - 0: GS + b'\x7c\x00', # Printing Density -50% - 1: GS + b'\x7c\x01', # Printing Density -37.5% - 2: GS + b'\x7c\x02', # Printing Density -25% - 3: GS + b'\x7c\x03', # Printing Density -12.5% - 4: GS + b'\x7c\x04', # Printing Density 0% - 5: GS + b'\x7c\x08', # Printing Density +50% - 6: GS + b'\x7c\x07', # Printing Density +37.5% - 7: GS + b'\x7c\x06', # Printing Density +25% - 8: GS + b'\x7c\x05' # Printing Density +12.5% + "smooth": { + True: GS + b"\x62\x01", # Smooth ON + False: GS + b"\x62\x00", # Smooth OFF }, - 'smooth': { - True: GS + b'\x62\x01', # Smooth ON - False: GS + b'\x62\x00' # Smooth OFF - }, - 'height': { # Custom text height + "height": { # Custom text height 1: 0x00, 2: 0x01, 3: 0x02, @@ -137,9 +142,9 @@ TXT_STYLE = { 5: 0x04, 6: 0x05, 7: 0x06, - 8: 0x07 + 8: 0x07, }, - 'width': { # Custom text width + "width": { # Custom text width 1: 0x00, 2: 0x10, 3: 0x20, @@ -147,101 +152,104 @@ TXT_STYLE = { 5: 0x40, 6: 0x50, 7: 0x60, - 8: 0x70 - } + 8: 0x70, + }, } # Fonts -SET_FONT = lambda n: ESC + b'\x4d' + n -TXT_FONT_A = SET_FONT(b'\x00') # Font type A -TXT_FONT_B = SET_FONT(b'\x01') # Font type B +SET_FONT = lambda n: ESC + b"\x4d" + n +TXT_FONT_A = SET_FONT(b"\x00") # Font type A +TXT_FONT_B = SET_FONT(b"\x01") # Font type B # Spacing -LINESPACING_RESET = ESC + b'2' +LINESPACING_RESET = ESC + b"2" LINESPACING_FUNCS = { - 60: ESC + b'A', # line_spacing/60 of an inch, 0 <= line_spacing <= 85 - 360: ESC + b'+', # line_spacing/360 of an inch, 0 <= line_spacing <= 255 - 180: ESC + b'3', # line_spacing/180 of an inch, 0 <= line_spacing <= 255 + 60: ESC + b"A", # line_spacing/60 of an inch, 0 <= line_spacing <= 85 + 360: ESC + b"+", # line_spacing/360 of an inch, 0 <= line_spacing <= 255 + 180: ESC + b"3", # line_spacing/180 of an inch, 0 <= line_spacing <= 255 } # Prefix to change the codepage. You need to attach a byte to indicate # the codepage to use. We use escpos-printer-db as the data source. -CODEPAGE_CHANGE = ESC + b'\x74' +CODEPAGE_CHANGE = ESC + b"\x74" # Barcode format -_SET_BARCODE_TXT_POS = lambda n: GS + b'H' + n -BARCODE_TXT_OFF = _SET_BARCODE_TXT_POS(b'\x00') # HRI barcode chars OFF -BARCODE_TXT_ABV = _SET_BARCODE_TXT_POS(b'\x01') # HRI barcode chars above -BARCODE_TXT_BLW = _SET_BARCODE_TXT_POS(b'\x02') # HRI barcode chars below -BARCODE_TXT_BTH = _SET_BARCODE_TXT_POS(b'\x03') # HRI both above and below +_SET_BARCODE_TXT_POS = lambda n: GS + b"H" + n +BARCODE_TXT_OFF = _SET_BARCODE_TXT_POS(b"\x00") # HRI barcode chars OFF +BARCODE_TXT_ABV = _SET_BARCODE_TXT_POS(b"\x01") # HRI barcode chars above +BARCODE_TXT_BLW = _SET_BARCODE_TXT_POS(b"\x02") # HRI barcode chars below +BARCODE_TXT_BTH = _SET_BARCODE_TXT_POS(b"\x03") # HRI both above and below -_SET_HRI_FONT = lambda n: GS + b'f' + n -BARCODE_FONT_A = _SET_HRI_FONT(b'\x00') # Font type A for HRI barcode chars -BARCODE_FONT_B = _SET_HRI_FONT(b'\x01') # Font type B for HRI barcode chars +_SET_HRI_FONT = lambda n: GS + b"f" + n +BARCODE_FONT_A = _SET_HRI_FONT(b"\x00") # Font type A for HRI barcode chars +BARCODE_FONT_B = _SET_HRI_FONT(b"\x01") # Font type B for HRI barcode chars -BARCODE_HEIGHT = GS + b'h' # Barcode Height [1-255] -BARCODE_WIDTH = GS + b'w' # Barcode Width [2-6] +BARCODE_HEIGHT = GS + b"h" # Barcode Height [1-255] +BARCODE_WIDTH = GS + b"w" # Barcode Width [2-6] # NOTE: This isn't actually an ESC/POS command. It's the common prefix to the # two "print bar code" commands: # - Type A: "GS k NUL" # - TYPE B: "GS k " # The latter command supports more barcode types -_SET_BARCODE_TYPE = lambda m: GS + b'k' + six.int2byte(m) +_SET_BARCODE_TYPE = lambda m: GS + b"k" + six.int2byte(m) # Barcodes for printing function type A BARCODE_TYPE_A = { - 'UPC-A': _SET_BARCODE_TYPE(0), - 'UPC-E': _SET_BARCODE_TYPE(1), - 'EAN13': _SET_BARCODE_TYPE(2), - 'EAN8': _SET_BARCODE_TYPE(3), - 'CODE39': _SET_BARCODE_TYPE(4), - 'ITF': _SET_BARCODE_TYPE(5), - 'NW7': _SET_BARCODE_TYPE(6), - 'CODABAR': _SET_BARCODE_TYPE(6), # Same as NW7 + "UPC-A": _SET_BARCODE_TYPE(0), + "UPC-E": _SET_BARCODE_TYPE(1), + "EAN13": _SET_BARCODE_TYPE(2), + "EAN8": _SET_BARCODE_TYPE(3), + "CODE39": _SET_BARCODE_TYPE(4), + "ITF": _SET_BARCODE_TYPE(5), + "NW7": _SET_BARCODE_TYPE(6), + "CODABAR": _SET_BARCODE_TYPE(6), # Same as NW7 } # Barcodes for printing function type B # The first 8 are the same barcodes as type A BARCODE_TYPE_B = { - 'UPC-A': _SET_BARCODE_TYPE(65), - 'UPC-E': _SET_BARCODE_TYPE(66), - 'EAN13': _SET_BARCODE_TYPE(67), - 'EAN8': _SET_BARCODE_TYPE(68), - 'CODE39': _SET_BARCODE_TYPE(69), - 'ITF': _SET_BARCODE_TYPE(70), - 'NW7': _SET_BARCODE_TYPE(71), - 'CODABAR': _SET_BARCODE_TYPE(71), # Same as NW7 - 'CODE93': _SET_BARCODE_TYPE(72), - 'CODE128': _SET_BARCODE_TYPE(73), - 'GS1-128': _SET_BARCODE_TYPE(74), - 'GS1 DATABAR OMNIDIRECTIONAL': _SET_BARCODE_TYPE(75), - 'GS1 DATABAR TRUNCATED': _SET_BARCODE_TYPE(76), - 'GS1 DATABAR LIMITED': _SET_BARCODE_TYPE(77), - 'GS1 DATABAR EXPANDED': _SET_BARCODE_TYPE(78), + "UPC-A": _SET_BARCODE_TYPE(65), + "UPC-E": _SET_BARCODE_TYPE(66), + "EAN13": _SET_BARCODE_TYPE(67), + "EAN8": _SET_BARCODE_TYPE(68), + "CODE39": _SET_BARCODE_TYPE(69), + "ITF": _SET_BARCODE_TYPE(70), + "NW7": _SET_BARCODE_TYPE(71), + "CODABAR": _SET_BARCODE_TYPE(71), # Same as NW7 + "CODE93": _SET_BARCODE_TYPE(72), + "CODE128": _SET_BARCODE_TYPE(73), + "GS1-128": _SET_BARCODE_TYPE(74), + "GS1 DATABAR OMNIDIRECTIONAL": _SET_BARCODE_TYPE(75), + "GS1 DATABAR TRUNCATED": _SET_BARCODE_TYPE(76), + "GS1 DATABAR LIMITED": _SET_BARCODE_TYPE(77), + "GS1 DATABAR EXPANDED": _SET_BARCODE_TYPE(78), } BARCODE_FORMATS = { - 'UPC-A': ([(11, 12)], "^[0-9]{11,12}$"), - 'UPC-E': ([(7, 8), (11, 12)], "^([0-9]{7,8}|[0-9]{11,12})$"), - 'EAN13': ([(12, 13)], "^[0-9]{12,13}$"), - 'EAN8': ([(7, 8)], "^[0-9]{7,8}$"), - 'CODE39': ([(1, 255)], "^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$"), - 'ITF': ([(2, 255)], "^([0-9]{2})+$"), - 'NW7': ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"), - 'CODABAR': ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"), # Same as NW7 - 'CODE93': ([(1, 255)], "^[\\x00-\\x7F]+$"), - 'CODE128': ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"), - 'GS1-128': ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"), # same as CODE128 - 'GS1 DATABAR OMNIDIRECTIONAL': ([(13,13)], "^[0-9]{13}$"), - 'GS1 DATABAR TRUNCATED': ([(13,13)], "^[0-9]{13}$"), # same as GS1 omnidirectional - 'GS1 DATABAR LIMITED': ([(13,13)], "^[01][0-9]{12}$"), - 'GS1 DATABAR EXPANDED': ([(2,255)], "^\([0-9][A-Za-z0-9 \!\"\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\_\{]+$"), + "UPC-A": ([(11, 12)], "^[0-9]{11,12}$"), + "UPC-E": ([(7, 8), (11, 12)], "^([0-9]{7,8}|[0-9]{11,12})$"), + "EAN13": ([(12, 13)], "^[0-9]{12,13}$"), + "EAN8": ([(7, 8)], "^[0-9]{7,8}$"), + "CODE39": ([(1, 255)], "^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$"), + "ITF": ([(2, 255)], "^([0-9]{2})+$"), + "NW7": ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"), + "CODABAR": ([(1, 255)], "^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$"), # Same as NW7 + "CODE93": ([(1, 255)], "^[\\x00-\\x7F]+$"), + "CODE128": ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"), + "GS1-128": ([(2, 255)], "^\{[A-C][\\x00-\\x7F]+$"), # same as CODE128 + "GS1 DATABAR OMNIDIRECTIONAL": ([(13, 13)], "^[0-9]{13}$"), + "GS1 DATABAR TRUNCATED": ([(13, 13)], "^[0-9]{13}$"), # same as GS1 omnidirectional + "GS1 DATABAR LIMITED": ([(13, 13)], "^[01][0-9]{12}$"), + "GS1 DATABAR EXPANDED": ( + [(2, 255)], + "^\([0-9][A-Za-z0-9 \!\"\%\&'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\_\{]+$", + ), } BARCODE_TYPES = { - 'A': BARCODE_TYPE_A, - 'B': BARCODE_TYPE_B, + "A": BARCODE_TYPE_A, + "B": BARCODE_TYPE_B, } # QRCode error correction levels @@ -258,17 +266,17 @@ QR_MICRO = 3 # Image format # NOTE: _PRINT_RASTER_IMG is the obsolete ESC/POS "print raster bit image" # command. The constants include a fragment of the data's header. -_PRINT_RASTER_IMG = lambda data: GS + b'v0' + data -S_RASTER_N = _PRINT_RASTER_IMG(b'\x00') # Set raster image normal size -S_RASTER_2W = _PRINT_RASTER_IMG(b'\x01') # Set raster image double width -S_RASTER_2H = _PRINT_RASTER_IMG(b'\x02') # Set raster image double height -S_RASTER_Q = _PRINT_RASTER_IMG(b'\x03') # Set raster image quadruple +_PRINT_RASTER_IMG = lambda data: GS + b"v0" + data +S_RASTER_N = _PRINT_RASTER_IMG(b"\x00") # Set raster image normal size +S_RASTER_2W = _PRINT_RASTER_IMG(b"\x01") # Set raster image double width +S_RASTER_2H = _PRINT_RASTER_IMG(b"\x02") # Set raster image double height +S_RASTER_Q = _PRINT_RASTER_IMG(b"\x03") # Set raster image quadruple # Status Command RT_STATUS = DLE + EOT -RT_STATUS_ONLINE = RT_STATUS + b'\x01' -RT_STATUS_PAPER = RT_STATUS + b'\x04' +RT_STATUS_ONLINE = RT_STATUS + b"\x01" +RT_STATUS_PAPER = RT_STATUS + b"\x04" RT_MASK_ONLINE = 8 RT_MASK_PAPER = 18 RT_MASK_LOWPAPER = 30 -RT_MASK_NOPAPER = 114 \ No newline at end of file +RT_MASK_NOPAPER = 114 diff --git a/src/escpos/escpos.py b/src/escpos/escpos.py index 012bb17..c714d31 100644 --- a/src/escpos/escpos.py +++ b/src/escpos/escpos.py @@ -22,17 +22,51 @@ from barcode.writer import ImageWriter import os -from .constants import ESC, GS, NUL, QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q -from .constants import QR_MODEL_1, QR_MODEL_2, QR_MICRO, BARCODE_TYPES, BARCODE_HEIGHT, BARCODE_WIDTH +from .constants import ( + ESC, + GS, + NUL, + QR_ECLEVEL_L, + QR_ECLEVEL_M, + QR_ECLEVEL_H, + QR_ECLEVEL_Q, +) +from .constants import ( + QR_MODEL_1, + QR_MODEL_2, + QR_MICRO, + BARCODE_TYPES, + BARCODE_HEIGHT, + BARCODE_WIDTH, +) from .constants import BARCODE_FONT_A, BARCODE_FONT_B, BARCODE_FORMATS -from .constants import BARCODE_TXT_OFF, BARCODE_TXT_BTH, BARCODE_TXT_ABV, BARCODE_TXT_BLW +from .constants import ( + BARCODE_TXT_OFF, + BARCODE_TXT_BTH, + BARCODE_TXT_ABV, + BARCODE_TXT_BLW, +) from .constants import TXT_SIZE, TXT_NORMAL from .constants import SET_FONT from .constants import LINESPACING_FUNCS, LINESPACING_RESET from .constants import LINE_DISPLAY_OPEN, LINE_DISPLAY_CLEAR, LINE_DISPLAY_CLOSE -from .constants import CD_KICK_DEC_SEQUENCE, CD_KICK_5, CD_KICK_2, PAPER_FULL_CUT, PAPER_PART_CUT +from .constants import ( + CD_KICK_DEC_SEQUENCE, + CD_KICK_5, + CD_KICK_2, + PAPER_FULL_CUT, + PAPER_PART_CUT, +) from .constants import HW_RESET, HW_SELECT, HW_INIT -from .constants import CTL_VT, CTL_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON +from .constants import ( + CTL_VT, + CTL_CR, + CTL_FF, + CTL_LF, + CTL_SET_HT, + PANEL_BUTTON_OFF, + PANEL_BUTTON_ON, +) from .constants import TXT_STYLE from .constants import RT_STATUS_ONLINE, RT_MASK_ONLINE from .constants import RT_STATUS_PAPER, RT_MASK_PAPER, RT_MASK_LOWPAPER, RT_MASK_NOPAPER @@ -50,27 +84,28 @@ from escpos.capabilities import get_profile, BARCODE_B @six.add_metaclass(ABCMeta) class Escpos(object): - """ ESC/POS Printer object + """ESC/POS Printer object This class is the abstract base class for an esc/pos-printer. The printer implementations are children of this class. """ + device = None def __init__(self, profile=None, magic_encode_args=None, **kwargs): - """ Initialize ESCPOS Printer + """Initialize ESCPOS Printer :param profile: Printer profile""" self.profile = get_profile(profile) self.magic = MagicEncode(self, **(magic_encode_args or {})) def __del__(self): - """ call self.close upon deletion """ + """call self.close upon deletion""" self.close() @abstractmethod def _raw(self, msg): - """ Sends raw data to the printer + """Sends raw data to the printer This function has to be individually implemented by the implementations. @@ -80,14 +115,21 @@ class Escpos(object): pass def _read(self): - """ Returns a NotImplementedError if the instance of the class doesn't override this method. + """Returns a NotImplementedError if the instance of the class doesn't override this method. :raises NotImplementedError """ raise NotImplementedError() - def image(self, img_source, high_density_vertical=True, high_density_horizontal=True, impl="bitImageRaster", - fragment_height=960, center=False): - """ Print an image + def image( + self, + img_source, + high_density_vertical=True, + high_density_horizontal=True, + impl="bitImageRaster", + fragment_height=960, + center=False, + ): + """Print an image You can select whether the printer should print in high density or not. The default value is high density. When printing in low density, the image will be stretched. @@ -116,14 +158,16 @@ class Escpos(object): im = EscposImage(img_source) try: - if self.profile.profile_data['media']['width']['pixels'] == "Unknown": - print("The media.width.pixel field of the printer profile is not set. " + - "The center flag will have no effect.") + if self.profile.profile_data["media"]["width"]["pixels"] == "Unknown": + print( + "The media.width.pixel field of the printer profile is not set. " + + "The center flag will have no effect." + ) - max_width = int(self.profile.profile_data['media']['width']['pixels']) + max_width = int(self.profile.profile_data["media"]["width"]["pixels"]) if im.width > max_width: - raise ImageWidthError('{} > {}'.format(im.width, max_width)) + raise ImageWidthError("{} > {}".format(im.width, max_width)) if center: im.center(max_width) @@ -137,41 +181,59 @@ class Escpos(object): if im.height > fragment_height: fragments = im.split(fragment_height) for fragment in fragments: - self.image(fragment, - high_density_vertical=high_density_vertical, - high_density_horizontal=high_density_horizontal, - impl=impl, - fragment_height=fragment_height) + self.image( + fragment, + high_density_vertical=high_density_vertical, + high_density_horizontal=high_density_horizontal, + impl=impl, + fragment_height=fragment_height, + ) return if impl == "bitImageRaster": # GS v 0, raster format bit image - density_byte = (0 if high_density_horizontal else 1) + (0 if high_density_vertical else 2) - header = GS + b"v0" + six.int2byte(density_byte) + self._int_low_high(im.width_bytes, 2) +\ - self._int_low_high(im.height, 2) + density_byte = (0 if high_density_horizontal else 1) + ( + 0 if high_density_vertical else 2 + ) + header = ( + GS + + b"v0" + + six.int2byte(density_byte) + + self._int_low_high(im.width_bytes, 2) + + self._int_low_high(im.height, 2) + ) self._raw(header + im.to_raster_format()) if impl == "graphics": # GS ( L raster format graphics - img_header = self._int_low_high(im.width, 2) + self._int_low_high(im.height, 2) - tone = b'0' - colors = b'1' + img_header = self._int_low_high(im.width, 2) + self._int_low_high( + im.height, 2 + ) + 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) 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) - self._image_send_graphics_data(b'0', b'2', b'') + self._image_send_graphics_data(b"0", b"p", header + raster_data) + self._image_send_graphics_data(b"0", b"2", b"") if impl == "bitImageColumn": # ESC *, column format bit image - density_byte = (1 if high_density_horizontal else 0) + (32 if high_density_vertical else 0) - header = ESC + b"*" + six.int2byte(density_byte) + self._int_low_high(im.width, 2) + density_byte = (1 if high_density_horizontal else 0) + ( + 32 if high_density_vertical else 0 + ) + header = ( + ESC + + b"*" + + six.int2byte(density_byte) + + self._int_low_high(im.width, 2) + ) outp = [ESC + b"3" + six.int2byte(16)] # Adjust line-feed size for blob in im.to_column_format(high_density_vertical): outp.append(header + blob + b"\n") outp.append(ESC + b"2") # Reset line-feed size - self._raw(b''.join(outp)) + self._raw(b"".join(outp)) def _image_send_graphics_data(self, m, fn, data): """ @@ -182,11 +244,19 @@ class Escpos(object): :param data: Data to send """ header = self._int_low_high(len(data) + 2, 2) - self._raw(GS + b'(L' + header + m + fn + data) + self._raw(GS + b"(L" + header + m + fn + data) - def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2, - native=False, center=False, impl="bitImageRaster"): - """ Print QR Code for the provided string + def qr( + self, + content, + ec=QR_ECLEVEL_L, + size=3, + model=QR_MODEL_2, + native=False, + center=False, + impl="bitImageRaster", + ): + """Print QR Code for the provided string :param content: The content of the code. Numeric data will be more efficiently compacted. :param ec: Error-correction level to use. One of QR_ECLEVEL_L (default), QR_ECLEVEL_M, QR_ECLEVEL_Q or @@ -206,50 +276,60 @@ class Escpos(object): if not 1 <= size <= 16: raise ValueError("Invalid block size (must be 1-16)") if model not in [QR_MODEL_1, QR_MODEL_2, QR_MICRO]: - raise ValueError("Invalid QR model (must be one of QR_MODEL_1, QR_MODEL_2, QR_MICRO)") + raise ValueError( + "Invalid QR model (must be one of QR_MODEL_1, QR_MODEL_2, QR_MICRO)" + ) if content == "": # Handle edge case by printing nothing. return if not native: # Map ESC/POS error correction levels to python 'qrcode' library constant and render to an image if model != QR_MODEL_2: - raise ValueError("Invalid QR model for qrlib rendering (must be QR_MODEL_2)") + raise ValueError( + "Invalid QR model for qrlib rendering (must be QR_MODEL_2)" + ) python_qr_ec = { QR_ECLEVEL_H: qrcode.constants.ERROR_CORRECT_H, QR_ECLEVEL_L: qrcode.constants.ERROR_CORRECT_L, QR_ECLEVEL_M: qrcode.constants.ERROR_CORRECT_M, - QR_ECLEVEL_Q: qrcode.constants.ERROR_CORRECT_Q + QR_ECLEVEL_Q: qrcode.constants.ERROR_CORRECT_Q, } - qr_code = qrcode.QRCode(version=None, box_size=size, border=1, error_correction=python_qr_ec[ec]) + qr_code = qrcode.QRCode( + version=None, box_size=size, border=1, error_correction=python_qr_ec[ec] + ) qr_code.add_data(content) qr_code.make(fit=True) qr_img = qr_code.make_image() im = qr_img._img.convert("RGB") # Convert the RGB image in printable image - self.text('\n') + self.text("\n") self.image(im, center=center, impl=impl) - self.text('\n') - self.text('\n') + self.text("\n") + self.text("\n") return if center: - raise NotImplementedError("Centering not implemented for native QR rendering") + raise NotImplementedError( + "Centering not implemented for native QR rendering" + ) # Native 2D code printing - cn = b'1' # Code type for QR code + cn = b"1" # Code type for QR code # Select model: 1, 2 or micro. - self._send_2d_code_data(six.int2byte(65), cn, six.int2byte(48 + model) + six.int2byte(0)) + self._send_2d_code_data( + six.int2byte(65), cn, six.int2byte(48 + model) + six.int2byte(0) + ) # Set dot size. self._send_2d_code_data(six.int2byte(67), cn, six.int2byte(size)) # Set error correction level: L, M, Q, or H self._send_2d_code_data(six.int2byte(69), cn, six.int2byte(48 + ec)) # Send content & print - self._send_2d_code_data(six.int2byte(80), cn, content.encode('utf-8'), b'0') - self._send_2d_code_data(six.int2byte(81), cn, b'', b'0') + self._send_2d_code_data(six.int2byte(80), cn, content.encode("utf-8"), b"0") + self._send_2d_code_data(six.int2byte(81), cn, b"", b"0") - def _send_2d_code_data(self, fn, cn, data, m=b''): - """ Wrapper for GS ( k, to calculate and send correct data length. + def _send_2d_code_data(self, fn, cn, data, m=b""): + """Wrapper for GS ( k, to calculate and send correct data length. :param fn: Function to use. :param cn: Output code type. Affects available data. @@ -259,28 +339,32 @@ class Escpos(object): if len(m) > 1 or len(cn) != 1 or len(fn) != 1: raise ValueError("cn and fn must be one byte each.") header = self._int_low_high(len(data) + len(m) + 2, 2) - self._raw(GS + b'(k' + header + cn + fn + m + data) + self._raw(GS + b"(k" + header + cn + fn + m + data) @staticmethod def _int_low_high(inp_number, out_bytes): - """ Generate multiple bytes for a number: In lower and higher parts, or more parts as needed. + """Generate multiple bytes for a number: In lower and higher parts, or more parts as needed. :param inp_number: Input number :param out_bytes: The number of bytes to output (1 - 4). """ - max_input = (256 << (out_bytes * 8) - 1) + max_input = 256 << (out_bytes * 8) - 1 if not 1 <= out_bytes <= 4: raise ValueError("Can only output 1-4 bytes") if not 0 <= inp_number <= max_input: - raise ValueError("Number too large. Can only output up to {0} in {1} bytes".format(max_input, out_bytes)) - outp = b'' + raise ValueError( + "Number too large. Can only output up to {0} in {1} bytes".format( + max_input, out_bytes + ) + ) + outp = b"" for _ in range(0, out_bytes): outp += six.int2byte(inp_number % 256) inp_number //= 256 return outp def charcode(self, code="AUTO"): - """ Set Character Code Table + """Set Character Code Table Sets the control sequence from ``CHARCODE`` in :py:mod:`escpos.constants` as active. It will be sent with the next text sequence. If you set the variable code to ``AUTO`` it will try to automatically guess the @@ -318,11 +402,23 @@ class Escpos(object): return False bounds, regex = BARCODE_FORMATS[bc] - return any(bound[0] <= len(code) <= bound[1] for bound in bounds) and re_match(regex, code) + return any(bound[0] <= len(code) <= bound[1] for bound in bounds) and re_match( + regex, code + ) - def barcode(self, code, bc, height=64, width=3, pos="BELOW", font="A", - align_ct=True, function_type=None, check=True): - """ Print Barcode + def barcode( + self, + code, + bc, + height=64, + width=3, + pos="BELOW", + font="A", + align_ct=True, + function_type=None, + check=True, + ): + """Print Barcode This method allows to print barcodes. The rendering of the barcode is done by the printer and therefore has to be supported by the unit. By default, this method will check whether your barcode text is correct, that is @@ -407,38 +503,46 @@ class Escpos(object): """ if function_type is None: # Choose the function type automatically. - if bc in BARCODE_TYPES['A']: - function_type = 'A' + if bc in BARCODE_TYPES["A"]: + function_type = "A" else: - if bc in BARCODE_TYPES['B']: + if bc in BARCODE_TYPES["B"]: if not self.profile.supports(BARCODE_B): - raise BarcodeTypeError(( - "Barcode type '{bc} not supported for " - "the current printer profile").format(bc=bc)) - function_type = 'B' + raise BarcodeTypeError( + ( + "Barcode type '{bc} not supported for " + "the current printer profile" + ).format(bc=bc) + ) + function_type = "B" else: - raise BarcodeTypeError(( - "Barcode type '{bc} is not valid").format(bc=bc)) + raise BarcodeTypeError( + ("Barcode type '{bc} is not valid").format(bc=bc) + ) bc_types = BARCODE_TYPES[function_type.upper()] if bc.upper() not in bc_types.keys(): - raise BarcodeTypeError(( - "Barcode '{bc}' not valid for barcode function type " - "{function_type}").format( + raise BarcodeTypeError( + ( + "Barcode '{bc}' not valid for barcode function type " + "{function_type}" + ).format( bc=bc, function_type=function_type, - )) + ) + ) if check and not self.check_barcode(bc, code): - raise BarcodeCodeError(( - "Barcode '{code}' not in a valid format for type '{bc}'").format( - code=code, - bc=bc, - )) + raise BarcodeCodeError( + ("Barcode '{code}' not in a valid format for type '{bc}'").format( + code=code, + bc=bc, + ) + ) # Align Bar Code() if align_ct: - self._raw(TXT_STYLE['align']['center']) + self._raw(TXT_STYLE["align"]["center"]) # Height if 1 <= height <= 255: self._raw(BARCODE_HEIGHT + six.int2byte(height)) @@ -478,35 +582,47 @@ class Escpos(object): if function_type.upper() == "A": self._raw(NUL) - def soft_barcode(self, barcode_type, data, impl='bitImageColumn', - module_height=5, module_width=0.2, text_distance=1, - center=True): + def soft_barcode( + self, + barcode_type, + data, + impl="bitImageColumn", + module_height=5, + module_width=0.2, + text_distance=1, + center=True, + ): image_writer = ImageWriter() # Check if barcode type exists if barcode_type not in barcode.PROVIDED_BARCODES: raise BarcodeTypeError( - 'Barcode type {} not supported by software barcode renderer' - .format(barcode_type)) + "Barcode type {} not supported by software barcode renderer".format( + barcode_type + ) + ) # Render the barcode to a fake file barcode_class = barcode.get_barcode_class(barcode_type) my_code = barcode_class(data, writer=image_writer) with open(os.devnull, "wb") as nullfile: - my_code.write(nullfile, { - 'module_height': module_height, - 'module_width': module_width, - 'text_distance': text_distance - }) + my_code.write( + nullfile, + { + "module_height": module_height, + "module_width": module_width, + "text_distance": text_distance, + }, + ) # Retrieve the Pillow image and print it image = my_code.writer._image self.image(image, impl=impl, center=center) def text(self, txt): - """ Print alpha-numeric text + """Print alpha-numeric text The text has to be encoded in the currently selected codepage. The input text has to be encoded in unicode. @@ -517,7 +633,7 @@ class Escpos(object): txt = six.text_type(txt) self.magic.write(txt) - def textln(self, txt=''): + def textln(self, txt=""): """Print alpha-numeric text with a newline The text has to be encoded in the currently selected codepage. @@ -526,7 +642,7 @@ class Escpos(object): :param txt: text to be printed with a newline :raises: :py:exc:`~escpos.exceptions.TextError` """ - self.text('{}\n'.format(txt)) + self.text("{}\n".format(txt)) def ln(self, count=1): """Print a newline or more @@ -535,12 +651,12 @@ class Escpos(object): :raises: :py:exc:`ValueError` if count < 0 """ if count < 0: - raise ValueError('Count cannot be lesser than 0') + raise ValueError("Count cannot be lesser than 0") if count > 0: - self.text('\n' * count) + self.text("\n" * count) def block_text(self, txt, font="0", columns=None): - """ Text is printed wrapped to specified columns + """Text is printed wrapped to specified columns Text has to be encoded in unicode. @@ -552,10 +668,23 @@ class Escpos(object): col_count = self.profile.get_columns(font) if columns is None else columns self.text(textwrap.fill(txt, col_count)) - def set(self, align='left', font='a', bold=False, underline=0, width=1, - height=1, density=9, invert=False, smooth=False, flip=False, - double_width=False, double_height=False, custom_size=False): - """ Set text properties by sending them to the printer + def set( + self, + align="left", + font="a", + bold=False, + underline=0, + width=1, + height=1, + density=9, + invert=False, + smooth=False, + flip=False, + double_width=False, + double_height=False, + custom_size=False, + ): + """Set text properties by sending them to the printer :param align: horizontal position for text, possible values are: @@ -596,37 +725,41 @@ class Escpos(object): """ if custom_size: - if 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and\ - isinstance(height, int): - size_byte = TXT_STYLE['width'][width] + TXT_STYLE['height'][height] + if ( + 1 <= width <= 8 + and 1 <= height <= 8 + and isinstance(width, int) + and isinstance(height, int) + ): + size_byte = TXT_STYLE["width"][width] + TXT_STYLE["height"][height] self._raw(TXT_SIZE + six.int2byte(size_byte)) else: raise SetVariableError() else: self._raw(TXT_NORMAL) if double_width and double_height: - self._raw(TXT_STYLE['size']['2x']) + self._raw(TXT_STYLE["size"]["2x"]) elif double_width: - self._raw(TXT_STYLE['size']['2w']) + self._raw(TXT_STYLE["size"]["2w"]) elif double_height: - self._raw(TXT_STYLE['size']['2h']) + self._raw(TXT_STYLE["size"]["2h"]) else: - self._raw(TXT_STYLE['size']['normal']) + self._raw(TXT_STYLE["size"]["normal"]) - self._raw(TXT_STYLE['flip'][flip]) - self._raw(TXT_STYLE['smooth'][smooth]) - self._raw(TXT_STYLE['bold'][bold]) - self._raw(TXT_STYLE['underline'][underline]) + self._raw(TXT_STYLE["flip"][flip]) + self._raw(TXT_STYLE["smooth"][smooth]) + self._raw(TXT_STYLE["bold"][bold]) + self._raw(TXT_STYLE["underline"][underline]) self._raw(SET_FONT(six.int2byte(self.profile.get_font(font)))) - self._raw(TXT_STYLE['align'][align]) + self._raw(TXT_STYLE["align"][align]) if density != 9: - self._raw(TXT_STYLE['density'][density]) + self._raw(TXT_STYLE["density"][density]) - self._raw(TXT_STYLE['invert'][invert]) + self._raw(TXT_STYLE["invert"][invert]) def line_spacing(self, spacing=None, divisor=180): - """ Set line character spacing. + """Set line character spacing. If no spacing is given, we reset it to the default. @@ -646,16 +779,19 @@ class Escpos(object): if divisor not in LINESPACING_FUNCS: raise ValueError("divisor must be either 360, 180 or 60") - if (divisor in [360, 180] - and (not(0 <= spacing <= 255))): - raise ValueError("spacing must be a int between 0 and 255 when divisor is 360 or 180") - if divisor == 60 and (not(0 <= spacing <= 85)): - raise ValueError("spacing must be a int between 0 and 85 when divisor is 60") + if divisor in [360, 180] and (not (0 <= spacing <= 255)): + raise ValueError( + "spacing must be a int between 0 and 255 when divisor is 360 or 180" + ) + if divisor == 60 and (not (0 <= spacing <= 85)): + raise ValueError( + "spacing must be a int between 0 and 85 when divisor is 60" + ) self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing)) - def cut(self, mode='FULL', feed=True): - """ Cut paper. + def cut(self, mode="FULL", feed=True): + """Cut paper. Without any arguments the paper will be cut completely. With 'mode=PART' a partial cut will be attempted. Note however, that not all models can do a partial cut. See the documentation of @@ -667,28 +803,28 @@ class Escpos(object): """ if not feed: - self._raw(GS + b'V' + six.int2byte(66) + b'\x00') + self._raw(GS + b"V" + six.int2byte(66) + b"\x00") return self.print_and_feed(6) mode = mode.upper() - if mode not in ('FULL', 'PART'): + if mode not in ("FULL", "PART"): raise ValueError("Mode must be one of ('FULL', 'PART')") if mode == "PART": - if self.profile.supports('paperPartCut'): + if self.profile.supports("paperPartCut"): self._raw(PAPER_PART_CUT) - elif self.profile.supports('paperFullCut'): + elif self.profile.supports("paperFullCut"): self._raw(PAPER_FULL_CUT) elif mode == "FULL": - if self.profile.supports('paperFullCut'): + if self.profile.supports("paperFullCut"): self._raw(PAPER_FULL_CUT) - elif self.profile.supports('paperPartCut'): + elif self.profile.supports("paperPartCut"): self._raw(PAPER_PART_CUT) def cashdraw(self, pin): - """ Send pulse to kick the cash drawer + """Send pulse to kick the cash drawer Kick cash drawer on pin 2 or pin 5 according to default parameter. For non default parameter send a decimal sequence i.e. [27,112,48] or [27,112,0,25,255] @@ -707,7 +843,7 @@ class Escpos(object): raise CashDrawerError(err) def linedisplay_select(self, select_display=False): - """ Selects the line display or the printer + """Selects the line display or the printer This method is used for line displays that are daisy-chained between your computer and printer. If you set `select_display` to true, only the display is selected and if you set it to false, @@ -722,7 +858,7 @@ class Escpos(object): self._raw(LINE_DISPLAY_CLOSE) def linedisplay_clear(self): - """ Clears the line display and resets the cursor + """Clears the line display and resets the cursor This method is used for line displays that are daisy-chained between your computer and printer. """ @@ -743,7 +879,7 @@ class Escpos(object): self.linedisplay_select(select_display=False) def hw(self, hw): - """ Hardware operations + """Hardware operations :param hw: hardware action, may be: @@ -761,12 +897,12 @@ class Escpos(object): pass def print_and_feed(self, n=1): - """ Print data in print buffer and feed *n* lines + """Print data in print buffer and feed *n* lines - if n not in range (0, 255) then ValueError will be raised + if n not in range (0, 255) then ValueError will be raised - :param n: number of n to feed. 0 <= n <= 255. default: 1 - :raises ValueError: if not 0 <= n <= 255 + :param n: number of n to feed. 0 <= n <= 255. default: 1 + :raises ValueError: if not 0 <= n <= 255 """ if 0 <= n <= 255: # ESC d n @@ -775,7 +911,7 @@ class Escpos(object): raise ValueError("n must be betwen 0 and 255") def control(self, ctl, count=5, tab_size=8): - """ Feed control sequences + """Feed control sequences :param ctl: string for the following control sequences: @@ -797,9 +933,9 @@ class Escpos(object): elif ctl.upper() == "CR": self._raw(CTL_CR) elif ctl.upper() == "HT": - if not (0 <= count <= 32 and - 1 <= tab_size <= 255 and - count * tab_size < 256): + if not ( + 0 <= count <= 32 and 1 <= tab_size <= 255 and count * tab_size < 256 + ): raise TabPosError() else: # Set tab positions @@ -811,7 +947,7 @@ class Escpos(object): self._raw(CTL_VT) def panel_buttons(self, enable=True): - """ Controls the panel buttons on the printer (e.g. FEED) + """Controls the panel buttons on the printer (e.g. FEED) When enable is set to False the panel buttons on the printer will be disabled. Calling the method with enable=True or without argument will enable the panel buttons. @@ -872,11 +1008,11 @@ class Escpos(object): status = self.query_status(RT_STATUS_PAPER) if len(status) == 0: return 2 - if (status[0] & RT_MASK_NOPAPER == RT_MASK_NOPAPER): + if status[0] & RT_MASK_NOPAPER == RT_MASK_NOPAPER: return 0 - if (status[0] & RT_MASK_LOWPAPER == RT_MASK_LOWPAPER): + if status[0] & RT_MASK_LOWPAPER == RT_MASK_LOWPAPER: return 1 - if (status[0] & RT_MASK_PAPER == RT_MASK_PAPER): + if status[0] & RT_MASK_PAPER == RT_MASK_PAPER: return 2 @@ -913,7 +1049,7 @@ class EscposIO(object): self.autoclose = autoclose def set(self, **kwargs): - """ Set the printer-parameters + """Set the printer-parameters Controls which parameters will be passed to :py:meth:`Escpos.set() `. For more information on the parameters see the :py:meth:`set() `-methods @@ -929,11 +1065,13 @@ class EscposIO(object): params.update(kwargs) if isinstance(text, six.text_type): - lines = text.split('\n') + lines = text.split("\n") elif isinstance(text, list) or isinstance(text, tuple): lines = text else: - lines = ["{0}".format(text), ] + lines = [ + "{0}".format(text), + ] # TODO check unicode handling # TODO flush? or on print? (this should prob rather be handled by the _raw-method) @@ -945,8 +1083,7 @@ class EscposIO(object): self.printer.text("{0}\n".format(line)) def close(self): - """ called upon closing the `with`-statement - """ + """called upon closing the `with`-statement""" self.printer.close() def __enter__(self, **kwargs): diff --git a/src/escpos/exceptions.py b/src/escpos/exceptions.py index e67ffe2..cdb7eea 100644 --- a/src/escpos/exceptions.py +++ b/src/escpos/exceptions.py @@ -27,7 +27,8 @@ Result/Exit codes: class Error(Exception): - """ Base class for ESC/POS errors """ + """Base class for ESC/POS errors""" + def __init__(self, msg, status=None): Exception.__init__(self) self.msg = msg @@ -40,12 +41,13 @@ class Error(Exception): class BarcodeTypeError(Error): - """ No Barcode type defined. + """No Barcode type defined. This exception indicates that no known barcode-type has been entered. The barcode-type has to be one of those specified in :py:meth:`escpos.escpos.Escpos.barcode`. The returned error code is `10`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -56,12 +58,13 @@ class BarcodeTypeError(Error): class BarcodeSizeError(Error): - """ Barcode size is out of range. + """Barcode size is out of range. This exception indicates that the values for the barcode size are out of range. The size of the barcode has to be in the range that is specified in :py:meth:`escpos.escpos.Escpos.barcode`. The resulting returncode is `20`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -72,12 +75,13 @@ class BarcodeSizeError(Error): class BarcodeCodeError(Error): - """ No Barcode code was supplied, or it is incorrect. + """No Barcode code was supplied, or it is incorrect. No data for the barcode has been supplied in :py:meth:`escpos.escpos.Escpos.barcode` or the the `check` parameter was True and the check failed. The returncode for this exception is `30`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -88,24 +92,28 @@ class BarcodeCodeError(Error): class ImageSizeError(Error): - """ Image height is longer than 255px and can't be printed. + """Image height is longer than 255px and can't be printed. The returncode for this exception is `40`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg self.resultcode = 40 def __str__(self): - return "Image height is longer than 255px and can't be printed ({msg})".format(msg=self.msg) + return "Image height is longer than 255px and can't be printed ({msg})".format( + msg=self.msg + ) class ImageWidthError(Error): - """ Image width is too large. + """Image width is too large. The return code for this exception is `41`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -116,26 +124,30 @@ class ImageWidthError(Error): class TextError(Error): - """ Text string must be supplied to the `text()` method. + """Text string must be supplied to the `text()` method. This exception is raised when an empty string is passed to :py:meth:`escpos.escpos.Escpos.text`. The returncode for this exception is `50`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg self.resultcode = 50 def __str__(self): - return "Text string must be supplied to the text() method ({msg})".format(msg=self.msg) + return "Text string must be supplied to the text() method ({msg})".format( + msg=self.msg + ) class CashDrawerError(Error): - """ Valid pin must be set in order to send pulse. + """Valid pin must be set in order to send pulse. A valid pin number has to be passed onto the method :py:meth:`escpos.escpos.Escpos.cashdraw`. The returncode for this exception is `60`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -146,27 +158,31 @@ class CashDrawerError(Error): class TabPosError(Error): - """ Valid tab positions must be set by using from 1 to 32 tabs, and between 1 and 255 tab size values. + """Valid tab positions must be set by using from 1 to 32 tabs, and between 1 and 255 tab size values. Both values multiplied must not exceed 255, since it is the maximum tab value. This exception is raised by :py:meth:`escpos.escpos.Escpos.control`. The returncode for this exception is `70`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg self.resultcode = 70 def __str__(self): - return "Valid tab positions must be in the range 0 to 16 ({msg})".format(msg=self.msg) + return "Valid tab positions must be in the range 0 to 16 ({msg})".format( + msg=self.msg + ) class CharCodeError(Error): - """ Valid char code must be set. + """Valid char code must be set. The supplied charcode-name in :py:meth:`escpos.escpos.Escpos.charcode` is unknown. Ths returncode for this exception is `80`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -177,11 +193,12 @@ class CharCodeError(Error): class USBNotFoundError(Error): - """ Device wasn't found (probably not plugged in) + """Device wasn't found (probably not plugged in) The USB device seems to be not plugged in. Ths returncode for this exception is `90`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -192,11 +209,12 @@ class USBNotFoundError(Error): class SetVariableError(Error): - """ A set method variable was out of range + """A set method variable was out of range Check set variables against minimum and maximum values Ths returncode for this exception is `100`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -208,12 +226,14 @@ class SetVariableError(Error): # Configuration errors + class ConfigNotFoundError(Error): - """ The configuration file was not found + """The configuration file was not found The default or passed configuration file could not be read Ths returncode for this exception is `200`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -224,11 +244,12 @@ class ConfigNotFoundError(Error): class ConfigSyntaxError(Error): - """ The configuration file is invalid + """The configuration file is invalid The syntax is incorrect Ths returncode for this exception is `210`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg @@ -239,11 +260,12 @@ class ConfigSyntaxError(Error): class ConfigSectionMissingError(Error): - """ The configuration file is missing a section + """The configuration file is missing a section The part of the config asked for doesn't exist in the loaded configuration Ths returncode for this exception is `220`. """ + def __init__(self, msg=""): Error.__init__(self, msg) self.msg = msg diff --git a/src/escpos/image.py b/src/escpos/image.py index e94a44a..f622ff8 100644 --- a/src/escpos/image.py +++ b/src/escpos/image.py @@ -37,7 +37,7 @@ class EscposImage(object): # Convert to white RGB background, paste over white background # to strip alpha. - img_original = img_original.convert('RGBA') + img_original = img_original.convert("RGBA") im = Image.new("RGB", img_original.size, (255, 255, 255)) im.paste(img_original, mask=img_original.split()[3]) # Convert down to greyscale @@ -85,7 +85,7 @@ class EscposImage(object): box = (left, top, left + line_height, top + height_pixels) im_slice = im.transform((line_height, height_pixels), Image.EXTENT, box) im_bytes = im_slice.tobytes() - yield(im_bytes) + yield (im_bytes) left += line_height def to_raster_format(self): @@ -101,7 +101,7 @@ class EscposImage(object): :param fragment_height: height of fragment :return: list of PIL objects """ - passes = int(math.ceil(self.height/fragment_height)) + passes = int(math.ceil(self.height / fragment_height)) fragments = [] for n in range(0, passes): left = 0 diff --git a/src/escpos/katakana.py b/src/escpos/katakana.py index f1d8b66..2eb99f7 100644 --- a/src/escpos/katakana.py +++ b/src/escpos/katakana.py @@ -34,69 +34,68 @@ def encode_katakana(text): TXT_ENC_KATAKANA_MAP = { # Maps UTF-8 Katakana symbols to KATAKANA Page Codes # TODO: has this really to be hardcoded? - # Half-Width Katakanas - '。': b'\xa1', - '「': b'\xa2', - '」': b'\xa3', - '、': b'\xa4', - '・': b'\xa5', - 'ヲ': b'\xa6', - 'ァ': b'\xa7', - 'ィ': b'\xa8', - 'ゥ': b'\xa9', - 'ェ': b'\xaa', - 'ォ': b'\xab', - 'ャ': b'\xac', - 'ュ': b'\xad', - 'ョ': b'\xae', - 'ッ': b'\xaf', - 'ー': b'\xb0', - 'ア': b'\xb1', - 'イ': b'\xb2', - 'ウ': b'\xb3', - 'エ': b'\xb4', - 'オ': b'\xb5', - 'カ': b'\xb6', - 'キ': b'\xb7', - 'ク': b'\xb8', - 'ケ': b'\xb9', - 'コ': b'\xba', - 'サ': b'\xbb', - 'シ': b'\xbc', - 'ス': b'\xbd', - 'セ': b'\xbe', - 'ソ': b'\xbf', - 'タ': b'\xc0', - 'チ': b'\xc1', - 'ツ': b'\xc2', - 'テ': b'\xc3', - 'ト': b'\xc4', - 'ナ': b'\xc5', - 'ニ': b'\xc6', - 'ヌ': b'\xc7', - 'ネ': b'\xc8', - 'ノ': b'\xc9', - 'ハ': b'\xca', - 'ヒ': b'\xcb', - 'フ': b'\xcc', - 'ヘ': b'\xcd', - 'ホ': b'\xce', - 'マ': b'\xcf', - 'ミ': b'\xd0', - 'ム': b'\xd1', - 'メ': b'\xd2', - 'モ': b'\xd3', - 'ヤ': b'\xd4', - 'ユ': b'\xd5', - 'ヨ': b'\xd6', - 'ラ': b'\xd7', - 'リ': b'\xd8', - 'ル': b'\xd9', - 'レ': b'\xda', - 'ロ': b'\xdb', - 'ワ': b'\xdc', - 'ン': b'\xdd', - '゙': b'\xde', - '゚': b'\xdf', + "。": b"\xa1", + "「": b"\xa2", + "」": b"\xa3", + "、": b"\xa4", + "・": b"\xa5", + "ヲ": b"\xa6", + "ァ": b"\xa7", + "ィ": b"\xa8", + "ゥ": b"\xa9", + "ェ": b"\xaa", + "ォ": b"\xab", + "ャ": b"\xac", + "ュ": b"\xad", + "ョ": b"\xae", + "ッ": b"\xaf", + "ー": b"\xb0", + "ア": b"\xb1", + "イ": b"\xb2", + "ウ": b"\xb3", + "エ": b"\xb4", + "オ": b"\xb5", + "カ": b"\xb6", + "キ": b"\xb7", + "ク": b"\xb8", + "ケ": b"\xb9", + "コ": b"\xba", + "サ": b"\xbb", + "シ": b"\xbc", + "ス": b"\xbd", + "セ": b"\xbe", + "ソ": b"\xbf", + "タ": b"\xc0", + "チ": b"\xc1", + "ツ": b"\xc2", + "テ": b"\xc3", + "ト": b"\xc4", + "ナ": b"\xc5", + "ニ": b"\xc6", + "ヌ": b"\xc7", + "ネ": b"\xc8", + "ノ": b"\xc9", + "ハ": b"\xca", + "ヒ": b"\xcb", + "フ": b"\xcc", + "ヘ": b"\xcd", + "ホ": b"\xce", + "マ": b"\xcf", + "ミ": b"\xd0", + "ム": b"\xd1", + "メ": b"\xd2", + "モ": b"\xd3", + "ヤ": b"\xd4", + "ユ": b"\xd5", + "ヨ": b"\xd6", + "ラ": b"\xd7", + "リ": b"\xd8", + "ル": b"\xd9", + "レ": b"\xda", + "ロ": b"\xdb", + "ワ": b"\xdc", + "ン": b"\xdd", + "゙": b"\xde", + "゚": b"\xdf", } diff --git a/src/escpos/magicencode.py b/src/escpos/magicencode.py index ace8ea2..11e1b02 100644 --- a/src/escpos/magicencode.py +++ b/src/escpos/magicencode.py @@ -55,10 +55,12 @@ class Encoder(object): """ encoding = CodePages.get_encoding_name(encoding) if encoding not in self.codepages: - raise ValueError(( - 'Encoding "{}" cannot be used for the current profile. ' - 'Valid encodings are: {}' - ).format(encoding, ','.join(self.codepages.keys()))) + raise ValueError( + ( + 'Encoding "{}" cannot be used for the current profile. ' + "Valid encodings are: {}" + ).format(encoding, ",".join(self.codepages.keys())) + ) return encoding @staticmethod @@ -70,16 +72,18 @@ class Encoder(object): :param encoding: The name of the encoding. This must appear in the CodePage list """ codepage = CodePages.get_encoding(encoding) - if 'data' in codepage: - encodable_chars = list("".join(codepage['data'])) - assert(len(encodable_chars) == 128) + if "data" in codepage: + encodable_chars = list("".join(codepage["data"])) + assert len(encodable_chars) == 128 return encodable_chars - elif 'python_encode' in codepage: + elif "python_encode" in codepage: encodable_chars = [u" "] * 128 for i in range(0, 128): codepoint = i + 128 try: - encodable_chars[i] = bytes([codepoint]).decode(codepage['python_encode']) + encodable_chars[i] = bytes([codepoint]).decode( + codepage["python_encode"] + ) except UnicodeDecodeError: # Non-encodable character, just skip it pass @@ -87,7 +91,7 @@ class Encoder(object): raise LookupError("Can't find a known encoding for {}".format(encoding)) def _get_codepage_char_map(self, encoding): - """ Get codepage character map + """Get codepage character map Process an encoding and return a map of UTF-characters to code points in this encoding. @@ -100,7 +104,9 @@ class Encoder(object): if encoding in self.available_characters: return self.available_characters[encoding] codepage_char_list = self._get_codepage_char_list(encoding) - codepage_char_map = dict((utf8, i + 128) for (i, utf8) in enumerate(codepage_char_list)) + codepage_char_map = dict( + (utf8, i + 128) for (i, utf8) in enumerate(codepage_char_list) + ) self.available_characters[encoding] = codepage_char_map return codepage_char_map @@ -123,7 +129,7 @@ class Encoder(object): @staticmethod def _encode_char(char, charmap, defaultchar): - """ Encode a single character with the given encoding map + """Encode a single character with the given encoding map :param char: char to encode :param charmap: dictionary for mapping characters in this code page @@ -134,23 +140,22 @@ class Encoder(object): return charmap[char] return ord(defaultchar) - def encode(self, text, encoding, defaultchar='?'): - """ Encode text under the given encoding + def encode(self, text, encoding, defaultchar="?"): + """Encode text under the given encoding :param text: Text to encode :param encoding: Encoding name to use (must be defined in capabilities) :param defaultchar: Fallback for non-encodable characters """ codepage_char_map = self._get_codepage_char_map(encoding) - output_bytes = bytes([self._encode_char(char, codepage_char_map, defaultchar) for char in text]) + output_bytes = bytes( + [self._encode_char(char, codepage_char_map, defaultchar) for char in text] + ) return output_bytes def __encoding_sort_func(self, item): key, index = item - return ( - key in self.used_encodings, - index - ) + return (key in self.used_encodings, index) def find_suitable_encoding(self, char): """The order of our search is a specific one: @@ -168,9 +173,7 @@ class Encoder(object): that the code page we pick for this character is actually supported. """ - sorted_encodings = sorted( - self.codepages.items(), - key=self.__encoding_sort_func) + sorted_encodings = sorted(self.codepages.items(), key=self.__encoding_sort_func) for encoding, _ in sorted_encodings: if self.can_encode(encoding, char): @@ -205,8 +208,10 @@ class MagicEncode(object): If the printer does not support a suitable code page, it can insert an error character. """ - def __init__(self, driver, encoding=None, disabled=False, - defaultsymbol='?', encoder=None): + + def __init__( + self, driver, encoding=None, disabled=False, defaultsymbol="?", encoder=None + ): """ :param driver: @@ -219,7 +224,7 @@ class MagicEncode(object): :param encoder: """ if disabled and not encoding: - raise Error('If you disable magic encode, you need to define an encoding!') + raise Error("If you disable magic encode, you need to define an encoding!") self.driver = driver self.encoder = encoder or Encoder(driver.profile.get_code_pages()) @@ -241,8 +246,7 @@ class MagicEncode(object): self.disabled = True def write(self, text): - """Write the text, automatically switching encodings. - """ + """Write the text, automatically switching encodings.""" if self.disabled: self.write_with_encoding(self.encoding, text) @@ -268,25 +272,26 @@ class MagicEncode(object): self.write_with_encoding(encoding, to_write) def _handle_character_failed(self, char): - """Called when no codepage was found to render a character. - """ + """Called when no codepage was found to render a character.""" # Writing the default symbol via write() allows us to avoid # unnecesary codepage switches. self.write(self.defaultsymbol) def write_with_encoding(self, encoding, text): if text is not None and type(text) is not six.text_type: - raise Error("The supplied text has to be unicode, but is of type {type}.".format( - type=type(text) - )) + raise Error( + "The supplied text has to be unicode, but is of type {type}.".format( + type=type(text) + ) + ) # We always know the current code page; if the new codepage # is different, emit a change command. if encoding != self.encoding: self.encoding = encoding self.driver._raw( - CODEPAGE_CHANGE + - six.int2byte(self.encoder.get_sequence(encoding))) + CODEPAGE_CHANGE + six.int2byte(self.encoder.get_sequence(encoding)) + ) if text: self.driver._raw(self.encoder.encode(text, encoding)) diff --git a/src/escpos/printer.py b/src/escpos/printer.py index 7a0f7f3..03ce645 100644 --- a/src/escpos/printer.py +++ b/src/escpos/printer.py @@ -19,7 +19,7 @@ from .exceptions import USBNotFoundError class Usb(Escpos): - """ USB printer + """USB printer This class describes a printer that natively speaks USB. @@ -30,8 +30,17 @@ class Usb(Escpos): """ - def __init__(self, idVendor, idProduct, usb_args=None, timeout=0, in_ep=0x82, out_ep=0x01, - *args, **kwargs): # noqa: N803 + def __init__( + self, + idVendor, + idProduct, + usb_args=None, + timeout=0, + in_ep=0x82, + out_ep=0x01, + *args, + **kwargs + ): # noqa: N803 """ :param idVendor: Vendor ID :param idProduct: Product ID @@ -47,13 +56,13 @@ class Usb(Escpos): usb_args = usb_args or {} if idVendor: - usb_args['idVendor'] = idVendor + usb_args["idVendor"] = idVendor if idProduct: - usb_args['idProduct'] = idProduct + usb_args["idProduct"] = idProduct self.open(usb_args) def open(self, usb_args): - """ Search device on USB tree and set it as escpos device. + """Search device on USB tree and set it as escpos device. :param usb_args: USB arguments """ @@ -92,7 +101,7 @@ class Usb(Escpos): print("Could not set configuration: {0}".format(str(e))) def _raw(self, msg): - """ Print any command sent in raw format + """Print any command sent in raw format :param msg: arbitrary code to be printed :type msg: bytes @@ -100,18 +109,18 @@ class Usb(Escpos): self.device.write(self.out_ep, msg, self.timeout) def _read(self): - """ Reads a data buffer and returns it to the caller. """ + """Reads a data buffer and returns it to the caller.""" return self.device.read(self.in_ep, 16) def close(self): - """ Release USB interface """ + """Release USB interface""" if self.device: usb.util.dispose_resources(self.device) self.device = None class Serial(Escpos): - """ Serial printer + """Serial printer This class describes a printer that is connected by serial interface. @@ -122,9 +131,19 @@ class Serial(Escpos): """ - def __init__(self, devfile="/dev/ttyS0", baudrate=9600, bytesize=8, timeout=1, - parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, - xonxoff=False, dsrdtr=True, *args, **kwargs): + def __init__( + self, + devfile="/dev/ttyS0", + baudrate=9600, + bytesize=8, + timeout=1, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + xonxoff=False, + dsrdtr=True, + *args, + **kwargs + ): """ :param devfile: Device file under dev filesystem @@ -149,13 +168,19 @@ class Serial(Escpos): self.open() def open(self): - """ Setup serial port and set is as escpos device """ + """Setup serial port and set is as escpos device""" if self.device is not None and self.device.is_open: self.close() - self.device = serial.Serial(port=self.devfile, baudrate=self.baudrate, - bytesize=self.bytesize, parity=self.parity, - stopbits=self.stopbits, timeout=self.timeout, - xonxoff=self.xonxoff, dsrdtr=self.dsrdtr) + self.device = serial.Serial( + port=self.devfile, + baudrate=self.baudrate, + bytesize=self.bytesize, + parity=self.parity, + stopbits=self.stopbits, + timeout=self.timeout, + xonxoff=self.xonxoff, + dsrdtr=self.dsrdtr, + ) if self.device is not None: print("Serial printer enabled") @@ -163,7 +188,7 @@ class Serial(Escpos): print("Unable to open serial printer on: {0}".format(str(self.devfile))) def _raw(self, msg): - """ Print any command sent in raw format + """Print any command sent in raw format :param msg: arbitrary code to be printed :type msg: bytes @@ -171,18 +196,18 @@ class Serial(Escpos): self.device.write(msg) def _read(self): - """ Reads a data buffer and returns it to the caller. """ + """Reads a data buffer and returns it to the caller.""" return self.device.read(16) def close(self): - """ Close Serial interface """ + """Close Serial interface""" if self.device is not None and self.device.is_open: self.device.flush() self.device.close() class Network(Escpos): - """ Network printer + """Network printer This class is used to attach to a networked printer. You can also use this in order to attach to a printer that is forwarded with ``socat``. @@ -218,7 +243,7 @@ class Network(Escpos): self.open() def open(self): - """ Open TCP socket with ``socket``-library and set it as escpos device """ + """Open TCP socket with ``socket``-library and set it as escpos device""" self.device = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.device.settimeout(self.timeout) self.device.connect((self.host, self.port)) @@ -227,7 +252,7 @@ class Network(Escpos): print("Could not open socket for {0}".format(self.host)) def _raw(self, msg): - """ Print any command sent in raw format + """Print any command sent in raw format :param msg: arbitrary code to be printed :type msg: bytes @@ -235,12 +260,12 @@ class Network(Escpos): self.device.sendall(msg) def _read(self): - """ Read data from the TCP socket """ + """Read data from the TCP socket""" return self.device.recv(16) def close(self): - """ Close TCP connection """ + """Close TCP connection""" if self.device is not None: try: self.device.shutdown(socket.SHUT_RDWR) @@ -250,7 +275,7 @@ class Network(Escpos): class File(Escpos): - """ Generic file printer + """Generic file printer This class is used for parallel port printer or other printers that are directly attached to the filesystem. Note that you should stay away from using USB-to-Parallel-Adapter since they are unreliable @@ -275,18 +300,18 @@ class File(Escpos): self.open() def open(self): - """ Open system file """ + """Open system file""" self.device = open(self.devfile, "wb") if self.device is None: print("Could not open the specified file {0}".format(self.devfile)) def flush(self): - """ Flush printing content """ + """Flush printing content""" self.device.flush() def _raw(self, msg): - """ Print any command sent in raw format + """Print any command sent in raw format :param msg: arbitrary code to be printed :type msg: bytes @@ -296,14 +321,14 @@ class File(Escpos): self.flush() def close(self): - """ Close system file """ + """Close system file""" if self.device is not None: self.device.flush() self.device.close() class Dummy(Escpos): - """ Dummy printer + """Dummy printer This class is used for saving commands to a variable, for use in situations where there is no need to send commands to an actual printer. This includes @@ -317,13 +342,12 @@ class Dummy(Escpos): """ def __init__(self, *args, **kwargs): - """ - """ + """ """ Escpos.__init__(self, *args, **kwargs) self._output_list = [] def _raw(self, msg): - """ Print any command sent in raw format + """Print any command sent in raw format :param msg: arbitrary code to be printed :type msg: bytes @@ -332,11 +356,11 @@ class Dummy(Escpos): @property def output(self): - """ Get the data that was sent to this printer """ - return b''.join(self._output_list) + """Get the data that was sent to this printer""" + return b"".join(self._output_list) def clear(self): - """ Clear the buffer of the printer + """Clear the buffer of the printer This method can be called if you send the contents to a physical printer and want to use the Dummy printer for new output. @@ -356,6 +380,7 @@ except ImportError: pass if _WIN32PRINT: + class Win32Raw(Escpos): def __init__(self, printer_name=None, *args, **kwargs): Escpos.__init__(self, *args, **kwargs) @@ -370,7 +395,9 @@ if _WIN32PRINT: if self.printer_name is None: raise Exception("Printer not found") self.hPrinter = win32print.OpenPrinter(self.printer_name) - self.current_job = win32print.StartDocPrinter(self.hPrinter, 1, (job_name, None, "RAW")) + self.current_job = win32print.StartDocPrinter( + self.hPrinter, 1, (job_name, None, "RAW") + ) win32print.StartPagePrinter(self.hPrinter) def close(self): @@ -382,7 +409,7 @@ if _WIN32PRINT: self.hPrinter = None def _raw(self, msg): - """ Print any command sent in raw format + """Print any command sent in raw format :param msg: arbitrary code to be printed :type msg: bytes diff --git a/test/test_abstract_base_class.py b/test/test_abstract_base_class.py index 5edc8a2..46f215b 100644 --- a/test/test_abstract_base_class.py +++ b/test/test_abstract_base_class.py @@ -21,6 +21,6 @@ def test_abstract_base_class_raises(): def test_abstract_base_class(): - """ test whether Escpos has the metaclass ABCMeta """ + """test whether Escpos has the metaclass ABCMeta""" assert issubclass(escpos.Escpos, object) assert type(escpos.Escpos) is ABCMeta diff --git a/test/test_cli.py b/test/test_cli.py index eba5b76..09e7629 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -9,40 +9,39 @@ from scripttest import TestFileEnvironment from nose.tools import assert_equal, nottest import escpos -TEST_DIR = os.path.abspath('test/test-cli-output') +TEST_DIR = os.path.abspath("test/test-cli-output") -DEVFILE_NAME = 'testfile' +DEVFILE_NAME = "testfile" DEVFILE = os.path.join(TEST_DIR, DEVFILE_NAME) -CONFIGFILE = 'testconfig.yaml' -CONFIG_YAML = ''' +CONFIGFILE = "testconfig.yaml" +CONFIG_YAML = """ --- printer: type: file devfile: {testfile} -'''.format( +""".format( testfile=DEVFILE, ) class TestCLI: - """ Contains setups, teardowns, and tests for CLI - """ + """Contains setups, teardowns, and tests for CLI""" @classmethod def setup_class(cls): - """ Create a config file to read from """ - with open(CONFIGFILE, 'w') as config: + """Create a config file to read from""" + with open(CONFIGFILE, "w") as config: config.write(CONFIG_YAML) @classmethod def teardown_class(cls): - """ Remove config file """ + """Remove config file""" os.remove(CONFIGFILE) def setup(self): - """ Create a file to print to and set up env""" + """Create a file to print to and set up env""" self.env = None self.default_args = None @@ -52,63 +51,59 @@ class TestCLI: ) self.default_args = ( - 'python-escpos', - '-c', + "python-escpos", + "-c", CONFIGFILE, ) - fhandle = open(DEVFILE, 'a') + fhandle = open(DEVFILE, "a") try: os.utime(DEVFILE, None) finally: fhandle.close() def teardown(self): - """ Destroy printer file and env """ + """Destroy printer file and env""" os.remove(DEVFILE) self.env.clear() def test_cli_help(self): - """ Test getting help from cli """ - result = self.env.run('python-escpos', '-h') + """Test getting help from cli""" + result = self.env.run("python-escpos", "-h") assert not result.stderr - assert 'usage' in result.stdout + assert "usage" in result.stdout def test_cli_version(self): - """ Test the version string """ - result = self.env.run('python-escpos', 'version') + """Test the version string""" + result = self.env.run("python-escpos", "version") assert not result.stderr assert_equal(escpos.__version__, result.stdout.strip()) @nottest # disable this test as it is not that easy anymore to predict the outcome of this call def test_cli_text(self): - """ Make sure text returns what we sent it """ - test_text = 'this is some text' + """Make sure text returns what we sent it""" + test_text = "this is some text" result = self.env.run( - *(self.default_args + ( - 'text', - '--txt', - test_text, - )) + *( + self.default_args + + ( + "text", + "--txt", + test_text, + ) + ) ) assert not result.stderr assert DEVFILE_NAME in result.files_updated.keys() - assert_equals( - result.files_updated[DEVFILE_NAME].bytes, - test_text + '\n' - ) + assert_equals(result.files_updated[DEVFILE_NAME].bytes, test_text + "\n") def test_cli_text_inavlid_args(self): - """ Test a failure to send valid arguments """ + """Test a failure to send valid arguments""" result = self.env.run( - *(self.default_args + ( - 'text', - '--invalid-param', - 'some data' - )), + *(self.default_args + ("text", "--invalid-param", "some data")), expect_error=True, expect_stderr=True ) assert_equal(result.returncode, 2) - assert 'error:' in result.stderr + assert "error:" in result.stderr assert not result.files_updated diff --git a/test/test_function_barcode.py b/test/test_function_barcode.py index a2db09c..b41c5dc 100644 --- a/test/test_function_barcode.py +++ b/test/test_function_barcode.py @@ -6,42 +6,51 @@ from escpos.exceptions import BarcodeTypeError, BarcodeCodeError import pytest -@pytest.mark.parametrize("bctype,data,expected", [ - ('EAN13', '4006381333931', - b'\x1ba\x01\x1dh@\x1dw\x03\x1df\x00\x1dH\x02\x1dk\x024006381333931\x00') -]) +@pytest.mark.parametrize( + "bctype,data,expected", + [ + ( + "EAN13", + "4006381333931", + b"\x1ba\x01\x1dh@\x1dw\x03\x1df\x00\x1dH\x02\x1dk\x024006381333931\x00", + ) + ], +) def test_barcode(bctype, data, expected): - """should generate different barcode types correctly. - """ + """should generate different barcode types correctly.""" instance = printer.Dummy() instance.barcode(data, bctype) assert instance.output == expected -@pytest.mark.parametrize("bctype,supports_b", [ - ('invalid', True), - ('CODE128', False), -]) +@pytest.mark.parametrize( + "bctype,supports_b", + [ + ("invalid", True), + ("CODE128", False), + ], +) def test_lacks_support(bctype, supports_b): - """should raise an error if the barcode type is not supported. - """ + """should raise an error if the barcode type is not supported.""" profile = Profile(features={BARCODE_B: supports_b}) instance = printer.Dummy(profile=profile) with pytest.raises(BarcodeTypeError): - instance.barcode('test', bctype) + instance.barcode("test", bctype) - assert instance.output == b'' + assert instance.output == b"" -@pytest.mark.parametrize("bctype,data", [ - ('EAN13', 'AA'), - ('CODE128', '{D2354AA'), -]) +@pytest.mark.parametrize( + "bctype,data", + [ + ("EAN13", "AA"), + ("CODE128", "{D2354AA"), + ], +) def test_code_check(bctype, data): - """should raise an error if the barcode code is invalid. - """ + """should raise an error if the barcode code is invalid.""" instance = printer.Dummy() with pytest.raises(BarcodeCodeError): instance.barcode(data, bctype) - assert instance.output == b'' + assert instance.output == b"" diff --git a/test/test_function_cashdraw.py b/test/test_function_cashdraw.py index 55d61bf..95665c6 100644 --- a/test/test_function_cashdraw.py +++ b/test/test_function_cashdraw.py @@ -6,10 +6,8 @@ import pytest def test_raise_CashDrawerError(): - """should raise an error if the sequence is invalid. - """ + """should raise an error if the sequence is invalid.""" instance = printer.Dummy() with pytest.raises(CashDrawerError): # call with sequence that is too long - instance.cashdraw([1,1,1,1,1,1]) - + instance.cashdraw([1, 1, 1, 1, 1, 1]) diff --git a/test/test_function_check_barcode.py b/test/test_function_check_barcode.py index 5a73b05..6cc9fc9 100644 --- a/test/test_function_check_barcode.py +++ b/test/test_function_check_barcode.py @@ -6,95 +6,101 @@ import escpos.printer as printer import pytest -@pytest.mark.parametrize("bctype,data", [ - ('UPC-A', '01234567890'), - ('UPC-A', '012345678905'), - ('UPC-E', '01234567'), - ('UPC-E', '0123456'), - ('UPC-E', '012345678905'), - ('EAN13', '0123456789012'), - ('EAN13', '012345678901'), - ('EAN8', '01234567'), - ('EAN8', '0123456'), - ('CODE39', 'ABC-1234'), - ('CODE39', 'ABC-1234-$$-+A'), - ('CODE39', '*WIKIPEDIA*'), - ('ITF', '010203040506070809'), - ('ITF', '11221133113344556677889900'), - ('CODABAR', 'A2030405060B'), - ('CODABAR', 'C11221133113344556677889900D'), - ('CODABAR', 'D0D'), - ('NW7', 'A2030405060B'), - ('NW7', 'C11221133113344556677889900D'), - ('NW7', 'D0D'), - ('CODE93', 'A2030405060B'), - ('CODE93', '+:$&23-7@$'), - ('CODE93', 'D0D'), - ('CODE128', '{A2030405060B'), - ('CODE128', '{C+:$&23-7@$'), - ('CODE128', '{B0D'), - ('GS1-128', '{A2030405060B'), - ('GS1-128', '{C+:$&23-7@$'), - ('GS1-128', '{B0D'), - ('GS1 DATABAR OMNIDIRECTIONAL', '0123456789123'), - ('GS1 DATABAR TRUNCATED', '0123456789123'), - ('GS1 DATABAR LIMITED', '0123456789123'), - ('GS1 DATABAR EXPANDED', '(9A{A20304+-%&06a0B'), - ('GS1 DATABAR EXPANDED', '(1 {C+:&23-7%'), - ('GS1 DATABAR EXPANDED', '(00000001234567678'), -]) +@pytest.mark.parametrize( + "bctype,data", + [ + ("UPC-A", "01234567890"), + ("UPC-A", "012345678905"), + ("UPC-E", "01234567"), + ("UPC-E", "0123456"), + ("UPC-E", "012345678905"), + ("EAN13", "0123456789012"), + ("EAN13", "012345678901"), + ("EAN8", "01234567"), + ("EAN8", "0123456"), + ("CODE39", "ABC-1234"), + ("CODE39", "ABC-1234-$$-+A"), + ("CODE39", "*WIKIPEDIA*"), + ("ITF", "010203040506070809"), + ("ITF", "11221133113344556677889900"), + ("CODABAR", "A2030405060B"), + ("CODABAR", "C11221133113344556677889900D"), + ("CODABAR", "D0D"), + ("NW7", "A2030405060B"), + ("NW7", "C11221133113344556677889900D"), + ("NW7", "D0D"), + ("CODE93", "A2030405060B"), + ("CODE93", "+:$&23-7@$"), + ("CODE93", "D0D"), + ("CODE128", "{A2030405060B"), + ("CODE128", "{C+:$&23-7@$"), + ("CODE128", "{B0D"), + ("GS1-128", "{A2030405060B"), + ("GS1-128", "{C+:$&23-7@$"), + ("GS1-128", "{B0D"), + ("GS1 DATABAR OMNIDIRECTIONAL", "0123456789123"), + ("GS1 DATABAR TRUNCATED", "0123456789123"), + ("GS1 DATABAR LIMITED", "0123456789123"), + ("GS1 DATABAR EXPANDED", "(9A{A20304+-%&06a0B"), + ("GS1 DATABAR EXPANDED", "(1 {C+:&23-7%"), + ("GS1 DATABAR EXPANDED", "(00000001234567678"), + ], +) def test_check_valid_barcode(bctype, data): - assert (printer.Escpos.check_barcode(bctype, data)) + assert printer.Escpos.check_barcode(bctype, data) -@pytest.mark.parametrize("bctype,data", [ - ('UPC-A', '01234567890123'), # too long - ('UPC-A', '0123456789'), # too short - ('UPC-A', '72527273-711'), # invalid '-' - ('UPC-A', 'A12345678901'), # invalid 'A' - ('UPC-E', '01234567890123'), # too long - ('UPC-E', '012345'), # too short - ('UPC-E', '72527-2'), # invalid '-' - ('UPC-E', 'A123456'), # invalid 'A' - ('EAN13', '0123456789'), # too short - ('EAN13', 'A123456789012'), # invalid 'A' - ('EAN13', '012345678901234'), # too long - ('EAN8', '012345'), # too short - ('EAN8', 'A123456789012'), # invalid 'A' - ('EAN8', '012345678901234'), # too long - ('CODE39', 'ALKJ_34'), # invalid '_' - ('CODE39', 'A' * 256), # too long - ('ITF', '010203040'), # odd length - ('ITF', '0' * 256), # too long - ('ITF', 'AB01'), # invalid 'A' - ('CODABAR', '010203040'), # no start/stop - ('CODABAR', '0' * 256), # too long - ('CODABAR', 'AB-01F'), # invalid 'B' - ('NW7', '010203040'), # no start/stop - ('NW7', '0' * 256), # too long - ('NW7', 'AB-01F'), # invalid 'B' - ('CODE93', 'é010203040'), # invalid 'é' - ('CODE93', '0' * 256), # too long - ('CODE128', '010203040'), # missing leading { - ('CODE128', '{D2354AA'), # second char not between A-C - ('CODE128', '0' * 256), # too long - ('GS1-128', '010203040'), # missing leading { - ('GS1-128', '{D2354AA'), # second char not between A-C - ('GS1-128', '0' * 256), # too long - ('GS1 DATABAR OMNIDIRECTIONAL', '01234567891234'), # too long - ('GS1 DATABAR OMNIDIRECTIONAL', '012345678912'), # too short - ('GS1 DATABAR OMNIDIRECTIONAL', '012345678A1234'), # invalid 'A' - ('GS1 DATABAR TRUNCATED', '01234567891234'), # too long - ('GS1 DATABAR TRUNCATED', '012345678912'), # too short - ('GS1 DATABAR TRUNCATED', '012345678A1234'), # invalid 'A' - ('GS1 DATABAR LIMITED', '01234567891234'), # too long - ('GS1 DATABAR LIMITED', '012345678912'), # too short - ('GS1 DATABAR LIMITED', '012345678A1234'), # invalid 'A' - ('GS1 DATABAR LIMITED', '02345678912341'), # invalid start (should be 01) - ('GS1 DATABAR EXPANDED', '010203040'), # missing leading ( - ('GS1-128', '(' + ('0' * 256)), # too long - ('GS1 DATABAR EXPANDED', '(a{D2354AA'), # second char not between 0-9 - ('GS1 DATABAR EXPANDED', 'IT will fail'), # first char not '(' -]) +@pytest.mark.parametrize( + "bctype,data", + [ + ("UPC-A", "01234567890123"), # too long + ("UPC-A", "0123456789"), # too short + ("UPC-A", "72527273-711"), # invalid '-' + ("UPC-A", "A12345678901"), # invalid 'A' + ("UPC-E", "01234567890123"), # too long + ("UPC-E", "012345"), # too short + ("UPC-E", "72527-2"), # invalid '-' + ("UPC-E", "A123456"), # invalid 'A' + ("EAN13", "0123456789"), # too short + ("EAN13", "A123456789012"), # invalid 'A' + ("EAN13", "012345678901234"), # too long + ("EAN8", "012345"), # too short + ("EAN8", "A123456789012"), # invalid 'A' + ("EAN8", "012345678901234"), # too long + ("CODE39", "ALKJ_34"), # invalid '_' + ("CODE39", "A" * 256), # too long + ("ITF", "010203040"), # odd length + ("ITF", "0" * 256), # too long + ("ITF", "AB01"), # invalid 'A' + ("CODABAR", "010203040"), # no start/stop + ("CODABAR", "0" * 256), # too long + ("CODABAR", "AB-01F"), # invalid 'B' + ("NW7", "010203040"), # no start/stop + ("NW7", "0" * 256), # too long + ("NW7", "AB-01F"), # invalid 'B' + ("CODE93", "é010203040"), # invalid 'é' + ("CODE93", "0" * 256), # too long + ("CODE128", "010203040"), # missing leading { + ("CODE128", "{D2354AA"), # second char not between A-C + ("CODE128", "0" * 256), # too long + ("GS1-128", "010203040"), # missing leading { + ("GS1-128", "{D2354AA"), # second char not between A-C + ("GS1-128", "0" * 256), # too long + ("GS1 DATABAR OMNIDIRECTIONAL", "01234567891234"), # too long + ("GS1 DATABAR OMNIDIRECTIONAL", "012345678912"), # too short + ("GS1 DATABAR OMNIDIRECTIONAL", "012345678A1234"), # invalid 'A' + ("GS1 DATABAR TRUNCATED", "01234567891234"), # too long + ("GS1 DATABAR TRUNCATED", "012345678912"), # too short + ("GS1 DATABAR TRUNCATED", "012345678A1234"), # invalid 'A' + ("GS1 DATABAR LIMITED", "01234567891234"), # too long + ("GS1 DATABAR LIMITED", "012345678912"), # too short + ("GS1 DATABAR LIMITED", "012345678A1234"), # invalid 'A' + ("GS1 DATABAR LIMITED", "02345678912341"), # invalid start (should be 01) + ("GS1 DATABAR EXPANDED", "010203040"), # missing leading ( + ("GS1-128", "(" + ("0" * 256)), # too long + ("GS1 DATABAR EXPANDED", "(a{D2354AA"), # second char not between 0-9 + ("GS1 DATABAR EXPANDED", "IT will fail"), # first char not '(' + ], +) def test_check_invalid_barcode(bctype, data): - assert (not printer.Escpos.check_barcode(bctype, data)) + assert not printer.Escpos.check_barcode(bctype, data) diff --git a/test/test_function_cut.py b/test/test_function_cut.py index 91e4bb3..75333c2 100644 --- a/test/test_function_cut.py +++ b/test/test_function_cut.py @@ -1,4 +1,3 @@ - import six import escpos.printer as printer @@ -9,5 +8,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' - assert(instance.output == expected) + expected = GS + b"V" + six.int2byte(66) + b"\x00" + assert instance.output == expected diff --git a/test/test_function_dummy_clear.py b/test/test_function_dummy_clear.py index 431eb9a..142a0a8 100644 --- a/test/test_function_dummy_clear.py +++ b/test/test_function_dummy_clear.py @@ -1,8 +1,9 @@ from nose.tools import assert_raises from escpos.printer import Dummy + def test_printer_dummy_clear(): printer = Dummy() printer.text("Hello") printer.clear() - assert(printer.output == b'') + assert printer.output == b"" diff --git a/test/test_function_image.py b/test/test_function_image.py index 28cc830..2f1a3d2 100644 --- a/test/test_function_image.py +++ b/test/test_function_image.py @@ -22,13 +22,13 @@ def test_bit_image_black(): Test printing solid black bit image (raster) """ instance = printer.Dummy() - instance.image('test/resources/canvas_black.png', impl="bitImageRaster") - assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\x80') + instance.image("test/resources/canvas_black.png", impl="bitImageRaster") + assert instance.output == b"\x1dv0\x00\x01\x00\x01\x00\x80" # Same thing w/ object created on the fly, rather than a filename instance = printer.Dummy() im = Image.new("RGB", (1, 1), (0, 0, 0)) instance.image(im, impl="bitImageRaster") - assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\x80') + assert instance.output == b"\x1dv0\x00\x01\x00\x01\x00\x80" def test_bit_image_white(): @@ -36,8 +36,8 @@ def test_bit_image_white(): Test printing solid white bit image (raster) """ instance = printer.Dummy() - instance.image('test/resources/canvas_white.png', impl="bitImageRaster") - assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\x00') + instance.image("test/resources/canvas_white.png", impl="bitImageRaster") + assert instance.output == b"\x1dv0\x00\x01\x00\x01\x00\x00" def test_bit_image_both(): @@ -45,8 +45,8 @@ def test_bit_image_both(): Test printing black/white bit image (raster) """ instance = printer.Dummy() - instance.image('test/resources/black_white.png', impl="bitImageRaster") - assert(instance.output == b'\x1dv0\x00\x01\x00\x02\x00\xc0\x00') + instance.image("test/resources/black_white.png", impl="bitImageRaster") + assert instance.output == b"\x1dv0\x00\x01\x00\x02\x00\xc0\x00" def test_bit_image_transparent(): @@ -54,8 +54,8 @@ def test_bit_image_transparent(): Test printing black/transparent bit image (raster) """ instance = printer.Dummy() - instance.image('test/resources/black_transparent.png', impl="bitImageRaster") - assert(instance.output == b'\x1dv0\x00\x01\x00\x02\x00\xc0\x00') + instance.image("test/resources/black_transparent.png", impl="bitImageRaster") + assert instance.output == b"\x1dv0\x00\x01\x00\x02\x00\xc0\x00" # Column format print @@ -64,8 +64,8 @@ def test_bit_image_colfmt_black(): Test printing solid black bit image (column format) """ instance = printer.Dummy() - instance.image('test/resources/canvas_black.png', impl="bitImageColumn") - assert(instance.output == b'\x1b3\x10\x1b*!\x01\x00\x80\x00\x00\x0a\x1b2') + instance.image("test/resources/canvas_black.png", impl="bitImageColumn") + assert instance.output == b"\x1b3\x10\x1b*!\x01\x00\x80\x00\x00\x0a\x1b2" def test_bit_image_colfmt_white(): @@ -73,8 +73,8 @@ def test_bit_image_colfmt_white(): Test printing solid white bit image (column format) """ instance = printer.Dummy() - instance.image('test/resources/canvas_white.png', impl="bitImageColumn") - assert(instance.output == b'\x1b3\x10\x1b*!\x01\x00\x00\x00\x00\x0a\x1b2') + instance.image("test/resources/canvas_white.png", impl="bitImageColumn") + assert instance.output == b"\x1b3\x10\x1b*!\x01\x00\x00\x00\x00\x0a\x1b2" def test_bit_image_colfmt_both(): @@ -82,8 +82,10 @@ def test_bit_image_colfmt_both(): Test printing black/white bit image (column format) """ instance = printer.Dummy() - instance.image('test/resources/black_white.png', impl="bitImageColumn") - assert(instance.output == b'\x1b3\x10\x1b*!\x02\x00\x80\x00\x00\x80\x00\x00\x0a\x1b2') + instance.image("test/resources/black_white.png", impl="bitImageColumn") + assert ( + instance.output == b"\x1b3\x10\x1b*!\x02\x00\x80\x00\x00\x80\x00\x00\x0a\x1b2" + ) def test_bit_image_colfmt_transparent(): @@ -91,8 +93,10 @@ def test_bit_image_colfmt_transparent(): Test printing black/transparent bit image (column format) """ instance = printer.Dummy() - instance.image('test/resources/black_transparent.png', impl="bitImageColumn") - assert(instance.output == b'\x1b3\x10\x1b*!\x02\x00\x80\x00\x00\x80\x00\x00\x0a\x1b2') + instance.image("test/resources/black_transparent.png", impl="bitImageColumn") + assert ( + instance.output == b"\x1b3\x10\x1b*!\x02\x00\x80\x00\x00\x80\x00\x00\x0a\x1b2" + ) # Graphics print @@ -101,8 +105,11 @@ def test_graphics_black(): Test printing solid black graphics """ instance = printer.Dummy() - instance.image('test/resources/canvas_black.png', impl="graphics") - assert(instance.output == b'\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x80\x1d(L\x02\x0002') + instance.image("test/resources/canvas_black.png", impl="graphics") + assert ( + instance.output + == b"\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x80\x1d(L\x02\x0002" + ) def test_graphics_white(): @@ -110,8 +117,11 @@ def test_graphics_white(): Test printing solid white graphics """ instance = printer.Dummy() - instance.image('test/resources/canvas_white.png', impl="graphics") - assert(instance.output == b'\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x00\x1d(L\x02\x0002') + instance.image("test/resources/canvas_white.png", impl="graphics") + assert ( + instance.output + == b"\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x00\x1d(L\x02\x0002" + ) def test_graphics_both(): @@ -119,8 +129,11 @@ def test_graphics_both(): Test printing black/white graphics """ instance = printer.Dummy() - instance.image('test/resources/black_white.png', impl="graphics") - assert(instance.output == b'\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002') + instance.image("test/resources/black_white.png", impl="graphics") + assert ( + instance.output + == b"\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002" + ) def test_graphics_transparent(): @@ -128,8 +141,11 @@ def test_graphics_transparent(): Test printing black/transparent graphics """ instance = printer.Dummy() - instance.image('test/resources/black_transparent.png', impl="graphics") - assert(instance.output == b'\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002') + instance.image("test/resources/black_transparent.png", impl="graphics") + assert ( + instance.output + == b"\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002" + ) def test_large_graphics(): @@ -137,20 +153,19 @@ def test_large_graphics(): Test whether 'large' graphics that induce a fragmentation are handled correctly. """ instance = printer.Dummy() - instance.image('test/resources/black_white.png', impl="bitImageRaster", fragment_height=1) - assert(instance.output == b'\x1dv0\x00\x01\x00\x01\x00\xc0\x1dv0\x00\x01\x00\x01\x00\x00') + instance.image( + "test/resources/black_white.png", impl="bitImageRaster", fragment_height=1 + ) + assert ( + instance.output + == b"\x1dv0\x00\x01\x00\x01\x00\xc0\x1dv0\x00\x01\x00\x01\x00\x00" + ) @pytest.fixture def dummy_with_width(): instance = printer.Dummy() - instance.profile.profile_data = { - 'media': { - 'width': { - 'pixels': 384 - } - } - } + instance.profile.profile_data = {"media": {"width": {"pixels": 384}}} return instance diff --git a/test/test_function_linedisplay.py b/test/test_function_linedisplay.py index 6b210a5..e9e53cc 100644 --- a/test/test_function_linedisplay.py +++ b/test/test_function_linedisplay.py @@ -15,17 +15,18 @@ def test_function_linedisplay_select_on(): """test the linedisplay_select function (activate)""" instance = printer.Dummy() instance.linedisplay_select(select_display=True) - assert(instance.output == b'\x1B\x3D\x02') + assert instance.output == b"\x1B\x3D\x02" + def test_function_linedisplay_select_off(): """test the linedisplay_select function (deactivate)""" instance = printer.Dummy() instance.linedisplay_select(select_display=False) - assert(instance.output == b'\x1B\x3D\x01') + assert instance.output == b"\x1B\x3D\x01" + def test_function_linedisplay_clear(): """test the linedisplay_clear function""" instance = printer.Dummy() instance.linedisplay_clear() - assert(instance.output == b'\x1B\x40') - + assert instance.output == b"\x1B\x40" diff --git a/test/test_function_panel_button.py b/test/test_function_panel_button.py index 417513f..39529f6 100644 --- a/test/test_function_panel_button.py +++ b/test/test_function_panel_button.py @@ -15,11 +15,11 @@ def test_function_panel_button_on(): """test the panel button function (enabling) by comparing output""" instance = printer.Dummy() instance.panel_buttons() - assert(instance.output == b'\x1B\x63\x35\x00') + assert instance.output == b"\x1B\x63\x35\x00" def test_function_panel_button_off(): """test the panel button function (disabling) by comparing output""" instance = printer.Dummy() instance.panel_buttons(False) - assert(instance.output == b'\x1B\x63\x35\x01') + assert instance.output == b"\x1B\x63\x35\x01" diff --git a/test/test_function_qr_native.py b/test/test_function_qr_native.py index fc67bd6..8dfed74 100644 --- a/test/test_function_qr_native.py +++ b/test/test_function_qr_native.py @@ -19,42 +19,51 @@ def test_defaults(): """Test QR code with defaults""" instance = printer.Dummy() instance.qr("1234", native=True) - expected = b'\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d' \ - b'(k\x07\x001P01234\x1d(k\x03\x001Q0' - assert(instance.output == expected) + expected = ( + b"\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d" + b"(k\x07\x001P01234\x1d(k\x03\x001Q0" + ) + assert instance.output == expected + def test_empty(): """Test QR printing blank code""" instance = printer.Dummy() instance.qr("", native=True) - assert(instance.output == b'') + assert instance.output == b"" def test_ec(): """Test QR error correction setting""" instance = printer.Dummy() instance.qr("1234", native=True, ec=QR_ECLEVEL_H) - expected = b'\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E3\x1d' \ - b'(k\x07\x001P01234\x1d(k\x03\x001Q0' - assert(instance.output == expected) + expected = ( + b"\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E3\x1d" + b"(k\x07\x001P01234\x1d(k\x03\x001Q0" + ) + assert instance.output == expected def test_size(): """Test QR box size""" instance = printer.Dummy() instance.qr("1234", native=True, size=7) - expected = b'\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x07\x1d(k\x03\x001E0\x1d' \ - b'(k\x07\x001P01234\x1d(k\x03\x001Q0' - assert(instance.output == expected) + expected = ( + b"\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x07\x1d(k\x03\x001E0\x1d" + b"(k\x07\x001P01234\x1d(k\x03\x001Q0" + ) + assert instance.output == expected def test_model(): """Test QR model""" instance = printer.Dummy() instance.qr("1234", native=True, model=QR_MODEL_1) - expected = b'\x1d(k\x04\x001A1\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d' \ - b'(k\x07\x001P01234\x1d(k\x03\x001Q0' - assert(instance.output == expected) + expected = ( + b"\x1d(k\x04\x001A1\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d" + b"(k\x07\x001P01234\x1d(k\x03\x001Q0" + ) + assert instance.output == expected @raises(ValueError) @@ -84,12 +93,14 @@ def test_image(): instance = printer.Dummy() instance.qr("1", native=False, size=1) print(instance.output) - expected = b'\x1bt\x00\n' \ - b'\x1dv0\x00\x03\x00\x17\x00\x00\x00\x00\x7f]\xfcA\x19\x04]it]et' \ - b']ItA=\x04\x7fU\xfc\x00\x0c\x00y~t4\x7f =\xa84j\xd9\xf0\x05\xd4\x90\x00' \ - b'i(\x7f<\xa8A \xd8]\'\xc4]y\xf8]E\x80Ar\x94\x7fR@\x00\x00\x00' \ - b'\n\n' - assert(instance.output == expected) + expected = ( + b"\x1bt\x00\n" + b"\x1dv0\x00\x03\x00\x17\x00\x00\x00\x00\x7f]\xfcA\x19\x04]it]et" + b"]ItA=\x04\x7fU\xfc\x00\x0c\x00y~t4\x7f =\xa84j\xd9\xf0\x05\xd4\x90\x00" + b"i(\x7f<\xa8A \xd8]'\xc4]y\xf8]E\x80Ar\x94\x7fR@\x00\x00\x00" + b"\n\n" + ) + assert instance.output == expected @raises(ValueError) diff --git a/test/test_function_qr_non-native.py b/test/test_function_qr_non-native.py index 804319e..e199e33 100644 --- a/test/test_function_qr_non-native.py +++ b/test/test_function_qr_non-native.py @@ -16,7 +16,7 @@ from escpos.printer import Dummy from PIL import Image -@mock.patch('escpos.printer.Dummy.image', spec=Dummy) +@mock.patch("escpos.printer.Dummy.image", spec=Dummy) def test_type_of_object_passed_to_image_function(img_function): """ Test the type of object that is passed to the image function during non-native qr-printing. diff --git a/test/test_function_set.py b/test/test_function_set.py index 08b1d47..910450e 100644 --- a/test/test_function_set.py +++ b/test/test_function_set.py @@ -1,4 +1,3 @@ - import six import escpos.printer as printer @@ -8,41 +7,46 @@ from escpos.constants import TXT_SIZE # Default test, please copy and paste this block to test set method calls + def test_default_values(): instance = printer.Dummy() instance.set() expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert(instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) + # Size tests + def test_set_size_2h(): instance = printer.Dummy() instance.set(double_height=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['2h'], # Double height text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["2h"], # Double height text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) def test_set_size_2w(): @@ -50,17 +54,18 @@ def test_set_size_2w(): instance.set(double_width=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['2w'], # Double width text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["2w"], # Double width text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) def test_set_size_2x(): @@ -68,17 +73,18 @@ def test_set_size_2x(): instance.set(double_height=True, double_width=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['2x'], # Double text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["2x"], # Double text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) def test_set_size_custom(): @@ -87,55 +93,61 @@ def test_set_size_custom(): expected_sequence = ( TXT_SIZE, # Custom text size, no normal reset - six.int2byte(TXT_STYLE['width'][8] + TXT_STYLE['height'][7]), - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + six.int2byte(TXT_STYLE["width"][8] + TXT_STYLE["height"][7]), + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) + # Flip + def test_set_flip(): instance = printer.Dummy() instance.set(flip=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][True], # Flip ON - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][True], # Flip ON + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) + # Smooth + def test_smooth(): instance = printer.Dummy() instance.set(smooth=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][True], # Smooth ON - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][True], # Smooth ON + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert(instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) # Type @@ -146,17 +158,18 @@ def test_set_bold(): instance.set(bold=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][True], # Bold ON - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][True], # Bold ON + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) def test_set_underline(): @@ -164,17 +177,18 @@ def test_set_underline(): instance.set(underline=1) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][1], # Underline ON, type 1 - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][1], # Underline ON, type 1 + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) def test_set_underline2(): @@ -182,59 +196,64 @@ def test_set_underline2(): instance.set(underline=2) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][2], # Underline ON, type 2 - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][2], # Underline ON, type 2 + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][False], # Inverted OFF ) - assert (instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) # Align + def test_align_center(): instance = printer.Dummy() - instance.set(align='center') + instance.set(align="center") expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['center'], # Align center - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["center"], # Align center + TXT_STYLE["invert"][False], # Inverted OFF ) - assert(instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) def test_align_right(): instance = printer.Dummy() - instance.set(align='right') + instance.set(align="right") expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['right'], # Align right - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["right"], # Align right + TXT_STYLE["invert"][False], # Inverted OFF ) - assert(instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) # Densities + def test_densities(): for density in range(8): @@ -242,35 +261,38 @@ def test_densities(): instance.set(density=density) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['density'][density], # Custom density from 0 to 8 - TXT_STYLE['invert'][False] # Inverted OFF + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["density"][density], # Custom density from 0 to 8 + TXT_STYLE["invert"][False], # Inverted OFF ) - assert(instance.output == b''.join(expected_sequence)) + assert instance.output == b"".join(expected_sequence) # Invert + def test_invert(): instance = printer.Dummy() instance.set(invert=True) expected_sequence = ( - TXT_NORMAL, TXT_STYLE['size']['normal'], # Normal text size - TXT_STYLE['flip'][False], # Flip OFF - TXT_STYLE['smooth'][False], # Smooth OFF - TXT_STYLE['bold'][False], # Bold OFF - TXT_STYLE['underline'][0], # Underline OFF - SET_FONT(b'\x00'), # Default font - TXT_STYLE['align']['left'], # Align left - TXT_STYLE['invert'][True] # Inverted ON + TXT_NORMAL, + TXT_STYLE["size"]["normal"], # Normal text size + TXT_STYLE["flip"][False], # Flip OFF + TXT_STYLE["smooth"][False], # Smooth OFF + TXT_STYLE["bold"][False], # Bold OFF + TXT_STYLE["underline"][0], # Underline OFF + SET_FONT(b"\x00"), # Default font + TXT_STYLE["align"]["left"], # Align left + TXT_STYLE["invert"][True], # Inverted ON ) - assert(instance.output == b''.join(expected_sequence)) \ No newline at end of file + assert instance.output == b"".join(expected_sequence) diff --git a/test/test_function_softbarcode.py b/test/test_function_softbarcode.py index 78ae5bc..2cbef17 100644 --- a/test/test_function_softbarcode.py +++ b/test/test_function_softbarcode.py @@ -9,11 +9,13 @@ import pytest def instance(): return printer.Dummy() + def test_soft_barcode_ean8_invalid(instance): """test with an invalid barcode""" with pytest.raises(barcode.errors.BarcodeError): instance.soft_barcode("ean8", "1234") + def test_soft_barcode_ean8(instance): """test with a valid ean8 barcode""" instance.soft_barcode("ean8", "1234567") diff --git a/test/test_function_text.py b/test/test_function_text.py index 9379902..8308536 100644 --- a/test/test_function_text.py +++ b/test/test_function_text.py @@ -16,13 +16,12 @@ from escpos.printer import Dummy def get_printer(): - return Dummy(magic_encode_args={'disabled': True, 'encoding': 'CP437'}) + return Dummy(magic_encode_args={"disabled": True, "encoding": "CP437"}) @given(text=st.text()) def test_text(text): - """Test that text() calls the MagicEncode object. - """ + """Test that text() calls the MagicEncode object.""" instance = get_printer() instance.magic.write = mock.Mock() instance.text(text) @@ -32,30 +31,32 @@ def test_text(text): def test_block_text(): printer = get_printer() printer.block_text( - "All the presidents men were eating falafel for breakfast.", font='a') - assert printer.output == \ - b'All the presidents men were eating falafel\nfor breakfast.' + "All the presidents men were eating falafel for breakfast.", font="a" + ) + assert ( + printer.output == b"All the presidents men were eating falafel\nfor breakfast." + ) def test_textln(): printer = get_printer() - printer.textln('hello, world') - assert printer.output == b'hello, world\n' + printer.textln("hello, world") + assert printer.output == b"hello, world\n" def test_textln_empty(): printer = get_printer() printer.textln() - assert printer.output == b'\n' + assert printer.output == b"\n" def test_ln(): printer = get_printer() printer.ln() - assert printer.output == b'\n' + assert printer.output == b"\n" def test_multiple_ln(): printer = get_printer() printer.ln(3) - assert printer.output == b'\n\n\n' + assert printer.output == b"\n\n\n" diff --git a/test/test_functions.py b/test/test_functions.py index 96b923a..1b58ca5 100644 --- a/test/test_functions.py +++ b/test/test_functions.py @@ -5,13 +5,13 @@ from escpos.printer import Dummy def test_line_spacing_code_gen(): printer = Dummy() printer.line_spacing(10) - assert printer.output == b'\x1b3\n' + assert printer.output == b"\x1b3\n" def test_line_spacing_rest(): printer = Dummy() printer.line_spacing() - assert printer.output == b'\x1b2' + assert printer.output == b"\x1b2" def test_line_spacing_error_handling(): diff --git a/test/test_image.py b/test/test_image.py index 4a9e8b0..258e4f1 100644 --- a/test/test_image.py +++ b/test/test_image.py @@ -15,57 +15,67 @@ def test_image_black(): """ Test rendering solid black image """ - for img_format in ['png', 'jpg', 'gif']: - _load_and_check_img('canvas_black.' + img_format, 1, 1, b'\x80', [b'\x80']) + for img_format in ["png", "jpg", "gif"]: + _load_and_check_img("canvas_black." + img_format, 1, 1, b"\x80", [b"\x80"]) def test_image_black_transparent(): """ Test rendering black/transparent image """ - for img_format in ['png', 'gif']: - _load_and_check_img('black_transparent.' + img_format, 2, 2, b'\xc0\x00', [b'\x80\x80']) + for img_format in ["png", "gif"]: + _load_and_check_img( + "black_transparent." + img_format, 2, 2, b"\xc0\x00", [b"\x80\x80"] + ) def test_image_black_white(): """ Test rendering black/white image """ - for img_format in ['png', 'jpg', 'gif']: - _load_and_check_img('black_white.' + img_format, 2, 2, b'\xc0\x00', [b'\x80\x80']) + for img_format in ["png", "jpg", "gif"]: + _load_and_check_img( + "black_white." + img_format, 2, 2, b"\xc0\x00", [b"\x80\x80"] + ) def test_image_white(): """ Test rendering solid white image """ - for img_format in ['png', 'jpg', 'gif']: - _load_and_check_img('canvas_white.' + img_format, 1, 1, b'\x00', [b'\x00']) + for img_format in ["png", "jpg", "gif"]: + _load_and_check_img("canvas_white." + img_format, 1, 1, b"\x00", [b"\x00"]) def test_split(): """ test whether the split-function works as expected """ - im = EscposImage('test/resources/black_white.png') + im = EscposImage("test/resources/black_white.png") (upper_part, lower_part) = im.split(1) upper_part = EscposImage(upper_part) lower_part = EscposImage(lower_part) - assert(upper_part.width == lower_part.width == 2) - assert(upper_part.height == lower_part.height == 1) - assert(upper_part.to_raster_format() == b'\xc0') - assert(lower_part.to_raster_format() == b'\x00') + assert upper_part.width == lower_part.width == 2 + assert upper_part.height == lower_part.height == 1 + assert upper_part.to_raster_format() == b"\xc0" + assert lower_part.to_raster_format() == b"\x00" -def _load_and_check_img(filename, width_expected, height_expected, raster_format_expected, column_format_expected): +def _load_and_check_img( + filename, + width_expected, + height_expected, + raster_format_expected, + column_format_expected, +): """ Load an image, and test whether raster & column formatted output, sizes, etc match expectations. """ - im = EscposImage('test/resources/' + filename) - assert(im.width == width_expected) - assert(im.height == height_expected) - assert(im.to_raster_format() == raster_format_expected) - i = 0 + im = EscposImage("test/resources/" + filename) + assert im.width == width_expected + assert im.height == height_expected + assert im.to_raster_format() == raster_format_expected + i = 0 for row in im.to_column_format(False): - assert(row == column_format_expected[i]) + assert row == column_format_expected[i] i += 1 diff --git a/test/test_load_module.py b/test/test_load_module.py index a14d99c..b5daa70 100644 --- a/test/test_load_module.py +++ b/test/test_load_module.py @@ -14,4 +14,4 @@ import escpos.printer as printer def test_instantiation(): """test the instantiation of a escpos-printer class and basic printing""" instance = printer.Dummy() - instance.text('This is a test\n') + instance.text("This is a test\n") diff --git a/test/test_magicencode.py b/test/test_magicencode.py index 10c6171..f45e1f0 100644 --- a/test/test_magicencode.py +++ b/test/test_magicencode.py @@ -24,17 +24,17 @@ class TestEncoder: """ def test_can_encode(self): - assert not Encoder({'CP437': 1}).can_encode('CP437', u'€') - assert Encoder({'CP437': 1}).can_encode('CP437', u'á') - assert not Encoder({'foobar': 1}).can_encode('foobar', 'a') + assert not Encoder({"CP437": 1}).can_encode("CP437", u"€") + assert Encoder({"CP437": 1}).can_encode("CP437", u"á") + assert not Encoder({"foobar": 1}).can_encode("foobar", "a") def test_find_suitable_encoding(self): - assert not Encoder({'CP437': 1}).find_suitable_encoding(u'€') - assert Encoder({'CP858': 1}).find_suitable_encoding(u'€') == 'CP858' + assert not Encoder({"CP437": 1}).find_suitable_encoding(u"€") + assert Encoder({"CP858": 1}).find_suitable_encoding(u"€") == "CP858" @raises(ValueError) def test_get_encoding(self): - Encoder({}).get_encoding_name('latin1') + Encoder({}).get_encoding_name("latin1") class TestMagicEncode: @@ -57,50 +57,50 @@ class TestMagicEncode: MagicEncode(driver, disabled=True) class TestWriteWithEncoding: - def test_init_from_none(self, driver): encode = MagicEncode(driver, encoding=None) - encode.write_with_encoding('CP858', '€ ist teuro.') - assert driver.output == b'\x1bt\x13\xd5 ist teuro.' + encode.write_with_encoding("CP858", "€ ist teuro.") + assert driver.output == b"\x1bt\x13\xd5 ist teuro." def test_change_from_another(self, driver): - encode = MagicEncode(driver, encoding='CP437') - encode.write_with_encoding('CP858', '€ ist teuro.') - assert driver.output == b'\x1bt\x13\xd5 ist teuro.' + encode = MagicEncode(driver, encoding="CP437") + encode.write_with_encoding("CP858", "€ ist teuro.") + assert driver.output == b"\x1bt\x13\xd5 ist teuro." def test_no_change(self, driver): - encode = MagicEncode(driver, encoding='CP858') - encode.write_with_encoding('CP858', '€ ist teuro.') - assert driver.output == b'\xd5 ist teuro.' + encode = MagicEncode(driver, encoding="CP858") + encode.write_with_encoding("CP858", "€ ist teuro.") + assert driver.output == b"\xd5 ist teuro." class TestWrite: - def test_write(self, driver): encode = MagicEncode(driver) - encode.write('€ ist teuro.') - assert driver.output == b'\x1bt\x0f\xa4 ist teuro.' + encode.write("€ ist teuro.") + assert driver.output == b"\x1bt\x0f\xa4 ist teuro." def test_write_disabled(self, driver): - encode = MagicEncode(driver, encoding='CP437', disabled=True) - encode.write('€ ist teuro.') - assert driver.output == b'? ist teuro.' + encode = MagicEncode(driver, encoding="CP437", disabled=True) + encode.write("€ ist teuro.") + assert driver.output == b"? ist teuro." def test_write_no_codepage(self, driver): encode = MagicEncode( - driver, defaultsymbol="_", encoder=Encoder({'CP437': 1}), - encoding='CP437') - encode.write(u'€ ist teuro.') - assert driver.output == b'_ ist teuro.' + driver, + defaultsymbol="_", + encoder=Encoder({"CP437": 1}), + encoding="CP437", + ) + encode.write(u"€ ist teuro.") + assert driver.output == b"_ ist teuro." class TestForceEncoding: - def test(self, driver): encode = MagicEncode(driver) - encode.force_encoding('CP437') - assert driver.output == b'\x1bt\x00' + encode.force_encoding("CP437") + assert driver.output == b"\x1bt\x00" - encode.write('€ ist teuro.') - assert driver.output == b'\x1bt\x00? ist teuro.' + encode.write("€ ist teuro.") + assert driver.output == b"\x1bt\x00? ist teuro." try: @@ -119,5 +119,5 @@ class TestKatakana: encode_katakana(text) def test_result(self): - assert encode_katakana('カタカナ') == b'\xb6\xc0\xb6\xc5' - assert encode_katakana("あいうえお") == b'\xb1\xb2\xb3\xb4\xb5' + assert encode_katakana("カタカナ") == b"\xb6\xc0\xb6\xc5" + assert encode_katakana("あいうえお") == b"\xb1\xb2\xb3\xb4\xb5" diff --git a/test/test_printer_file.py b/test/test_printer_file.py index 6cf3c5f..b7c89e4 100644 --- a/test/test_printer_file.py +++ b/test/test_printer_file.py @@ -18,16 +18,16 @@ from hypothesis.strategies import text import escpos.printer as printer if six.PY3: - mock_open_call = 'builtins.open' + mock_open_call = "builtins.open" else: - mock_open_call = '__builtin__.open' + mock_open_call = "__builtin__.open" @pytest.mark.skip("this test is broken and has to be fixed or discarded") @given(path=text()) def test_load_file_printer(mocker, path): """test the loading of the file-printer""" - mock_escpos = mocker.patch('escpos.escpos.Escpos.__init__') + mock_escpos = mocker.patch("escpos.escpos.Escpos.__init__") mock_open = mocker.patch(mock_open_call) printer.File(devfile=path) assert mock_escpos.called @@ -38,9 +38,9 @@ def test_load_file_printer(mocker, path): @given(txt=text()) def test_auto_flush(mocker, txt): """test auto_flush in file-printer""" - mock_escpos = mocker.patch('escpos.escpos.Escpos.__init__') + mock_escpos = mocker.patch("escpos.escpos.Escpos.__init__") mock_open = mocker.patch(mock_open_call) - mock_device = mocker.patch.object(printer.File, 'device') + mock_device = mocker.patch.object(printer.File, "device") p = printer.File(auto_flush=False) # inject the mocked device-object @@ -60,7 +60,7 @@ def test_auto_flush(mocker, txt): def test_flush_on_close(mocker, txt): """test flush on close in file-printer""" mock_open = mocker.patch(mock_open_call) - mock_device = mocker.patch.object(printer.File, 'device') + mock_device = mocker.patch.object(printer.File, "device") p = printer.File(auto_flush=False) # inject the mocked device-object diff --git a/test/test_printer_network.py b/test/test_printer_network.py index 0669653..a1fada1 100644 --- a/test/test_printer_network.py +++ b/test/test_printer_network.py @@ -11,6 +11,7 @@ def instance(): socket.socket.connect = mock.Mock() return printer.Network("localhost") + def test_close_without_open(instance): """try to close without opening (should fail gracefully) @@ -20,4 +21,4 @@ def test_close_without_open(instance): (if possible, this should not raise) """ instance.close() - instance.close() \ No newline at end of file + instance.close() diff --git a/test/test_profile.py b/test/test_profile.py index 5f4fa4b..7793cb5 100644 --- a/test/test_profile.py +++ b/test/test_profile.py @@ -4,35 +4,33 @@ from escpos.capabilities import get_profile, NotSupported, BARCODE_B, Profile @pytest.fixture def profile(): - return get_profile('default') + return get_profile("default") class TestBaseProfile: - """Test the `BaseProfile` class. - """ + """Test the `BaseProfile` class.""" def test_get_font(self, profile): with pytest.raises(NotSupported): - assert profile.get_font('3') + assert profile.get_font("3") assert profile.get_font(1) == 1 - assert profile.get_font('a') == 0 + assert profile.get_font("a") == 0 def test_supports(self, profile): - assert not profile.supports('asdf asdf') + assert not profile.supports("asdf asdf") assert profile.supports(BARCODE_B) def test_get_columns(self, profile): - assert profile.get_columns('a') > 5 + assert profile.get_columns("a") > 5 with pytest.raises(NotSupported): - assert profile.get_columns('asdfasdf') + assert profile.get_columns("asdfasdf") class TestCustomProfile: - """Test custom profile options with the `Profile` class. - """ + """Test custom profile options with the `Profile` class.""" def test_columns(self): - assert Profile(columns=10).get_columns('sdfasdf') == 10 + assert Profile(columns=10).get_columns("sdfasdf") == 10 def test_features(self): - assert Profile(features={'foo': True}).supports('foo') + assert Profile(features={"foo": True}).supports("foo") diff --git a/test/test_with_statement.py b/test/test_with_statement.py index f7a8410..8891d12 100644 --- a/test/test_with_statement.py +++ b/test/test_with_statement.py @@ -16,5 +16,5 @@ def test_with_statement(): """Use with statement""" dummy_printer = printer.Dummy() with escpos.EscposIO(dummy_printer) as p: - p.writelines('Some text.\n') + p.writelines("Some text.\n") # TODO extend these tests as they don't really do anything at the moment From 5bb0642b5d4557ad5c2f2e2286f15557acb8c83f Mon Sep 17 00:00:00 2001 From: Patrick Kanzler <4189642+patkan@users.noreply.github.com> Date: Sat, 30 Oct 2021 22:21:34 +0200 Subject: [PATCH 65/68] housekeeping tasks (#464) * update settings to use black * update python versions * enquote version numbers * update dependency * set explicit build command * add newline * add command to checkout * add run step * chain * sudo * test * newer sphinx version * update sphinx * clean up setuptools usage * use tox * install tox * tox --- .github/workflows/documentation.yml | 11 +++++++---- .github/workflows/pythonpackage.yml | 2 +- .vscode/settings.json | 21 ++++++++++++--------- doc/conf.py | 15 ++++----------- doc/requirements.txt | 2 +- setup.cfg | 3 ++- tox.ini | 9 ++++++--- 7 files changed, 33 insertions(+), 30 deletions(-) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 81be6a9..f74a954 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -22,7 +22,10 @@ jobs: - uses: actions/checkout@v2.3.5 with: submodules: 'recursive' - - uses: ammaraskar/sphinx-action@0.4 - with: - docs-folder: "doc/" - pre-build-command: "apt-get update -y && apt-get install -y git python3-sphinx graphviz libenchant1c2a" \ No newline at end of file + - name: Install packages + run: + sudo apt-get update -y && + sudo apt-get install -y git python3-sphinx graphviz libenchant1c2a && + sudo pip install tox + - name: Test doc build + run: tox -e docs diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index b4719a5..fc46967 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v2.3.5 diff --git a/.vscode/settings.json b/.vscode/settings.json index 32ab8c0..ca826d1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,14 @@ { - "restructuredtext.confPath": "${workspaceFolder}/doc", - "files.watcherExclude": { - "**/.git/objects/**": true, - "**/.git/subtree-cache/**": true, - "**/node_modules/*/**": true, - "**/.eggs/**": true, - "**/.hypothesis/**": true, - "**/.tox/**": true, - } + "restructuredtext.confPath": "${workspaceFolder}/doc", + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/node_modules/*/**": true, + "**/.eggs/**": true, + "**/.hypothesis/**": true, + "**/.tox/**": true, + }, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "python.formatting.provider": "black", } \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py index e81e20b..24d0b05 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -15,11 +15,9 @@ import sys import os +from importlib.metadata import version as imp_version + on_rtd = os.getenv("READTHEDOCS") == "True" -if on_rtd: - import escpos -else: - from setuptools_scm import get_version # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -75,14 +73,9 @@ copyright = u"2016, Manuel F Martinez and others" # |version| and |release|, also used in various other places throughout the # built documents. # -if on_rtd: - # The full version, including alpha/beta/rc tags. - release = escpos.__version__ -else: - # locally setuptools_scm should work - release = get_version(root=root) +release = imp_version("python-escpos") # The short X.Y version. -version = ".".join(release.split(".")[:2]) # The short X.Y version. +version = ".".join(release.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/requirements.txt b/doc/requirements.txt index 728e22d..09e59da 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -6,5 +6,5 @@ sphinx-rtd-theme setuptools setuptools-scm docutils>=0.12 -sphinxcontrib-spelling>=5 +sphinxcontrib-spelling>=7.2.0 python-barcode>=0.11.0,<1 diff --git a/setup.cfg b/setup.cfg index 3423563..ef78c82 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,6 +22,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Programming Language :: Python :: Implementation :: CPython Topic :: Software Development :: Libraries :: Python Modules Topic :: Office/Business :: Financial :: Point-Of-Sale @@ -59,7 +60,7 @@ tests_require = mock hypothesis>4 flake8 - sphinxcontrib-spelling>=5 + sphinxcontrib-spelling>=7.2.0 [nosetests] verbosity=3 diff --git a/tox.ini b/tox.ini index 1501eb8..ea27473 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py35, py36, py37, py38, docs, flake8 +envlist = py35, py36, py37, py38, py39, py310, docs, flake8 [gh-actions] python = @@ -7,6 +7,8 @@ python = 3.6: py36 3.7: py37 3.8: py38 + 3.9: py39 + 3.10: py310 [testenv] deps = nose @@ -25,10 +27,11 @@ passenv = ESCPOS_CAPABILITIES_PICKLE_DIR ESCPOS_CAPABILITIES_FILE CI TRAVIS TRAV [testenv:docs] basepython = python changedir = doc -deps = sphinx>=1.5.1 +deps = sphinx>=3.0.0 setuptools_scm python-barcode - sphinxcontrib-spelling>=5 + sphinxcontrib-spelling>=7.2.0 + sphinx_rtd_theme commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html [testenv:flake8] From b16f44257e7318060fbd0c2bf548239947734359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Nov 2021 22:52:04 +0100 Subject: [PATCH 66/68] Bump actions/setup-python from 2.2.2 to 2.3.1 (#468) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.2.2 to 2.3.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.2.2...v2.3.1) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index fc46967..4ff3de6 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v2.3.1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 219b43d35ae65fd33e6c2f84b8255103144f1f4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:40:09 +0200 Subject: [PATCH 67/68] Bump actions/setup-python from 2.3.1 to 3.1.1 (#479) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2.3.1 to 3.1.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2.3.1...v3.1.1) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pythonpackage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 4ff3de6..b79db24 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,7 +22,7 @@ jobs: with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.3.1 + uses: actions/setup-python@v3.1.1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 5e80764ff525d05fc8028b12866e859bbda136a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:51:19 +0200 Subject: [PATCH 68/68] Bump actions/checkout from 2 to 3 (#477) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/black.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/documentation.yml | 2 +- .github/workflows/pythonpackage.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index b04fb15..9065b5e 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -6,5 +6,5 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: psf/black@stable diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c390002..2ec1833 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2.3.5 + uses: actions/checkout@v3 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f74a954..3b44bb6 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -19,7 +19,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: - - uses: actions/checkout@v2.3.5 + - uses: actions/checkout@v3 with: submodules: 'recursive' - name: Install packages diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index b79db24..43d282c 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -18,7 +18,7 @@ jobs: python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] steps: - - uses: actions/checkout@v2.3.5 + - uses: actions/checkout@v3 with: submodules: 'recursive' - name: Set up Python ${{ matrix.python-version }}