From 91a1c4d937ff0225057d65e3859bcab8e0c74b8b Mon Sep 17 00:00:00 2001 From: Chris Beaven Date: Mon, 14 Mar 2022 16:20:11 +1300 Subject: [PATCH] Tidy up some typing --- qrcode/compat/__init__.py | 0 qrcode/compat/etree.py | 4 +++ qrcode/compat/pil.py | 12 +++++++++ qrcode/console_scripts.py | 2 +- qrcode/image/pil.py | 10 +------- qrcode/image/pure.py | 19 +++++++------- qrcode/image/styledpil.py | 9 +++---- qrcode/image/styles/colormasks.py | 25 ++++++------------ qrcode/image/styles/moduledrawers/base.py | 5 ++-- qrcode/image/styles/moduledrawers/pil.py | 31 ++++++----------------- qrcode/image/styles/moduledrawers/svg.py | 14 ++++------ qrcode/image/svg.py | 16 ++++++------ qrcode/tests/test_example.py | 9 +------ qrcode/tests/test_qrcode.py | 13 ++++------ qrcode/tests/test_script.py | 9 +------ 15 files changed, 68 insertions(+), 110 deletions(-) create mode 100644 qrcode/compat/__init__.py create mode 100644 qrcode/compat/etree.py create mode 100644 qrcode/compat/pil.py diff --git a/qrcode/compat/__init__.py b/qrcode/compat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qrcode/compat/etree.py b/qrcode/compat/etree.py new file mode 100644 index 00000000..6739d227 --- /dev/null +++ b/qrcode/compat/etree.py @@ -0,0 +1,4 @@ +try: + import lxml.etree as ET # type: ignore # noqa: F401 +except ImportError: + import xml.etree.ElementTree as ET # type: ignore # noqa: F401 diff --git a/qrcode/compat/pil.py b/qrcode/compat/pil.py new file mode 100644 index 00000000..de6c6cb7 --- /dev/null +++ b/qrcode/compat/pil.py @@ -0,0 +1,12 @@ +# Try to import PIL in either of the two ways it can be installed. +Image = None +ImageDraw = None + +try: + from PIL import Image, ImageDraw # type: ignore # noqa: F401 +except ImportError: # pragma: no cover + try: + import Image # type: ignore # noqa: F401 + import ImageDraw # type: ignore # noqa: F401 + except ImportError: + pass diff --git a/qrcode/console_scripts.py b/qrcode/console_scripts.py index efca8c87..ce56e27f 100755 --- a/qrcode/console_scripts.py +++ b/qrcode/console_scripts.py @@ -15,7 +15,7 @@ # The next block is added to get the terminal to display properly on MS platforms if sys.platform.startswith(("win", "cygwin")): # pragma: no cover - import colorama + import colorama # type: ignore colorama.init() diff --git a/qrcode/image/pil.py b/qrcode/image/pil.py index 989eeda3..5767148d 100644 --- a/qrcode/image/pil.py +++ b/qrcode/image/pil.py @@ -1,13 +1,5 @@ -# Needed on case-insensitive filesystems - -# Try to import PIL in either of the two ways it can be installed. -try: - from PIL import Image, ImageDraw -except ImportError: # pragma: no cover - import Image - import ImageDraw - import qrcode.image.base +from qrcode.compat.pil import Image, ImageDraw class PilImage(qrcode.image.base.BaseImage): diff --git a/qrcode/image/pure.py b/qrcode/image/pure.py index d7b14128..de1944ed 100644 --- a/qrcode/image/pure.py +++ b/qrcode/image/pure.py @@ -1,10 +1,10 @@ import qrcode.image.base -from pymaging import Image -from pymaging.colors import RGB -from pymaging.formats import registry -from pymaging.shapes import Line -from pymaging.webcolors import Black, White -from pymaging_png.png import PNG +from pymaging import Image # type: ignore +from pymaging.colors import RGB # type: ignore +from pymaging.formats import registry # type: ignore +from pymaging.shapes import Line # type: ignore +from pymaging.webcolors import Black, White # type: ignore +from pymaging_png.png import PNG # type: ignore class PymagingImage(qrcode.image.base.BaseImage): @@ -43,9 +43,8 @@ def check_kind(self, kind, transform=None, **kwargs): """ pymaging (pymaging_png at least) uses lower case for the type. """ - if transform is None: - def transform(x): - return x.lower() + def lower_case(x): + return x.lower() - return super().check_kind(kind, transform=transform, **kwargs) + return super().check_kind(kind, transform=transform or lower_case, **kwargs) diff --git a/qrcode/image/styledpil.py b/qrcode/image/styledpil.py index 7a03e25b..1e6d20e7 100644 --- a/qrcode/image/styledpil.py +++ b/qrcode/image/styledpil.py @@ -1,13 +1,8 @@ # Needed on case-insensitive filesystems from __future__ import absolute_import -# Try to import PIL in either of the two ways it can be installed. -try: - from PIL import Image -except ImportError: # pragma: no cover - import Image - import qrcode.image.base +from qrcode.compat.pil import Image from qrcode.image.styles.colormasks import QRColorMask, SolidFillColorMask from qrcode.image.styles.moduledrawers import SquareModuleDrawer @@ -88,6 +83,8 @@ def process(self): self.draw_embeded_image() def draw_embeded_image(self): + if not self.embeded_image: + return total_width, _ = self._img.size total_width = int(total_width) logo_width_ish = int(total_width / 4) diff --git a/qrcode/image/styles/colormasks.py b/qrcode/image/styles/colormasks.py index 1a61f443..1887c12c 100644 --- a/qrcode/image/styles/colormasks.py +++ b/qrcode/image/styles/colormasks.py @@ -1,14 +1,10 @@ # Needed on case-insensitive filesystems from __future__ import absolute_import -# Try to import PIL in either of the two ways it can be installed. -try: - from PIL import Image -except ImportError: # pragma: no cover - import Image - import math +from qrcode.compat.pil import Image + class QRColorMask: """ @@ -78,19 +74,14 @@ def extrap_num(self, n1, n2, interped_num): # find the interpolation coefficient between two numbers def extrap_color(self, col1, col2, interped_color): - normed = list( - filter( - lambda i: i is not None, - [ - self.extrap_num(col1[i], col2[i], interped_color[i]) - for i in range(len(col1)) - ], - ) - ) + normed = [] + for c1, c2, ci in zip(col1, col2, interped_color): + extrap = self.extrap_num(c1, c2, ci) + if extrap is not None: + normed.append(extrap) if not normed: return None - else: - return sum(normed) / len(normed) + return sum(normed) / len(normed) class SolidFillColorMask(QRColorMask): diff --git a/qrcode/image/styles/moduledrawers/base.py b/qrcode/image/styles/moduledrawers/base.py index 054db306..8de33059 100644 --- a/qrcode/image/styles/moduledrawers/base.py +++ b/qrcode/image/styles/moduledrawers/base.py @@ -1,11 +1,10 @@ from __future__ import absolute_import import abc -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING if TYPE_CHECKING: from qrcode.image.base import BaseImage - from qrcode.main import ActiveWithNeighbors class QRModuleDrawer(abc.ABC): @@ -33,5 +32,5 @@ def initialize(self, img: "BaseImage") -> None: self.img = img @abc.abstractmethod - def drawrect(self, box, is_active: "Union[bool, ActiveWithNeighbors]") -> None: + def drawrect(self, box, is_active) -> None: ... diff --git a/qrcode/image/styles/moduledrawers/pil.py b/qrcode/image/styles/moduledrawers/pil.py index fcfb1b7b..b1ad21eb 100644 --- a/qrcode/image/styles/moduledrawers/pil.py +++ b/qrcode/image/styles/moduledrawers/pil.py @@ -1,18 +1,11 @@ # Needed on case-insensitive filesystems from __future__ import absolute_import -import abc -from typing import TYPE_CHECKING, List, Union +from typing import TYPE_CHECKING, List +from qrcode.compat.pil import Image, ImageDraw from qrcode.image.styles.moduledrawers.base import QRModuleDrawer -# Try to import PIL in either of the two ways it can be installed. -try: - from PIL import Image, ImageDraw -except ImportError: # pragma: no cover - import Image - import ImageDraw - if TYPE_CHECKING: from qrcode.image.styledpil import StyledPilImage from qrcode.main import ActiveWithNeighbors @@ -33,10 +26,6 @@ class StyledPilQRModuleDrawer(QRModuleDrawer): img: "StyledPilImage" - @abc.abstractmethod - def drawrect(self, box, is_active: Union[bool, "ActiveWithNeighbors"]) -> None: - ... - class SquareModuleDrawer(StyledPilQRModuleDrawer): """ @@ -47,7 +36,7 @@ def initialize(self, *args, **kwargs): super().initialize(*args, **kwargs) self.imgDraw = ImageDraw.Draw(self.img._img) - def drawrect(self, box, is_active): + def drawrect(self, box, is_active: bool): if is_active: self.imgDraw.rectangle(box, fill=self.img.paint_color) @@ -68,7 +57,7 @@ def initialize(self, *args, **kwargs): self.imgDraw = ImageDraw.Draw(self.img._img) self.delta = (1 - self.size_ratio) * self.img.box_size / 2 - def drawrect(self, box, is_active: "Union[bool, ActiveWithNeighbors]"): + def drawrect(self, box, is_active: bool): if is_active: smaller_box = ( box[0][0] + self.delta, @@ -100,7 +89,7 @@ def initialize(self, *args, **kwargs): ) self.circle = self.circle.resize((box_size, box_size), Image.LANCZOS) - def drawrect(self, box, is_active: "Union[bool, ActiveWithNeighbors]"): + def drawrect(self, box, is_active: bool): if is_active: self.img._img.paste(self.circle, (box[0][0], box[0][1])) @@ -150,9 +139,7 @@ def setup_corners(self): self.SE_ROUND = self.NW_ROUND.transpose(Image.ROTATE_180) self.NE_ROUND = self.NW_ROUND.transpose(Image.FLIP_LEFT_RIGHT) - def drawrect( - self, box: List[List[int]], is_active: "Union[bool, ActiveWithNeighbors]" - ): + def drawrect(self, box: List[List[int]], is_active: "ActiveWithNeighbors"): if not is_active: return # find rounded edges @@ -212,8 +199,7 @@ def setup_edges(self): self.ROUND_TOP = base.resize((shrunken_width, height), Image.LANCZOS) self.ROUND_BOTTOM = self.ROUND_TOP.transpose(Image.FLIP_TOP_BOTTOM) - def drawrect(self, box, is_active: "Union[bool, ActiveWithNeighbors]"): - assert not isinstance(is_active, bool) + def drawrect(self, box, is_active: "ActiveWithNeighbors"): if is_active: # find rounded edges top_rounded = not is_active.N @@ -266,8 +252,7 @@ def setup_edges(self): self.ROUND_LEFT = base.resize((width, shrunken_height), Image.LANCZOS) self.ROUND_RIGHT = self.ROUND_LEFT.transpose(Image.FLIP_LEFT_RIGHT) - def drawrect(self, box, is_active: "Union[bool, ActiveWithNeighbors]"): - assert not isinstance(is_active, bool) + def drawrect(self, box, is_active: "ActiveWithNeighbors"): if is_active: # find rounded edges left_rounded = not is_active.W diff --git a/qrcode/image/styles/moduledrawers/svg.py b/qrcode/image/styles/moduledrawers/svg.py index 8d1859dc..6e629759 100644 --- a/qrcode/image/styles/moduledrawers/svg.py +++ b/qrcode/image/styles/moduledrawers/svg.py @@ -2,12 +2,8 @@ from decimal import Decimal from typing import TYPE_CHECKING, NamedTuple -try: - import lxml.etree as ET -except ImportError: - import xml.etree.ElementTree as ET # type: ignore - from qrcode.image.styles.moduledrawers.base import QRModuleDrawer +from qrcode.compat.etree import ET if TYPE_CHECKING: from qrcode.image.svg import SvgFragmentImage, SvgPathImage @@ -58,7 +54,7 @@ def initialize(self, *args, **kwargs) -> None: super().initialize(*args, **kwargs) self.tag_qname = ET.QName(self.img._SVG_namespace, self.tag) - def drawrect(self, box, is_active): + def drawrect(self, box, is_active: bool): if not is_active: return self.img._img.append(self.el(box)) @@ -76,7 +72,7 @@ def initialize(self, *args, **kwargs) -> None: def el(self, box): coords = self.coords(box) return ET.Element( - self.tag_qname, + self.tag_qname, # type: ignore x=self.img.units(coords.x0), y=self.img.units(coords.y0), width=self.unit_size, @@ -94,7 +90,7 @@ def initialize(self, *args, **kwargs) -> None: def el(self, box): coords = self.coords(box) return ET.Element( - self.tag_qname, + self.tag_qname, # type: ignore cx=self.img.units(coords.xh), cy=self.img.units(coords.yh), r=self.radius, @@ -104,7 +100,7 @@ def el(self, box): class SvgPathQRModuleDrawer(BaseSvgQRModuleDrawer): img: "SvgPathImage" - def drawrect(self, box, is_active): + def drawrect(self, box, is_active: bool): if not is_active: return self.img._subpaths.append(self.subpath(box)) diff --git a/qrcode/image/svg.py b/qrcode/image/svg.py index ee57edca..bf0ec870 100644 --- a/qrcode/image/svg.py +++ b/qrcode/image/svg.py @@ -5,14 +5,10 @@ from typing_extensions import Literal import qrcode.image.base +from qrcode.compat.etree import ET from qrcode.image.styles.moduledrawers import svg as svg_drawers from qrcode.image.styles.moduledrawers.base import QRModuleDrawer -try: - import lxml.etree as ET -except ImportError: - import xml.etree.ElementTree as ET # type: ignore - class SvgFragmentImage(qrcode.image.base.BaseImageWithDrawer): """ @@ -71,7 +67,11 @@ def _svg(self, tag=None, version="1.1", **kwargs): tag = ET.QName(self._SVG_namespace, "svg") dimension = self.units(self.pixel_size) return ET.Element( - tag, width=dimension, height=dimension, version=version, **kwargs + tag, # type: ignore + width=dimension, + height=dimension, + version=version, + **kwargs, ) def _write(self, stream): @@ -126,7 +126,7 @@ class SvgPathImage(SvgImage): } needs_processing = True - path: ET.Element = None + path: Optional[ET.Element] = None default_drawer_class: Type[QRModuleDrawer] = svg_drawers.SvgPathSquareDrawer drawer_aliases = { "circle": (svg_drawers.SvgPathCircleDrawer, {}), @@ -154,7 +154,7 @@ def process(self): # Store the path just in case someone wants to use it again or in some # unique way. self.path = ET.Element( - ET.QName("path"), + ET.QName("path"), # type: ignore d="".join(self._subpaths), id="qr-path", **self.QR_PATH_STYLE, diff --git a/qrcode/tests/test_example.py b/qrcode/tests/test_example.py index 5d14f8d9..2e03dc53 100644 --- a/qrcode/tests/test_example.py +++ b/qrcode/tests/test_example.py @@ -2,14 +2,7 @@ from unittest import mock from qrcode import run_example - -try: - from PIL import Image -except ImportError: - try: - import Image - except ImportError: - Image = None +from qrcode.compat.pil import Image class ExampleTest(unittest.TestCase): diff --git a/qrcode/tests/test_qrcode.py b/qrcode/tests/test_qrcode.py index 53693ebe..8db66bf4 100644 --- a/qrcode/tests/test_qrcode.py +++ b/qrcode/tests/test_qrcode.py @@ -7,23 +7,20 @@ import qrcode import qrcode.util +from qrcode.compat.pil import Image as pil_Image from qrcode.exceptions import DataOverflowError from qrcode.image.base import BaseImage -from qrcode.image.styles import moduledrawers +from qrcode.image.styledpil import StyledPilImage +from qrcode.image.styles import colormasks, moduledrawers from qrcode.util import MODE_8BIT_BYTE, MODE_ALPHA_NUM, MODE_NUMBER, QRData try: import pymaging_png # type: ignore + from qrcode.image.pure import PymagingImage except ImportError: # pragma: no cover pymaging_png = None -try: - from qrcode.image.pil import Image as pil_Image - from qrcode.image.styledpil import StyledPilImage - from qrcode.image.styles import colormasks -except ImportError: - pil_Image = None UNICODE_TEXT = "\u03b1\u03b2\u03b3" WHITE = (255, 255, 255) @@ -204,7 +201,7 @@ def test_render_pymaging_png_bad_kind(self): img.save(io.BytesIO(), kind="FISH") @unittest.skipIf(not pil_Image, "Requires PIL") - def test_render_styled_pil_image(self): + def test_render_styled_Image(self): qr = qrcode.QRCode(error_correction=qrcode.ERROR_CORRECT_L) qr.add_data(UNICODE_TEXT) img = qr.make_image(image_factory=StyledPilImage) diff --git a/qrcode/tests/test_script.py b/qrcode/tests/test_script.py index 69353581..4ae4ccbc 100644 --- a/qrcode/tests/test_script.py +++ b/qrcode/tests/test_script.py @@ -5,16 +5,9 @@ from tempfile import mkdtemp from unittest import mock +from qrcode.compat.pil import Image from qrcode.console_scripts import commas, main -try: - from PIL import Image -except ImportError: - try: - import Image - except ImportError: - Image = None - def bad_read(): raise UnicodeDecodeError("utf-8", b"0x80", 0, 1, "invalid start byte")