Skip to content

Commit

Permalink
Refs #31949 -- Made @vary_on_(cookie/headers) decorators work with as…
Browse files Browse the repository at this point in the history
…ync functions.
  • Loading branch information
LomaxOnTheRun authored and felixxm committed Jul 10, 2023
1 parent fb1c763 commit b7a17b0
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 6 deletions.
22 changes: 16 additions & 6 deletions django/views/decorators/vary.py
@@ -1,5 +1,7 @@
from functools import wraps

from asgiref.sync import iscoroutinefunction

from django.utils.cache import patch_vary_headers


Expand All @@ -16,13 +18,21 @@ def index(request):
"""

def decorator(func):
@wraps(func)
def inner_func(*args, **kwargs):
response = func(*args, **kwargs)
patch_vary_headers(response, headers)
return response
if iscoroutinefunction(func):

async def _view_wrapper(request, *args, **kwargs):
response = await func(request, *args, **kwargs)
patch_vary_headers(response, headers)
return response

else:

def _view_wrapper(request, *args, **kwargs):
response = func(request, *args, **kwargs)
patch_vary_headers(response, headers)
return response

return inner_func
return wraps(func)(_view_wrapper)

return decorator

Expand Down
2 changes: 2 additions & 0 deletions docs/releases/5.0.txt
Expand Up @@ -268,6 +268,8 @@ Decorators
* :func:`~django.views.decorators.http.require_GET`
* :func:`~django.views.decorators.http.require_POST`
* :func:`~django.views.decorators.http.require_safe`
* :func:`~django.views.decorators.vary.vary_on_cookie`
* :func:`~django.views.decorators.vary.vary_on_headers`
* ``xframe_options_deny()``
* ``xframe_options_sameorigin()``
* ``xframe_options_exempt()``
Expand Down
2 changes: 2 additions & 0 deletions docs/topics/async.txt
Expand Up @@ -94,6 +94,8 @@ view functions:
* :func:`~django.views.decorators.http.require_GET`
* :func:`~django.views.decorators.http.require_POST`
* :func:`~django.views.decorators.http.require_safe`
* :func:`~django.views.decorators.vary.vary_on_cookie`
* :func:`~django.views.decorators.vary.vary_on_headers`
* ``xframe_options_deny()``
* ``xframe_options_sameorigin()``
* ``xframe_options_exempt()``
Expand Down
8 changes: 8 additions & 0 deletions docs/topics/http/decorators.txt
Expand Up @@ -115,13 +115,21 @@ caching based on specific request headers.

.. function:: vary_on_cookie(func)

.. versionchanged:: 5.0

Support for wrapping asynchronous view functions was added.

.. function:: vary_on_headers(*headers)

The ``Vary`` header defines which request headers a cache mechanism should take
into account when building its cache key.

See :ref:`using vary headers <using-vary-headers>`.

.. versionchanged:: 5.0

Support for wrapping asynchronous view functions was added.

.. module:: django.views.decorators.cache

Caching
Expand Down
69 changes: 69 additions & 0 deletions tests/decorators/test_vary.py
@@ -0,0 +1,69 @@
from asgiref.sync import iscoroutinefunction

from django.http import HttpRequest, HttpResponse
from django.test import SimpleTestCase
from django.views.decorators.vary import vary_on_cookie, vary_on_headers


class VaryOnHeadersTests(SimpleTestCase):
def test_wrapped_sync_function_is_not_coroutine_function(self):
def sync_view(request):
return HttpResponse()

wrapped_view = vary_on_headers()(sync_view)
self.assertIs(iscoroutinefunction(wrapped_view), False)

def test_wrapped_async_function_is_coroutine_function(self):
async def async_view(request):
return HttpResponse()

wrapped_view = vary_on_headers()(async_view)
self.assertIs(iscoroutinefunction(wrapped_view), True)

def test_vary_on_headers_decorator(self):
@vary_on_headers("Header", "Another-header")
def sync_view(request):
return HttpResponse()

response = sync_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Header, Another-header")

async def test_vary_on_headers_decorator_async_view(self):
@vary_on_headers("Header", "Another-header")
async def async_view(request):
return HttpResponse()

response = await async_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Header, Another-header")


class VaryOnCookieTests(SimpleTestCase):
def test_wrapped_sync_function_is_not_coroutine_function(self):
def sync_view(request):
return HttpResponse()

wrapped_view = vary_on_cookie(sync_view)
self.assertIs(iscoroutinefunction(wrapped_view), False)

def test_wrapped_async_function_is_coroutine_function(self):
async def async_view(request):
return HttpResponse()

wrapped_view = vary_on_cookie(async_view)
self.assertIs(iscoroutinefunction(wrapped_view), True)

def test_vary_on_cookie_decorator(self):
@vary_on_cookie
def sync_view(request):
return HttpResponse()

response = sync_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Cookie")

async def test_vary_on_cookie_decorator_async_view(self):
@vary_on_cookie
async def async_view(request):
return HttpResponse()

response = await async_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Cookie")

0 comments on commit b7a17b0

Please sign in to comment.