Skip to content

Commit

Permalink
Changed Image mode property to be read-only by default
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Jul 28, 2023
1 parent 07623d1 commit b54cd8a
Show file tree
Hide file tree
Showing 48 changed files with 124 additions and 114 deletions.
6 changes: 6 additions & 0 deletions Tests/test_image.py
Expand Up @@ -135,6 +135,12 @@ def test_width_height(self):
with pytest.raises(AttributeError):
im.size = (3, 4)

def test_set_mode(self):
im = Image.new("RGB", (1, 1))

with pytest.raises(AttributeError):
im.mode = "P"

def test_invalid_image(self):
im = io.BytesIO(b"")
with pytest.raises(UnidentifiedImageError):
Expand Down
4 changes: 2 additions & 2 deletions Tests/test_imagefile.py
Expand Up @@ -136,7 +136,7 @@ def test_no_format(self):

class DummyImageFile(ImageFile.ImageFile):
def _open(self):
self.mode = "RGB"
self._mode = "RGB"
self._size = (1, 1)

im = DummyImageFile(buf)
Expand Down Expand Up @@ -217,7 +217,7 @@ def cleanup(self):
class MockImageFile(ImageFile.ImageFile):
def _open(self):
self.rawmode = "RGBA"
self.mode = "RGBA"
self._mode = "RGBA"
self._size = (200, 200)
self.tile = [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 32, None)]

Expand Down
4 changes: 2 additions & 2 deletions Tests/test_pickle.py
Expand Up @@ -75,13 +75,13 @@ def test_pickle_la_mode_with_palette(tmp_path):

# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
im.mode = "LA"
im._mode = "LA"
with open(filename, "wb") as f:
pickle.dump(im, f, protocol)
with open(filename, "rb") as f:
loaded_im = pickle.load(f)

im.mode = "PA"
im._mode = "PA"
assert im == loaded_im


Expand Down
2 changes: 1 addition & 1 deletion docs/example/DdsImagePlugin.py
Expand Up @@ -225,7 +225,7 @@ def _open(self):

flags, height, width = struct.unpack("<3I", header.read(12))
self._size = (width, height)
self.mode = "RGBA"
self._mode = "RGBA"

pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
struct.unpack("<11I", header.read(44)) # reserved
Expand Down
6 changes: 3 additions & 3 deletions docs/handbook/writing-your-own-image-plugin.rst
Expand Up @@ -72,11 +72,11 @@ true color.
# mode setting
bits = int(header[3])
if bits == 1:
self.mode = "1"
self._mode = "1"
elif bits == 8:
self.mode = "L"
self._mode = "L"
elif bits == 24:
self.mode = "RGB"
self._mode = "RGB"
else:
msg = "unknown number of bits"
raise SyntaxError(msg)
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/BlpImagePlugin.py
Expand Up @@ -266,7 +266,7 @@ def _open(self):
msg = f"Bad BLP magic {repr(self.magic)}"
raise BLPFormatError(msg)

self.mode = "RGBA" if self._blp_alpha_depth else "RGB"
self._mode = "RGBA" if self._blp_alpha_depth else "RGB"
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]


Expand Down
10 changes: 5 additions & 5 deletions src/PIL/BmpImagePlugin.py
Expand Up @@ -163,7 +163,7 @@ def _bitmap(self, header=0, offset=0):
offset += 4 * file_info["colors"]

