Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete coverage for the qrcode package #12675

Merged
merged 5 commits into from
Sep 20, 2024
Merged
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
1 change: 0 additions & 1 deletion pyrightconfig.stricter.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@
"stubs/pywin32",
"stubs/pyxdg",
"stubs/PyYAML",
"stubs/qrcode",
"stubs/redis",
"stubs/reportlab",
"stubs/requests",
Expand Down
11 changes: 11 additions & 0 deletions stubs/qrcode/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ qrcode\.compat\..*
qrcode\.tests
qrcode\.tests\..*

# Stub-only module
qrcode._types

# Parameter "data" has unhelpful default value, which creates a QR code with string "None".
qrcode\.make
qrcode\.main\.make
Expand All @@ -12,5 +15,13 @@ qrcode\.main\.make
# class of the default class assigned to the attribute.
qrcode\.image\..*\.default_drawer_class

# Implementation has marked these methods as abstract without the class
# or its bases deriving from abc.ABCMeta
qrcode\.image\.base\.BaseImage\.(drawrect|new_image|save)

# The implementation sets this attribute to None on the class but instances
# always set this to a PIL image instance.
qrcode\.image\.styles\.moduledrawers\.(pil\.)?CircleModuleDrawer.circle

# Leaked loop counter
qrcode.base.i
3 changes: 2 additions & 1 deletion stubs/qrcode/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
version = "7.4.*"
upstream_repository = "https://github.com/lincolnloop/python-qrcode"
requires = ["types-Pillow>=9.1.0"]

[tool.stubtest]
extras = ["lxml"]
extras = ["pil"]
4 changes: 2 additions & 2 deletions stubs/qrcode/qrcode/LUT.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from _typeshed import Incomplete
from typing import Final

rsPoly_LUT: Incomplete
rsPoly_LUT: Final[dict[int, list[int]]]
16 changes: 14 additions & 2 deletions stubs/qrcode/qrcode/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
from _typeshed import ConvertibleToInt

from qrcode import image as image
from qrcode.constants import (
ERROR_CORRECT_H as ERROR_CORRECT_H,
ERROR_CORRECT_L as ERROR_CORRECT_L,
ERROR_CORRECT_M as ERROR_CORRECT_M,
ERROR_CORRECT_Q as ERROR_CORRECT_Q,
)
from qrcode.main import make as make
from qrcode.main import GenericImage, QRCode as QRCode, make as make

from ._types import ErrorCorrect, MaskPattern

def run_example(data: str = "http://www.lincolnloop.com", *args, **kwargs) -> None: ...
def run_example(
data: str = "http://www.lincolnloop.com",
version: ConvertibleToInt | None = None,
error_correction: ErrorCorrect = 0,
box_size: ConvertibleToInt = 10,
border: ConvertibleToInt = 4,
image_factory: type[GenericImage] | None = None,
mask_pattern: MaskPattern | None = None,
) -> None: ...
15 changes: 15 additions & 0 deletions stubs/qrcode/qrcode/_types.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Type aliases used in this stub package
from _typeshed import SupportsWrite
from typing import Any, Protocol
from typing_extensions import TypeAlias

Box: TypeAlias = tuple[tuple[int, int], tuple[int, int]]
Ink: TypeAlias = tuple[int, int, int] | tuple[int, int, int, int]

# Don't try to make these Literal[x, y, z] as this really wreaks
# havoc with overloads in mypy.
ErrorCorrect: TypeAlias = int
MaskPattern: TypeAlias = int

class Writeable(SupportsWrite[bytes], Protocol):
def seek(self, offset: int, /) -> Any: ...
32 changes: 17 additions & 15 deletions stubs/qrcode/qrcode/base.pyi
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
from _typeshed import Incomplete
from typing import NamedTuple
from collections.abc import Iterator
from typing import NamedTuple, SupportsIndex

EXP_TABLE: Incomplete
LOG_TABLE: Incomplete
RS_BLOCK_OFFSET: Incomplete
RS_BLOCK_TABLE: Incomplete
from ._types import ErrorCorrect

