Merge branch 'main' into plainPPM

This commit is contained in:
Andrew Murray 2022-03-06 13:38:19 +11:00 committed by GitHub
commit d20f39d02e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 179 additions and 142 deletions

View File

@ -31,13 +31,13 @@ jobs:
language: python language: python
dry-run: false dry-run: false
- name: Upload New Crash - name: Upload New Crash
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: failure() && steps.build.outcome == 'success' if: failure() && steps.build.outcome == 'success'
with: with:
name: artifacts name: artifacts
path: ./out/artifacts path: ./out/artifacts
- name: Upload Legacy Crash - name: Upload Legacy Crash
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: steps.run.outcome == 'success' if: steps.run.outcome == 'success'
with: with:
name: crash name: crash

View File

@ -10,7 +10,7 @@ jobs:
name: Lint name: Lint
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: pre-commit cache - name: pre-commit cache
uses: actions/cache@v2 uses: actions/cache@v2
@ -21,7 +21,7 @@ jobs:
lint-pre-commit- lint-pre-commit-
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v3
with: with:
python-version: "3.10" python-version: "3.10"
cache: pip cache: pip

View File

@ -41,7 +41,7 @@ jobs:
name: ${{ matrix.docker }} name: ${{ matrix.docker }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Build system information - name: Build system information
run: python3 .github/workflows/system-info.py run: python3 .github/workflows/system-info.py

View File

@ -29,7 +29,7 @@ jobs:
steps: steps:
- name: Checkout Pillow - name: Checkout Pillow
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Set up shell - name: Set up shell
run: echo "C:\msys64\usr\bin\" >> $env:GITHUB_PATH run: echo "C:\msys64\usr\bin\" >> $env:GITHUB_PATH

View File

@ -28,7 +28,7 @@ jobs:
name: ${{ matrix.docker }} name: ${{ matrix.docker }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Build system information - name: Build system information
run: python3 .github/workflows/system-info.py run: python3 .github/workflows/system-info.py

View File

@ -23,17 +23,17 @@ jobs:
steps: steps:
- name: Checkout Pillow - name: Checkout Pillow
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Checkout cached dependencies - name: Checkout cached dependencies
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
repository: python-pillow/pillow-depends repository: python-pillow/pillow-depends
path: winbuild\depends path: winbuild\depends
# sets env: pythonLocation # sets env: pythonLocation
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }} architecture: ${{ matrix.architecture }}
@ -156,7 +156,7 @@ jobs:
shell: bash shell: bash
- name: Upload errors - name: Upload errors
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: errors name: errors
@ -182,7 +182,7 @@ jobs:
winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel
shell: cmd shell: cmd
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v3
if: "github.event_name != 'pull_request'" if: "github.event_name != 'pull_request'"
with: with:
name: ${{ steps.wheel.outputs.dist }} name: ${{ steps.wheel.outputs.dist }}

View File

@ -36,10 +36,10 @@ jobs:
name: ${{ matrix.os }} Python ${{ matrix.python-version }} name: ${{ matrix.os }} Python ${{ matrix.python-version }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2 uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
cache: pip cache: pip
@ -84,7 +84,7 @@ jobs:
mkdir -p Tests/errors mkdir -p Tests/errors
- name: Upload errors - name: Upload errors
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: errors name: errors

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Scan - name: Scan
uses: tidelift/alignment-action@main uses: tidelift/alignment-action@main
env: env:

View File

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: f1d4e742c91dd5179d742b0db9293c4472b765f8 # frozen: 21.12b0 rev: fc0be6eb1e2a96091e6f64009ee5e9081bf8b6c6 # frozen: 22.1.0
hooks: hooks:
- id: black - id: black
args: ["--target-version", "py37"] args: ["--target-version", "py37"]
@ -19,7 +19,7 @@ repos:
- id: yesqa - id: yesqa
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: 3592548bbd98528887eeed63486cf6c9bae00b98 # frozen: v1.1.10 rev: ca52c4245639abd55c970e6bbbca95cab3de22d8 # frozen: v1.1.13
hooks: hooks:
- id: remove-tabs - id: remove-tabs
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)

View File

@ -4,5 +4,5 @@ import sys
from PIL import Image from PIL import Image
if sys.maxsize < 2 ** 32: if sys.maxsize < 2**32:
im = Image.new("L", (999999, 999999), 0) im = Image.new("L", (999999, 999999), 0)

View File

@ -23,7 +23,7 @@ YDIM = 32769
XDIM = 48000 XDIM = 48000
pytestmark = pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="requires 64-bit system") pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit system")
def _write_png(tmp_path, xdim, ydim): def _write_png(tmp_path, xdim, ydim):

View File

@ -19,7 +19,7 @@ YDIM = 32769
XDIM = 48000 XDIM = 48000
pytestmark = pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="requires 64-bit system") pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit system")
def _write_png(tmp_path, xdim, ydim): def _write_png(tmp_path, xdim, ydim):

View File