# ---------------------- Check bit depth for unusual unsupported values
self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
self._mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
if self.mode is None:
msg = f"Unsupported BMP pixel depth ({file_info['bits']})"
raise OSError(msg)
Expand Down Expand Up @@ -200,7 +200,7 @@ def _bitmap(self, header=0, offset=0):
and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
):
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
self.mode = "RGBA" if "A" in raw_mode else self.mode
self._mode = "RGBA" if "A" in raw_mode else self.mode
elif (
file_info["bits"] in (24, 16)
and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
Expand All @@ -214,7 +214,7 @@ def _bitmap(self, header=0, offset=0):
raise OSError(msg)
elif file_info["compression"] == self.RAW:
if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset
raw_mode, self.mode = "BGRA", "RGBA"
raw_mode, self._mode = "BGRA", "RGBA"
elif file_info["compression"] in (self.RLE8, self.RLE4):
decoder_name = "bmp_rle"
else:
Expand Down Expand Up @@ -245,10 +245,10 @@ def _bitmap(self, header=0, offset=0):

# ------- If all colors are grey, white or black, ditch palette
if greyscale:
self.mode = "1" if file_info["colors"] == 2 else "L"
self._mode = "1" if file_info["colors"] == 2 else "L"
raw_mode = self.mode
else:
self.mode = "P"
self._mode = "P"
self.palette = ImagePalette.raw(
"BGRX" if padding == 4 else "BGR", palette
)
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/BufrStubImagePlugin.py
Expand Up @@ -46,7 +46,7 @@ def _open(self):
self.fp.seek(offset)

# make something up
self.mode = "F"
self._mode = "F"
self._size = 1, 1

loader = self._load()
Expand Down
22 changes: 11 additions & 11 deletions src/PIL/DdsImagePlugin.py
Expand Up @@ -128,7 +128,7 @@ def _open(self):

flags, height, width = struct.unpack("<3I", header.read(12))
self._size = (width, height)
self.mode = "RGBA"
self._mode = "RGBA"

pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
struct.unpack("<11I", header.read(44)) # reserved
Expand All @@ -141,9 +141,9 @@ def _open(self):
if pfflags & DDPF_LUMINANCE:
# Texture contains uncompressed L or LA data
if pfflags & DDPF_ALPHAPIXELS:
self.mode = "LA"
self._mode = "LA"
else:
self.mode = "L"
self._mode = "L"

self.tile = [("raw", (0, 0) + self.size, 0, (self.mode, 0, 1))]
elif pfflags & DDPF_RGB:
Expand All @@ -153,7 +153,7 @@ def _open(self):
if pfflags & DDPF_ALPHAPIXELS:
rawmode += masks[0xFF000000]
else:
self.mode = "RGB"
self._mode = "RGB"
rawmode += masks[0xFF0000] + masks[0xFF00] + masks[0xFF]

self.tile = [("raw", (0, 0) + self.size, 0, (rawmode[::-1], 0, 1))]
Expand All @@ -172,15 +172,15 @@ def _open(self):
elif fourcc == b"ATI1":
self.pixel_format = "BC4"
n = 4
self.mode = "L"
self._mode = "L"
elif fourcc == b"ATI2":
self.pixel_format = "BC5"
n = 5
self.mode = "RGB"
self._mode = "RGB"
elif fourcc == b"BC5S":
self.pixel_format = "BC5S"
n = 5
self.mode = "RGB"
self._mode = "RGB"
elif fourcc == b"DX10":
data_start += 20
# ignoring flags which pertain to volume textures and cubemaps
Expand All @@ -189,19 +189,19 @@ def _open(self):
if dxgi_format in (DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM):
self.pixel_format = "BC5"
n = 5
self.mode = "RGB"
self._mode = "RGB"
elif dxgi_format == DXGI_FORMAT_BC5_SNORM:
self.pixel_format = "BC5S"
n = 5
self.mode = "RGB"
self._mode = "RGB"
elif dxgi_format == DXGI_FORMAT_BC6H_UF16:
self.pixel_format = "BC6H"
n = 6
self.mode = "RGB"
self._mode = "RGB"
elif dxgi_format == DXGI_FORMAT_BC6H_SF16:
self.pixel_format = "BC6HS"
n = 6
self.mode = "RGB"
self._mode = "RGB"
elif dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7"
n = 7
Expand Down
8 changes: 4 additions & 4 deletions src/PIL/EpsImagePlugin.py
Expand Up @@ -227,7 +227,7 @@ def _open(self):
# go to offset - start of "%!PS"
self.fp.seek(offset)

self.mode = "RGB"
self._mode = "RGB"
self._size = None

byte_arr = bytearray(255)
Expand Down Expand Up @@ -344,10 +344,10 @@ def check_required_header_comments():
]

if bit_depth == 1:
self.mode = "1"
self._mode = "1"
elif bit_depth == 8:
try:
self.mode = self.mode_map[mode_id]
self._mode = self.mode_map[mode_id]
except ValueError:
break
else:
Expand Down Expand Up @@ -391,7 +391,7 @@ def load(self, scale=1, transparency=False):
# Load EPS via Ghostscript
if self.tile:
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
self.mode = self.im.mode
self._mode = self.im.mode
self._size = self.im.size
self.tile = []
return Image.Image.load(self)
Expand Down
8 changes: 4 additions & 4 deletions src/PIL/FitsImagePlugin.py
Expand Up @@ -51,14 +51,14 @@ def _open(self):

number_of_bits = int(headers[b"BITPIX"])
if number_of_bits == 8:
self.mode = "L"
self._mode = "L"
elif number_of_bits == 16:
self.mode = "I"
self._mode = "I"

Check warning on line 56 in src/PIL/FitsImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/FitsImagePlugin.py#L56

Added line #L56 was not covered by tests
# rawmode = "I;16S"
elif number_of_bits == 32:
self.mode = "I"
self._mode = "I"

Check warning on line 59 in src/PIL/FitsImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/FitsImagePlugin.py#L59

Added line #L59 was not covered by tests
elif number_of_bits in (-32, -64):
self.mode = "F"
self._mode = "F"

Check warning on line 61 in src/PIL/FitsImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/FitsImagePlugin.py#L61

