Skip to content

Commit

Permalink
Add setattr guard to check that correct attr is being set
Browse files Browse the repository at this point in the history
  • Loading branch information
hugorodgerbrown committed Sep 11, 2023
1 parent 178ce00 commit d8e3b40
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
21 changes: 21 additions & 0 deletions anonymiser/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,27 @@ class BaseAnonymiser:
# override with a list of fields to exclude from anonymisation report
exclude_rules = (lambda f: f.is_relation or isinstance(f, models.AutoField),)

def __setattr__(self, __name: str, __value: Any) -> None:
"""
Prevent setting of attribute on the anonymiser itself.
This is a common mistake when writing anonymiser functions -
inside the `anonymise_FOO` method you call `self.FOO = "bar"`
instead of `obj.FOO = "bar"`, because that's the natural way to
write it.
This will raise an AttributeError if you try to set an attribute
that looks like it maps to an anonymiser method.
"""
if hasattr(self, f"anonymise_{__name}"):
raise AttributeError(
"Cannot set anonymiser attributes directly - did you mean to "
"use 'obj' instead of 'self' in method "
f"`{self.__class__.__name__}.anonymise_{__name}`?"
)
super().__setattr__(__name, __value)

def get_model_fields(self) -> list[models.Field]:
"""Return a list of fields on the model."""
if not self.model:
Expand Down
8 changes: 8 additions & 0 deletions tests/anon.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ class UserAnonymiser(BaseAnonymiser):

def anonymise_first_name(self, obj: User) -> None:
obj.first_name = "Anonymous"


class BadUserAnonymiser(BaseAnonymiser):
model = User

def anonymise_first_name(self, obj: User) -> None:
# this is not allowed - should be obj.first_name.
self.first_name = "Anonymous"
7 changes: 6 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from anonymiser.models import FieldSummaryData

from .anon import UserAnonymiser
from .anon import BadUserAnonymiser, UserAnonymiser
from .models import User


Expand Down Expand Up @@ -96,3 +96,8 @@ def test_anonymise_queryset(
) -> None:
assert user_anonymiser.anonymise_queryset(User.objects.none()) == 0
assert user_anonymiser.anonymise_queryset(User.objects.all()) == 1


def test_bad_anonymiser() -> None:
with pytest.raises(AttributeError):
BadUserAnonymiser().anonymise_field(User(), "first_name")

0 comments on commit d8e3b40

Please sign in to comment.