@ -110,9 +110,9 @@ class TestCoreMemory:
with pytest.raises(ValueError): with pytest.raises(ValueError):
Image.core.set_blocks_max(-1) Image.core.set_blocks_max(-1)
if sys.maxsize < 2 ** 32: if sys.maxsize < 2**32:
with pytest.raises(ValueError): with pytest.raises(ValueError):
Image.core.set_blocks_max(2 ** 29) Image.core.set_blocks_max(2**29)
@pytest.mark.skipif(is_pypy(), reason="Images not collected") @pytest.mark.skipif(is_pypy(), reason="Images not collected")
def test_set_blocks_max_stats(self): def test_set_blocks_max_stats(self):

View File

@ -196,6 +196,13 @@ def test__accept_false():
assert not output assert not output
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
DdsImagePlugin.DdsImageFile(invalid_file)
def test_short_header(): def test_short_header():
"""Check a short header""" """Check a short header"""
with open(TEST_FILE_DXT5, "rb") as f: with open(TEST_FILE_DXT5, "rb") as f:

View File

@ -16,6 +16,13 @@ def test_load_dxt1():
assert_image_similar(im, target.convert("RGBA"), 15) assert_image_similar(im, target.convert("RGBA"), 15)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
FtexImagePlugin.FtexImageFile(invalid_file)
def test_constants_deprecation(): def test_constants_deprecation():
for enum, prefix in { for enum, prefix in {
FtexImagePlugin.Format: "FORMAT_", FtexImagePlugin.Format: "FORMAT_",

View File

@ -218,7 +218,7 @@ class TestFileLibTiff(LibTiffTestCase):
values = { values = {
2: "test", 2: "test",
3: 1, 3: 1,
4: 2 ** 20, 4: 2**20,
5: TiffImagePlugin.IFDRational(100, 1), 5: TiffImagePlugin.IFDRational(100, 1),
12: 1.05, 12: 1.05,
} }
@ -1019,7 +1019,7 @@ class TestFileLibTiff(LibTiffTestCase):
im = hopper("RGB").resize((256, 256)) im = hopper("RGB").resize((256, 256))
out = str(tmp_path / "temp.tif") out = str(tmp_path / "temp.tif")
TiffImagePlugin.STRIP_SIZE = 2 ** 18 TiffImagePlugin.STRIP_SIZE = 2**18
try: try:
im.save(out, compression="tiff_adobe_deflate") im.save(out, compression="tiff_adobe_deflate")

View File

@ -258,7 +258,7 @@ def test_ifd_unsigned_rational(tmp_path):
im = hopper() im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2() info = TiffImagePlugin.ImageFileDirectory_v2()
max_long = 2 ** 32 - 1 max_long = 2**32 - 1
# 4 bytes unsigned long # 4 bytes unsigned long
numerator = max_long numerator = max_long
@ -290,8 +290,8 @@ def test_ifd_signed_rational(tmp_path):
info = TiffImagePlugin.ImageFileDirectory_v2() info = TiffImagePlugin.ImageFileDirectory_v2()
# pair of 4 byte signed longs # pair of 4 byte signed longs
numerator = 2 ** 31 - 1 numerator = 2**31 - 1
denominator = -(2 ** 31) denominator = -(2**31)
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
@ -302,8 +302,8 @@ def test_ifd_signed_rational(tmp_path):
assert numerator == reloaded.tag_v2[37380].numerator assert numerator == reloaded.tag_v2[37380].numerator
assert denominator == reloaded.tag_v2[37380].denominator assert denominator == reloaded.tag_v2[37380].denominator
numerator = -(2 ** 31) numerator = -(2**31)
denominator = 2 ** 31 - 1 denominator = 2**31 - 1
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
@ -315,7 +315,7 @@ def test_ifd_signed_rational(tmp_path):
assert denominator == reloaded.tag_v2[37380].denominator assert denominator == reloaded.tag_v2[37380].denominator
# out of bounds of 4 byte signed long # out of bounds of 4 byte signed long
numerator = -(2 ** 31) - 1 numerator = -(2**31) - 1
denominator = 1 denominator = 1
info[37380] = TiffImagePlugin.IFDRational(numerator, denominator) info[37380] = TiffImagePlugin.IFDRational(numerator, denominator)
@ -324,7 +324,7 @@ def test_ifd_signed_rational(tmp_path):
im.save(out, tiffinfo=info, compression="raw") im.save(out, tiffinfo=info, compression="raw")
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
assert 2 ** 31 - 1 == reloaded.tag_v2[37380].numerator assert 2**31 - 1 == reloaded.tag_v2[37380].numerator
assert -1 == reloaded.tag_v2[37380].denominator assert -1 == reloaded.tag_v2[37380].denominator

View File

@ -128,7 +128,7 @@ class TestFileWebp:
self._roundtrip(tmp_path, "P", 50.0) self._roundtrip(tmp_path, "P", 50.0)
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system") @pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system")
def test_write_encoding_error_message(self, tmp_path): def test_write_encoding_error_message(self, tmp_path):
temp_file = str(tmp_path / "temp.webp") temp_file = str(tmp_path / "temp.webp")
im = Image.new("RGB", (15000, 15000)) im = Image.new("RGB", (15000, 15000))

View File

@ -2,7 +2,7 @@ from io import BytesIO
import pytest import pytest
from PIL import Image from PIL import Image, XbmImagePlugin
from .helper import hopper from .helper import hopper
@ -63,6 +63,13 @@ def test_open_filename_with_underscore():
assert im.size == (128, 128) assert im.size == (128, 128)
def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
XbmImagePlugin.XbmImageFile(invalid_file)
def test_save_wrong_mode(tmp_path): def test_save_wrong_mode(tmp_path):
im = hopper() im = hopper()
out = str(tmp_path / "temp.xbm") out = str(tmp_path / "temp.xbm")

View File

@ -205,10 +205,10 @@ class TestImageGetPixel(AccessTest):
# see https://github.com/python-pillow/Pillow/issues/452 # see https://github.com/python-pillow/Pillow/issues/452
# pixelaccess is using signed int* instead of uint* # pixelaccess is using signed int* instead of uint*
for mode in ("I;16", "I;16B"): for mode in ("I;16", "I;16B"):
self.check(mode, 2 ** 15 - 1) self.check(mode, 2**15 - 1)
self.check(mode, 2 ** 15) self.check(mode, 2**15)
self.check(mode, 2 ** 15 + 1) self.check(mode, 2**15 + 1)
self.check(mode, 2 ** 16 - 1) self.check(mode, 2**16 - 1)
def test_p_putpixel_rgb_rgba(self): def test_p_putpixel_rgb_rgba(self):
for color in [(255, 0, 0), (255, 0, 0, 255)]: for color in [(255, 0, 0), (255, 0, 0, 255)]:
@ -386,7 +386,7 @@ class TestImagePutPixelError(AccessTest):
def test_putpixel_overflow_error(self, mode): def test_putpixel_overflow_error(self, mode):
im = hopper(mode) im = hopper(mode)
with pytest.raises(OverflowError): with pytest.raises(OverflowError):
im.putpixel((0, 0), 2 ** 80) im.putpixel((0, 0), 2**80)
def test_putpixel_unrecognized_mode(self): def test_putpixel_unrecognized_mode(self):
im = hopper("BGR;15") im = hopper("BGR;15")

View File

@ -38,7 +38,7 @@ def test_long_integers():
assert put(0xFFFFFFFF) == (255, 255, 255, 255) assert put(0xFFFFFFFF) == (255, 255, 255, 255)
assert put(-1) == (255, 255, 255, 255) assert put(-1) == (255, 255, 255, 255)
assert put(-1) == (255, 255, 255, 255) assert put(-1) == (255, 255, 255, 255)
if sys.maxsize > 2 ** 32: if sys.maxsize > 2**32:
assert put(sys.maxsize) == (255, 255, 255, 255) assert put(sys.maxsize) == (255, 255, 255, 255)
else: else:
assert put(sys.maxsize) == (255, 255, 255, 127) assert put(sys.maxsize) == (255, 255, 255, 127)

View File

@ -303,7 +303,7 @@ def test_extended_information():
def assert_truncated_tuple_equal(tup1, tup2, digits=10): def assert_truncated_tuple_equal(tup1, tup2, digits=10):
# Helper function to reduce precision of tuples of floats # Helper function to reduce precision of tuples of floats
# recursively and then check equality. # recursively and then check equality.
power = 10 ** digits power = 10**digits
def truncate_tuple(tuple_or_float): def truncate_tuple(tuple_or_float):
return tuple( return tuple(

View File

@ -32,10 +32,10 @@ def test_rgb():
def checkrgb(r, g, b): def checkrgb(r, g, b):
val = ImageQt.rgb(r, g, b) val = ImageQt.rgb(r, g, b)
val = val % 2 ** 24 # drop the alpha val = val % 2**24 # drop the alpha
assert val >> 16 == r assert val >> 16 == r
assert ((val >> 8) % 2 ** 8) == g assert ((val >> 8) % 2**8) == g
assert val % 2 ** 8 == b assert val % 2**8 == b
checkrgb(0, 0, 0) checkrgb(0, 0, 0)
checkrgb(255, 0, 0) checkrgb(255, 0, 0)

View File

@ -51,8 +51,8 @@ def test_constant():
st = ImageStat.Stat(im) st = ImageStat.Stat(im)
assert st.extrema[0] == (128, 128) assert st.extrema[0] == (128, 128)
assert st.sum[0] == 128 ** 3 assert st.sum[0] == 128**3
assert st.sum2[0] == 128 ** 4 assert st.sum2[0] == 128**4
assert st.mean[0] == 128 assert st.mean[0] == 128
assert st.median[0] == 128 assert st.median[0] == 128
assert st.rms[0] == 128 assert st.rms[0] == 128

View File

@ -36,7 +36,7 @@ def test_tobytes():
Image.MAX_IMAGE_PIXELS = max_pixels Image.MAX_IMAGE_PIXELS = max_pixels
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system") @pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system")
def test_ysize(): def test_ysize():
numpy = pytest.importorskip("numpy", reason="NumPy not installed") numpy = pytest.importorskip("numpy", reason="NumPy not installed")

View File

@ -115,6 +115,6 @@ def test_pdf_repr():
assert pdf_repr(True) == b"true" assert pdf_repr(True) == b"true"
assert pdf_repr(False) == b"false" assert pdf_repr(False) == b"false"
assert pdf_repr(None) == b"null" assert pdf_repr(None) == b"null"
assert pdf_repr(b"a)/b\\(c") == br"(a\)/b\\\(c)" assert pdf_repr(b"a)/b\\(c") == rb"(a\)/b\\\(c)"
assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]" assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]"
assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>" assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>"

View File

@ -210,7 +210,9 @@ class DdsImageFile(ImageFile.ImageFile):
format_description = "DirectDraw Surface" format_description = "DirectDraw Surface"
def _open(self): def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8)) if not _accept(self.fp.read(4)):
raise SyntaxError("not a DDS file")
(header_size,) = struct.unpack("<I", self.fp.read(4))
if header_size != 124: if header_size != 124:
raise OSError(f"Unsupported header size {repr(header_size)}") raise OSError(f"Unsupported header size {repr(header_size)}")
header_bytes = self.fp.read(header_size - 4) header_bytes = self.fp.read(header_size - 4)

