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

Add type hints for ImageCms #7913

Merged
merged 16 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
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
36 changes: 30 additions & 6 deletions Tests/test_imagecms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
import os
import re
import shutil
import sys
from io import BytesIO
from pathlib import Path
from typing import Any

import pytest

from PIL import Image, ImageMode, features
from PIL import Image, ImageMode, ImageWin, features

from .helper import (
assert_image,
assert_image_equal,
assert_image_similar,
assert_image_similar_tofile,
hopper,
is_pypy,
)

try:
Expand Down Expand Up @@ -213,6 +215,10 @@ def test_display_profile() -> None:
# try fetching the profile for the current display device
ImageCms.get_display_profile()

if sys.platform == "win32":
ImageCms.get_display_profile(ImageWin.HDC(0))
ImageCms.get_display_profile(ImageWin.HWND(0))


def test_lab_color_profile() -> None:
ImageCms.createProfile("LAB", 5000)
Expand Down Expand Up @@ -496,16 +502,34 @@ def test_non_ascii_path(tmp_path: Path) -> None:


def test_profile_typesafety() -> None:
"""Profile init type safety

prepatch, these would segfault, postpatch they should emit a typeerror
"""

# does not segfault
with pytest.raises(TypeError, match="Invalid type for Profile"):
ImageCms.ImageCmsProfile(0).tobytes()
with pytest.raises(TypeError, match="Invalid type for Profile"):
ImageCms.ImageCmsProfile(1).tobytes()

# also check core function
with pytest.raises(TypeError):
ImageCms.core.profile_tobytes(0)
with pytest.raises(TypeError):
ImageCms.core.profile_tobytes(1)

if not is_pypy():
# core profile should not be directly instantiable
with pytest.raises(TypeError):
ImageCms.core.CmsProfile()
with pytest.raises(TypeError):
ImageCms.core.CmsProfile(0)


@pytest.mark.skipif(is_pypy(), reason="fails on PyPy")
def test_transform_typesafety() -> None:
# core transform should not be directly instantiable
with pytest.raises(TypeError):
ImageCms.core.CmsProfile()
with pytest.raises(TypeError):
ImageCms.core.CmsProfile(0)


def assert_aux_channel_preserved(
mode: str, transform_in_place: bool, preserved_channel: str
Expand Down
5 changes: 4 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@
# generating warnings in “nitpicky mode”. Note that type should include the domain name
# if present. Example entries would be ('py:func', 'int') or
# ('envvar', 'LD_LIBRARY_PATH').
# nitpick_ignore = []
nitpick_ignore = [
# sphinx does not understand `typing.Literal[-1]`
nulano marked this conversation as resolved.
Show resolved Hide resolved
("py:obj", "typing.Literal[-1, 1]"),
]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will be fixed in a future release of sphinx: sphinx-doc/sphinx#11904



# -- Options for HTML output ----------------------------------------------
Expand Down
52 changes: 26 additions & 26 deletions docs/reference/ImageCms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ can be easily displayed in a chromaticity diagram, for example).
:canonical: PIL._imagingcms.CmsProfile

.. py:attribute:: creation_date
:type: Optional[datetime.datetime]
:type: datetime.datetime | None

Date and time this profile was first created (see 7.2.1 of ICC.1:2010).

Expand Down Expand Up @@ -156,74 +156,74 @@ can be easily displayed in a chromaticity diagram, for example).
not been calculated (see 7.2.18 of ICC.1:2010).

.. py:attribute:: copyright
:type: Optional[str]
:type: str | None

The text copyright information for the profile (see 9.2.21 of ICC.1:2010).

.. py:attribute:: manufacturer
:type: Optional[str]
:type: str | None

The (English) display string for the device manufacturer (see
9.2.22 of ICC.1:2010).

.. py:attribute:: model
:type: Optional[str]
:type: str | None

The (English) display string for the device model of the device
for which this profile is created (see 9.2.23 of ICC.1:2010).

.. py:attribute:: profile_description
:type: Optional[str]
:type: str | None

The (English) display string for the profile description (see
9.2.41 of ICC.1:2010).

.. py:attribute:: target
:type: Optional[str]
:type: str | None

The name of the registered characterization data set, or the
measurement data for a characterization target (see 9.2.14 of
ICC.1:2010).

.. py:attribute:: red_colorant
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The first column in the matrix used in matrix/TRC transforms (see 9.2.44 of ICC.1:2010).

