Skip to content

Commit 510bc05

Browse files
committed
Added frombytes() to allow for unlimited parsing
1 parent 0888dc0 commit 510bc05

2 files changed

Lines changed: 38 additions & 9 deletions

File tree

Tests/test_file_gimppalette.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ def test_sanity() -> None:
1616
GimpPaletteFile(fp)
1717

1818
with open("Tests/images/bad_palette_file.gpl", "rb") as fp:
19-
with pytest.raises(SyntaxError):
19+
with pytest.raises(SyntaxError, match="bad palette file"):
2020
GimpPaletteFile(fp)
2121

2222
with open("Tests/images/bad_palette_entry.gpl", "rb") as fp:
23-
with pytest.raises(ValueError):
23+
with pytest.raises(ValueError, match="bad palette entry"):
2424
GimpPaletteFile(fp)
2525

2626

@@ -40,12 +40,26 @@ def test_get_palette(filename: str, size: int) -> None:
4040
assert len(palette) / 3 == size
4141

4242

43-
def test_palette_limit() -> None:
43+
def test_frombytes() -> None:
4444
with open("Tests/images/full_gimp_palette.gpl", "rb") as fp:
45-
data = fp.read()
45+
full_data = fp.read()
4646

4747
# Test that __init__ only reads 256 entries
48-
data = data.replace(b"#\n", b"") + b" 0 0 0 Index 256"
48+
data = full_data.replace(b"#\n", b"") + b" 0 0 0 Index 256"
4949
b = BytesIO(data)
5050
palette = GimpPaletteFile(b)
5151
assert len(palette.palette) / 3 == 256
52+
53+
# Test that frombytes() can read beyond that
54+
palette = GimpPaletteFile.frombytes(data)
55+
assert len(palette.palette) / 3 == 257
56+
57+
# Test that __init__ raises an error if a comment is too long
58+
data = full_data[:-1] + b"a" * 100
59+
b = BytesIO(data)
60+
with pytest.raises(SyntaxError, match="bad palette file"):
61+
palette = GimpPaletteFile(b)
62+
63+
# Test that frombytes() can read the data regardless
64+
palette = GimpPaletteFile.frombytes(data)
65+
assert len(palette.palette) / 3 == 256

src/PIL/GimpPaletteFile.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from __future__ import annotations
1717

1818
import re
19+
from io import BytesIO
1920
from typing import IO
2021

2122

@@ -24,21 +25,26 @@ class GimpPaletteFile:
2425

2526
rawmode = "RGB"
2627

27-
def __init__(self, fp: IO[bytes]) -> None:
28+
def _read(self, fp: IO[bytes], limit: bool = True) -> None:
2829
if not fp.readline().startswith(b"GIMP Palette"):
2930
msg = "not a GIMP palette file"
3031
raise SyntaxError(msg)
3132

3233
palette: list[int] = []
33-
for _ in range(256 + 3):
34+
i = 0
35+
while True:
36+
if limit and i == 256 + 3:
37+
break
38+
39+
i += 1
3440
s = fp.readline()
3541
if not s:
3642
break
3743

3844
# skip fields and comment lines
3945
if re.match(rb"\w+:|#", s):
4046
continue
41-
if len(s) > 100:
47+
if limit and len(s) > 100:
4248
msg = "bad palette file"
4349
raise SyntaxError(msg)
4450

@@ -48,10 +54,19 @@ def __init__(self, fp: IO[bytes]) -> None:
4854
raise ValueError(msg)
4955

5056
palette += (int(v[i]) for i in range(3))
51-
if len(palette) == 768:
57+
if limit and len(palette) == 768:
5258
break
5359

5460
self.palette = bytes(palette)
5561

62+
def __init__(self, fp: IO[bytes]) -> None:
63+
self._read(fp)
64+
65+
@classmethod
66+
def frombytes(cls, data: bytes) -> GimpPaletteFile:
67+
self = cls.__new__(cls)
68+
self._read(BytesIO(data), False)
69+
return self
70+
5671
def getpalette(self) -> tuple[bytes, str]:
5772
return self.palette, self.rawmode

0 commit comments

Comments
 (0)