Merge pull request #298 from python-escpos/development

release v3.0a4
This commit is contained in:
Patrick Kanzler 2018-05-15 23:12:17 +02:00 committed by GitHub
commit 0051c876bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 141 additions and 64 deletions

1
.gitignore vendored
View File

@ -20,6 +20,7 @@ dist/
.coverage .coverage
src/escpos/version.py src/escpos/version.py
.hypothesis .hypothesis
.pytest_cache/
# testing temporary directories # testing temporary directories
test/test-cli-output/ test/test-cli-output/

1
.gitmodules vendored
View File

@ -1,3 +1,4 @@
[submodule "capabilities-data"] [submodule "capabilities-data"]
path = capabilities-data path = capabilities-data
url = https://github.com/receipt-print-hq/escpos-printer-db.git url = https://github.com/receipt-print-hq/escpos-printer-db.git
branch = master

View File

@ -8,5 +8,7 @@ Cody (Quantified Code Bot) <cody@quantifiedcode.com> Cody <cody@quantifiedcode.c
Renato Lorenzi <renato.lorenzi@senior.com.br> Renato.Lorenzi <renato.lorenzi@senior.com.br> Renato Lorenzi <renato.lorenzi@senior.com.br> Renato.Lorenzi <renato.lorenzi@senior.com.br>
Ahmed Tahri <nyuubi.10@gmail.com> TAHRI Ahmed <nyuubi.10@gmail.com> Ahmed Tahri <nyuubi.10@gmail.com> TAHRI Ahmed <nyuubi.10@gmail.com>
Michael Elsdörfer <michael@elsdoerfer.com> Michael Elsdörfer <michael@elsdoerfer.info> Michael Elsdörfer <michael@elsdoerfer.com> Michael Elsdörfer <michael@elsdoerfer.info>
Juanmi Taboada <juanmi@juanmitaboada.com> Juanmi Taboada <juanmi@juanmitaboada.com>
csoft2k <csoft2k@hotmail.com> csoft2k <csoft2k@hotmail.com>
Sergio Pulgarin <sergio.pulgarin@gmail.com> Sergio Pulgarin <sergio.pulgarin@gmail.com>
reck31 <rakesh.gunduka@gmail.com>

View File

@ -7,12 +7,13 @@ addons:
apt: apt:
packages: packages:
- graphviz - graphviz
env:
global:
- ESCPOS_CAPABILITIES_FILE=/home/travis/build/python-escpos/python-escpos/capabilities-data/dist/capabilities.json
matrix: matrix:
include: include:
- python: 2.7 - python: 2.7
env: TOXENV=py27 env: TOXENV=py27
- python: 3.3
env: TOXENV=py33
- python: 3.4 - python: 3.4
env: TOXENV=py34 env: TOXENV=py34
- python: 3.5 - python: 3.5
@ -21,6 +22,8 @@ matrix:
env: TOXENV=py36 env: TOXENV=py36
- python: 3.6-dev - python: 3.6-dev
env: TOXENV=py36 env: TOXENV=py36
- python: 3.7-dev
env: TOXENV=py37
- python: nightly - python: nightly
env: TOXENV=py37 env: TOXENV=py37
- python: pypy - python: pypy
@ -35,6 +38,7 @@ matrix:
env: TOXENV=flake8 env: TOXENV=flake8
allow_failures: allow_failures:
- python: 3.6-dev - python: 3.6-dev
- python: 3.7-dev
- python: nightly - python: nightly
- python: pypy3 - python: pypy3
before_install: before_install:
@ -59,4 +63,4 @@ deploy:
tags: true tags: true
repo: python-escpos/python-escpos repo: python-escpos/python-escpos
branch: master branch: master
condition: $TRAVIS_PYTHON_VERSION = "3.5" condition: $TRAVIS_PYTHON_VERSION = "3.6"

View File

@ -10,6 +10,7 @@ Dean Rispin
Dmytro Katyukha Dmytro Katyukha
Hark Hark
Joel Lehtonen Joel Lehtonen
kennedy
Kristi Kristi
ldos ldos
Lucy Linder Lucy Linder
@ -19,7 +20,9 @@ Michael Elsdörfer
mrwunderbar666 mrwunderbar666
Nathan Bookham Nathan Bookham
Patrick Kanzler Patrick Kanzler
primax79
Qian Linfeng Qian Linfeng
reck31
Renato Lorenzi Renato Lorenzi
Romain Porte Romain Porte
Sam Cheng Sam Cheng

