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 <[email protected]>
+
+- 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 ``[email protected]``
+
+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 =