View File

@ -14,6 +14,16 @@ for a region of an image.
statistics. You can also pass in a previously calculated histogram. statistics. You can also pass in a previously calculated histogram.
:param image: A PIL image, or a precalculated histogram. :param image: A PIL image, or a precalculated histogram.
.. note::
For a PIL image, calculations rely on the
:py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are
grouped into 256 bins, even if the image has more than 8 bits per
channel. So ``I`` and ``F`` mode images have a maximum ``mean``,
``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum
of more than 255.
:param mask: An optional mask. :param mask: An optional mask.
.. py:attribute:: extrema .. py:attribute:: extrema

View File

@ -167,7 +167,7 @@ def _find_library_dirs_ldconfig():
# Assuming GLIBC's ldconfig (with option -p) # Assuming GLIBC's ldconfig (with option -p)
# Alpine Linux uses musl that can't print cache # Alpine Linux uses musl that can't print cache
args = ["/sbin/ldconfig", "-p"] args = ["/sbin/ldconfig", "-p"]
expr = fr".*\({abi_type}.*\) => (.*)" expr = rf".*\({abi_type}.*\) => (.*)"
env = dict(os.environ) env = dict(os.environ)
env["LC_ALL"] = "C" env["LC_ALL"] = "C"
env["LANG"] = "C" env["LANG"] = "C"

