Skip to content

Commit

Permalink
Merge branch 'main' into init
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Jul 28, 2024
2 parents cab66d7 + 7b8a031 commit 1b92e78
Show file tree
Hide file tree
Showing 148 changed files with 3,840 additions and 1,387 deletions.
10 changes: 8 additions & 2 deletions .ci/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,18 @@ python3 -m pip install -U pytest-timeout
python3 -m pip install pyroma

if [[ $(uname) != CYGWIN* ]]; then
python3 -m pip install numpy
# TODO Update condition when NumPy supports free-threading
if [[ "$PYTHON_GIL" == "0" ]]; then
python3 -m pip install numpy --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
else
python3 -m pip install numpy
fi

# PyQt6 doesn't support PyPy3
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
sudo apt-get -qq install libegl1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0
python3 -m pip install pyqt6
# TODO Update condition when pyqt6 supports free-threading
if ! [[ "$PYTHON_GIL" == "0" ]]; then python3 -m pip install pyqt6 ; fi
fi

# Pyroma uses non-isolated build and fails with old setuptools
Expand Down
2 changes: 1 addition & 1 deletion .ci/requirements-mypy.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
mypy==1.10.1
mypy==1.11.0
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
BasedOnStyle: Google
AlwaysBreakAfterReturnType: All
AllowShortIfStatementsOnASingleLine: false
AlignAfterOpenBracket: AlwaysBreak
AlignAfterOpenBracket: BlockIndent
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/cifuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ concurrency:

jobs:
Fuzzing:
# Disabled until google/oss-fuzz#11419 upgrades Python to 3.9+
if: false
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
Expand Down
30 changes: 20 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,24 @@ jobs:
"3.9",
]
include:
- python-version: "3.11"
PYTHONOPTIMIZE: 1
REVERSE: "--reverse"
- python-version: "3.10"
PYTHONOPTIMIZE: 2
- { python-version: "3.11", PYTHONOPTIMIZE: 1, REVERSE: "--reverse" }
- { python-version: "3.10", PYTHONOPTIMIZE: 2 }
# Free-threaded
- { os: "ubuntu-latest", python-version: "3.13-dev", disable-gil: true }
# M1 only available for 3.10+
- os: "macos-13"
python-version: "3.9"
- { os: "macos-13", python-version: "3.9" }
exclude:
- os: "macos-14"
python-version: "3.9"
- { os: "macos-14", python-version: "3.9" }

runs-on: ${{ matrix.os }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }} ${{ matrix.disable-gil && 'free-threaded' || '' }}

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
if: "${{ !matrix.disable-gil }}"
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
Expand All @@ -78,6 +76,18 @@ jobs:
".ci/*.sh"
"pyproject.toml"
- name: Set up Python ${{ matrix.python-version }} (free-threaded)
uses: deadsnakes/action@v3.1.0
if: "${{ matrix.disable-gil }}"
with:
python-version: ${{ matrix.python-version }}
nogil: ${{ matrix.disable-gil }}

- name: Set PYTHON_GIL
if: "${{ matrix.disable-gil }}"
run: |
echo "PYTHON_GIL=0" >> $GITHUB_ENV
- name: Build system information
run: python3 .github/workflows/system-info.py

Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/wheels-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ elif [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then
else
yum install -y fribidi
fi

if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ]; then
# TODO Update condition when NumPy supports free-threading
if [ $(python3 -c "import sysconfig;print(sysconfig.get_config_var('Py_GIL_DISABLED'))") == "1" ]; then
python3 -m pip install numpy --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
else
python3 -m pip install numpy
fi
fi

if [ ! -d "test-images-main" ]; then
Expand Down
33 changes: 32 additions & 1 deletion .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Wheels

on:
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# │ │ │ │ │
- cron: "42 1 * * 0,3"
push:
paths:
- ".ci/requirements-cibw.txt"
Expand Down Expand Up @@ -33,6 +41,7 @@ env:

