Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions manage_breast_screening/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,28 @@ def basic_auth_exempt(view_func: Callable) -> Callable:

Uses a registry approach that is decorator-order independent.
"""
_basic_auth_exempt_views.add(view_func)
_basic_auth_exempt_views.add(view_func_identifier(view_func))
return view_func


def is_basic_auth_exempt(view_func: Callable) -> bool:
"""Check if a view function is exempt from BasicAuthMiddleware."""
return view_func in _basic_auth_exempt_views
return view_func_identifier(view_func) in _basic_auth_exempt_views


def current_provider_exempt(view_func: Callable) -> Callable:
"""Mark a view function as exempt from CurrentProviderMiddleware.

Uses a registry approach that is decorator-order independent.
"""
_current_provider_exempt_views.add(view_func)
_current_provider_exempt_views.add(view_func_identifier(view_func))
return view_func


def is_current_provider_exempt(view_func: Callable) -> bool:
"""Check if a view function is exempt from CurrentProviderMiddleware."""
return view_func in _current_provider_exempt_views
return view_func_identifier(view_func) in _current_provider_exempt_views


def view_func_identifier(view_func: Callable) -> str:
return f"{view_func.__module__}.{view_func.__qualname__}"
36 changes: 32 additions & 4 deletions manage_breast_screening/core/tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from functools import wraps

from manage_breast_screening.core.decorators import (
_basic_auth_exempt_views,
basic_auth_exempt,
is_basic_auth_exempt,
view_func_identifier,
)


Expand All @@ -17,7 +20,7 @@ def view_func():
basic_auth_exempt(view_func)

assert is_basic_auth_exempt(view_func)
assert view_func in _basic_auth_exempt_views
assert view_func_identifier(view_func) in _basic_auth_exempt_views

def test_multiple_decorations_maintain_unique_set(self):
def view_func():
Expand All @@ -30,7 +33,7 @@ def view_func():

# Should only appear once in the registry
assert len(_basic_auth_exempt_views) == 1
assert view_func in _basic_auth_exempt_views
assert view_func_identifier(view_func) in _basic_auth_exempt_views
assert is_basic_auth_exempt(view_func)

def test_different_views_are_tracked_separately(self):
Expand All @@ -52,7 +55,7 @@ def view_func():
pass

assert not is_basic_auth_exempt(view_func)
assert view_func not in _basic_auth_exempt_views
assert view_func_identifier(view_func) not in _basic_auth_exempt_views

def test_decorator_order_independence(self):
def other_decorator(func):
Expand All @@ -70,8 +73,33 @@ def view_func():

# Original function should be in registry regardless of order
assert is_basic_auth_exempt(view_func)
assert view_func in _basic_auth_exempt_views
assert view_func_identifier(view_func) in _basic_auth_exempt_views

# Decorated functions should work the same way
assert decorated1 is not view_func # other_decorator wraps it
assert decorated2 is not view_func # other_decorator wraps it

def test_functools_wraps_interoperability(self):
def functools_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)

return wraps(func)(wrapper)

@functools_decorator
@basic_auth_exempt
@functools_decorator
def view_func():
pass

assert is_basic_auth_exempt(view_func)
assert view_func_identifier(view_func) in _basic_auth_exempt_views

def test_view_func_identifier(self):
def view_func():
pass

assert (
view_func_identifier(view_func)
== f"{view_func.__module__}.{view_func.__qualname__}"
)
Loading