Fix call_view_method warnings for view classes

master
Cristi Vîjdea 2018-12-28 16:19:09 +02:00
parent 2bc9addc99
commit b8512bda8e
2 changed files with 30 additions and 5 deletions

View File

@ -1,3 +1,5 @@
import six
import inspect import inspect
import logging import logging
@ -12,21 +14,44 @@ NotHandled = object()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def is_callable_method(cls_or_instance, method_name):
method = getattr(cls_or_instance, method_name)
if inspect.ismethod(method) and getattr(method, '__self__', None):
# bound classmethod or instance method
return method, True
try:
# inspect.getattr_static was added in python 3.2
from inspect import getattr_static
# on python 3, both unbound instance methods (i.e. getattr(cls, mth)) and static methods are plain functions
# getattr_static allows us to check the type of the method descriptor; for `@staticmethod` this is staticmethod
return method, isinstance(getattr_static(cls_or_instance, method_name, None), staticmethod)
except ImportError:
# python 2 still has unbound methods, so ismethod <=> !staticmethod TODO: remove when dropping python 2.7
return method, not inspect.ismethod(method)
def call_view_method(view, method_name, fallback_attr=None, default=None): def call_view_method(view, method_name, fallback_attr=None, default=None):
"""Call a view method which might throw an exception. If an exception is thrown, log an informative error message """Call a view method which might throw an exception. If an exception is thrown, log an informative error message
and return the value of fallback_attr, or default if not present. and return the value of fallback_attr, or default if not present. The method must be callable without any arguments
except cls or self.
:param rest_framework.views.APIView view: :param view: view class or instance; if a class is passed, instance methods won't be called
:type view: rest_framework.views.APIView or type[rest_framework.views.APIView]
:param str method_name: name of a method on the view :param str method_name: name of a method on the view
:param str fallback_attr: name of an attribute on the view to fall back on, if calling the method fails :param str fallback_attr: name of an attribute on the view to fall back on, if calling the method fails
:param default: default value if all else fails :param default: default value if all else fails
:return: view method's return value, or value of view's fallback_attr, or default :return: view method's return value, or value of view's fallback_attr, or default
:rtype: any or None
""" """
if hasattr(view, method_name): if hasattr(view, method_name):
try: try:
return getattr(view, method_name)() view_method, is_callabale = is_callable_method(view, method_name)
if is_callabale:
return view_method()
except Exception: # pragma: no cover except Exception: # pragma: no cover
logger.warning("view's %s.get_parsers raised exception during schema generation; use " logger.warning("view's %s raised exception during schema generation; use "
"`getattr(self, 'swagger_fake_view', False)` to detect and short-circuit this", "`getattr(self, 'swagger_fake_view', False)` to detect and short-circuit this",
type(view).__name__, exc_info=True) type(view).__name__, exc_info=True)

View File

@ -177,7 +177,7 @@ def get_queryset_from_view(view, serializer=None):
:return: queryset or ``None`` :return: queryset or ``None``
""" """
try: try:
queryset = call_view_method(view, 'get_queryset', 'queryset', None) queryset = call_view_method(view, 'get_queryset', 'queryset')
if queryset is not None and serializer is not None: if queryset is not None and serializer is not None:
# make sure the view is actually using *this* serializer # make sure the view is actually using *this* serializer