def glog(n): ...
def gexp(n): ...
EXP_TABLE: list[int]
LOG_TABLE: list[int]
RS_BLOCK_OFFSET: dict[ErrorCorrect, int]
RS_BLOCK_TABLE: tuple[tuple[int, int, int] | tuple[int, int, int, int, int, int], ...]
mjpieters marked this conversation as resolved.
Show resolved Hide resolved

def glog(n: int) -> int: ...
def gexp(n: int) -> int: ...

class Polynomial:
num: Incomplete
def __init__(self, num, shift) -> None: ...
def __getitem__(self, index): ...
def __iter__(self): ...
num: list[int]
def __init__(self, num: list[int], shift: int) -> None: ...
def __getitem__(self, index: SupportsIndex) -> int: ...
def __iter__(self) -> Iterator[int]: ...
def __len__(self) -> int: ...
def __mul__(self, other): ...
def __mod__(self, other): ...
def __mul__(self, other: Polynomial) -> Polynomial: ...
def __mod__(self, other: Polynomial) -> Polynomial: ...

class RSBlock(NamedTuple):
total_count: int
data_count: int

def rs_blocks(version, error_correction): ...
def rs_blocks(version: int, error_correction: ErrorCorrect) -> list[RSBlock]: ...
12 changes: 6 additions & 6 deletions stubs/qrcode/qrcode/console_scripts.pyi
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from _typeshed import Incomplete
from collections.abc import Iterable
from collections.abc import Iterable, Sequence

from qrcode.image.base import BaseImage, DrawerAliases as DrawerAliases
from ._types import ErrorCorrect
from .image.base import BaseImage, DrawerAliases as DrawerAliases

default_factories: Incomplete
error_correction: Incomplete
default_factories: dict[str, str]
error_correction: dict[str, ErrorCorrect]

def main(args: Incomplete | None = None) -> None: ...
def main(args: Sequence[str] | None = None) -> None: ...
def get_factory(module: str) -> type[BaseImage]: ...
def get_drawer_help() -> str: ...
def commas(items: Iterable[str], joiner: str = "or") -> str: ...
71 changes: 44 additions & 27 deletions stubs/qrcode/qrcode/image/base.pyi
Original file line number Diff line number Diff line change
@@ -1,50 +1,67 @@
import abc
from _typeshed import Incomplete
from typing import Any
from collections.abc import Callable
from typing import IO, Any
from typing_extensions import TypeAlias

from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
from qrcode.main import QRCode
from ..main import ModulesType, QRCode
from .styles.moduledrawers.base import QRModuleDrawer

# The second element of the value tuple are keyword arguments used when
# constructing instances of the QRModuleDrawer type.
DrawerAliases: TypeAlias = dict[str, tuple[type[QRModuleDrawer], dict[str, Any]]]

class BaseImage(metaclass=abc.ABCMeta):
class BaseImage:
kind: str | None
allowed_kinds: tuple[str] | None
needs_context: bool
needs_processing: bool
needs_drawrect: bool
border: Incomplete
width: Incomplete
box_size: Incomplete
pixel_size: Incomplete
modules: Incomplete
def __init__(self, border, width, box_size, *args, **kwargs) -> None: ...
@abc.abstractmethod
def drawrect(self, row, col): ...
def drawrect_context(self, row: int, col: int, qr: QRCode[Incomplete]): ...
border: int
width: int
box_size: int
pixel_size: int
modules: list[list[bool | None]]
# the class accepts arbitrary additional positional arguments to accommodate
# subclasses with additional arguments. kwargs are forwarded to the `new_image()` call.
def __init__(
self, border: int, width: int, box_size: int, *args: Any, qrcode_modules: ModulesType | None, **kwargs: Any
) -> None: ...
def drawrect(self, row: int, col: int) -> None: ...
def drawrect_context(self, row: int, col: int, qr: QRCode[Any]) -> None: ...
def process(self) -> None: ...
@abc.abstractmethod
def save(self, stream, kind: Incomplete | None = None): ...
def pixel_box(self, row, col): ...
@abc.abstractmethod
def new_image(self, **kwargs) -> Any: ...
def save(self, stream: IO[bytes], kind: str | None = None) -> None: ...
def pixel_box(self, row: int, col: int) -> tuple[tuple[int, int], tuple[int, int]]: ...
# the new_image method accepts arbitrary keyword arguments to accommodate
# subclasses with additional arguments.
def new_image(self, **kwargs: Any) -> Any: ...
def init_new_image(self) -> None: ...
def get_image(self, **kwargs): ...
def check_kind(self, kind, transform: Incomplete | None = None): ...
def is_eye(self, row: int, col: int): ...
# the get_image method accepts arbitrary keyword arguments to accommodate
# subclasses with additional arguments.
def get_image(self, **kwargs: Any) -> Any: ...
def check_kind(self, kind: str | None, transform: Callable[[str | None], str | None] | None = None) -> str | None: ...
def is_eye(self, row: int, col: int) -> bool: ...