View File

@ -102,7 +102,7 @@ class BmpImageFile(ImageFile.ImageFile):
file_info["height"] = ( file_info["height"] = (
i32(header_data, 4) i32(header_data, 4)
if not file_info["y_flip"] if not file_info["y_flip"]
else 2 ** 32 - i32(header_data, 4) else 2**32 - i32(header_data, 4)
) )
file_info["planes"] = i16(header_data, 8) file_info["planes"] = i16(header_data, 8)
file_info["bits"] = i16(header_data, 10) file_info["bits"] = i16(header_data, 10)
@ -322,7 +322,7 @@ def _save(im, fp, filename, bitmap_header=True):
if bitmap_header: if bitmap_header:
offset = 14 + header + colors * 4 offset = 14 + header + colors * 4
file_size = offset + image file_size = offset + image
if file_size > 2 ** 32 - 1: if file_size > 2**32 - 1:
raise ValueError("File size is too large for the BMP format") raise ValueError("File size is too large for the BMP format")
fp.write( fp.write(
b"BM" # file type (magic) b"BM" # file type (magic)

View File

@ -111,7 +111,9 @@ class DdsImageFile(ImageFile.ImageFile):
format_description = "DirectDraw Surface" format_description = "DirectDraw Surface"
def _open(self): def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8)) if not _accept(self.fp.read(4)):
raise SyntaxError("not a DDS file")
(header_size,) = struct.unpack("<I", self.fp.read(4))
if header_size != 124: if header_size != 124:
raise OSError(f"Unsupported header size {repr(header_size)}") raise OSError(f"Unsupported header size {repr(header_size)}")
header_bytes = self.fp.read(header_size - 4) header_bytes = self.fp.read(header_size - 4)

View File

