From 119dff7a067272d7a73c9cbff96b2f168a859d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20T=C3=B6rnqvist?= Date: Sun, 19 May 2013 18:01:12 +0300 Subject: [PATCH 1/4] Introduce validator metaclass --- rest_framework/class_validator.py | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 rest_framework/class_validator.py diff --git a/rest_framework/class_validator.py b/rest_framework/class_validator.py new file mode 100644 index 000000000..68d3d9ba3 --- /dev/null +++ b/rest_framework/class_validator.py @@ -0,0 +1,41 @@ +"""Attribute checking for Python classes +""" + +class InvalidAttributeError(AttributeError): + """ + Use this for invalid attributes because AttributeError means not found + """ + + pass + + +class ValidatorMeta(type): + """ + Metaclass to guard against setting unrecognized attributes + Set it as __metaclass__ as low in the inheritance chain as possible + """ + def __new__(cls, name, parents, kwargs): + """Creates the new class, and sees if it has only known attributes + """ + new_cls = type.__new__(cls, name, parents, kwargs) + + ## Fail only if the new class defines an api + if not new_cls.__dict__.has_key('valid_attributes'): + return new_cls + + ## Do some sanity checks to not fail in vain + for attr, val in kwargs.items(): + if attr == 'valid_attributes': + continue + if attr.startswith('_'): + continue + # Methods are always allowed + if callable(val): + continue + + # Ensure validity + if not attr in new_cls.valid_attributes: + raise InvalidAttributeError(attr) + + return new_cls + From c2a17ab6a3eb2e1221dd7804e3b7297667bba984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20T=C3=B6rnqvist?= Date: Sun, 19 May 2013 18:03:27 +0300 Subject: [PATCH 2/4] Conceptual design for strong attributes in APIView --- rest_framework/views.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rest_framework/views.py b/rest_framework/views.py index 555fa2f40..7d19847bc 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -14,6 +14,14 @@ from rest_framework.utils.formatting import get_view_name, get_view_description class APIView(View): + __metaclass__ = ValidatorMeta + valid_attributes = set(('settings', 'renderer_classes', 'parser_classes', + 'authentication_classes', 'throttle_classes', + 'permission_classes', 'content_negotiation_class', + 'allowed_methods', 'default_response_headers', + 'as_view', + )) + settings = api_settings renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES From 5314927b6b12e9dcfb1c09f18392f44b8b98e365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20T=C3=B6rnqvist?= Date: Sun, 19 May 2013 18:04:17 +0300 Subject: [PATCH 3/4] Demonstration (in code, look at the diff) of attribute validation --- rest_framework/tests/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py index 994cf6dc3..eb0a78f65 100644 --- a/rest_framework/tests/views.py +++ b/rest_framework/tests/views.py @@ -12,6 +12,10 @@ factory = RequestFactory() class BasicView(APIView): + ## Uncomment this to see how attribute validation works + # valid_attributes = ('foo', ) + # foo = 'foo' + # bar = 'bar' def get(self, request, *args, **kwargs): return Response({'method': 'GET'}) From 365ad2f6007ca1b9fb5a617ee2dd3d0f219c1f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20T=C3=B6rnqvist?= Date: Sun, 19 May 2013 18:15:58 +0300 Subject: [PATCH 4/4] Oops! Forgot the import from the push --- rest_framework/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework/views.py b/rest_framework/views.py index 7d19847bc..343292e97 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -10,6 +10,7 @@ from rest_framework.compat import View from rest_framework.response import Response from rest_framework.request import Request from rest_framework.settings import api_settings +from rest_framework.class_validator import ValidatorMeta from rest_framework.utils.formatting import get_view_name, get_view_description