Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:

jobs:
linting:
name: Code Quality Checks
name: Code Linting & Formatting
runs-on: ubuntu-latest
steps:
- name: Checkout Code
Expand All @@ -23,4 +23,21 @@ jobs:

- name: Code Formatting
if: always()
run: uv run ruff format --check qrcode
run: uv run ruff format --check qrcode

type-checking:
name: Type Checking (mypy)
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: "3.12"

- name: Type Checking
run: uv run mypy qrcode
104 changes: 95 additions & 9 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,117 @@
name: Testsuite Run

on: [push]
on: [push, pull_request]

jobs:
test:
name: Test Python ${{ matrix.python-version }}
name: Test Python ${{ matrix.python-version }} (${{ matrix.extra }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
extra: [pil, png, none]

steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Test with ${{ matrix.extra }}
run: |
if [ "${{ matrix.extra }}" = "none" ]; then
uv run --group dev pytest --cov=qrcode --cov-report=xml --cov-report=term-missing
else
uv run --extra ${{ matrix.extra }} --group dev pytest --cov=qrcode --cov-report=xml --cov-report=term-missing
fi

- name: Upload coverage to Codecov
if: matrix.python-version == '3.12' && matrix.extra == 'pil'
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

type-check:
name: Type checking (mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Run mypy
run: uv run mypy qrcode/

lint:
name: Linting (ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Run ruff check
run: uv run ruff check qrcode/

build-wheel:
name: Build wheel (packaging verification)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Test with pil
run: uv run --extra pil --group dev pytest
- name: Build wheel and sdist
run: uv build

- name: Test with png
run: uv run --extra png --group dev pytest
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels
path: dist/

- name: Test with none
run: uv run --group dev pytest
docs-build:
name: Build documentation (Sphinx)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"

- run: uv sync --group dev

- run: uv run sphinx-build -b html doc/ doc/_build/html

- name: Upload documentation artifact
uses: actions/upload-artifact@v4
with:
name: sphinx-docs
path: doc/_build/html/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
build/
cov.xml
dist/
doc/_build/
htmlcov/
poetry.lock
uv.lock
Expand Down
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Pre-commit hooks for python-qrcode
# Install: pip install pre-commit && pre-commit install
# Run manually: pre-commit run --all-files

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.14
hooks:
- id: ruff-check
args: [--fix]
exclude: ^qrcode/tests/
- id: ruff-format

- repo: local
hooks:
- id: mypy
name: mypy
entry: .venv/bin/mypy qrcode/
language: system
types: [python]
exclude: ^qrcode/tests/
135 changes: 135 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,141 @@
Changes
=======

.. towncrier-release-announcement-start

## [9.0] — Breaking Changes
============================

Format follows `Keep a Changelog <https://keepachangelog.com/>`_.

**This is a major release with breaking changes.** See the migration guide below.

Removed
-------

- **Deprecated ``embeded_*`` parameters removed** (TASK-36). The misspelled
parameters ``embeded_image_path``, ``embeded_image``, ``embeded_image_ratio``,
and ``embeded_image_resample`` are no longer accepted by
:class:`~qrcode.image.styledpil.StyledPilImage`. Use the correctly spelled
``embedded_*`` variants instead.

- **Deprecated PIL drawer imports removed** (TASK-37). Importing drawers directly
from ``qrcode.image.styles.moduledrawers`` is no longer supported. Import from
the submodule instead::

# Old (removed in v9.0)
from qrcode.image.styles.moduledrawers import SquareModuleDrawer

# New (required since v9.0)
from qrcode.image.styles.moduledrawers.pil import SquareModuleDrawer

- **Deprecated ``draw_embeded_image()`` method removed** (TASK-38). The misspelled
alias on :class:`~qrcode.image.styledpil.StyledPilImage` has been removed.
Use :meth:`~qrcode.image.styledpil.StyledPilImage.draw_embedded_image` instead.

Changed
-------

- ``QRCode.print_ascii()`` now accepts an ``out`` parameter for writing to a
custom text stream and restored the original CP437 block-character rendering.
- ``QRCode.print_tty()`` method restored with ANSI terminal escape code output.

Migration Guide
---------------

1. **Replace misspelled parameters:**

.. code-block:: python

# Before (v8.x)
StyledPilImage(embeded_image_path="logo.png")

# After (v9.0+)
StyledPilImage(embedded_image_path="logo.png")

2. **Update drawer imports:**

.. code-block:: python

# Before (v8.x)
from qrcode.image.styles.moduledrawers import SquareModuleDrawer

# After (v9.0+)
from qrcode.image.styles.moduledrawers.pil import SquareModuleDrawer

3. **Update method calls:**

.. code-block:: python

# Before (v8.x)
img.draw_embeded_image()

# After (v9.0+)
img.draw_embedded_image()

.. towncrier-release-announcement-start

[Unreleased]
============

Format follows `Keep a Changelog <https://keepachangelog.com/>`_.

Added
-----

- **py.typed** file for PEP 561 native type stub support (P0)
- Type hints on core modules: ``main.py``, ``util.py``, ``base.py``,
``exceptions.py``, ``image/base.py``, factory subclasses,
``console_scripts.py``, ``colormasks.py``, ``moduledrawers/pil.py`` (P0–P2)
- Google-style docstrings on all public classes and methods in styled image
modules (``QRColorMask`` subclasses, ``QRModuleDrawer`` subclasses) (P1–P2)
- CLI options ``--box-size``, ``--border``, ``--qr-version`` for fine-grained
QR code control from the command line (TASK-23)
- Regression tests with visual determinism checks (``test_visual_determinism.py``) (TASK-12)
- Parametrised combination tests for all mode × error-correction levels
(``test_combinations.py``) (TASK-13)
- Shared pytest fixtures in ``conftest.py`` reused across test modules (TASK-14)
- Custom eyes support via ``eye_patterns`` parameter on ``QRCode`` / image
factories (Issue #237, TASK-15)
- Codecov badge and upload workflow in CI (TASK-18)
- Wheel build job in CI for packaging verification (TASK-26)

Changed
-------

- Refactored ``console_scripts.main()`` into ``_parse_args``, ``_create_qr``,
helper functions — main entry point < 40 lines (TASK-16)
- Improved ``BaseImage`` API documentation with custom factory example (TASK-17)
- CHANGES.rst restructured to semantic changelog format (this change, TASK-24)

Deprecated
----------

- Importing PIL drawers from ``qrcode.image.styles.moduledrawers`` is deprecated;
import directly from ``qrcode.image.styles.moduledrawers.pil`` instead.
Will be removed in v9.0.
- Parameters ``embeded_image`` / ``embeded_image_path`` (typo) are deprecated;
use ``embedded_image`` / ``embedded_image_path``. Will be removed in v9.0.

Fixed
-----

- Thread safety issue in ``bisect_left`` usage (Fixes #421)
- ``ValueError: glog(0)`` when encoding zero-heavy data (Fixes #330)
- Mask evaluation now includes format info, version info, and dark module per
ISO 18004 §7.8.3.1 (#389)
- **``optimal_data_chunks()`` no longer drops short non-matching segments** — data
integrity is preserved when the optimiser splits mixed-type input (TASK-40).
Previously, segments shorter than *minimum* were silently discarded.
- Added ``__all__`` exports to all public modules for explicit API surface (TASK-44)

Security
--------

- No security changes in this release.

.. towncrier-release-announcement-end

Deprecation Warnings
====================

Expand Down
Loading