Access parameters in parameterized decorators
<p>I'm trying to understand python decorator from this <a href="https://realpython.com/blog/python/primer-on-python-decorators/" rel="nofollow">doc</a>. And was writing my own decorator required to handle API Exceptions.</p>

<p>But in my decorator, I'm not getting how to access arguments (<code>method</code>, <code>api</code>, and  <code>data</code>) inside my Custom Decorator. </p>

<p>I know, I haven't passed <code>method</code> anywhere because I'm not getting where to pass so that I can accept it in my decorator.</p>

<p>Here is my decorator:</p>

<pre><code>import requests as r
import json
def handle_api_exceptions(http_api_call):
    """ """

    def wrapper(*args, **kwargs):
        """ """
        response = {}
        try:
            response = http_api_call(*args, **kwargs)
            if response.ok:
                result =  response.json()
                if result.get('code', 200) in INVALID_STATUS_CODES: #INVALID_STATUS_CODES = [1801, 1803, 1806,... ]
                    response = {"data":{}, "status":False}
                else:
                    return result
            else:
                capture_failed_requests(method, api, data, response, 
                                    error=None, reason=response.reason)
                return {}
        except r.exceptions.ConnectionError as e:
            capture_failed_requests(method, api, data, response, error=e,
                                         reason="Connection Aborted")
            return {}
        except json.decoder.JSONDecodeError as e:
            capture_failed_requests(method, api, data, response, error=e,
                                         reason="Invalid Response")
            return {}
        except r.exceptions.ReadTimeout as e:
            capture_failed_requests(method, api, data, response, error=e,
                                         reason="Request Timed Out")
            return {} 
        except Exception as e:
            capture_failed_requests(method, api, data, response, error=e,
                                         reason="Internal Server Error")
        return {}
    return wrapper
</code></pre>

<p>Custom GET, POST API requests:</p>

<pre><code>@handle_api_exceptions
def get(self, api, data, token=None):
    """ """
    if token:data.update(self.get_token(token))
    response = r.get(api, data, verify=self.config.SSL_VERIFY,
                      timeout=self.config.REQUEST_TIMEOUT)
    return response

@handle_api_exceptions
def post(self, api, data, token=None):
    """ """
    if token:
        data.update(self.get_secret_token(token))
    response = r.post(api, data, verify=self.config.SSL_VERIFY,
                      timeout=self.config.REQUEST_TIMEOUT)
    return response        


def post_call(self):
    """ """
    api = "http://192.168.0.24/api/v1/reset/"
    data = {"k1":[], "k2":{}, "id":123456} #-- Some Key val
    return self.post(api, data, token="SOME_SECRET_TOKEN")
</code></pre>

<p>Query is : How to pass <code>method</code>, <code>api</code>, and  <code>data</code> in <code>capture_failed_requests()</code>?</p>

<p><code>data</code> and <code>api</code> are in the <code>args</code> inside <code>wrapper</code>. <code>method</code> you will have to provide separately, e.g. by parameterising the decorator (see e.g. <a href="http://stackoverflow.com/q/5929107/3001761">python decorators with parameters</a>). You could therefore do this as follows:</p>

<pre><code>def handle_api_exceptions(method):
    def decorator(http_api_call): 
        def wrapper(api, data, *args, **kwargs):
            ...
</code></pre>

<p>and your decorating would become e.g.</p>

<pre><code>@handle_api_exceptions(method='GET')
def get(self, api, data, token=None):
    ...
</code></pre>

<p>Alternatively, you could use <code>method = http_api_call.__name__</code> (which would give you <code>'get'</code>), and avoid the extra layer of nesting and duplication of the method name.</p>

<hr>

<p>Note that I have removed the empty docstrings - either write an actual docstring (I like <a href="http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html" rel="nofollow">Google-style</a>, but YMMV) or don't have one at all. If your linting rules <em>require</em> docstrings, that's because whoever set it up wants you to write <em>useful</em> ones, not just cheat on it.</p>

python python-decorators