mirror of
https://github.com/python-escpos/python-escpos
synced 2025-09-13 09:09:58 +00:00
Compare commits
6 Commits
b85d5b907d
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1349ad85bf | ||
![]() |
11d46fdb66 | ||
![]() |
1a780e8f80 | ||
![]() |
c702204231 | ||
![]() |
00d3a1301f | ||
![]() |
ebd6f88acf |
2
.github/workflows/black.yml
vendored
2
.github/workflows/black.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
|||||||
black-code-style:
|
black-code-style:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: psf/black@stable
|
- uses: psf/black@stable
|
||||||
with:
|
with:
|
||||||
version: "23.12.0"
|
version: "23.12.0"
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
|
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
|
6
.github/workflows/pythonpackage-windows.yml
vendored
6
.github/workflows/pythonpackage-windows.yml
vendored
@@ -12,14 +12,14 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.11', '3.12']
|
python-version: ['3.11', '3.12', '3.13']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
6
.github/workflows/pythonpackage.yml
vendored
6
.github/workflows/pythonpackage.yml
vendored
@@ -15,14 +15,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
|
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
appdirs==1.4.4
|
platformdirs==4.3.8
|
||||||
argcomplete==3.0.8
|
argcomplete==3.0.8
|
||||||
blinker==1.6.2
|
blinker==1.6.2
|
||||||
click==8.1.3
|
click==8.1.3
|
||||||
|
@@ -24,6 +24,7 @@ classifiers =
|
|||||||
Programming Language :: Python :: 3.10
|
Programming Language :: Python :: 3.10
|
||||||
Programming Language :: Python :: 3.11
|
Programming Language :: Python :: 3.11
|
||||||
Programming Language :: Python :: 3.12
|
Programming Language :: Python :: 3.12
|
||||||
|
Programming Language :: Python :: 3.13
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
Topic :: Software Development :: Libraries :: Python Modules
|
Topic :: Software Development :: Libraries :: Python Modules
|
||||||
Topic :: Office/Business :: Financial :: Point-Of-Sale
|
Topic :: Office/Business :: Financial :: Point-Of-Sale
|
||||||
@@ -42,7 +43,7 @@ install_requires =
|
|||||||
python-barcode>=0.15.0,<1
|
python-barcode>=0.15.0,<1
|
||||||
setuptools
|
setuptools
|
||||||
six
|
six
|
||||||
appdirs
|
platformdirs
|
||||||
PyYAML
|
PyYAML
|
||||||
argcomplete
|
argcomplete
|
||||||
importlib_resources
|
importlib_resources
|
||||||
|
@@ -13,9 +13,10 @@ from typing import Any, Dict, Optional, Type
|
|||||||
import importlib_resources
|
import importlib_resources
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
if environ.get("ESCPOS_CAPABILITIES_DEBUG", 0):
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
pickle_dir = environ.get("ESCPOS_CAPABILITIES_PICKLE_DIR", mkdtemp())
|
pickle_dir = environ.get("ESCPOS_CAPABILITIES_PICKLE_DIR", mkdtemp())
|
||||||
pickle_path = path.join(pickle_dir, f"{platform.python_version()}.capabilities.pickle")
|
pickle_path = path.join(pickle_dir, f"{platform.python_version()}.capabilities.pickle")
|
||||||
# get a temporary file from importlib_resources if no file is specified in env
|
# get a temporary file from importlib_resources if no file is specified in env
|
||||||
|
@@ -5,7 +5,7 @@ This module contains the implementations of abstract base class :py:class:`Confi
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import appdirs
|
import platformdirs
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from . import exceptions, printer
|
from . import exceptions, printer
|
||||||
@@ -55,7 +55,7 @@ class Config:
|
|||||||
|
|
||||||
if not config_path:
|
if not config_path:
|
||||||
config_path = os.path.join(
|
config_path = os.path.join(
|
||||||
appdirs.user_config_dir(self._app_name), self._config_file
|
platformdirs.user_config_dir(self._app_name), self._config_file
|
||||||
)
|
)
|
||||||
if isinstance(config_path, pathlib.Path):
|
if isinstance(config_path, pathlib.Path):
|
||||||
# store string if posixpath
|
# store string if posixpath
|
||||||
|
@@ -11,6 +11,7 @@ This module contains the abstract base class :py:class:`Escpos`.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
@@ -108,7 +109,7 @@ SW_BARCODE_NAMES = {
|
|||||||
for name in barcode.PROVIDED_BARCODES
|
for name in barcode.PROVIDED_BARCODES
|
||||||
}
|
}
|
||||||
|
|
||||||
Alignment = Union[Literal["center", "left", "right"], str]
|
Alignment = Union[Literal["center", "left", "right", "justify"], str]
|
||||||
|
|
||||||
|
|
||||||
class Escpos(object, metaclass=ABCMeta):
|
class Escpos(object, metaclass=ABCMeta):
|
||||||
@@ -920,7 +921,21 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
self.text(textwrap.fill(txt, col_count))
|
self.text(textwrap.fill(txt, col_count))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
def _justify(txt: str, width: int) -> str:
|
||||||
|
"""Justify-text on left AND right sides by padding spaces.
|
||||||
|
|
||||||
|
code by: Georgina Skibinski https://stackoverflow.com/a/66087666
|
||||||
|
suggested by agordon @https://github.com/python-escpos/python-escpos/pull/652
|
||||||
|
"""
|
||||||
|
prev_txt = txt
|
||||||
|
while (length := width - len(txt)) > 0:
|
||||||
|
txt = re.sub(r"(\s+)", r"\1 ", txt, count=length)
|
||||||
|
if txt == prev_txt:
|
||||||
|
break
|
||||||
|
return txt.rjust(width)
|
||||||
|
|
||||||
def _padding(
|
def _padding(
|
||||||
|
self,
|
||||||
text: str,
|
text: str,
|
||||||
width: int,
|
width: int,
|
||||||
align: Alignment = "center",
|
align: Alignment = "center",
|
||||||
@@ -936,6 +951,10 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
text = f"{text:<{width}}"
|
text = f"{text:<{width}}"
|
||||||
elif align == "right":
|
elif align == "right":
|
||||||
text = f"{text:>{width}}"
|
text = f"{text:>{width}}"
|
||||||
|
elif align == "justify":
|
||||||
|
text = self._justify(text, width)
|
||||||
|
else:
|
||||||
|
raise ValueError("Expected a valid alignment: center|left|right|justify")
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
@@ -972,7 +991,7 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
textwrap.wrap(text, widths[i], break_long_words=False)
|
textwrap.wrap(text, widths[i], break_long_words=False)
|
||||||
for i, text in enumerate(text_list)
|
for i, text in enumerate(text_list)
|
||||||
]
|
]
|
||||||
max_len = max(*[len(text_group) for text_group in wrapped])
|
max_len = max(0, *[len(text_group) for text_group in wrapped])
|
||||||
text_colums = []
|
text_colums = []
|
||||||
for i in range(max_len):
|
for i in range(max_len):
|
||||||
row = ["" for _ in range(n_cols)]
|
row = ["" for _ in range(n_cols)]
|
||||||
@@ -1013,6 +1032,9 @@ class Escpos(object, metaclass=ABCMeta):
|
|||||||
If the list of alignment items is shorter than the list of strings then
|
If the list of alignment items is shorter than the list of strings then
|
||||||
the last alignment of the list will be applied till the last string (column).
|
the last alignment of the list will be applied till the last string (column).
|
||||||
"""
|
"""
|
||||||
|
if not all([text_list, widths, align]):
|
||||||
|
raise TypeError("Value can't be of type None")
|
||||||
|
|
||||||
n_cols = len(text_list)
|
n_cols = len(text_list)
|
||||||
|
|
||||||
if isinstance(widths, int):
|
if isinstance(widths, int):
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
"""
|
"""
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import appdirs
|
import platformdirs
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import escpos.exceptions
|
import escpos.exceptions
|
||||||
@@ -80,7 +80,7 @@ def test_config_load_from_appdir() -> None:
|
|||||||
|
|
||||||
# generate a dummy config
|
# generate a dummy config
|
||||||
config_file = (
|
config_file = (
|
||||||
pathlib.Path(appdirs.user_config_dir(config.Config._app_name))
|
pathlib.Path(platformdirs.user_config_dir(config.Config._app_name))
|
||||||
/ config.Config._config_file
|
/ config.Config._config_file
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -33,11 +33,11 @@ def test_add_padding_into_cols(driver) -> None:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
output = driver._add_padding_into_cols(
|
output = driver._add_padding_into_cols(
|
||||||
text_list=["col1", "col2", "col3"],
|
text_list=["col1", "col2", "col3", "col 4"],
|
||||||
widths=[6, 6, 6],
|
widths=[6, 6, 6, 6],
|
||||||
align=["center", "left", "right"],
|
align=["center", "left", "right", "justify"],
|
||||||
)
|
)
|
||||||
assert output == [" col1 ", "col2 ", " col3"]
|
assert output == [" col1 ", "col2 ", " col3", "col 4"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("text_list", ["", [], None])
|
@pytest.mark.parametrize("text_list", ["", [], None])
|
||||||
@@ -55,7 +55,7 @@ def test_software_columns_invalid_args(driver, text_list, widths, align) -> None
|
|||||||
|
|
||||||
bad_args = [bad_text_list, bad_widths, bad_align]
|
bad_args = [bad_text_list, bad_widths, bad_align]
|
||||||
for kwargs in bad_args:
|
for kwargs in bad_args:
|
||||||
with pytest.raises(Exception):
|
with pytest.raises((TypeError, ValueError)):
|
||||||
driver.software_columns(**kwargs)
|
driver.software_columns(**kwargs)
|
||||||
driver.close()
|
driver.close()
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ def test_software_columns_invalid_args(driver, text_list, widths, align) -> None
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("widths", [[10, 10, 10], [10], 30])
|
@pytest.mark.parametrize("widths", [[10, 10, 10], [10], 30])
|
||||||
@pytest.mark.parametrize("align", [["center", "left", "right"], ["center"], "center"])
|
@pytest.mark.parametrize("align", [["center", "left", "right"], ["center"], "justify"])
|
||||||
def test_software_columns_valid_args(driver, text_list, widths, align) -> None:
|
def test_software_columns_valid_args(driver, text_list, widths, align) -> None:
|
||||||
"""
|
"""
|
||||||
GIVEN a dummy printer object
|
GIVEN a dummy printer object
|
||||||
|
4
tox.ini
4
tox.ini
@@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = py38, py39, py310, py311, docs, flake8
|
envlist = py38, py39, py310, py311, py312, py313, docs, flake8
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
python =
|
python =
|
||||||
@@ -11,6 +11,7 @@ python =
|
|||||||
3.10: py310
|
3.10: py310
|
||||||
3.11: py311
|
3.11: py311
|
||||||
3.12: py312
|
3.12: py312
|
||||||
|
3.13: py313
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = jaconv
|
deps = jaconv
|
||||||
@@ -54,7 +55,6 @@ deps = mypy
|
|||||||
types-six
|
types-six
|
||||||
types-mock
|
types-mock
|
||||||
types-PyYAML
|
types-PyYAML
|
||||||
types-appdirs
|
|
||||||
types-Pillow
|
types-Pillow
|
||||||
types-pyserial
|
types-pyserial
|
||||||
types-pywin32>=306.0.0.6
|
types-pywin32>=306.0.0.6
|
||||||
|
Reference in New Issue
Block a user