mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-03 21:24:31 +03:00
Merge branch 'main' of ssh://github.com/python-pillow/Pillow into fix-alpha-for-overlapping-glyphs
This commit is contained in:
commit
11bea8fea6
|
@ -13,7 +13,7 @@ indent_style = space
|
||||||
|
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.yml]
|
[*.{toml,yml}]
|
||||||
# Two-space indentation
|
# Two-space indentation
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v3.13.0
|
rev: v0.1.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: ruff
|
||||||
args: [--py38-plus]
|
args: [--fix, --exit-non-zero-on-fix]
|
||||||
|
|
||||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||||
rev: 23.9.1
|
rev: 23.10.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args: [--target-version=py38]
|
args: [--target-version=py38]
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/isort
|
|
||||||
rev: 5.12.0
|
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/bandit
|
- repo: https://github.com/PyCQA/bandit
|
||||||
rev: 1.7.5
|
rev: 1.7.5
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -23,32 +18,19 @@ repos:
|
||||||
args: [--severity-level=high]
|
args: [--severity-level=high]
|
||||||
files: ^src/
|
files: ^src/
|
||||||
|
|
||||||
- repo: https://github.com/asottile/yesqa
|
|
||||||
rev: v1.5.0
|
|
||||||
hooks:
|
|
||||||
- id: yesqa
|
|
||||||
|
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
rev: v1.5.4
|
rev: v1.5.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: remove-tabs
|
- id: remove-tabs
|
||||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
|
||||||
rev: 6.1.0
|
|
||||||
hooks:
|
|
||||||
- id: flake8
|
|
||||||
additional_dependencies:
|
|
||||||
[flake8-2020, flake8-errmsg, flake8-implicit-str-concat, flake8-logging]
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||||
rev: v1.10.0
|
rev: v1.10.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: python-check-blanket-noqa
|
|
||||||
- id: rst-backticks
|
- id: rst-backticks
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v4.5.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-executables-have-shebangs
|
- id: check-executables-have-shebangs
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
|
@ -61,17 +43,17 @@ repos:
|
||||||
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/
|
exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/
|
||||||
|
|
||||||
- repo: https://github.com/sphinx-contrib/sphinx-lint
|
- repo: https://github.com/sphinx-contrib/sphinx-lint
|
||||||
rev: v0.6.8
|
rev: v0.8.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: sphinx-lint
|
- id: sphinx-lint
|
||||||
|
|
||||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
rev: 1.2.0
|
rev: 1.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyproject-fmt
|
- id: pyproject-fmt
|
||||||
|
|
||||||
- repo: https://github.com/abravalheri/validate-pyproject
|
- repo: https://github.com/abravalheri/validate-pyproject
|
||||||
rev: v0.14
|
rev: v0.15
|
||||||
hooks:
|
hooks:
|
||||||
- id: validate-pyproject
|
- id: validate-pyproject
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ include *.md
|
||||||
include *.py
|
include *.py
|
||||||
include *.rst
|
include *.rst
|
||||||
include *.sh
|
include *.sh
|
||||||
|
include *.toml
|
||||||
include *.txt
|
include *.txt
|
||||||
include *.yaml
|
include *.yaml
|
||||||
include .flake8
|
include .flake8
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -49,7 +49,7 @@ help:
|
||||||
@echo " install make and install"
|
@echo " install make and install"
|
||||||
@echo " install-coverage make and install with C coverage"
|
@echo " install-coverage make and install with C coverage"
|
||||||
@echo " lint run the lint checks"
|
@echo " lint run the lint checks"
|
||||||
@echo " lint-fix run Black and isort to (mostly) fix lint issues"
|
@echo " lint-fix run Ruff to (mostly) fix lint issues"
|
||||||
@echo " release-test run code and package tests before release"
|
@echo " release-test run code and package tests before release"
|
||||||
@echo " test run tests on installed Pillow"
|
@echo " test run tests on installed Pillow"
|
||||||
|
|
||||||
|
@ -118,6 +118,6 @@ lint:
|
||||||
.PHONY: lint-fix
|
.PHONY: lint-fix
|
||||||
lint-fix:
|
lint-fix:
|
||||||
python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black
|
python3 -c "import black" > /dev/null 2>&1 || python3 -m pip install black
|
||||||
python3 -c "import isort" > /dev/null 2>&1 || python3 -m pip install isort
|
|
||||||
python3 -m black --target-version py38 .
|
python3 -m black --target-version py38 .
|
||||||
python3 -m isort .
|
python3 -c "import ruff" > /dev/null 2>&1 || python3 -m pip install ruff
|
||||||
|
python3 -m ruff --fix .
|
||||||
|
|
|
@ -45,7 +45,7 @@ def test_direct():
|
||||||
|
|
||||||
assert caccess[(0, 0)] == access[(0, 0)]
|
assert caccess[(0, 0)] == access[(0, 0)]
|
||||||
|
|
||||||
print("Size: %sx%s" % im.size)
|
print("Size: %sx%s" % im.size) # noqa: UP031
|
||||||
timer(iterate_get, "PyAccess - get", im.size, access)
|
timer(iterate_get, "PyAccess - get", im.size, access)
|
||||||
timer(iterate_set, "PyAccess - set", im.size, access)
|
timer(iterate_set, "PyAccess - set", im.size, access)
|
||||||
timer(iterate_get, "C-api - get", im.size, caccess)
|
timer(iterate_get, "C-api - get", im.size, caccess)
|
||||||
|
|
|
@ -350,7 +350,7 @@ def test_apng_save(tmp_path):
|
||||||
im.load()
|
im.load()
|
||||||
assert not im.is_animated
|
assert not im.is_animated
|
||||||
assert im.n_frames == 1
|
assert im.n_frames == 1
|
||||||
assert im.get_format_mimetype() == "image/apng"
|
assert im.get_format_mimetype() == "image/png"
|
||||||
assert im.info.get("default_image") is None
|
assert im.info.get("default_image") is None
|
||||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||||
|
@ -450,26 +450,29 @@ def test_apng_save_duration_loop(tmp_path):
|
||||||
test_file, save_all=True, append_images=[frame, frame], duration=[500, 100, 150]
|
test_file, save_all=True, append_images=[frame, frame], duration=[500, 100, 150]
|
||||||
)
|
)
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
|
||||||
assert im.n_frames == 1
|
assert im.n_frames == 1
|
||||||
assert im.info.get("duration") == 750
|
assert "duration" not in im.info
|
||||||
|
|
||||||
|
different_frame = Image.new("RGBA", (128, 64))
|
||||||
|
frame.save(
|
||||||
|
test_file,
|
||||||
|
save_all=True,
|
||||||
|
append_images=[frame, different_frame],
|
||||||
|
duration=[500, 100, 150],
|
||||||
|
)
|
||||||
|
with Image.open(test_file) as im:
|
||||||
|
assert im.n_frames == 2
|
||||||
|
assert im.info["duration"] == 600
|
||||||
|
|
||||||
|
im.seek(1)
|
||||||
|
assert im.info["duration"] == 150
|
||||||
|
|
||||||
# test info duration
|
# test info duration
|
||||||
frame.info["duration"] = 750
|
frame.info["duration"] = 300
|
||||||
frame.save(test_file, save_all=True)
|
frame.save(test_file, save_all=True, append_images=[frame, different_frame])
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
assert im.info.get("duration") == 750
|
assert im.n_frames == 2
|
||||||
|
assert im.info["duration"] == 600
|
||||||
|
|
||||||
def test_apng_save_duplicate_duration(tmp_path):
|
|
||||||
test_file = str(tmp_path / "temp.png")
|
|
||||||
frame = Image.new("RGB", (1, 1))
|
|
||||||
|
|
||||||
# Test a single duration is correctly combined across duplicate frames
|
|
||||||
frame.save(test_file, save_all=True, append_images=[frame, frame], duration=500)
|
|
||||||
with Image.open(test_file) as im:
|
|
||||||
assert im.n_frames == 1
|
|
||||||
assert im.info.get("duration") == 1500
|
|
||||||
|
|
||||||
|
|
||||||
def test_apng_save_disposal(tmp_path):
|
def test_apng_save_disposal(tmp_path):
|
||||||
|
@ -674,7 +677,8 @@ def test_seek_after_close():
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ("RGBA", "RGB", "P"))
|
@pytest.mark.parametrize("mode", ("RGBA", "RGB", "P"))
|
||||||
@pytest.mark.parametrize("default_image", (True, False))
|
@pytest.mark.parametrize("default_image", (True, False))
|
||||||
def test_different_modes_in_later_frames(mode, default_image, tmp_path):
|
@pytest.mark.parametrize("duplicate", (True, False))
|
||||||
|
def test_different_modes_in_later_frames(mode, default_image, duplicate, tmp_path):
|
||||||
test_file = str(tmp_path / "temp.png")
|
test_file = str(tmp_path / "temp.png")
|
||||||
|
|
||||||
im = Image.new("L", (1, 1))
|
im = Image.new("L", (1, 1))
|
||||||
|
@ -682,7 +686,7 @@ def test_different_modes_in_later_frames(mode, default_image, tmp_path):
|
||||||
test_file,
|
test_file,
|
||||||
save_all=True,
|
save_all=True,
|
||||||
default_image=default_image,
|
default_image=default_image,
|
||||||
append_images=[Image.new(mode, (1, 1))],
|
append_images=[im.convert(mode) if duplicate else Image.new(mode, (1, 1), 1)],
|
||||||
)
|
)
|
||||||
with Image.open(test_file) as reloaded:
|
with Image.open(test_file) as reloaded:
|
||||||
assert reloaded.mode == mode
|
assert reloaded.mode == mode
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from setuptools.build_meta import * # noqa: F401, F403
|
from setuptools.build_meta import * # noqa: F403
|
||||||
from setuptools.build_meta import build_wheel
|
from setuptools.build_meta import build_wheel
|
||||||
|
|
||||||
backend_class = build_wheel.__self__.__class__
|
backend_class = build_wheel.__self__.__class__
|
||||||
|
|
14
docs/conf.py
14
docs/conf.py
|
@ -318,14 +318,14 @@ def setup(app):
|
||||||
|
|
||||||
|
|
||||||
linkcheck_allowed_redirects = {
|
linkcheck_allowed_redirects = {
|
||||||
r"https://www.bestpractices.dev/projects/6331": r"https://www.bestpractices.dev/en/.*", # noqa: E501
|
r"https://www.bestpractices.dev/projects/6331": r"https://www.bestpractices.dev/en/.*",
|
||||||
r"https://badges.gitter.im/python-pillow/Pillow.svg": r"https://badges.gitter.im/repo.svg", # noqa: E501
|
r"https://badges.gitter.im/python-pillow/Pillow.svg": r"https://badges.gitter.im/repo.svg",
|
||||||
r"https://gitter.im/python-pillow/Pillow?.*": r"https://app.gitter.im/#/room/#python-pillow_Pillow:gitter.im?.*", # noqa: E501
|
r"https://gitter.im/python-pillow/Pillow?.*": r"https://app.gitter.im/#/room/#python-pillow_Pillow:gitter.im?.*",
|
||||||
r"https://pillow.readthedocs.io/?badge=latest": r"https://pillow.readthedocs.io/en/stable/?badge=latest", # noqa: E501
|
r"https://pillow.readthedocs.io/?badge=latest": r"https://pillow.readthedocs.io/en/stable/?badge=latest",
|
||||||
r"https://pillow.readthedocs.io": r"https://pillow.readthedocs.io/en/stable/",
|
r"https://pillow.readthedocs.io": r"https://pillow.readthedocs.io/en/stable/",
|
||||||
r"https://tidelift.com/badges/package/pypi/Pillow?.*": r"https://img.shields.io/badge/.*", # noqa: E501
|
r"https://tidelift.com/badges/package/pypi/Pillow?.*": r"https://img.shields.io/badge/.*",
|
||||||
r"https://zenodo.org/badge/17549/python-pillow/Pillow.svg": r"https://zenodo.org/badge/doi/[\.0-9]+/zenodo.[0-9]+.svg", # noqa: E501
|
r"https://zenodo.org/badge/17549/python-pillow/Pillow.svg": r"https://zenodo.org/badge/doi/[\.0-9]+/zenodo.[0-9]+.svg",
|
||||||
r"https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow": r"https://zenodo.org/record/[0-9]+", # noqa: E501
|
r"https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow": r"https://zenodo.org/record/[0-9]+",
|
||||||
}
|
}
|
||||||
|
|
||||||
# sphinx.ext.extlinks
|
# sphinx.ext.extlinks
|
||||||
|
|
|
@ -10,7 +10,7 @@ Deprecated features
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Below are features which are considered deprecated. Where appropriate,
|
Below are features which are considered deprecated. Where appropriate,
|
||||||
a ``DeprecationWarning`` is issued.
|
a :py:exc:`DeprecationWarning` is issued.
|
||||||
|
|
||||||
PSFile
|
PSFile
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
@ -267,7 +267,7 @@ ImageFile.raise_ioerror
|
||||||
.. deprecated:: 7.2.0
|
.. deprecated:: 7.2.0
|
||||||
.. versionremoved:: 9.0.0
|
.. versionremoved:: 9.0.0
|
||||||
|
|
||||||
``IOError`` was merged into ``OSError`` in Python 3.3.
|
:py:exc:`IOError` was merged into :py:exc:`OSError` in Python 3.3.
|
||||||
So, ``ImageFile.raise_ioerror`` has been removed.
|
So, ``ImageFile.raise_ioerror`` has been removed.
|
||||||
Use ``ImageFile.raise_oserror`` instead.
|
Use ``ImageFile.raise_oserror`` instead.
|
||||||
|
|
||||||
|
@ -293,9 +293,9 @@ im.offset
|
||||||
``im.offset()`` has been removed, call :py:func:`.ImageChops.offset()` instead.
|
``im.offset()`` has been removed, call :py:func:`.ImageChops.offset()` instead.
|
||||||
|
|
||||||
It was documented as deprecated in PIL 1.1.2,
|
It was documented as deprecated in PIL 1.1.2,
|
||||||
raised a ``DeprecationWarning`` since 1.1.5,
|
raised a :py:exc:`DeprecationWarning` since 1.1.5,
|
||||||
an ``Exception`` since Pillow 3.0.0
|
an :py:exc:`Exception` since Pillow 3.0.0
|
||||||
and ``NotImplementedError`` since 3.3.0.
|
and :py:exc:`NotImplementedError` since 3.3.0.
|
||||||
|
|
||||||
Image.fromstring, im.fromstring and im.tostring
|
Image.fromstring, im.fromstring and im.tostring
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -307,9 +307,9 @@ Image.fromstring, im.fromstring and im.tostring
|
||||||
* ``im.fromstring()`` has been removed, call :py:meth:`~PIL.Image.Image.frombytes()` instead.
|
* ``im.fromstring()`` has been removed, call :py:meth:`~PIL.Image.Image.frombytes()` instead.
|
||||||
* ``im.tostring()`` has been removed, call :py:meth:`~PIL.Image.Image.tobytes()` instead.
|
* ``im.tostring()`` has been removed, call :py:meth:`~PIL.Image.Image.tobytes()` instead.
|
||||||
|
|
||||||
They issued a ``DeprecationWarning`` since 2.0.0,
|
They issued a :py:exc:`DeprecationWarning` since 2.0.0,
|
||||||
an ``Exception`` since 3.0.0
|
an :py:exc:`Exception` since 3.0.0
|
||||||
and ``NotImplementedError`` since 3.3.0.
|
and :py:exc:`NotImplementedError` since 3.3.0.
|
||||||
|
|
||||||
ImageCms.CmsProfile attributes
|
ImageCms.CmsProfile attributes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -318,7 +318,7 @@ ImageCms.CmsProfile attributes
|
||||||
.. versionremoved:: 8.0.0
|
.. versionremoved:: 8.0.0
|
||||||
|
|
||||||
Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed. From 6.0.0,
|
Some attributes in :py:class:`PIL.ImageCms.CmsProfile` have been removed. From 6.0.0,
|
||||||
they issued a ``DeprecationWarning``:
|
they issued a :py:exc:`DeprecationWarning`:
|
||||||
|
|
||||||
======================== ===================================================
|
======================== ===================================================
|
||||||
Removed Use instead
|
Removed Use instead
|
||||||
|
@ -442,7 +442,7 @@ PIL.OleFileIO
|
||||||
.. deprecated:: 4.0.0
|
.. deprecated:: 4.0.0
|
||||||
.. versionremoved:: 6.0.0
|
.. versionremoved:: 6.0.0
|
||||||
|
|
||||||
PIL.OleFileIO was removed as a vendored file in Pillow 4.0.0 (2017-01) in favour of
|
``PIL.OleFileIO`` was removed as a vendored file in Pillow 4.0.0 (2017-01) in favour of
|
||||||
the upstream :pypi:`olefile` Python package, and replaced with an ``ImportError`` in 5.0.0
|
the upstream :pypi:`olefile` Python package, and replaced with an :py:exc:`ImportError` in 5.0.0
|
||||||
(2018-01). The deprecated file has now been removed from Pillow. If needed, install from
|
(2018-01). The deprecated file has now been removed from Pillow. If needed, install from
|
||||||
PyPI (eg. ``python3 -m pip install olefile``).
|
PyPI (eg. ``python3 -m pip install olefile``).
|
||||||
|
|
|
@ -20,7 +20,7 @@ the imToolkit package.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
To protect against potential DOS attacks when using arbitrary strings as
|
To protect against potential DOS attacks when using arbitrary strings as
|
||||||
text input, Pillow will raise a ``ValueError`` if the number of characters
|
text input, Pillow will raise a :py:exc:`ValueError` if the number of characters
|
||||||
is over a certain limit, :py:data:`MAX_STRING_LENGTH`.
|
is over a certain limit, :py:data:`MAX_STRING_LENGTH`.
|
||||||
|
|
||||||
This threshold can be changed by setting
|
This threshold can be changed by setting
|
||||||
|
@ -89,5 +89,5 @@ Constants
|
||||||
.. data:: MAX_STRING_LENGTH
|
.. data:: MAX_STRING_LENGTH
|
||||||
|
|
||||||
Set to 1,000,000, to protect against potential DOS attacks. Pillow will
|
Set to 1,000,000, to protect against potential DOS attacks. Pillow will
|
||||||
raise a ``ValueError`` if the number of characters is over this limit. The
|
raise a :py:exc:`ValueError` if the number of characters is over this limit. The
|
||||||
check can be disabled by setting ``ImageFont.MAX_STRING_LENGTH = None``.
|
check can be disabled by setting ``ImageFont.MAX_STRING_LENGTH = None``.
|
||||||
|
|
|
@ -8,7 +8,7 @@ Setting image mode
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
If you attempt to set the mode of an image directly, e.g.
|
If you attempt to set the mode of an image directly, e.g.
|
||||||
``im.mode = "RGBA"``, you will now receive an ``AttributeError``. This is
|
``im.mode = "RGBA"``, you will now receive an :py:exc:`AttributeError`. This is
|
||||||
not about removing existing functionality, but instead about raising an
|
not about removing existing functionality, but instead about raising an
|
||||||
explicit error to prevent later consequences. The ``convert`` method is the
|
explicit error to prevent later consequences. The ``convert`` method is the
|
||||||
correct way to change an image's mode.
|
correct way to change an image's mode.
|
||||||
|
|
|
@ -10,7 +10,7 @@ operations. As a result PIL was unable to open them as images, requiring a wrap
|
||||||
``cStringIO`` or ``BytesIO``.
|
``cStringIO`` or ``BytesIO``.
|
||||||
|
|
||||||
Now new functionality has been added to ``Image.open()`` by way of an ``.seek(0)`` check and
|
Now new functionality has been added to ``Image.open()`` by way of an ``.seek(0)`` check and
|
||||||
catch on exception ``AttributeError`` or ``io.UnsupportedOperation``. If this is caught we
|
catch on exception :py:exc:`AttributeError` or :py:exc:`io.UnsupportedOperation`. If this is caught we
|
||||||
attempt to wrap the object using ``io.BytesIO`` (which will only work on buffer-file-like
|
attempt to wrap the object using ``io.BytesIO`` (which will only work on buffer-file-like
|
||||||
objects).
|
objects).
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ Deprecation Warning when Saving JPEGs
|
||||||
|
|
||||||
JPEG images cannot contain an alpha channel. Pillow prior to 3.4.0
|
JPEG images cannot contain an alpha channel. Pillow prior to 3.4.0
|
||||||
silently drops the alpha channel. With this release Pillow will now
|
silently drops the alpha channel. With this release Pillow will now
|
||||||
issue a ``DeprecationWarning`` when attempting to save a ``RGBA`` mode
|
issue a :py:exc:`DeprecationWarning` when attempting to save a ``RGBA`` mode
|
||||||
image as a JPEG. This will become an error in Pillow 4.2.
|
image as a JPEG. This will become an error in Pillow 4.2.
|
||||||
|
|
||||||
New DDS Decoders
|
New DDS Decoders
|
||||||
|
|
|
@ -8,7 +8,7 @@ Image size
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
If you attempt to set the size of an image directly, e.g.
|
If you attempt to set the size of an image directly, e.g.
|
||||||
``im.size = (100, 100)``, you will now receive an ``AttributeError``. This is
|
``im.size = (100, 100)``, you will now receive an :py:exc:`AttributeError`. This is
|
||||||
not about removing existing functionality, but instead about raising an
|
not about removing existing functionality, but instead about raising an
|
||||||
explicit error to prevent later consequences. The ``resize`` method is the
|
explicit error to prevent later consequences. The ``resize`` method is the
|
||||||
correct way to change an image's size.
|
correct way to change an image's size.
|
||||||
|
@ -16,7 +16,8 @@ correct way to change an image's size.
|
||||||
The exceptions to this are:
|
The exceptions to this are:
|
||||||
|
|
||||||
* The ICO and ICNS image formats, which use ``im.size = (100, 100)`` to select a subimage.
|
* The ICO and ICNS image formats, which use ``im.size = (100, 100)`` to select a subimage.
|
||||||
* The TIFF image format, which now has a ``DeprecationWarning`` for this action, as direct image size setting was previously necessary to work around an issue with tile extents.
|
* The TIFF image format, which now has a :py:exc:`DeprecationWarning` for this action,
|
||||||
|
as direct image size setting was previously necessary to work around an issue with tile extents.
|
||||||
|
|
||||||
|
|
||||||
API Additions
|
API Additions
|
||||||
|
|
|
@ -15,7 +15,7 @@ PNG: Handle IDAT chunks after image end
|
||||||
|
|
||||||
Some PNG images have multiple IDAT chunks. In some cases, Pillow will stop
|
Some PNG images have multiple IDAT chunks. In some cases, Pillow will stop
|
||||||
reading image data before the IDAT chunks finish. A regression caused an
|
reading image data before the IDAT chunks finish. A regression caused an
|
||||||
``EOFError`` exception when previously there was none. This is now fixed, and
|
:py:exc:`EOFError` exception when previously there was none. This is now fixed, and
|
||||||
file reading continues in case there are subsequent text chunks.
|
file reading continues in case there are subsequent text chunks.
|
||||||
|
|
||||||
PNG: MIME type
|
PNG: MIME type
|
||||||
|
@ -30,7 +30,7 @@ File closing
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
A regression caused an unsupported image file to report a
|
A regression caused an unsupported image file to report a
|
||||||
``ValueError: seek of closed file`` exception instead of an ``OSError``. This
|
``ValueError: seek of closed file`` exception instead of an :py:exc:`OSError`. This
|
||||||
has been fixed by ensuring that image plugins only close their internal ``__fp``
|
has been fixed by ensuring that image plugins only close their internal ``__fp``
|
||||||
if they are not the same as ``ImageFile``'s ``fp``, allowing each to manage their own
|
if they are not the same as ``ImageFile``'s ``fp``, allowing each to manage their own
|
||||||
file pointers.
|
file pointers.
|
||||||
|
|
|
@ -103,7 +103,7 @@ ImageCms.CmsProfile attributes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Some attributes in ``ImageCms.CmsProfile`` have been deprecated since Pillow 3.2.0. From
|
Some attributes in ``ImageCms.CmsProfile`` have been deprecated since Pillow 3.2.0. From
|
||||||
6.0.0, they issue a ``DeprecationWarning``:
|
6.0.0, they issue a :py:exc:`DeprecationWarning`:
|
||||||
|
|
||||||
======================== ===============================
|
======================== ===============================
|
||||||
Deprecated Use instead
|
Deprecated Use instead
|
||||||
|
|
|
@ -58,7 +58,7 @@ file. ``ImageFont.FreeTypeFont`` has four new methods,
|
||||||
:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_name` for using named styles, and
|
:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_name` for using named styles, and
|
||||||
:py:meth:`PIL.ImageFont.FreeTypeFont.get_variation_axes` and
|
:py:meth:`PIL.ImageFont.FreeTypeFont.get_variation_axes` and
|
||||||
:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_axes` for using font axes
|
:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_axes` for using font axes
|
||||||
instead. An ``IOError`` will be raised if the font is not a variation font. FreeType
|
instead. An :py:exc:`IOError` will be raised if the font is not a variation font. FreeType
|
||||||
2.9.1 or greater is required.
|
2.9.1 or greater is required.
|
||||||
|
|
||||||
Other Changes
|
Other Changes
|
||||||
|
|
|
@ -85,7 +85,7 @@ Custom unidentified image error
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Pillow will now throw a custom ``UnidentifiedImageError`` when an image cannot be
|
Pillow will now throw a custom ``UnidentifiedImageError`` when an image cannot be
|
||||||
identified. For backwards compatibility, this will inherit from ``OSError``.
|
identified. For backwards compatibility, this will inherit from :py:exc:`OSError`.
|
||||||
|
|
||||||
New argument ``reducing_gap`` for Image.resize() and Image.thumbnail() methods
|
New argument ``reducing_gap`` for Image.resize() and Image.thumbnail() methods
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -7,7 +7,7 @@ Fix another regression seeking PNG files
|
||||||
This fixes a regression introduced in 7.1.0 when adding support for APNG files.
|
This fixes a regression introduced in 7.1.0 when adding support for APNG files.
|
||||||
|
|
||||||
When calling ``seek(n)`` on a regular PNG where ``n > 0``, it failed to raise an
|
When calling ``seek(n)`` on a regular PNG where ``n > 0``, it failed to raise an
|
||||||
``EOFError`` as it should have done, resulting in:
|
:py:exc:`EOFError` as it should have done, resulting in:
|
||||||
|
|
||||||
.. code-block:: pycon
|
.. code-block:: pycon
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,6 @@ a custom :py:class:`~PIL.ImageShow.Viewer` class.
|
||||||
ImageFile.raise_ioerror
|
ImageFile.raise_ioerror
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror``
|
:py:exc:`IOError` was merged into :py:exc:`OSError` in Python 3.3. So, ``ImageFile.raise_ioerror``
|
||||||
is now deprecated and will be removed in a future release. Use
|
is now deprecated and will be removed in a future release. Use
|
||||||
``ImageFile.raise_oserror`` instead.
|
``ImageFile.raise_oserror`` instead.
|
||||||
|
|
|
@ -168,7 +168,7 @@ offset.
|
||||||
Error for large BMP files
|
Error for large BMP files
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Previously, if a BMP file was too large, an ``OSError`` would be raised. Now,
|
Previously, if a BMP file was too large, an :py:exc:`OSError` would be raised. Now,
|
||||||
``DecompressionBombError`` is used instead, as Pillow already uses for other formats.
|
``DecompressionBombError`` is used instead, as Pillow already uses for other formats.
|
||||||
|
|
||||||
Dark theme for docs
|
Dark theme for docs
|
||||||
|
|
|
@ -22,9 +22,10 @@ Catch OSError when checking if destination is sys.stdout
|
||||||
========================================================
|
========================================================
|
||||||
|
|
||||||
In 8.3.0, a check to see if the destination was ``sys.stdout`` when saving an image was
|
In 8.3.0, a check to see if the destination was ``sys.stdout`` when saving an image was
|
||||||
updated. This lead to an OSError being raised if the environment restricted access.
|
updated. This lead to an :py:exc:`OSError` being raised if the environment restricted
|
||||||
|
access.
|
||||||
|
|
||||||
The OSError is now silently caught.
|
The :py:exc:`OSError` is now silently caught.
|
||||||
|
|
||||||
Fixed removing orientation in ImageOps.exif_transpose
|
Fixed removing orientation in ImageOps.exif_transpose
|
||||||
=====================================================
|
=====================================================
|
||||||
|
@ -34,7 +35,7 @@ original image EXIF data was not modified, and the orientation was only removed
|
||||||
the modified copy.
|
the modified copy.
|
||||||
|
|
||||||
However, for certain images the orientation was already missing from the modified
|
However, for certain images the orientation was already missing from the modified
|
||||||
image, leading to a KeyError.
|
image, leading to a :py:exc:`KeyError`.
|
||||||
|
|
||||||
This error has been resolved, and the copying of metadata to the modified image
|
This error has been resolved, and the copying of metadata to the modified image
|
||||||
improved.
|
improved.
|
||||||
|
|
|
@ -63,7 +63,7 @@ a custom :py:class:`~PIL.ImageShow.Viewer` class.
|
||||||
ImageFile.raise_ioerror
|
ImageFile.raise_ioerror
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror``
|
:py:exc:`IOError` was merged into :py:exc:`OSError` in Python 3.3. So, ``ImageFile.raise_ioerror``
|
||||||
has been removed. Use ``ImageFile.raise_oserror`` instead.
|
has been removed. Use ``ImageFile.raise_oserror`` instead.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,14 @@ Raise an error when performing a negative crop
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Performing a negative crop on an image previously just returned a ``(0, 0)`` image. Now
|
Performing a negative crop on an image previously just returned a ``(0, 0)`` image. Now
|
||||||
it will raise a ``ValueError``, to help reduce confusion if a user has unintentionally
|
it will raise a :py:exc:`ValueError`, to help reduce confusion if a user has unintentionally
|
||||||
provided the wrong arguments.
|
provided the wrong arguments.
|
||||||
|
|
||||||
Added specific error if path coordinate type is incorrect
|
Added specific error if path coordinate type is incorrect
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Rather than returning a ``SystemError``, passing the incorrect types of coordinates into
|
Rather than returning a :py:exc:`SystemError`, passing the incorrect types of coordinates into
|
||||||
a path will now raise a more specific ``ValueError``, with the message "incorrect
|
a path will now raise a more specific :py:exc:`ValueError`, with the message "incorrect
|
||||||
coordinate type".
|
coordinate type".
|
||||||
|
|
||||||
Replace requirements.txt with extras
|
Replace requirements.txt with extras
|
||||||
|
|
|
@ -77,8 +77,34 @@ package-dir = {"" = "src"}
|
||||||
[tool.setuptools.dynamic]
|
[tool.setuptools.dynamic]
|
||||||
version = {attr = "PIL.__version__"}
|
version = {attr = "PIL.__version__"}
|
||||||
|
|
||||||
[tool.isort]
|
[tool.ruff]
|
||||||
profile = "black"
|
target-version = "py38"
|
||||||
|
line-length = 88
|
||||||
|
select = [
|
||||||
|
"E", # pycodestyle errors
|
||||||
|
"EM", # flake8-errmsg
|
||||||
|
"F", # pyflakes errors
|
||||||
|
"I", # isort
|
||||||
|
"ISC", # flake8-implicit-str-concat
|
||||||
|
"PGH", # pygrep-hooks
|
||||||
|
"RUF100", # unused noqa (yesqa)
|
||||||
|
"UP", # pyupgrade
|
||||||
|
"W", # pycodestyle warnings
|
||||||
|
"YTT", # flake8-2020
|
||||||
|
# "LOG", # TODO: enable flake8-logging when it's not in preview anymore
|
||||||
|
]
|
||||||
|
extend-ignore = [
|
||||||
|
"E203", # Whitespace before ':'
|
||||||
|
"E221", # Multiple spaces before operator
|
||||||
|
"E226", # Missing whitespace around arithmetic operator
|
||||||
|
"E241", # Multiple spaces after ','
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.per-file-ignores]
|
||||||
|
"Tests/*.py" = ["I001"]
|
||||||
|
|
||||||
|
[tool.ruff.isort]
|
||||||
|
known-first-party = ["PIL"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = "-ra --color=yes"
|
addopts = "-ra --color=yes"
|
||||||
|
|
|
@ -122,7 +122,7 @@ def Ghostscript(tile, size, fp, scale=1, transparency=False):
|
||||||
gs_binary,
|
gs_binary,
|
||||||
"-q", # quiet mode
|
"-q", # quiet mode
|
||||||
"-g%dx%d" % size, # set output geometry (pixels)
|
"-g%dx%d" % size, # set output geometry (pixels)
|
||||||
"-r%fx%f" % res, # set input DPI (dots per inch)
|
"-r%fx%f" % res, # set input DPI (dots per inch) # noqa: UP031
|
||||||
"-dBATCH", # exit after processing
|
"-dBATCH", # exit after processing
|
||||||
"-dNOPAUSE", # don't pause between pages
|
"-dNOPAUSE", # don't pause between pages
|
||||||
"-dSAFER", # safe mode
|
"-dSAFER", # safe mode
|
||||||
|
|
|
@ -392,7 +392,7 @@ if __name__ == "__main__":
|
||||||
imf = IcnsImageFile(fp)
|
imf = IcnsImageFile(fp)
|
||||||
for size in imf.info["sizes"]:
|
for size in imf.info["sizes"]:
|
||||||
imf.size = size
|
imf.size = size
|
||||||
imf.save("out-%s-%s-%s.png" % size)
|
imf.save("out-%s-%s-%s.png" % size) # noqa: UP031
|
||||||
with Image.open(sys.argv[1]) as im:
|
with Image.open(sys.argv[1]) as im:
|
||||||
im.save("out.png")
|
im.save("out.png")
|
||||||
if sys.platform == "windows":
|
if sys.platform == "windows":
|
||||||
|
|
|
@ -3100,7 +3100,7 @@ def fromarray(obj, mode=None):
|
||||||
try:
|
try:
|
||||||
mode, rawmode = _fromarray_typemap[typekey]
|
mode, rawmode = _fromarray_typemap[typekey]
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
msg = "Cannot handle this data type: %s, %s" % typekey
|
msg = "Cannot handle this data type: %s, %s" % typekey # noqa: UP031
|
||||||
raise TypeError(msg) from e
|
raise TypeError(msg) from e
|
||||||
else:
|
else:
|
||||||
rawmode = mode
|
rawmode = mode
|
||||||
|
|
|
@ -222,7 +222,7 @@ class UnsharpMask(MultibandFilter):
|
||||||
|
|
||||||
.. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking
|
.. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking
|
||||||
|
|
||||||
""" # noqa: E501
|
"""
|
||||||
|
|
||||||
name = "UnsharpMask"
|
name = "UnsharpMask"
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,9 @@ import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from . import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
from ._binary import i8
|
from ._binary import i8, o8
|
||||||
from ._binary import i16be as i16
|
from ._binary import i16be as i16
|
||||||
from ._binary import i32be as i32
|
from ._binary import i32be as i32
|
||||||
from ._binary import o8
|
|
||||||
|
|
||||||
COMPRESSION = {1: "raw", 5: "jpeg"}
|
COMPRESSION = {1: "raw", 5: "jpeg"}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ class IndirectReference(
|
||||||
collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])
|
collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])
|
||||||
):
|
):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s %s R" % self
|
return "%s %s R" % self # noqa: UP031
|
||||||
|
|
||||||
def __bytes__(self):
|
def __bytes__(self):
|
||||||
return self.__str__().encode("us-ascii")
|
return self.__str__().encode("us-ascii")
|
||||||
|
@ -103,7 +103,7 @@ class IndirectReference(
|
||||||
|
|
||||||
class IndirectObjectDef(IndirectReference):
|
class IndirectObjectDef(IndirectReference):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s %s obj" % self
|
return "%s %s obj" % self # noqa: UP031
|
||||||
|
|
||||||
|
|
||||||
class XrefTable:
|
class XrefTable:
|
||||||
|
|
|
@ -1156,6 +1156,9 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
|
||||||
encoderinfo["duration"] = duration
|
encoderinfo["duration"] = duration
|
||||||
im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
|
im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
|
||||||
|
|
||||||
|
if len(im_frames) == 1 and not default_image:
|
||||||
|
return im_frames[0]["im"]
|
||||||
|
|
||||||
# animation control
|
# animation control
|
||||||
chunk(
|
chunk(
|
||||||
fp,
|
fp,
|
||||||
|
@ -1391,8 +1394,10 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
|
||||||
chunk(fp, b"eXIf", exif)
|
chunk(fp, b"eXIf", exif)
|
||||||
|
|
||||||
if save_all:
|
if save_all:
|
||||||
_write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
|
im = _write_multiple_frames(
|
||||||
else:
|
im, fp, chunk, rawmode, default_image, append_images
|
||||||
|
)
|
||||||
|
if im:
|
||||||
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
|
ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
|
||||||
|
|
||||||
if info:
|
if info:
|
||||||
|
|
|
@ -244,7 +244,7 @@ class _PyAccessI16_L(PyAccess):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
color = min(color[0], 65535)
|
color = min(color[0], 65535)
|
||||||
|
|
||||||
pixel.l = color & 0xFF # noqa: E741
|
pixel.l = color & 0xFF
|
||||||
pixel.r = color >> 8
|
pixel.r = color >> 8
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ class _PyAccessI16_B(PyAccess):
|
||||||
except Exception:
|
except Exception:
|
||||||
color = min(color[0], 65535)
|
color = min(color[0], 65535)
|
||||||
|
|
||||||
pixel.l = color >> 8 # noqa: E741
|
pixel.l = color >> 8
|
||||||
pixel.r = color & 0xFF
|
pixel.r = color & 0xFF
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -239,7 +239,7 @@ DEPS = {
|
||||||
"libs": ["*.lib"],
|
"libs": ["*.lib"],
|
||||||
},
|
},
|
||||||
"freetype": {
|
"freetype": {
|
||||||
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.13.2.tar.gz", # noqa: E501
|
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.13.2.tar.gz",
|
||||||
"filename": "freetype-2.13.2.tar.gz",
|
"filename": "freetype-2.13.2.tar.gz",
|
||||||
"dir": "freetype-2.13.2",
|
"dir": "freetype-2.13.2",
|
||||||
"license": ["LICENSE.TXT", r"docs\FTL.TXT", r"docs\GPLv2.TXT"],
|
"license": ["LICENSE.TXT", r"docs\FTL.TXT", r"docs\GPLv2.TXT"],
|
||||||
|
@ -321,7 +321,7 @@ DEPS = {
|
||||||
},
|
},
|
||||||
"libimagequant": {
|
"libimagequant": {
|
||||||
# commit: Merge branch 'master' into msvc (matches 2.17.0 tag)
|
# commit: Merge branch 'master' into msvc (matches 2.17.0 tag)
|
||||||
"url": "https://github.com/ImageOptim/libimagequant/archive/e4c1334be0eff290af5e2b4155057c2953a313ab.zip", # noqa: E501
|
"url": "https://github.com/ImageOptim/libimagequant/archive/e4c1334be0eff290af5e2b4155057c2953a313ab.zip",
|
||||||
"filename": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab.zip",
|
"filename": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab.zip",
|
||||||
"dir": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab",
|
"dir": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab",
|
||||||
"license": "COPYRIGHT",
|
"license": "COPYRIGHT",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user