diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07799f2..c6569c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,26 +8,13 @@ on: jobs: - analyze: - name: CodeQL - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2.3.4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: python - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 - msgcheck: runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v2.2.2 - - uses: actions/checkout@v2.3.4 + - uses: actions/setup-python@v3 + - uses: actions/checkout@v3 - run: sudo apt install -y gettext aspell libenchant-dev - - uses: actions/cache@v2.1.6 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('lint-requirements.txt') }} @@ -48,9 +35,9 @@ jobs: - "pydocstyle ." runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v2.2.2 - - uses: actions/checkout@v2.3.4 - - uses: actions/cache@v2.1.6 + - uses: actions/setup-python@v3 + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('lint-requirements.txt') }} @@ -65,9 +52,9 @@ jobs: steps: - name: Install gettext run: sudo apt install gettext -y - - uses: actions/setup-python@v2.2.2 + - uses: actions/setup-python@v3 - run: python -m pip install --upgrade pip setuptools wheel twine readme-renderer - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 - run: python setup.py sdist bdist_wheel - run: python -m twine check dist/* - uses: actions/upload-artifact@v2 @@ -82,23 +69,22 @@ jobs: strategy: matrix: python-version: - - "3.7" - "3.8" - "3.9" + - "3.10" django-version: - - "2.2" - - "3.1" - - "3.2rc1" + - "3.2a" + - "4.0a" extra: - "" - "progressbar" steps: - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - run: sudo apt install gettext -y - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 - run: python -m pip install --upgrade pip setuptools codecov wheel - run: python -m pip install .[${{ matrix.extra }}] if: ${{ matrix.extra }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 30eb296..bfa7850 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.3.4 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install gettext diff --git a/lint-requirements.txt b/lint-requirements.txt index a8978cc..7aae3f5 100644 --- a/lint-requirements.txt +++ b/lint-requirements.txt @@ -1,6 +1,6 @@ -bandit==1.7.0 -black==21.5b1 -flake8==3.9.2 -isort==5.8.0 -msgcheck==3.1 +bandit==1.7.4 +black==22.3.0 +flake8==4.0.1 +isort==5.10.1 +msgcheck==4.0.0 pydocstyle==6.1.1 diff --git a/setup.cfg b/setup.cfg index 696d7c0..10e8e2b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,15 +20,14 @@ classifier = Topic :: Software Development Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Framework :: Django - Framework :: Django :: 2.2 - Framework :: Django :: 3.1 Framework :: Django :: 3.2 + Framework :: Django :: 4.0 -python_requires = >=3.7 +python_requires = >=3.8 [options] include_package_data = True diff --git a/stdimage/models.py b/stdimage/models.py index 0eb6943..97ff514 100644 --- a/stdimage/models.py +++ b/stdimage/models.py @@ -144,6 +144,25 @@ def delete_variations(self): variation_name = self.get_variation_name(self.name, variation) self.storage.delete(variation_name) + def __getstate__(self): + state = super().__getstate__() + state["variations"] = {} + for variation_name in self.field.variations: + variation = getattr(self, variation_name) + variation_state = variation.__getstate__() + state["variations"][variation_name] = variation_state + return state + + def __setstate__(self, state): + variations = state["variations"] + state.pop("variations") + super().__setstate__(state) + for key, value in variations.items(): + cls = ImageFieldFile + field = cls.__new__(cls) + setattr(self, key, field) + getattr(self, key).__setstate__(value) + class StdImageField(ImageField): """ diff --git a/tests/settings.py b/tests/settings.py index 03ebbd9..480a744 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -45,4 +45,4 @@ SECRET_KEY = "foobar" -USE_L10N = True +USE_TZ = True diff --git a/tests/test_models.py b/tests/test_models.py index 772737d..e8d47f2 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,13 +1,17 @@ import io import os import time +from copy import deepcopy import pytest from django.conf import settings from django.core.files.storage import default_storage from django.core.files.uploadedfile import SimpleUploadedFile +from django.db.models.fields.files import ImageFieldFile from PIL import Image +from stdimage.models import StdImageFieldFile + from . import models from .models import ( AdminDeleteModel, @@ -170,6 +174,47 @@ def test_defer(self, db, django_assert_num_queries): deferred.image assert instance.image.thumbnail == deferred.image.thumbnail + @pytest.mark.django_db + def test_variations_deepcopy(self): + """Tests test_variations() with a deep copied object""" + instance_original = ResizeModel.objects.create( + image=self.fixtures["600x400.jpg"] + ) + instance = deepcopy(instance_original) + assert isinstance(instance.image, StdImageFieldFile) + + assert hasattr(instance.image, "thumbnail") + assert hasattr(instance.image, "medium") + + assert isinstance(instance.image.thumbnail, ImageFieldFile) + assert isinstance(instance.image.medium, ImageFieldFile) + + source_file = self.fixtures["600x400.jpg"] + + assert os.path.exists(os.path.join(IMG_DIR, "600x400.jpg")) + assert instance.image.width == 600 + assert instance.image.height == 400 + path = os.path.join(IMG_DIR, "600x400.jpg") + + with open(path, "rb") as f: + source_file.seek(0) + assert source_file.read() == f.read() + + path = os.path.join(IMG_DIR, "600x400.medium.jpg") + assert os.path.exists(path) + assert instance.image.medium.width == 400 + assert instance.image.medium.height <= 400 + with open(os.path.join(IMG_DIR, "600x400.medium.jpg"), "rb") as f: + source_file.seek(0) + assert source_file.read() != f.read() + + assert os.path.exists(os.path.join(IMG_DIR, "600x400.thumbnail.jpg")) + assert instance.image.thumbnail.width == 100 + assert instance.image.thumbnail.height <= 75 + with open(os.path.join(IMG_DIR, "600x400.thumbnail.jpg"), "rb") as f: + source_file.seek(0) + assert source_file.read() != f.read() + class TestUtils(TestStdImage): """Tests Utils"""