@ -26,7 +26,11 @@ from ._binary import o8
def _accept(prefix): def _accept(prefix):
return len(prefix) >= 6 and i16(prefix, 4) in [0xAF11, 0xAF12] return (
len(prefix) >= 6
and i16(prefix, 4) in [0xAF11, 0xAF12]
and i16(prefix, 14) in [0, 3] # flags
)
## ##
@ -44,11 +48,7 @@ class FliImageFile(ImageFile.ImageFile):
# HEAD # HEAD
s = self.fp.read(128) s = self.fp.read(128)
if not ( if not (_accept(s) and s[20:22] == b"\x00\x00"):
_accept(s)
and i16(s, 14) in [0, 3] # flags
and s[20:22] == b"\x00\x00" # reserved
):
raise SyntaxError("not an FLI/FLC file") raise SyntaxError("not an FLI/FLC file")
# frames # frames

View File

@ -94,7 +94,8 @@ class FtexImageFile(ImageFile.ImageFile):
format_description = "Texture File Format (IW2:EOC)" format_description = "Texture File Format (IW2:EOC)"
def _open(self): def _open(self):
struct.unpack("<I", self.fp.read(4)) # magic if not _accept(self.fp.read(4)):
raise SyntaxError("not an FTEX file")
struct.unpack("<i", self.fp.read(4)) # version struct.unpack("<i", self.fp.read(4)) # version
self._size = struct.unpack("<2i", self.fp.read(8)) self._size = struct.unpack("<2i", self.fp.read(8))
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8)) mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))

View File

@ -43,9 +43,9 @@ class GbrImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
header_size = i32(self.fp.read(4)) header_size = i32(self.fp.read(4))
version = i32(self.fp.read(4))
if header_size < 20: if header_size < 20:
raise SyntaxError("not a GIMP brush") raise SyntaxError("not a GIMP brush")
version = i32(self.fp.read(4))
if version not in (1, 2): if version not in (1, 2):
raise SyntaxError(f"Unsupported GIMP brush version: {version}") raise SyntaxError(f"Unsupported GIMP brush version: {version}")

View File

@ -38,7 +38,7 @@ class GimpPaletteFile:
break break
# skip fields and comment lines # skip fields and comment lines
if re.match(br"\w+:|#", s): if re.match(rb"\w+:|#", s):
continue continue
if len(s) > 100: if len(s) > 100:
raise SyntaxError("bad palette file") raise SyntaxError("bad palette file")

View File

@ -167,7 +167,7 @@ class IcnsFile:
self.dct = dct = {} self.dct = dct = {}
self.fobj = fobj self.fobj = fobj
sig, filesize = nextheader(fobj) sig, filesize = nextheader(fobj)
if sig != MAGIC: if not _accept(sig):
raise SyntaxError("not an icns file") raise SyntaxError("not an icns file")
i = HEADERSIZE i = HEADERSIZE
while i < filesize: while i < filesize:

View File

@ -100,7 +100,7 @@ for i in range(2, 33):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Read IM directory # Read IM directory
split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$")
def number(s): def number(s):

View File

@ -1492,11 +1492,12 @@ class Image:
def histogram(self, mask=None, extrema=None): def histogram(self, mask=None, extrema=None):
""" """
Returns a histogram for the image. The histogram is returned as Returns a histogram for the image. The histogram is returned as a
a list of pixel counts, one for each pixel value in the source list of pixel counts, one for each pixel value in the source
image. If the image has more than one band, the histograms for image. Counts are grouped into 256 bins for each band, even if
all bands are concatenated (for example, the histogram for an the image has more than 8 bits per band. If the image has more
"RGB" image contains 768 values). than one band, the histograms for all bands are concatenated (for
example, the histogram for an "RGB" image contains 768 values).
A bilevel image (mode "1") is treated as a greyscale ("L") image A bilevel image (mode "1") is treated as a greyscale ("L") image
by this method. by this method.

View File

@ -91,7 +91,7 @@ class Stat:
for i in range(0, len(self.h), 256): for i in range(0, len(self.h), 256):
sum2 = 0.0 sum2 = 0.0
for j in range(256): for j in range(256):
sum2 += (j ** 2) * float(self.h[i + j]) sum2 += (j**2) * float(self.h[i + j])
v.append(sum2) v.append(sum2)
return v return v

View File

@ -22,7 +22,7 @@ from . import Image, ImageFile
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------
field = re.compile(br"([a-z]*) ([^ \r\n]*)") field = re.compile(rb"([a-z]*) ([^ \r\n]*)")
## ##

View File

@ -132,7 +132,7 @@ def _res_to_dpi(num, denom, exp):
calculated as (num / denom) * 10^exp and stored in dots per meter, calculated as (num / denom) * 10^exp and stored in dots per meter,
to floating-point dots per inch.""" to floating-point dots per inch."""
if denom != 0: if denom != 0:
return (254 * num * (10 ** exp)) / (10000 * denom) return (254 * num * (10**exp)) / (10000 * denom)
def _parse_jp2_header(fp): def _parse_jp2_header(fp):

View File

