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

Added type hints for ImageShow #7712

Merged
merged 2 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ exclude = [
'^src/PIL/FpxImagePlugin.py$',
'^src/PIL/Image.py$',
'^src/PIL/ImageQt.py$',
'^src/PIL/ImageShow.py$',
'^src/PIL/ImImagePlugin.py$',
'^src/PIL/MicImagePlugin.py$',
'^src/PIL/PdfParser.py$',
Expand Down
6 changes: 4 additions & 2 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def _conv_type_shape(im):
_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B")


def getmodebase(mode):
def getmodebase(mode: str) -> str:
"""
Gets the "base" mode for given mode. This function returns "L" for
images that contain grayscale data, and "RGB" for images that
Expand Down Expand Up @@ -583,7 +583,9 @@ def _ensure_mutable(self):
else:
self.load()

def _dump(self, file=None, format=None, **options):
def _dump(
self, file: str | None = None, format: str | None = None, **options
) -> str:
suffix = ""
if format:
suffix = "." + format
Expand Down
60 changes: 35 additions & 25 deletions src/PIL/ImageShow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@
#
from __future__ import annotations

import abc
import os
import shutil
import subprocess
import sys
from shlex import quote
from typing import Any

from . import Image

_viewers = []


def register(viewer, order=1):
def register(viewer, order: int = 1) -> None:
"""
The :py:func:`register` function is used to register additional viewers::

Expand All @@ -49,7 +51,7 @@
_viewers.insert(0, viewer)


def show(image, title=None, **options):
def show(image: Image.Image, title: str | None = None, **options: Any) -> bool:
r"""
Display a given image.

Expand All @@ -69,7 +71,7 @@

# main api

def show(self, image, **options):
def show(self, image: Image.Image, **options: Any) -> int:
"""
The main function for displaying an image.
Converts the given image to the target format and displays it.
Expand All @@ -87,32 +89,32 @@

# hook methods

format = None
format: str | None = None
"""The format to convert the image into."""
options = {}
options: dict[str, Any] = {}
"""Additional options used to convert the image."""

def get_format(self, image):
def get_format(self, image: Image.Image) -> str | None:
"""Return format name, or ``None`` to save as PGM/PPM."""
return self.format

def get_command(self, file, **options):
def get_command(self, file: str, **options: Any) -> str:
"""
Returns the command used to display the file.
Not implemented in the base class.
"""
msg = "unavailable in base viewer"
raise NotImplementedError(msg)

def save_image(self, image):
def save_image(self, image: Image.Image) -> str:
"""Save to temporary file and return filename."""
return image._dump(format=self.get_format(image), **self.options)

def show_image(self, image, **options):
def show_image(self, image: Image.Image, **options: Any) -> int:
"""Display the given image."""
return self.show_file(self.save_image(image), **options)

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand All @@ -129,7 +131,7 @@
format = "PNG"
options = {"compress_level": 1, "save_all": True}

def get_command(self, file, **options):
def get_command(self, file: str, **options: Any) -> str:
return (
f'start "Pillow" /WAIT "{file}" '
"&& ping -n 4 127.0.0.1 >NUL "
Expand All @@ -147,14 +149,14 @@
format = "PNG"
options = {"compress_level": 1, "save_all": True}

def get_command(self, file, **options):
def get_command(self, file: str, **options: Any) -> str:
# on darwin open returns immediately resulting in the temp
# file removal while app is opening
command = "open -a Preview.app"
command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
return command

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand All @@ -180,7 +182,11 @@
format = "PNG"
options = {"compress_level": 1, "save_all": True}

def get_command(self, file, **options):
@abc.abstractmethod
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we exclude this from coverage?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, done

def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]:
pass

Check warning on line 187 in src/PIL/ImageShow.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/ImageShow.py#L187

Added line #L187 was not covered by tests

def get_command(self, file: str, **options: Any) -> str:
command = self.get_command_ex(file, **options)[0]
return f"({command} {quote(file)}"

Expand All @@ -190,11 +196,11 @@
The freedesktop.org ``xdg-open`` command.
"""

def get_command_ex(self, file, **options):
def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]:
command = executable = "xdg-open"
return command, executable

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand All @@ -208,13 +214,15 @@
This viewer supports the ``title`` parameter.
"""

def get_command_ex(self, file, title=None, **options):
def get_command_ex(
self, file: str, title: str | None = None, **options: Any
) -> tuple[str, str]:
command = executable = "display"
if title:
command += f" -title {quote(title)}"
return command, executable

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand All @@ -231,12 +239,12 @@
class GmDisplayViewer(UnixViewer):
"""The GraphicsMagick ``gm display`` command."""

def get_command_ex(self, file, **options):
def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]:
executable = "gm"
command = "gm display"
return command, executable

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand All @@ -247,12 +255,12 @@
class EogViewer(UnixViewer):
"""The GNOME Image Viewer ``eog`` command."""

def get_command_ex(self, file, **options):
def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]:
executable = "eog"
command = "eog -n"
return command, executable

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand All @@ -266,15 +274,17 @@
This viewer supports the ``title`` parameter.
"""

def get_command_ex(self, file, title=None, **options):
def get_command_ex(
self, file: str, title: str | None = None, **options: Any
) -> tuple[str, str]:
# note: xv is pretty outdated. most modern systems have
# imagemagick's display command instead.
command = executable = "xv"
if title:
command += f" -name {quote(title)}"
return command, executable

def show_file(self, path, **options):
def show_file(self, path: str, **options: Any) -> int:
"""
Display given file.
"""
Expand Down Expand Up @@ -304,7 +314,7 @@
class IPythonViewer(Viewer):
"""The viewer for IPython frontends."""

def show_image(self, image, **options):
def show_image(self, image: Image.Image, **options: Any) -> int:
ipython_display(image)
return 1

Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ commands =
[testenv:mypy]
skip_install = true
deps =
ipython
mypy==1.7.1
numpy
extras =
Expand Down