Added line #L61 was not covered by tests
# rawmode = "F" if number_of_bits == -32 else "F;64F"

offset = math.ceil(self.fp.tell() / 2880) * 2880
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/FliImagePlugin.py
Expand Up @@ -56,7 +56,7 @@ def _open(self):
self.is_animated = self.n_frames > 1

# image characteristics
self.mode = "P"
self._mode = "P"
self._size = i16(s, 8), i16(s, 10)

# animation speed
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/FpxImagePlugin.py
Expand Up @@ -106,7 +106,7 @@ def _open_index(self, index=1):
# note: for now, we ignore the "uncalibrated" flag
colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF)

self.mode, self.rawmode = MODES[tuple(colors)]
self._mode, self.rawmode = MODES[tuple(colors)]

# load JPEG tables, if any
self.jpeg = {}
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/FtexImagePlugin.py
Expand Up @@ -77,7 +77,7 @@ def _open(self):
self._size = struct.unpack("<2i", self.fp.read(8))
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))

self.mode = "RGB"
self._mode = "RGB"

# Only support single-format files.
# I don't know of any multi-format file.
Expand All @@ -90,7 +90,7 @@ def _open(self):
data = self.fp.read(mipmap_size)

if format == Format.DXT1:
self.mode = "RGBA"
self._mode = "RGBA"
self.tile = [("bcn", (0, 0) + self.size, 0, 1)]
elif format == Format.UNCOMPRESSED:
self.tile = [("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))]
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/GbrImagePlugin.py
Expand Up @@ -73,9 +73,9 @@ def _open(self):
comment = self.fp.read(comment_length)[:-1]

if color_depth == 1:
self.mode = "L"
self._mode = "L"

Check warning on line 76 in src/PIL/GbrImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/GbrImagePlugin.py#L76

Added line #L76 was not covered by tests
else:
self.mode = "RGBA"
self._mode = "RGBA"

self._size = width, height

Expand Down
2 changes: 1 addition & 1 deletion src/PIL/GdImageFile.py
Expand Up @@ -51,7 +51,7 @@ def _open(self):
msg = "Not a valid GD 2.x .gd file"
raise SyntaxError(msg)

self.mode = "L" # FIXME: "P"
self._mode = "L" # FIXME: "P"
self._size = i16(s, 2), i16(s, 4)

true_color = s[6]
Expand Down
18 changes: 9 additions & 9 deletions src/PIL/GifImagePlugin.py
Expand Up @@ -304,11 +304,11 @@ def _seek(self, frame, update_image=True):
if frame == 0:
if self._frame_palette:
if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS:
self.mode = "RGBA" if frame_transparency is not None else "RGB"
self._mode = "RGBA" if frame_transparency is not None else "RGB"
else:
self.mode = "P"
self._mode = "P"
else:
self.mode = "L"
self._mode = "L"

if not palette and self.global_palette:
from copy import copy
Expand All @@ -325,10 +325,10 @@ def _seek(self, frame, update_image=True):
if "transparency" in self.info:
self.im.putpalettealpha(self.info["transparency"], 0)
self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG)
self.mode = "RGBA"
self._mode = "RGBA"
del self.info["transparency"]
else:
self.mode = "RGB"
self._mode = "RGB"
self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG)

def _rgb(color):
Expand Down Expand Up @@ -424,7 +424,7 @@ def load_prepare(self):
self.im.putpalette(*self._frame_palette.getdata())
else:
self.im = None
self.mode = temp_mode
self._mode = temp_mode
self._frame_palette = None

super().load_prepare()
Expand All @@ -434,9 +434,9 @@ def load_end(self):
if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS:
if self._frame_transparency is not None:
self.im.putpalettealpha(self._frame_transparency, 0)
self.mode = "RGBA"
self._mode = "RGBA"
else:
self.mode = "RGB"
self._mode = "RGB"
self.im = self.im.convert(self.mode, Image.Dither.FLOYDSTEINBERG)
return
if not self._prev_im:
Expand All @@ -449,7 +449,7 @@ def load_end(self):
frame_im = self._crop(frame_im, self.dispose_extent)

self.im = self._prev_im
self.mode = self.im.mode
self._mode = self.im.mode
if frame_im.mode == "RGBA":
self.im.paste(frame_im, self.dispose_extent, frame_im)
else:
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/GribStubImagePlugin.py
Expand Up @@ -46,7 +46,7 @@ def _open(self):
self.fp.seek(offset)

# make something up
self.mode = "F"
self._mode = "F"
self._size = 1, 1

loader = self._load()
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/Hdf5StubImagePlugin.py
Expand Up @@ -46,7 +46,7 @@ def _open(self):
self.fp.seek(offset)

# make something up
self.mode = "F"
self._mode = "F"
self._size = 1, 1

loader = self._load()
Expand Down

0 comments on commit b54cd8a

Please sign in to comment.