jobs:
build-1-QEMU-emulated-wheels:
if: github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'
name: aarch64 ${{ matrix.python-version }} ${{ matrix.spec }}
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -88,6 +97,7 @@ jobs:
path: ./wheelhouse/*.whl

build-2-native-wheels:
if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow'
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
Expand Down Expand Up @@ -129,6 +139,7 @@ jobs:
env:
CIBW_ARCHS: ${{ matrix.cibw_arch }}
CIBW_BUILD: ${{ matrix.build }}
CIBW_FREE_THREADED_SUPPORT: True
CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
CIBW_PRERELEASE_PYTHONS: True
Expand All @@ -140,6 +151,7 @@ jobs:
path: ./wheelhouse/*.whl

windows:
if: github.event_name != 'schedule' || github.repository_owner == 'python-pillow'
name: Windows ${{ matrix.cibw_arch }}
runs-on: windows-latest
strategy:
Expand Down Expand Up @@ -201,6 +213,7 @@ jobs:
CIBW_ARCHS: ${{ matrix.cibw_arch }}
CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
CIBW_CACHE_PATH: "C:\\cibw"
CIBW_FREE_THREADED_SUPPORT: True
CIBW_PRERELEASE_PYTHONS: True
CIBW_TEST_SKIP: "*-win_arm64"
CIBW_TEST_COMMAND: 'docker run --rm
Expand All @@ -225,6 +238,7 @@ jobs:
path: winbuild\build\bin\fribidi*

sdist:
if: github.event_name != 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -243,8 +257,25 @@ jobs:
name: dist-sdist
path: dist/*.tar.gz

scientific-python-nightly-wheels-publish:
if: github.repository_owner == 'python-pillow' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
needs: [build-2-native-wheels, windows]
runs-on: ubuntu-latest
name: Upload wheels to scientific-python-nightly-wheels
steps:
- uses: actions/download-artifact@v4
with:
pattern: dist-*
path: dist
merge-multiple: true
- name: Upload wheels to scientific-python-nightly-wheels
uses: scientific-python/upload-nightly-action@b67d7fcc0396e1128a474d1ab2b48aa94680f9fc # 0.5.0
with:
artifacts_path: dist
anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }}

pypi-publish:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
if: github.repository_owner == 'python-pillow' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
needs: [build-1-QEMU-emulated-wheels, build-2-native-wheels, windows, sdist]
runs-on: ubuntu-latest
name: Upload release to PyPI
Expand Down
12 changes: 12 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ Changelog (Pillow)
11.0.0 (unreleased)
-------------------

- Deprecate ImageMath lambda_eval and unsafe_eval options argument #8242
[radarhere]

- Changed ContainerIO to subclass IO #8240
[radarhere]

- Move away from APIs that use borrowed references under the free-threaded build #8216
[hugovk, lysnikolaou]

- Allow size argument to resize() to be a NumPy array #8201
[radarhere]

- Drop support for Python 3.8 #8183
[hugovk, radarhere]

Expand Down
91 changes: 80 additions & 11 deletions Tests/test_file_container.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from __future__ import annotations

from typing import Literal

import pytest

from PIL import ContainerIO, Image
Expand All @@ -23,6 +21,13 @@ def test_isatty() -> None:
assert container.isatty() is False


def test_seekable() -> None:
with hopper() as im:
container = ContainerIO.ContainerIO(im, 0, 0)

assert container.seekable() is True


@pytest.mark.parametrize(
"mode, expected_position",
(
Expand All @@ -31,7 +36,7 @@ def test_isatty() -> None:
(2, 100),
),
)
def test_seek_mode(mode: Literal[0, 1, 2], expected_position: int) -> None:
def test_seek_mode(mode: int, expected_position: int) -> None:
# Arrange
with open(TEST_FILE, "rb") as fh:
container = ContainerIO.ContainerIO(fh, 22, 100)
Expand All @@ -44,14 +49,22 @@ def test_seek_mode(mode: Literal[0, 1, 2], expected_position: int) -> None:
assert container.tell() == expected_position


@pytest.mark.parametrize("bytesmode", (True, False))
def test_readable(bytesmode: bool) -> None:
with open(TEST_FILE, "rb" if bytesmode else "r") as fh:
container = ContainerIO.ContainerIO(fh, 0, 120)

assert container.readable() is True


@pytest.mark.parametrize("bytesmode", (True, False))
def test_read_n0(bytesmode: bool) -> None:
# Arrange
with open(TEST_FILE, "rb" if bytesmode else "r") as fh:
container = ContainerIO.ContainerIO(fh, 22, 100)

# Act
container.seek(81)
assert container.seek(81) == 81
data = container.read()

# Assert
Expand All @@ -67,7 +80,7 @@ def test_read_n(bytesmode: bool) -> None:
container = ContainerIO.ContainerIO(fh, 22, 100)

# Act
container.seek(81)
assert container.seek(81) == 81
data = container.read(3)

# Assert
Expand All @@ -83,7 +96,7 @@ def test_read_eof(bytesmode: bool) -> None:
container = ContainerIO.ContainerIO(fh, 22, 100)

# Act
container.seek(100)
assert container.seek(100) == 100
data = container.read()

# Assert
Expand All @@ -94,21 +107,65 @@ def test_read_eof(bytesmode: bool) -> None:

@pytest.mark.parametrize("bytesmode", (True, False))
def test_readline(bytesmode: bool) -> None:
# Arrange
with open(TEST_FILE, "rb" if bytesmode else "r") as fh:
container = ContainerIO.ContainerIO(fh, 0, 120)

# Act
data = container.readline()

# Assert
if bytesmode:
data = data.decode()
assert data == "This is line 1\n"

data = container.readline(4)
if bytesmode:
data = data.decode()
assert data == "This"


@pytest.mark.parametrize("bytesmode", (True, False))
def test_readlines(bytesmode: bool) -> None:
expected = [
"This is line 1\n",
"This is line 2\n",
"This is line 3\n",
"This is line 4\n",
"This is line 5\n",
"This is line 6\n",
"This is line 7\n",
"This is line 8\n",
]
with open(TEST_FILE, "rb" if bytesmode else "r") as fh:
container = ContainerIO.ContainerIO(fh, 0, 120)

data = container.readlines()
if bytesmode:
data = [line.decode() for line in data]
assert data == expected

assert container.seek(0) == 0

data = container.readlines(2)
if bytesmode:
data = [line.decode() for line in data]
assert data == expected[:2]


@pytest.mark.parametrize("bytesmode", (True, False))
def test_write(bytesmode: bool) -> None:
with open(TEST_FILE, "rb" if bytesmode else "r") as fh:
container = ContainerIO.ContainerIO(fh, 0, 120)

assert container.writable() is False

with pytest.raises(NotImplementedError):
container.write(b"" if bytesmode else "")
with pytest.raises(NotImplementedError):
container.writelines([])
with pytest.raises(NotImplementedError):
container.truncate()


@pytest.mark.parametrize("bytesmode", (True, False))
def test_iter(bytesmode: bool) -> None:
# Arrange
expected = [
"This is line 1\n",
Expand All @@ -124,9 +181,21 @@ def test_readlines(bytesmode: bool) -> None:
container = ContainerIO.ContainerIO(fh, 0, 120)

# Act
data = container.readlines()
data = []
for line in container:
data.append(line)

# Assert
if bytesmode:
data = [line.decode() for line in data]
assert data == expected


@pytest.mark.parametrize("bytesmode", (True, False))
def test_file(bytesmode: bool) -> None:
with open(TEST_FILE, "rb" if bytesmode else "r") as fh:
container = ContainerIO.ContainerIO(fh, 0, 120)

assert isinstance(container.fileno(), int)
container.flush()
container.close()
1 change: 1 addition & 0 deletions Tests/test_file_iptc.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def test_getiptcinfo_fotostation() -> None:
iptc = IptcImagePlugin.getiptcinfo(im)

# Assert
assert iptc is not None
for tag in iptc.keys():
if tag[0] == 240:
return
Expand Down
Loading

0 comments on commit 1b92e78

Please sign in to comment.