class BaseImageWithDrawer(BaseImage, metaclass=abc.ABCMeta):
class BaseImageWithDrawer(BaseImage):
default_drawer_class: type[QRModuleDrawer]
drawer_aliases: DrawerAliases
def get_default_module_drawer(self) -> QRModuleDrawer: ...
def get_default_eye_drawer(self) -> QRModuleDrawer: ...
needs_context: bool
module_drawer: QRModuleDrawer
eye_drawer: QRModuleDrawer
# the class accepts arbitrary additional positional arguments to accommodate
# subclasses with additional arguments. kwargs are forwarded to the `new_image()` call
# via the BaseImage.__init__ method.
def __init__(
self, *args, module_drawer: QRModuleDrawer | str | None = None, eye_drawer: QRModuleDrawer | str | None = None, **kwargs
self,
border: int,
width: int,
box_size: int,
*args: Any,
qrcode_modules: ModulesType | None,
module_drawer: QRModuleDrawer | str | None = None,
eye_drawer: QRModuleDrawer | str | None = None,
**kwargs: Any,
) -> None: ...
def get_drawer(self, drawer: QRModuleDrawer | str | None) -> QRModuleDrawer | None: ...
def init_new_image(self): ...
def drawrect_context(self, row: int, col: int, qr: QRCode[Incomplete]): ...
def init_new_image(self) -> None: ...
def drawrect_context(self, row: int, col: int, qr: QRCode[Any]) -> None: ...
37 changes: 28 additions & 9 deletions stubs/qrcode/qrcode/image/pil.pyi
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
from _typeshed import Incomplete
from pathlib import Path
from typing import Any, Literal

import qrcode.image.base
from PIL import Image

class PilImage(qrcode.image.base.BaseImage):
kind: str
fill_color: Incomplete
def new_image(self, **kwargs): ...
def drawrect(self, row, col) -> None: ...
def save(self, stream, format: Incomplete | None = None, **kwargs) -> None: ... # type: ignore[override]
def __getattr__(self, name): ...
from .._types import Writeable
from . import base

class PilImage(base.BaseImage):
kind: Literal["PNG"]
fill_color: str
# the new_image and get_image methods accept arbitrary keyword arguments to
# accommodate subclasses with additional arguments.
def new_image(self, *, back_color: str = "white", fill_color: str = "black", **kwargs: Any) -> Image.Image: ...
def get_image(self, **kwargs: Any) -> Image.Image: ...
def drawrect(self, row: int, col: int) -> None: ...
# kwargs are passed on to PIL.Image.save, which also accepts arbitrary keyword arguments.
def save( # type: ignore[override]
self,
stream: str | bytes | Path | Writeable,
format: str | None = None,
*,
kind: str | None = None,
save_all: bool = ...,
bitmap_format: Literal["bmp", "png"] = ...,
optimize: bool = ...,
**kwargs: Any,
) -> None: ...
# attribute access is forwarded to the wrapped PIL.Image.Image instance.
def __getattr__(self, name: str) -> Any: ...
27 changes: 17 additions & 10 deletions stubs/qrcode/qrcode/image/pure.pyi
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
from _typeshed import Incomplete
from _typeshed import SupportsWrite
from collections.abc import Generator
from typing import Any, Literal
from typing_extensions import TypeAlias

import qrcode.image.base
from . import base

class PyPNGImage(qrcode.image.base.BaseImage):
# png.Writer; no types available
_Writer: TypeAlias = Any

