From fdbb1870d44b2a831d37b0545f5bdba2d147518e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 08:52:46 -0500 Subject: [PATCH 001/309] CI: Add a cygwin run to GitHub Actions. Requested for the _imagingtk fix. --- .github/workflows/test-cygwin.yml | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/test-cygwin.yml diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml new file mode 100644 index 00000000000..4275e65a67b --- /dev/null +++ b/.github/workflows/test-cygwin.yml @@ -0,0 +1,48 @@ +name: Test Cygwin + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: windows-2019 + name: Cygwin Python 3.8 + + steps: + - name: Checkout Pillow + uses: actions/checkout@v2 + - name: Set up Cygwin + uses: egor-tensin/setup-cygwin@v3 + with: + platform: x64 + packages: > + python38-devel python38-tkinter libfreetype-devel + libimagequant-devel libjpeg-devel liblcms2-devel + libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel + libxcb-devel python38-olefile python38-pathlib + python38-numpy netpbm ImageMagick jpeg xorg-server-extra + xorg-server-common xinit python38-setuptools + python38-wheel + install-dir: 'C:\tools\cygwin' + - name: Clean up path + uses: egor-tensin/cleanup-path@v2 + with: + dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Build Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 setup.py bdist_wheel + - name: Install Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + - name: Test Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 selftest.py + xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 67bef87a070af4e239707de64670ac833031e46c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 18:19:22 -0500 Subject: [PATCH 002/309] CI: Specify where the command line goes in the shell. I think this is how specifying the shell works. The documentation isn't terribly clear. --- .github/workflows/test-cygwin.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 4275e65a67b..c22fe2bec70 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -34,15 +34,15 @@ jobs: with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - name: Build Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 setup.py bdist_wheel - name: Install Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl - name: Test Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 selftest.py xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From b798989f33b4ef275360cb491d0c555079fd899e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:08:28 -0500 Subject: [PATCH 003/309] CI: Make sure all python requirements are installed in Cygwin CI. For some reason wheel wasn't installed properly. --- .github/workflows/test-cygwin.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c22fe2bec70..cb0c84ef53c 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -21,18 +21,24 @@ jobs: with: platform: x64 packages: > - python38-devel python38-tkinter libfreetype-devel - libimagequant-devel libjpeg-devel liblcms2-devel - libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel - libxcb-devel python38-olefile python38-pathlib - python38-numpy netpbm ImageMagick jpeg xorg-server-extra - xorg-server-common xinit python38-setuptools - python38-wheel + libfreetype-devel libimagequant-devel libjpeg-devel + liblcms2-devel libopenjp2-devel libraqm-devel + libtiff-devel libwebp-devel libxcb-devel netpbm + ImageMagick jpeg xorg-server-extra xorg-server-common + xinit python38-setuptools python38-devel python38-tkinter + python38-wheel python38-pip python38-olefile + python38-numpy python38-pytest python38-sphinx + python38-packaging python38-cffi install-dir: 'C:\tools\cygwin' - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Ensure python dependencies installed + shell: 'C:\tools\cygwin\bin\dash.exe {0}' + run: | + /usr/bin/python3.8 -m pip install pip wheel setuptools + /usr/bin/python3.8 -m pip install -r requirements.txt - name: Build Pillow shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | From a4495decf878298abb8b379251c6cf98aa662736 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:57:44 -0500 Subject: [PATCH 004/309] CI: Stop using dash so the DOS line endings don't throw things off. --- .github/workflows/test-cygwin.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index cb0c84ef53c..904658f80a2 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -29,26 +29,23 @@ jobs: python38-wheel python38-pip python38-olefile python38-numpy python38-pytest python38-sphinx python38-packaging python38-cffi + libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel install-dir: 'C:\tools\cygwin' - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - name: Ensure python dependencies installed - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 -m pip install pip wheel setuptools - /usr/bin/python3.8 -m pip install -r requirements.txt + C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools + C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt - name: Build Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 setup.py bdist_wheel + C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + C:\tools\cygwin\bin\python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl - name: Test Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 selftest.py - xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\python3.8 selftest.py + C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 8ebac45f24fa53bcdb148b418222b41db0455626 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:14:41 -0500 Subject: [PATCH 005/309] CI: Explicitly use dash to get shell globbing. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 904658f80a2..c1404b2c68b 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow run: | - C:\tools\cygwin\bin\python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install *.whl' - name: Test Pillow run: | C:\tools\cygwin\bin\python3.8 selftest.py From cd087c600775c52af65a1a3ea6ff9818718f63a9 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:20:47 -0500 Subject: [PATCH 006/309] CI: Fix the path to the build wheels. setup.py bdist_wheel goes to dist/*.whl pip wheel goes to *.whl --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c1404b2c68b..ff496443b00 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow run: | - C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install *.whl' + C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - name: Test Pillow run: | C:\tools\cygwin\bin\python3.8 selftest.py From 1daaf9273ceb94fd15e84cbe4f9f095ed7f6669c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:53:43 -0500 Subject: [PATCH 007/309] CI: Split the config check from the actual test. I'm using selftest.py to check whether I've installed everything. Pytest actually finds and runs the tests. For some reason that wasn't running earlier. --- .github/workflows/test-cygwin.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index ff496443b00..94711051441 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -45,7 +45,9 @@ jobs: - name: Install Pillow run: | C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - - name: Test Pillow + - name: Check Pillow configuration run: | C:\tools\cygwin\bin\python3.8 selftest.py + - name: Test Pillow + run: | C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 9ec4034bff2f03bac37ac0ec0e60f4c0bbaf3313 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 08:26:24 -0500 Subject: [PATCH 008/309] CI: Incorporate suggestions from PR. Upload coverage information, add Cygwin to the list of systems with CI, space out entries. --- .github/workflows/test-cygwin.yml | 28 ++++++++++++++++++++-------- docs/installation.rst | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 94711051441..d7c74e7bc65 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -1,12 +1,6 @@ name: Test Cygwin -on: - push: - branches: - - main - pull_request: - branches: - - main +on: [push, pull_request, workflow_dispatch] jobs: build: @@ -16,6 +10,7 @@ jobs: steps: - name: Checkout Pillow uses: actions/checkout@v2 + - name: Set up Cygwin uses: egor-tensin/setup-cygwin@v3 with: @@ -31,23 +26,40 @@ jobs: python38-packaging python38-cffi libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel install-dir: 'C:\tools\cygwin' + - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - - name: Ensure python dependencies installed + + - name: Build system information + run: C:\tools\cygwin\bin\python3.8 .github/workflows/system-info.py + + - name: Ensure Python dependencies are installed run: | C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt + - name: Build Pillow run: | C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel + - name: Install Pillow run: | C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' + - name: Check Pillow configuration run: | C:\tools\cygwin\bin\python3.8 selftest.py + - name: Test Pillow run: | C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + + - name: After success + run: | + C:\tools\cygwin\bin\dash .ci/after_success.sh + + - name: Upload coverage + run: | + C:\tools\cygwin\bin\bash -c 'bash <(curl -s https://codecov.io/bash) -F' diff --git a/docs/installation.rst b/docs/installation.rst index 1fb6897d279..3e5123c7a53 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,6 +460,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| Cygwin | 3.8 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | From 407abbfa186d545cd2dc4850c0f6f4d29032c386 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:12:31 -0500 Subject: [PATCH 009/309] CI: Try to get tests fully working The actual test step wasn't running, so try to run that as a shell script rather than an executable. Also get more of the dependencies installed. --- .github/workflows/test-cygwin.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d7c74e7bc65..24bd6a2be20 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -23,8 +23,7 @@ jobs: xinit python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy python38-pytest python38-sphinx - python38-packaging python38-cffi - libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel + python38-packaging python38-cffi python38-requests install-dir: 'C:\tools\cygwin' - name: Clean up path @@ -54,11 +53,11 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh - name: After success run: | - C:\tools\cygwin\bin\dash .ci/after_success.sh + C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage run: | From 4cfb1a854f531afdab4c0c4bb7a4fc6fc754e69a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:13:56 -0500 Subject: [PATCH 010/309] CI: Use the new GitHub Action for codecov The old bash downloader will be removed soon. --- .github/workflows/test-cygwin.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 24bd6a2be20..8bb3e4cdd4d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,5 +60,8 @@ jobs: C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage - run: | - C:\tools\cygwin\bin\bash -c 'bash <(curl -s https://codecov.io/bash) -F' + uses: codecov/codecov-actions@v2 + with: + file: ./coverage.xml + flags: GHA_Cygwin + name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} From 90cf149cbe9449934767eba376f690d37b1cc53a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:17:28 -0500 Subject: [PATCH 011/309] CI: Fix the spelling on the codecov repository. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 8bb3e4cdd4d..6215b72f8f5 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,7 +60,7 @@ jobs: C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-actions@v2 + uses: codecov/codecov-action@v2 with: file: ./coverage.xml flags: GHA_Cygwin From 7fd3e9977e7dd0c04e4e72fdc65f59253f0e6281 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:59:45 -0500 Subject: [PATCH 012/309] CI: Make sure correct version of python is used for testing. Cygwin is trying to use the highest-available Python version. One of the Python packages has scripts in /usr/bin that should be in the python39- subpackage. --- .ci/test.sh | 4 ++-- .github/workflows/test-cygwin.yml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index 8ff7c5f6483..b0f019d6607 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,6 +2,6 @@ set -e -python3 -c "from PIL import Image" +python3$1 -c "from PIL import Image" -python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE +python3$1 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6215b72f8f5..bbe5eccee72 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,8 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\dash -c "mv .ci/test.sh .ci/test.sh.dos && /bin/tr -d '\r' <.ci/test.sh.dos >test.sh" + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success run: | From 1c25d95d5abf3ae8b4de6b4e55d42c8f8593cf80 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 09:55:24 -0500 Subject: [PATCH 013/309] CI: Get cygwin tests running They segfault in one of the NumPy tests, but they run. --- .github/workflows/test-cygwin.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index bbe5eccee72..d3ab0a36149 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,9 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\dash -c "mv .ci/test.sh .ci/test.sh.dos && /bin/tr -d '\r' <.ci/test.sh.dos >test.sh" + C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos + C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" + C:\tools\cygwin\bin\chmod u+x .ci/test.sh C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success @@ -66,3 +68,10 @@ jobs: file: ./coverage.xml flags: GHA_Cygwin name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} + + - name: After failure + if: failure() + run: | + C:\tools\cygwin\bin\uname -a + C:\tools\cygwin\bin\python3.8 -m pip list + C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow From b9fee08c5911dcdb38d320260589cce3229c5c72 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 10:00:14 -0500 Subject: [PATCH 014/309] TST: Parametrize numpy roundtrip to find failing case Segfaults are annoying to debug. --- .github/workflows/test-cygwin.yml | 2 ++ Tests/test_numpy.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d3ab0a36149..b6a305b7151 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -38,6 +38,7 @@ jobs: run: | C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt + C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked - name: Build Pillow run: | @@ -56,6 +57,7 @@ jobs: C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" C:\tools\cygwin\bin\chmod u+x .ci/test.sh + C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index def7adf3f02..936474fe8d2 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -189,8 +189,9 @@ def test_putdata(): assert len(im.getdata()) == len(arr) -def test_roundtrip_eye(): - for dtype in ( +@pytest.mark.parametrize( + "dtype", + ( bool, numpy.bool8, numpy.int8, @@ -202,9 +203,11 @@ def test_roundtrip_eye(): float, numpy.float32, numpy.float64, - ): - arr = numpy.eye(10, dtype=dtype) - numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) + ), +) +def test_roundtrip_eye(dtype): + arr = numpy.eye(10, dtype=dtype) + numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) def test_zero_size(): From 77c8a07f1c6f5330146a31023de06d6f541e4735 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:28:30 -0500 Subject: [PATCH 015/309] DOC: Rearrange the Cygwin entries on the support matrix. From a suggestion on the PR, make it clearer that Cygwin runs on Windows. Also record the Cygwin versions in the check against Python versions. --- docs/installation.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 3e5123c7a53..d77e2742f2c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,8 +460,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Cygwin | 3.8 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | @@ -482,6 +480,8 @@ These platforms are built and tested for every change. | Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 | | +----------------------------+---------------------+ | | 3.9/MinGW | x86, x86-64 | +| +----------------------------+---------------------+ +| | 3.8/Cygwin | x86-64 | +----------------------------------+----------------------------+---------------------+ @@ -560,6 +560,8 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+---------------------------+------------------+--------------+ | Windows 10 | 3.7 | 7.1.0 |x86-64 | +----------------------------------+---------------------------+------------------+--------------+ +| Windows 10/Cygwin 3.3 | 3.6, 3.7, 3.8, 3.9 | 8.4.0 |x86-64 | ++----------------------------------+---------------------------+------------------+--------------+ | Windows 8.1 Pro | 2.6, 2.7, 3.2, 3.3, 3.4 | 2.4.0 |x86,x86-64 | +----------------------------------+---------------------------+------------------+--------------+ | Windows 8 Pro | 2.6, 2.7, 3.2, 3.3, 3.4a3 | 2.2.0 |x86,x86-64 | From 7ad8fdb67796300ab52e97e432bfe34d17860ad7 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:34:30 -0500 Subject: [PATCH 016/309] CI: Get all the tests running on Cygwin. --- .github/workflows/test-cygwin.yml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b6a305b7151..f43460293b8 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -18,12 +18,13 @@ jobs: packages: > libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel - libtiff-devel libwebp-devel libxcb-devel netpbm - ImageMagick jpeg xorg-server-extra xorg-server-common - xinit python38-setuptools python38-devel python38-tkinter - python38-wheel python38-pip python38-olefile - python38-numpy python38-pytest python38-sphinx - python38-packaging python38-cffi python38-requests + libtiff-devel libwebp-devel libxcb-devel libpng-devel + netpbm ImageMagick jpeg xorg-server-extra + xorg-server-common xinit subversion python38-setuptools + python38-devel python38-tkinter python38-wheel + python38-pip python38-olefile python38-numpy + python38-pytest python38-sphinx python38-packaging + python38-cffi python38-requests install-dir: 'C:\tools\cygwin' - name: Clean up path @@ -40,6 +41,12 @@ jobs: C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked + - name: Download extra test images + run: | + C:\tools\cygwin\bin\dash -c "tr -d '\r' install_extra_test_images.sh" + C:\tools\cygwin\bin\dash -c "mv install_extra_test_images.sh depends/install_extra_test_images.sh" + C:\tools\cygwin\bin\dash -c "cd depends; dash install_extra_test_images.sh" + - name: Build Pillow run: | C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel @@ -57,11 +64,16 @@ jobs: C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" C:\tools\cygwin\bin\chmod u+x .ci/test.sh + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 --ignore=Tests/test_numpy.py + + - name: Test the possibly-segfaulting NumPy tests separately + run: | C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success run: | + C:\tools\cygwin\bin\mv .ci/after_success.sh .ci/after_success.sh.dos + C:\tools\cygwin\bin\dash -c "tr -d '\r' <.ci/after_success.sh.dos >.ci/after_success.sh" C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage From a23131c22e18d51b25398334f8f1e7b07a90d8ac Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:49:45 -0500 Subject: [PATCH 017/309] CI: Stop trying to run the netpbm tests. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f43460293b8..10b83fe880a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -19,7 +19,7 @@ jobs: libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libpng-devel - netpbm ImageMagick jpeg xorg-server-extra + ImageMagick jpeg xorg-server-extra xorg-server-common xinit subversion python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy From 4e7e70fd7982c6cf478b20bb1d11e9140283dccc Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 8 Jan 2022 10:08:18 -0500 Subject: [PATCH 018/309] CI: Install netpbm and dependencies on Cygwin CI. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 10b83fe880a..162522c0aa3 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -19,7 +19,7 @@ jobs: libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libpng-devel - ImageMagick jpeg xorg-server-extra + ImageMagick jpeg netpbm perl xorg-server-extra xorg-server-common xinit subversion python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy From e1c27358ba72a2a9f797e2eec81f90d9676c484c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 14:44:16 +1100 Subject: [PATCH 019/309] Switched to windows-latest --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 162522c0aa3..ca18309be98 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch] jobs: build: - runs-on: windows-2019 + runs-on: windows-latest name: Cygwin Python 3.8 steps: From 629340654c72f67baa8bedd61d9e38141c0222df Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:31:20 +1100 Subject: [PATCH 020/309] Switched to cygwin/cygwin-install-action --- .ci/after_success.sh | 4 +- .github/workflows/test-cygwin.yml | 63 +++++++++++-------------------- 2 files changed, 23 insertions(+), 44 deletions(-) diff --git a/.ci/after_success.sh b/.ci/after_success.sh index 53832c573dd..23a6fcd4d45 100755 --- a/.ci/after_success.sh +++ b/.ci/after_success.sh @@ -3,7 +3,7 @@ # gather the coverage data python3 -m pip install codecov if [[ $MATRIX_DOCKER ]]; then - coverage xml --ignore-errors + python3 -m coverage xml --ignore-errors else - coverage xml + python3 -m coverage xml fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index ca18309be98..022ef480848 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -8,73 +8,52 @@ jobs: name: Cygwin Python 3.8 steps: + - name: Fix line endings + run: | + git config --global core.autocrlf input + - name: Checkout Pillow uses: actions/checkout@v2 - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 + - name: Install Cygwin + uses: cygwin/cygwin-install-action@v1 with: - platform: x64 + platform: x86_64 packages: > - libfreetype-devel libimagequant-devel libjpeg-devel - liblcms2-devel libopenjp2-devel libraqm-devel - libtiff-devel libwebp-devel libxcb-devel libpng-devel - ImageMagick jpeg netpbm perl xorg-server-extra - xorg-server-common xinit subversion python38-setuptools - python38-devel python38-tkinter python38-wheel - python38-pip python38-olefile python38-numpy - python38-pytest python38-sphinx python38-packaging - python38-cffi python38-requests - install-dir: 'C:\tools\cygwin' - - - name: Clean up path - uses: egor-tensin/cleanup-path@v2 - with: - dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + ImageMagick python38-cffi python38-numpy python38-sip + python38-devel python38-tkinter ghostscript libfreetype-devel + libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel + libraqm-devel libtiff-devel libwebp-devel libxcb-devel + libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information - run: C:\tools\cygwin\bin\python3.8 .github/workflows/system-info.py + run: | + bash.exe -c "python3 .github/workflows/system-info.py" - name: Ensure Python dependencies are installed run: | - C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools - C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt - C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked + bash.exe -c "python3 -m pip install pip wheel setuptools" + bash.exe -c "python3 -m pip install -r requirements.txt" - name: Download extra test images run: | - C:\tools\cygwin\bin\dash -c "tr -d '\r' install_extra_test_images.sh" - C:\tools\cygwin\bin\dash -c "mv install_extra_test_images.sh depends/install_extra_test_images.sh" - C:\tools\cygwin\bin\dash -c "cd depends; dash install_extra_test_images.sh" + bash.exe -c "cd depends; dash install_extra_test_images.sh" - name: Build Pillow run: | - C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel + bash.exe -c "python3 setup.py bdist_wheel" - name: Install Pillow run: | - C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - - - name: Check Pillow configuration - run: | - C:\tools\cygwin\bin\python3.8 selftest.py + bash.exe -c "python3 -m pip install dist/*.whl" - name: Test Pillow run: | - C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos - C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" - C:\tools\cygwin\bin\chmod u+x .ci/test.sh - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 --ignore=Tests/test_numpy.py - - - name: Test the possibly-segfaulting NumPy tests separately - run: | - C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip + bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh - name: After success run: | - C:\tools\cygwin\bin\mv .ci/after_success.sh .ci/after_success.sh.dos - C:\tools\cygwin\bin\dash -c "tr -d '\r' <.ci/after_success.sh.dos >.ci/after_success.sh" - C:\tools\cygwin\bin\bash .ci/after_success.sh + bash.exe .ci/after_success.sh - name: Upload coverage uses: codecov/codecov-action@v2 From 728bc7d7789aeba50a6720282bf9b90da5389693 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:05:54 +1100 Subject: [PATCH 021/309] Added matrix to test Python 3.7 and 32-bit --- .github/workflows/test-cygwin.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 022ef480848..77d55682ffe 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -5,7 +5,15 @@ on: [push, pull_request, workflow_dispatch] jobs: build: runs-on: windows-latest - name: Cygwin Python 3.8 + strategy: + fail-fast: false + matrix: + python-minor-version: [7, 8] + architecture: ["x86", "x86_64"] + + timeout-minutes: 30 + + name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} steps: - name: Fix line endings @@ -18,12 +26,15 @@ jobs: - name: Install Cygwin uses: cygwin/cygwin-install-action@v1 with: - platform: x86_64 + platform: ${{ matrix.architecture }} packages: > - ImageMagick python38-cffi python38-numpy python38-sip - python38-devel python38-tkinter ghostscript libfreetype-devel - libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel - libraqm-devel libtiff-devel libwebp-devel libxcb-devel + ImageMagick python3${{ matrix.python-minor-version }}-cffi + python3${{ matrix.python-minor-version }}-devel + python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-sip + python3${{ matrix.python-minor-version }}-tkinter ghostscript + libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel + libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information @@ -60,7 +71,7 @@ jobs: with: file: ./coverage.xml flags: GHA_Cygwin - name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} + name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} - name: After failure if: failure() From 4be3b760f2de24770823f1f9d592618b197c116e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:26:38 +1100 Subject: [PATCH 022/309] Connected Cygwin jobs to mergify --- .github/mergify.yml | 1 + .github/workflows/test-cygwin.yml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.github/mergify.yml b/.github/mergify.yml index 8b289bda671..8dfa07f4ec5 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -8,6 +8,7 @@ pull_request_rules: - status-success=Docker Test Successful - status-success=Windows Test Successful - status-success=MinGW Test Successful + - status-success=Cygwin Test Successful - status-success=continuous-integration/appveyor/pr actions: merge: diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 77d55682ffe..b78e1b231f0 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -79,3 +79,11 @@ jobs: C:\tools\cygwin\bin\uname -a C:\tools\cygwin\bin\python3.8 -m pip list C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow + + success: + needs: build + runs-on: ubuntu-latest + name: Cygwin Test Successful + steps: + - name: Success + run: echo Cygwin Test Successful From 8d2c56ec1f85bec979bad2178309c8c9491bc1c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:09:49 +1100 Subject: [PATCH 023/309] Upload errors on failure --- .github/workflows/test-cygwin.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b78e1b231f0..f4fbfd1f869 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -62,6 +62,18 @@ jobs: run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + - name: Prepare to upload errors + if: failure() + run: | + mkdir -p Tests/errors + + - name: Upload errors + uses: actions/upload-artifact@v2 + if: failure() + with: + name: errors + path: Tests/errors + - name: After success run: | bash.exe .ci/after_success.sh @@ -73,13 +85,6 @@ jobs: flags: GHA_Cygwin name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} - - name: After failure - if: failure() - run: | - C:\tools\cygwin\bin\uname -a - C:\tools\cygwin\bin\python3.8 -m pip list - C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow - success: needs: build runs-on: ubuntu-latest From 05637393a62a79ce2e58e7458667cb8e8845a45a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:14:31 +1100 Subject: [PATCH 024/309] Install Pillow directly --- .ci/build_cygwin.sh | 8 +++++ .ci/install.sh | 55 ++++++++++++++++++------------- .github/workflows/test-cygwin.yml | 19 +++-------- 3 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 .ci/build_cygwin.sh diff --git a/.ci/build_cygwin.sh b/.ci/build_cygwin.sh new file mode 100644 index 00000000000..d621a53364f --- /dev/null +++ b/.ci/build_cygwin.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +python3 -m coverage erase +make clean +CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . +python3 selftest.py diff --git a/.ci/install.sh b/.ci/install.sh index efc57a6414a..ec427ff27d4 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -13,17 +13,20 @@ aptget_update() return 1 fi } -aptget_update || aptget_update retry || aptget_update retry +if [[ $(uname) != CYGWIN* ]]; then + aptget_update || aptget_update retry || aptget_update retry +fi set -e -sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ - ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ - cmake meson imagemagick libharfbuzz-dev libfribidi-dev +if [[ $(uname) != CYGWIN* ]]; then + sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ + ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ + cmake meson imagemagick libharfbuzz-dev libfribidi-dev + python3 -m pip install --upgrade pip +fi -python3 -m pip install --upgrade pip python3 -m pip install --upgrade wheel -PYTHONOPTIMIZE=0 python3 -m pip install cffi python3 -m pip install coverage python3 -m pip install defusedxml python3 -m pip install olefile @@ -32,24 +35,30 @@ python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma python3 -m pip install test-image-results -python3 -m pip install numpy - -# PyQt5 doesn't support PyPy3 -if [[ $GHA_PYTHON_VERSION == 3.* ]]; then - # arm64, ppc64le, s390x CPUs: - # "ERROR: Could not find a version that satisfies the requirement pyqt5" - sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools - python3 -m pip install pyqt5 -fi -# webp -pushd depends && ./install_webp.sh && popd +if [[ $(uname) != CYGWIN* ]]; then + PYTHONOPTIMIZE=0 python3 -m pip install cffi + python3 -m pip install numpy -# libimagequant -pushd depends && ./install_imagequant.sh && popd + # PyQt5 doesn't support PyPy3 + if [[ $GHA_PYTHON_VERSION == 3.* ]]; then + # arm64, ppc64le, s390x CPUs: + # "ERROR: Could not find a version that satisfies the requirement pyqt5" + sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools + python3 -m pip install pyqt5 + fi -# raqm -pushd depends && ./install_raqm.sh && popd + # webp + pushd depends && ./install_webp.sh && popd -# extra test images -pushd depends && ./install_extra_test_images.sh && popd + # libimagequant + pushd depends && ./install_imagequant.sh && popd + + # raqm + pushd depends && ./install_raqm.sh && popd + + # extra test images + pushd depends && ./install_extra_test_images.sh && popd +else + cd depends && ./install_extra_test_images.sh && cd .. +fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f4fbfd1f869..6e2dc5b596d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -41,24 +41,15 @@ jobs: run: | bash.exe -c "python3 .github/workflows/system-info.py" - - name: Ensure Python dependencies are installed + - name: Install dependencies run: | - bash.exe -c "python3 -m pip install pip wheel setuptools" - bash.exe -c "python3 -m pip install -r requirements.txt" + bash.exe .ci/install.sh - - name: Download extra test images + - name: Build run: | - bash.exe -c "cd depends; dash install_extra_test_images.sh" + bash.exe .ci/build_cygwin.sh - - name: Build Pillow - run: | - bash.exe -c "python3 setup.py bdist_wheel" - - - name: Install Pillow - run: | - bash.exe -c "python3 -m pip install dist/*.whl" - - - name: Test Pillow + - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 6fded1ac97a5d2a588a7b60e49869525d04eb6f3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 14:07:51 +1100 Subject: [PATCH 025/309] Install cjpeg and djpeg --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6e2dc5b596d..2dcdecd3009 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -28,7 +28,7 @@ jobs: with: platform: ${{ matrix.architecture }} packages: > - ImageMagick python3${{ matrix.python-minor-version }}-cffi + ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-sip From 131212368d4f57f23b6c3a4520b3ca9545a57282 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 15:55:44 +1100 Subject: [PATCH 026/309] Install netpbm --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 2dcdecd3009..e169a31419e 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -35,7 +35,7 @@ jobs: python3${{ matrix.python-minor-version }}-tkinter ghostscript libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel - libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel + libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information run: | From 2d6dee1dae7c6f4ce639e1042c05152ef5d14c9e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 6 Feb 2022 07:34:15 -0500 Subject: [PATCH 027/309] CI: Try to get Cygwin workflow working. --- .github/workflows/test-cygwin.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index e169a31419e..3b133254206 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -11,7 +11,7 @@ jobs: python-minor-version: [7, 8] architecture: ["x86", "x86_64"] - timeout-minutes: 30 + timeout-minutes: 40 name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} @@ -31,11 +31,18 @@ jobs: ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-cython liblapack-devel gcc-g++ python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-tkinter ghostscript libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel + subversion make + + - name: Add Lapack to PATH + uses: egor-tensin/cleanup-path@v1 + with: + dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' - name: Build system information run: | @@ -45,10 +52,22 @@ jobs: run: | bash.exe .ci/install.sh + - name: Install a different NumPy + run: | + bash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*'" + + - name: Check imports + run: | + bash.exe -c "python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)'" + - name: Build run: | bash.exe .ci/build_cygwin.sh + - name: Rebase dlls + run: | + bash.exe -c '/usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe)' + - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh @@ -56,7 +75,7 @@ jobs: - name: Prepare to upload errors if: failure() run: | - mkdir -p Tests/errors + dash.exe -c "mkdir -p Tests/errors" - name: Upload errors uses: actions/upload-artifact@v2 From 5588f572de4cd386f576fc761e50d373cf1ca239 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 6 Feb 2022 12:03:36 -0500 Subject: [PATCH 028/309] BUG: Only set title in ImageShow.DisplayViewer when title provided --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f8829fc21e0..d76c2609016 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -269,7 +269,7 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") args = ["display"] - if "title" in options: + if "title" in options and options["title"] is not None: args += ["-name", options["title"]] args.append(path) From 4d0e294eb0b5ede4a4c92293aa4ef2794c3b6025 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:00:37 -0500 Subject: [PATCH 029/309] CI: Revert changes to test.sh Some early runs needed to explicitly specify the minor version to avoid calling python3.9 which had none of the dependencies installed. That problem should be fixed. --- .ci/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index b0f019d6607..8ff7c5f6483 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,6 +2,6 @@ set -e -python3$1 -c "from PIL import Image" +python3 -c "from PIL import Image" -python3$1 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE +python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE From ecb64fe2103b9393fd5cf77d3965504e1749ce6d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Feb 2022 09:12:01 +1100 Subject: [PATCH 030/309] Allow 1 mode images to be inverted --- Tests/test_imageops.py | 1 + src/PIL/ImageOps.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 6aa1cf35edf..87fffa7b724 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -63,6 +63,7 @@ def test_sanity(): ImageOps.grayscale(hopper("L")) ImageOps.grayscale(hopper("RGB")) + ImageOps.invert(hopper("1")) ImageOps.invert(hopper("L")) ImageOps.invert(hopper("RGB")) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index b170e9d8cc9..86db777d98c 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -523,7 +523,7 @@ def invert(image): lut = [] for i in range(256): lut.append(255 - i) - return _lut(image, lut) + return image.point(lut) if image.mode == "1" else _lut(image, lut) def mirror(image): From 0c4f11a603a780231c12d5cdc8f8b6cf135b70fd Mon Sep 17 00:00:00 2001 From: David Miguel Susano Pinto Date: Mon, 14 Mar 2022 17:01:03 +0000 Subject: [PATCH 031/309] doc: document "keep" for JPEG save quality option. --- docs/handbook/image-file-formats.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 17808dbc463..d5a898150b8 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -364,10 +364,13 @@ The :py:meth:`~PIL.Image.open` method may set the following The :py:meth:`~PIL.Image.Image.save` method supports the following options: **quality** - The image quality, on a scale from 0 (worst) to 95 (best). The default is - 75. Values above 95 should be avoided; 100 disables portions of the JPEG - compression algorithm, and results in large files with hardly any gain in - image quality. + + The image quality, on a scale from 0 (worst) to 95 (best), or the string + `"keep"`. The default is 75. Values above 95 should be avoided; 100 disables + portions of the JPEG compression algorithm, and results in large files with + hardly any gain in image quality. The value `"keep"` is only valid for JPEG + files and will retain the original image quality level, subsampling, and + quantization tables. **optimize** If present and true, indicates that the encoder should make an extra pass From 7928e944cb73589014bfebaea15042a92bc3c68f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 20 Mar 2022 15:09:01 +1100 Subject: [PATCH 032/309] Keep subsequent L frames without transparency as L --- Tests/images/no_palette.gif | Bin 0 -> 48 bytes Tests/test_file_gif.py | 10 ++++++++- docs/handbook/image-file-formats.rst | 5 +++-- src/PIL/GifImagePlugin.py | 32 +++++++++++++++------------ 4 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 Tests/images/no_palette.gif diff --git a/Tests/images/no_palette.gif b/Tests/images/no_palette.gif new file mode 100644 index 0000000000000000000000000000000000000000..0432ebcb61c30a77eb385b323a0bd6c8d8577ef3 GIT binary patch literal 48 gcmZ?wbh9u|WMp7u00JEl0cLZsFfg*PU Date: Sun, 20 Mar 2022 16:28:31 +1100 Subject: [PATCH 033/309] Added setting to convert first GIF frame to RGB --- Tests/test_file_gif.py | 12 ++++++++++++ docs/handbook/image-file-formats.rst | 7 +++++++ src/PIL/GifImagePlugin.py | 26 ++++++++++++++++++++------ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index ba277a776fa..8d804628076 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -78,6 +78,18 @@ def test_l_mode_subsequent_frames(): assert im.load()[0, 0] == (0, 255) +def test_strategy(): + with Image.open(TEST_GIF) as im: + expected = im.convert("RGB") + + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.ALWAYS + with Image.open(TEST_GIF) as im: + assert im.mode == "RGB" + assert_image_equal(im, expected) + + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.AFTER_FIRST + + def test_optimize(): def test_grayscale(optimize): im = Image.new("L", (1, 1), 0) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 5db1b8c7d0a..85004d8e3f3 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -110,6 +110,13 @@ images. Seeking to later frames in a ``P`` image will change the image to ``RGB`` (or ``RGBA`` if the first frame had transparency). ``L`` images will stay in ``L`` mode (or change to ``LA`` if the first frame had transparency). +If you would prefer the first ``P`` image frame to be ``RGB``, so that ``P`` +frames are always converted to ``RGB`` or ``RGBA`` mode, there is a setting +available:: + + from PIL import GifImagePlugin + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.ALWAYS + The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 764eda6ee00..7f9c1540033 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -28,12 +28,21 @@ import math import os import subprocess +from enum import IntEnum from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence from ._binary import i16le as i16 from ._binary import o8 from ._binary import o16le as o16 + +class ModeStrategy(IntEnum): + AFTER_FIRST = 0 + ALWAYS = 1 + + +PALETTE_TO_RGB = ModeStrategy.AFTER_FIRST + # -------------------------------------------------------------------- # Identify/read GIF files @@ -355,18 +364,22 @@ def _rgb(color): del self.info[k] if frame == 0: - self.mode = "P" if frame_palette else "L" + if frame_palette: + self.mode = "RGB" if PALETTE_TO_RGB == ModeStrategy.ALWAYS else "P" + else: + self.mode = "L" - if self.mode == "P" and not palette: + if not palette and self.global_palette: from copy import copy palette = copy(self.global_palette) self.palette = palette else: - self._frame_palette = frame_palette self._frame_transparency = frame_transparency + self._frame_palette = frame_palette def load_prepare(self): + self.mode = "P" if self._frame_palette else "L" if self.__frame == 0: if "transparency" in self.info: self.im = Image.core.fill( @@ -375,18 +388,19 @@ def load_prepare(self): else: self._prev_im = self.im if self._frame_palette: - self.mode = "P" self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) self.im.putpalette(*self._frame_palette.getdata()) - self._frame_palette = None else: - self.mode = "L" self.im = None + self._frame_palette = None super().load_prepare() def load_end(self): if self.__frame == 0: + if self.mode == "P" and PALETTE_TO_RGB == ModeStrategy.ALWAYS: + self.mode = "RGB" + self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) return if self.mode == "P": if self._frame_transparency is not None: From ce8c682748339928d8de986147fe37149dfcbb8a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 22 Mar 2022 20:28:49 +1100 Subject: [PATCH 034/309] Added setting to convert to RGB only at a different palette --- Tests/test_file_gif.py | 65 ++++++++++--- docs/handbook/image-file-formats.rst | 16 +++- src/PIL/GifImagePlugin.py | 131 +++++++++++++++------------ src/decode.c | 4 +- src/libImaging/Gif.h | 3 + src/libImaging/GifDecode.c | 36 +++++--- 6 files changed, 164 insertions(+), 91 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 8d804628076..df6142ec7f4 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -79,15 +79,37 @@ def test_l_mode_subsequent_frames(): def test_strategy(): - with Image.open(TEST_GIF) as im: - expected = im.convert("RGB") + with Image.open("Tests/images/chi.gif") as im: + expected_zero = im.convert("RGB") - GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.ALWAYS - with Image.open(TEST_GIF) as im: - assert im.mode == "RGB" - assert_image_equal(im, expected) + im.seek(1) + expected_one = im.convert("RGB") + + try: + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.ALWAYS + with Image.open("Tests/images/chi.gif") as im: + assert im.mode == "RGB" + assert_image_equal(im, expected_zero) + + GifImagePlugin.PALETTE_TO_RGB = ( + GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY + ) + # Stay in P mode with only a global palette + with Image.open("Tests/images/chi.gif") as im: + assert im.mode == "P" - GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.AFTER_FIRST + im.seek(1) + assert im.mode == "P" + assert_image_equal(im.convert("RGB"), expected_one) + + # Change to RGB mode when a frame has an individual palette + with Image.open("Tests/images/iss634.gif") as im: + assert im.mode == "P" + + im.seek(1) + assert im.mode == "RGB" + finally: + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.AFTER_FIRST def test_optimize(): @@ -414,12 +436,29 @@ def test_dispose_background_transparency(): assert px[35, 30][3] == 0 -def test_transparent_dispose(): - expected_colors = [ - (2, 1, 2), - ((0, 255, 24, 255), (0, 0, 255, 255), (0, 255, 24, 255)), - ((0, 0, 0, 0), (0, 0, 255, 255), (0, 0, 0, 0)), - ] +@pytest.mark.parametrize( + "mode_strategy, expected_colors", + ( + ( + GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY, + ( + (2, 1, 2), + (0, 1, 0), + (2, 1, 2), + ), + ), + ( + GifImagePlugin.ModeStrategy.AFTER_FIRST, + ( + (2, 1, 2), + ((0, 255, 24, 255), (0, 0, 255, 255), (0, 255, 24, 255)), + ((0, 0, 0, 0), (0, 0, 255, 255), (0, 0, 0, 0)), + ), + ), + ), +) +def test_transparent_dispose(mode_strategy, expected_colors): + GifImagePlugin.PALETTE_TO_RGB = mode_strategy with Image.open("Tests/images/transparent_dispose.gif") as img: for frame in range(3): img.seek(frame) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 85004d8e3f3..6dffe834e92 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -110,13 +110,25 @@ images. Seeking to later frames in a ``P`` image will change the image to ``RGB`` (or ``RGBA`` if the first frame had transparency). ``L`` images will stay in ``L`` mode (or change to ``LA`` if the first frame had transparency). -If you would prefer the first ``P`` image frame to be ``RGB``, so that ``P`` -frames are always converted to ``RGB`` or ``RGBA`` mode, there is a setting +``P`` mode images are changed to ``RGB`` because each frame of a GIF may +introduce up to 256 colors. Because ``P`` can only have up to 256 colors, the +image is converted to handle all of the colors. + +If you would prefer the first ``P`` image frame to be ``RGB`` as well, so that +every ``P`` frame is converted to ``RGB`` or ``RGBA`` mode, there is a setting available:: from PIL import GifImagePlugin GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.ALWAYS +GIF frames do not always contain individual palettes however. If there is only +a global palette, then all of the colors can fit within ``P`` mode. If you would +prefer the frames to be kept as ``P`` in that case, there is also a setting +available:: + + from PIL import GifImagePlugin + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY + The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 7f9c1540033..d22d2896640 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -39,6 +39,7 @@ class ModeStrategy(IntEnum): AFTER_FIRST = 0 ALWAYS = 1 + DIFFERENT_PALETTE_ONLY = 2 PALETTE_TO_RGB = ModeStrategy.AFTER_FIRST @@ -152,7 +153,6 @@ def _seek(self, frame, update_image=True): # rewind self.__offset = 0 self.dispose = None - self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1 self.__frame = -1 self.__fp.seek(self.__rewind) self.disposal_method = 0 @@ -180,33 +180,12 @@ def _seek(self, frame, update_image=True): self.tile = [] - if update_image: - if self.__frame == 1: - self.pyaccess = None - if self.mode == "P": - if "transparency" in self.info: - self.im.putpalettealpha(self.info["transparency"], 0) - self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) - self.mode = "RGBA" - del self.info["transparency"] - else: - self.mode = "RGB" - self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) - elif "transparency" in self.info: - self.im = self.im.convert_transparent( - "LA", self.info["transparency"] - ) - self.mode = "LA" - del self.info["transparency"] - - if self.dispose: - self.im.paste(self.dispose, self.dispose_extent) - palette = None info = {} frame_transparency = None interlace = None + frame_dispose_extent = None while True: if not s: @@ -273,7 +252,7 @@ def _seek(self, frame, update_image=True): x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6) if x1 > self.size[0] or y1 > self.size[1]: self._size = max(x1, self.size[0]), max(y1, self.size[1]) - self.dispose_extent = x0, y0, x1, y1 + frame_dispose_extent = x0, y0, x1, y1 flags = s[8] interlace = (flags & 64) != 0 @@ -298,15 +277,48 @@ def _seek(self, frame, update_image=True): if not update_image: return - frame_palette = palette or self.global_palette + if self.dispose: + self.im.paste(self.dispose, self.dispose_extent) + + self._frame_palette = palette or self.global_palette + if frame == 0: + if self._frame_palette: + self.mode = "RGB" if PALETTE_TO_RGB == ModeStrategy.ALWAYS else "P" + else: + self.mode = "L" + + if not palette and self.global_palette: + from copy import copy + + palette = copy(self.global_palette) + self.palette = palette + else: + self._frame_transparency = frame_transparency + if self.mode == "P": + if PALETTE_TO_RGB != ModeStrategy.DIFFERENT_PALETTE_ONLY or palette: + self.pyaccess = None + if "transparency" in self.info: + self.im.putpalettealpha(self.info["transparency"], 0) + self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) + self.mode = "RGBA" + del self.info["transparency"] + else: + self.mode = "RGB" + self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) + elif self.mode == "L" and "transparency" in self.info: + self.pyaccess = None + self.im = self.im.convert_transparent("LA", self.info["transparency"]) + self.mode = "LA" + del self.info["transparency"] def _rgb(color): - if frame_palette: - color = tuple(frame_palette.palette[color * 3 : color * 3 + 3]) + if self._frame_palette: + color = tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]) else: color = (color, color, color) return color + self.dispose_extent = frame_dispose_extent try: if self.disposal_method < 2: # do not dispose or none specified @@ -321,13 +333,17 @@ def _rgb(color): Image._decompression_bomb_check(dispose_size) # by convention, attempt to use transparency first + dispose_mode = "P" color = self.info.get("transparency", frame_transparency) if color is not None: - dispose_mode = "RGBA" - color = _rgb(color) + (0,) + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(color) + (0,) else: - dispose_mode = "RGB" - color = _rgb(self.info.get("background", 0)) + color = self.info.get("background", 0) + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGB" + color = _rgb(color) self.dispose = Image.core.fill(dispose_mode, dispose_size, color) else: # replace with previous contents @@ -339,21 +355,28 @@ def _rgb(color): dispose_size = (x1 - x0, y1 - y0) Image._decompression_bomb_check(dispose_size) - self.dispose = Image.core.fill( - "RGBA", dispose_size, _rgb(frame_transparency) + (0,) - ) + dispose_mode = "P" + color = frame_transparency + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(frame_transparency) + (0,) + self.dispose = Image.core.fill(dispose_mode, dispose_size, color) except AttributeError: pass if interlace is not None: - if frame == 0 and frame_transparency is not None: - self.info["transparency"] = frame_transparency + transparency = -1 + if frame_transparency is not None: + if frame == 0: + self.info["transparency"] = frame_transparency + elif self.mode not in ("RGB", "RGBA", "LA"): + transparency = frame_transparency self.tile = [ ( "gif", (x0, y0, x1, y1), self.__offset, - (bits, interlace), + (bits, interlace, transparency), ) ] @@ -363,35 +386,22 @@ def _rgb(color): elif k in self.info: del self.info[k] - if frame == 0: - if frame_palette: - self.mode = "RGB" if PALETTE_TO_RGB == ModeStrategy.ALWAYS else "P" - else: - self.mode = "L" - - if not palette and self.global_palette: - from copy import copy - - palette = copy(self.global_palette) - self.palette = palette - else: - self._frame_transparency = frame_transparency - self._frame_palette = frame_palette - def load_prepare(self): - self.mode = "P" if self._frame_palette else "L" + temp_mode = "P" if self._frame_palette else "L" + self._prev_im = None if self.__frame == 0: if "transparency" in self.info: self.im = Image.core.fill( - self.mode, self.size, self.info["transparency"] + temp_mode, self.size, self.info["transparency"] ) - else: + elif self.mode in ("RGB", "RGBA", "LA"): self._prev_im = self.im if self._frame_palette: self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) self.im.putpalette(*self._frame_palette.getdata()) else: self.im = None + self.mode = temp_mode self._frame_palette = None super().load_prepare() @@ -402,17 +412,18 @@ def load_end(self): self.mode = "RGB" self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) return - if self.mode == "P": + if self.mode == "P" and self._prev_im: if self._frame_transparency is not None: self.im.putpalettealpha(self._frame_transparency, 0) frame_im = self.im.convert("RGBA") else: frame_im = self.im.convert("RGB") + elif self.mode == "L" and self._frame_transparency is not None: + frame_im = self.im.convert_transparent("LA", self._frame_transparency) else: - if self._frame_transparency is not None: - frame_im = self.im.convert_transparent("LA", self._frame_transparency) - else: - frame_im = self.im + if not self._prev_im: + return + frame_im = self.im frame_im = self._crop(frame_im, self.dispose_extent) self.im = self._prev_im diff --git a/src/decode.c b/src/decode.c index e236264cdb4..cb018a4e706 100644 --- a/src/decode.c +++ b/src/decode.c @@ -433,7 +433,8 @@ PyImaging_GifDecoderNew(PyObject *self, PyObject *args) { char *mode; int bits = 8; int interlace = 0; - if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace)) { + int transparency = -1; + if (!PyArg_ParseTuple(args, "s|iii", &mode, &bits, &interlace, &transparency)) { return NULL; } @@ -451,6 +452,7 @@ PyImaging_GifDecoderNew(PyObject *self, PyObject *args) { ((GIFDECODERSTATE *)decoder->state.context)->bits = bits; ((GIFDECODERSTATE *)decoder->state.context)->interlace = interlace; + ((GIFDECODERSTATE *)decoder->state.context)->transparency = transparency; return (PyObject *)decoder; } diff --git a/src/libImaging/Gif.h b/src/libImaging/Gif.h index 4029bbfe5f1..5d7e2bdaa96 100644 --- a/src/libImaging/Gif.h +++ b/src/libImaging/Gif.h @@ -30,6 +30,9 @@ typedef struct { */ int interlace; + /* The transparent palette index, or -1 for no transparency */ + int transparency; + /* PRIVATE CONTEXT (set by decoder) */ /* Interlace parameters */ diff --git a/src/libImaging/GifDecode.c b/src/libImaging/GifDecode.c index 30478e24aac..0be4771cdeb 100644 --- a/src/libImaging/GifDecode.c +++ b/src/libImaging/GifDecode.c @@ -248,27 +248,33 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t /* To squeeze some extra pixels out of this loop, we test for some common cases and handle them separately. */ - if (i == 1) { - if (state->x < state->xsize - 1) { - /* Single pixel, not at the end of the line. */ - *out++ = p[0]; - state->x++; + /* This cannot be used if there is transparency */ + if (context->transparency == -1) { + if (i == 1) { + if (state->x < state->xsize - 1) { + /* Single pixel, not at the end of the line. */ + *out++ = p[0]; + state->x++; + continue; + } + } else if (state->x + i <= state->xsize) { + /* This string fits into current line. */ + memcpy(out, p, i); + out += i; + state->x += i; + if (state->x == state->xsize) { + NEWLINE(state, context); + } continue; } - } else if (state->x + i <= state->xsize) { - /* This string fits into current line. */ - memcpy(out, p, i); - out += i; - state->x += i; - if (state->x == state->xsize) { - NEWLINE(state, context); - } - continue; } /* No shortcut, copy pixel by pixel */ for (c = 0; c < i; c++) { - *out++ = p[c]; + if (p[c] != context->transparency) { + *out = p[c]; + } + out++; if (++state->x >= state->xsize) { NEWLINE(state, context); } From c5efe60c370c3e89db17f1a40747f1d73a40e27b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 22 Mar 2022 22:07:37 +1100 Subject: [PATCH 035/309] Reverted converting L with transparency to LA after first frame --- Tests/images/no_palette_with_transparency.gif | Bin 64 -> 1604 bytes Tests/test_file_gif.py | 43 ++++++++---------- docs/handbook/image-file-formats.rst | 3 +- src/PIL/GifImagePlugin.py | 31 ++++++------- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/Tests/images/no_palette_with_transparency.gif b/Tests/images/no_palette_with_transparency.gif index 3cd1c0c48eb5bcd8c572b1ad034955190d801e6a..031bdcfce10dc119f4479085810427cccd925671 100644 GIT binary patch literal 1604 zcmeIx!(tr>06@_fyRmKCP8!>`ZQBjnxQ%Vwwr$(CF_ZOt!t`$M`iM!0a&Q^|1Aqac zfUkgnfPjI4K|nx2K|#U5z`()5As`?iAt9llprE0lVPIfjVPWCm;J$tP1`iL9fPjFA zh=_!Qgp7=gf`WpIii(DYhK`Pofq{XEiHU`Ug^i7kgM)*Mi;IVchmVg>KtMo9NJvCP zL`+OfLPA1HN=imXMovynK|w)DNl8UTMNLgjLqkJLOG`&bM^8`Bz`(%B$jHRR#LUdh z!otGJ%F4#Z#?H>p!NI}F$;rjV#m&vl!^6YN%ge{d$Is6%ARr(pDER&RcOfAmVPRnr z5fM>QQ86(wadB}82?~1%F4>>>gw9s+WPwX#>U3x=H}Mc z*7o-H&d$#6?(W{+-v0jn!NI}d;o;HI(ed%|$;rv->FL?o+4=eT#l^+t<>l4Y)%ErD z&CSj2?d{#&-TnRj!^6Yl+9Rw+xz?b$H&L#=O^JaC=dYf3849s m@MQu32@U{)0!2Xp{AYb}`HST*mj5l5e`$O|y`dujxc>pnng=)l literal 64 ncmZ?wbhEHbWMp7u00PCIEI|4{gARxT7UN)HU}RyzEny7+38e<8 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index df6142ec7f4..54cc4524990 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -59,23 +59,15 @@ def test_invalid_file(): GifImagePlugin.GifImageFile(invalid_file) -def test_l_mode_subsequent_frames(): - with Image.open("Tests/images/no_palette.gif") as im: - assert im.mode == "L" - assert im.load()[0, 0] == 0 - - im.seek(1) - assert im.mode == "L" - assert im.load()[0, 0] == 0 - +def test_l_mode_transparency(): with Image.open("Tests/images/no_palette_with_transparency.gif") as im: assert im.mode == "L" - assert im.load()[0, 0] == 0 + assert im.load()[0, 0] == 128 assert im.info["transparency"] == 255 im.seek(1) - assert im.mode == "LA" - assert im.load()[0, 0] == (0, 255) + assert im.mode == "L" + assert im.load()[0, 0] == 128 def test_strategy(): @@ -440,31 +432,34 @@ def test_dispose_background_transparency(): "mode_strategy, expected_colors", ( ( - GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY, + GifImagePlugin.ModeStrategy.AFTER_FIRST, ( (2, 1, 2), - (0, 1, 0), - (2, 1, 2), + ((0, 255, 24, 255), (0, 0, 255, 255), (0, 255, 24, 255)), + ((0, 0, 0, 0), (0, 0, 255, 255), (0, 0, 0, 0)), ), ), ( - GifImagePlugin.ModeStrategy.AFTER_FIRST, + GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY, ( (2, 1, 2), - ((0, 255, 24, 255), (0, 0, 255, 255), (0, 255, 24, 255)), - ((0, 0, 0, 0), (0, 0, 255, 255), (0, 0, 0, 0)), + (0, 1, 0), + (2, 1, 2), ), ), ), ) def test_transparent_dispose(mode_strategy, expected_colors): GifImagePlugin.PALETTE_TO_RGB = mode_strategy - with Image.open("Tests/images/transparent_dispose.gif") as img: - for frame in range(3): - img.seek(frame) - for x in range(3): - color = img.getpixel((x, 0)) - assert color == expected_colors[frame][x] + try: + with Image.open("Tests/images/transparent_dispose.gif") as img: + for frame in range(3): + img.seek(frame) + for x in range(3): + color = img.getpixel((x, 0)) + assert color == expected_colors[frame][x] + finally: + GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.AFTER_FIRST def test_dispose_previous(): diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 6dffe834e92..6309562501e 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -107,8 +107,7 @@ are used or GIF89a is already in use. GIF files are initially read as grayscale (``L``) or palette mode (``P``) images. Seeking to later frames in a ``P`` image will change the image to -``RGB`` (or ``RGBA`` if the first frame had transparency). ``L`` images will -stay in ``L`` mode (or change to ``LA`` if the first frame had transparency). +``RGB`` (or ``RGBA`` if the first frame had transparency). ``P`` mode images are changed to ``RGB`` because each frame of a GIF may introduce up to 256 colors. Because ``P`` can only have up to 256 colors, the diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index d22d2896640..1d59edf091a 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -71,6 +71,12 @@ def data(self): return self.fp.read(s[0]) return None + def _is_palette_needed(self, p): + for i in range(0, len(p), 3): + if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): + return True + return False + def _open(self): # Screen @@ -89,11 +95,9 @@ def _open(self): self.info["background"] = s[11] # check if palette contains colour indices p = self.fp.read(3 << bits) - for i in range(0, len(p), 3): - if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): - p = ImagePalette.raw("RGB", p) - self.global_palette = self.palette = p - break + if self._is_palette_needed(p): + p = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = p self.__fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() @@ -259,7 +263,9 @@ def _seek(self, frame, update_image=True): if flags & 128: bits = (flags & 7) + 1 - palette = ImagePalette.raw("RGB", self.fp.read(3 << bits)) + p = self.fp.read(3 << bits) + if self._is_palette_needed(p): + palette = ImagePalette.raw("RGB", p) # image data bits = self.fp.read(1)[0] @@ -305,11 +311,6 @@ def _seek(self, frame, update_image=True): else: self.mode = "RGB" self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) - elif self.mode == "L" and "transparency" in self.info: - self.pyaccess = None - self.im = self.im.convert_transparent("LA", self.info["transparency"]) - self.mode = "LA" - del self.info["transparency"] def _rgb(color): if self._frame_palette: @@ -369,7 +370,7 @@ def _rgb(color): if frame_transparency is not None: if frame == 0: self.info["transparency"] = frame_transparency - elif self.mode not in ("RGB", "RGBA", "LA"): + elif self.mode not in ("RGB", "RGBA"): transparency = frame_transparency self.tile = [ ( @@ -394,7 +395,7 @@ def load_prepare(self): self.im = Image.core.fill( temp_mode, self.size, self.info["transparency"] ) - elif self.mode in ("RGB", "RGBA", "LA"): + elif self.mode in ("RGB", "RGBA"): self._prev_im = self.im if self._frame_palette: self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) @@ -418,8 +419,6 @@ def load_end(self): frame_im = self.im.convert("RGBA") else: frame_im = self.im.convert("RGB") - elif self.mode == "L" and self._frame_transparency is not None: - frame_im = self.im.convert_transparent("LA", self._frame_transparency) else: if not self._prev_im: return @@ -428,7 +427,7 @@ def load_end(self): self.im = self._prev_im self.mode = self.im.mode - if frame_im.mode in ("LA", "RGBA"): + if frame_im.mode == "RGBA": self.im.paste(frame_im, self.dispose_extent, frame_im) else: self.im.paste(frame_im, self.dispose_extent) From 1acc32d1d91c4fe3c49942f315464a5804fd4ce4 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 22 Mar 2022 22:34:04 +1100 Subject: [PATCH 036/309] Updated syntax --- docs/handbook/image-file-formats.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index d5a898150b8..2f7eaa6ed4c 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -364,11 +364,10 @@ The :py:meth:`~PIL.Image.open` method may set the following The :py:meth:`~PIL.Image.Image.save` method supports the following options: **quality** - The image quality, on a scale from 0 (worst) to 95 (best), or the string - `"keep"`. The default is 75. Values above 95 should be avoided; 100 disables + ``keep``. The default is 75. Values above 95 should be avoided; 100 disables portions of the JPEG compression algorithm, and results in large files with - hardly any gain in image quality. The value `"keep"` is only valid for JPEG + hardly any gain in image quality. The value ``keep`` is only valid for JPEG files and will retain the original image quality level, subsampling, and quantization tables. From 55852d53925214809c71a4bc99dfac8f5e9356db Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 26 Mar 2022 13:44:27 +1100 Subject: [PATCH 037/309] Use consistent term --- docs/handbook/image-file-formats.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 2f7eaa6ed4c..849c7ddf7fb 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -369,7 +369,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: portions of the JPEG compression algorithm, and results in large files with hardly any gain in image quality. The value ``keep`` is only valid for JPEG files and will retain the original image quality level, subsampling, and - quantization tables. + qtables. **optimize** If present and true, indicates that the encoder should make an extra pass From 33022eef1677bcc24dc1a03628ca10e9af9302a0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 28 Mar 2022 22:02:26 +1100 Subject: [PATCH 038/309] Added versionadded --- src/PIL/GifImagePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 1d59edf091a..1be47c7b745 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -37,11 +37,14 @@ class ModeStrategy(IntEnum): + """.. versionadded:: 9.1.0""" + AFTER_FIRST = 0 ALWAYS = 1 DIFFERENT_PALETTE_ONLY = 2 +#: .. versionadded:: 9.1.0 PALETTE_TO_RGB = ModeStrategy.AFTER_FIRST # -------------------------------------------------------------------- From abd9eda49986ac6508f4ca431a62a49349aff12b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 26 Mar 2022 20:17:44 +0200 Subject: [PATCH 039/309] Replace Read the Docs Sphinx Theme with Furo --- .github/workflows/test.yml | 2 +- docs/conf.py | 8 +- docs/resources/css/dark.css | 1990 --------------------------------- docs/resources/css/light.css | 8 - docs/resources/css/styles.css | 8 - docs/resources/js/script.js | 58 - setup.cfg | 2 +- 7 files changed, 3 insertions(+), 2073 deletions(-) delete mode 100644 docs/resources/css/light.css delete mode 100644 docs/resources/css/styles.css delete mode 100644 docs/resources/js/script.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fef442cfd4f..7b13addfdfd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -93,7 +93,7 @@ jobs: - name: Docs if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.10 run: | - python3 -m pip install sphinx-copybutton sphinx-issues sphinx-removed-in sphinx-rtd-theme sphinxext-opengraph + python3 -m pip install furo sphinx-copybutton sphinx-issues sphinx-removed-in sphinxext-opengraph make doccheck - name: After success diff --git a/docs/conf.py b/docs/conf.py index 7bbe8c4c96f..03aa74a1490 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,8 +16,6 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) -import sphinx_rtd_theme - import PIL # -- General configuration ------------------------------------------------ @@ -126,8 +124,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +html_theme = "furo" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -311,10 +308,7 @@ def setup(app): - app.add_js_file("js/script.js") - app.add_css_file("css/styles.css") app.add_css_file("css/dark.css") - app.add_css_file("css/light.css") # GitHub repo for sphinx-issues diff --git a/docs/resources/css/dark.css b/docs/resources/css/dark.css index 8866c07eabd..11f8ecf24a2 100644 --- a/docs/resources/css/dark.css +++ b/docs/resources/css/dark.css @@ -1,1994 +1,4 @@ @media (prefers-color-scheme: dark) { - html { - background-color: #181a1b !important; - } - - html, body, input, textarea, select, button { - background-color: #181a1b; - } - - html, body, input, textarea, select, button { - border-color: #736b5e; - color: #e8e6e3; - } - - a { - color: #3391ff; - } - - table { - border-color: #545b5e; - } - - ::placeholder { - color: #b2aba1; - } - - input:-webkit-autofill, - textarea:-webkit-autofill, - select:-webkit-autofill { - background-color: #555b00 !important; - color: #e8e6e3 !important; - } - - ::selection { - background-color: #004daa !important; - color: #e8e6e3 !important; - } - - ::-moz-selection { - background-color: #004daa !important; - color: #e8e6e3 !important; - } - - /* Invert Style */ - .jfk-bubble.gtx-bubble, embed[type="application/pdf"] { - filter: invert(100%) hue-rotate(180deg) contrast(90%) !important; - } - - /* Override Style */ - .vimvixen-hint { - background-color: #7b5300 !important; - border-color: #d8b013 !important; - color: #f3e8c8 !important; - } - - ::placeholder { - opacity: 0.5 !important; - } - - /* Variables Style */ - :root { - --darkreader-neutral-background: #181a1b; - --darkreader-neutral-text: #e8e6e3; - --darkreader-selection-background: #004daa; - --darkreader-selection-text: #e8e6e3; - } - - /* Modified CSS */ - a:hover, - a:active { - outline-color: initial; - } - - abbr[title] { - border-bottom-color: initial; - } - - ins { - background-image: initial; - background-color: rgb(112, 112, 0); - color: rgb(232, 230, 227); - text-decoration-color: initial; - } - - mark { - background-image: initial; - background-color: rgb(204, 204, 0); - color: rgb(232, 230, 227); - } - - ul, - ol, - dl { - list-style-image: none; - } - - li { - list-style-image: initial; - } - - img { - border-color: initial; - } - - fieldset { - border-color: initial; - } - - legend { - border-color: initial; - } - - .chromeframe { - background-image: initial; - background-color: rgb(53, 57, 59); - color: rgb(232, 230, 227); - } - - .ir { - border-color: initial; - background-color: transparent; - } - - .visuallyhidden { - border-color: initial; - } - - .fa-border { - border-color: rgb(53, 57, 59); - } - - .fa-inverse { - color: rgb(232, 230, 227); - } - - .sr-only { - border-color: initial; - } - - .fa::before, - .wy-menu-vertical li span.toctree-expand::before, - .wy-menu-vertical li.on a span.toctree-expand::before, - .wy-menu-vertical li.current > a span.toctree-expand::before, - .rst-content .admonition-title::before, - .rst-content h1 .headerlink::before, - .rst-content h2 .headerlink::before, - .rst-content h3 .headerlink::before, - .rst-content h4 .headerlink::before, - .rst-content h5 .headerlink::before, - .rst-content h6 .headerlink::before, - .rst-content dl dt .headerlink::before, - .rst-content p.caption .headerlink::before, - .rst-content table > caption .headerlink::before, - .rst-content .code-block-caption .headerlink::before, - .rst-content tt.download span:first-child::before, - .rst-content code.download span:first-child::before, - .icon::before, - .wy-dropdown .caret::before, - .wy-inline-validate.wy-inline-validate-success .wy-input-context::before, - .wy-inline-validate.wy-inline-validate-danger .wy-input-context::before, - .wy-inline-validate.wy-inline-validate-warning .wy-input-context::before, - .wy-inline-validate.wy-inline-validate-info .wy-input-context::before { - text-decoration-color: inherit; - } - - a .fa, - a .wy-menu-vertical li span.toctree-expand, - .wy-menu-vertical li a span.toctree-expand, - .wy-menu-vertical li.on a span.toctree-expand, - .wy-menu-vertical li.current > a span.toctree-expand, - a .rst-content .admonition-title, - .rst-content a .admonition-title, - a .rst-content h1 .headerlink, - .rst-content h1 a .headerlink, - a .rst-content h2 .headerlink, - .rst-content h2 a .headerlink, - a .rst-content h3 .headerlink, - .rst-content h3 a .headerlink, - a .rst-content h4 .headerlink, - .rst-content h4 a .headerlink, - a .rst-content h5 .headerlink, - .rst-content h5 a .headerlink, - a .rst-content h6 .headerlink, - .rst-content h6 a .headerlink, - a .rst-content dl dt .headerlink, - .rst-content dl dt a .headerlink, - a .rst-content p.caption .headerlink, - .rst-content p.caption a .headerlink, - a .rst-content table > caption .headerlink, - .rst-content table > caption a .headerlink, - a .rst-content .code-block-caption .headerlink, - .rst-content .code-block-caption a .headerlink, - a .rst-content tt.download span:first-child, - .rst-content tt.download a span:first-child, - a .rst-content code.download span:first-child, - .rst-content code.download a span:first-child, - a .icon { - text-decoration-color: inherit; - } - - .wy-alert, - .rst-content .note, - .rst-content .attention, - .rst-content .caution, - .rst-content .danger, - .rst-content .error, - .rst-content .hint, - .rst-content .important, - .rst-content .tip, - .rst-content .warning, - .rst-content .seealso, - .rst-content .admonition-todo, - .rst-content .admonition { - background-image: initial; - background-color: rgb(32, 35, 36); - } - - .wy-alert-title, - .rst-content .admonition-title { - color: rgb(232, 230, 227); - background-image: initial; - background-color: rgb(29, 91, 131); - } - - .wy-alert.wy-alert-danger, - .rst-content .wy-alert-danger.note, - .rst-content .wy-alert-danger.attention, - .rst-content .wy-alert-danger.caution, - .rst-content .danger, - .rst-content .error, - .rst-content .wy-alert-danger.hint, - .rst-content .wy-alert-danger.important, - .rst-content .wy-alert-danger.tip, - .rst-content .wy-alert-danger.warning, - .rst-content .wy-alert-danger.seealso, - .rst-content .wy-alert-danger.admonition-todo, - .rst-content .wy-alert-danger.admonition { - background-image: initial; - background-color: rgb(52, 12, 8); - } - - .wy-alert.wy-alert-danger .wy-alert-title, - .rst-content .wy-alert-danger.note .wy-alert-title, - .rst-content .wy-alert-danger.attention .wy-alert-title, - .rst-content .wy-alert-danger.caution .wy-alert-title, - .rst-content .danger .wy-alert-title, - .rst-content .error .wy-alert-title, - .rst-content .wy-alert-danger.hint .wy-alert-title, - .rst-content .wy-alert-danger.important .wy-alert-title, - .rst-content .wy-alert-danger.tip .wy-alert-title, - .rst-content .wy-alert-danger.warning .wy-alert-title, - .rst-content .wy-alert-danger.seealso .wy-alert-title, - .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, - .rst-content .wy-alert-danger.admonition .wy-alert-title, - .wy-alert.wy-alert-danger .rst-content .admonition-title, - .rst-content .wy-alert.wy-alert-danger .admonition-title, - .rst-content .wy-alert-danger.note .admonition-title, - .rst-content .wy-alert-danger.attention .admonition-title, - .rst-content .wy-alert-danger.caution .admonition-title, - .rst-content .danger .admonition-title, - .rst-content .error .admonition-title, - .rst-content .wy-alert-danger.hint .admonition-title, - .rst-content .wy-alert-danger.important .admonition-title, - .rst-content .wy-alert-danger.tip .admonition-title, - .rst-content .wy-alert-danger.warning .admonition-title, - .rst-content .wy-alert-danger.seealso .admonition-title, - .rst-content .wy-alert-danger.admonition-todo .admonition-title, - .rst-content .wy-alert-danger.admonition .admonition-title { - background-image: initial; - background-color: rgb(108, 22, 13); - } - - .wy-alert.wy-alert-warning, - .rst-content .wy-alert-warning.note, - .rst-content .attention, - .rst-content .caution, - .rst-content .wy-alert-warning.danger, - .rst-content .wy-alert-warning.error, - .rst-content .wy-alert-warning.hint, - .rst-content .wy-alert-warning.important, - .rst-content .wy-alert-warning.tip, - .rst-content .warning, - .rst-content .wy-alert-warning.seealso, - .rst-content .admonition-todo, - .rst-content .wy-alert-warning.admonition { - background-image: initial; - background-color: rgb(82, 53, 0); - } - - .wy-alert.wy-alert-warning .wy-alert-title, - .rst-content .wy-alert-warning.note .wy-alert-title, - .rst-content .attention .wy-alert-title, - .rst-content .caution .wy-alert-title, - .rst-content .wy-alert-warning.danger .wy-alert-title, - .rst-content .wy-alert-warning.error .wy-alert-title, - .rst-content .wy-alert-warning.hint .wy-alert-title, - .rst-content .wy-alert-warning.important .wy-alert-title, - .rst-content .wy-alert-warning.tip .wy-alert-title, - .rst-content .warning .wy-alert-title, - .rst-content .wy-alert-warning.seealso .wy-alert-title, - .rst-content .admonition-todo .wy-alert-title, - .rst-content .wy-alert-warning.admonition .wy-alert-title, - .wy-alert.wy-alert-warning .rst-content .admonition-title, - .rst-content .wy-alert.wy-alert-warning .admonition-title, - .rst-content .wy-alert-warning.note .admonition-title, - .rst-content .attention .admonition-title, - .rst-content .caution .admonition-title, - .rst-content .wy-alert-warning.danger .admonition-title, - .rst-content .wy-alert-warning.error .admonition-title, - .rst-content .wy-alert-warning.hint .admonition-title, - .rst-content .wy-alert-warning.important .admonition-title, - .rst-content .wy-alert-warning.tip .admonition-title, - .rst-content .warning .admonition-title, - .rst-content .wy-alert-warning.seealso .admonition-title, - .rst-content .admonition-todo .admonition-title, - .rst-content .wy-alert-warning.admonition .admonition-title { - background-image: initial; - background-color: rgb(123, 65, 14); - } - - .wy-alert.wy-alert-info, - .rst-content .note, - .rst-content .wy-alert-info.attention, - .rst-content .wy-alert-info.caution, - .rst-content .wy-alert-info.danger, - .rst-content .wy-alert-info.error, - .rst-content .wy-alert-info.hint, - .rst-content .wy-alert-info.important, - .rst-content .wy-alert-info.tip, - .rst-content .wy-alert-info.warning, - .rst-content .seealso, - .rst-content .wy-alert-info.admonition-todo, - .rst-content .wy-alert-info.admonition { - background-image: initial; - background-color: rgb(32, 35, 36); - } - - .wy-alert.wy-alert-info .wy-alert-title, - .rst-content .note .wy-alert-title, - .rst-content .wy-alert-info.attention .wy-alert-title, - .rst-content .wy-alert-info.caution .wy-alert-title, - .rst-content .wy-alert-info.danger .wy-alert-title, - .rst-content .wy-alert-info.error .wy-alert-title, - .rst-content .wy-alert-info.hint .wy-alert-title, - .rst-content .wy-alert-info.important .wy-alert-title, - .rst-content .wy-alert-info.tip .wy-alert-title, - .rst-content .wy-alert-info.warning .wy-alert-title, - .rst-content .seealso .wy-alert-title, - .rst-content .wy-alert-info.admonition-todo .wy-alert-title, - .rst-content .wy-alert-info.admonition .wy-alert-title, - .wy-alert.wy-alert-info .rst-content .admonition-title, - .rst-content .wy-alert.wy-alert-info .admonition-title, - .rst-content .note .admonition-title, - .rst-content .wy-alert-info.attention .admonition-title, - .rst-content .wy-alert-info.caution .admonition-title, - .rst-content .wy-alert-info.danger .admonition-title, - .rst-content .wy-alert-info.error .admonition-title, - .rst-content .wy-alert-info.hint .admonition-title, - .rst-content .wy-alert-info.important .admonition-title, - .rst-content .wy-alert-info.tip .admonition-title, - .rst-content .wy-alert-info.warning .admonition-title, - .rst-content .seealso .admonition-title, - .rst-content .wy-alert-info.admonition-todo .admonition-title, - .rst-content .wy-alert-info.admonition .admonition-title { - background-image: initial; - background-color: rgb(29, 91, 131); - } - - .wy-alert.wy-alert-success, - .rst-content .wy-alert-success.note, - .rst-content .wy-alert-success.attention, - .rst-content .wy-alert-success.caution, - .rst-content .wy-alert-success.danger, - .rst-content .wy-alert-success.error, - .rst-content .hint, - .rst-content .important, - .rst-content .tip, - .rst-content .wy-alert-success.warning, - .rst-content .wy-alert-success.seealso, - .rst-content .wy-alert-success.admonition-todo, - .rst-content .wy-alert-success.admonition { - background-image: initial; - background-color: rgb(9, 66, 58); - } - - .wy-alert.wy-alert-success .wy-alert-title, - .rst-content .wy-alert-success.note .wy-alert-title, - .rst-content .wy-alert-success.attention .wy-alert-title, - .rst-content .wy-alert-success.caution .wy-alert-title, - .rst-content .wy-alert-success.danger .wy-alert-title, - .rst-content .wy-alert-success.error .wy-alert-title, - .rst-content .hint .wy-alert-title, - .rst-content .important .wy-alert-title, - .rst-content .tip .wy-alert-title, - .rst-content .wy-alert-success.warning .wy-alert-title, - .rst-content .wy-alert-success.seealso .wy-alert-title, - .rst-content .wy-alert-success.admonition-todo .wy-alert-title, - .rst-content .wy-alert-success.admonition .wy-alert-title, - .wy-alert.wy-alert-success .rst-content .admonition-title, - .rst-content .wy-alert.wy-alert-success .admonition-title, - .rst-content .wy-alert-success.note .admonition-title, - .rst-content .wy-alert-success.attention .admonition-title, - .rst-content .wy-alert-success.caution .admonition-title, - .rst-content .wy-alert-success.danger .admonition-title, - .rst-content .wy-alert-success.error .admonition-title, - .rst-content .hint .admonition-title, - .rst-content .important .admonition-title, - .rst-content .tip .admonition-title, - .rst-content .wy-alert-success.warning .admonition-title, - .rst-content .wy-alert-success.seealso .admonition-title, - .rst-content .wy-alert-success.admonition-todo .admonition-title, - .rst-content .wy-alert-success.admonition .admonition-title { - background-image: initial; - background-color: rgb(21, 150, 125); - } - - .wy-alert.wy-alert-neutral, - .rst-content .wy-alert-neutral.note, - .rst-content .wy-alert-neutral.attention, - .rst-content .wy-alert-neutral.caution, - .rst-content .wy-alert-neutral.danger, - .rst-content .wy-alert-neutral.error, - .rst-content .wy-alert-neutral.hint, - .rst-content .wy-alert-neutral.important, - .rst-content .wy-alert-neutral.tip, - .rst-content .wy-alert-neutral.warning, - .rst-content .wy-alert-neutral.seealso, - .rst-content .wy-alert-neutral.admonition-todo, - .rst-content .wy-alert-neutral.admonition { - background-image: initial; - background-color: rgb(27, 36, 36); - } - - .wy-alert.wy-alert-neutral .wy-alert-title, - .rst-content .wy-alert-neutral.note .wy-alert-title, - .rst-content .wy-alert-neutral.attention .wy-alert-title, - .rst-content .wy-alert-neutral.caution .wy-alert-title, - .rst-content .wy-alert-neutral.danger .wy-alert-title, - .rst-content .wy-alert-neutral.error .wy-alert-title, - .rst-content .wy-alert-neutral.hint .wy-alert-title, - .rst-content .wy-alert-neutral.important .wy-alert-title, - .rst-content .wy-alert-neutral.tip .wy-alert-title, - .rst-content .wy-alert-neutral.warning .wy-alert-title, - .rst-content .wy-alert-neutral.seealso .wy-alert-title, - .rst-content .wy-alert-neutral.admonition-todo .wy-alert-title, - .rst-content .wy-alert-neutral.admonition .wy-alert-title, - .wy-alert.wy-alert-neutral .rst-content .admonition-title, - .rst-content .wy-alert.wy-alert-neutral .admonition-title, - .rst-content .wy-alert-neutral.note .admonition-title, - .rst-content .wy-alert-neutral.attention .admonition-title, - .rst-content .wy-alert-neutral.caution .admonition-title, - .rst-content .wy-alert-neutral.danger .admonition-title, - .rst-content .wy-alert-neutral.error .admonition-title, - .rst-content .wy-alert-neutral.hint .admonition-title, - .rst-content .wy-alert-neutral.important .admonition-title, - .rst-content .wy-alert-neutral.tip .admonition-title, - .rst-content .wy-alert-neutral.warning .admonition-title, - .rst-content .wy-alert-neutral.seealso .admonition-title, - .rst-content .wy-alert-neutral.admonition-todo .admonition-title, - .rst-content .wy-alert-neutral.admonition .admonition-title { - color: rgb(192, 186, 178); - background-image: initial; - background-color: rgb(40, 43, 45); - } - - .wy-alert.wy-alert-neutral a, - .rst-content .wy-alert-neutral.note a, - .rst-content .wy-alert-neutral.attention a, - .rst-content .wy-alert-neutral.caution a, - .rst-content .wy-alert-neutral.danger a, - .rst-content .wy-alert-neutral.error a, - .rst-content .wy-alert-neutral.hint a, - .rst-content .wy-alert-neutral.important a, - .rst-content .wy-alert-neutral.tip a, - .rst-content .wy-alert-neutral.warning a, - .rst-content .wy-alert-neutral.seealso a, - .rst-content .wy-alert-neutral.admonition-todo a, - .rst-content .wy-alert-neutral.admonition a { - color: rgb(84, 164, 217); - } - - .wy-tray-container li { - background-image: initial; - background-color: transparent; - color: rgb(232, 230, 227); - box-shadow: rgba(0, 0, 0, 0.1) 0px 5px 5px 0px; - } - - .wy-tray-container li.wy-tray-item-success { - background-image: initial; - background-color: rgb(31, 139, 77); - } - - .wy-tray-container li.wy-tray-item-info { - background-image: initial; - background-color: rgb(33, 102, 148); - } - - .wy-tray-container li.wy-tray-item-warning { - background-image: initial; - background-color: rgb(178, 94, 20); - } - - .wy-tray-container li.wy-tray-item-danger { - background-image: initial; - background-color: rgb(162, 33, 20); - } - - .btn { - color: rgb(232, 230, 227); - border-color: rgba(140, 130, 115, 0.1); - background-color: rgb(31, 139, 77); - text-decoration-color: initial; - box-shadow: rgba(24, 26, 27, 0.5) 0px 1px 2px -1px inset, - rgba(0, 0, 0, 0.1) 0px -2px 0px 0px inset; - } - - .btn-hover { - background-image: initial; - background-color: rgb(37, 114, 165); - color: rgb(232, 230, 227); - } - - .btn:hover { - background-image: initial; - background-color: rgb(35, 156, 86); - color: rgb(232, 230, 227); - } - - .btn:focus { - background-image: initial; - background-color: rgb(35, 156, 86); - outline-color: initial; - } - - .btn:active { - box-shadow: rgba(0, 0, 0, 0.05) 0px -1px 0px 0px inset, - rgba(0, 0, 0, 0.1) 0px 2px 0px 0px inset; - } - - .btn:visited { - color: rgb(232, 230, 227); - } - - .btn:disabled { - background-image: none; - box-shadow: none; - } - - .btn-disabled { - background-image: none; - box-shadow: none; - } - - .btn-disabled:hover, - .btn-disabled:focus, - .btn-disabled:active { - background-image: none; - box-shadow: none; - } - - .btn-info { - background-color: rgb(33, 102, 148) !important; - } - - .btn-info:hover { - background-color: rgb(37, 114, 165) !important; - } - - .btn-neutral { - background-color: rgb(27, 36, 36) !important; - color: rgb(192, 186, 178) !important; - } - - .btn-neutral:hover { - color: rgb(192, 186, 178); - background-color: rgb(34, 44, 44) !important; - } - - .btn-neutral:visited { - color: rgb(192, 186, 178) !important; - } - - .btn-success { - background-color: rgb(31, 139, 77) !important; - } - - .btn-success:hover { - background-color: rgb(27, 122, 68) !important; - } - - .btn-danger { - background-color: rgb(162, 33, 20) !important; - } - - .btn-danger:hover { - background-color: rgb(149, 30, 18) !important; - } - - .btn-warning { - background-color: rgb(178, 94, 20) !important; - } - - .btn-warning:hover { - background-color: rgb(165, 87, 18) !important; - } - - .btn-invert { - background-color: rgb(26, 28, 29); - } - - .btn-invert:hover { - background-color: rgb(35, 38, 40) !important; - } - - .btn-link { - color: rgb(84, 164, 217); - box-shadow: none; - background-color: transparent !important; - border-color: transparent !important; - } - - .btn-link:hover { - box-shadow: none; - background-color: transparent !important; - color: rgb(79, 162, 216) !important; - } - - .btn-link:active { - box-shadow: none; - background-color: transparent !important; - color: rgb(79, 162, 216) !important; - } - - .btn-link:visited { - color: rgb(164, 103, 188); - } - - .wy-dropdown-menu { - background-image: initial; - background-color: rgb(26, 28, 29); - border-color: rgb(60, 65, 67); - box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 2px 0px; - } - - .wy-dropdown-menu > dd > a { - color: rgb(192, 186, 178); - } - - .wy-dropdown-menu > dd > a:hover { - background-image: initial; - background-color: rgb(33, 102, 148); - color: rgb(232, 230, 227); - } - - .wy-dropdown-menu > dd.divider { - border-top-color: rgb(60, 65, 67); - } - - .wy-dropdown-menu > dd.call-to-action { - background-image: initial; - background-color: rgb(40, 43, 45); - } - - .wy-dropdown-menu > dd.call-to-action:hover { - background-image: initial; - background-color: rgb(40, 43, 45); - } - - .wy-dropdown-menu > dd.call-to-action .btn { - color: rgb(232, 230, 227); - } - - .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu { - background-image: initial; - background-color: rgb(26, 28, 29); - } - - .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover { - background-image: initial; - background-color: rgb(33, 102, 148); - color: rgb(232, 230, 227); - } - - .wy-dropdown-arrow::before { - border-bottom-color: rgb(51, 55, 57); - border-left-color: transparent; - border-right-color: transparent; - } - - fieldset { - border-color: initial; - } - - legend { - border-color: initial; - } - - label { - color: rgb(200, 195, 188); - } - - .wy-control-group.wy-control-group-required > label::after { - color: rgb(233, 88, 73); - } - - .wy-form-message-inline { - color: rgb(168, 160, 149); - } - - .wy-form-message { - color: rgb(168, 160, 149); - } - - input[type="text"], input[type="password"], input[type="email"], input[type="url"], input[type="date"], input[type="month"], input[type="time"], input[type="datetime"], input[type="datetime-local"], input[type="week"], input[type="number"], input[type="search"], input[type="tel"], input[type="color"] { - border-color: rgb(62, 68, 70); - box-shadow: rgb(43, 47, 49) 0px 1px 3px inset; - } - - input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus, input[type="url"]:focus, input[type="date"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="week"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus { - outline-color: initial; - border-color: rgb(123, 114, 101); - } - - input.no-focus:focus { - border-color: rgb(62, 68, 70) !important; - } - - input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { - outline-color: rgb(13, 113, 167); - } - - input[type="text"][disabled], input[type="password"][disabled], input[type="email"][disabled], input[type="url"][disabled], input[type="date"][disabled], input[type="month"][disabled], input[type="time"][disabled], input[type="datetime"][disabled], input[type="datetime-local"][disabled], input[type="week"][disabled], input[type="number"][disabled], input[type="search"][disabled], input[type="tel"][disabled], input[type="color"][disabled] { - background-color: rgb(27, 29, 30); - } - - input:focus:invalid, - textarea:focus:invalid, - select:focus:invalid { - color: rgb(233, 88, 73); - border-color: rgb(149, 31, 18); - } - - input:focus:invalid:focus, - textarea:focus:invalid:focus, - select:focus:invalid:focus { - border-color: rgb(149, 31, 18); - } - - input[type="file"]:focus:invalid:focus, input[type="radio"]:focus:invalid:focus, input[type="checkbox"]:focus:invalid:focus { - outline-color: rgb(149, 31, 18); - } - - select, - textarea { - border-color: rgb(62, 68, 70); - box-shadow: rgb(43, 47, 49) 0px 1px 3px inset; - } - - select { - border-color: rgb(62, 68, 70); - background-color: rgb(24, 26, 27); - } - - select:focus, - textarea:focus { - outline-color: initial; - } - - select[disabled], - textarea[disabled], - input[readonly], - select[readonly], - textarea[readonly] { - background-color: rgb(27, 29, 30); - } - - .wy-checkbox, - .wy-radio { - color: rgb(192, 186, 178); - } - - .wy-input-prefix .wy-input-context, - .wy-input-suffix .wy-input-context { - background-color: rgb(27, 36, 36); - border-color: rgb(62, 68, 70); - color: rgb(168, 160, 149); - } - - .wy-input-suffix .wy-input-context { - border-left-color: initial; - } - - .wy-input-prefix .wy-input-context { - border-right-color: initial; - } - - .wy-switch::before { - background-image: initial; - background-color: rgb(53, 57, 59); - } - - .wy-switch::after { - background-image: initial; - background-color: rgb(82, 88, 92); - } - - .wy-switch span { - color: rgb(200, 195, 188); - } - - .wy-switch.active::before { - background-image: initial; - background-color: rgb(24, 106, 58); - } - - .wy-switch.active::after { - background-image: initial; - background-color: rgb(31, 139, 77); - } - - .wy-control-group.wy-control-group-error .wy-form-message, - .wy-control-group.wy-control-group-error > label { - color: rgb(233, 88, 73); - } - - .wy-control-group.wy-control-group-error input[type="text"], .wy-control-group.wy-control-group-error input[type="password"], .wy-control-group.wy-control-group-error input[type="email"], .wy-control-group.wy-control-group-error input[type="url"], .wy-control-group.wy-control-group-error input[type="date"], .wy-control-group.wy-control-group-error input[type="month"], .wy-control-group.wy-control-group-error input[type="time"], .wy-control-group.wy-control-group-error input[type="datetime"], .wy-control-group.wy-control-group-error input[type="datetime-local"], .wy-control-group.wy-control-group-error input[type="week"], .wy-control-group.wy-control-group-error input[type="number"], .wy-control-group.wy-control-group-error input[type="search"], .wy-control-group.wy-control-group-error input[type="tel"], .wy-control-group.wy-control-group-error input[type="color"] { - border-color: rgb(149, 31, 18); - } - - .wy-control-group.wy-control-group-error textarea { - border-color: rgb(149, 31, 18); - } - - .wy-inline-validate.wy-inline-validate-success .wy-input-context { - color: rgb(92, 218, 145); - } - - .wy-inline-validate.wy-inline-validate-danger .wy-input-context { - color: rgb(233, 88, 73); - } - - .wy-inline-validate.wy-inline-validate-warning .wy-input-context { - color: rgb(232, 138, 54); - } - - .wy-inline-validate.wy-inline-validate-info .wy-input-context { - color: rgb(84, 164, 217); - } - - .wy-table caption, - .rst-content table.docutils caption, - .rst-content table.field-list caption { - color: rgb(232, 230, 227); - } - - .wy-table thead, - .rst-content table.docutils thead, - .rst-content table.field-list thead { - color: rgb(232, 230, 227); - } - - .wy-table thead th, - .rst-content table.docutils thead th, - .rst-content table.field-list thead th { - border-bottom-color: rgb(56, 61, 63); - } - - .wy-table td, - .rst-content table.docutils td, - .rst-content table.field-list td { - background-color: transparent; - } - - .wy-table-secondary { - color: rgb(152, 143, 129); - } - - .wy-table-tertiary { - color: rgb(152, 143, 129); - } - - .wy-table-odd td, - .wy-table-striped tr:nth-child(2n-1) td, - .rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td { - background-color: rgb(27, 36, 36); - } - - .wy-table-backed { - background-color: rgb(27, 36, 36); - } - - .wy-table-bordered-all, - .rst-content table.docutils { - border-color: rgb(56, 61, 63); - } - - .wy-table-bordered-all td, - .rst-content table.docutils td { - border-bottom-color: rgb(56, 61, 63); - border-left-color: rgb(56, 61, 63); - } - - .wy-table-bordered { - border-color: rgb(56, 61, 63); - } - - .wy-table-bordered-rows td { - border-bottom-color: rgb(56, 61, 63); - } - - .wy-table-horizontal td, - .wy-table-horizontal th { - border-bottom-color: rgb(56, 61, 63); - } - - a { - color: rgb(84, 164, 217); - text-decoration-color: initial; - } - - a:hover { - color: rgb(68, 156, 214); - } - - a:visited { - color: rgb(164, 103, 188); - } - - body { - color: rgb(192, 186, 178); - background-image: initial; - background-color: rgb(33, 35, 37); - } - - .wy-text-strike { - text-decoration-color: initial; - } - - .wy-text-warning { - color: rgb(232, 138, 54) !important; - } - - a.wy-text-warning:hover { - color: rgb(236, 157, 87) !important; - } - - .wy-text-info { - color: rgb(84, 164, 217) !important; - } - - a.wy-text-info:hover { - color: rgb(79, 162, 216) !important; - } - - .wy-text-success { - color: rgb(92, 218, 145) !important; - } - - a.wy-text-success:hover { - color: rgb(73, 214, 133) !important; - } - - .wy-text-danger { - color: rgb(233, 88, 73) !important; - } - - a.wy-text-danger:hover { - color: rgb(237, 118, 104) !important; - } - - .wy-text-neutral { - color: rgb(192, 186, 178) !important; - } - - a.wy-text-neutral:hover { - color: rgb(176, 169, 159) !important; - } - - hr { - border-right-color: initial; - border-bottom-color: initial; - border-left-color: initial; - border-top-color: rgb(56, 61, 63); - } - - code, - .rst-content tt, - .rst-content code { - background-image: initial; - background-color: rgb(24, 26, 27); - border-color: rgb(56, 61, 63); - color: rgb(233, 88, 73); - } - - .wy-plain-list-disc, - .rst-content .section ul, - .rst-content .toctree-wrapper ul, - article ul { - list-style-image: initial; - } - - .wy-plain-list-disc li, - .rst-content .section ul li, - .rst-content .toctree-wrapper ul li, - article ul li { - list-style-image: initial; - } - - .wy-plain-list-disc li li, - .rst-content .section ul li li, - .rst-content .toctree-wrapper ul li li, - article ul li li { - list-style-image: initial; - } - - .wy-plain-list-disc li li li, - .rst-content .section ul li li li, - .rst-content .toctree-wrapper ul li li li, - article ul li li li { - list-style-image: initial; - } - - .wy-plain-list-disc li ol li, - .rst-content .section ul li ol li, - .rst-content .toctree-wrapper ul li ol li, - article ul li ol li { - list-style-image: initial; - } - - .wy-plain-list-decimal, - .rst-content .section ol, - .rst-content ol.arabic, - article ol { - list-style-image: initial; - } - - .wy-plain-list-decimal li, - .rst-content .section ol li, - .rst-content ol.arabic li, - article ol li { - list-style-image: initial; - } - - .wy-plain-list-decimal li ul li, - .rst-content .section ol li ul li, - .rst-content ol.arabic li ul li, - article ol li ul li { - list-style-image: initial; - } - - .wy-breadcrumbs li code, - .wy-breadcrumbs li .rst-content tt, - .rst-content .wy-breadcrumbs li tt { - border-color: initial; - background-image: none; - background-color: initial; - } - - .wy-breadcrumbs li code.literal, - .wy-breadcrumbs li .rst-content tt.literal, - .rst-content .wy-breadcrumbs li tt.literal { - color: rgb(192, 186, 178); - } - - .wy-breadcrumbs-extra { - color: rgb(184, 178, 169); - } - - .wy-menu a:hover { - text-decoration-color: initial; - } - - .wy-menu-horiz li:hover { - background-image: initial; - background-color: rgba(24, 26, 27, 0.1); - } - - .wy-menu-horiz li.divide-left { - border-left-color: rgb(119, 110, 98); - } - - .wy-menu-horiz li.divide-right { - border-right-color: rgb(119, 110, 98); - } - - .wy-menu-vertical header, - .wy-menu-vertical p.caption { - color: rgb(99, 161, 201); - } - - .wy-menu-vertical li.divide-top { - border-top-color: rgb(119, 110, 98); - } - - .wy-menu-vertical li.divide-bottom { - border-bottom-color: rgb(119, 110, 98); - } - - .wy-menu-vertical li.current { - background-image: initial; - background-color: rgb(40, 43, 45); - } - - .wy-menu-vertical li.current a { - color: rgb(152, 143, 129); - border-right-color: rgb(63, 69, 71); - } - - .wy-menu-vertical li.current a:hover { - background-image: initial; - background-color: rgb(47, 51, 53); - } - - .wy-menu-vertical li code, - .wy-menu-vertical li .rst-content tt, - .rst-content .wy-menu-vertical li tt { - border-color: initial; - background-image: inherit; - background-color: inherit; - color: inherit; - } - - .wy-menu-vertical li span.toctree-expand { - color: rgb(183, 177, 168); - } - - .wy-menu-vertical li.on a, - .wy-menu-vertical li.current > a { - color: rgb(192, 186, 178); - background-image: initial; - background-color: rgb(26, 28, 29); - border-color: initial; - } - - .wy-menu-vertical li.on a:hover, - .wy-menu-vertical li.current > a:hover { - background-image: initial; - background-color: rgb(26, 28, 29); - } - - .wy-menu-vertical li.on a:hover span.toctree-expand, - .wy-menu-vertical li.current > a:hover span.toctree-expand { - color: rgb(152, 143, 129); - } - - .wy-menu-vertical li.on a span.toctree-expand, - .wy-menu-vertical li.current > a span.toctree-expand { - color: rgb(200, 195, 188); - } - - .wy-menu-vertical li.toctree-l1.current > a { - border-bottom-color: rgb(63, 69, 71); - border-top-color: rgb(63, 69, 71); - } - - .wy-menu-vertical li.toctree-l2 a, - .wy-menu-vertical li.toctree-l3 a, - .wy-menu-vertical li.toctree-l4 a { - color: rgb(192, 186, 178); - } - - .wy-menu-vertical li.toctree-l2.current > a { - background-image: initial; - background-color: rgb(54, 59, 61); - } - - .wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a { - background-image: initial; - background-color: rgb(54, 59, 61); - } - - .wy-menu-vertical li.toctree-l2 a:hover span.toctree-expand { - color: rgb(152, 143, 129); - } - - .wy-menu-vertical li.toctree-l2 span.toctree-expand { - color: rgb(174, 167, 156); - } - - .wy-menu-vertical li.toctree-l3.current > a { - background-image: initial; - background-color: rgb(61, 66, 69); - } - - .wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a { - background-image: initial; - background-color: rgb(61, 66, 69); - } - - .wy-menu-vertical li.toctree-l3 a:hover span.toctree-expand { - color: rgb(152, 143, 129); - } - - .wy-menu-vertical li.toctree-l3 span.toctree-expand { - color: rgb(166, 158, 146); - } - - .wy-menu-vertical li.toctree-l2.current a, - .wy-menu-vertical li.toctree-l3.current a { - background-color: #363636; - } - - .wy-menu-vertical li ul li a { - color: rgb(208, 204, 198); - } - - .wy-menu-vertical a { - color: rgb(208, 204, 198); - } - - .wy-menu-vertical a:hover { - background-color: rgb(57, 62, 64); - } - - .wy-menu-vertical a:hover span.toctree-expand { - color: rgb(208, 204, 198); - } - - .wy-menu-vertical a:active { - background-color: rgb(33, 102, 148); - color: rgb(232, 230, 227); - } - - .wy-menu-vertical a:active span.toctree-expand { - color: rgb(232, 230, 227); - } - - .wy-side-nav-search { - background-color: rgb(33, 102, 148); - color: rgb(230, 228, 225); - } - - .wy-side-nav-search input[type="text"] { - border-color: rgb(35, 111, 160); - } - - .wy-side-nav-search img { - background-color: rgb(33, 102, 148); - } - - .wy-side-nav-search > a, - .wy-side-nav-search .wy-dropdown > a { - color: rgb(230, 228, 225); - } - - .wy-side-nav-search > a:hover, - .wy-side-nav-search .wy-dropdown > a:hover { - background-image: initial; - background-color: rgba(24, 26, 27, 0.1); - } - - .wy-side-nav-search > a img.logo, - .wy-side-nav-search .wy-dropdown > a img.logo { - background-image: initial; - background-color: transparent; - } - - .wy-side-nav-search > div.version { - color: rgba(232, 230, 227, 0.3); - } - - .wy-nav .wy-menu-vertical header { - color: rgb(84, 164, 217); - } - - .wy-nav .wy-menu-vertical a { - color: rgb(184, 178, 169); - } - - .wy-nav .wy-menu-vertical a:hover { - background-color: rgb(33, 102, 148); - color: rgb(232, 230, 227); - } - - .wy-body-for-nav { - background-image: initial; - background-color: rgb(24, 26, 27); - } - - .wy-nav-side { - color: rgb(169, 161, 150); - background-image: initial; - background-color: rgb(38, 41, 43); - } - - .wy-nav-top { - background-image: initial; - background-color: rgb(33, 102, 148); - color: rgb(232, 230, 227); - } - - .wy-nav-top a { - color: rgb(232, 230, 227); - } - - .wy-nav-top img { - background-color: rgb(33, 102, 148); - } - - .wy-nav-content-wrap { - background-image: initial; - background-color: rgb(26, 28, 29); - } - - .wy-body-mask { - background-image: initial; - background-color: rgba(0, 0, 0, 0.2); - } - - footer { - color: rgb(152, 143, 129); - } - - footer span.commit code, - footer span.commit .rst-content tt, - .rst-content footer span.commit tt { - background-image: none; - background-color: initial; - border-color: initial; - color: rgb(152, 143, 129); - } - - #search-results .search li { - border-bottom-color: rgb(56, 61, 63); - } - - #search-results .search li:first-child { - border-top-color: rgb(56, 61, 63); - } - - #search-results .context { - color: rgb(152, 143, 129); - } - - @media screen and (min-width: 1100px) { - .wy-nav-content-wrap { - background-image: initial; - background-color: rgba(0, 0, 0, 0.05); - } - - .wy-nav-content { - background-image: initial; - background-color: rgb(26, 28, 29); - } - } - .rst-versions { - color: rgb(230, 228, 225); - background-image: initial; - background-color: rgb(23, 24, 25); - } - - .rst-versions a { - color: rgb(84, 164, 217); - text-decoration-color: initial; - } - - .rst-versions .rst-current-version { - background-color: rgb(29, 31, 32); - color: rgb(92, 218, 145); - } - - .rst-versions .rst-current-version .fa, - .rst-versions .rst-current-version .wy-menu-vertical li span.toctree-expand, - .wy-menu-vertical li .rst-versions .rst-current-version span.toctree-expand, - .rst-versions .rst-current-version .rst-content .admonition-title, - .rst-content .rst-versions .rst-current-version .admonition-title, - .rst-versions .rst-current-version .rst-content h1 .headerlink, - .rst-content h1 .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content h2 .headerlink, - .rst-content h2 .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content h3 .headerlink, - .rst-content h3 .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content h4 .headerlink, - .rst-content h4 .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content h5 .headerlink, - .rst-content h5 .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content h6 .headerlink, - .rst-content h6 .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content dl dt .headerlink, - .rst-content dl dt .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content p.caption .headerlink, - .rst-content p.caption .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content table > caption .headerlink, - .rst-content table > caption .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content .code-block-caption .headerlink, - .rst-content .code-block-caption .rst-versions .rst-current-version .headerlink, - .rst-versions .rst-current-version .rst-content tt.download span:first-child, - .rst-content tt.download .rst-versions .rst-current-version span:first-child, - .rst-versions .rst-current-version .rst-content code.download span:first-child, - .rst-content code.download .rst-versions .rst-current-version span:first-child, - .rst-versions .rst-current-version .icon { - color: rgb(230, 228, 225); - } - - .rst-versions .rst-current-version.rst-out-of-date { - background-color: rgb(162, 33, 20); - color: rgb(232, 230, 227); - } - - .rst-versions .rst-current-version.rst-active-old-version { - background-color: rgb(192, 156, 11); - color: rgb(232, 230, 227); - } - - .rst-versions .rst-other-versions { - color: rgb(152, 143, 129); - } - - .rst-versions .rst-other-versions hr { - border-right-color: initial; - border-bottom-color: initial; - border-left-color: initial; - border-top-color: rgb(119, 111, 98); - } - - .rst-versions .rst-other-versions dd a { - color: rgb(230, 228, 225); - } - - .rst-versions.rst-badge { - border-color: initial; - } - - .rst-content abbr[title] { - text-decoration-color: initial; - } - - .rst-content.style-external-links a.reference.external::after { - color: rgb(184, 178, 169); - } - - .rst-content pre.literal-block, .rst-content div[class^="highlight"] { - border-color: rgb(56, 61, 63); - } - - .rst-content pre.literal-block div[class^="highlight"], .rst-content div[class^="highlight"] div[class^="highlight"] { - border-color: initial; - } - - .rst-content .linenodiv pre { - border-right-color: rgb(54, 59, 61); - } - - .rst-content .admonition table { - border-color: rgba(140, 130, 115, 0.1); - } - - .rst-content .admonition table td, - .rst-content .admonition table th { - background-image: initial !important; - background-color: transparent !important; - border-color: rgba(140, 130, 115, 0.1) !important; - } - - .rst-content .section ol.loweralpha, - .rst-content .section ol.loweralpha li { - list-style-image: initial; - } - - .rst-content .section ol.upperalpha, - .rst-content .section ol.upperalpha li { - list-style-image: initial; - } - - .rst-content .toc-backref { - color: rgb(192, 186, 178); - } - - .rst-content .sidebar { - background-image: initial; - background-color: rgb(27, 36, 36); - border-color: rgb(56, 61, 63); - } - - .rst-content .sidebar .sidebar-title { - background-image: initial; - background-color: rgb(40, 43, 45); - } - - .rst-content .highlighted { - background-image: initial; - background-color: rgb(192, 156, 11); - } - - .rst-content table.docutils.citation, - .rst-content table.docutils.footnote { - background-image: none; - background-color: initial; - border-color: initial; - color: rgb(152, 143, 129); - } - - .rst-content table.docutils.citation td, - .rst-content table.docutils.citation tr, - .rst-content table.docutils.footnote td, - .rst-content table.docutils.footnote tr { - border-color: initial; - background-color: transparent !important; - } - - .rst-content table.docutils.citation tt, - .rst-content table.docutils.citation code, - .rst-content table.docutils.footnote tt, - .rst-content table.docutils.footnote code { - color: rgb(178, 172, 162); - } - - .rst-content table.docutils th { - border-color: rgb(56, 61, 63); - } - - .rst-content table.field-list { - border-color: initial; - } - - .rst-content table.field-list td { - border-color: initial; - } - - .rst-content tt, - .rst-content tt, - .rst-content code { - color: rgb(232, 230, 227); - } - - .rst-content tt.literal, - .rst-content tt.literal, - .rst-content code.literal { - color: rgb(233, 88, 73); - } - - .rst-content tt.xref, - a .rst-content tt, - .rst-content tt.xref, - .rst-content code.xref, - a .rst-content tt, - a .rst-content code { - color: rgb(192, 186, 178); - } - - .rst-content a tt, - .rst-content a tt, - .rst-content a code { - color: rgb(84, 164, 217); - } - - .rst-content dl:not(.docutils) dt { - background-image: initial; - background-color: rgb(32, 35, 36); - color: rgb(84, 164, 217); - border-top-color: rgb(28, 89, 128); - } - - .rst-content dl:not(.docutils) dt::before { - color: rgb(109, 178, 223); - } - - .rst-content dl:not(.docutils) dt .headerlink { - color: rgb(192, 186, 178); - } - - .rst-content dl:not(.docutils) dl dt { - border-top-color: initial; - border-right-color: initial; - border-bottom-color: initial; - border-left-color: rgb(62, 68, 70); - background-image: initial; - background-color: rgb(32, 35, 37); - color: rgb(178, 172, 162); - } - - .rst-content dl:not(.docutils) dl dt .headerlink { - color: rgb(192, 186, 178); - } - - .rst-content dl:not(.docutils) tt.descname, - .rst-content dl:not(.docutils) tt.descclassname, - .rst-content dl:not(.docutils) tt.descname, - .rst-content dl:not(.docutils) code.descname, - .rst-content dl:not(.docutils) tt.descclassname, - .rst-content dl:not(.docutils) code.descclassname { - background-color: transparent; - border-color: initial; - } - - .rst-content dl:not(.docutils) .optional { - color: rgb(232, 230, 227); - } - - .rst-content .viewcode-link, - .rst-content .viewcode-back { - color: rgb(92, 218, 145); - } - - .rst-content tt.download, - .rst-content code.download { - background-image: inherit; - background-color: inherit; - color: inherit; - border-color: inherit; - } - - .rst-content .guilabel { - border-color: rgb(27, 84, 122); - background-image: initial; - background-color: rgb(32, 35, 36); - } - - span[id*="MathJax-Span"] { - color: rgb(192, 186, 178); - } - - .highlight .hll { - background-color: rgb(82, 82, 0); - } - - .highlight { - background-image: initial; - background-color: rgb(61, 82, 0); - } - - .highlight .c { - color: rgb(119, 179, 195); - } - - .highlight .err { - border-color: rgb(179, 0, 0); - } - - .highlight .k { - color: rgb(126, 255, 163); - } - - .highlight .o { - color: rgb(168, 160, 149); - } - - .highlight .ch { - color: rgb(119, 179, 195); - } - - .highlight .cm { - color: rgb(119, 179, 195); - } - - .highlight .cp { - color: rgb(126, 255, 163); - } - - .highlight .cpf { - color: rgb(119, 179, 195); - } - - .highlight .c1 { - color: rgb(119, 179, 195); - } - - .highlight .cs { - color: rgb(119, 179, 195); - background-color: rgb(60, 0, 0); - } - - .highlight .gd { - color: rgb(255, 92, 92); - } - - .highlight .gr { - color: rgb(255, 26, 26); - } - - .highlight .gh { - color: rgb(127, 174, 255); - } - - .highlight .gi { - color: rgb(92, 255, 92); - } - - .highlight .go { - color: rgb(200, 195, 188); - } - - .highlight .gp { - color: rgb(246, 147, 68); - } - - .highlight .gu { - color: rgb(255, 114, 255); - } - - .highlight .gt { - color: rgb(71, 160, 255); - } - - .highlight .kc { - color: rgb(126, 255, 163); - } - - .highlight .kd { - color: rgb(126, 255, 163); - } - - .highlight .kn { - color: rgb(126, 255, 163); - } - - .highlight .kp { - color: rgb(126, 255, 163); - } - - .highlight .kr { - color: rgb(126, 255, 163); - } - - .highlight .kt { - color: rgb(255, 137, 103); - } - - .highlight .m { - color: rgb(125, 222, 174); - } - - .highlight .s { - color: rgb(123, 166, 202); - } - - .highlight .na { - color: rgb(123, 166, 202); - } - - .highlight .nb { - color: rgb(126, 255, 163); - } - - .highlight .nc { - color: rgb(81, 194, 242); - } - - .highlight .no { - color: rgb(103, 177, 215); - } - - .highlight .nd { - color: rgb(178, 172, 162); - } - - .highlight .ni { - color: rgb(217, 100, 73); - } - - .highlight .ne { - color: rgb(126, 255, 163); - } - - .highlight .nf { - color: rgb(131, 186, 249); - } - - .highlight .nl { - color: rgb(137, 193, 255); - } - - .highlight .nn { - color: rgb(81, 194, 242); - } - - .highlight .nt { - color: rgb(138, 191, 249); - } - - .highlight .nv { - color: rgb(190, 103, 215); - } - - .highlight .ow { - color: rgb(126, 255, 163); - } - - .highlight .w { - color: rgb(189, 183, 175); - } - - .highlight .mb { - color: rgb(125, 222, 174); - } - - .highlight .mf { - color: rgb(125, 222, 174); - } - - .highlight .mh { - color: rgb(125, 222, 174); - } - - .highlight .mi { - color: rgb(125, 222, 174); - } - - .highlight .mo { - color: rgb(125, 222, 174); - } - - .highlight .sa { - color: rgb(123, 166, 202); - } - - .highlight .sb { - color: rgb(123, 166, 202); - } - - .highlight .sc { - color: rgb(123, 166, 202); - } - - .highlight .dl { - color: rgb(123, 166, 202); - } - - .highlight .sd { - color: rgb(123, 166, 202); - } - - .highlight .s2 { - color: rgb(123, 166, 202); - } - - .highlight .se { - color: rgb(123, 166, 202); - } - - .highlight .sh { - color: rgb(123, 166, 202); - } - - .highlight .si { - color: rgb(117, 168, 209); - } - - .highlight .sx { - color: rgb(246, 147, 68); - } - - .highlight .sr { - color: rgb(133, 182, 224); - } - - .highlight .s1 { - color: rgb(123, 166, 202); - } - - .highlight .ss { - color: rgb(188, 230, 128); - } - - .highlight .bp { - color: rgb(126, 255, 163); - } - - .highlight .fm { - color: rgb(131, 186, 249); - } - - .highlight .vc { - color: rgb(190, 103, 215); - } - - .highlight .vg { - color: rgb(190, 103, 215); - } - - .highlight .vi { - color: rgb(190, 103, 215); - } - - .highlight .vm { - color: rgb(190, 103, 215); - } - - .highlight .il { - color: rgb(125, 222, 174); - } - - .rst-other-versions a { - border-color: initial; - } - - .ethical-sidebar .ethical-image-link, - .ethical-footer .ethical-image-link { - border-color: initial; - } - - .ethical-sidebar, - .ethical-footer { - background-color: rgb(34, 36, 38); - border-color: rgb(62, 68, 70); - color: rgb(226, 223, 219); - } - - .ethical-sidebar ul { - list-style-image: initial; - } - - .ethical-sidebar ul li { - background-color: rgb(5, 77, 121); - color: rgb(232, 230, 227); - } - - .ethical-sidebar a, - .ethical-sidebar a:visited, - .ethical-sidebar a:hover, - .ethical-sidebar a:active, - .ethical-footer a, - .ethical-footer a:visited, - .ethical-footer a:hover, - .ethical-footer a:active { - color: rgb(226, 223, 219); - text-decoration-color: initial !important; - border-bottom-color: initial !important; - } - - .ethical-callout a { - color: rgb(161, 153, 141) !important; - text-decoration-color: initial !important; - } - - .ethical-fixedfooter { - background-color: rgb(34, 36, 38); - border-top-color: rgb(66, 72, 74); - color: rgb(192, 186, 178); - } - - .ethical-fixedfooter .ethical-text::before { - background-color: rgb(61, 140, 64); - color: rgb(232, 230, 227); - } - - .ethical-fixedfooter .ethical-callout { - color: rgb(168, 160, 149); - } - - .ethical-fixedfooter a, - .ethical-fixedfooter a:hover, - .ethical-fixedfooter a:active, - .ethical-fixedfooter a:visited { - color: rgb(192, 186, 178); - text-decoration-color: initial; - } - - .ethical-rtd .ethical-sidebar { - color: rgb(184, 178, 169); - } - - .ethical-alabaster a.ethical-image-link { - border-color: initial !important; - } - - .ethical-dark-theme .ethical-sidebar { - background-color: rgb(58, 62, 65); - border-color: rgb(75, 81, 84); - color: rgb(193, 188, 180) !important; - } - - .ethical-dark-theme a, - .ethical-dark-theme a:visited { - color: rgb(216, 213, 208) !important; - border-bottom-color: initial !important; - } - - .ethical-dark-theme .ethical-callout a { - color: rgb(184, 178, 169) !important; - } - - .keep-us-sustainable { - border-color: rgb(87, 133, 38); - } - - .keep-us-sustainable a, - .keep-us-sustainable a:hover, - .keep-us-sustainable a:visited { - text-decoration-color: initial; - } - - .wy-body-for-nav .keep-us-sustainable { - color: rgb(184, 178, 169); - } - - .wy-body-for-nav .keep-us-sustainable a { - color: rgb(222, 219, 215); - } - /* For black-on-white/transparent images at handbook/text-anchors.html */ #text-anchors img { filter: invert(1) brightness(0.85) hue-rotate(-60deg); diff --git a/docs/resources/css/light.css b/docs/resources/css/light.css deleted file mode 100644 index 04edd7b16b9..00000000000 --- a/docs/resources/css/light.css +++ /dev/null @@ -1,8 +0,0 @@ -@media (prefers-color-scheme: light) { - - .wy-menu-vertical li.toctree-l2.current a, - .wy-menu-vertical li.toctree-l3.current a { - background-color: #c9c9c9; - } - -} diff --git a/docs/resources/css/styles.css b/docs/resources/css/styles.css deleted file mode 100644 index 111f84085b7..00000000000 --- a/docs/resources/css/styles.css +++ /dev/null @@ -1,8 +0,0 @@ -th p { - margin-bottom: 0; -} - -.rst-content tr .line-block { - font-size: 1rem; - margin-bottom: 0; -} diff --git a/docs/resources/js/script.js b/docs/resources/js/script.js deleted file mode 100644 index 5cb6494ea59..00000000000 --- a/docs/resources/js/script.js +++ /dev/null @@ -1,58 +0,0 @@ -jQuery(document).ready(function ($) { - setTimeout(function () { - var sectionID = 'base'; - var search = function ($section, $sidebarItem) { - $section.children('.section, .function, .method').each(function () { - if ($(this).hasClass('section')) { - sectionID = $(this).attr('id'); - search($(this), $sidebarItem.parent().find('[href="#'+sectionID+'"]')); - } else { - var $dt = $(this).children('dt'); - var id = $dt.attr('id'); - if (id === undefined) { - return; - } - - var $functionsUL = $sidebarItem.siblings('[data-sectionID='+sectionID+']'); - if (!$functionsUL.length) { - $functionsUL = $('