Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-django-tastypie for openSUSE:Factory checked in at 2022-02-26 17:02:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-django-tastypie (Old) and /work/SRC/openSUSE:Factory/.python-django-tastypie.new.1958 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-tastypie" Sat Feb 26 17:02:12 2022 rev:17 rq:957670 version:0.14.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-django-tastypie/python-django-tastypie.changes 2021-06-09 21:53:14.706575593 +0200 +++ /work/SRC/openSUSE:Factory/.python-django-tastypie.new.1958/python-django-tastypie.changes 2022-02-26 17:02:43.859542078 +0100 @@ -1,0 +2,8 @@ +Fri Feb 25 21:59:18 UTC 2022 - Matej Cepl <mc...@suse.com> + +- Update to 0.14.4: + - Django 4.0 support +- Fix testing of the package. +- Remove upstreamed patch merged_pr_1624_chunk.patch + +------------------------------------------------------------------- Old: ---- merged_pr_1624_chunk.patch v0.14.3.tar.gz New: ---- v0.14.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-django-tastypie.spec ++++++ --- /var/tmp/diff_new_pack.PQac9b/_old 2022-02-26 17:02:44.351542156 +0100 +++ /var/tmp/diff_new_pack.PQac9b/_new 2022-02-26 17:02:44.359542158 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-django-tastypie # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,14 +18,12 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-django-tastypie -Version: 0.14.3 +Version: 0.14.4 Release: 0 Summary: A webservice API framework layer for Django License: BSD-3-Clause URL: https://github.com/django-tastypie/django-tastypie Source: https://github.com/django-tastypie/django-tastypie/archive/v%{version}.tar.gz -# PATCH-FIX-UPSTREAM merged_pr_1624_chunk.patch -- based on PR 1624 -Patch0: merged_pr_1624_chunk.patch BuildRequires: %{python_module Django >= 1.11.0} BuildRequires: %{python_module PyYAML} BuildRequires: %{python_module biplist} @@ -53,7 +51,8 @@ %prep %setup -q -n django-tastypie-%{version} -%patch0 -p1 +%autopatch -p1 + # https://github.com/django-tastypie/django-tastypie/issues/1617 sed -Ei 's/(test_apikey_and_authentication_enforce_user|test_is_authenticated)/_\1/' tests/core/tests/authentication.py @@ -67,16 +66,16 @@ %check # The tests are doing what is specified in tox.ini %{python_expand export PYTHONPATH=${PWD}:${PWD}/tests/ -django-admin.py-%{$python_bin_suffix} test -p '*' core.tests --settings=settings_core -django-admin.py-%{$python_bin_suffix} test basic.tests --settings=settings_basic -django-admin.py-%{$python_bin_suffix} test related_resource.tests --settings=settings_related -django-admin.py-%{$python_bin_suffix} test alphanumeric.tests --settings=settings_alphanumeric -django-admin.py-%{$python_bin_suffix} test authorization.tests --settings=settings_authorization -django-admin.py-%{$python_bin_suffix} test content_gfk.tests --settings=settings_content_gfk -django-admin.py-%{$python_bin_suffix} test customuser.tests --settings=settings_customuser -django-admin.py-%{$python_bin_suffix} test namespaced.tests --settings=settings_namespaced -django-admin.py-%{$python_bin_suffix} test slashless.tests --settings=settings_slashless -django-admin.py-%{$python_bin_suffix} test validation.tests --settings=settings_validation +django-admin-%{$python_bin_suffix} test -v 3 -p '*' core.tests --settings=settings_core +django-admin-%{$python_bin_suffix} test -v 3 basic.tests --settings=settings_basic +django-admin-%{$python_bin_suffix} test -v 3 related_resource.tests --settings=settings_related +django-admin-%{$python_bin_suffix} test -v 3 alphanumeric.tests --settings=settings_alphanumeric +django-admin-%{$python_bin_suffix} test -v 3 authorization.tests --settings=settings_authorization +django-admin-%{$python_bin_suffix} test -v 3 content_gfk.tests --settings=settings_content_gfk +django-admin-%{$python_bin_suffix} test -v 3 customuser.tests --settings=settings_customuser +django-admin-%{$python_bin_suffix} test -v 3 namespaced.tests --settings=settings_namespaced +django-admin-%{$python_bin_suffix} test -v 3 slashless.tests --settings=settings_slashless +django-admin-%{$python_bin_suffix} test -v 3 validation.tests --settings=settings_validation } %files %{python_files} ++++++ v0.14.3.tar.gz -> v0.14.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/.github/workflows/python-package.yml new/django-tastypie-0.14.4/.github/workflows/python-package.yml --- old/django-tastypie-0.14.3/.github/workflows/python-package.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/django-tastypie-0.14.4/.github/workflows/python-package.yml 2022-01-04 02:18:10.000000000 +0100 @@ -0,0 +1,59 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Test Matrix + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + django-version: ["2.2", "3.2", "4.0"] # Todo: add "dev" back + exclude: + - python-version: "3.6" + django-version: "4.0" +# - python-version: "3.6" +# django-version: "dev" + - python-version: "3.7" + django-version: "4.0" +# - python-version: "3.7" +# django-version: "dev" + + steps: + - uses: actions/checkout@v2 + - name: Install OS dependencies + run: | + sudo apt install -y binutils libproj-dev gdal-bin libsqlite3-mod-spatialite + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel virtualenv tox coveralls + - name: Erase cached coverage + run: | + coverage erase + - name: Flake8 + run: | + tox -e py${{matrix.python-version}}-flake8 + - name: Docs + run: | + tox -e py${{matrix.python-version}}-docs + - name: Test with Tox + run: | + tox -e py${{matrix.python-version}}-dj${{matrix.django-version}} + - name: Upload coverage data to coveralls.io + run: coveralls --service=github + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/.travis.yml new/django-tastypie-0.14.4/.travis.yml --- old/django-tastypie-0.14.3/.travis.yml 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,68 +0,0 @@ -sudo: false -dist: bionic - -language: python - -python: - - "2.7" - - "3.5" - - "3.6" - - "3.7" - -env: - - MODE=flake8 - - MODE=flake8-strict - - MODE=docs - - DJANGO_VERSION=dj111 - - DJANGO_VERSION=dj22 - - DJANGO_VERSION=dj30 - - DJANGO_VERSION=djdev - -matrix: - allow_failures: - - env: DJANGO_VERSION=djdev - - env: MODE=flake8-strict - exclude: - - python: "2.7" - env: DJANGO_VERSION=djdev - - python: "2.7" - env: DJANGO_VERSION=dj22 - - python: "2.7" - env: DJANGO_VERSION=dj30 - - python: "3.5" - env: DJANGO_VERSION=dj30 - - python: "3.5" - env: DJANGO_VERSION=djdev - -before_install: - - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 762E3157 - -addons: - apt: - packages: - - binutils - - libproj-dev - - gdal-bin - - libsqlite3-mod-spatialite - -cache: - directories: - - $HOME/.cache/pip - -before_cache: - - rm -f $HOME/.cache/pip/log/debug.log - -# command to install dependencies -install: - - pip install -U pip - - pip install -U wheel virtualenv - - pip install tox coveralls - -after_success: - - coveralls - -# command to run tests -script: - - apt list --installed - - coverage erase - - tox -e py${TRAVIS_PYTHON_VERSION/./}-${DJANGO_VERSION}${MODE} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/README.rst new/django-tastypie-0.14.4/README.rst --- old/django-tastypie-0.14.3/README.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/README.rst 2022-01-04 02:18:10.000000000 +0100 @@ -6,8 +6,8 @@ :target: https://django-tastypie.readthedocs.io/ :alt: Docs -.. image:: https://travis-ci.org/django-tastypie/django-tastypie.svg?branch=master - :target: https://travis-ci.org/django-tastypie/django-tastypie +.. image:: https://github.com/django-tastypie/django-tastypie/actions/workflows/python-package.yml/badge.svg + :target: https://github.com/django-tastypie/django-tastypie/actions :alt: CI .. image:: https://coveralls.io/repos/django-tastypie/django-tastypie/badge.svg?service=github @@ -34,8 +34,8 @@ Core ---- -* Python 2.7+ or Python 3.4+ (Whatever is supported by your version of Django) -* Django 1.11, 2.2 (LTS releases) or Django 3.0 (latest release) +* Python 3.6+, preferably 3.8+ (Whatever is supported by your version of Django) +* Django 2.2, 3.2 (LTS releases) or Django 4.0 (latest release) * dateutil (http://labix.org/python-dateutil) >= 2.1 Format Support @@ -71,7 +71,7 @@ # urls.py # ======= - from django.conf.urls import url, include + from django.urls.conf import re_path, include from tastypie.api import Api from myapp.api import EntryResource @@ -80,7 +80,7 @@ urlpatterns = [ # The normal jazz here then... - url(r'^api/', include(v1_api.urls)), + re_path(r'^api/', include(v1_api.urls)), ] That gets you a fully working, read-write API for the ``Entry`` model that diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/api.rst new/django-tastypie-0.14.4/docs/api.rst --- old/django-tastypie-0.14.3/docs/api.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/api.rst 2022-01-04 02:18:10.000000000 +0100 @@ -17,7 +17,7 @@ A sample api definition might look something like (usually located in a URLconf):: - from django.conf.urls import url, include + from django.urls.conf import re_path, include from tastypie.api import Api from myapp.api.resources import UserResource, EntryResource @@ -27,7 +27,7 @@ # Standard bits... urlpatterns = [ - url(r'^api/', include(v1_api.urls)), + re_path(r'^api/', include(v1_api.urls)), ] For namespaced urls see :ref:`namespaces` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/code/myproject/urls.py new/django-tastypie-0.14.4/docs/code/myproject/urls.py --- old/django-tastypie-0.14.3/docs/code/myproject/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/code/myproject/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from django.contrib import admin urlpatterns = [ @@ -6,5 +6,5 @@ # url(r'^$', 'myproject.views.home', name='home'), # url(r'^blog/', include('blog.urls')), - url(r'^admin/', include(admin.site.urls)), + re_path(r'^admin/', include(admin.site.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/cookbook.rst new/django-tastypie-0.14.4/docs/cookbook.rst --- old/django-tastypie-0.14.3/docs/cookbook.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/cookbook.rst 2022-01-04 02:18:10.000000000 +0100 @@ -242,7 +242,7 @@ .. testcode:: # myapp/api/resources.py - from django.conf.urls import url + from django.urls.conf import re_path from django.contrib.auth.models import User @@ -253,7 +253,7 @@ def prepend_urls(self): return [ - url(r"^(?P<resource_name>%s)/(?P<username>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), + re_path(r"^(?P<resource_name>%s)/(?P<username>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), ] .. testoutput:: @@ -285,13 +285,13 @@ return super(MyModelResource, self).dispatch(request_type, request, **kwargs) # urls.py - from django.conf.urls import url, include + from django.urls.conf import re_path, include mymodel_resource = MyModelResource() urlpatterns = [ # The normal jazz here, then... - url(r'^api/(?P<username>\w+)/', include(mymodel_resource.urls)), + re_path(r'^api/(?P<username>\w+)/', include(mymodel_resource.urls)), ] .. testoutput:: @@ -320,7 +320,7 @@ def prepend_urls(self): return [ - url(r"^(?P<resource_name>%s)/(?P<pk>\w[\w/-]*)/children%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('get_children'), name="api_get_children"), + re_path(r"^(?P<resource_name>%s)/(?P<pk>\w[\w/-]*)/children%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('get_children'), name="api_get_children"), ] def get_children(self, request, **kwargs): @@ -350,7 +350,7 @@ We leave the CRUD methods of the resource alone, choosing to add a new endpoint at ``/api/v1/notes/search/``:: - from django.conf.urls import url, include + from django.urls.conf import re_path, include from django.core.paginator import Paginator, InvalidPage from django.http import Http404 from haystack.query import SearchQuerySet @@ -366,7 +366,7 @@ def prepend_urls(self): return [ - url(r"^(?P<resource_name>%s)/search%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('get_search'), name="api_get_search"), + re_path(r"^(?P<resource_name>%s)/search%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('get_search'), name="api_get_search"), ] def get_search(self, request, **kwargs): @@ -560,10 +560,10 @@ the response format as a file extension, e.g. /api/v1/users.json """ return [ - url(r"^(?P<resource_name>%s)\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('dispatch_list'), name="api_dispatch_list"), - url(r"^(?P<resource_name>%s)/schema\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('get_schema'), name="api_get_schema"), - url(r"^(?P<resource_name>%s)/set/(?P<pk_list>\w[\w/;-]*)\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('get_multiple'), name="api_get_multiple"), - url(r"^(?P<resource_name>%s)/(?P<pk>\w[\w/-]*)\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), + re_path(r"^(?P<resource_name>%s)\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('dispatch_list'), name="api_dispatch_list"), + re_path(r"^(?P<resource_name>%s)/schema\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('get_schema'), name="api_get_schema"), + re_path(r"^(?P<resource_name>%s)/set/(?P<pk_list>\w[\w/;-]*)\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('get_multiple'), name="api_get_multiple"), + re_path(r"^(?P<resource_name>%s)/(?P<pk>\w[\w/-]*)\.(?P<format>\w+)$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), ] def determine_format(self, request): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/index.rst new/django-tastypie-0.14.4/docs/index.rst --- old/django-tastypie-0.14.3/docs/index.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/index.rst 2022-01-04 02:18:10.000000000 +0100 @@ -61,7 +61,7 @@ 4. In your root URLconf, add the following code (around where the admin code might be):: - from django.conf.urls import url, include + from django.urls.conf import re_path, include from tastypie.api import Api from my_app.api.resources import MyModelResource @@ -71,7 +71,7 @@ urlpatterns = [ # ...more URLconf bits here... # Then add: - url(r'^api/', include(v1_api.urls)), + re_path(r'^api/', include(v1_api.urls)), ] 5. Hit http://localhost:8000/api/v1/?format=json in your browser! @@ -83,8 +83,8 @@ Core ---- -* Python 2.7+ or Python 3.4+ (Whatever is supported by your version of Django) -* Django 1.11, 2.2 (LTS releases) or Django 3.0 (latest release) +* Python 3.6+, preferably 3.8+ (Whatever is supported by your version of Django) +* Django 2.2, 3.2 (LTS releases) or Django 4.0 (latest release) * dateutil (http://labix.org/python-dateutil) >= 2.1 Format Support diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/interacting.rst new/django-tastypie-0.14.4/docs/interacting.rst --- old/django-tastypie-0.14.3/docs/interacting.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/interacting.rst 2022-01-04 02:18:10.000000000 +0100 @@ -45,7 +45,7 @@ # urls.py - from django.conf.urls import url, include + from django.urls.conf import re_path, include from tastypie.api import Api from myapp.api.resources import EntryResource, UserResource @@ -55,8 +55,8 @@ urlpatterns = [ # The normal jazz here... - url(r'^blog/', include('myapp.urls')), - url(r'^api/', include(v1_api.urls)), + re_path(r'^blog/', include('myapp.urls')), + re_path(r'^api/', include(v1_api.urls)), ] Let's fire up a shell & start exploring the API! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/namespaces.rst new/django-tastypie-0.14.4/docs/namespaces.rst --- old/django-tastypie-0.14.3/docs/namespaces.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/namespaces.rst 2022-01-04 02:18:10.000000000 +0100 @@ -8,7 +8,7 @@ A sample definition of your API in this case would be something like:: - from django.conf.urls import url, include + from django.urls.conf import re_path, include from tastypie.api import NamespacedApi from my_application.api.resources import NamespacedUserResource @@ -16,7 +16,7 @@ api.register(NamespacedUserResource()) urlpatterns = [ - url(r'^api/', include(api.urls, namespace='special')), + re_path(r'^api/', include(api.urls, namespace='special')), ] And your model resource:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/release_notes/index.rst new/django-tastypie-0.14.4/docs/release_notes/index.rst --- old/django-tastypie-0.14.3/docs/release_notes/index.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/release_notes/index.rst 2022-01-04 02:18:10.000000000 +0100 @@ -5,6 +5,7 @@ :maxdepth: 1 dev + v0.14.4 v0.14.3 v0.14.2 v0.14.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/release_notes/v0.14.4.rst new/django-tastypie-0.14.4/docs/release_notes/v0.14.4.rst --- old/django-tastypie-0.14.3/docs/release_notes/v0.14.4.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/release_notes/v0.14.4.rst 2022-01-04 02:18:10.000000000 +0100 @@ -0,0 +1,9 @@ +v0.14.4 +======= + +:date: 2022-01-03 + +Added support for Django 4.0. +Drops explicit support for Django 3.0, 3.1 (non-LTS). +Drops all support for Python 2. +Fixes a TZ bug in date-formatted Retry-After responses from throttling revealed by Django 4.0's switch to Zoneinfo. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/docs/tutorial.rst new/django-tastypie-0.14.4/docs/tutorial.rst --- old/django-tastypie-0.14.3/docs/tutorial.rst 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/docs/tutorial.rst 2022-01-04 02:18:10.000000000 +0100 @@ -131,15 +131,15 @@ ``urls``:: # urls.py - from django.conf.urls import url, include + from django.urls.conf import re_path, include from myapp.api import EntryResource entry_resource = EntryResource() urlpatterns = [ # The normal jazz here... - url(r'^blog/', include('myapp.urls')), - url(r'^api/', include(entry_resource.urls)), + re_path(r'^blog/', include('myapp.urls')), + re_path(r'^api/', include(entry_resource.urls)), ] Now it's just a matter of firing up server (``./manage.py runserver``) and @@ -258,7 +258,7 @@ following:: # urls.py - from django.conf.urls import url, include + from django.urls.conf import re_path, include from tastypie.api import Api from myapp.api import EntryResource, UserResource @@ -268,8 +268,8 @@ urlpatterns = [ # The normal jazz here... - url(r'^blog/', include('myapp.urls')), - url(r'^api/', include(v1_api.urls)), + re_path(r'^blog/', include('myapp.urls')), + re_path(r'^api/', include(v1_api.urls)), ] Note that we're now creating an :class:`~tastypie.api.Api` instance, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tastypie/__init__.py new/django-tastypie-0.14.4/tastypie/__init__.py --- old/django-tastypie-0.14.3/tastypie/__init__.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tastypie/__init__.py 2022-01-04 02:18:10.000000000 +0100 @@ -3,7 +3,7 @@ __author__ = 'Daniel Lindsley & the Tastypie core team' -VERSION = (0, 14, 3) +VERSION = (0, 14, 4) __short_version__ = '.'.join(map(str, VERSION[0:2])) __version__ = ''.join(['.'.join(map(str, VERSION[0:3])), ''.join(VERSION[3:])]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tastypie/api.py new/django-tastypie-0.14.4/tastypie/api.py --- old/django-tastypie-0.14.3/tastypie/api.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tastypie/api.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,6 +1,5 @@ from __future__ import unicode_literals import warnings -from django.conf.urls import url, include from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponse, HttpResponseBadRequest from tastypie.compat import reverse @@ -9,6 +8,7 @@ from tastypie.utils import is_valid_jsonp_callback_value, string_to_python, trailing_slash from tastypie.utils.mime import determine_format, build_content_type from tastypie.resources import Resource +from django.urls.conf import re_path, include class Api(object): @@ -103,12 +103,12 @@ ``Resources`` beneath it. """ pattern_list = [ - url(r"^(?P<api_name>%s)%s$" % (self.api_name, trailing_slash), self.wrap_view('top_level'), name="api_%s_top_level" % self.api_name), + re_path(r"^(?P<api_name>%s)%s$" % (self.api_name, trailing_slash), self.wrap_view('top_level'), name="api_%s_top_level" % self.api_name), ] for name in sorted(self._registry.keys()): self._registry[name].api_name = self.api_name - pattern_list.append(url(r"^(?P<api_name>%s)/" % self.api_name, include(self._registry[name].urls))) + pattern_list.append(re_path(r"^(?P<api_name>%s)/" % self.api_name, include(self._registry[name].urls))) urlpatterns = self.prepend_urls() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tastypie/authentication.py new/django-tastypie-0.14.4/tastypie/authentication.py --- old/django-tastypie-0.14.3/tastypie/authentication.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tastypie/authentication.py 2022-01-04 02:18:10.000000000 +0100 @@ -9,13 +9,13 @@ from django.conf import settings from django.contrib.auth import authenticate from django.core.exceptions import ImproperlyConfigured -from django.middleware.csrf import _sanitize_token, constant_time_compare -from django.utils.translation import ugettext as _ +from django.middleware.csrf import _sanitize_token +from django.utils.translation import gettext as _ from six.moves.urllib.parse import urlparse from tastypie.compat import ( - get_user_model, get_username_field, unsalt_token, is_authenticated + get_user_model, get_username_field, compare_sanitized_tokens, InvalidTokenFormat ) from tastypie.http import HttpUnauthorized @@ -306,10 +306,10 @@ # the serialized bodies. if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): - return is_authenticated(request.user) + return request.user.is_authenticated if getattr(request, '_dont_enforce_csrf_checks', False): - return is_authenticated(request.user) + return request.user.is_authenticated csrf_token = _sanitize_token(request.COOKIES.get(settings.CSRF_COOKIE_NAME, '')) @@ -325,13 +325,15 @@ return False request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') - request_csrf_token = _sanitize_token(request_csrf_token) + try: + request_csrf_token = _sanitize_token(request_csrf_token) + except InvalidTokenFormat: + return False - if not constant_time_compare(unsalt_token(request_csrf_token), - unsalt_token(csrf_token)): + if not compare_sanitized_tokens(request_csrf_token, csrf_token): return False - return is_authenticated(request.user) + return request.user.is_authenticated def get_identifier(self, request): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tastypie/compat.py new/django-tastypie-0.14.4/tastypie/compat.py --- old/django-tastypie-0.14.3/tastypie/compat.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tastypie/compat.py 2022-01-04 02:18:10.000000000 +0100 @@ -13,16 +13,6 @@ AUTH_USER_MODEL = settings.AUTH_USER_MODEL -def is_authenticated(user): - """ - Django is changing User.is_authenticated into a property. Calling it - will be deprecated by Django 2.0 and a warning in 1.10+. - """ - if django.VERSION < (1, 10): - return bool(user.is_authenticated()) - return bool(user.is_authenticated) - - def get_username_field(): return get_user_model().USERNAME_FIELD @@ -31,21 +21,16 @@ return meta.model_name -atomic_decorator = django.db.transaction.atomic - -# Compatability for salted vs unsalted CSRF tokens; -# Django 1.10's _sanitize_token also hashes it, so it can't be compared directly. -# Solution is to call _sanitize_token on both tokens, then unsalt or noop both -try: - from django.middleware.csrf import _unsalt_cipher_token - - def unsalt_token(token): - return _unsalt_cipher_token(token) -except ImportError: +def is_ajax(request): + """ + Handle multiple ways of detecting an ajax request. Probably nearly useless. + """ + if hasattr(request, 'is_ajax'): + return request.is_ajax() + return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest' - def unsalt_token(token): - return token +atomic_decorator = django.db.transaction.atomic # force_text deprecated in 2.2, removed in 3.0 # note that in 1.1.x, force_str and force_text both exist, but force_str behaves @@ -54,3 +39,38 @@ from django.utils.encoding import force_text as force_str # noqa else: from django.utils.encoding import force_str # noqa + + +compare_sanitized_tokens = None + +# django 4.0 +try: + from django.middleware.csrf import _does_token_match, InvalidTokenFormat + compare_sanitized_tokens = _does_token_match +except ImportError: + pass + + +# django 3.2 +if compare_sanitized_tokens is None: + try: + from django.middleware.csrf import _compare_masked_tokens + compare_sanitized_tokens = _compare_masked_tokens + class InvalidTokenFormat(Exception): # noqa + pass + except ImportError: + pass + +# django 2.2 +if compare_sanitized_tokens is None: + try: + from django.middleware.csrf import _unsalt_cipher_token, constant_time_compare + + def compare_sanitized_tokens(request_csrf_token, csrf_token): + return constant_time_compare(_unsalt_cipher_token(request_csrf_token), + _unsalt_cipher_token(csrf_token)) + + class InvalidTokenFormat(Exception): # noqa + pass + except ImportError: # pragma: no cover + raise ImportError("Couldn't find a way to compare csrf tokens safely") # pragma: no cover diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tastypie/resources.py new/django-tastypie-0.14.4/tastypie/resources.py --- old/django-tastypie-0.14.3/tastypie/resources.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tastypie/resources.py 2022-01-04 02:18:10.000000000 +0100 @@ -10,13 +10,14 @@ from wsgiref.handlers import format_date_time from django.conf import settings -from django.conf.urls import url from django.core.exceptions import ( ObjectDoesNotExist, MultipleObjectsReturned, ValidationError, FieldDoesNotExist ) from django.core.signals import got_request_exception from django.core.exceptions import ImproperlyConfigured from django.db.models.fields.related import ForeignKey +from django.urls.conf import re_path +from tastypie.utils.timezone import make_naive_utc try: from django.contrib.gis.db.models.fields import GeometryField except (ImproperlyConfigured, ImportError): @@ -40,7 +41,7 @@ from tastypie.authorization import ReadOnlyAuthorization from tastypie.bundle import Bundle from tastypie.cache import NoCache -from tastypie.compat import NoReverseMatch, reverse, Resolver404, get_script_prefix +from tastypie.compat import NoReverseMatch, reverse, Resolver404, get_script_prefix, is_ajax from tastypie.constants import ALL, ALL_WITH_RELATIONS from tastypie.exceptions import ( NotFound, BadRequest, InvalidFilterError, HydrationError, InvalidSortError, @@ -240,7 +241,7 @@ # ``Cache-Control`` available then patch the header. patch_cache_control(response, **self._meta.cache.cache_control()) - if request.is_ajax() and not response.has_header("Cache-Control"): + if is_ajax(request) and not response.has_header("Cache-Control"): # IE excessively caches XMLHttpRequests, so we're disabling # the browser cache here. # See http://www.enhanceie.com/ie/bugs.asp for details. @@ -338,10 +339,10 @@ The standard URLs this ``Resource`` should respond to. """ return [ - url(r"^(?P<resource_name>%s)%s$" % (self._meta.resource_name, trailing_slash), self.wrap_view('dispatch_list'), name="api_dispatch_list"), - url(r"^(?P<resource_name>%s)/schema%s$" % (self._meta.resource_name, trailing_slash), self.wrap_view('get_schema'), name="api_get_schema"), - url(r"^(?P<resource_name>%s)/set/(?P<%s_list>.*?)%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash), self.wrap_view('get_multiple'), name="api_get_multiple"), - url(r"^(?P<resource_name>%s)/(?P<%s>.*?)%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash), self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), + re_path(r"^(?P<resource_name>%s)%s$" % (self._meta.resource_name, trailing_slash), self.wrap_view('dispatch_list'), name="api_dispatch_list"), + re_path(r"^(?P<resource_name>%s)/schema%s$" % (self._meta.resource_name, trailing_slash), self.wrap_view('get_schema'), name="api_get_schema"), + re_path(r"^(?P<resource_name>%s)/set/(?P<%s_list>.*?)%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash), self.wrap_view('get_multiple'), name="api_get_multiple"), + re_path(r"^(?P<resource_name>%s)/(?P<%s>.*?)%s$" % (self._meta.resource_name, self._meta.detail_uri_name, trailing_slash), self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), ] def override_urls(self): @@ -602,7 +603,9 @@ if isinstance(throttle, int) and not isinstance(throttle, bool): response['Retry-After'] = throttle elif isinstance(throttle, datetime): - response['Retry-After'] = format_date_time(mktime(throttle.timetuple())) + # change to UTC (GMT) and make naive, to avoid wsgiref also doing an implicit TZ conversion + throttle_utc = make_naive_utc(throttle) + response['Retry-After'] = format_date_time(mktime(throttle_utc.timetuple())) raise ImmediateHttpResponse(response=response) @@ -1910,7 +1913,7 @@ result = fields.FloatField elif internal_type in ('DecimalField',): result = fields.DecimalField - elif internal_type in ('IntegerField', 'PositiveIntegerField', 'PositiveSmallIntegerField', 'SmallIntegerField', 'AutoField', 'BigIntegerField'): + elif internal_type in ('IntegerField', 'PositiveIntegerField', 'PositiveSmallIntegerField', 'SmallIntegerField', 'AutoField', 'BigIntegerField', 'BigAutoField'): result = fields.IntegerField elif internal_type in ('FileField', 'ImageField'): result = fields.FileField diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tastypie/utils/timezone.py new/django-tastypie-0.14.4/tastypie/utils/timezone.py --- old/django-tastypie-0.14.3/tastypie/utils/timezone.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tastypie/utils/timezone.py 2022-01-04 02:18:10.000000000 +0100 @@ -19,6 +19,15 @@ return value +def make_naive_utc(value): + """ + Translate a datetime to UTC, then strip TZ info; useful as a last step before creating the + Retry-After header. + """ + utc_value = timezone.localtime(value, timezone.utc) + return timezone.make_naive(utc_value) + + def now(): d = timezone.now() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/alphanumeric/urls.py new/django-tastypie-0.14.4/tests/alphanumeric/urls.py --- old/django-tastypie-0.14.3/tests/alphanumeric/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/alphanumeric/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,6 +1,6 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path urlpatterns = [ - url(r'^api/', include('alphanumeric.api.urls')), + re_path(r'^api/', include('alphanumeric.api.urls')), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/authorization/urls.py new/django-tastypie-0.14.4/tests/authorization/urls.py --- old/django-tastypie-0.14.3/tests/authorization/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/authorization/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import url, include +from django.urls.conf import include, re_path from tastypie.api import Api @@ -12,5 +12,5 @@ v1_api.register(UserResource()) urlpatterns = [ - url(r'^api/', include(v1_api.urls)), + re_path(r'^api/', include(v1_api.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/basic/urls.py new/django-tastypie-0.14.4/tests/basic/urls.py --- old/django-tastypie-0.14.3/tests/basic/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/basic/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,6 +1,6 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path urlpatterns = [ - url(r'^api/', include('basic.api.urls')), + re_path(r'^api/', include('basic.api.urls')), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/content_gfk/urls.py new/django-tastypie-0.14.4/tests/content_gfk/urls.py --- old/django-tastypie-0.14.3/tests/content_gfk/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/content_gfk/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,6 +1,6 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path urlpatterns = [ - url(r'^api/', include('content_gfk.api.urls')), + re_path(r'^api/', include('content_gfk.api.urls')), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/migrations/0001_initial.py new/django-tastypie-0.14.4/tests/core/migrations/0001_initial.py --- old/django-tastypie-0.14.3/tests/core/migrations/0001_initial.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/migrations/0001_initial.py 2022-01-04 02:18:10.000000000 +0100 @@ -114,4 +114,10 @@ ], bases=('core.note',), ), + migrations.CreateModel( + name='BigAutoNowModel', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False, verbose_name='ID')), + ], + ), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/models.py new/django-tastypie-0.14.4/tests/core/models.py --- old/django-tastypie-0.14.3/tests/core/models.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/models.py 2022-01-04 02:18:10.000000000 +0100 @@ -97,6 +97,13 @@ app_label = 'core' +class BigAutoNowModel(models.Model): + id = models.BigAutoField(primary_key=True) + + class Meta: + app_label = 'core' + + class Counter(models.Model): name = models.CharField(max_length=30) slug = models.SlugField(unique=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/tests/__init__.py new/django-tastypie-0.14.4/tests/core/tests/__init__.py --- old/django-tastypie-0.14.3/tests/core/tests/__init__.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/tests/__init__.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,6 +1,6 @@ +import doctest import warnings warnings.simplefilter('ignore', Warning) # noqa -import doctest from core.tests.api import * # noqa from core.tests.authentication import * # noqa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/tests/api_urls.py new/django-tastypie-0.14.4/tests/core/tests/api_urls.py --- old/django-tastypie-0.14.3/tests/core/tests/api_urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/tests/api_urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from core.tests.api import Api, NoteResource, UserResource @@ -8,5 +8,5 @@ api.register(UserResource()) urlpatterns = [ - url(r'^api/', include(api.urls)), + re_path(r'^api/', include(api.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/tests/manual_urls.py new/django-tastypie-0.14.4/tests/core/tests/manual_urls.py --- old/django-tastypie-0.14.3/tests/core/tests/manual_urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/tests/manual_urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,9 +1,9 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from core.tests.resources import NoteResource note_resource = NoteResource() urlpatterns = [ - url(r'^', include(note_resource.urls)), + re_path(r'^', include(note_resource.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/tests/resource_urls.py new/django-tastypie-0.14.4/tests/core/tests/resource_urls.py --- old/django-tastypie-0.14.3/tests/core/tests/resource_urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/tests/resource_urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from django.contrib.auth.models import User from tastypie import fields from tastypie.resources import ModelResource @@ -33,5 +33,5 @@ api.register(SubjectResource()) urlpatterns = [ - url(r'^api/', include(api.urls)), + re_path(r'^api/', include(api.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/core/tests/resources.py new/django-tastypie-0.14.4/tests/core/tests/resources.py --- old/django-tastypie-0.14.3/tests/core/tests/resources.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/core/tests/resources.py 2022-01-04 02:18:10.000000000 +0100 @@ -18,6 +18,7 @@ from django.core.cache import cache from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist, ImproperlyConfigured from django.core import mail +from time import mktime try: from django.urls import reverse except ImportError: @@ -36,7 +37,7 @@ UnsupportedSerializationFormat, UnsupportedDeserializationFormat, ) from tastypie import fields, http -from tastypie.compat import is_authenticated, force_str +from tastypie.compat import force_str from tastypie.paginator import Paginator from tastypie.resources import ( ALL, ALL_WITH_RELATIONS, convert_post_to_put, convert_post_to_patch, @@ -49,7 +50,7 @@ from core.models import ( Note, NoteWithEditor, Subject, MediaBit, AutoNowNote, DateRecord, Counter, - MyDefaultPKModel, MyUUIDModel, MyRelatedUUIDModel, + MyDefaultPKModel, MyUUIDModel, MyRelatedUUIDModel, BigAutoNowModel, ) from core.tests.mocks import MockRequest from core.utils import adjust_schema, SimpleHandler @@ -1056,6 +1057,19 @@ return '/api/v1/autonownotes/%s/' % bundle_or_obj.obj.id +class BigAutoNowModelResource(ModelResource): + class Meta: + resource_name = 'bigautonowmodels' + queryset = BigAutoNowModel.objects.all() + authorization = Authorization() + + def get_resource_uri(self, bundle_or_obj=None, url_name='api_dispatch_list'): + if bundle_or_obj is None: + return '/api/v1/bigautonowmodels/' + + return '/api/v1/bigautonowmodels/%s/' % bundle_or_obj.obj.id + + class CustomPaginator(Paginator): def page(self): data = super(CustomPaginator, self).page() @@ -1338,7 +1352,7 @@ class PerUserAuthorization(Authorization): def read_list(self, object_list, bundle): if bundle.request and hasattr(bundle.request, 'user'): - if is_authenticated(bundle.request.user): + if bundle.request.user.is_authenticated: object_list = object_list.filter(author=bundle.request.user) else: object_list = object_list.none() @@ -1661,6 +1675,20 @@ self.assertEqual(annr.fields['updated'].readonly, False) self.assertEqual(annr.fields['updated'].unique, False) + def test_big_auto_field(self): + annr = BigAutoNowModelResource() + self.assertEqual(len(annr.fields), 2) + self.assertEqual(sorted(annr.fields.keys()), ['id', 'resource_uri']) + + self.assertTrue(isinstance(annr.fields['id'], fields.IntegerField)) + self.assertEqual(annr.fields['id'].attribute, 'id') + self.assertEqual(annr.fields['id'].blank, True) + self.assertEqual(annr.fields['id']._default, '') + self.assertEqual(annr.fields['id'].instance_name, 'id') + self.assertEqual(annr.fields['id'].null, False) + self.assertEqual(annr.fields['id'].readonly, False) + self.assertEqual(annr.fields['id'].unique, True) + def test_invalid_model_resource(self): """ Test error message regarding ModelResource lacking object_class and queryset. @@ -4156,10 +4184,71 @@ @patch('tastypie.throttle.time') @override_settings(DEBUG=False) def test_check_datetime_throttling(self, mocked_time): - mocked_time.time.return_value = time.time() retry_after = datetime.datetime(year=2014, month=8, day=8, hour=8, minute=55, tzinfo=timezone.utc) - retry_after_str = 'Fri, 08 Aug 2014 14:55:00 GMT' + mocked_time.time.return_value = mktime(retry_after.timetuple()) + retry_after_str = 'Fri, 08 Aug 2014 08:55:00 GMT' + + resource = ThrottledNoteResource() + _orginal_throttle = resource._meta.throttle + + class DatetimeThrottle(resource._meta.throttle.__class__): + def should_be_throttled(self, *args, **kwargs): + ret = super(DatetimeThrottle, self).should_be_throttled(*args, **kwargs) + if ret: + return retry_after + return False + resource._meta.throttle = DatetimeThrottle( + throttle_at=resource._meta.throttle.throttle_at, + timeframe=resource._meta.throttle.timeframe, + expiration=resource._meta.throttle.expiration + ) + + request = HttpRequest() + request.GET = {'format': 'json'} + request.method = 'GET' + + # Not throttled. + resp = resource.dispatch('list', request) + self.assertEqual(resp.status_code, 200) + self.assertEqual(len(cache.get('noaddr_nohost_accesses')), 1) + + # Not throttled. + resp = resource.dispatch('list', request) + self.assertEqual(resp.status_code, 200) + self.assertEqual(len(cache.get('noaddr_nohost_accesses')), 2) + + # Throttled. + with self.assertRaises(ImmediateHttpResponse) as ctx: + resp = resource.dispatch('list', request) + e = ctx.exception + self.assertEqual(e.response.status_code, 429) + self.assertEqual(e.response['Retry-After'], retry_after_str) + self.assertEqual(len(cache.get('noaddr_nohost_accesses')), 2) + + # Throttled. + with self.assertRaises(ImmediateHttpResponse) as ctx: + resp = resource.dispatch('list', request) + e = ctx.exception + self.assertEqual(e.response.status_code, 429) + self.assertEqual(e.response['Retry-After'], retry_after_str) + self.assertEqual(len(cache.get('noaddr_nohost_accesses')), 2) + + # Check the ``wrap_view``. + resp = resource.wrap_view('dispatch_list')(request) + self.assertEqual(resp.status_code, 429) + self.assertEqual(resp['Retry-After'], retry_after_str) + self.assertEqual(len(cache.get('noaddr_nohost_accesses')), 2) + + resource._meta.throttle = _orginal_throttle + + @patch('tastypie.throttle.time') + @override_settings(DEBUG=False, USE_TZ=False) + def test_check_datetime_throttling_notz(self, mocked_time): + + retry_after = datetime.datetime(year=2014, month=8, day=8, hour=8, minute=55, tzinfo=timezone.utc) + mocked_time.time.return_value = mktime(retry_after.timetuple()) + retry_after_str = 'Fri, 08 Aug 2014 08:55:00 GMT' resource = ThrottledNoteResource() _orginal_throttle = resource._meta.throttle @@ -4937,7 +5026,7 @@ self.assertEqual(resp.status_code, 200) self.assertEqual(resp.content.decode('utf-8'), '{"content": "This is my very first post using my shiny new API. Pretty sweet, huh?", "created": "2010-03-30T20:05:00", "id": 1, "is_active": true, "resource_uri": "/api/v1/notes/1/", "slug": "first-post", "title": "First Post!", "updated": "2010-03-30T20:05:00"}') self.assertTrue(resp.has_header('Cache-Control')) - self.assertEqual(resp._headers['cache-control'], ('Cache-Control', 'no-cache')) + self.assertEqual(resp['Cache-Control'], 'no-cache') # Now as Ajax. request.META = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'} @@ -4945,7 +5034,7 @@ self.assertEqual(resp.status_code, 200) self.assertEqual(resp.content.decode('utf-8'), '{"content": "This is my very first post using my shiny new API. Pretty sweet, huh?", "created": "2010-03-30T20:05:00", "id": 1, "is_active": true, "resource_uri": "/api/v1/notes/1/", "slug": "first-post", "title": "First Post!", "updated": "2010-03-30T20:05:00"}') self.assertTrue(resp.has_header('cache-control')) - self.assertEqual(resp._headers['cache-control'], ('Cache-Control', 'no-cache')) + self.assertEqual(resp['Cache-Control'], 'no-cache') def test_custom_paginator(self): mock_request = MockRequest() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/gis/urls.py new/django-tastypie-0.14.4/tests/gis/urls.py --- old/django-tastypie-0.14.3/tests/gis/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/gis/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,6 +1,6 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path urlpatterns = [ - url(r'^api/', include('gis.api.urls')), + re_path(r'^api/', include('gis.api.urls')), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/namespaced/api/urls.py new/django-tastypie-0.14.4/tests/namespaced/api/urls.py --- old/django-tastypie-0.14.3/tests/namespaced/api/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/namespaced/api/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,5 +1,5 @@ from django.conf import settings -from django.conf.urls import include, url +from django.urls.conf import include, re_path from tastypie.api import NamespacedApi from namespaced.api.resources import NamespacedNoteResource, NamespacedUserResource @@ -14,5 +14,5 @@ included = include(api.urls, namespace='special') urlpatterns = [ - url(r'^api/', included), + re_path(r'^api/', included), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/profilingtests/urls.py new/django-tastypie-0.14.4/tests/profilingtests/urls.py --- old/django-tastypie-0.14.3/tests/profilingtests/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/profilingtests/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from tastypie.api import Api @@ -10,5 +10,5 @@ api.register(UserResource()) urlpatterns = [ - url(r'^api/', include(api.urls)), + re_path(r'^api/', include(api.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/requirements.txt new/django-tastypie-0.14.4/tests/requirements.txt --- old/django-tastypie-0.14.3/tests/requirements.txt 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/requirements.txt 2022-01-04 02:18:10.000000000 +0100 @@ -4,5 +4,5 @@ defusedxml lxml mock<1.1.0 -pytz==2013b0 +pytz==2021.1 PyYAML diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/settings.py new/django-tastypie-0.14.4/tests/settings.py --- old/django-tastypie-0.14.3/tests/settings.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/settings.py 2022-01-04 02:18:10.000000000 +0100 @@ -38,6 +38,7 @@ 'NAME': DATABASE_NAME, } } +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' ALLOWED_HOSTS = ['example.com'] @@ -91,3 +92,5 @@ if DJANGO_VERSION >= DJANGO_20: MIDDLEWARE.remove('django.contrib.auth.middleware.SessionAuthenticationMiddleware') + +SECURE_REFERRER_POLICY = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/slashless/api/urls.py new/django-tastypie-0.14.4/tests/slashless/api/urls.py --- old/django-tastypie-0.14.3/tests/slashless/api/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/slashless/api/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from tastypie.api import Api from slashless.api.resources import NoteResource, UserResource @@ -8,5 +8,5 @@ api.register(UserResource(), canonical=True) urlpatterns = [ - url(r'^api/', include(api.urls)), + re_path(r'^api/', include(api.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tests/validation/api/urls.py new/django-tastypie-0.14.4/tests/validation/api/urls.py --- old/django-tastypie-0.14.3/tests/validation/api/urls.py 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tests/validation/api/urls.py 2022-01-04 02:18:10.000000000 +0100 @@ -1,4 +1,4 @@ -from django.conf.urls import include, url +from django.urls.conf import include, re_path from tastypie.api import Api @@ -12,5 +12,5 @@ api.register(AnnotatedNoteResource(), canonical=True) urlpatterns = [ - url(r'^api/', include(api.urls)), + re_path(r'^api/', include(api.urls)), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.3/tox.ini new/django-tastypie-0.14.4/tox.ini --- old/django-tastypie-0.14.3/tox.ini 2020-01-06 15:43:54.000000000 +0100 +++ new/django-tastypie-0.14.4/tox.ini 2022-01-04 02:18:10.000000000 +0100 @@ -1,32 +1,33 @@ [tox] envlist = - py{27}-dj{111} - py{36,37}-dj{22,30,dev} - py{27,37}-docs, - py{27,37}-flake8, - py{27,37}-flake8-strict + py{3.6,3.7,3.8,3.9,3.10}-dj{2.2,3.2} + py{3.8,3.9,3.10}-dj{4.0,dev} + py{3.6,3.7,3.8,3.9,3.10}-docs, + py{3.6,3.7,3.8,3.9,3.10}-flake8, + py{3.8,3.9,3.10}-flake8-strict skipsdist=True [testenv] usedevelop=True -test-executable = - {envbindir}/coverage run --append --source=tastypie,tests {envbindir}/django-admin.py setenv = PYTHONPATH = {toxinidir}:{toxinidir}/tests PYTHONWARNINGS = always + TESTEXE = {envbindir}/coverage run --append --source=tastypie,tests {envbindir}/django-admin.py + dj{4.0,dev}: TESTEXE = {envbindir}/coverage run --append --source=tastypie,tests {envbindir}/django-admin + commands = - dj{111,22,30,dev}: {[testenv]test-executable} test -p '*' core.tests --settings=settings_core - dj{111,22,30,dev}: {[testenv]test-executable} test basic.tests --settings=settings_basic - dj{111,22,30,dev}: {[testenv]test-executable} test related_resource.tests --settings=settings_related - dj{111,22,30,dev}: {[testenv]test-executable} test alphanumeric.tests --settings=settings_alphanumeric - dj{111,22,30,dev}: {[testenv]test-executable} test authorization.tests --settings=settings_authorization - dj{111,22,30,dev}: {[testenv]test-executable} test content_gfk.tests --settings=settings_content_gfk - dj{111,22,30,dev}: {[testenv]test-executable} test customuser.tests --settings=settings_customuser - dj{111,22,30,dev}: {[testenv]test-executable} test namespaced.tests --settings=settings_namespaced - dj{111,22,30,dev}: {[testenv]test-executable} test slashless.tests --settings=settings_slashless - dj{111,22,30,dev}: {[testenv]test-executable} test validation.tests --settings=settings_validation - dj{111,22,30,dev}: {[testenv]test-executable} test gis.tests --settings=settings_gis_spatialite + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test -p '*' core.tests --settings=settings_core + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test basic.tests --settings=settings_basic + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test related_resource.tests --settings=settings_related + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test alphanumeric.tests --settings=settings_alphanumeric + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test authorization.tests --settings=settings_authorization + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test content_gfk.tests --settings=settings_content_gfk + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test customuser.tests --settings=settings_customuser + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test namespaced.tests --settings=settings_namespaced + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test slashless.tests --settings=settings_slashless + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test validation.tests --settings=settings_validation + dj{2.2,3.2,4.0,dev}: {env:TESTEXE} test gis.tests --settings=settings_gis_spatialite docs: sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html docs: sphinx-build -W -b doctest -d {envtmpdir}/doctrees . {envtmpdir}/html @@ -35,27 +36,23 @@ flake8-strict: {envbindir}/flake8 --ignore=E128 --max-complexity 10 . basepython = - py27: python2.7 - py35: python3.5 - py36: python3.6 - py37: python3.7 + py3.6: python3.6 + py3.7: python3.7 + py3.8: python3.8 + py3.9: python3.9 + py3.10: python3.10 deps = - dj111: Django>=1.11,<1.12 - dj22: Django>=2.2,<2.3 - dj30: Django>=3.0,<3.1 - djdev: https://github.com/django/django/archive/master.tar.gz - - py27-dj{111}: django-oauth-plus==2.2.9 - py27-dj{111}: python-digest - py27-dj{111}: oauth2 - py27-dj{111}: pysqlite - py{35,36,37}-dj{111,22,30,dev}: python3-digest>=1.8b4 - dj{111,22,30,dev}: -r{toxinidir}/tests/requirements.txt + dj2.2: Django>=2.2,<2.3 + dj3.2: Django>=3.2,<3.3 + dj4.0: Django>=4.0,<4.1 + djdev: https://github.com/django/django/archive/refs/heads/main.zip + + dj{2.2,3.2,4.0,dev}: python3-digest>=1.8b4 + dj{2.2,3.2,4.0,dev}: -r{toxinidir}/tests/requirements.txt + py{3.6,3.7}-docs: Django~=2.2 + py{3.7,3.8,3.9,3.10}-docs: Django<4.1 docs: Sphinx - py27-docs: Django<2.0 - py35-docs: Django<3.0 - py{36,37}-docs: Django<3.1 docs: mock docs: sphinx_rtd_theme