The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: green_colorant
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The second column in the matrix used in matrix/TRC transforms (see 9.2.30 of ICC.1:2010).

The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: blue_colorant
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The third column in the matrix used in matrix/TRC transforms (see 9.2.4 of ICC.1:2010).

The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: luminance
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The absolute luminance of emissive devices in candelas per square
metre as described by the Y channel (see 9.2.32 of ICC.1:2010).

The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: chromaticity
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | None

The data of the phosphor/colorant chromaticity set used (red,
green and blue channels, see 9.2.16 of ICC.1:2010).

The value is in the format ``((x, y, Y), (x, y, Y), (x, y, Y))``, if available.

.. py:attribute:: chromatic_adaption
:type: tuple[tuple[float]]
:type: tuple[tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]], tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]]] | None

The chromatic adaption matrix converts a color measured using the
actual illumination conditions and relative to the actual adopted
Expand All @@ -249,34 +249,34 @@ can be easily displayed in a chromaticity diagram, for example).
9.2.19 of ICC.1:2010).

.. py:attribute:: colorimetric_intent
:type: Optional[str]
:type: str | None

4-character string (padded with whitespace) identifying the image
state of PCS colorimetry produced using the colorimetric intent
transforms (see 9.2.20 of ICC.1:2010 for details).

.. py:attribute:: perceptual_rendering_intent_gamut
:type: Optional[str]
:type: str | None

4-character string (padded with whitespace) identifying the (one)
standard reference medium gamut (see 9.2.37 of ICC.1:2010 for
details).

.. py:attribute:: saturation_rendering_intent_gamut
:type: Optional[str]
:type: str | None

4-character string (padded with whitespace) identifying the (one)
standard reference medium gamut (see 9.2.37 of ICC.1:2010 for
details).

.. py:attribute:: technology
:type: Optional[str]
:type: str | None

4-character string (padded with whitespace) identifying the device
technology (see 9.2.47 of ICC.1:2010 for details).

.. py:attribute:: media_black_point
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

This tag specifies the media black point and is used for
generating absolute colorimetry.
Expand All @@ -287,41 +287,41 @@ can be easily displayed in a chromaticity diagram, for example).
The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: media_white_point_temperature
:type: Optional[float]
:type: float | None

Calculates the white point temperature (see the LCMS documentation
for more information).

.. py:attribute:: viewing_condition
:type: Optional[str]
:type: str | None

The (English) display string for the viewing conditions (see
9.2.48 of ICC.1:2010).

.. py:attribute:: screening_description
:type: Optional[str]
:type: str | None

The (English) display string for the screening conditions.

This tag was available in ICC 3.2, but it is removed from
version 4.

.. py:attribute:: red_primary
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The XYZ-transformed of the RGB primary color red (1, 0, 0).

The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: green_primary
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The XYZ-transformed of the RGB primary color green (0, 1, 0).

The value is in the format ``((X, Y, Z), (x, y, Y))``, if available.

.. py:attribute:: blue_primary
:type: Optional[tuple[tuple[float]]]
:type: tuple[tuple[float, float, float], tuple[float, float, float]] | None

The XYZ-transformed of the RGB primary color blue (0, 0, 1).

Expand All @@ -334,7 +334,7 @@ can be easily displayed in a chromaticity diagram, for example).
documentation on LCMS).

.. py:attribute:: clut
:type: dict[tuple[bool]]
:type: dict[int, tuple[bool, bool, bool]] | None

Returns a dictionary of all supported intents and directions for
the CLUT model.
Expand All @@ -353,7 +353,7 @@ can be easily displayed in a chromaticity diagram, for example).
that intent is supported for that direction.

.. py:attribute:: intent_supported
:type: dict[tuple[bool]]
:type: dict[int, tuple[bool, bool, bool]] | None

Returns a dictionary of all supported intents and directions.

Expand All @@ -372,7 +372,7 @@ can be easily displayed in a chromaticity diagram, for example).

There is one function defined on the class:

.. py:method:: is_intent_supported(intent, direction)
.. py:method:: is_intent_supported(intent: int, direction: int, /)

Returns if the intent is supported for the given direction.

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ ignore = [
"E221", # Multiple spaces before operator
"E226", # Missing whitespace around arithmetic operator
"E241", # Multiple spaces after ','
"PYI026", # flake8-pyi: typing.TypeAlias added in Python 3.10
"PYI034", # flake8-pyi: typing.Self added in Python 3.11
]

Expand Down