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

Unix viewers #6005

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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 .github/workflows/test-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ jobs:
amazon-2-amd64,
arch,
centos-7-amd64,
centos-8-amd64,
centos-stream-8-amd64,
debian-10-buster-x86,
debian-11-bullseye-x86,
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-mingw.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch]

jobs:
build:
runs-on: windows-2019
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch]

jobs:
build:
runs-on: windows-2019
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
Expand Down
15 changes: 15 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ Changelog (Pillow)
9.1.0 (unreleased)
------------------

- Enable arm64 for MSVC on Windows #5811
[gaborkertesz-linaro, gaborkertesz]

- Keep IPython/Jupyter text/plain output stable #5891
[shamrin, radarhere]

- Raise an error when performing a negative crop #5972
[radarhere, hugovk]

Expand All @@ -26,6 +32,15 @@ Changelog (Pillow)
- Remove readonly from Image.__eq__ #5930
[hugovk]

9.0.1 (2022-02-03)
------------------

- In show_file, use os.remove to remove temporary images. CVE-2022-24303 #6010
[radarhere, hugovk]

- Restrict builtins within lambdas for ImageMath.eval. CVE-2022-22817 #6009
[radarhere]

9.0.0 (2022-01-02)
------------------

Expand Down
3 changes: 1 addition & 2 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@
class test_image_results:
@staticmethod
def upload(a, b):
a.show()
b.show()
return None

elif "GITHUB_ACTIONS" in os.environ:
HAS_UPLOADER = True
Expand Down
9 changes: 9 additions & 0 deletions Tests/test_file_eps.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ def test_sanity():
assert image2_scale2.format == "EPS"


@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available")
def test_load():
with Image.open(FILE1) as im:
assert im.load()[0, 0] == (255, 255, 255)

# Test again now that it has already been loaded once
assert im.load()[0, 0] == (255, 255, 255)


def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"

Expand Down
22 changes: 15 additions & 7 deletions Tests/test_file_gbr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@
from .helper import assert_image_equal_tofile


def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"

with pytest.raises(SyntaxError):
GbrImagePlugin.GbrImageFile(invalid_file)


def test_gbr_file():
with Image.open("Tests/images/gbr.gbr") as im:
assert_image_equal_tofile(im, "Tests/images/gbr.png")


def test_load():
with Image.open("Tests/images/gbr.gbr") as im:
assert im.load()[0, 0] == (0, 0, 0, 0)

# Test again now that it has already been loaded once
assert im.load()[0, 0] == (0, 0, 0, 0)


def test_multiple_load_operations():
with Image.open("Tests/images/gbr.gbr") as im:
im.load()
im.load()
assert_image_equal_tofile(im, "Tests/images/gbr.png")


def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"

with pytest.raises(SyntaxError):
GbrImagePlugin.GbrImageFile(invalid_file)
8 changes: 8 additions & 0 deletions Tests/test_file_icns.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ def test_sanity():
assert im.format == "ICNS"


def test_load():
with Image.open(TEST_FILE) as im:
assert im.load()[0, 0] == (0, 0, 0, 0)

# Test again now that it has already been loaded once
assert im.load()[0, 0] == (0, 0, 0, 0)


def test_save(tmp_path):
temp_file = str(tmp_path / "temp.icns")

Expand Down
5 changes: 5 additions & 0 deletions Tests/test_file_ico.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ def test_sanity():
assert im.get_format_mimetype() == "image/x-icon"


def test_load():
with Image.open(TEST_ICO_FILE) as im:
assert im.load()[0, 0] == (1, 1, 9, 255)


def test_mask():
with Image.open("Tests/images/hopper_mask.ico") as im:
assert_image_equal_tofile(im, "Tests/images/hopper_mask.png")
Expand Down
16 changes: 10 additions & 6 deletions Tests/test_file_wal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

from .helper import assert_image_equal_tofile

TEST_FILE = "Tests/images/hopper.wal"

def test_open():
# Arrange
TEST_FILE = "Tests/images/hopper.wal"

# Act
def test_open():
with WalImageFile.open(TEST_FILE) as im:

# Assert
assert im.format == "WAL"
assert im.format_description == "Quake2 Texture"
assert im.mode == "P"
Expand All @@ -19,3 +15,11 @@ def test_open():
assert isinstance(im, WalImageFile.WalImageFile)

assert_image_equal_tofile(im, "Tests/images/hopper_wal.png")


def test_load():
with WalImageFile.open(TEST_FILE) as im:
assert im.load()[0, 0] == 122

# Test again now that it has already been loaded once
assert im.load()[0, 0] == 122
6 changes: 6 additions & 0 deletions Tests/test_file_wmf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def test_load_raw():
assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref.png", 2.0)


def test_load():
with Image.open("Tests/images/drawing.emf") as im:
if hasattr(Image.core, "drawwmf"):
assert im.load()[0, 0] == (255, 255, 255)


def test_register_handler(tmp_path):
class TestHandler:
methodCalled = False
Expand Down
12 changes: 10 additions & 2 deletions Tests/test_imagemath.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,17 @@ def test_ops():
assert pixel(ImageMath.eval("float(B)**33", images)) == "F 8589934592.0"


