mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-10 19:56:59 +03:00
Raise exception when field source is a built-in (#6766)
This commit is contained in:
parent
91ea138406
commit
7179ea9984
|
@ -47,10 +47,24 @@ class empty:
|
|||
pass
|
||||
|
||||
|
||||
class BuiltinSignatureError(Exception):
|
||||
"""
|
||||
Built-in function signatures are not inspectable. This exception is raised
|
||||
so the serializer can raise a helpful error message.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def is_simple_callable(obj):
|
||||
"""
|
||||
True if the object is a callable that takes no arguments.
|
||||
"""
|
||||
# Bail early since we cannot inspect built-in function signatures.
|
||||
if inspect.isbuiltin(obj):
|
||||
raise BuiltinSignatureError(
|
||||
'Built-in function signatures are not inspectable. '
|
||||
'Wrap the function call in a simple, pure Python function.')
|
||||
|
||||
if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)):
|
||||
return False
|
||||
|
||||
|
@ -427,6 +441,18 @@ class Field:
|
|||
"""
|
||||
try:
|
||||
return get_attribute(instance, self.source_attrs)
|
||||
except BuiltinSignatureError as exc:
|
||||
msg = (
|
||||
'Field source for `{serializer}.{field}` maps to a built-in '
|
||||
'function type and is invalid. Define a property or method on '
|
||||
'the `{instance}` instance that wraps the call to the built-in '
|
||||
'function.'.format(
|
||||
serializer=self.parent.__class__.__name__,
|
||||
field=self.field_name,
|
||||
instance=instance.__class__.__name__,
|
||||
)
|
||||
)
|
||||
raise type(exc)(msg)
|
||||
except (KeyError, AttributeError) as exc:
|
||||
if self.default is not empty:
|
||||
return self.get_default()
|
||||
|
|
|
@ -14,7 +14,9 @@ from django.utils.timezone import activate, deactivate, override, utc
|
|||
import rest_framework
|
||||
from rest_framework import exceptions, serializers
|
||||
from rest_framework.compat import ProhibitNullCharactersValidator
|
||||
from rest_framework.fields import DjangoImageField, is_simple_callable
|
||||
from rest_framework.fields import (
|
||||
BuiltinSignatureError, DjangoImageField, is_simple_callable
|
||||
)
|
||||
|
||||
# Tests for helper functions.
|
||||
# ---------------------------
|
||||
|
@ -86,6 +88,18 @@ class TestIsSimpleCallable:
|
|||
|
||||
assert is_simple_callable(ChoiceModel().get_choice_field_display)
|
||||
|
||||
def test_builtin_function(self):
|
||||
# Built-in function signatures are not easily inspectable, so the
|
||||
# current expectation is to just raise a helpful error message.
|
||||
timestamp = datetime.datetime.now()
|
||||
|
||||
with pytest.raises(BuiltinSignatureError) as exc_info:
|
||||
is_simple_callable(timestamp.date)
|
||||
|
||||
assert str(exc_info.value) == (
|
||||
'Built-in function signatures are not inspectable. Wrap the '
|
||||
'function call in a simple, pure Python function.')
|
||||
|
||||
def test_type_annotation(self):
|
||||
# The annotation will otherwise raise a syntax error in python < 3.5
|
||||
locals = {}
|
||||
|
@ -206,6 +220,18 @@ class TestSource:
|
|||
|
||||
assert 'method call failed' in str(exc_info.value)
|
||||
|
||||
def test_builtin_callable_source_raises(self):
|
||||
class BuiltinSerializer(serializers.Serializer):
|
||||
date = serializers.ReadOnlyField(source='timestamp.date')
|
||||
|
||||
with pytest.raises(BuiltinSignatureError) as exc_info:
|
||||
BuiltinSerializer({'timestamp': datetime.datetime.now()}).data
|
||||
|
||||
assert str(exc_info.value) == (
|
||||
'Field source for `BuiltinSerializer.date` maps to a built-in '
|
||||
'function type and is invalid. Define a property or method on '
|
||||
'the `dict` instance that wraps the call to the built-in function.')
|
||||
|
||||
|
||||
class TestReadOnly:
|
||||
def setup(self):
|
||||
|
|
Loading…
Reference in New Issue
Block a user