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

5 Commits

Author SHA1 Message Date
Gertjan van den Burg
11d46fdb66 Make logging.basicConfig conditional (#670)
Co-authored-by: Patrick Kanzler <4189642+patkan@users.noreply.github.com>
2025-08-25 01:36:17 +02:00
Hasan Sezer Taşan
1a780e8f80 ref(package) replace appdirs with platformdirs in configuration and requirements files (#697)
* ref(package) replace appdirs with platformdirs in configuration and requirements files

* ref: remove types-appdirs from dependencies in tox.ini
2025-08-25 01:30:25 +02:00
dependabot[bot]
c702204231 Bump actions/checkout from 4 to 5 (#694)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 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/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-25 01:22:31 +02:00
Benito López
00d3a1301f Switch to python 3.13 (#693)
Co-authored-by: Patrick Kanzler <4189642+patkan@users.noreply.github.com>
2025-08-11 01:25:11 +02:00
Benito López
ebd6f88acf Add justify to text alignment of software_columns() Fixes #689 (#690)
* Add method: justify

* Add justify test

* Add another justify test

* Please the linter

* Allow single-item text_list in _rearrange_into_cols()

* Add parameter checks to software_columns

* Test for specific errors
2025-08-11 01:21:29 +02:00
13 changed files with 49 additions and 25 deletions

View File

@@ -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"

View File

@@ -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.

View File

@@ -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

View File

@@ -12,10 +12,10 @@ 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 }}

View File

@@ -15,10 +15,10 @@ 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 }}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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
) )

View 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

View File

@@ -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