def test_prevent_exec():
@pytest.mark.parametrize(
"expression",
(
"exec('pass')",
"(lambda: exec('pass'))()",
"(lambda: (lambda: exec('pass'))())()",
),
)
def test_prevent_exec(expression):
with pytest.raises(ValueError):
ImageMath.eval("exec('pass')")
ImageMath.eval(expression)


def test_logical():
Expand Down
16 changes: 10 additions & 6 deletions Tests/test_imageshow.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ def show_image(self, image, **options):
ImageShow._viewers.pop(0)


@pytest.mark.skipif(
not on_ci() or is_win32(),
reason="Only run on CIs; hangs on Windows CIs",
@pytest.mark.skip(
reason="""Due to implementation of Unix and Windows viewers",
a program or a test relying on the viewer will not terminate"
"till the image is explicitly closed"
"""
)
def test_show():
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
Expand All @@ -54,7 +56,7 @@ def test_show():
def test_viewer():
viewer = ImageShow.Viewer()

assert viewer.get_format(None) is None
assert viewer.get_format(None) == "PNG"

with pytest.raises(NotImplementedError):
viewer.get_command(None)
Expand Down Expand Up @@ -85,11 +87,13 @@ def test_ipythonviewer():
not on_ci() or is_win32(),
reason="Only run on CIs; hangs on Windows CIs",
)
def test_file_deprecated():
def test_file_deprecated(tmp_path):
f = str(tmp_path / "temp.jpg")
for viewer in ImageShow._viewers:
hopper().save(f)
with pytest.warns(DeprecationWarning):
try:
viewer.show_file(file="test.jpg")
viewer.show_file(file=f)
except NotImplementedError:
pass
with pytest.raises(TypeError):
Expand Down
13 changes: 8 additions & 5 deletions Tests/test_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,9 @@ def test_putdata():
assert len(im.getdata()) == len(arr)


def test_roundtrip_eye():
for dtype in (
@pytest.mark.parametrize(
"dtype",
(
bool,
numpy.bool8,
numpy.int8,
Expand All @@ -202,9 +203,11 @@ def test_roundtrip_eye():
float,
numpy.float32,
numpy.float64,
):
arr = numpy.eye(10, dtype=dtype)
numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr)))
),
)
def test_roundtrip_eye(dtype):
arr = numpy.eye(10, dtype=dtype)
numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr)))


def test_zero_size():
Expand Down
2 changes: 1 addition & 1 deletion depends/install_raqm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# install raqm


archive=libraqm-0.8.0
archive=libraqm-0.9.0

./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz

Expand Down
6 changes: 3 additions & 3 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Many of Pillow's features require external libraries:
* **littlecms** provides color management

* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
above uses liblcms2. Tested with **1.19** and **2.7-2.12**.
above uses liblcms2. Tested with **1.19** and **2.7-2.13**.

* **libwebp** provides the WebP format.

Expand Down Expand Up @@ -453,8 +453,6 @@ These platforms are built and tested for every change.
+----------------------------------+----------------------------+---------------------+
| CentOS 7 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS 8 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| CentOS Stream 8 | 3.9 | x86-64 |
+----------------------------------+----------------------------+---------------------+
| Debian 10 Buster | 3.7 | x86 |
Expand Down Expand Up @@ -530,6 +528,8 @@ These platforms have been reported to work at the versions mentioned.
+----------------------------------+---------------------------+------------------+--------------+
| CentOS 6.3 | 2.7, 3.3 | |x86 |
+----------------------------------+---------------------------+------------------+--------------+
| CentOS 8 | 3.9 | 9.0.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Fedora 23 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+---------------------------+------------------+--------------+
| Ubuntu Linux 12.04 LTS (Precise) | | 2.6, 3.2, 3.3, 3.4, 3.5 | 3.4.1 |x86,x86-64 |
Expand Down
12 changes: 6 additions & 6 deletions src/PIL/EpsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,12 @@ def _find_offset(self, fp):

def load(self, scale=1, transparency=False):
# Load EPS via Ghostscript
if not self.tile:
return
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self.mode = self.im.mode
self._size = self.im.size
self.tile = []
if self.tile:
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self.mode = self.im.mode
self._size = self.im.size
self.tile = []
return Image.Image.load(self)

def load_seek(self, *args, **kwargs):
# we can't incrementally load, so force ImageFile.parser to
Expand Down
10 changes: 4 additions & 6 deletions src/PIL/GbrImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,10 @@ def _open(self):
self._data_size = width * height * color_depth

def load(self):
if self.im:
# Already loaded
return

self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
if not self.im:
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
return Image.Image.load(self)


#
Expand Down
9 changes: 5 additions & 4 deletions src/PIL/IcnsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,21 +286,22 @@ def load(self):
self.best_size[1] * self.best_size[2],
)

Image.Image.load(self)
px = Image.Image.load(self)
if self.im and self.im.size == self.size:
# Already loaded
return
return px
self.load_prepare()
# This is likely NOT the best way to do it, but whatever.
im = self.icns.getimage(self.best_size)

# If this is a PNG or JPEG 2000, it won't be loaded yet
im.load()
px = im.load()

self.im = im.im
self.mode = im.mode
self.size = im.size
self.load_end()

return px


def _save(im, fp, filename):
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/IcoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def size(self, value):
def load(self):
if self.im and self.im.size == self.size:
# Already loaded
return
return Image.Image.load(self)
im = self.ico.getimage(self.size)
# if tile is PNG, it won't really be loaded yet
im.load()
Expand Down