Skip to content

Commit

Permalink
Merge pull request #7236 from radarhere/pyaccess
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Jun 29, 2023
2 parents b2387f9 + b5ce319 commit 2de1bf2
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 25 deletions.
51 changes: 28 additions & 23 deletions Tests/test_image_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,13 @@ def test_p_putpixel_rgb_rgba(self, mode, color):
assert im.convert("RGBA").getpixel((0, 0)) == (255, 0, 0, alpha)


@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffiPutPixel(TestImagePutPixel):
_need_cffi_access = True


@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.skipif(cffi is None, reason="No CFFI")
class TestCffiGetPixel(TestImageGetPixel):
_need_cffi_access = True
Expand All @@ -252,7 +254,8 @@ def _test_get_access(self, im):
Using private interfaces, forcing a capi access and
a pyaccess for the same image"""
caccess = im.im.pixel_access(False)
access = PyAccess.new(im, False)
with pytest.warns(DeprecationWarning):
access = PyAccess.new(im, False)

w, h = im.size
for x in range(0, w, 10):
Expand All @@ -264,20 +267,16 @@ def _test_get_access(self, im):
access[(access.xsize + 1, access.ysize + 1)]

def test_get_vs_c(self):
rgb = hopper("RGB")
rgb.load()
self._test_get_access(rgb)
self._test_get_access(hopper("RGBA"))
self._test_get_access(hopper("L"))
self._test_get_access(hopper("LA"))
self._test_get_access(hopper("1"))
self._test_get_access(hopper("P"))
# self._test_get_access(hopper('PA')) # PA -- how do I make a PA image?
self._test_get_access(hopper("F"))
with pytest.warns(DeprecationWarning):
rgb = hopper("RGB")
rgb.load()
self._test_get_access(rgb)
for mode in ("RGBA", "L", "LA", "1", "P", "F"):
self._test_get_access(hopper(mode))

for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
im = Image.new(mode, (10, 10), 40000)
self._test_get_access(im)
for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
im = Image.new(mode, (10, 10), 40000)
self._test_get_access(im)

# These don't actually appear to be modes that I can actually make,
# as unpack sets them directly into the I mode.
Expand All @@ -292,7 +291,8 @@ def _test_set_access(self, im, color):
Using private interfaces, forcing a capi access and
a pyaccess for the same image"""
caccess = im.im.pixel_access(False)
access = PyAccess.new(im, False)
with pytest.warns(DeprecationWarning):
access = PyAccess.new(im, False)

w, h = im.size
for x in range(0, w, 10):
Expand All @@ -301,13 +301,15 @@ def _test_set_access(self, im, color):
assert color == caccess[(x, y)]

# Attempt to set the value on a read-only image
access = PyAccess.new(im, True)
with pytest.warns(DeprecationWarning):
access = PyAccess.new(im, True)
with pytest.raises(ValueError):
access[(0, 0)] = color

def test_set_vs_c(self):
rgb = hopper("RGB")
rgb.load()
with pytest.warns(DeprecationWarning):
rgb.load()
self._test_set_access(rgb, (255, 128, 0))
self._test_set_access(hopper("RGBA"), (255, 192, 128, 0))
self._test_set_access(hopper("L"), 128)
Expand All @@ -326,6 +328,7 @@ def test_set_vs_c(self):
# im = Image.new('I;32B', (10, 10), 2**10)
# self._test_set_access(im, 2**13-1)

@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_not_implemented(self):
assert PyAccess.new(hopper("BGR;15")) is None

Expand All @@ -335,7 +338,8 @@ def test_reference_counting(self):

for _ in range(10):
# Do not save references to the image, only to the access object
px = Image.new("L", (size, 1), 0).load()
with pytest.warns(DeprecationWarning):
px = Image.new("L", (size, 1), 0).load()
for i in range(size):
# pixels can contain garbage if image is released
assert px[i, 0] == 0
Expand All @@ -344,12 +348,13 @@ def test_reference_counting(self):
def test_p_putpixel_rgb_rgba(self, mode):
for color in ((255, 0, 0), (255, 0, 0, 127 if mode == "PA" else 255)):
im = Image.new(mode, (1, 1))
access = PyAccess.new(im, False)
access.putpixel((0, 0), color)
with pytest.warns(DeprecationWarning):
access = PyAccess.new(im, False)
access.putpixel((0, 0), color)

if len(color) == 3:
color += (255,)
assert im.convert("RGBA").getpixel((0, 0)) == color
if len(color) == 3:
color += (255,)
assert im.convert("RGBA").getpixel((0, 0)) == color


class TestImagePutPixelError(AccessTest):
Expand Down
12 changes: 12 additions & 0 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ be removed in Pillow 11 (2024-10-15). This class was only made as a helper to
be used internally, so there is no replacement. If you need this functionality
though, it is a very short class that can easily be recreated in your own code.

PyAccess and Image.USE_CFFI_ACCESS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. deprecated:: 10.0.0

Since Pillow's C API is now faster than PyAccess on PyPy,
:py:mod:`~PIL.PyAccess` has been deprecated and will be removed in Pillow
11.0.0 (2024-10-15). Pillow's C API will now be used by default on PyPy instead.

``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, is
similarly deprecated.

Removed features
----------------

Expand Down
13 changes: 13 additions & 0 deletions docs/releasenotes/10.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ Image.coerce_e

This undocumented method has been removed.

Deprecations
============

PyAccess and Image.USE_CFFI_ACCESS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Since Pillow's C API is now faster than PyAccess on PyPy,
:py:mod:`~PIL.PyAccess` has been deprecated and will be removed in Pillow
11.0.0 (2024-10-15). Pillow's C API will now be used by default on PyPy instead.

``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, is
similarly deprecated.

API Changes
===========

Expand Down
3 changes: 1 addition & 2 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ class DecompressionBombError(Exception):
raise


# works everywhere, win for pypy, not cpython
USE_CFFI_ACCESS = hasattr(sys, "pypy_version_info")
USE_CFFI_ACCESS = False
try:
import cffi
except ImportError:
Expand Down
3 changes: 3 additions & 0 deletions src/PIL/PyAccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import logging
import sys

from ._deprecate import deprecate

try:
from cffi import FFI

Expand All @@ -47,6 +49,7 @@

class PyAccess:
def __init__(self, img, readonly=False):
deprecate("PyAccess", 11)
vals = dict(img.im.unsafe_ptrs)
self.readonly = readonly
self.image8 = ffi.cast("unsigned char **", vals["image8"])
Expand Down

0 comments on commit 2de1bf2

Please sign in to comment.