Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
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
Loading