class PyPNGImage(base.BaseImage):
kind: str
allowed_kinds: Incomplete
needs_drawrect: bool
def new_image(self, **kwargs): ...
def drawrect(self, row, col) -> None: ...
def save(self, stream, kind: Incomplete | None = None) -> None: ...
def rows_iter(self) -> Generator[Incomplete, Incomplete, None]: ...
def border_rows_iter(self) -> Generator[Incomplete, None, None]: ...
allowed_kinds: tuple[Literal["PNG"]]
# the new_image and get_image methods accept arbitrary keyword arguments to
# accommodate subclasses with additional arguments.
def new_image(self, **kwargs: Any) -> _Writer: ...
def get_image(self, **kwargs: Any) -> _Writer: ...
def drawrect(self, row: int, col: int) -> None: ...
def save(self, stream: SupportsWrite[bytes], kind: str | None = None) -> None: ...
def rows_iter(self) -> Generator[list[int], Any, None]: ...
def border_rows_iter(self) -> Generator[list[int], Any, None]: ...

PymagingImage = PyPNGImage
70 changes: 52 additions & 18 deletions stubs/qrcode/qrcode/image/styledpil.pyi
Original file line number Diff line number Diff line change
@@ -1,22 +1,56 @@
import abc
from _typeshed import Incomplete
from _typeshed import SupportsRead
from pathlib import Path
from typing import Any, Literal

import qrcode.image.base
from qrcode.image.styles.colormasks import QRColorMask
from qrcode.image.styles.moduledrawers import SquareModuleDrawer
from PIL import Image

class StyledPilImage(qrcode.image.base.BaseImageWithDrawer, metaclass=abc.ABCMeta):
kind: str
needs_processing: bool
from .._types import Ink, Writeable
from ..main import ModulesType
from . import base
from .styles.colormasks import QRColorMask
from .styles.moduledrawers import SquareModuleDrawer
from .styles.moduledrawers.base import QRModuleDrawer

class StyledPilImage(base.BaseImageWithDrawer):
kind: Literal["PNG"]
color_mask: QRColorMask
default_drawer_class = SquareModuleDrawer
embeded_image: Incomplete
embeded_image_resample: Incomplete
paint_color: Incomplete
def __init__(self, *args, **kwargs) -> None: ...
def new_image(self, **kwargs): ...
def init_new_image(self) -> None: ...
def process(self) -> None: ...
default_drawer_class: type[SquareModuleDrawer]
embeded_image: Image.Image
embeded_image_resample: Image.Resampling
paint_color: Ink
# the class accepts arbitrary additional positional arguments to accommodate
# subclasses with additional arguments. kwargs are forwarded to the `new_image()` call
# via the BaseImage.__init__ method.
def __init__(
self,
border: int,
width: int,
box_size: int,
*args: Any,
qrcode_modules: ModulesType | None,
module_drawer: QRModuleDrawer | str | None = None,
eye_drawer: QRModuleDrawer | str | None = None,
color_mask: QRColorMask = ...,
embeded_image_path: str | bytes | Path | SupportsRead[bytes] | None = None,
embeded_image: Image.Image | None = None,
embeded_image_resample: Image.Resampling = ...,
**kwargs: Any,
) -> None: ...
# the new_image method accepts arbitrary keyword arguments to accommodate
# subclasses with additional arguments.
def new_image(self, **kwargs: Any) -> Image.Image: ...
def draw_embeded_image(self) -> None: ...
def save(self, stream, format: Incomplete | None = None, **kwargs) -> None: ... # type: ignore[override]
def __getattr__(self, name): ...
# kwargs are passed on to PIL.Image.save, which also accepts arbitrary keyword arguments.
def save( # type: ignore[override]
self,
stream: str | bytes | Path | Writeable,
format: str | None = None,
*,
kind: str | None = None,
save_all: bool = ...,
bitmap_format: Literal["bmp", "png"] = ...,
optimize: bool = ...,
**kwargs: Any,
) -> None: ...
# attribute access is forwarded to the wrapped PIL.Image.Image instance.
def __getattr__(self, name: str) -> Any: ...
Loading