{
    "file_diffs": [
        {
            "old_file_content": "from PIL import Image, ImageMath\n\n\ndef pixel(im):\n    if hasattr(im, \"im\"):\n        return f\"{im.mode} {repr(im.getpixel((0, 0)))}\"\n    else:\n        if isinstance(im, int):\n            return int(im)  # hack to deal with booleans\n        print(im)\n\n\nA = Image.new(\"L\", (1, 1), 1)\nB = Image.new(\"L\", (1, 1), 2)\nZ = Image.new(\"L\", (1, 1), 0)  # Z for zero\nF = Image.new(\"F\", (1, 1), 3)\nI = Image.new(\"I\", (1, 1), 4)  # noqa: E741\n\nA2 = A.resize((2, 2))\nB2 = B.resize((2, 2))\n\nimages = {\"A\": A, \"B\": B, \"F\": F, \"I\": I}\n\n\ndef test_sanity():\n    assert ImageMath.eval(\"1\") == 1\n    assert ImageMath.eval(\"1+A\", A=2) == 3\n    assert pixel(ImageMath.eval(\"A+B\", A=A, B=B)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A+B\", images)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"float(A)+B\", images)) == \"F 3.0\"\n    assert pixel(ImageMath.eval(\"int(float(A)+B)\", images)) == \"I 3\"\n\n\ndef test_ops():\n    assert pixel(ImageMath.eval(\"-A\", images)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"+B\", images)) == \"L 2\"\n\n    assert pixel(ImageMath.eval(\"A+B\", images)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A-B\", images)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"A*B\", images)) == \"I 2\"\n    assert pixel(ImageMath.eval(\"A/B\", images)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B**2\", images)) == \"I 4\"\n    assert pixel(ImageMath.eval(\"B**33\", images)) == \"I 2147483647\"\n\n    assert pixel(ImageMath.eval(\"float(A)+B\", images)) == \"F 3.0\"\n    assert pixel(ImageMath.eval(\"float(A)-B\", images)) == \"F -1.0\"\n    assert pixel(ImageMath.eval(\"float(A)*B\", images)) == \"F 2.0\"\n    assert pixel(ImageMath.eval(\"float(A)/B\", images)) == \"F 0.5\"\n    assert pixel(ImageMath.eval(\"float(B)**2\", images)) == \"F 4.0\"\n    assert pixel(ImageMath.eval(\"float(B)**33\", images)) == \"F 8589934592.0\"\n\n\ndef test_logical():\n    assert pixel(ImageMath.eval(\"not A\", images)) == 0\n    assert pixel(ImageMath.eval(\"A and B\", images)) == \"L 2\"\n    assert pixel(ImageMath.eval(\"A or B\", images)) == \"L 1\"\n\n\ndef test_convert():\n    assert pixel(ImageMath.eval(\"convert(A+B, 'L')\", images)) == \"L 3\"\n    assert pixel(ImageMath.eval(\"convert(A+B, '1')\", images)) == \"1 0\"\n    assert pixel(ImageMath.eval(\"convert(A+B, 'RGB')\", images)) == \"RGB (3, 3, 3)\"\n\n\ndef test_compare():\n    assert pixel(ImageMath.eval(\"min(A, B)\", images)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"max(A, B)\", images)) == \"I 2\"\n    assert pixel(ImageMath.eval(\"A == 1\", images)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A == 2\", images)) == \"I 0\"\n\n\ndef test_one_image_larger():\n    assert pixel(ImageMath.eval(\"A+B\", A=A2, B=B)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A+B\", A=A, B=B2)) == \"I 3\"\n\n\ndef test_abs():\n    assert pixel(ImageMath.eval(\"abs(A)\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"abs(B)\", B=B)) == \"I 2\"\n\n\ndef test_binary_mod():\n    assert pixel(ImageMath.eval(\"A%A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B%B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A%B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B%A\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z%A\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z%B\", B=B, Z=Z)) == \"I 0\"\n\n\ndef test_bitwise_invert():\n    assert pixel(ImageMath.eval(\"~Z\", Z=Z)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"~A\", A=A)) == \"I -2\"\n    assert pixel(ImageMath.eval(\"~B\", B=B)) == \"I -3\"\n\n\ndef test_bitwise_and():\n    assert pixel(ImageMath.eval(\"Z&Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z&A\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A&Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A&A\", A=A, Z=Z)) == \"I 1\"\n\n\ndef test_bitwise_or():\n    assert pixel(ImageMath.eval(\"Z|Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z|A\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A|Z\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A|A\", A=A, Z=Z)) == \"I 1\"\n\n\ndef test_bitwise_xor():\n    assert pixel(ImageMath.eval(\"Z^Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z^A\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A^Z\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A^A\", A=A, Z=Z)) == \"I 0\"\n\n\ndef test_bitwise_leftshift():\n    assert pixel(ImageMath.eval(\"Z<<0\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z<<1\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A<<0\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A<<1\", A=A)) == \"I 2\"\n\n\ndef test_bitwise_rightshift():\n    assert pixel(ImageMath.eval(\"Z>>0\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z>>1\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A>>0\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A>>1\", A=A)) == \"I 0\"\n\n\ndef test_logical_eq():\n    assert pixel(ImageMath.eval(\"A==A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B==B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A==B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B==A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_ne():\n    assert pixel(ImageMath.eval(\"A!=A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B!=B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A!=B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B!=A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_lt():\n    assert pixel(ImageMath.eval(\"A<A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B<B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A<B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_le():\n    assert pixel(ImageMath.eval(\"A<=A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<=B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A<=B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<=A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_gt():\n    assert pixel(ImageMath.eval(\"A>A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A>B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_ge():\n    assert pixel(ImageMath.eval(\"A>=A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B>=B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A>=B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>=A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_equal():\n    assert pixel(ImageMath.eval(\"equal(A, A)\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(B, B)\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(Z, Z)\", Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(A, B)\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"equal(B, A)\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"equal(A, Z)\", A=A, Z=Z)) == \"I 0\"\n\n\ndef test_logical_not_equal():\n    assert pixel(ImageMath.eval(\"notequal(A, A)\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(B, B)\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(Z, Z)\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(A, B)\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"notequal(B, A)\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"notequal(A, Z)\", A=A, Z=Z)) == \"I 1\"\n",
            "new_file_content": "import pytest\n\nfrom PIL import Image, ImageMath\n\n\ndef pixel(im):\n    if hasattr(im, \"im\"):\n        return f\"{im.mode} {repr(im.getpixel((0, 0)))}\"\n    else:\n        if isinstance(im, int):\n            return int(im)  # hack to deal with booleans\n        print(im)\n\n\nA = Image.new(\"L\", (1, 1), 1)\nB = Image.new(\"L\", (1, 1), 2)\nZ = Image.new(\"L\", (1, 1), 0)  # Z for zero\nF = Image.new(\"F\", (1, 1), 3)\nI = Image.new(\"I\", (1, 1), 4)  # noqa: E741\n\nA2 = A.resize((2, 2))\nB2 = B.resize((2, 2))\n\nimages = {\"A\": A, \"B\": B, \"F\": F, \"I\": I}\n\n\ndef test_sanity():\n    assert ImageMath.eval(\"1\") == 1\n    assert ImageMath.eval(\"1+A\", A=2) == 3\n    assert pixel(ImageMath.eval(\"A+B\", A=A, B=B)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A+B\", images)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"float(A)+B\", images)) == \"F 3.0\"\n    assert pixel(ImageMath.eval(\"int(float(A)+B)\", images)) == \"I 3\"\n\n\ndef test_ops():\n    assert pixel(ImageMath.eval(\"-A\", images)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"+B\", images)) == \"L 2\"\n\n    assert pixel(ImageMath.eval(\"A+B\", images)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A-B\", images)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"A*B\", images)) == \"I 2\"\n    assert pixel(ImageMath.eval(\"A/B\", images)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B**2\", images)) == \"I 4\"\n    assert pixel(ImageMath.eval(\"B**33\", images)) == \"I 2147483647\"\n\n    assert pixel(ImageMath.eval(\"float(A)+B\", images)) == \"F 3.0\"\n    assert pixel(ImageMath.eval(\"float(A)-B\", images)) == \"F -1.0\"\n    assert pixel(ImageMath.eval(\"float(A)*B\", images)) == \"F 2.0\"\n    assert pixel(ImageMath.eval(\"float(A)/B\", images)) == \"F 0.5\"\n    assert pixel(ImageMath.eval(\"float(B)**2\", images)) == \"F 4.0\"\n    assert pixel(ImageMath.eval(\"float(B)**33\", images)) == \"F 8589934592.0\"\n\n\ndef test_prevent_exec():\n    with pytest.raises(ValueError):\n        ImageMath.eval(\"exec('pass')\")\n\n\ndef test_logical():\n    assert pixel(ImageMath.eval(\"not A\", images)) == 0\n    assert pixel(ImageMath.eval(\"A and B\", images)) == \"L 2\"\n    assert pixel(ImageMath.eval(\"A or B\", images)) == \"L 1\"\n\n\ndef test_convert():\n    assert pixel(ImageMath.eval(\"convert(A+B, 'L')\", images)) == \"L 3\"\n    assert pixel(ImageMath.eval(\"convert(A+B, '1')\", images)) == \"1 0\"\n    assert pixel(ImageMath.eval(\"convert(A+B, 'RGB')\", images)) == \"RGB (3, 3, 3)\"\n\n\ndef test_compare():\n    assert pixel(ImageMath.eval(\"min(A, B)\", images)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"max(A, B)\", images)) == \"I 2\"\n    assert pixel(ImageMath.eval(\"A == 1\", images)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A == 2\", images)) == \"I 0\"\n\n\ndef test_one_image_larger():\n    assert pixel(ImageMath.eval(\"A+B\", A=A2, B=B)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A+B\", A=A, B=B2)) == \"I 3\"\n\n\ndef test_abs():\n    assert pixel(ImageMath.eval(\"abs(A)\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"abs(B)\", B=B)) == \"I 2\"\n\n\ndef test_binary_mod():\n    assert pixel(ImageMath.eval(\"A%A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B%B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A%B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B%A\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z%A\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z%B\", B=B, Z=Z)) == \"I 0\"\n\n\ndef test_bitwise_invert():\n    assert pixel(ImageMath.eval(\"~Z\", Z=Z)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"~A\", A=A)) == \"I -2\"\n    assert pixel(ImageMath.eval(\"~B\", B=B)) == \"I -3\"\n\n\ndef test_bitwise_and():\n    assert pixel(ImageMath.eval(\"Z&Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z&A\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A&Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A&A\", A=A, Z=Z)) == \"I 1\"\n\n\ndef test_bitwise_or():\n    assert pixel(ImageMath.eval(\"Z|Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z|A\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A|Z\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A|A\", A=A, Z=Z)) == \"I 1\"\n\n\ndef test_bitwise_xor():\n    assert pixel(ImageMath.eval(\"Z^Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z^A\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A^Z\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A^A\", A=A, Z=Z)) == \"I 0\"\n\n\ndef test_bitwise_leftshift():\n    assert pixel(ImageMath.eval(\"Z<<0\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z<<1\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A<<0\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A<<1\", A=A)) == \"I 2\"\n\n\ndef test_bitwise_rightshift():\n    assert pixel(ImageMath.eval(\"Z>>0\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z>>1\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A>>0\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A>>1\", A=A)) == \"I 0\"\n\n\ndef test_logical_eq():\n    assert pixel(ImageMath.eval(\"A==A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B==B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A==B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B==A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_ne():\n    assert pixel(ImageMath.eval(\"A!=A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B!=B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A!=B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B!=A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_lt():\n    assert pixel(ImageMath.eval(\"A<A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B<B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A<B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_le():\n    assert pixel(ImageMath.eval(\"A<=A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<=B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A<=B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<=A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_gt():\n    assert pixel(ImageMath.eval(\"A>A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A>B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_ge():\n    assert pixel(ImageMath.eval(\"A>=A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B>=B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A>=B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>=A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_equal():\n    assert pixel(ImageMath.eval(\"equal(A, A)\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(B, B)\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(Z, Z)\", Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(A, B)\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"equal(B, A)\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"equal(A, Z)\", A=A, Z=Z)) == \"I 0\"\n\n\ndef test_logical_not_equal():\n    assert pixel(ImageMath.eval(\"notequal(A, A)\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(B, B)\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(Z, Z)\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(A, B)\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"notequal(B, A)\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"notequal(A, Z)\", A=A, Z=Z)) == \"I 1\"\n",
            "header": {
                "file": {
                    "path": "Tests/test_imagemath.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "e7afd1abf",
                "new_commit_hash": "25811aa89",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Tests/test_imagemath.py"
            },
            "plus_file": {
                "path": "b/Tests/test_imagemath.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1,
                            "length": 3
                        },
                        "new_range": {
                            "start": 1,
                            "length": 5
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "import pytest",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "from PIL import Image, ImageMath",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_imagemath.py",
                            "type": "import",
                            "name": "pytest",
                            "content": "import pytest",
                            "ast_type_str": "Import",
                            "start_lineno": 1,
                            "end_lineno": 1,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 50,
                            "length": 6
                        },
                        "new_range": {
                            "start": 52,
                            "length": 11
                        },
                        "section": "def test_ops():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    assert pixel(ImageMath.eval(\"float(B)**33\", images)) == \"F 8589934592.0\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "def test_prevent_exec():",
                                "type": "added"
                            },
                            {
                                "content": "    with pytest.raises(ValueError):",
                                "type": "added"
                            },
                            {
                                "content": "        ImageMath.eval(\"exec('pass')\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "def test_logical():",
                                "type": "context"
                            },
                            {
                                "content": "    assert pixel(ImageMath.eval(\"not A\", images)) == 0",
                                "type": "context"
                            },
                            {
                                "content": "    assert pixel(ImageMath.eval(\"A and B\", images)) == \"L 2\"",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_imagemath.py",
                            "type": "function",
                            "name": "test_prevent_exec",
                            "content": "def test_prevent_exec():\n    with pytest.raises(ValueError):\n        ImageMath.eval(\"exec('pass')\")",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 55,
                            "end_lineno": 57,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "9.0.0\n-----\n\nFredrik Lundh\n=============\n\nThis release is dedicated to the memory of Fredrik Lundh, aka Effbot, who died in\nNovember 2021. Fredrik created PIL in 1995 and he was instrumental in the early\nsuccess of Python.\n\n`Guido wrote <https://mail.python.org/archives/list/python-dev@python.org/thread/36Q5QBILL3QIFIA3KHNGFBNJQKXKN7SD/>`_:\n\n    Fredrik was an early Python contributor (e.g. Elementtree and the 're'\n    module) and his enthusiasm for the language and community were inspiring\n    for all who encountered him or his work. He spent countless hours on\n    comp.lang.python answering questions from newbies and advanced users alike.\n\n    He also co-founded an early Python startup, Secret Labs AB, which among\n    other software released an IDE named PythonWorks. Fredrik also created the\n    Python Imaging Library (PIL) which is still THE way to interact with images\n    in Python, now most often through its Pillow fork. His effbot.org site was\n    a valuable resource for generations of Python users, especially its Tkinter\n    documentation.\n\nThank you, Fredrik.\n\nBackwards Incompatible Changes\n==============================\n\nPython 3.6\n^^^^^^^^^^\n\nPillow has dropped support for Python 3.6, which reached end-of-life on 2021-12-23.\n\nPILLOW_VERSION constant\n^^^^^^^^^^^^^^^^^^^^^^^\n\n``PILLOW_VERSION`` has been removed. Use ``__version__`` instead.\n\nFreeType 2.7\n^^^^^^^^^^^^\n\nSupport for FreeType 2.7 has been removed; FreeType 2.8 is the minimum supported.\n\nWe recommend upgrading to at least `FreeType`_ 2.10.4, which fixed a severe\nvulnerability introduced in FreeType 2.6 (:cve:`CVE-2020-15999`).\n\n.. _FreeType: https://www.freetype.org\n\nImage.show command parameter\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``command`` parameter has been removed. Use a subclass of\n:py:class:`PIL.ImageShow.Viewer` instead.\n\nImage._showxv\n^^^^^^^^^^^^^\n\n``Image._showxv`` has been removed. Use :py:meth:`~PIL.Image.Image.show`\ninstead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add\na custom :py:class:`~PIL.ImageShow.Viewer` class.\n\nImageFile.raise_ioerror\n^^^^^^^^^^^^^^^^^^^^^^^\n\n``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror``\nhas been removed. Use ``ImageFile.raise_oserror`` instead.\n\n\nAPI Changes\n===========\n\nAdded line width parameter to ImageDraw polygon\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAn optional line ``width`` parameter has been added to ``ImageDraw.Draw.polygon``.\n\n\nAPI Additions\n=============\n\nImageShow.XDGViewer\n^^^^^^^^^^^^^^^^^^^\n\nIf ``xdg-open`` is present on Linux, this new :py:class:`PIL.ImageShow.Viewer` subclass\nwill be registered. It displays images using the application selected by the system.\n\nIt is higher in priority than the other default :py:class:`PIL.ImageShow.Viewer`\ninstances, so it will be preferred by ``im.show()`` or :py:func:`.ImageShow.show()`.\n\nAdded support for \"title\" argument to DisplayViewer\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSupport has been added for the \"title\" argument in\n:py:class:`~PIL.ImageShow.UnixViewer.DisplayViewer`, so that when ``im.show()`` or\n:py:func:`.ImageShow.show()` use the ``display`` command line tool, the \"title\"\nargument will also now be supported, e.g. ``im.show(title=\"My Image\")`` and\n``ImageShow.show(im, title=\"My Image\")``.\n\nSecurity\n========\n\nEnsure JpegImagePlugin stops at the end of a truncated file\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``JpegImagePlugin`` may append an EOF marker to the end of a truncated file, so that\nthe last segment of the data will still be processed by the decoder.\n\nIf the EOF marker is not detected as such however, this could lead to an infinite\nloop where ``JpegImagePlugin`` keeps trying to end the file.\n\nRemove consecutive duplicate tiles that only differ by their offset\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo prevent attempts to slow down loading times for images, if an image has consecutive\nduplicate tiles that only differ by their offset, only load the last tile. Credit to\nGoogle's `OSS-Fuzz`_ project for finding this issue.\n\nFixed ImagePath.Path array handling\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nCWE-126 and CWE-665 were found when initializing ``ImagePath.Path``.\n\n.. _OSS-Fuzz: https://github.com/google/oss-fuzz\n\nOther Changes\n=============\n\nConvert subsequent GIF frames to RGB or RGBA\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSince each frame of a GIF can have up to 256 colors, after the first frame it is\npossible for there to be too many colors to fit in a P mode image. To allow for this,\nseeking to any subsequent GIF frame will now convert the image to RGB or RGBA,\ndepending on whether or not the first frame had transparency.\n\nSwitched to libjpeg-turbo in macOS and Linux wheels\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe Pillow wheels from PyPI for macOS and Linux have switched from libjpeg to\nlibjpeg-turbo. It is a fork of libjpeg, popular for its speed.\n\nAdded support for pickling TrueType fonts\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTrueType fonts may now be pickled and unpickled. For example:\n\n.. code-block:: python\n\n    import pickle\n    from PIL import ImageFont\n\n    font = ImageFont.truetype(\"arial.ttf\", size=30)\n    pickled_font = pickle.dumps(font, protocol=pickle.HIGHEST_PROTOCOL)\n\n    # Later...\n    unpickled_font = pickle.loads(pickled_font)\n\nAdded support for additional TGA orientations\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTGA images with top right or bottom right orientations are now supported.\n",
            "new_file_content": "9.0.0\n-----\n\nFredrik Lundh\n=============\n\nThis release is dedicated to the memory of Fredrik Lundh, aka Effbot, who died in\nNovember 2021. Fredrik created PIL in 1995 and he was instrumental in the early\nsuccess of Python.\n\n`Guido wrote <https://mail.python.org/archives/list/python-dev@python.org/thread/36Q5QBILL3QIFIA3KHNGFBNJQKXKN7SD/>`_:\n\n    Fredrik was an early Python contributor (e.g. Elementtree and the 're'\n    module) and his enthusiasm for the language and community were inspiring\n    for all who encountered him or his work. He spent countless hours on\n    comp.lang.python answering questions from newbies and advanced users alike.\n\n    He also co-founded an early Python startup, Secret Labs AB, which among\n    other software released an IDE named PythonWorks. Fredrik also created the\n    Python Imaging Library (PIL) which is still THE way to interact with images\n    in Python, now most often through its Pillow fork. His effbot.org site was\n    a valuable resource for generations of Python users, especially its Tkinter\n    documentation.\n\nThank you, Fredrik.\n\nBackwards Incompatible Changes\n==============================\n\nPython 3.6\n^^^^^^^^^^\n\nPillow has dropped support for Python 3.6, which reached end-of-life on 2021-12-23.\n\nPILLOW_VERSION constant\n^^^^^^^^^^^^^^^^^^^^^^^\n\n``PILLOW_VERSION`` has been removed. Use ``__version__`` instead.\n\nFreeType 2.7\n^^^^^^^^^^^^\n\nSupport for FreeType 2.7 has been removed; FreeType 2.8 is the minimum supported.\n\nWe recommend upgrading to at least `FreeType`_ 2.10.4, which fixed a severe\nvulnerability introduced in FreeType 2.6 (:cve:`CVE-2020-15999`).\n\n.. _FreeType: https://www.freetype.org\n\nImage.show command parameter\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe ``command`` parameter has been removed. Use a subclass of\n:py:class:`PIL.ImageShow.Viewer` instead.\n\nImage._showxv\n^^^^^^^^^^^^^\n\n``Image._showxv`` has been removed. Use :py:meth:`~PIL.Image.Image.show`\ninstead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add\na custom :py:class:`~PIL.ImageShow.Viewer` class.\n\nImageFile.raise_ioerror\n^^^^^^^^^^^^^^^^^^^^^^^\n\n``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror``\nhas been removed. Use ``ImageFile.raise_oserror`` instead.\n\n\nAPI Changes\n===========\n\nAdded line width parameter to ImageDraw polygon\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAn optional line ``width`` parameter has been added to ``ImageDraw.Draw.polygon``.\n\n\nAPI Additions\n=============\n\nImageShow.XDGViewer\n^^^^^^^^^^^^^^^^^^^\n\nIf ``xdg-open`` is present on Linux, this new :py:class:`PIL.ImageShow.Viewer` subclass\nwill be registered. It displays images using the application selected by the system.\n\nIt is higher in priority than the other default :py:class:`PIL.ImageShow.Viewer`\ninstances, so it will be preferred by ``im.show()`` or :py:func:`.ImageShow.show()`.\n\nAdded support for \"title\" argument to DisplayViewer\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSupport has been added for the \"title\" argument in\n:py:class:`~PIL.ImageShow.UnixViewer.DisplayViewer`, so that when ``im.show()`` or\n:py:func:`.ImageShow.show()` use the ``display`` command line tool, the \"title\"\nargument will also now be supported, e.g. ``im.show(title=\"My Image\")`` and\n``ImageShow.show(im, title=\"My Image\")``.\n\nSecurity\n========\n\nEnsure JpegImagePlugin stops at the end of a truncated file\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``JpegImagePlugin`` may append an EOF marker to the end of a truncated file, so that\nthe last segment of the data will still be processed by the decoder.\n\nIf the EOF marker is not detected as such however, this could lead to an infinite\nloop where ``JpegImagePlugin`` keeps trying to end the file.\n\nRemove consecutive duplicate tiles that only differ by their offset\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo prevent attempts to slow down loading times for images, if an image has consecutive\nduplicate tiles that only differ by their offset, only load the last tile. Credit to\nGoogle's `OSS-Fuzz`_ project for finding this issue.\n\nRestrict builtins available to ImageMath.eval\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo limit :py:class:`PIL.ImageMath` to working with images, Pillow will now restrict the\nbuiltins available to :py:meth:`PIL.ImageMath.eval`. This will help prevent problems\narising if users evaluate arbitrary expressions, such as\n``ImageMath.eval(\"exec(exit())\")``.\n\nFixed ImagePath.Path array handling\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nCWE-126 and CWE-665 were found when initializing ``ImagePath.Path``.\n\n.. _OSS-Fuzz: https://github.com/google/oss-fuzz\n\nOther Changes\n=============\n\nConvert subsequent GIF frames to RGB or RGBA\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSince each frame of a GIF can have up to 256 colors, after the first frame it is\npossible for there to be too many colors to fit in a P mode image. To allow for this,\nseeking to any subsequent GIF frame will now convert the image to RGB or RGBA,\ndepending on whether or not the first frame had transparency.\n\nSwitched to libjpeg-turbo in macOS and Linux wheels\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe Pillow wheels from PyPI for macOS and Linux have switched from libjpeg to\nlibjpeg-turbo. It is a fork of libjpeg, popular for its speed.\n\nAdded support for pickling TrueType fonts\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTrueType fonts may now be pickled and unpickled. For example:\n\n.. code-block:: python\n\n    import pickle\n    from PIL import ImageFont\n\n    font = ImageFont.truetype(\"arial.ttf\", size=30)\n    pickled_font = pickle.dumps(font, protocol=pickle.HIGHEST_PROTOCOL)\n\n    # Later...\n    unpickled_font = pickle.loads(pickled_font)\n\nAdded support for additional TGA orientations\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTGA images with top right or bottom right orientations are now supported.\n",
            "header": {
                "file": {
                    "path": "docs/releasenotes/9.0.0.rst"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "e778b20b6",
                "new_commit_hash": "fb542636e",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/docs/releasenotes/9.0.0.rst"
            },
            "plus_file": {
                "path": "b/docs/releasenotes/9.0.0.rst"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 116,
                            "length": 6
                        },
                        "new_range": {
                            "start": 116,
                            "length": 14
                        },
                        "section": "To prevent attempts to slow down loading times for images, if an image has conse"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "duplicate tiles that only differ by their offset, only load the last tile. Credit to",
                                "type": "context"
                            },
                            {
                                "content": "Google's `OSS-Fuzz`_ project for finding this issue.",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "Restrict builtins available to ImageMath.eval",
                                "type": "added"
                            },
                            {
                                "content": "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "To limit :py:class:`PIL.ImageMath` to working with images, Pillow will now restrict the",
                                "type": "added"
                            },
                            {
                                "content": "builtins available to :py:meth:`PIL.ImageMath.eval`. This will help prevent problems",
                                "type": "added"
                            },
                            {
                                "content": "arising if users evaluate arbitrary expressions, such as",
                                "type": "added"
                            },
                            {
                                "content": "``ImageMath.eval(\"exec(exit())\")``.",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "Fixed ImagePath.Path array handling",
                                "type": "context"
                            },
                            {
                                "content": "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "#\n# The Python Imaging Library\n# $Id$\n#\n# a simple math add-on for the Python Imaging Library\n#\n# History:\n# 1999-02-15 fl   Original PIL Plus release\n# 2005-05-05 fl   Simplified and cleaned up for PIL 1.1.6\n# 2005-09-12 fl   Fixed int() and float() for Python 2.4.1\n#\n# Copyright (c) 1999-2005 by Secret Labs AB\n# Copyright (c) 2005 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport builtins\n\nfrom . import Image, _imagingmath\n\nVERBOSE = 0\n\n\ndef _isconstant(v):\n    return isinstance(v, (int, float))\n\n\nclass _Operand:\n    \"\"\"Wraps an image operand, providing standard operators\"\"\"\n\n    def __init__(self, im):\n        self.im = im\n\n    def __fixup(self, im1):\n        # convert image to suitable mode\n        if isinstance(im1, _Operand):\n            # argument was an image.\n            if im1.im.mode in (\"1\", \"L\"):\n                return im1.im.convert(\"I\")\n            elif im1.im.mode in (\"I\", \"F\"):\n                return im1.im\n            else:\n                raise ValueError(f\"unsupported mode: {im1.im.mode}\")\n        else:\n            # argument was a constant\n            if _isconstant(im1) and self.im.mode in (\"1\", \"L\", \"I\"):\n                return Image.new(\"I\", self.im.size, im1)\n            else:\n                return Image.new(\"F\", self.im.size, im1)\n\n    def apply(self, op, im1, im2=None, mode=None):\n        im1 = self.__fixup(im1)\n        if im2 is None:\n            # unary operation\n            out = Image.new(mode or im1.mode, im1.size, None)\n            im1.load()\n            try:\n                op = getattr(_imagingmath, op + \"_\" + im1.mode)\n            except AttributeError as e:\n                raise TypeError(f\"bad operand type for '{op}'\") from e\n            _imagingmath.unop(op, out.im.id, im1.im.id)\n        else:\n            # binary operation\n            im2 = self.__fixup(im2)\n            if im1.mode != im2.mode:\n                # convert both arguments to floating point\n                if im1.mode != \"F\":\n                    im1 = im1.convert(\"F\")\n                if im2.mode != \"F\":\n                    im2 = im2.convert(\"F\")\n                if im1.mode != im2.mode:\n                    raise ValueError(\"mode mismatch\")\n            if im1.size != im2.size:\n                # crop both arguments to a common size\n                size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))\n                if im1.size != size:\n                    im1 = im1.crop((0, 0) + size)\n                if im2.size != size:\n                    im2 = im2.crop((0, 0) + size)\n                out = Image.new(mode or im1.mode, size, None)\n            else:\n                out = Image.new(mode or im1.mode, im1.size, None)\n            im1.load()\n            im2.load()\n            try:\n                op = getattr(_imagingmath, op + \"_\" + im1.mode)\n            except AttributeError as e:\n                raise TypeError(f\"bad operand type for '{op}'\") from e\n            _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)\n        return _Operand(out)\n\n    # unary operators\n    def __bool__(self):\n        # an image is \"true\" if it contains at least one non-zero pixel\n        return self.im.getbbox() is not None\n\n    def __abs__(self):\n        return self.apply(\"abs\", self)\n\n    def __pos__(self):\n        return self\n\n    def __neg__(self):\n        return self.apply(\"neg\", self)\n\n    # binary operators\n    def __add__(self, other):\n        return self.apply(\"add\", self, other)\n\n    def __radd__(self, other):\n        return self.apply(\"add\", other, self)\n\n    def __sub__(self, other):\n        return self.apply(\"sub\", self, other)\n\n    def __rsub__(self, other):\n        return self.apply(\"sub\", other, self)\n\n    def __mul__(self, other):\n        return self.apply(\"mul\", self, other)\n\n    def __rmul__(self, other):\n        return self.apply(\"mul\", other, self)\n\n    def __truediv__(self, other):\n        return self.apply(\"div\", self, other)\n\n    def __rtruediv__(self, other):\n        return self.apply(\"div\", other, self)\n\n    def __mod__(self, other):\n        return self.apply(\"mod\", self, other)\n\n    def __rmod__(self, other):\n        return self.apply(\"mod\", other, self)\n\n    def __pow__(self, other):\n        return self.apply(\"pow\", self, other)\n\n    def __rpow__(self, other):\n        return self.apply(\"pow\", other, self)\n\n    # bitwise\n    def __invert__(self):\n        return self.apply(\"invert\", self)\n\n    def __and__(self, other):\n        return self.apply(\"and\", self, other)\n\n    def __rand__(self, other):\n        return self.apply(\"and\", other, self)\n\n    def __or__(self, other):\n        return self.apply(\"or\", self, other)\n\n    def __ror__(self, other):\n        return self.apply(\"or\", other, self)\n\n    def __xor__(self, other):\n        return self.apply(\"xor\", self, other)\n\n    def __rxor__(self, other):\n        return self.apply(\"xor\", other, self)\n\n    def __lshift__(self, other):\n        return self.apply(\"lshift\", self, other)\n\n    def __rshift__(self, other):\n        return self.apply(\"rshift\", self, other)\n\n    # logical\n    def __eq__(self, other):\n        return self.apply(\"eq\", self, other)\n\n    def __ne__(self, other):\n        return self.apply(\"ne\", self, other)\n\n    def __lt__(self, other):\n        return self.apply(\"lt\", self, other)\n\n    def __le__(self, other):\n        return self.apply(\"le\", self, other)\n\n    def __gt__(self, other):\n        return self.apply(\"gt\", self, other)\n\n    def __ge__(self, other):\n        return self.apply(\"ge\", self, other)\n\n\n# conversions\ndef imagemath_int(self):\n    return _Operand(self.im.convert(\"I\"))\n\n\ndef imagemath_float(self):\n    return _Operand(self.im.convert(\"F\"))\n\n\n# logical\ndef imagemath_equal(self, other):\n    return self.apply(\"eq\", self, other, mode=\"I\")\n\n\ndef imagemath_notequal(self, other):\n    return self.apply(\"ne\", self, other, mode=\"I\")\n\n\ndef imagemath_min(self, other):\n    return self.apply(\"min\", self, other)\n\n\ndef imagemath_max(self, other):\n    return self.apply(\"max\", self, other)\n\n\ndef imagemath_convert(self, mode):\n    return _Operand(self.im.convert(mode))\n\n\nops = {}\nfor k, v in list(globals().items()):\n    if k[:10] == \"imagemath_\":\n        ops[k[10:]] = v\n\n\ndef eval(expression, _dict={}, **kw):\n    \"\"\"\n    Evaluates an image expression.\n\n    :param expression: A string containing a Python-style expression.\n    :param options: Values to add to the evaluation context.  You\n                    can either use a dictionary, or one or more keyword\n                    arguments.\n    :return: The evaluated expression. This is usually an image object, but can\n             also be an integer, a floating point value, or a pixel tuple,\n             depending on the expression.\n    \"\"\"\n\n    # build execution namespace\n    args = ops.copy()\n    args.update(_dict)\n    args.update(kw)\n    for k, v in list(args.items()):\n        if hasattr(v, \"im\"):\n            args[k] = _Operand(v)\n\n    out = builtins.eval(expression, args)\n    try:\n        return out.im\n    except AttributeError:\n        return out\n",
            "new_file_content": "#\n# The Python Imaging Library\n# $Id$\n#\n# a simple math add-on for the Python Imaging Library\n#\n# History:\n# 1999-02-15 fl   Original PIL Plus release\n# 2005-05-05 fl   Simplified and cleaned up for PIL 1.1.6\n# 2005-09-12 fl   Fixed int() and float() for Python 2.4.1\n#\n# Copyright (c) 1999-2005 by Secret Labs AB\n# Copyright (c) 2005 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport builtins\n\nfrom . import Image, _imagingmath\n\nVERBOSE = 0\n\n\ndef _isconstant(v):\n    return isinstance(v, (int, float))\n\n\nclass _Operand:\n    \"\"\"Wraps an image operand, providing standard operators\"\"\"\n\n    def __init__(self, im):\n        self.im = im\n\n    def __fixup(self, im1):\n        # convert image to suitable mode\n        if isinstance(im1, _Operand):\n            # argument was an image.\n            if im1.im.mode in (\"1\", \"L\"):\n                return im1.im.convert(\"I\")\n            elif im1.im.mode in (\"I\", \"F\"):\n                return im1.im\n            else:\n                raise ValueError(f\"unsupported mode: {im1.im.mode}\")\n        else:\n            # argument was a constant\n            if _isconstant(im1) and self.im.mode in (\"1\", \"L\", \"I\"):\n                return Image.new(\"I\", self.im.size, im1)\n            else:\n                return Image.new(\"F\", self.im.size, im1)\n\n    def apply(self, op, im1, im2=None, mode=None):\n        im1 = self.__fixup(im1)\n        if im2 is None:\n            # unary operation\n            out = Image.new(mode or im1.mode, im1.size, None)\n            im1.load()\n            try:\n                op = getattr(_imagingmath, op + \"_\" + im1.mode)\n            except AttributeError as e:\n                raise TypeError(f\"bad operand type for '{op}'\") from e\n            _imagingmath.unop(op, out.im.id, im1.im.id)\n        else:\n            # binary operation\n            im2 = self.__fixup(im2)\n            if im1.mode != im2.mode:\n                # convert both arguments to floating point\n                if im1.mode != \"F\":\n                    im1 = im1.convert(\"F\")\n                if im2.mode != \"F\":\n                    im2 = im2.convert(\"F\")\n                if im1.mode != im2.mode:\n                    raise ValueError(\"mode mismatch\")\n            if im1.size != im2.size:\n                # crop both arguments to a common size\n                size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))\n                if im1.size != size:\n                    im1 = im1.crop((0, 0) + size)\n                if im2.size != size:\n                    im2 = im2.crop((0, 0) + size)\n                out = Image.new(mode or im1.mode, size, None)\n            else:\n                out = Image.new(mode or im1.mode, im1.size, None)\n            im1.load()\n            im2.load()\n            try:\n                op = getattr(_imagingmath, op + \"_\" + im1.mode)\n            except AttributeError as e:\n                raise TypeError(f\"bad operand type for '{op}'\") from e\n            _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)\n        return _Operand(out)\n\n    # unary operators\n    def __bool__(self):\n        # an image is \"true\" if it contains at least one non-zero pixel\n        return self.im.getbbox() is not None\n\n    def __abs__(self):\n        return self.apply(\"abs\", self)\n\n    def __pos__(self):\n        return self\n\n    def __neg__(self):\n        return self.apply(\"neg\", self)\n\n    # binary operators\n    def __add__(self, other):\n        return self.apply(\"add\", self, other)\n\n    def __radd__(self, other):\n        return self.apply(\"add\", other, self)\n\n    def __sub__(self, other):\n        return self.apply(\"sub\", self, other)\n\n    def __rsub__(self, other):\n        return self.apply(\"sub\", other, self)\n\n    def __mul__(self, other):\n        return self.apply(\"mul\", self, other)\n\n    def __rmul__(self, other):\n        return self.apply(\"mul\", other, self)\n\n    def __truediv__(self, other):\n        return self.apply(\"div\", self, other)\n\n    def __rtruediv__(self, other):\n        return self.apply(\"div\", other, self)\n\n    def __mod__(self, other):\n        return self.apply(\"mod\", self, other)\n\n    def __rmod__(self, other):\n        return self.apply(\"mod\", other, self)\n\n    def __pow__(self, other):\n        return self.apply(\"pow\", self, other)\n\n    def __rpow__(self, other):\n        return self.apply(\"pow\", other, self)\n\n    # bitwise\n    def __invert__(self):\n        return self.apply(\"invert\", self)\n\n    def __and__(self, other):\n        return self.apply(\"and\", self, other)\n\n    def __rand__(self, other):\n        return self.apply(\"and\", other, self)\n\n    def __or__(self, other):\n        return self.apply(\"or\", self, other)\n\n    def __ror__(self, other):\n        return self.apply(\"or\", other, self)\n\n    def __xor__(self, other):\n        return self.apply(\"xor\", self, other)\n\n    def __rxor__(self, other):\n        return self.apply(\"xor\", other, self)\n\n    def __lshift__(self, other):\n        return self.apply(\"lshift\", self, other)\n\n    def __rshift__(self, other):\n        return self.apply(\"rshift\", self, other)\n\n    # logical\n    def __eq__(self, other):\n        return self.apply(\"eq\", self, other)\n\n    def __ne__(self, other):\n        return self.apply(\"ne\", self, other)\n\n    def __lt__(self, other):\n        return self.apply(\"lt\", self, other)\n\n    def __le__(self, other):\n        return self.apply(\"le\", self, other)\n\n    def __gt__(self, other):\n        return self.apply(\"gt\", self, other)\n\n    def __ge__(self, other):\n        return self.apply(\"ge\", self, other)\n\n\n# conversions\ndef imagemath_int(self):\n    return _Operand(self.im.convert(\"I\"))\n\n\ndef imagemath_float(self):\n    return _Operand(self.im.convert(\"F\"))\n\n\n# logical\ndef imagemath_equal(self, other):\n    return self.apply(\"eq\", self, other, mode=\"I\")\n\n\ndef imagemath_notequal(self, other):\n    return self.apply(\"ne\", self, other, mode=\"I\")\n\n\ndef imagemath_min(self, other):\n    return self.apply(\"min\", self, other)\n\n\ndef imagemath_max(self, other):\n    return self.apply(\"max\", self, other)\n\n\ndef imagemath_convert(self, mode):\n    return _Operand(self.im.convert(mode))\n\n\nops = {}\nfor k, v in list(globals().items()):\n    if k[:10] == \"imagemath_\":\n        ops[k[10:]] = v\n\n\ndef eval(expression, _dict={}, **kw):\n    \"\"\"\n    Evaluates an image expression.\n\n    :param expression: A string containing a Python-style expression.\n    :param options: Values to add to the evaluation context.  You\n                    can either use a dictionary, or one or more keyword\n                    arguments.\n    :return: The evaluated expression. This is usually an image object, but can\n             also be an integer, a floating point value, or a pixel tuple,\n             depending on the expression.\n    \"\"\"\n\n    # build execution namespace\n    args = ops.copy()\n    args.update(_dict)\n    args.update(kw)\n    for k, v in list(args.items()):\n        if hasattr(v, \"im\"):\n            args[k] = _Operand(v)\n\n    code = compile(expression, \"<string>\", \"eval\")\n    for name in code.co_names:\n        if name not in args and name != \"abs\":\n            raise ValueError(f\"'{name}' not allowed\")\n\n    out = builtins.eval(expression, {\"__builtins\": {\"abs\": abs}}, args)\n    try:\n        return out.im\n    except AttributeError:\n        return out\n",
            "header": {
                "file": {
                    "path": "src/PIL/ImageMath.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "7f9c88e14",
                "new_commit_hash": "06bea800d",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/src/PIL/ImageMath.py"
            },
            "plus_file": {
                "path": "b/src/PIL/ImageMath.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 246,
                            "length": 7
                        },
                        "new_range": {
                            "start": 246,
                            "length": 12
                        },
                        "section": "def eval(expression, _dict={}, **kw):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        if hasattr(v, \"im\"):",
                                "type": "context"
                            },
                            {
                                "content": "            args[k] = _Operand(v)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    out = builtins.eval(expression, args)",
                                "type": "deleted"
                            },
                            {
                                "content": "    code = compile(expression, \"<string>\", \"eval\")",
                                "type": "added"
                            },
                            {
                                "content": "    for name in code.co_names:",
                                "type": "added"
                            },
                            {
                                "content": "        if name not in args and name != \"abs\":",
                                "type": "added"
                            },
                            {
                                "content": "            raise ValueError(f\"'{name}' not allowed\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    out = builtins.eval(expression, {\"__builtins\": {\"abs\": abs}}, args)",
                                "type": "added"
                            },
                            {
                                "content": "    try:",
                                "type": "context"
                            },
                            {
                                "content": "        return out.im",
                                "type": "context"
                            },
                            {
                                "content": "    except AttributeError:",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "src/PIL/ImageMath.py",
                            "type": "function",
                            "name": "eval",
                            "content": "def eval(expression, _dict={}, **kw):\n    \"\"\"\n    Evaluates an image expression.\n\n    :param expression: A string containing a Python-style expression.\n    :param options: Values to add to the evaluation context.  You\n                    can either use a dictionary, or one or more keyword\n                    arguments.\n    :return: The evaluated expression. This is usually an image object, but can\n             also be an integer, a floating point value, or a pixel tuple,\n             depending on the expression.\n    \"\"\"\n\n    # build execution namespace\n    args = ops.copy()\n    args.update(_dict)\n    args.update(kw)\n    for k, v in list(args.items()):\n        if hasattr(v, \"im\"):\n            args[k] = _Operand(v)\n\n    out = builtins.eval(expression, args)\n    try:\n        return out.im\n    except AttributeError:\n        return out",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 228,
                            "end_lineno": 253,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "8531b01d6cdf0b70f256f93092caa2a5d91afc11^",
    "new_commit_hash": "8531b01d6cdf0b70f256f93092caa2a5d91afc11",
    "commit_message": "Restrict builtins for ImageMath.eval",
    "commit_date": "2022-01-02T17:23:49+11:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "8531b01d6cdf0b70f256f93092caa2a5d91afc11",
    "test_file_codes": [
        "import pytest\n\nfrom PIL import Image, ImageMath\n\n\ndef pixel(im):\n    if hasattr(im, \"im\"):\n        return f\"{im.mode} {repr(im.getpixel((0, 0)))}\"\n    else:\n        if isinstance(im, int):\n            return int(im)  # hack to deal with booleans\n        print(im)\n\n\nA = Image.new(\"L\", (1, 1), 1)\nB = Image.new(\"L\", (1, 1), 2)\nZ = Image.new(\"L\", (1, 1), 0)  # Z for zero\nF = Image.new(\"F\", (1, 1), 3)\nI = Image.new(\"I\", (1, 1), 4)  # noqa: E741\n\nA2 = A.resize((2, 2))\nB2 = B.resize((2, 2))\n\nimages = {\"A\": A, \"B\": B, \"F\": F, \"I\": I}\n\n\ndef test_sanity():\n    assert ImageMath.eval(\"1\") == 1\n    assert ImageMath.eval(\"1+A\", A=2) == 3\n    assert pixel(ImageMath.eval(\"A+B\", A=A, B=B)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A+B\", images)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"float(A)+B\", images)) == \"F 3.0\"\n    assert pixel(ImageMath.eval(\"int(float(A)+B)\", images)) == \"I 3\"\n\n\ndef test_ops():\n    assert pixel(ImageMath.eval(\"-A\", images)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"+B\", images)) == \"L 2\"\n\n    assert pixel(ImageMath.eval(\"A+B\", images)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A-B\", images)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"A*B\", images)) == \"I 2\"\n    assert pixel(ImageMath.eval(\"A/B\", images)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B**2\", images)) == \"I 4\"\n    assert pixel(ImageMath.eval(\"B**33\", images)) == \"I 2147483647\"\n\n    assert pixel(ImageMath.eval(\"float(A)+B\", images)) == \"F 3.0\"\n    assert pixel(ImageMath.eval(\"float(A)-B\", images)) == \"F -1.0\"\n    assert pixel(ImageMath.eval(\"float(A)*B\", images)) == \"F 2.0\"\n    assert pixel(ImageMath.eval(\"float(A)/B\", images)) == \"F 0.5\"\n    assert pixel(ImageMath.eval(\"float(B)**2\", images)) == \"F 4.0\"\n    assert pixel(ImageMath.eval(\"float(B)**33\", images)) == \"F 8589934592.0\"\n\n\ndef test_prevent_exec():\n    with pytest.raises(ValueError):\n        ImageMath.eval(\"exec('pass')\")\n\n\ndef test_logical():\n    assert pixel(ImageMath.eval(\"not A\", images)) == 0\n    assert pixel(ImageMath.eval(\"A and B\", images)) == \"L 2\"\n    assert pixel(ImageMath.eval(\"A or B\", images)) == \"L 1\"\n\n\ndef test_convert():\n    assert pixel(ImageMath.eval(\"convert(A+B, 'L')\", images)) == \"L 3\"\n    assert pixel(ImageMath.eval(\"convert(A+B, '1')\", images)) == \"1 0\"\n    assert pixel(ImageMath.eval(\"convert(A+B, 'RGB')\", images)) == \"RGB (3, 3, 3)\"\n\n\ndef test_compare():\n    assert pixel(ImageMath.eval(\"min(A, B)\", images)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"max(A, B)\", images)) == \"I 2\"\n    assert pixel(ImageMath.eval(\"A == 1\", images)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A == 2\", images)) == \"I 0\"\n\n\ndef test_one_image_larger():\n    assert pixel(ImageMath.eval(\"A+B\", A=A2, B=B)) == \"I 3\"\n    assert pixel(ImageMath.eval(\"A+B\", A=A, B=B2)) == \"I 3\"\n\n\ndef test_abs():\n    assert pixel(ImageMath.eval(\"abs(A)\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"abs(B)\", B=B)) == \"I 2\"\n\n\ndef test_binary_mod():\n    assert pixel(ImageMath.eval(\"A%A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B%B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A%B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B%A\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z%A\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z%B\", B=B, Z=Z)) == \"I 0\"\n\n\ndef test_bitwise_invert():\n    assert pixel(ImageMath.eval(\"~Z\", Z=Z)) == \"I -1\"\n    assert pixel(ImageMath.eval(\"~A\", A=A)) == \"I -2\"\n    assert pixel(ImageMath.eval(\"~B\", B=B)) == \"I -3\"\n\n\ndef test_bitwise_and():\n    assert pixel(ImageMath.eval(\"Z&Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z&A\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A&Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A&A\", A=A, Z=Z)) == \"I 1\"\n\n\ndef test_bitwise_or():\n    assert pixel(ImageMath.eval(\"Z|Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z|A\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A|Z\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A|A\", A=A, Z=Z)) == \"I 1\"\n\n\ndef test_bitwise_xor():\n    assert pixel(ImageMath.eval(\"Z^Z\", A=A, Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z^A\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A^Z\", A=A, Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A^A\", A=A, Z=Z)) == \"I 0\"\n\n\ndef test_bitwise_leftshift():\n    assert pixel(ImageMath.eval(\"Z<<0\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z<<1\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A<<0\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A<<1\", A=A)) == \"I 2\"\n\n\ndef test_bitwise_rightshift():\n    assert pixel(ImageMath.eval(\"Z>>0\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"Z>>1\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A>>0\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A>>1\", A=A)) == \"I 0\"\n\n\ndef test_logical_eq():\n    assert pixel(ImageMath.eval(\"A==A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B==B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A==B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B==A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_ne():\n    assert pixel(ImageMath.eval(\"A!=A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B!=B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A!=B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B!=A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_lt():\n    assert pixel(ImageMath.eval(\"A<A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B<B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A<B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_le():\n    assert pixel(ImageMath.eval(\"A<=A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<=B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A<=B\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B<=A\", A=A, B=B)) == \"I 0\"\n\n\ndef test_logical_gt():\n    assert pixel(ImageMath.eval(\"A>A\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>B\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"A>B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_ge():\n    assert pixel(ImageMath.eval(\"A>=A\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"B>=B\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"A>=B\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"B>=A\", A=A, B=B)) == \"I 1\"\n\n\ndef test_logical_equal():\n    assert pixel(ImageMath.eval(\"equal(A, A)\", A=A)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(B, B)\", B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(Z, Z)\", Z=Z)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"equal(A, B)\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"equal(B, A)\", A=A, B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"equal(A, Z)\", A=A, Z=Z)) == \"I 0\"\n\n\ndef test_logical_not_equal():\n    assert pixel(ImageMath.eval(\"notequal(A, A)\", A=A)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(B, B)\", B=B)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(Z, Z)\", Z=Z)) == \"I 0\"\n    assert pixel(ImageMath.eval(\"notequal(A, B)\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"notequal(B, A)\", A=A, B=B)) == \"I 1\"\n    assert pixel(ImageMath.eval(\"notequal(A, Z)\", A=A, Z=Z)) == \"I 1\"\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Pillow installation attempts...\nVerifying Pillow installation...\n✅ Pillow installation successful!\nSuccessfully installed Pillow\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 12 packages in 14ms\nInstalled 12 packages in 130ms\n + coverage==7.6.10\n + exceptiongroup==1.2.2\n + iniconfig==2.0.0\n + packaging==24.2\n + pluggy==1.5.0\n + pyqt5==5.15.11\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.16.1\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + setuptools==75.8.0\n + tomli==2.2.1\nResolved 1 package in 890ms\nPrepared 1 package in 33.54s\nInstalled 1 package in 1ms\n + pillow==9.0.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_8531b01d6cdf0b70f256f93092caa2a5d91afc11)\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "\u001b[1m============================= test session starts ==============================\u001b[0m\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_8531b01d6cdf0b70f256f93092caa2a5d91afc11\nconfigfile: setup.cfg\nplugins: cov-6.0.0\ncollected 23 items\n\nr2e_tests/test_1.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m                              [100%]\u001b[0m\n\n==================================== PASSES ====================================\n\u001b[36m\u001b[1m=========================== short test summary info ============================\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_sanity\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_ops\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_prevent_exec\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_convert\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_compare\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_one_image_larger\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_abs\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_binary_mod\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_invert\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_and\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_or\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_xor\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_leftshift\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_rightshift\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_eq\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_ne\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_lt\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_le\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_gt\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_ge\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_equal\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_not_equal\u001b[0m\n\u001b[32m============================== \u001b[32m\u001b[1m23 passed\u001b[0m\u001b[32m in 0.14s\u001b[0m\u001b[32m ==============================\u001b[0m\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "\u001b[1m============================= test session starts ==============================\u001b[0m\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_8531b01d6cdf0b70f256f93092caa2a5d91afc11\nconfigfile: setup.cfg\nplugins: cov-6.0.0\ncollected 23 items\n\nr2e_tests/test_1.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[31mF\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[31m                              [100%]\u001b[0m\n\n=================================== FAILURES ===================================\n\u001b[31m\u001b[1m______________________________ test_prevent_exec _______________________________\u001b[0m\n\n    def test_prevent_exec():\n        with pytest.raises(ValueError):\n>           ImageMath.eval(\"exec('pass')\")\n\u001b[1m\u001b[31mE           Failed: DID NOT RAISE <class 'ValueError'>\u001b[0m\n\n\u001b[1m\u001b[31mr2e_tests/test_1.py\u001b[0m:57: Failed\n==================================== PASSES ====================================\n\u001b[36m\u001b[1m=========================== short test summary info ============================\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_sanity\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_ops\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_convert\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_compare\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_one_image_larger\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_abs\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_binary_mod\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_invert\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_and\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_or\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_xor\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_leftshift\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_bitwise_rightshift\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_eq\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_ne\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_lt\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_le\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_gt\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_ge\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_equal\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_logical_not_equal\u001b[0m\n\u001b[31mFAILED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_prevent_exec\u001b[0m - Failed: DID NOT RAISE <class 'ValueError'>\n\u001b[31m========================= \u001b[31m\u001b[1m1 failed\u001b[0m, \u001b[32m22 passed\u001b[0m\u001b[31m in 0.07s\u001b[0m\u001b[31m =========================\u001b[0m\n",
    "old_commit_res_stderr": ""
}
Tests/test_imagemath.py docs/releasenotes/9.0.0.rst src/PIL/ImageMath.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** ImageMath.eval Allows Execution of Unauthorized Built-ins, Enabling Code Injection

**Description:**
The `ImageMath.eval` function previously permitted the use of arbitrary built-in functions, which poses a security risk by allowing the execution of unauthorized code. For example, evaluating an expression with `exec` did not raise an error, potentially enabling malicious code execution.

**Example Code:**
```python
from PIL import ImageMath

# Attempting to execute arbitrary code
ImageMath.eval("exec('pass')")
```

**Expected Behavior:**
`ImageMath.eval` should restrict the available built-in functions to a safe subset (e.g., only `abs`). Attempts to use unauthorized built-ins like `exec` should raise a `ValueError` indicating that the function is not allowed.

**Actual Behavior:**
Using unauthorized built-ins such as `exec` in `ImageMath.eval` does not raise an error, allowing arbitrary code execution without any restrictions.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### src/PIL/ImageMath.py
#
# The Python Imaging Library
# $Id$
#
# a simple math add-on for the Python Imaging Library
#
# History:
# 1999-02-15 fl   Original PIL Plus release
# 2005-05-05 fl   Simplified and cleaned up for PIL 1.1.6
# 2005-09-12 fl   Fixed int() and float() for Python 2.4.1
#
# Copyright (c) 1999-2005 by Secret Labs AB
# Copyright (c) 2005 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#

import builtins

from . import Image, _imagingmath

VERBOSE = 0


def _isconstant(v):
    return isinstance(v, (int, float))


class _Operand:
    """Wraps an image operand, providing standard operators"""

    def __init__(self, im):
        self.im = im

    def __fixup(self, im1):
        # convert image to suitable mode
        if isinstance(im1, _Operand):
            # argument was an image.
            if im1.im.mode in ("1", "L"):
                return im1.im.convert("I")
            elif im1.im.mode in ("I", "F"):
                return im1.im
            else:
                raise ValueError(f"unsupported mode: {im1.im.mode}")
        else:
            # argument was a constant
            if _isconstant(im1) and self.im.mode in ("1", "L", "I"):
                return Image.new("I", self.im.size, im1)
            else:
                return Image.new("F", self.im.size, im1)

    def apply(self, op, im1, im2=None, mode=None):
        im1 = self.__fixup(im1)
        if im2 is None:
            # unary operation
            out = Image.new(mode or im1.mode, im1.size, None)
            im1.load()
            try:
                op = getattr(_imagingmath, op + "_" + im1.mode)
            except AttributeError as e:
                raise TypeError(f"bad operand type for '{op}'") from e
            _imagingmath.unop(op, out.im.id, im1.im.id)
        else:
            # binary operation
            im2 = self.__fixup(im2)
            if im1.mode != im2.mode:
                # convert both arguments to floating point
                if im1.mode != "F":
                    im1 = im1.convert("F")
                if im2.mode != "F":
                    im2 = im2.convert("F")
                if im1.mode != im2.mode:
                    raise ValueError("mode mismatch")
            if im1.size != im2.size:
                # crop both arguments to a common size
                size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))
                if im1.size != size:
                    im1 = im1.crop((0, 0) + size)
                if im2.size != size:
                    im2 = im2.crop((0, 0) + size)
                out = Image.new(mode or im1.mode, size, None)
            else:
                out = Image.new(mode or im1.mode, im1.size, None)
            im1.load()
            im2.load()
            try:
                op = getattr(_imagingmath, op + "_" + im1.mode)
            except AttributeError as e:
                raise TypeError(f"bad operand type for '{op}'") from e
            _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
        return _Operand(out)

    # unary operators
    def __bool__(self):
        # an image is "true" if it contains at least one non-zero pixel
        return self.im.getbbox() is not None

    def __abs__(self):
        return self.apply("abs", self)

    def __pos__(self):
        return self

    def __neg__(self):
        return self.apply("neg", self)

    # binary operators
    def __add__(self, other):
        return self.apply("add", self, other)

    def __radd__(self, other):
        return self.apply("add", other, self)

    def __sub__(self, other):
        return self.apply("sub", self, other)

    def __rsub__(self, other):
        return self.apply("sub", other, self)

    def __mul__(self, other):
        return self.apply("mul", self, other)

    def __rmul__(self, other):
        return self.apply("mul", other, self)

    def __truediv__(self, other):
        return self.apply("div", self, other)

    def __rtruediv__(self, other):
        return self.apply("div", other, self)

    def __mod__(self, other):
        return self.apply("mod", self, other)

    def __rmod__(self, other):
        return self.apply("mod", other, self)

    def __pow__(self, other):
        return self.apply("pow", self, other)

    def __rpow__(self, other):
        return self.apply("pow", other, self)

    # bitwise
    def __invert__(self):
        return self.apply("invert", self)

    def __and__(self, other):
        return self.apply("and", self, other)

    def __rand__(self, other):
        return self.apply("and", other, self)

    def __or__(self, other):
        return self.apply("or", self, other)

    def __ror__(self, other):
        return self.apply("or", other, self)

    def __xor__(self, other):
        return self.apply("xor", self, other)

    def __rxor__(self, other):
        return self.apply("xor", other, self)

    def __lshift__(self, other):
        return self.apply("lshift", self, other)

    def __rshift__(self, other):
        return self.apply("rshift", self, other)

    # logical
    def __eq__(self, other):
        return self.apply("eq", self, other)

    def __ne__(self, other):
        return self.apply("ne", self, other)

    def __lt__(self, other):
        return self.apply("lt", self, other)

    def __le__(self, other):
        return self.apply("le", self, other)

    def __gt__(self, other):
        return self.apply("gt", self, other)

    def __ge__(self, other):
        return self.apply("ge", self, other)


# conversions
def imagemath_int(self):
    return _Operand(self.im.convert("I"))


def imagemath_float(self):
    return _Operand(self.im.convert("F"))


# logical
def imagemath_equal(self, other):
    return self.apply("eq", self, other, mode="I")


def imagemath_notequal(self, other):
    return self.apply("ne", self, other, mode="I")


def imagemath_min(self, other):
    return self.apply("min", self, other)


def imagemath_max(self, other):
    return self.apply("max", self, other)


def imagemath_convert(self, mode):
    return _Operand(self.im.convert(mode))


ops = {}
for k, v in list(globals().items()):
    if k[:10] == "imagemath_":
        ops[k[10:]] = v


def eval(expression, _dict={}, **kw):
    """
    Evaluates an image expression.

    :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.
    :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.
    """

    # build execution namespace
    args = ops.copy()
    args.update(_dict)
    args.update(kw)
    for k, v in list(args.items()):
        if hasattr(v, "im"):
            args[k] = _Operand(v)

    out = builtins.eval(expression, args)
    try:
        return out.im
    except AttributeError:
        return out

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** ImageMath.eval Allows Execution of Unauthorized Built-ins, Enabling Code Injection

**Description:**
The `ImageMath.eval` function previously permitted the use of arbitrary built-in functions, which poses a security risk by allowing the execution of unauthorized code. For example, evaluating an expression with `exec` did not raise an error, potentially enabling malicious code execution.

**Example Code:**
```python
from PIL import ImageMath

# Attempting to execute arbitrary code
ImageMath.eval("exec('pass')")
```

**Expected Behavior:**
`ImageMath.eval` should restrict the available built-in functions to a safe subset (e.g., only `abs`). Attempts to use unauthorized built-ins like `exec` should raise a `ValueError` indicating that the function is not allowed.

**Actual Behavior:**
Using unauthorized built-ins such as `exec` in `ImageMath.eval` does not raise an error, allowing arbitrary code execution without any restrictions.

[/ISSUE]
{
    "\u001b[1mtest_sanity\u001b[0m": "PASSED",
    "\u001b[1mtest_ops\u001b[0m": "PASSED",
    "\u001b[1mtest_prevent_exec\u001b[0m": "PASSED",
    "\u001b[1mtest_logical\u001b[0m": "PASSED",
    "\u001b[1mtest_convert\u001b[0m": "PASSED",
    "\u001b[1mtest_compare\u001b[0m": "PASSED",
    "\u001b[1mtest_one_image_larger\u001b[0m": "PASSED",
    "\u001b[1mtest_abs\u001b[0m": "PASSED",
    "\u001b[1mtest_binary_mod\u001b[0m": "PASSED",
    "\u001b[1mtest_bitwise_invert\u001b[0m": "PASSED",
    "\u001b[1mtest_bitwise_and\u001b[0m": "PASSED",
    "\u001b[1mtest_bitwise_or\u001b[0m": "PASSED",
    "\u001b[1mtest_bitwise_xor\u001b[0m": "PASSED",
    "\u001b[1mtest_bitwise_leftshift\u001b[0m": "PASSED",
    "\u001b[1mtest_bitwise_rightshift\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_eq\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_ne\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_lt\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_le\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_gt\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_ge\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_equal\u001b[0m": "PASSED",
    "\u001b[1mtest_logical_not_equal\u001b[0m": "PASSED"
}
diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py
index 7f9c88e14..06bea800d 100644
--- a/src/PIL/ImageMath.py
+++ b/src/PIL/ImageMath.py
@@ -246,7 +246,12 @@ def eval(expression, _dict={}, **kw):
         if hasattr(v, "im"):
             args[k] = _Operand(v)
 
-    out = builtins.eval(expression, args)
+    code = compile(expression, "<string>", "eval")
+    for name in code.co_names:
+        if name not in args and name != "abs":
+            raise ValueError(f"'{name}' not allowed")
+
+    out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args)
     try:
         return out.im
     except AttributeError:
 

{"src/PIL/ImageMath.py": "#\n# The Python Imaging Library\n# $Id$\n#\n# a simple math add-on for the Python Imaging Library\n#\n# History:\n# 1999-02-15 fl   Original PIL Plus release\n# 2005-05-05 fl   Simplified and cleaned up for PIL 1.1.6\n# 2005-09-12 fl   Fixed int() and float() for Python 2.4.1\n#\n# Copyright (c) 1999-2005 by Secret Labs AB\n# Copyright (c) 2005 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport builtins\n\nfrom . import Image, _imagingmath\n\nVERBOSE = 0\n\n\ndef _isconstant(v):\n    return isinstance(v, (int, float))\n\n\nclass _Operand:\n    \"\"\"Wraps an image operand, providing standard operators\"\"\"\n\n    def __init__(self, im):\n        self.im = im\n\n    def __fixup(self, im1):\n        # convert image to suitable mode\n        if isinstance(im1, _Operand):\n            # argument was an image.\n            if im1.im.mode in (\"1\", \"L\"):\n                return im1.im.convert(\"I\")\n            elif im1.im.mode in (\"I\", \"F\"):\n                return im1.im\n            else:\n                raise ValueError(f\"unsupported mode: {im1.im.mode}\")\n        else:\n            # argument was a constant\n            if _isconstant(im1) and self.im.mode in (\"1\", \"L\", \"I\"):\n                return Image.new(\"I\", self.im.size, im1)\n            else:\n                return Image.new(\"F\", self.im.size, im1)\n\n    def apply(self, op, im1, im2=None, mode=None):\n        im1 = self.__fixup(im1)\n        if im2 is None:\n            # unary operation\n            out = Image.new(mode or im1.mode, im1.size, None)\n            im1.load()\n            try:\n                op = getattr(_imagingmath, op + \"_\" + im1.mode)\n            except AttributeError as e:\n                raise TypeError(f\"bad operand type for '{op}'\") from e\n            _imagingmath.unop(op, out.im.id, im1.im.id)\n        else:\n            # binary operation\n            im2 = self.__fixup(im2)\n            if im1.mode != im2.mode:\n                # convert both arguments to floating point\n                if im1.mode != \"F\":\n                    im1 = im1.convert(\"F\")\n                if im2.mode != \"F\":\n                    im2 = im2.convert(\"F\")\n                if im1.mode != im2.mode:\n                    raise ValueError(\"mode mismatch\")\n            if im1.size != im2.size:\n                # crop both arguments to a common size\n                size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))\n                if im1.size != size:\n                    im1 = im1.crop((0, 0) + size)\n                if im2.size != size:\n                    im2 = im2.crop((0, 0) + size)\n                out = Image.new(mode or im1.mode, size, None)\n            else:\n                out = Image.new(mode or im1.mode, im1.size, None)\n            im1.load()\n            im2.load()\n            try:\n                op = getattr(_imagingmath, op + \"_\" + im1.mode)\n            except AttributeError as e:\n                raise TypeError(f\"bad operand type for '{op}'\") from e\n            _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)\n        return _Operand(out)\n\n    # unary operators\n    def __bool__(self):\n        # an image is \"true\" if it contains at least one non-zero pixel\n        return self.im.getbbox() is not None\n\n    def __abs__(self):\n        return self.apply(\"abs\", self)\n\n    def __pos__(self):\n        return self\n\n    def __neg__(self):\n        return self.apply(\"neg\", self)\n\n    # binary operators\n    def __add__(self, other):\n        return self.apply(\"add\", self, other)\n\n    def __radd__(self, other):\n        return self.apply(\"add\", other, self)\n\n    def __sub__(self, other):\n        return self.apply(\"sub\", self, other)\n\n    def __rsub__(self, other):\n        return self.apply(\"sub\", other, self)\n\n    def __mul__(self, other):\n        return self.apply(\"mul\", self, other)\n\n    def __rmul__(self, other):\n        return self.apply(\"mul\", other, self)\n\n    def __truediv__(self, other):\n        return self.apply(\"div\", self, other)\n\n    def __rtruediv__(self, other):\n        return self.apply(\"div\", other, self)\n\n    def __mod__(self, other):\n        return self.apply(\"mod\", self, other)\n\n    def __rmod__(self, other):\n        return self.apply(\"mod\", other, self)\n\n    def __pow__(self, other):\n        return self.apply(\"pow\", self, other)\n\n    def __rpow__(self, other):\n        return self.apply(\"pow\", other, self)\n\n    # bitwise\n    def __invert__(self):\n        return self.apply(\"invert\", self)\n\n    def __and__(self, other):\n        return self.apply(\"and\", self, other)\n\n    def __rand__(self, other):\n        return self.apply(\"and\", other, self)\n\n    def __or__(self, other):\n        return self.apply(\"or\", self, other)\n\n    def __ror__(self, other):\n        return self.apply(\"or\", other, self)\n\n    def __xor__(self, other):\n        return self.apply(\"xor\", self, other)\n\n    def __rxor__(self, other):\n        return self.apply(\"xor\", other, self)\n\n    def __lshift__(self, other):\n        return self.apply(\"lshift\", self, other)\n\n    def __rshift__(self, other):\n        return self.apply(\"rshift\", self, other)\n\n    # logical\n    def __eq__(self, other):\n        return self.apply(\"eq\", self, other)\n\n    def __ne__(self, other):\n        return self.apply(\"ne\", self, other)\n\n    def __lt__(self, other):\n        return self.apply(\"lt\", self, other)\n\n    def __le__(self, other):\n        return self.apply(\"le\", self, other)\n\n    def __gt__(self, other):\n        return self.apply(\"gt\", self, other)\n\n    def __ge__(self, other):\n        return self.apply(\"ge\", self, other)\n\n\n# conversions\ndef imagemath_int(self):\n    return _Operand(self.im.convert(\"I\"))\n\n\ndef imagemath_float(self):\n    return _Operand(self.im.convert(\"F\"))\n\n\n# logical\ndef imagemath_equal(self, other):\n    return self.apply(\"eq\", self, other, mode=\"I\")\n\n\ndef imagemath_notequal(self, other):\n    return self.apply(\"ne\", self, other, mode=\"I\")\n\n\ndef imagemath_min(self, other):\n    return self.apply(\"min\", self, other)\n\n\ndef imagemath_max(self, other):\n    return self.apply(\"max\", self, other)\n\n\ndef imagemath_convert(self, mode):\n    return _Operand(self.im.convert(mode))\n\n\nops = {}\nfor k, v in list(globals().items()):\n    if k[:10] == \"imagemath_\":\n        ops[k[10:]] = v\n\n\ndef eval(expression, _dict={}, **kw):\n    \"\"\"\n    Evaluates an image expression.\n\n    :param expression: A string containing a Python-style expression.\n    :param options: Values to add to the evaluation context.  You\n                    can either use a dictionary, or one or more keyword\n                    arguments.\n    :return: The evaluated expression. This is usually an image object, but can\n             also be an integer, a floating point value, or a pixel tuple,\n             depending on the expression.\n    \"\"\"\n\n    # build execution namespace\n    args = ops.copy()\n    args.update(_dict)\n    args.update(kw)\n    for k, v in list(args.items()):\n        if hasattr(v, \"im\"):\n            args[k] = _Operand(v)\n\n    out = builtins.eval(expression, args)\n    try:\n        return out.im\n    except AttributeError:\n        return out\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** ImageMath.eval Allows Execution of Unauthorized Built-ins, Enabling Code Injection

**Description:**
The `ImageMath.eval` function previously permitted the use of arbitrary built-in functions, which poses a security risk by allowing the execution of unauthorized code. For example, evaluating an expression with `exec` did not raise an error, potentially enabling malicious code execution.

**Example Code:**
```python
from PIL import ImageMath

# Attempting to execute arbitrary code
ImageMath.eval("exec('pass')")
```

**Expected Behavior:**
`ImageMath.eval` should restrict the available built-in functions to a safe subset (e.g., only `abs`). Attempts to use unauthorized built-ins like `exec` should raise a `ValueError` indicating that the function is not allowed.

**Actual Behavior:**
Using unauthorized built-ins such as `exec` in `ImageMath.eval` does not raise an error, allowing arbitrary code execution without any restrictions.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### src/PIL/ImageMath.py
#
# The Python Imaging Library
# $Id$
#
# a simple math add-on for the Python Imaging Library
#
# History:
# 1999-02-15 fl   Original PIL Plus release
# 2005-05-05 fl   Simplified and cleaned up for PIL 1.1.6
# 2005-09-12 fl   Fixed int() and float() for Python 2.4.1
#
# Copyright (c) 1999-2005 by Secret Labs AB
# Copyright (c) 2005 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#

import builtins

from . import Image, _imagingmath

VERBOSE = 0


def _isconstant(v):
    return isinstance(v, (int, float))


class _Operand:
    """Wraps an image operand, providing standard operators"""

    def __init__(self, im):
        self.im = im

    def __fixup(self, im1):
        # convert image to suitable mode
        if isinstance(im1, _Operand):
            # argument was an image.
            if im1.im.mode in ("1", "L"):
                return im1.im.convert("I")
            elif im1.im.mode in ("I", "F"):
                return im1.im
            else:
                raise ValueError(f"unsupported mode: {im1.im.mode}")
        else:
            # argument was a constant
            if _isconstant(im1) and self.im.mode in ("1", "L", "I"):
                return Image.new("I", self.im.size, im1)
            else:
                return Image.new("F", self.im.size, im1)

    def apply(self, op, im1, im2=None, mode=None):
        im1 = self.__fixup(im1)
        if im2 is None:
            # unary operation
            out = Image.new(mode or im1.mode, im1.size, None)
            im1.load()
            try:
                op = getattr(_imagingmath, op + "_" + im1.mode)
            except AttributeError as e:
                raise TypeError(f"bad operand type for '{op}'") from e
            _imagingmath.unop(op, out.im.id, im1.im.id)
        else:
            # binary operation
            im2 = self.__fixup(im2)
            if im1.mode != im2.mode:
                # convert both arguments to floating point
                if im1.mode != "F":
                    im1 = im1.convert("F")
                if im2.mode != "F":
                    im2 = im2.convert("F")
                if im1.mode != im2.mode:
                    raise ValueError("mode mismatch")
            if im1.size != im2.size:
                # crop both arguments to a common size
                size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1]))
                if im1.size != size:
                    im1 = im1.crop((0, 0) + size)
                if im2.size != size:
                    im2 = im2.crop((0, 0) + size)
                out = Image.new(mode or im1.mode, size, None)
            else:
                out = Image.new(mode or im1.mode, im1.size, None)
            im1.load()
            im2.load()
            try:
                op = getattr(_imagingmath, op + "_" + im1.mode)
            except AttributeError as e:
                raise TypeError(f"bad operand type for '{op}'") from e
            _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
        return _Operand(out)

    # unary operators
    def __bool__(self):
        # an image is "true" if it contains at least one non-zero pixel
        return self.im.getbbox() is not None

    def __abs__(self):
        return self.apply("abs", self)

    def __pos__(self):
        return self

    def __neg__(self):
        return self.apply("neg", self)

    # binary operators
    def __add__(self, other):
        return self.apply("add", self, other)

    def __radd__(self, other):
        return self.apply("add", other, self)

    def __sub__(self, other):
        return self.apply("sub", self, other)

    def __rsub__(self, other):
        return self.apply("sub", other, self)

    def __mul__(self, other):
        return self.apply("mul", self, other)

    def __rmul__(self, other):
        return self.apply("mul", other, self)

    def __truediv__(self, other):
        return self.apply("div", self, other)

    def __rtruediv__(self, other):
        return self.apply("div", other, self)

    def __mod__(self, other):
        return self.apply("mod", self, other)

    def __rmod__(self, other):
        return self.apply("mod", other, self)

    def __pow__(self, other):
        return self.apply("pow", self, other)

    def __rpow__(self, other):
        return self.apply("pow", other, self)

    # bitwise
    def __invert__(self):
        return self.apply("invert", self)

    def __and__(self, other):
        return self.apply("and", self, other)

    def __rand__(self, other):
        return self.apply("and", other, self)

    def __or__(self, other):
        return self.apply("or", self, other)

    def __ror__(self, other):
        return self.apply("or", other, self)

    def __xor__(self, other):
        return self.apply("xor", self, other)

    def __rxor__(self, other):
        return self.apply("xor", other, self)

    def __lshift__(self, other):
        return self.apply("lshift", self, other)

    def __rshift__(self, other):
        return self.apply("rshift", self, other)

    # logical
    def __eq__(self, other):
        return self.apply("eq", self, other)

    def __ne__(self, other):
        return self.apply("ne", self, other)

    def __lt__(self, other):
        return self.apply("lt", self, other)

    def __le__(self, other):
        return self.apply("le", self, other)

    def __gt__(self, other):
        return self.apply("gt", self, other)

    def __ge__(self, other):
        return self.apply("ge", self, other)


# conversions
def imagemath_int(self):
    return _Operand(self.im.convert("I"))


def imagemath_float(self):
    return _Operand(self.im.convert("F"))


# logical
def imagemath_equal(self, other):
    return self.apply("eq", self, other, mode="I")


def imagemath_notequal(self, other):
    return self.apply("ne", self, other, mode="I")


def imagemath_min(self, other):
    return self.apply("min", self, other)


def imagemath_max(self, other):
    return self.apply("max", self, other)


def imagemath_convert(self, mode):
    return _Operand(self.im.convert(mode))


ops = {}
for k, v in list(globals().items()):
    if k[:10] == "imagemath_":
        ops[k[10:]] = v


def eval(expression, _dict={}, **kw):
    """
    Evaluates an image expression.

    :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.
    :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.
    """

    # build execution namespace
    args = ops.copy()
    args.update(_dict)
    args.update(kw)
    for k, v in list(args.items()):
        if hasattr(v, "im"):
            args[k] = _Operand(v)

    out = builtins.eval(expression, args)
    try:
        return out.im
    except AttributeError:
        return out

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user