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 2024-11-22 23:51:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-django-tastypie (Old) and /work/SRC/openSUSE:Factory/.python-django-tastypie.new.28523 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-tastypie" Fri Nov 22 23:51:33 2024 rev:26 rq:1225688 version:0.15.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-django-tastypie/python-django-tastypie.changes 2024-07-25 15:56:28.440816420 +0200 +++ /work/SRC/openSUSE:Factory/.python-django-tastypie.new.28523/python-django-tastypie.changes 2024-11-22 23:52:09.614986289 +0100 @@ -1,0 +2,10 @@ +Fri Nov 22 00:11:33 UTC 2024 - Steve Kowalik <steven.kowa...@suse.com> + +- Update to 0.15.0: + * Pin Sphinx to last known working version for now + * Fix race condition between POST / PATCH resources by using update_fields + * Use non-deprecated assertion methods + * Django 5.1 support +- Drop patch correct-assertion-methods.patch, included upstream. + +------------------------------------------------------------------- Old: ---- correct-assertion-methods.patch v0.14.7.tar.gz New: ---- v0.15.0.tar.gz BETA DEBUG BEGIN: Old: * Django 5.1 support - Drop patch correct-assertion-methods.patch, included upstream. BETA DEBUG END: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-django-tastypie.spec ++++++ --- /var/tmp/diff_new_pack.8Kb96e/_old 2024-11-22 23:52:11.831078611 +0100 +++ /var/tmp/diff_new_pack.8Kb96e/_new 2024-11-22 23:52:11.847079278 +0100 @@ -18,16 +18,15 @@ %{?sle15_python_module_pythons} Name: python-django-tastypie -Version: 0.14.7 +Version: 0.15.0 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 gh#django-tastypie/django-tastypie#1667 -Patch0: correct-assertion-methods.patch -BuildRequires: %{python_module Django >= 1.11.0} +BuildRequires: %{python_module Django >= 4.0} BuildRequires: %{python_module PyYAML} +BuildRequires: %{python_module base >= 3.8} BuildRequires: %{python_module biplist} BuildRequires: %{python_module defusedxml} BuildRequires: %{python_module lxml} @@ -39,7 +38,7 @@ BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-Django >= 1.11.0 +Requires: python-Django >= 4.0 Requires: python-python-dateutil >= 2.1 Requires: python-python-mimeparse >= 0.1.4 Recommends: python-PyYAML ++++++ v0.14.7.tar.gz -> v0.15.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/.github/dependabot.yml new/django-tastypie-0.15.0/.github/dependabot.yml --- old/django-tastypie-0.14.7/.github/dependabot.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/django-tastypie-0.15.0/.github/dependabot.yml 2024-11-20 22:43:26.000000000 +0100 @@ -0,0 +1,13 @@ +# Keep GitHub Actions up to date with GitHub's Dependabot... +# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + groups: + github-actions: + patterns: + - "*" # Group all Actions updates into a single larger pull request + schedule: + interval: weekly diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/.github/workflows/python-package.yml new/django-tastypie-0.15.0/.github/workflows/python-package.yml --- old/django-tastypie-0.14.7/.github/workflows/python-package.yml 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/.github/workflows/python-package.yml 2024-11-20 22:43:26.000000000 +0100 @@ -15,42 +15,30 @@ strategy: fail-fast: false matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] - django-version: ["3.2", "4.0", "4.1", "4.2", "5.0"] # Todo: add "dev" back + python-version: ["3.8", "3.9", "3.10", "3.11"] + django-version: ["4.0", "4.1", "4.2", "5.0", "5.1"] # Todo: add "dev" back exclude: - - python-version: "3.6" - django-version: "4.0" - - python-version: "3.7" - django-version: "4.0" - - python-version: "3.6" - django-version: "4.1" - - python-version: "3.7" - django-version: "4.1" - - python-version: "3.6" - django-version: "4.2" - - python-version: "3.7" - django-version: "4.2" - - python-version: "3.6" - django-version: "5.0" - - python-version: "3.7" - django-version: "5.0" - python-version: "3.8" django-version: "5.0" + - python-version: "3.8" + django-version: "5.1" - python-version: "3.9" django-version: "5.0" + - python-version: "3.9" + django-version: "5.1" # - python-version: "3.6" # django-version: "dev" # - python-version: "3.7" # django-version: "dev" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - 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@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/BACKWARDS-INCOMPATIBLE.txt new/django-tastypie-0.15.0/BACKWARDS-INCOMPATIBLE.txt --- old/django-tastypie-0.14.7/BACKWARDS-INCOMPATIBLE.txt 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/BACKWARDS-INCOMPATIBLE.txt 2024-11-20 22:43:26.000000000 +0100 @@ -1,5 +1,11 @@ -Master (v0.9.16) -================ +v0.15.0 +======= + +[2024-10-15] 2444fce - PATCH requests no longer save entire objects, instead only updating fields specified in the payload. This is correct behavior and an unusual edge case, but has been broken for more than a decade so existing workarounds may break as a result. + + +v0.9.16 +====== [2012-12-11] abc0bef - Changed response code of PUT with always_return_data=True from 202 to 200 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/README.rst new/django-tastypie-0.15.0/README.rst --- old/django-tastypie-0.14.7/README.rst 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/README.rst 2024-11-20 22:43:26.000000000 +0100 @@ -34,8 +34,8 @@ Core ---- -* Python 3.6+, preferably 3.8+ (Whatever is supported by your version of Django) -* Django 4.2, 3.2 (LTS releases), or Django 4.0, 4.1, and 5.0 (intermediate releases) +* Python 3.8+ (Whatever is supported by your version of Django) +* Django 4.2 (LTS releases) or Django 4.0+ (intermediate releases, including 5.0 and 5.1) * 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.7/SECURITY.rst new/django-tastypie-0.15.0/SECURITY.rst --- old/django-tastypie-0.14.7/SECURITY.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/django-tastypie-0.15.0/SECURITY.rst 2024-11-20 22:43:26.000000000 +0100 @@ -0,0 +1,18 @@ +=============== +django-tastypie +=============== + +Security Policy +=============== + +Tastypie is committed to providing a flexible and secure API, and was designed +with many security features and options in mind. Due to the complex nature of +APIs and the constant discovery of new attack vectors and vulnerabilities, +no software is immune to security holes. We rely on our community to report +and help us investigate security issues. + +If you come across a security hole **please do not open a Github issue**. +Instead, **drop us an email** at ``tastypie-secur...@googlegroups.com`` + +We'll then work together to investigate and resolve the problem so we can +announce a solution along with the vulnerability. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/docs/conf.py new/django-tastypie-0.15.0/docs/conf.py --- old/django-tastypie-0.14.7/docs/conf.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/docs/conf.py 2024-11-20 22:43:26.000000000 +0100 @@ -108,12 +108,11 @@ html_theme = 'default' try: - import sphinx_rtd_theme + import sphinx_rtd_theme # noqa except ImportError: pass else: html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/docs/cookbook.rst new/django-tastypie-0.15.0/docs/cookbook.rst --- old/django-tastypie-0.14.7/docs/cookbook.rst 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/docs/cookbook.rst 2024-11-20 22:43:26.000000000 +0100 @@ -8,7 +8,7 @@ ----------------------------- It is common to use django to provision OAuth 2.0 tokens for users and then -have Tasty Pie use these tokens to authenticate users to the API. `Follow this tutorial <http://ianalexandr.com/blog/building-a-true-oauth-20-api-with-django-and-tasty-pie.html>`_ and `use this custom authentication class <https://github.com/ianalexander/django-oauth2-tastypie>`_ to enable +have Tasty Pie use these tokens to authenticate users to the API. `Follow this tutorial <https://web.archive.org/web/20160308015637/http://ianalexandr.com/blog/building-a-true-oauth-20-api-with-django-and-tasty-pie.html>`_ and `use this custom authentication class <https://github.com/ianalexander/django-oauth2-tastypie>`_ to enable OAuth 2.0 authentication with Tasty Pie. .. testsetup:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/docs/index.rst new/django-tastypie-0.15.0/docs/index.rst --- old/django-tastypie-0.14.7/docs/index.rst 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/docs/index.rst 2024-11-20 22:43:26.000000000 +0100 @@ -83,8 +83,8 @@ Core ---- -* 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) +* Python 3.8+ (Whatever is supported by your version of Django) +* Django 4.2+ (may work with other versions, but the most recent LTS is our priority) * 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.7/docs/release_notes/index.rst new/django-tastypie-0.15.0/docs/release_notes/index.rst --- old/django-tastypie-0.14.7/docs/release_notes/index.rst 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/docs/release_notes/index.rst 2024-11-20 22:43:26.000000000 +0100 @@ -5,6 +5,7 @@ :maxdepth: 1 dev + v0.15.0 v0.14.7 v0.14.6 v0.14.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/docs/release_notes/v0.15.0.rst new/django-tastypie-0.15.0/docs/release_notes/v0.15.0.rst --- old/django-tastypie-0.14.7/docs/release_notes/v0.15.0.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/django-tastypie-0.15.0/docs/release_notes/v0.15.0.rst 2024-11-20 22:43:26.000000000 +0100 @@ -0,0 +1,8 @@ +v0.15.0 +======= + +:date: 2024-10-15 + +- Officially supports Django 5.1 (though no changes were necessary) +- Dropped official support for Django 3.2. +- #1617 Fixed a longstanding race condition in PATCH requests that saved entire objects rather than only applying changes from the sent payload diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/docs/requirements.txt new/django-tastypie-0.15.0/docs/requirements.txt --- old/django-tastypie-0.14.7/docs/requirements.txt 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/docs/requirements.txt 2024-11-20 22:43:26.000000000 +0100 @@ -1,2 +1,3 @@ -sphinx~=7.1.2 -sphinx-rtd-theme==1.3.0rc1 +Sphinx~=7.1.2 +sphinx-rtd-theme~=3.0.1 +mock~=5.1.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tastypie/__init__.py new/django-tastypie-0.15.0/tastypie/__init__.py --- old/django-tastypie-0.14.7/tastypie/__init__.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tastypie/__init__.py 2024-11-20 22:43:26.000000000 +0100 @@ -1,6 +1,6 @@ __author__ = 'Daniel Lindsley & the Tastypie core team' -VERSION = (0, 14, 7) +VERSION = (0, 15, 0) __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.7/tastypie/resources.py new/django-tastypie-0.15.0/tastypie/resources.py --- old/django-tastypie-0.14.7/tastypie/resources.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tastypie/resources.py 2024-11-20 22:43:26.000000000 +0100 @@ -1690,6 +1690,24 @@ # Now update the bundle in-place. deserialized = self.deserialize(request, request.body, format=request.META.get('CONTENT_TYPE', 'application/json')) + # Create a place to store the names of those fields we want to update + bundle.update_fields = [] + bundle.m2m_update_fields = [] + # When we get to the obj.save() stage, we need to know which fields have changed + # Otherwise we can't do a proper update. Thus, + # For every key in deserialized (e.g. the fields submitted in the PATCH) + for key in deserialized: + # If the key is a property of the object, lets add it to the list, except: + if hasattr(bundle.obj, key): + # Can't update_fields an m2m field, so instead add it to patch_m2m_fields + if getattr(self.fields[key], 'is_m2m', False): + bundle.m2m_update_fields.append(key) + continue + # Don't add if it is the id/pk field, can't patch that. + if key == 'id' or key == 'pk': + continue + # No more checks. Add it. + bundle.update_fields.append(key) self.update_in_place(request, bundle, deserialized) if not self._meta.always_return_data: @@ -2393,7 +2411,10 @@ obj_id = self.create_identifier(bundle.obj) if obj_id not in bundle.objects_saved or bundle.obj._state.adding: - bundle.obj.save() + if hasattr(bundle, 'update_fields'): + bundle.obj.save(update_fields=bundle.update_fields) + else: + bundle.obj.save() obj_id = self.create_identifier(bundle.obj) bundle.objects_saved.add(obj_id) @@ -2507,6 +2528,19 @@ if field_object.readonly: continue + # If this is a PATCH, make sure that this field name is one of the + # patched fields (recorded in the update_fields property of the bundle). + # Otherwise, we do not want to save / recreate this field. + if hasattr(bundle, 'update_fields'): + # This bundle is from a PATCH, we should not save an M2M field + # unless it was present in the PATCH + if field_name not in bundle.m2m_update_fields: + continue # Skip this field_name + else: # this field name WAS in the patch, lets save it. + pass + else: # Not a PATCH operation, carry on normally. + pass + # Get the manager. related_mngr = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/core/models.py new/django-tastypie-0.15.0/tests/core/models.py --- old/django-tastypie-0.14.7/tests/core/models.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/core/models.py 2024-11-20 22:43:26.000000000 +0100 @@ -1,3 +1,4 @@ +import time from itertools import count import uuid @@ -47,6 +48,16 @@ app_label = 'core' +class SlowNote(Note): + class Meta: + proxy = True + app_label = 'core' + + def save(self, *args, **kwargs): + time.sleep(1) + return super(SlowNote, self).save(*args, **kwargs) + + class NoteWithEditor(Note): editor = models.ForeignKey(AUTH_USER_MODEL, related_name='notes_edited', on_delete=models.CASCADE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/core/tests/__init__.py new/django-tastypie-0.15.0/tests/core/tests/__init__.py --- old/django-tastypie-0.14.7/tests/core/tests/__init__.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/core/tests/__init__.py 2024-11-20 22:43:26.000000000 +0100 @@ -15,6 +15,7 @@ from core.tests.throttle import * # noqa from core.tests.utils import * # noqa from core.tests.validation import * # noqa +from core.tests.race_condition import * # noqa # Explicitly add doctests to suite; Django's test runner stopped diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/core/tests/api.py new/django-tastypie-0.15.0/tests/core/tests/api.py --- old/django-tastypie-0.14.7/tests/core/tests/api.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/core/tests/api.py 2024-11-20 22:43:26.000000000 +0100 @@ -6,11 +6,12 @@ from django.test.utils import override_settings from tastypie.api import Api +from tastypie.authorization import Authorization from tastypie.exceptions import NotRegistered, BadRequest from tastypie.resources import ModelResource from tastypie.serializers import Serializer -from core.models import Note +from core.models import Note, SlowNote from core.utils import adjust_schema User = get_user_model() @@ -19,6 +20,14 @@ class Meta: resource_name = 'notes' queryset = Note.objects.filter(is_active=True) + authorization = Authorization() + + +class SlowNoteResource(ModelResource): + class Meta: + resource_name = 'slownotes' + queryset = SlowNote.objects.filter(is_active=True) + authorization = Authorization() class UserResource(ModelResource): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/core/tests/api_urls.py new/django-tastypie-0.15.0/tests/core/tests/api_urls.py --- old/django-tastypie-0.14.7/tests/core/tests/api_urls.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/core/tests/api_urls.py 2024-11-20 22:43:26.000000000 +0100 @@ -2,10 +2,12 @@ from core.tests.api import Api, NoteResource, UserResource +from tests.core.tests.api import SlowNoteResource api = Api() api.register(NoteResource()) api.register(UserResource()) +api.register(SlowNoteResource()) urlpatterns = [ re_path(r'^api/', include(api.urls)), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/core/tests/race_condition.py new/django-tastypie-0.15.0/tests/core/tests/race_condition.py --- old/django-tastypie-0.14.7/tests/core/tests/race_condition.py 1970-01-01 01:00:00.000000000 +0100 +++ new/django-tastypie-0.15.0/tests/core/tests/race_condition.py 2024-11-20 22:43:26.000000000 +0100 @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +import threading + +from django.contrib.auth import get_user_model + +try: + from django.urls import reverse +except ImportError: + from django.core.urlresolvers import reverse +from django.test import LiveServerTestCase, Client + +from core.models import ( + Note +) + +User = get_user_model() + + +class ApiConcurrentPatchTestCase(LiveServerTestCase): + + fixtures = ['note_testdata.json'] + + def setUp(self): + super(ApiConcurrentPatchTestCase, self).setUp() + self.user = User.objects.get(username='johndoe') + self.note = Note.objects.get(pk=1) + self.client = Client() + + def test_concurrent_patch_requests(self): + mapping_url = f"{self.live_server_url}{reverse('api_dispatch_detail', kwargs={'api_name': 'v1', 'resource_name': 'slownotes', 'pk': self.note.pk})}" + + response1 = self.client.patch( + mapping_url, data={ + 'title': 'original_title', 'slug': 'original_slug', 'content': 'original_content' + }, content_type='application/json' + ) + print(f"Concurrent PATCH request with HTTP status code: {response1.status_code}") + self.assertEqual(response1.status_code, 202) # 202 Accepted + + def patch_request(data): + local_client = Client() + response = local_client.patch( + mapping_url, + data=data, + content_type='application/json' + ) + print(f"Concurrent PATCH request with data {data} HTTP status code: {response.status_code}") + + # Define PATCH requests data + data1 = {'title': 'new_title'} + data2 = {'slug': 'new_slug'} + data3 = {'content': 'new_content'} + + # Create threads to simulate concurrent requests + thread1 = threading.Thread(target=patch_request, args=(data1,)) + thread2 = threading.Thread(target=patch_request, args=(data2,)) + thread3 = threading.Thread(target=patch_request, args=(data3,)) + + # Start threads + thread1.start() + thread2.start() + thread3.start() + + # Wait for threads to finish + thread1.join() + thread2.join() + thread3.join() + + # Refresh the object from the database + self.note.refresh_from_db() + + # Check final values (exact value check since race conditions should be handled) + self.assertEqual(self.note.title, "new_title") + self.assertEqual(self.note.slug, "new_slug") + self.assertEqual(self.note.content, "new_content") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/core/tests/resources.py new/django-tastypie-0.15.0/tests/core/tests/resources.py --- old/django-tastypie-0.14.7/tests/core/tests/resources.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/core/tests/resources.py 2024-11-20 22:43:26.000000000 +0100 @@ -302,19 +302,19 @@ def test_fields(self): basic = BasicResource() self.assertEqual(len(basic.fields), 4) - self.assert_('name' in basic.fields) + self.assertIn('name', basic.fields) self.assertEqual(isinstance(basic.fields['name'], fields.CharField), True) self.assertEqual(basic.fields['name']._resource, basic.__class__) self.assertEqual(basic.fields['name'].instance_name, 'name') - self.assert_('view_count' in basic.fields) + self.assertIn('view_count', basic.fields) self.assertEqual(isinstance(basic.fields['view_count'], fields.IntegerField), True) self.assertEqual(basic.fields['view_count']._resource, basic.__class__) self.assertEqual(basic.fields['view_count'].instance_name, 'view_count') - self.assert_('date_joined' in basic.fields) + self.assertIn('date_joined', basic.fields) self.assertEqual(isinstance(basic.fields['date_joined'], fields.DateTimeField), True) self.assertEqual(basic.fields['date_joined']._resource, basic.__class__) self.assertEqual(basic.fields['date_joined'].instance_name, 'date_joined') - self.assert_('resource_uri' in basic.fields) + self.assertIn('resource_uri', basic.fields) self.assertEqual(isinstance(basic.fields['resource_uri'], fields.CharField), True) self.assertEqual(basic.fields['resource_uri']._resource, basic.__class__) self.assertEqual(basic.fields['resource_uri'].instance_name, 'resource_uri') @@ -322,35 +322,35 @@ another = AnotherBasicResource() self.assertEqual(len(another.fields), 8) - self.assert_('name' in another.fields) + self.assertIn('name', another.fields) self.assertEqual(isinstance(another.name, fields.CharField), True) self.assertEqual(another.fields['name']._resource, another.__class__) self.assertEqual(another.fields['name'].instance_name, 'name') - self.assert_('view_count' in another.fields) + self.assertIn('view_count', another.fields) self.assertEqual(isinstance(another.view_count, fields.IntegerField), True) self.assertEqual(another.fields['view_count']._resource, another.__class__) self.assertEqual(another.fields['view_count'].instance_name, 'view_count') - self.assert_('date_joined' in another.fields) + self.assertIn('date_joined', another.fields) self.assertEqual(isinstance(another.date_joined, fields.DateField), True) self.assertEqual(another.fields['date_joined']._resource, another.__class__) self.assertEqual(another.fields['date_joined'].instance_name, 'date_joined') - self.assert_('is_active' in another.fields) + self.assertIn('is_active', another.fields) self.assertEqual(isinstance(another.is_active, fields.BooleanField), True) self.assertEqual(another.fields['is_active']._resource, another.__class__) self.assertEqual(another.fields['is_active'].instance_name, 'is_active') - self.assert_('aliases' in another.fields) + self.assertIn('aliases', another.fields) self.assertEqual(isinstance(another.aliases, fields.ListField), True) self.assertEqual(another.fields['aliases']._resource, another.__class__) self.assertEqual(another.fields['aliases'].instance_name, 'aliases') - self.assert_('meta' in another.fields) + self.assertIn('meta', another.fields) self.assertEqual(isinstance(another.meta, fields.DictField), True) self.assertEqual(another.fields['meta']._resource, another.__class__) self.assertEqual(another.fields['meta'].instance_name, 'meta') - self.assert_('owed' in another.fields) + self.assertIn('owed', another.fields) self.assertEqual(isinstance(another.owed, fields.DecimalField), True) self.assertEqual(another.fields['owed']._resource, another.__class__) self.assertEqual(another.fields['owed'].instance_name, 'owed') - self.assert_('resource_uri' in another.fields) + self.assertIn('resource_uri', another.fields) self.assertEqual(isinstance(another.resource_uri, fields.CharField), True) self.assertEqual(another.fields['resource_uri']._resource, another.__class__) self.assertEqual(another.fields['resource_uri'].instance_name, 'resource_uri') @@ -358,15 +358,15 @@ nouri = NoUriBasicResource() self.assertEqual(len(nouri.fields), 3) - self.assert_('name' in nouri.fields) + self.assertIn('name', nouri.fields) self.assertEqual(isinstance(nouri.name, fields.CharField), True) self.assertEqual(nouri.fields['name']._resource, nouri.__class__) self.assertEqual(nouri.fields['name'].instance_name, 'name') - self.assert_('view_count' in nouri.fields) + self.assertIn('view_count', nouri.fields) self.assertEqual(isinstance(nouri.view_count, fields.IntegerField), True) self.assertEqual(nouri.fields['view_count']._resource, nouri.__class__) self.assertEqual(nouri.fields['view_count'].instance_name, 'view_count') - self.assert_('date_joined' in nouri.fields) + self.assertIn('date_joined', nouri.fields) self.assertEqual(isinstance(nouri.date_joined, fields.DateTimeField), True) self.assertEqual(nouri.fields['date_joined']._resource, nouri.__class__) self.assertEqual(nouri.fields['date_joined'].instance_name, 'date_joined') @@ -553,7 +553,7 @@ empty_null_bundle = Bundle(obj=obj, data={}) hydrated = nullable.full_hydrate(empty_null_bundle) - self.assertEquals(hydrated.obj.name, "Daniel") + self.assertEqual(hydrated.obj.name, "Daniel") def test_full_hydrate__can_put_null_to_clear_related_value(self): class RelatedBasicResource(BasicResource): @@ -890,8 +890,8 @@ request.method = 'GET' basic_resource_list = json.loads(force_str(basic.get_list(request).content))['objects'] - self.assertEquals(basic_resource_list[0]['name'], 'Daniel') - self.assertEquals(basic_resource_list[0]['date_joined'], u'2010-03-30T09:00:00') + self.assertEqual(basic_resource_list[0]['name'], 'Daniel') + self.assertEqual(basic_resource_list[0]['date_joined'], u'2010-03-30T09:00:00') self.assertNotIn('view_count', basic_resource_list[0]) @@ -1622,7 +1622,7 @@ # some related bits here & self-referential bits later on. resource_1 = RelatedNoteResource() self.assertEqual(len(resource_1.fields), 8) - self.assert_('author' in resource_1.fields) + self.assertIn('author', resource_1.fields) self.assertTrue(isinstance(resource_1.fields['author'], fields.ToOneField)) self.assertEqual(resource_1.fields['author']._resource, resource_1.__class__) self.assertEqual(resource_1.fields['author'].instance_name, 'author') @@ -4522,9 +4522,9 @@ }, obj=Counter()) cr.obj_create(counter_bundle) - self.assertEquals(counter_bundle._create_auth_call_count, 1) - self.assertEquals(counter_bundle.obj.name, "About") - self.assertEquals(counter_bundle.obj.slug, "about") + self.assertEqual(counter_bundle._create_auth_call_count, 1) + self.assertEqual(counter_bundle.obj.name, "About") + self.assertEqual(counter_bundle.obj.slug, "about") def test_obj_update(self): self.assertEqual(Note.objects.all().count(), 6) @@ -4688,9 +4688,9 @@ cr.obj_update(counter_bundle, pk=1) counter = Counter.objects.get(pk=1) - self.assertEquals(counter_bundle._update_auth_call_count, 1) - self.assertEquals(counter_bundle.obj.name, "Signups") - self.assertEquals(counter_bundle.obj.slug, "signups") + self.assertEqual(counter_bundle._update_auth_call_count, 1) + self.assertEqual(counter_bundle.obj.name, "Signups") + self.assertEqual(counter_bundle.obj.slug, "signups") def test_lookup_kwargs_with_identifiers__field_without_attr(self): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/namespaced/tests.py new/django-tastypie-0.15.0/tests/namespaced/tests.py --- old/django-tastypie-0.14.7/tests/namespaced/tests.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/namespaced/tests.py 2024-11-20 22:43:26.000000000 +0100 @@ -18,5 +18,5 @@ self.assertRaises(NoReverseMatch, reverse, 'api_v1_top_level') self.assertRaises(NoReverseMatch, reverse, 'special:api_v1_top_level') - self.assertEquals(reverse('special:api_v1_top_level', kwargs={'api_name': 'v1'}), '/api/v1/') - self.assertEquals(reverse('special:api_dispatch_list', kwargs={'api_name': 'v1', 'resource_name': 'notes'}), '/api/v1/notes/') + self.assertEqual(reverse('special:api_v1_top_level', kwargs={'api_name': 'v1'}), '/api/v1/') + self.assertEqual(reverse('special:api_dispatch_list', kwargs={'api_name': 'v1', 'resource_name': 'notes'}), '/api/v1/notes/') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tests/settings.py new/django-tastypie-0.15.0/tests/settings.py --- old/django-tastypie-0.14.7/tests/settings.py 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tests/settings.py 2024-11-20 22:43:26.000000000 +0100 @@ -59,8 +59,8 @@ 'django.contrib.auth.hashers.CryptPasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', + 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', - 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', ] # Django 5.0 removed this hasher diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-tastypie-0.14.7/tox.ini new/django-tastypie-0.15.0/tox.ini --- old/django-tastypie-0.14.7/tox.ini 2024-04-23 22:25:21.000000000 +0200 +++ new/django-tastypie-0.15.0/tox.ini 2024-11-20 22:43:26.000000000 +0100 @@ -1,10 +1,9 @@ [tox] envlist = - py{3.6,3.7,3.8,3.9,3.10}-dj{3.2,} py{3.8,3.9,3.10,3.11}-dj{4.0,4.1,4.2,dev} - py{3.9,3.10,3.11}-dj{5.0} - py{3.6,3.7,3.8,3.9,3.10,3.11}-docs, - py{3.6,3.7,3.8,3.9,3.10,3.11}-flake8, + py{3.8,3.9,3.10,3.11}-docs, + py{3.9,3.10,3.11}-dj{5.0,5.1} + py{3.8,3.9,3.10,3.11}-flake8, py{3.8,3.9,3.10,3.11}-flake8-strict skipsdist=True @@ -15,20 +14,20 @@ PYTHONPATH = {toxinidir}:{toxinidir}/tests PYTHONWARNINGS = always TESTEXE = {envbindir}/coverage run --append --source=tastypie,tests {envbindir}/django-admin.py - dj{4.0,4.1,4.2,5.0,dev}: TESTEXE = {envbindir}/coverage run --append --source=tastypie,tests {envbindir}/django-admin + dj{4.0,4.1,4.2,5.0,5.1,dev}: TESTEXE = {envbindir}/coverage run --append --source=tastypie,tests {envbindir}/django-admin commands = - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test -p '*' core.tests --settings=settings_core - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test basic.tests --settings=settings_basic - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test related_resource.tests --settings=settings_related - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test alphanumeric.tests --settings=settings_alphanumeric - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test authorization.tests --settings=settings_authorization - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test content_gfk.tests --settings=settings_content_gfk - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test customuser.tests --settings=settings_customuser - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test namespaced.tests --settings=settings_namespaced - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test slashless.tests --settings=settings_slashless - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test validation.tests --settings=settings_validation - dj{3.2,4.0,4.1,4.2,5.0,dev}: {env:TESTEXE} test gis.tests --settings=settings_gis_spatialite + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test -p '*' core.tests --settings=settings_core + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test basic.tests --settings=settings_basic + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test related_resource.tests --settings=settings_related + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test alphanumeric.tests --settings=settings_alphanumeric + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test authorization.tests --settings=settings_authorization + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test content_gfk.tests --settings=settings_content_gfk + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test customuser.tests --settings=settings_customuser + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test namespaced.tests --settings=settings_namespaced + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test slashless.tests --settings=settings_slashless + dj{4.0,4.1,4.2,5.0,5.1,dev}: {env:TESTEXE} test validation.tests --settings=settings_validation + dj{4.0,4.1,4.2,5.0,5.1,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 @@ -37,31 +36,26 @@ flake8-strict: {envbindir}/flake8 --ignore=E128 --max-complexity 10 . basepython = - py3.6: python3.6 - py3.7: python3.7 py3.8: python3.8 py3.9: python3.9 py3.10: python3.10 py3.11: python3.11 deps = - dj3.2: Django>=3.2,<3.3 dj4.0: Django>=4.0,<4.1 dj4.1: Django>=4.1,<4.2 dj4.2: Django>=4.2,<4.3 dj5.0: Django>=5.0,<5.1 + dj5.1: Django>=5.1,<5.2 djdev: https://github.com/django/django/archive/refs/heads/main.zip - dj{3.2,4.0,4.1,4.2,dev}: python3-digest>=1.8b4 - dj{3.2,4.0,4.1,4.2,5.0,dev}: -r{toxinidir}/tests/requirements.txt - dj{3.2,4.0,4.1,4.2,5.0,dev}: -r{toxinidir}/requirements.txt + dj{4.0,4.1,4.2,dev}: python3-digest>=1.8b4 + dj{4.0,4.1,4.2,5.0,5.1,dev}: -r{toxinidir}/tests/requirements.txt + dj{4.0,4.1,4.2,5.0,5.1,dev}: -r{toxinidir}/requirements.txt - py{3.6,3.7}-docs: Django~=3.2 py{3.8,3.9}-docs: Django<4.3 - py{3.10,3.11}-docs: Django~=5.0 - docs: Sphinx - docs: mock - docs: sphinx_rtd_theme + py{3.10,3.11}-docs: Django~=5.1 docs: -r{toxinidir}/requirements.txt + docs: -r{toxinidir}/docs/requirements.txt {flake8,flake8-strict}: flake8 changedir =