Source code for boxsdk.util.api_call_decorator

# coding: utf-8

from __future__ import unicode_literals, absolute_import

from functools import update_wrapper, wraps

from ..object.cloneable import Cloneable


[docs]def api_call(method): """ Designates the decorated method as one that makes a Box API call. The decorated method can then accept a new keyword argument `extra_network_parameters`, a dictionary of key-value pairs to be passed to the network layer for API calls made by the method. The decorated method must belong to a subclass of `Cloneable` as using this decorator and then passing a `extra_network_parameters` parameter to the method will cause the object's clone method to be called. :param method: The method to decorate. :type method: `callable` :return: A wrapped method that can pass extra request data to the network layer. :rtype: :class:`APICallWrapper` """ return APICallWrapper(method)
[docs]class APICallWrapper(object): def __init__(self, func_that_makes_an_api_call): super(APICallWrapper, self).__init__() self._func_that_makes_an_api_call = func_that_makes_an_api_call self.__name__ = func_that_makes_an_api_call.__name__ update_wrapper(self, func_that_makes_an_api_call) def __call__(self, cloneable_instance, *args, **kwargs): return self.__get__(cloneable_instance, type(cloneable_instance))(*args, **kwargs) def __get__(self, _instance, owner): # `APICallWrapper` is imitating a function. For native functions, # ```func.__get__(None, cls)``` always returns `func`. if _instance is None: return self if isinstance(owner, type) and not issubclass(owner, Cloneable): raise TypeError( "descriptor {name!r} must be owned by a 'Cloneable' subclass, not {owner.__name__}" .format(name=self.__name__, owner=owner) ) expected_type = owner or Cloneable if not isinstance(_instance, expected_type): raise TypeError( "descriptor {name!r} for {expected_type.__name__!r} objects doesn't apply to {instance.__class__.__name__!r} object" .format(name=self.__name__, expected_type=expected_type, instance=_instance) ) @wraps(self._func_that_makes_an_api_call) def call(instance, *args, **kwargs): extra_network_parameters = kwargs.pop('extra_network_parameters', None) if extra_network_parameters: # If extra_network_parameters is specified, then clone the instance, and specify the parameters # as the defaults to be used. instance = instance.clone(instance.session.with_default_network_request_kwargs(extra_network_parameters)) method = self._func_that_makes_an_api_call.__get__(instance, owner) return method(*args, **kwargs) # Since the caller passed a non-`None` instance to `__get__()`, they # want a bound method back, not an unbound function. Thus, we must bind # `call()` to `_instance` and then return that bound method. return call.__get__(_instance, owner)