@ -576,42 +576,42 @@ class PdfParser:
self.xref_table[reference.object_id] = (offset, 0) self.xref_table[reference.object_id] = (offset, 0)
return reference return reference
delimiter = br"[][()<>{}/%]" delimiter = rb"[][()<>{}/%]"
delimiter_or_ws = br"[][()<>{}/%\000\011\012\014\015\040]" delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]"
whitespace = br"[\000\011\012\014\015\040]" whitespace = rb"[\000\011\012\014\015\040]"
whitespace_or_hex = br"[\000\011\012\014\015\0400-9a-fA-F]" whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]"
whitespace_optional = whitespace + b"*" whitespace_optional = whitespace + b"*"
whitespace_mandatory = whitespace + b"+" whitespace_mandatory = whitespace + b"+"
# No "\012" aka "\n" or "\015" aka "\r": # No "\012" aka "\n" or "\015" aka "\r":
whitespace_optional_no_nl = br"[\000\011\014\040]*" whitespace_optional_no_nl = rb"[\000\011\014\040]*"
newline_only = br"[\r\n]+" newline_only = rb"[\r\n]+"
newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl
re_trailer_end = re.compile( re_trailer_end = re.compile(
whitespace_mandatory whitespace_mandatory
+ br"trailer" + rb"trailer"
+ whitespace_optional + whitespace_optional
+ br"\<\<(.*\>\>)" + rb"\<\<(.*\>\>)"
+ newline + newline
+ br"startxref" + rb"startxref"
+ newline + newline
+ br"([0-9]+)" + rb"([0-9]+)"
+ newline + newline
+ br"%%EOF" + rb"%%EOF"
+ whitespace_optional + whitespace_optional
+ br"$", + rb"$",
re.DOTALL, re.DOTALL,
) )
re_trailer_prev = re.compile( re_trailer_prev = re.compile(
whitespace_optional whitespace_optional
+ br"trailer" + rb"trailer"
+ whitespace_optional + whitespace_optional
+ br"\<\<(.*?\>\>)" + rb"\<\<(.*?\>\>)"
+ newline + newline
+ br"startxref" + rb"startxref"
+ newline + newline
+ br"([0-9]+)" + rb"([0-9]+)"
+ newline + newline
+ br"%%EOF" + rb"%%EOF"
+ whitespace_optional, + whitespace_optional,
re.DOTALL, re.DOTALL,
) )
@ -655,12 +655,12 @@ class PdfParser:
re_whitespace_optional = re.compile(whitespace_optional) re_whitespace_optional = re.compile(whitespace_optional)
re_name = re.compile( re_name = re.compile(
whitespace_optional whitespace_optional
+ br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + rb"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?="
+ delimiter_or_ws + delimiter_or_ws
+ br")" + rb")"
) )
re_dict_start = re.compile(whitespace_optional + br"\<\<") re_dict_start = re.compile(whitespace_optional + rb"\<\<")
re_dict_end = re.compile(whitespace_optional + br"\>\>" + whitespace_optional) re_dict_end = re.compile(whitespace_optional + rb"\>\>" + whitespace_optional)
@classmethod @classmethod
def interpret_trailer(cls, trailer_data): def interpret_trailer(cls, trailer_data):
@ -689,7 +689,7 @@ class PdfParser:
) )
return trailer return trailer
re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?") re_hashes_in_name = re.compile(rb"([^#]*)(#([0-9a-fA-F]{2}))?")
@classmethod @classmethod
def interpret_name(cls, raw, as_text=False): def interpret_name(cls, raw, as_text=False):
@ -704,53 +704,53 @@ class PdfParser:
else: else:
return bytes(name) return bytes(name)
re_null = re.compile(whitespace_optional + br"null(?=" + delimiter_or_ws + br")") re_null = re.compile(whitespace_optional + rb"null(?=" + delimiter_or_ws + rb")")
re_true = re.compile(whitespace_optional + br"true(?=" + delimiter_or_ws + br")") re_true = re.compile(whitespace_optional + rb"true(?=" + delimiter_or_ws + rb")")
re_false = re.compile(whitespace_optional + br"false(?=" + delimiter_or_ws + br")") re_false = re.compile(whitespace_optional + rb"false(?=" + delimiter_or_ws + rb")")
re_int = re.compile( re_int = re.compile(
whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")" whitespace_optional + rb"([-+]?[0-9]+)(?=" + delimiter_or_ws + rb")"
) )
re_real = re.compile( re_real = re.compile(
whitespace_optional whitespace_optional
+ br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + rb"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?="
+ delimiter_or_ws + delimiter_or_ws
+ br")" + rb")"
) )
re_array_start = re.compile(whitespace_optional + br"\[") re_array_start = re.compile(whitespace_optional + rb"\[")
re_array_end = re.compile(whitespace_optional + br"]") re_array_end = re.compile(whitespace_optional + rb"]")
re_string_hex = re.compile( re_string_hex = re.compile(
whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>" whitespace_optional + rb"\<(" + whitespace_or_hex + rb"*)\>"
) )
re_string_lit = re.compile(whitespace_optional + br"\(") re_string_lit = re.compile(whitespace_optional + rb"\(")
re_indirect_reference = re.compile( re_indirect_reference = re.compile(
whitespace_optional whitespace_optional
+ br"([-+]?[0-9]+)" + rb"([-+]?[0-9]+)"
+ whitespace_mandatory + whitespace_mandatory
+ br"([-+]?[0-9]+)" + rb"([-+]?[0-9]+)"
+ whitespace_mandatory + whitespace_mandatory
+ br"R(?=" + rb"R(?="
+ delimiter_or_ws + delimiter_or_ws
+ br")" + rb")"
) )
re_indirect_def_start = re.compile( re_indirect_def_start = re.compile(
whitespace_optional whitespace_optional
+ br"([-+]?[0-9]+)" + rb"([-+]?[0-9]+)"
+ whitespace_mandatory + whitespace_mandatory
+ br"([-+]?[0-9]+)" + rb"([-+]?[0-9]+)"
+ whitespace_mandatory + whitespace_mandatory
+ br"obj(?=" + rb"obj(?="
+ delimiter_or_ws + delimiter_or_ws
+ br")" + rb")"
) )
re_indirect_def_end = re.compile( re_indirect_def_end = re.compile(
whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")" whitespace_optional + rb"endobj(?=" + delimiter_or_ws + rb")"
) )
re_comment = re.compile( re_comment = re.compile(
br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*" rb"(" + whitespace_optional + rb"%[^\r\n]*" + newline + rb")*"
) )
re_stream_start = re.compile(whitespace_optional + br"stream\r?\n") re_stream_start = re.compile(whitespace_optional + rb"stream\r?\n")
re_stream_end = re.compile( re_stream_end = re.compile(
whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")" whitespace_optional + rb"endstream(?=" + delimiter_or_ws + rb")"
) )
@classmethod @classmethod
@ -876,7 +876,7 @@ class PdfParser:
raise PdfFormatError("unrecognized object: " + repr(data[offset : offset + 32])) raise PdfFormatError("unrecognized object: " + repr(data[offset : offset + 32]))
re_lit_str_token = re.compile( re_lit_str_token = re.compile(
br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))" rb"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))"
) )
escaped_chars = { escaped_chars = {
b"n": b"\n", b"n": b"\n",
@ -922,16 +922,16 @@ class PdfParser:
offset = m.end() offset = m.end()
raise PdfFormatError("unfinished literal string") raise PdfFormatError("unfinished literal string")
re_xref_section_start = re.compile(whitespace_optional + br"xref" + newline) re_xref_section_start = re.compile(whitespace_optional + rb"xref" + newline)
re_xref_subsection_start = re.compile( re_xref_subsection_start = re.compile(
whitespace_optional whitespace_optional
+ br"([0-9]+)" + rb"([0-9]+)"
+ whitespace_mandatory + whitespace_mandatory
+ br"([0-9]+)" + rb"([0-9]+)"
+ whitespace_optional + whitespace_optional
+ newline_only + newline_only
) )
re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") re_xref_entry = re.compile(rb"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)")
def read_xref_table(self, xref_section_offset): def read_xref_table(self, xref_section_offset):
subsection_found = False subsection_found = False

View File

@ -48,7 +48,7 @@ from ._binary import o32be as o32
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
is_cid = re.compile(br"\w\w\w\w").match is_cid = re.compile(rb"\w\w\w\w").match
_MAGIC = b"\211PNG\r\n\032\n" _MAGIC = b"\211PNG\r\n\032\n"

View File

@ -120,7 +120,7 @@ class PpmImageFile(ImageFile.ImageFile):
if maxval > 255: if maxval > 255:
if not mode == "L": if not mode == "L":
raise ValueError(f"Too many colors for band: {maxval}") raise ValueError(f"Too many colors for band: {maxval}")
if maxval < 2 ** 16: if maxval < 2**16:
self.mode = "I" self.mode = "I"
rawmode = "I;16B" rawmode = "I;16B"
else: else:
@ -310,7 +310,7 @@ def _save(im, fp, filename):
elif im.mode == "L": elif im.mode == "L":
rawmode, head = "L", b"P5" rawmode, head = "L", b"P5"
elif im.mode == "I": elif im.mode == "I":
if im.getextrema()[1] < 2 ** 16: if im.getextrema()[1] < 2**16:
rawmode, head = "I;16B", b"P5" rawmode, head = "I;16B", b"P5"
else: else:
rawmode, head = "I;32B", b"P5" rawmode, head = "I;32B", b"P5"

View File

@ -493,7 +493,7 @@ class ImageFileDirectory_v2(MutableMapping):
endianness. endianness.
:param prefix: Override the endianness of the file. :param prefix: Override the endianness of the file.
""" """
if ifh[:4] not in PREFIXES: if not _accept(ifh):
raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)") raise SyntaxError(f"not a TIFF file (header {repr(ifh)} not valid)")
self._prefix = prefix if prefix is not None else ifh[:2] self._prefix = prefix if prefix is not None else ifh[:2]
if self._prefix == MM: if self._prefix == MM:
@ -577,9 +577,9 @@ class ImageFileDirectory_v2(MutableMapping):
else TiffTags.SIGNED_RATIONAL else TiffTags.SIGNED_RATIONAL
) )
elif all(isinstance(v, int) for v in values): elif all(isinstance(v, int) for v in values):
if all(0 <= v < 2 ** 16 for v in values): if all(0 <= v < 2**16 for v in values):
self.tagtype[tag] = TiffTags.SHORT self.tagtype[tag] = TiffTags.SHORT
elif all(-(2 ** 15) < v < 2 ** 15 for v in values): elif all(-(2**15) < v < 2**15 for v in values):
self.tagtype[tag] = TiffTags.SIGNED_SHORT self.tagtype[tag] = TiffTags.SIGNED_SHORT
else: else:
self.tagtype[tag] = ( self.tagtype[tag] = (
@ -734,7 +734,7 @@ class ImageFileDirectory_v2(MutableMapping):
@_register_writer(5) @_register_writer(5)
def write_rational(self, *values): def write_rational(self, *values):
return b"".join( return b"".join(
self._pack("2L", *_limit_rational(frac, 2 ** 32 - 1)) for frac in values self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values
) )
@_register_loader(7, 1) @_register_loader(7, 1)
@ -757,7 +757,7 @@ class ImageFileDirectory_v2(MutableMapping):
@_register_writer(10) @_register_writer(10)
def write_signed_rational(self, *values): def write_signed_rational(self, *values):
return b"".join( return b"".join(
self._pack("2l", *_limit_signed_rational(frac, 2 ** 31 - 1, -(2 ** 31))) self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31)))
for frac in values for frac in values
) )
@ -1670,7 +1670,7 @@ def _save(im, fp, filename):
strip_byte_counts = 1 if stride == 0 else stride * rows_per_strip strip_byte_counts = 1 if stride == 0 else stride * rows_per_strip
strips_per_image = (im.size[1] + rows_per_strip - 1) // rows_per_strip strips_per_image = (im.size[1] + rows_per_strip - 1) // rows_per_strip
ifd[ROWSPERSTRIP] = rows_per_strip ifd[ROWSPERSTRIP] = rows_per_strip
if strip_byte_counts >= 2 ** 16: if strip_byte_counts >= 2**16:
ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG
ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + ( ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + (
stride * im.size[1] - strip_byte_counts * (strips_per_image - 1), stride * im.size[1] - strip_byte_counts * (strips_per_image - 1),

View File

@ -21,7 +21,6 @@
from . import Image, ImageFile from . import Image, ImageFile
from ._binary import i16le as word from ._binary import i16le as word
from ._binary import i32le as dword
from ._binary import si16le as short from ._binary import si16le as short
from ._binary import si32le as _long from ._binary import si32le as _long
@ -112,7 +111,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
if s[22:26] != b"\x01\x00\t\x00": if s[22:26] != b"\x01\x00\t\x00":
raise SyntaxError("Unsupported WMF file format") raise SyntaxError("Unsupported WMF file format")
elif dword(s) == 1 and s[40:44] == b" EMF": elif s[:4] == b"\x01\x00\x00\x00" and s[40:44] == b" EMF":
# enhanced metafile # enhanced metafile
# get bounding box # get bounding box

View File

@ -25,7 +25,7 @@ from . import Image, ImageFile
# XBM header # XBM header
xbm_head = re.compile( xbm_head = re.compile(
br"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+" rb"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+"
b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+" b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+"
b"(?P<hotspot>" b"(?P<hotspot>"
b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+" b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+"
@ -52,18 +52,19 @@ class XbmImageFile(ImageFile.ImageFile):
m = xbm_head.match(self.fp.read(512)) m = xbm_head.match(self.fp.read(512))
if m: if not m:
raise SyntaxError("not a XBM file")
xsize = int(m.group("width")) xsize = int(m.group("width"))
ysize = int(m.group("height")) ysize = int(m.group("height"))
if m.group("hotspot"): if m.group("hotspot"):
self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot"))) self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot")))
self.mode = "1" self.mode = "1"
self._size = xsize, ysize self._size = xsize, ysize
self.tile = [("xbm", (0, 0) + self.size, m.end(), None)] self.tile = [("xbm", (0, 0) + self.size, m.end(), None)]
def _save(im, fp, filename): def _save(im, fp, filename):

View File

@ -464,7 +464,7 @@ def build_dep_all():
if dep_name in disabled: if dep_name in disabled:
continue continue
script = build_dep(dep_name) script = build_dep(dep_name)
lines.append(fr'cmd.exe /c "{{build_dir}}\{script}"') lines.append(rf'cmd.exe /c "{{build_dir}}\{script}"')
lines.append("if errorlevel 1 echo Build failed! && exit /B 1") lines.append("if errorlevel 1 echo Build failed! && exit /B 1")
lines.append("@echo All Pillow dependencies built successfully!") lines.append("@echo All Pillow dependencies built successfully!")
write_script("build_dep_all.cmd", lines) write_script("build_dep_all.cmd", lines)