This commit is contained in:
Ryan P Kilby 2016-10-10 00:33:39 +00:00 committed by GitHub
commit e98b1215b7
4 changed files with 89 additions and 15 deletions

View File

@ -14,7 +14,6 @@ env:
- TOX_ENV=py35-django18 - TOX_ENV=py35-django18
- TOX_ENV=py34-django18 - TOX_ENV=py34-django18
- TOX_ENV=py33-django18 - TOX_ENV=py33-django18
- TOX_ENV=py32-django18
- TOX_ENV=py27-django18 - TOX_ENV=py27-django18
- TOX_ENV=py27-django110 - TOX_ENV=py27-django110
- TOX_ENV=py35-django110 - TOX_ENV=py35-django110

View File

@ -49,20 +49,34 @@ class empty:
pass pass
def is_simple_callable(obj): if six.PY3:
""" def is_simple_callable(obj):
True if the object is a callable that takes no arguments. """
""" True if the object is a callable that takes no arguments.
function = inspect.isfunction(obj) """
method = inspect.ismethod(obj) if not callable(obj):
return False
if not (function or method): sig = inspect.signature(obj)
return False params = sig.parameters.values()
return all(param.default != param.empty for param in params)
args, _, _, defaults = inspect.getargspec(obj) else:
len_args = len(args) if function else len(args) - 1 def is_simple_callable(obj):
len_defaults = len(defaults) if defaults else 0 function = inspect.isfunction(obj)
return len_args <= len_defaults method = inspect.ismethod(obj)
if not (function or method):
return False
if method:
is_unbound = obj.im_self is None
args, _, _, defaults = inspect.getargspec(obj)
len_args = len(args) if function or is_unbound else len(args) - 1
len_defaults = len(defaults) if defaults else 0
return len_args <= len_defaults
def get_attribute(instance, attrs): def get_attribute(instance, attrs):

View File

@ -1,6 +1,7 @@
import datetime import datetime
import os import os
import re import re
import unittest
import uuid import uuid
from decimal import Decimal from decimal import Decimal
@ -11,6 +12,67 @@ from django.utils import six, timezone
import rest_framework import rest_framework
from rest_framework import serializers from rest_framework import serializers
from rest_framework.fields import is_simple_callable
try:
import typings
except ImportError:
typings = False
# Tests for helper functions.
# ---------------------------
class TestIsSimpleCallable:
def test_method(self):
class Foo:
@classmethod
def classmethod(cls):
pass
def valid(self):
pass
def valid_kwargs(self, param='value'):
pass
def invalid(self, param):
pass
assert is_simple_callable(Foo.classmethod)
# unbound methods
assert not is_simple_callable(Foo.valid)
assert not is_simple_callable(Foo.valid_kwargs)
assert not is_simple_callable(Foo.invalid)
# bound methods
assert is_simple_callable(Foo().valid)
assert is_simple_callable(Foo().valid_kwargs)
assert not is_simple_callable(Foo().invalid)
def test_function(self):
def simple():
pass
def valid(param='value', param2='value'):
pass
def invalid(param, param2='value'):
pass
assert is_simple_callable(simple)
assert is_simple_callable(valid)
assert not is_simple_callable(invalid)
@unittest.skipUnless(typings, 'requires python 3.5')
def test_type_annotation(self):
# The annotation will otherwise raise a syntax error in python < 3.5
exec("def valid(param: str='value'): pass", locals())
valid = locals()['valid']
assert is_simple_callable(valid)
# Tests for field keyword arguments and core functionality. # Tests for field keyword arguments and core functionality.

View File

@ -4,7 +4,7 @@ addopts=--tb=short
[tox] [tox]
envlist = envlist =
py27-{lint,docs}, py27-{lint,docs},
{py27,py32,py33,py34,py35}-django18, {py27,py33,py34,py35}-django18,
{py27,py34,py35}-django19, {py27,py34,py35}-django19,
{py27,py34,py35}-django110, {py27,py34,py35}-django110,
{py27,py34,py35}-django{master} {py27,py34,py35}-django{master}
@ -25,7 +25,6 @@ basepython =
py35: python3.5 py35: python3.5
py34: python3.4 py34: python3.4
py33: python3.3 py33: python3.3
py32: python3.2
py27: python2.7 py27: python2.7
[testenv:py27-lint] [testenv:py27-lint]