mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 09:26:16 +03:00
Merge pull request #8242 from radarhere/imagemath_options
This commit is contained in:
commit
97e5381dbb
|
@ -1,5 +1,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageMath
|
||||
|
||||
|
||||
|
@ -19,7 +23,7 @@ I = Image.new("I", (1, 1), 4) # noqa: E741
|
|||
A2 = A.resize((2, 2))
|
||||
B2 = B.resize((2, 2))
|
||||
|
||||
images = {"A": A, "B": B, "F": F, "I": I}
|
||||
images: dict[str, Any] = {"A": A, "B": B, "F": F, "I": I}
|
||||
|
||||
|
||||
def test_sanity() -> None:
|
||||
|
@ -30,13 +34,13 @@ def test_sanity() -> None:
|
|||
== "I 3"
|
||||
)
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] + args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] + args["B"], **images))
|
||||
== "I 3"
|
||||
)
|
||||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["float"](args["A"]) + args["B"], images
|
||||
lambda args: args["float"](args["A"]) + args["B"], **images
|
||||
)
|
||||
)
|
||||
== "F 3.0"
|
||||
|
@ -44,42 +48,47 @@ def test_sanity() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["int"](args["float"](args["A"]) + args["B"]), images
|
||||
lambda args: args["int"](args["float"](args["A"]) + args["B"]), **images
|
||||
)
|
||||
)
|
||||
== "I 3"
|
||||
)
|
||||
|
||||
|
||||
def test_options_deprecated() -> None:
|
||||
with pytest.warns(DeprecationWarning):
|
||||
assert ImageMath.lambda_eval(lambda args: 1, images) == 1
|
||||
|
||||
|
||||
def test_ops() -> None:
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] * -1, images)) == "I -1"
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] * -1, **images)) == "I -1"
|
||||
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] + args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] + args["B"], **images))
|
||||
== "I 3"
|
||||
)
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] - args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] - args["B"], **images))
|
||||
== "I -1"
|
||||
)
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] * args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] * args["B"], **images))
|
||||
== "I 2"
|
||||
)
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] / args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] / args["B"], **images))
|
||||
== "I 0"
|
||||
)
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["B"] ** 2, images)) == "I 4"
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["B"] ** 2, **images)) == "I 4"
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["B"] ** 33, images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["B"] ** 33, **images))
|
||||
== "I 2147483647"
|
||||
)
|
||||
|
||||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["float"](args["A"]) + args["B"], images
|
||||
lambda args: args["float"](args["A"]) + args["B"], **images
|
||||
)
|
||||
)
|
||||
== "F 3.0"
|
||||
|
@ -87,7 +96,7 @@ def test_ops() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["float"](args["A"]) - args["B"], images
|
||||
lambda args: args["float"](args["A"]) - args["B"], **images
|
||||
)
|
||||
)
|
||||
== "F -1.0"
|
||||
|
@ -95,7 +104,7 @@ def test_ops() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["float"](args["A"]) * args["B"], images
|
||||
lambda args: args["float"](args["A"]) * args["B"], **images
|
||||
)
|
||||
)
|
||||
== "F 2.0"
|
||||
|
@ -103,31 +112,33 @@ def test_ops() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["float"](args["A"]) / args["B"], images
|
||||
lambda args: args["float"](args["A"]) / args["B"], **images
|
||||
)
|
||||
)
|
||||
== "F 0.5"
|
||||
)
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["float"](args["B"]) ** 2, images))
|
||||
pixel(
|
||||
ImageMath.lambda_eval(lambda args: args["float"](args["B"]) ** 2, **images)
|
||||
)
|
||||
== "F 4.0"
|
||||
)
|
||||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(lambda args: args["float"](args["B"]) ** 33, images)
|
||||
ImageMath.lambda_eval(lambda args: args["float"](args["B"]) ** 33, **images)
|
||||
)
|
||||
== "F 8589934592.0"
|
||||
)
|
||||
|
||||
|
||||
def test_logical() -> None:
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: not args["A"], images)) == 0
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: not args["A"], **images)) == 0
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] and args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] and args["B"], **images))
|
||||
== "L 2"
|
||||
)
|
||||
assert (
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] or args["B"], images))
|
||||
pixel(ImageMath.lambda_eval(lambda args: args["A"] or args["B"], **images))
|
||||
== "L 1"
|
||||
)
|
||||
|
||||
|
@ -136,7 +147,7 @@ def test_convert() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["convert"](args["A"] + args["B"], "L"), images
|
||||
lambda args: args["convert"](args["A"] + args["B"], "L"), **images
|
||||
)
|
||||
)
|
||||
== "L 3"
|
||||
|
@ -144,7 +155,7 @@ def test_convert() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["convert"](args["A"] + args["B"], "1"), images
|
||||
lambda args: args["convert"](args["A"] + args["B"], "1"), **images
|
||||
)
|
||||
)
|
||||
== "1 0"
|
||||
|
@ -152,7 +163,7 @@ def test_convert() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["convert"](args["A"] + args["B"], "RGB"), images
|
||||
lambda args: args["convert"](args["A"] + args["B"], "RGB"), **images
|
||||
)
|
||||
)
|
||||
== "RGB (3, 3, 3)"
|
||||
|
@ -163,7 +174,7 @@ def test_compare() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["min"](args["A"], args["B"]), images
|
||||
lambda args: args["min"](args["A"], args["B"]), **images
|
||||
)
|
||||
)
|
||||
== "I 1"
|
||||
|
@ -171,13 +182,13 @@ def test_compare() -> None:
|
|||
assert (
|
||||
pixel(
|
||||
ImageMath.lambda_eval(
|
||||
lambda args: args["max"](args["A"], args["B"]), images
|
||||
lambda args: args["max"](args["A"], args["B"]), **images
|
||||
)
|
||||
)
|
||||
== "I 2"
|
||||
)
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] == 1, images)) == "I 1"
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] == 2, images)) == "I 0"
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] == 1, **images)) == "I 1"
|
||||
assert pixel(ImageMath.lambda_eval(lambda args: args["A"] == 2, **images)) == "I 0"
|
||||
|
||||
|
||||
def test_one_image_larger() -> None:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from PIL import Image, ImageMath
|
||||
|
@ -21,16 +23,16 @@ I = Image.new("I", (1, 1), 4) # noqa: E741
|
|||
A2 = A.resize((2, 2))
|
||||
B2 = B.resize((2, 2))
|
||||
|
||||
images = {"A": A, "B": B, "F": F, "I": I}
|
||||
images: dict[str, Any] = {"A": A, "B": B, "F": F, "I": I}
|
||||
|
||||
|
||||
def test_sanity() -> None:
|
||||
assert ImageMath.unsafe_eval("1") == 1
|
||||
assert ImageMath.unsafe_eval("1+A", A=2) == 3
|
||||
assert pixel(ImageMath.unsafe_eval("A+B", A=A, B=B)) == "I 3"
|
||||
assert pixel(ImageMath.unsafe_eval("A+B", images)) == "I 3"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)+B", images)) == "F 3.0"
|
||||
assert pixel(ImageMath.unsafe_eval("int(float(A)+B)", images)) == "I 3"
|
||||
assert pixel(ImageMath.unsafe_eval("A+B", **images)) == "I 3"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)+B", **images)) == "F 3.0"
|
||||
assert pixel(ImageMath.unsafe_eval("int(float(A)+B)", **images)) == "I 3"
|
||||
|
||||
|
||||
def test_eval_deprecated() -> None:
|
||||
|
@ -38,23 +40,28 @@ def test_eval_deprecated() -> None:
|
|||
assert ImageMath.eval("1") == 1
|
||||
|
||||
|
||||
def test_options_deprecated() -> None:
|
||||
with pytest.warns(DeprecationWarning):
|
||||
assert ImageMath.unsafe_eval("1", images) == 1
|
||||
|
||||
|
||||
def test_ops() -> None:
|
||||
assert pixel(ImageMath.unsafe_eval("-A", images)) == "I -1"
|
||||
assert pixel(ImageMath.unsafe_eval("+B", images)) == "L 2"
|
||||
assert pixel(ImageMath.unsafe_eval("-A", **images)) == "I -1"
|
||||
assert pixel(ImageMath.unsafe_eval("+B", **images)) == "L 2"
|
||||
|
||||
assert pixel(ImageMath.unsafe_eval("A+B", images)) == "I 3"
|
||||
assert pixel(ImageMath.unsafe_eval("A-B", images)) == "I -1"
|
||||
assert pixel(ImageMath.unsafe_eval("A*B", images)) == "I 2"
|
||||
assert pixel(ImageMath.unsafe_eval("A/B", images)) == "I 0"
|
||||
assert pixel(ImageMath.unsafe_eval("B**2", images)) == "I 4"
|
||||
assert pixel(ImageMath.unsafe_eval("B**33", images)) == "I 2147483647"
|
||||
assert pixel(ImageMath.unsafe_eval("A+B", **images)) == "I 3"
|
||||
assert pixel(ImageMath.unsafe_eval("A-B", **images)) == "I -1"
|
||||
assert pixel(ImageMath.unsafe_eval("A*B", **images)) == "I 2"
|
||||
assert pixel(ImageMath.unsafe_eval("A/B", **images)) == "I 0"
|
||||
assert pixel(ImageMath.unsafe_eval("B**2", **images)) == "I 4"
|
||||
assert pixel(ImageMath.unsafe_eval("B**33", **images)) == "I 2147483647"
|
||||
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)+B", images)) == "F 3.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)-B", images)) == "F -1.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)*B", images)) == "F 2.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)/B", images)) == "F 0.5"
|
||||
assert pixel(ImageMath.unsafe_eval("float(B)**2", images)) == "F 4.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(B)**33", images)) == "F 8589934592.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)+B", **images)) == "F 3.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)-B", **images)) == "F -1.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)*B", **images)) == "F 2.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(A)/B", **images)) == "F 0.5"
|
||||
assert pixel(ImageMath.unsafe_eval("float(B)**2", **images)) == "F 4.0"
|
||||
assert pixel(ImageMath.unsafe_eval("float(B)**33", **images)) == "F 8589934592.0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -72,33 +79,33 @@ def test_prevent_exec(expression: str) -> None:
|
|||
|
||||
def test_prevent_double_underscores() -> None:
|
||||
with pytest.raises(ValueError):
|
||||
ImageMath.unsafe_eval("1", {"__": None})
|
||||
ImageMath.unsafe_eval("1", __=None)
|
||||
|
||||
|
||||
def test_prevent_builtins() -> None:
|
||||
with pytest.raises(ValueError):
|
||||
ImageMath.unsafe_eval("(lambda: exec('exit()'))()", {"exec": None})
|
||||
ImageMath.unsafe_eval("(lambda: exec('exit()'))()", exec=None)
|
||||
|
||||
|
||||
def test_logical() -> None:
|
||||
assert pixel(ImageMath.unsafe_eval("not A", images)) == 0
|
||||
assert pixel(ImageMath.unsafe_eval("A and B", images)) == "L 2"
|
||||
assert pixel(ImageMath.unsafe_eval("A or B", images)) == "L 1"
|
||||
assert pixel(ImageMath.unsafe_eval("not A", **images)) == 0
|
||||
assert pixel(ImageMath.unsafe_eval("A and B", **images)) == "L 2"
|
||||
assert pixel(ImageMath.unsafe_eval("A or B", **images)) == "L 1"
|
||||
|
||||
|
||||
def test_convert() -> None:
|
||||
assert pixel(ImageMath.unsafe_eval("convert(A+B, 'L')", images)) == "L 3"
|
||||
assert pixel(ImageMath.unsafe_eval("convert(A+B, '1')", images)) == "1 0"
|
||||
assert pixel(ImageMath.unsafe_eval("convert(A+B, 'L')", **images)) == "L 3"
|
||||
assert pixel(ImageMath.unsafe_eval("convert(A+B, '1')", **images)) == "1 0"
|
||||
assert (
|
||||
pixel(ImageMath.unsafe_eval("convert(A+B, 'RGB')", images)) == "RGB (3, 3, 3)"
|
||||
pixel(ImageMath.unsafe_eval("convert(A+B, 'RGB')", **images)) == "RGB (3, 3, 3)"
|
||||
)
|
||||
|
||||
|
||||
def test_compare() -> None:
|
||||
assert pixel(ImageMath.unsafe_eval("min(A, B)", images)) == "I 1"
|
||||
assert pixel(ImageMath.unsafe_eval("max(A, B)", images)) == "I 2"
|
||||
assert pixel(ImageMath.unsafe_eval("A == 1", images)) == "I 1"
|
||||
assert pixel(ImageMath.unsafe_eval("A == 2", images)) == "I 0"
|
||||
assert pixel(ImageMath.unsafe_eval("min(A, B)", **images)) == "I 1"
|
||||
assert pixel(ImageMath.unsafe_eval("max(A, B)", **images)) == "I 2"
|
||||
assert pixel(ImageMath.unsafe_eval("A == 1", **images)) == "I 1"
|
||||
assert pixel(ImageMath.unsafe_eval("A == 2", **images)) == "I 0"
|
||||
|
||||
|
||||
def test_one_image_larger() -> None:
|
||||
|
|
|
@ -109,6 +109,15 @@ ImageDraw.getdraw hints parameter
|
|||
|
||||
The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated.
|
||||
|
||||
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. deprecated:: 11.0.0
|
||||
|
||||
The ``options`` parameter in :py:meth:`~PIL.ImageMath.lambda_eval()` and
|
||||
:py:meth:`~PIL.ImageMath.unsafe_eval()` has been deprecated. One or more keyword
|
||||
arguments can be used instead.
|
||||
|
||||
Removed features
|
||||
----------------
|
||||
|
||||
|
|
|
@ -31,20 +31,21 @@ Example: Using the :py:mod:`~PIL.ImageMath` module
|
|||
b=im2
|
||||
)
|
||||
|
||||
.. py:function:: lambda_eval(expression, options)
|
||||
.. py:function:: lambda_eval(expression, options, **kw)
|
||||
|
||||
Returns the result of an image function.
|
||||
|
||||
:param expression: A function that receives a dictionary.
|
||||
:param options: Values to add to the function's dictionary, mapping image
|
||||
names to Image instances. You can use one or more keyword
|
||||
arguments instead of a dictionary, as shown in the above
|
||||
example. Note that the names must be valid Python
|
||||
identifiers.
|
||||
:param options: Values to add to the function's dictionary. Note that the names
|
||||
must be valid Python identifiers. Deprecated.
|
||||
You can instead use one or more keyword arguments, as
|
||||
shown in the above example.
|
||||
:param \**kw: Values to add to the function's dictionary, mapping image names to
|
||||
Image instances.
|
||||
:return: An image, an integer value, a floating point value,
|
||||
or a pixel tuple, depending on the expression.
|
||||
|
||||
.. py:function:: unsafe_eval(expression, options)
|
||||
.. py:function:: unsafe_eval(expression, options, **kw)
|
||||
|
||||
Evaluates an image expression.
|
||||
|
||||
|
@ -61,11 +62,12 @@ Example: Using the :py:mod:`~PIL.ImageMath` module
|
|||
:param expression: A string which uses the standard Python expression
|
||||
syntax. In addition to the standard operators, you can
|
||||
also use the functions described below.
|
||||
:param options: Values to add to the function's dictionary, mapping image
|
||||
names to Image instances. You can use one or more keyword
|
||||
arguments instead of a dictionary, as shown in the above
|
||||
example. Note that the names must be valid Python
|
||||
identifiers.
|
||||
:param options: Values to add to the evaluation context. Note that the names must
|
||||
be valid Python identifiers. Deprecated.
|
||||
You can instead use one or more keyword arguments, as
|
||||
shown in the above example.
|
||||
:param \**kw: Values to add to the evaluation context, mapping image names to Image
|
||||
instances.
|
||||
:return: An image, an integer value, a floating point value,
|
||||
or a pixel tuple, depending on the expression.
|
||||
|
||||
|
|
|
@ -43,10 +43,12 @@ similarly removed.
|
|||
Deprecations
|
||||
============
|
||||
|
||||
TODO
|
||||
^^^^
|
||||
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TODO
|
||||
The ``options`` parameter in :py:meth:`~PIL.ImageMath.lambda_eval()` and
|
||||
:py:meth:`~PIL.ImageMath.unsafe_eval()` has been deprecated. One or more
|
||||
keyword arguments can be used instead.
|
||||
|
||||
API Changes
|
||||
===========
|
||||
|
|
|
@ -249,14 +249,21 @@ def lambda_eval(
|
|||
:py:func:`~PIL.Image.merge` function.
|
||||
|
||||
:param expression: A function that receives a dictionary.
|
||||
:param options: Values to add to the function's dictionary. You
|
||||
can either use a dictionary, or one or more keyword
|
||||
arguments.
|
||||
:param options: Values to add to the function's dictionary. Deprecated.
|
||||
You can instead use one or more keyword arguments.
|
||||
:param **kw: Values to add to the function's dictionary.
|
||||
:return: The expression result. This is usually an image object, but can
|
||||
also be an integer, a floating point value, or a pixel tuple,
|
||||
depending on the expression.
|
||||
"""
|
||||
|
||||
if options:
|
||||
deprecate(
|
||||
"ImageMath.lambda_eval options",
|
||||
12,
|
||||
"ImageMath.lambda_eval keyword arguments",
|
||||
)
|
||||
|
||||
args: dict[str, Any] = ops.copy()
|
||||
args.update(options)
|
||||
args.update(kw)
|
||||
|
@ -287,14 +294,21 @@ def unsafe_eval(
|
|||
:py:func:`~PIL.Image.merge` function.
|
||||
|
||||
:param expression: A string containing a Python-style expression.
|
||||
:param options: Values to add to the evaluation context. You
|
||||
can either use a dictionary, or one or more keyword
|
||||
arguments.
|
||||
:param options: Values to add to the evaluation context. Deprecated.
|
||||
You can instead use one or more keyword arguments.
|
||||
:param **kw: Values to add to the evaluation context.
|
||||
:return: The evaluated expression. This is usually an image object, but can
|
||||
also be an integer, a floating point value, or a pixel tuple,
|
||||
depending on the expression.
|
||||
"""
|
||||
|
||||
if options:
|
||||
deprecate(
|
||||
"ImageMath.unsafe_eval options",
|
||||
12,
|
||||
"ImageMath.unsafe_eval keyword arguments",
|
||||
)
|
||||
|
||||
# build execution namespace
|
||||
args: dict[str, Any] = ops.copy()
|
||||
for k in list(options.keys()) + list(kw.keys()):
|
||||
|
|
Loading…
Reference in New Issue
Block a user