View File

@ -1,6 +1,30 @@
********* *********
Changelog Changelog
********* *********
2018-05-15 - Version 3.0a4 - "Kakistocrat"
------------------------------------------
This release is the fifth alpha release of the new version 3.0. Please
be aware that the API will still change until v3.0 is released.
changes
^^^^^^^
- raise exception when TypeError occurs in cashdraw (#268)
- Feature/clear content in dummy printer (#271)
- fix is_online() (#282)
- improve documentation
- Modified submodule to always pull from master branch (#283)
- parameter for implementation of nonnative qrcode (#289)
- improve platform independence (#296)
contributors
^^^^^^^^^^^^
- Christoph Heuel
- Patrick Kanzler
- kennedy
- primax79
- reck31
- Thijs Triemstra
2017-10-08 - Version 3.0a3 - "Just Testing" 2017-10-08 - Version 3.0a3 - "Just Testing"
------------------------------------------- -------------------------------------------
This release is the fourth alpha release of the new version 3.0. Please This release is the fourth alpha release of the new version 3.0. Please

@ -1 +1 @@
Subproject commit fd207aa9debc9671405226598a086b282f9f3ad5 Subproject commit 8885283d71a43758aa63d633fd2301df13dce9d5

View File

@ -3,7 +3,6 @@
import os import os
import sys import sys
from setuptools import find_packages, setup from setuptools import find_packages, setup
from setuptools.command.test import test as test_command
base_dir = os.path.dirname(__file__) base_dir = os.path.dirname(__file__)
@ -19,33 +18,6 @@ def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read() return open(os.path.join(os.path.dirname(__file__), fname)).read()
class Tox(test_command):
"""proxy class that enables tox to be run with setup.py test"""
user_options = [('tox-args=', 'a', "Arguments to pass to tox")]
def initialize_options(self):
"""initialize the user-options"""
test_command.initialize_options(self)
self.tox_args = None
def finalize_options(self):
"""finalize user-options"""
test_command.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
"""run tox and pass on user-options"""
# import here, cause outside the eggs aren't loaded
import tox
import shlex
args = self.tox_args
if args:
args = shlex.split(self.tox_args)
errno = tox.cmdline(args=args)
sys.exit(errno)
setuptools_scm_template = """\ setuptools_scm_template = """\
# coding: utf-8 # coding: utf-8
# file generated by setuptools_scm # file generated by setuptools_scm
@ -96,7 +68,6 @@ setup(
'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
@ -125,16 +96,15 @@ setup(
tests_require=[ tests_require=[
'jaconv', 'jaconv',
'tox', 'tox',
'pytest!=3.2.0', 'pytest!=3.2.0,!=3.3.0',
'pytest-cov', 'pytest-cov',
'pytest-mock', 'pytest-mock',
'nose', 'nose',
'scripttest', 'scripttest',
'mock', 'mock',
'hypothesis', 'hypothesis!=3.56.9',
'flake8' 'flake8'
], ],
cmdclass={'test': Tox},
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'python-escpos = escpos.cli:main' 'python-escpos = escpos.cli:main'

View File

@ -7,13 +7,14 @@ import time
import six import six
import yaml import yaml
from tempfile import gettempdir
import platform
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', gettempdir())
pickle_dir = environ.get('ESCPOS_CAPABILITIES_PICKLE_DIR', '/tmp/') pickle_path = path.join(pickle_dir, '{v}.capabilities.pickle'.format(v=platform.python_version()))
pickle_path = path.join(pickle_dir, 'capabilities.pickle')
capabilities_path = environ.get( capabilities_path = environ.get(
'ESCPOS_CAPABILITIES_FILE', 'ESCPOS_CAPABILITIES_FILE',
path.join(path.dirname(__file__), 'capabilities.json')) path.join(path.dirname(__file__), 'capabilities.json'))
@ -47,7 +48,7 @@ PROFILES = CAPABILITIES['profiles']
class NotSupported(Exception): class NotSupported(Exception):
"""Raised if a requested feature is not suppored by the """Raised if a requested feature is not supported by the
printer profile. printer profile.
""" """
pass pass
@ -90,7 +91,7 @@ class BaseProfile(object):
return self.features.get(feature) return self.features.get(feature)
def get_code_pages(self): 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()} return {v: k for k, v in self.codePages.items()}

View File

@ -24,6 +24,8 @@ from re import match as re_match
import barcode import barcode
from barcode.writer import ImageWriter 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 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 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_FONT_A, BARCODE_FONT_B, BARCODE_FORMATS
@ -180,7 +182,7 @@ class Escpos(object):
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, def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2,
native=False, center=False): native=False, center=False, impl="bitImageRaster"):
""" Print QR Code for the provided string """ Print QR Code for the provided string
:param content: The content of the code. Numeric data will be more efficiently compacted. :param content: The content of the code. Numeric data will be more efficiently compacted.
@ -222,7 +224,7 @@ class Escpos(object):
# Convert the RGB image in printable image # Convert the RGB image in printable image
self.text('\n') self.text('\n')
self.image(im, center=center) self.image(im, center=center, impl=impl)
self.text('\n') self.text('\n')
self.text('\n') self.text('\n')
return return
@ -487,11 +489,12 @@ class Escpos(object):
barcode_class = barcode.get_barcode_class(barcode_type) barcode_class = barcode.get_barcode_class(barcode_type)
my_code = barcode_class(data, writer=image_writer) my_code = barcode_class(data, writer=image_writer)
my_code.write("/dev/null", { with open(os.devnull, "wb") as nullfile:
'module_height': module_height, my_code.write(nullfile, {
'module_width': module_width, 'module_height': module_height,
'text_distance': text_distance 'module_width': module_width,
}) 'text_distance': text_distance
})
# Retrieve the Pillow image and print it # Retrieve the Pillow image and print it
image = my_code.writer._image image = my_code.writer._image
@ -697,8 +700,8 @@ class Escpos(object):
else: else:
try: try:
self._raw(CD_KICK_DEC_SEQUENCE(*pin)) self._raw(CD_KICK_DEC_SEQUENCE(*pin))
except: except TypeError as err:
raise CashDrawerError() raise CashDrawerError(err)
def linedisplay_select(self, select_display=False): def linedisplay_select(self, select_display=False):
""" Selects the line display or the printer """ Selects the line display or the printer
@ -728,6 +731,7 @@ class Escpos(object):
You should connect a line display to your printer. You can do this by daisy-chaining You should connect a line display to your printer. You can do this by daisy-chaining
the display between your computer and printer. the display between your computer and printer.
:param text: Text to display :param text: Text to display
""" """
self.linedisplay_select(select_display=True) self.linedisplay_select(select_display=True)
@ -827,30 +831,41 @@ class Escpos(object):
self._raw(PANEL_BUTTON_OFF) self._raw(PANEL_BUTTON_OFF)
def query_status(self, mode): def query_status(self, mode):
""" Queries the printer for its status, and returns an array of integers containing it. """
Queries the printer for its status, and returns an array of integers containing it.
:param mode: Integer that sets the status mode queried to the printer. :param mode: Integer that sets the status mode queried to the printer.
RT_STATUS_ONLINE: Printer status. - RT_STATUS_ONLINE: Printer status.
RT_STATUS_PAPER: Paper sensor. - RT_STATUS_PAPER: Paper sensor.
:rtype: array(integer)""" :rtype: array(integer)
"""
self._raw(mode) self._raw(mode)
time.sleep(1) time.sleep(1)
status = self._read() status = self._read()
return status return status
def is_online(self): def is_online(self):
""" Queries the printer its online status. """
When online, returns True; False otherwise. Queries the online status of the printer.
:rtype: bool: True if online, False if offline."""
:returns: When online, returns ``True``; ``False`` otherwise.
:rtype: bool
"""
status = self.query_status(RT_STATUS_ONLINE) status = self.query_status(RT_STATUS_ONLINE)
if len(status) == 0: if len(status) == 0:
return False return False
return not (status & RT_MASK_ONLINE) return not (status[0] & RT_MASK_ONLINE)
def paper_status(self): def paper_status(self):
""" Queries the printer its paper status. """
Queries the paper status of the printer.
Returns 2 if there is plenty of paper, 1 if the paper has arrived to Returns 2 if there is plenty of paper, 1 if the paper has arrived to
the near-end sensor and 0 if there is no paper. the near-end sensor and 0 if there is no paper.
:rtype: int: 2: Paper is adequate. 1: Paper ending. 0: No paper."""
:returns: 2: Paper is adequate. 1: Paper ending. 0: No paper.
:rtype: int
"""
status = self.query_status(RT_STATUS_PAPER) status = self.query_status(RT_STATUS_PAPER)
if len(status) == 0: if len(status) == 0:
return 2 return 2
@ -866,7 +881,7 @@ class EscposIO(object):
"""ESC/POS Printer IO object """ESC/POS Printer IO object
Allows the class to be used together with the `with`-statement. You have to define a printer instance Allows the class to be used together with the `with`-statement. You have to define a printer instance
and assign it to the EsposIO-class. and assign it to the EscposIO class.
This example explains the usage: This example explains the usage:
.. code-block:: Python .. code-block:: Python

View File

@ -312,5 +312,13 @@ class Dummy(Escpos):
""" Get the data that was sent to this printer """ """ Get the data that was sent to this printer """
return b''.join(self._output_list) return b''.join(self._output_list)
def clear(self):
""" 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.
"""
del self._output_list[:]
def close(self): def close(self):
pass pass

View File

@ -0,0 +1,19 @@
#!/usr/bin/python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import escpos.printer as printer
from escpos.exceptions import CashDrawerError
import pytest
def test_raise_CashDrawerError():
"""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])

View File

@ -0,0 +1,8 @@
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'')

View File

@ -82,6 +82,7 @@ def test_invalid_model():
instance.qr("1234", native=True, model="Hello") instance.qr("1234", native=True, model="Hello")
@pytest.mark.skip("this test has to be debugged")
def test_image(): def test_image():
"""Test QR as image""" """Test QR as image"""
instance = printer.Dummy() instance = printer.Dummy()

View File

@ -0,0 +1,16 @@
#!/usr/bin/python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import escpos.printer as printer
import pytest
def test_soft_barcode():
"""just execute soft_barcode
"""
instance = printer.Dummy()
instance.soft_barcode("ean8", "1234")

View File

@ -27,6 +27,7 @@ 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")
@settings(use_coverage=False) @settings(use_coverage=False)
@given(path=text()) @given(path=text())
def test_load_file_printer(mocker, path): def test_load_file_printer(mocker, path):
@ -38,6 +39,7 @@ def test_load_file_printer(mocker, path):
mock_open.assert_called_with(path, "wb") mock_open.assert_called_with(path, "wb")
@pytest.mark.skip("this test is broken and has to be fixed or discarded")
@settings(deadline=None, use_coverage=False) @settings(deadline=None, use_coverage=False)
@given(txt=text()) @given(txt=text())
def test_auto_flush(mocker, txt): def test_auto_flush(mocker, txt):
@ -59,6 +61,7 @@ def test_auto_flush(mocker, txt):
assert mock_device.flush.called assert mock_device.flush.called
@pytest.mark.skip("this test is broken and has to be fixed or discarded")
@settings(deadline=None, use_coverage=False) @settings(deadline=None, use_coverage=False)
@given(txt=text()) @given(txt=text())
def test_flush_on_close(mocker, txt): def test_flush_on_close(mocker, txt):

View File

@ -7,12 +7,13 @@ deps = nose
coverage coverage
scripttest scripttest
mock mock
pytest!=3.2.0 pytest!=3.2.0,!=3.3.0
pytest-cov pytest-cov
pytest-mock pytest-mock
hypothesis hypothesis!=3.56.9
viivakoodi viivakoodi
commands = py.test --cov escpos commands = py.test --cov escpos
passenv = ESCPOS_CAPABILITIES_PICKLE_DIR ESCPOS_CAPABILITIES_FILE CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* CODECOV_*
[testenv:docs] [testenv:docs]
basepython = python basepython = python