diff --git a/.github/workflows/check-reserved-keywords.yml b/.github/workflows/check-reserved-keywords.yml index 8dc23dc3..22f8b335 100644 --- a/.github/workflows/check-reserved-keywords.yml +++ b/.github/workflows/check-reserved-keywords.yml @@ -10,10 +10,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.11 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15ac556e..a7de04a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,13 +14,13 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ['3.11'] - toxenv: [django42, quality] + python-version: ['3.11', '3.12'] + toxenv: [django42, django52, quality] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -37,7 +37,7 @@ jobs: - name: Run Coverage if: matrix.python-version == '3.11' && matrix.toxenv=='django42' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: flags: unittests fail_ci_if_error: true @@ -51,10 +51,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.11 @@ -63,7 +63,7 @@ jobs: - name: Install Dependencies run: | - pip install "Django>=4.2,<4.3" + pip install "Django>=4.2,<5.0" pip install -r requirements/ci.txt pip install -r requirements/test.txt diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index ed0ff513..1bc4399b 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -11,9 +11,9 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.11 diff --git a/.github/workflows/verify_changed_contract.yml b/.github/workflows/verify_changed_contract.yml index 9673967c..0928a1f0 100644 --- a/.github/workflows/verify_changed_contract.yml +++ b/.github/workflows/verify_changed_contract.yml @@ -19,10 +19,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: setup python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: 3.11 diff --git a/edxval/settings/base.py b/edxval/settings/base.py index 71e41f60..54cd70b8 100644 --- a/edxval/settings/base.py +++ b/edxval/settings/base.py @@ -23,7 +23,7 @@ } # Hosts/domain names that are valid for this site; required if DEBUG is False -# See https://docs.djangoproject.com/en/1.4/ref/settings/#allowed-hosts +# See https://docs.djangoproject.com/en/5.2/ref/settings/#allowed-hosts ALLOWED_HOSTS = [] # Local time zone for this installation. Choices can be found here: @@ -42,10 +42,6 @@ # to load the internationalization machinery. USE_I18N = True -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale. -USE_L10N = True - # If you set this to False, Django will not use timezone-aware datetimes. USE_TZ = True @@ -179,7 +175,6 @@ DIRECTORY_PREFIX='video-transcripts/', ) - # Required by Django 2.2 to run management commands. TEMPLATES = [ { diff --git a/edxval/tests/test_storages.py b/edxval/tests/test_storages.py new file mode 100644 index 00000000..0814a221 --- /dev/null +++ b/edxval/tests/test_storages.py @@ -0,0 +1,99 @@ + +""" +Unit tests for django-storages +""" + +from unittest import TestCase + +from django.conf import settings +from django.test.utils import override_settings + +from edxval.utils import get_video_image_storage, get_video_transcript_storage +from storages.backends.s3boto3 import S3Boto3Storage # pylint: disable=wrong-import-order + + +class S3Boto3TestCase(TestCase): + """Unit tests for verifying the S3Boto3 storage backend selection logic""" + + def setUp(self): + self.storage = S3Boto3Storage() + + def test_video_image_backend(self): + # settings file contains the `VIDEO_IMAGE_SETTINGS` but dont'have STORAGE_CLASS + # so it returns the default storage. + + storage = get_video_image_storage() + storage_class = storage.__class__ + + self.assertEqual( + 'django.core.files.storage.filesystem.FileSystemStorage', + f"{storage_class.__module__}.{storage_class.__name__}", + ) + + @override_settings(VIDEO_IMAGE_SETTINGS={ + 'STORAGE_CLASS': 'storages.backends.s3boto3.S3Boto3Storage', + 'STORAGE_KWARGS': { + 'bucket_name': 'test', + 'default_acl': 'public', + 'location': 'abc/def' + } + }) + def test_video_image_backend_with_params(self): + storage = get_video_image_storage() + self.assertIsInstance(storage, S3Boto3Storage) + self.assertEqual(storage.bucket_name, "test") + self.assertEqual(storage.default_acl, 'public') + self.assertEqual(storage.location, "abc/def") + + def test_video_image_without_storages_settings(self): + # Remove VIDEO_IMAGE_SETTINGS from settings safely + if hasattr(settings, 'VIDEO_IMAGE_SETTINGS'): + del settings.VIDEO_IMAGE_SETTINGS + + storage = get_video_image_storage() + storage_class = storage.__class__ + + self.assertEqual( + 'django.core.files.storage.filesystem.FileSystemStorage', + f"{storage_class.__module__}.{storage_class.__name__}", + ) + + def test_video_transcript_backend(self): + # settings file contains the `VIDEO_TRANSCRIPTS_SETTINGS` but dont'have STORAGE_CLASS + # so it returns the default storage. + + storage = get_video_transcript_storage() + storage_class = storage.__class__ + + self.assertEqual( + 'django.core.files.storage.filesystem.FileSystemStorage', + f"{storage_class.__module__}.{storage_class.__name__}", + ) + + @override_settings(VIDEO_TRANSCRIPTS_SETTINGS={ + 'STORAGE_CLASS': 'storages.backends.s3boto3.S3Boto3Storage', + 'STORAGE_KWARGS': { + 'bucket_name': 'test', + 'default_acl': 'private', + 'location': 'abc/' + } + }) + def test_transcript_storage_backend_with_params(self): + storage = get_video_transcript_storage() + self.assertIsInstance(storage, S3Boto3Storage) + self.assertEqual(storage.bucket_name, "test") + self.assertEqual(storage.default_acl, 'private') + self.assertEqual(storage.location, 'abc/') + + def test_video_transcript_without_storages_settings(self): + # Remove VIDEO_TRANSCRIPTS_SETTINGS from settings. + if hasattr(settings, 'VIDEO_TRANSCRIPTS_SETTINGS'): + del settings.VIDEO_TRANSCRIPTS_SETTINGS + + storage = get_video_transcript_storage() + storage_class = storage.__class__ + + self.assertEqual( + 'django.core.files.storage.filesystem.FileSystemStorage', + f"{storage_class.__module__}.{storage_class.__name__}", + ) diff --git a/edxval/utils.py b/edxval/utils.py index a65dce00..483bce94 100644 --- a/edxval/utils.py +++ b/edxval/utils.py @@ -7,7 +7,7 @@ from django.conf import settings from django.core.exceptions import ValidationError -from django.core.files.storage import get_storage_class +from django.utils.module_loading import import_string from fs.path import combine from pysrt import SubRipFile @@ -150,18 +150,39 @@ def video_image_path(video_image_instance, filename): # pylint:disable=unused-a return '{}{}'.format(settings.VIDEO_IMAGE_SETTINGS.get('DIRECTORY_PREFIX', ''), filename) +def get_configured_storage(settings_key): + """ + Generic function to return a configured Django storage backend + based on the settings dictionary at `settings_key`. This function falls + back to the `default` storage class if there is no `STORAGE_CLASS` entry + under the `setting_key` object. + """ + config = getattr(settings, settings_key, {}) + # Retrieve the storage class path and kwargs from the settings + storage_class_path = config.get('STORAGE_CLASS') + options = config.get('STORAGE_KWARGS', {}) + + # following code only runs for default storages + if not storage_class_path: + storage_class_path = ( + getattr(settings, 'DEFAULT_FILE_STORAGE', None) or + getattr(settings, 'STORAGES', {}).get('default', {}).get('BACKEND') or + 'django.core.files.storage.FileSystemStorage' + ) + + # For Django 5.x, pick options if available + options = getattr(settings, 'STORAGES', {}).get('default', {}).get('OPTIONS', {}) + + # Import the storage class dynamically + storage_class = import_string(storage_class_path) + return storage_class(**options) + + def get_video_image_storage(): """ Return the configured django storage backend. """ - if hasattr(settings, 'VIDEO_IMAGE_SETTINGS'): - return get_storage_class( - settings.VIDEO_IMAGE_SETTINGS.get('STORAGE_CLASS'), - )(**settings.VIDEO_IMAGE_SETTINGS.get('STORAGE_KWARGS', {})) - - # during edx-platform loading this method gets called but settings are not ready yet - # so in that case we will return default(FileSystemStorage) storage class instance - return get_storage_class()() + return get_configured_storage('VIDEO_IMAGE_SETTINGS') def video_transcript_path(video_transcript_instance, filename): # pylint:disable=unused-argument @@ -179,14 +200,7 @@ def get_video_transcript_storage(): """ Return the configured django storage backend for video transcripts. """ - if hasattr(settings, 'VIDEO_TRANSCRIPTS_SETTINGS'): - return get_storage_class( - settings.VIDEO_TRANSCRIPTS_SETTINGS.get('STORAGE_CLASS'), - )(**settings.VIDEO_TRANSCRIPTS_SETTINGS.get('STORAGE_KWARGS', {})) - - # during edx-platform loading this method gets called but settings are not ready yet - # so in that case we will return default(FileSystemStorage) storage class instance - return get_storage_class()() + return get_configured_storage('VIDEO_TRANSCRIPTS_SETTINGS') def create_file_in_fs(file_data, file_name, file_system, static_dir): diff --git a/pylintrc b/pylintrc index 3d02a0de..62b59a0c 100644 --- a/pylintrc +++ b/pylintrc @@ -64,7 +64,7 @@ # SERIOUSLY. # # ------------------------------ -# Generated by edx-lint version: 5.3.7 +# Generated by edx-lint version: 5.6.0 # ------------------------------ [MASTER] ignore = migrations @@ -286,7 +286,7 @@ disable = feature-toggle-needs-doc, illegal-waffle-usage, - logging-fstring-interpolation,,unicode-format-string, consider-using-with, consider-using-dict-items, django-not-configured, consider-iterating-dictionary, arguments-renamed, no-name-in-module, c-extension-no-member, use-dict-literal + logging-fstring-interpolation,,consider-using-with, consider-using-dict-items, django-not-configured, consider-iterating-dictionary, arguments-renamed, no-name-in-module, c-extension-no-member, use-dict-literal [REPORTS] output-format = text @@ -383,4 +383,4 @@ int-import-graph = [EXCEPTIONS] overgeneral-exceptions = builtins.Exception -# 9e382112fd455855f6d0f08cef2bdb5077da2d63 +# 45943ee539dbe5737a3e92f2b981411d8150123b diff --git a/pylintrc_tweaks b/pylintrc_tweaks index 99c2a782..a54f1442 100644 --- a/pylintrc_tweaks +++ b/pylintrc_tweaks @@ -7,5 +7,4 @@ max-line-length = 120 [MESSAGES CONTROL] -# Disable unicode-format-string -disable+ = ,unicode-format-string, consider-using-with, consider-using-dict-items, django-not-configured, consider-iterating-dictionary, arguments-renamed, no-name-in-module, c-extension-no-member, use-dict-literal +disable+ = ,consider-using-with, consider-using-dict-items, django-not-configured, consider-iterating-dictionary, arguments-renamed, no-name-in-module, c-extension-no-member, use-dict-literal diff --git a/requirements/ci.txt b/requirements/ci.txt index d290a813..968283d3 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -6,15 +6,15 @@ # cachetools==5.5.2 # via tox -certifi==2025.1.31 +certifi==2025.4.26 # via requests chardet==5.2.0 # via tox -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests colorama==0.4.6 # via tox -coverage[toml]==7.6.12 +coverage[toml]==7.8.0 # via coveralls coveralls==4.0.1 # via -r requirements/ci.in @@ -22,17 +22,17 @@ distlib==0.3.9 # via virtualenv docopt==0.6.2 # via coveralls -filelock==3.17.0 +filelock==3.18.0 # via # tox # virtualenv idna==3.10 # via requests -packaging==24.2 +packaging==25.0 # via # pyproject-api # tox -platformdirs==4.3.6 +platformdirs==4.3.7 # via # tox # virtualenv @@ -42,11 +42,11 @@ pyproject-api==1.9.0 # via tox requests==2.32.3 # via coveralls -tox==4.24.1 +tox==4.25.0 # via -r requirements/ci.in urllib3==2.2.3 # via # -c requirements/common_constraints.txt # requests -virtualenv==20.29.3 +virtualenv==20.30.0 # via tox diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ee0577a1..116e8bf2 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -10,8 +10,3 @@ # Common constraints for edx repos -c common_constraints.txt - -# 5.4.0 is breaking for Python 3.8 and 3.11 CI checks with error -# importlib.resources' has no attribute 'files' -# To be unpinned once edx-val moves to Python 3.12 -edx-lint==5.3.7 \ No newline at end of file diff --git a/requirements/dev.txt b/requirements/dev.txt index ff4c2d6c..cbee17b1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -6,13 +6,13 @@ # annotated-types==0.7.0 # via pydantic -anyio==4.8.0 +anyio==4.9.0 # via starlette appdirs==1.4.4 # via fs asgiref==3.8.1 # via django -astroid==3.3.8 +astroid==3.3.9 # via # pylint # pylint-celery @@ -22,7 +22,7 @@ cachetools==5.5.2 # via # -r requirements/base.in # tox -certifi==2025.1.31 +certifi==2025.4.26 # via requests cffi==1.17.1 # via @@ -34,7 +34,7 @@ chardet==5.2.0 # diff-cover # pysrt # tox -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests click==8.1.8 # via @@ -46,28 +46,26 @@ click==8.1.8 # uvicorn click-log==0.4.0 # via edx-lint -code-annotations==2.2.0 +code-annotations==2.3.0 # via # edx-lint # edx-toggles colorama==0.4.6 # via tox -coverage[toml]==7.6.12 +coverage[toml]==7.8.0 # via # -r requirements/test.in # coveralls # pytest-cov coveralls==4.0.1 # via -r requirements/ci.in -cryptography==44.0.2 - # via - # pyjwt - # secretstorage +cryptography==44.0.3 + # via pyjwt ddt==1.7.2 # via -r requirements/test.in -diff-cover==9.2.3 +diff-cover==9.2.4 # via -r requirements/dev.in -dill==0.3.9 +dill==0.4.0 # via pylint distlib==0.3.9 # via virtualenv @@ -91,14 +89,14 @@ django-crum==0.7.9 # edx-toggles django-model-utils==5.0.0 # via -r requirements/base.in -django-storages==1.14.5 +django-storages==1.14.6 # via -r requirements/base.in django-waffle==4.2.0 # via # edx-django-utils # edx-drf-extensions # edx-toggles -djangorestframework==3.15.2 +djangorestframework==3.16.0 # via # drf-jwt # edx-drf-extensions @@ -110,31 +108,29 @@ docutils==0.21.2 # via readme-renderer drf-jwt==1.19.2 # via edx-drf-extensions -edx-django-release-util==1.4.0 +edx-django-release-util==1.5.0 # via -r requirements/base.in -edx-django-utils==7.2.0 +edx-django-utils==7.4.0 # via # edx-drf-extensions # edx-toggles -edx-drf-extensions==10.5.0 +edx-drf-extensions==10.6.0 # via -r requirements/base.in -edx-lint==5.3.7 - # via - # -c requirements/constraints.txt - # -r requirements/quality.in -edx-opaque-keys==2.11.0 +edx-lint==5.6.0 + # via -r requirements/quality.in +edx-opaque-keys==3.0.0 # via edx-drf-extensions edx-toggles==5.3.0 # via -r requirements/base.in -fastapi==0.115.11 +fastapi==0.115.12 # via pact-python -filelock==3.17.0 +filelock==3.18.0 # via # tox # virtualenv fs==2.4.16 # via -r requirements/test.in -h11==0.14.0 +h11==0.16.0 # via uvicorn id==1.5.0 # via twine @@ -143,9 +139,9 @@ idna==3.10 # anyio # requests # yarl -importlib-metadata==8.6.1 +importlib-metadata==8.7.0 # via keyring -iniconfig==2.0.0 +iniconfig==2.1.0 # via pytest isort==6.0.1 # via @@ -157,17 +153,13 @@ jaraco-context==6.0.1 # via keyring jaraco-functools==4.1.0 # via keyring -jeepney==0.9.0 - # via - # keyring - # secretstorage jinja2==3.1.6 # via # code-annotations # diff-cover keyring==25.6.0 # via twine -lxml==5.3.1 +lxml==5.4.0 # via -r requirements/base.in markdown-it-py==3.0.0 # via rich @@ -179,17 +171,17 @@ mdurl==0.1.2 # via markdown-it-py mock==5.2.0 # via -r requirements/test.in -more-itertools==10.6.0 +more-itertools==10.7.0 # via # jaraco-classes # jaraco-functools -multidict==6.1.0 +multidict==6.4.3 # via yarl -newrelic==10.7.0 +newrelic==10.11.0 # via edx-django-utils nh3==0.2.21 # via readme-renderer -packaging==24.2 +packaging==25.0 # via # pyproject-api # pytest @@ -199,9 +191,9 @@ pact-python==2.3.1 # via -r requirements/test.in pbr==6.1.1 # via stevedore -pillow==11.1.0 +pillow==11.2.1 # via -r requirements/base.in -platformdirs==4.3.6 +platformdirs==4.3.7 # via # pylint # tox @@ -211,19 +203,19 @@ pluggy==1.5.0 # diff-cover # pytest # tox -propcache==0.3.0 +propcache==0.3.1 # via yarl psutil==6.1.1 # via # edx-django-utils # pact-python -pycodestyle==2.12.1 +pycodestyle==2.13.0 # via -r requirements/quality.in pycparser==2.22 # via cffi -pydantic==2.10.6 +pydantic==2.11.4 # via fastapi -pydantic-core==2.27.2 +pydantic-core==2.33.2 # via pydantic pydocstyle==6.3.0 # via -r requirements/quality.in @@ -236,7 +228,7 @@ pyjwt[crypto]==2.10.1 # via # drf-jwt # edx-drf-extensions -pylint==3.3.4 +pylint==3.3.7 # via # edx-lint # pylint-celery @@ -250,7 +242,7 @@ pylint-plugin-utils==0.8.2 # via # pylint-celery # pylint-django -pymongo==4.11.2 +pymongo==4.12.1 # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils @@ -262,9 +254,9 @@ pytest==8.3.5 # via # pytest-cov # pytest-django -pytest-cov==6.0.0 +pytest-cov==6.1.1 # via -r requirements/test.in -pytest-django==4.10.0 +pytest-django==4.11.1 # via -r requirements/test.in python-slugify==8.0.4 # via code-annotations @@ -286,14 +278,12 @@ requests==2.32.3 # twine requests-toolbelt==1.0.0 # via twine -responses==0.25.6 +responses==0.25.7 # via -r requirements/test.in rfc3986==2.0.0 # via twine -rich==13.9.4 +rich==14.0.0 # via twine -secretstorage==3.3.3 - # via keyring semantic-version==2.10.0 # via edx-drf-extensions six==1.17.0 @@ -308,7 +298,7 @@ snowballstemmer==2.2.0 # via pydocstyle sqlparse==0.5.3 # via django -starlette==0.46.0 +starlette==0.46.2 # via fastapi stevedore==5.4.1 # via @@ -319,28 +309,31 @@ text-unidecode==1.3 # via python-slugify tomlkit==0.13.2 # via pylint -tox==4.24.1 +tox==4.25.0 # via -r requirements/ci.in twine==6.1.0 # via -r requirements/quality.in -typing-extensions==4.12.2 +typing-extensions==4.13.2 # via # anyio # edx-opaque-keys # fastapi # pydantic # pydantic-core + # typing-inspection +typing-inspection==0.4.0 + # via pydantic urllib3==2.2.3 # via # -c requirements/common_constraints.txt # requests # responses # twine -uvicorn==0.34.0 +uvicorn==0.34.2 # via pact-python -virtualenv==20.29.3 +virtualenv==20.30.0 # via tox -yarl==1.18.3 +yarl==1.20.0 # via pact-python zipp==3.21.0 # via importlib-metadata diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index 5461e435..1dd71b5e 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -8,7 +8,7 @@ build==1.2.2.post1 # via pip-tools click==8.1.8 # via pip-tools -packaging==24.2 +packaging==25.0 # via build pip-tools==7.4.1 # via -r requirements/pip-tools.in diff --git a/requirements/pip.txt b/requirements/pip.txt index 38e7a677..5d3482a4 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -10,7 +10,7 @@ wheel==0.45.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via - # -c /home/runner/work/edx-val/edx-val/requirements/common_constraints.txt + # -c requirements/common_constraints.txt # -r requirements/pip.in -setuptools==75.8.2 +setuptools==80.3.1 # via -r requirements/pip.in diff --git a/requirements/quality.txt b/requirements/quality.txt index 4df792cc..63b1b541 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -6,13 +6,13 @@ # annotated-types==0.7.0 # via pydantic -anyio==4.8.0 +anyio==4.9.0 # via starlette appdirs==1.4.4 # via fs asgiref==3.8.1 # via django -astroid==3.3.8 +astroid==3.3.9 # via # pylint # pylint-celery @@ -20,7 +20,7 @@ backports-tarfile==1.2.0 # via jaraco-context cachetools==5.5.2 # via -r requirements/base.in -certifi==2025.1.31 +certifi==2025.4.26 # via requests cffi==1.17.1 # via @@ -29,7 +29,7 @@ cffi==1.17.1 # pynacl chardet==5.2.0 # via pysrt -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests click==8.1.8 # via @@ -41,21 +41,19 @@ click==8.1.8 # uvicorn click-log==0.4.0 # via edx-lint -code-annotations==2.2.0 +code-annotations==2.3.0 # via # edx-lint # edx-toggles -coverage[toml]==7.6.12 +coverage[toml]==7.8.0 # via # -r requirements/test.in # pytest-cov -cryptography==44.0.2 - # via - # pyjwt - # secretstorage +cryptography==44.0.3 + # via pyjwt ddt==1.7.2 # via -r requirements/test.in -dill==0.3.9 +dill==0.4.0 # via pylint django==4.2.20 # via @@ -77,14 +75,14 @@ django-crum==0.7.9 # edx-toggles django-model-utils==5.0.0 # via -r requirements/base.in -django-storages==1.14.5 +django-storages==1.14.6 # via -r requirements/base.in django-waffle==4.2.0 # via # edx-django-utils # edx-drf-extensions # edx-toggles -djangorestframework==3.15.2 +djangorestframework==3.16.0 # via # drf-jwt # edx-drf-extensions @@ -94,27 +92,25 @@ docutils==0.21.2 # via readme-renderer drf-jwt==1.19.2 # via edx-drf-extensions -edx-django-release-util==1.4.0 +edx-django-release-util==1.5.0 # via -r requirements/base.in -edx-django-utils==7.2.0 +edx-django-utils==7.4.0 # via # edx-drf-extensions # edx-toggles -edx-drf-extensions==10.5.0 +edx-drf-extensions==10.6.0 # via -r requirements/base.in -edx-lint==5.3.7 - # via - # -c requirements/constraints.txt - # -r requirements/quality.in -edx-opaque-keys==2.11.0 +edx-lint==5.6.0 + # via -r requirements/quality.in +edx-opaque-keys==3.0.0 # via edx-drf-extensions edx-toggles==5.3.0 # via -r requirements/base.in -fastapi==0.115.11 +fastapi==0.115.12 # via pact-python fs==2.4.16 # via -r requirements/test.in -h11==0.14.0 +h11==0.16.0 # via uvicorn id==1.5.0 # via twine @@ -123,9 +119,9 @@ idna==3.10 # anyio # requests # yarl -importlib-metadata==8.6.1 +importlib-metadata==8.7.0 # via keyring -iniconfig==2.0.0 +iniconfig==2.1.0 # via pytest isort==6.0.1 # via @@ -137,15 +133,11 @@ jaraco-context==6.0.1 # via keyring jaraco-functools==4.1.0 # via keyring -jeepney==0.9.0 - # via - # keyring - # secretstorage jinja2==3.1.6 # via code-annotations keyring==25.6.0 # via twine -lxml==5.3.1 +lxml==5.4.0 # via -r requirements/base.in markdown-it-py==3.0.0 # via rich @@ -157,17 +149,17 @@ mdurl==0.1.2 # via markdown-it-py mock==5.2.0 # via -r requirements/test.in -more-itertools==10.6.0 +more-itertools==10.7.0 # via # jaraco-classes # jaraco-functools -multidict==6.1.0 +multidict==6.4.3 # via yarl -newrelic==10.7.0 +newrelic==10.11.0 # via edx-django-utils nh3==0.2.21 # via readme-renderer -packaging==24.2 +packaging==25.0 # via # pytest # twine @@ -175,25 +167,25 @@ pact-python==2.3.1 # via -r requirements/test.in pbr==6.1.1 # via stevedore -pillow==11.1.0 +pillow==11.2.1 # via -r requirements/base.in -platformdirs==4.3.6 +platformdirs==4.3.7 # via pylint pluggy==1.5.0 # via pytest -propcache==0.3.0 +propcache==0.3.1 # via yarl psutil==6.1.1 # via # edx-django-utils # pact-python -pycodestyle==2.12.1 +pycodestyle==2.13.0 # via -r requirements/quality.in pycparser==2.22 # via cffi -pydantic==2.10.6 +pydantic==2.11.4 # via fastapi -pydantic-core==2.27.2 +pydantic-core==2.33.2 # via pydantic pydocstyle==6.3.0 # via -r requirements/quality.in @@ -205,7 +197,7 @@ pyjwt[crypto]==2.10.1 # via # drf-jwt # edx-drf-extensions -pylint==3.3.4 +pylint==3.3.7 # via # edx-lint # pylint-celery @@ -219,7 +211,7 @@ pylint-plugin-utils==0.8.2 # via # pylint-celery # pylint-django -pymongo==4.11.2 +pymongo==4.12.1 # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils @@ -229,9 +221,9 @@ pytest==8.3.5 # via # pytest-cov # pytest-django -pytest-cov==6.0.0 +pytest-cov==6.1.1 # via -r requirements/test.in -pytest-django==4.10.0 +pytest-django==4.11.1 # via -r requirements/test.in python-slugify==8.0.4 # via code-annotations @@ -252,14 +244,12 @@ requests==2.32.3 # twine requests-toolbelt==1.0.0 # via twine -responses==0.25.6 +responses==0.25.7 # via -r requirements/test.in rfc3986==2.0.0 # via twine -rich==13.9.4 +rich==14.0.0 # via twine -secretstorage==3.3.3 - # via keyring semantic-version==2.10.0 # via edx-drf-extensions six==1.17.0 @@ -274,7 +264,7 @@ snowballstemmer==2.2.0 # via pydocstyle sqlparse==0.5.3 # via django -starlette==0.46.0 +starlette==0.46.2 # via fastapi stevedore==5.4.1 # via @@ -287,22 +277,25 @@ tomlkit==0.13.2 # via pylint twine==6.1.0 # via -r requirements/quality.in -typing-extensions==4.12.2 +typing-extensions==4.13.2 # via # anyio # edx-opaque-keys # fastapi # pydantic # pydantic-core + # typing-inspection +typing-inspection==0.4.0 + # via pydantic urllib3==2.2.3 # via # -c requirements/common_constraints.txt # requests # responses # twine -uvicorn==0.34.0 +uvicorn==0.34.2 # via pact-python -yarl==1.18.3 +yarl==1.20.0 # via pact-python zipp==3.21.0 # via importlib-metadata diff --git a/requirements/test.in b/requirements/test.in index 91fe06ce..6d4fee44 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -2,6 +2,7 @@ -c constraints.txt +boto3 coverage ddt fs diff --git a/requirements/test.txt b/requirements/test.txt index 387269e4..70844c77 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -6,15 +6,21 @@ # annotated-types==0.7.0 # via pydantic -anyio==4.8.0 +anyio==4.9.0 # via starlette appdirs==1.4.4 # via fs asgiref==3.8.1 # via django +boto3==1.38.8 + # via -r requirements/test.in +botocore==1.38.8 + # via + # boto3 + # s3transfer cachetools==5.5.2 # via -r requirements/base.in -certifi==2025.1.31 +certifi==2025.4.26 # via requests cffi==1.17.1 # via @@ -23,7 +29,7 @@ cffi==1.17.1 # pynacl chardet==5.2.0 # via pysrt -charset-normalizer==3.4.1 +charset-normalizer==3.4.2 # via requests click==8.1.8 # via @@ -31,13 +37,13 @@ click==8.1.8 # edx-django-utils # pact-python # uvicorn -code-annotations==2.2.0 +code-annotations==2.3.0 # via edx-toggles -coverage[toml]==7.6.12 +coverage[toml]==7.8.0 # via # -r requirements/test.in # pytest-cov -cryptography==44.0.2 +cryptography==44.0.3 # via pyjwt ddt==1.7.2 # via -r requirements/test.in @@ -60,14 +66,14 @@ django-crum==0.7.9 # edx-toggles django-model-utils==5.0.0 # via -r requirements/base.in -django-storages==1.14.5 +django-storages==1.14.6 # via -r requirements/base.in django-waffle==4.2.0 # via # edx-django-utils # edx-drf-extensions # edx-toggles -djangorestframework==3.15.2 +djangorestframework==3.16.0 # via # drf-jwt # edx-drf-extensions @@ -75,54 +81,54 @@ dnspython==2.7.0 # via pymongo drf-jwt==1.19.2 # via edx-drf-extensions -edx-django-release-util==1.4.0 +edx-django-release-util==1.5.0 # via -r requirements/base.in -edx-django-utils==7.2.0 +edx-django-utils==7.4.0 # via # edx-drf-extensions # edx-toggles -edx-drf-extensions==10.5.0 +edx-drf-extensions==10.6.0 # via -r requirements/base.in -edx-opaque-keys==2.11.0 +edx-opaque-keys==3.0.0 # via edx-drf-extensions edx-toggles==5.3.0 # via -r requirements/base.in -fastapi==0.115.11 +fastapi==0.115.12 # via pact-python fs==2.4.16 # via -r requirements/test.in -h11==0.14.0 +h11==0.16.0 # via uvicorn idna==3.10 # via # anyio # requests # yarl -iniconfig==2.0.0 +iniconfig==2.1.0 # via pytest jinja2==3.1.6 # via code-annotations -lxml==5.3.1 +lxml==5.4.0 # via -r requirements/base.in markupsafe==3.0.2 # via jinja2 mock==5.2.0 # via -r requirements/test.in -multidict==6.1.0 +multidict==6.4.3 # via yarl -newrelic==10.7.0 +newrelic==10.11.0 # via edx-django-utils -packaging==24.2 +packaging==25.0 # via pytest pact-python==2.3.1 # via -r requirements/test.in pbr==6.1.1 # via stevedore -pillow==11.1.0 +pillow==11.2.1 # via -r requirements/base.in pluggy==1.5.0 # via pytest -propcache==0.3.0 +propcache==0.3.1 # via yarl psutil==6.1.1 # via @@ -130,15 +136,15 @@ psutil==6.1.1 # pact-python pycparser==2.22 # via cffi -pydantic==2.10.6 +pydantic==2.11.4 # via fastapi -pydantic-core==2.27.2 +pydantic-core==2.33.2 # via pydantic pyjwt[crypto]==2.10.1 # via # drf-jwt # edx-drf-extensions -pymongo==4.11.2 +pymongo==4.12.1 # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils @@ -148,9 +154,9 @@ pytest==8.3.5 # via # pytest-cov # pytest-django -pytest-cov==6.0.0 +pytest-cov==6.1.1 # via -r requirements/test.in -pytest-django==4.10.0 +pytest-django==4.11.1 # via -r requirements/test.in python-slugify==8.0.4 # via code-annotations @@ -164,8 +170,10 @@ requests==2.32.3 # edx-drf-extensions # pact-python # responses -responses==0.25.6 +responses==0.25.7 # via -r requirements/test.in +s3transfer==0.12.0 + # via boto3 semantic-version==2.10.0 # via edx-drf-extensions six==1.17.0 @@ -177,7 +185,7 @@ sniffio==1.3.1 # via anyio sqlparse==0.5.3 # via django -starlette==0.46.0 +starlette==0.46.2 # via fastapi stevedore==5.4.1 # via @@ -186,21 +194,24 @@ stevedore==5.4.1 # edx-opaque-keys text-unidecode==1.3 # via python-slugify -typing-extensions==4.12.2 +typing-extensions==4.13.2 # via # anyio # edx-opaque-keys # fastapi # pydantic # pydantic-core + # typing-inspection +typing-inspection==0.4.0 + # via pydantic urllib3==2.2.3 # via # -c requirements/common_constraints.txt # requests # responses -uvicorn==0.34.0 +uvicorn==0.34.2 # via pact-python -yarl==1.18.3 +yarl==1.20.0 # via pact-python # The following packages are considered to be unsafe in a requirements file: diff --git a/setup.py b/setup.py index b51c9efe..2e85c87b 100644 --- a/setup.py +++ b/setup.py @@ -87,8 +87,10 @@ def get_version(*file_paths): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Framework :: Django', 'Framework :: Django :: 4.2', + 'Framework :: Django :: 5.2', ], packages=PACKAGES, install_requires=load_requirements('requirements/base.in'), diff --git a/tox.ini b/tox.ini index d78a48cc..675f98da 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,10 @@ [tox] -envlist = py{38,311}-django{42}, quality +envlist = py{311,312}-django{42,52}, quality [testenv] deps = - django42: Django>=4.2,<4.3 + django42: Django>=4.2,<5.0 + django52: Django>=5.2,<6.0 -r{toxinidir}/requirements/test.txt commands = python -Wd -m pytest {posargs}