1

I have an API that talks to different APIs, most of them are exposed as restful web services. I am using python requests and I am using requests_toolbet to log all the requests/responses for debugging purposes. My code looks like:

def call_api(self, paramas):
    print('----------> calling API')
    url = self.base_url + params...
    headers = {'Authorization': 'Bearer ' + BEARER_TOKEN}
    resp = requests.get(url, headers=headers)
    print(dump.dump_all(resp).decode('utf-8'))
    return resp.json()

As you might imagine, every single request have a pretty similar code for logging:

    resp = requests.get(url, headers=headers)
    print(dump.dump_all(resp).decode('utf-8'))

I was wondering if there is a way that I can remove the same code using decorators or so. I am a newbie in python so any help is really appreciated.

Rene Enriquez
  • 1,071
  • 10
  • 29

2 Answers2

3

There is a way to do this with decorators that could shorten your code significantly, but I'm not sure that decorators are the way to go in this case. In Python, it is much preferred to write explicit code that clearly represents what is happening, and doesn't obfuscate what's actually happening. Here's a better (in my opinion) way to approach this problem: make it its own function, like so...

import requests, dump

def make_request(url, headers):
  resp = requests.get(url, headers=headers)
  print(dump.dump_all(resp).decode('utf-8'))
  return resp.json()

class MyAPIAdapter:
  base_url = 'https://www.example.com/'

  def call_api(self, params):
    url = self.base_url + params...
    headers = {'Authorization': 'Bearer ' + BEARER_TOKEN}
    return make_request(url, headers)

This way, we can clearly see that the call_api execution does actually call out to something else, and other code is being executed,

Of course, there technically is a way to do this with a decorator, but it ends up being more lines of code, and kind of confusing, hence the preferred method above.

import requests, dump

def basic_request(func):
  def wrapper(*args, **kwargs):
    print('Calling %s...' % func.__name__)
    url, headers = func(*args, **kwargs)
    resp = requests.get(url, headers=headers)
    print(dump.dump_all(resp).decode('utf-8'))
    return resp.json()

  return wrapper

class MyAPIAdapter:
  base_url = 'https://www.example.com/'

  @basic_request
  def call_api(self, params):
    url = self.base_url + params...
    headers = {'Authorization': 'Bearer ' + BEARER_TOKEN}
    return (url, headers)
David Culbreth
  • 2,337
  • 16
  • 25
0

Yes, you can use decorators. You can write your own for simple cases. Also try functool wraps.

An example from an article well explained

>>> import functools
>>> def echo(fn):
...     @functools.wraps(fn)
...     def wrapped(*v, **k):
...         ....
...    return wrapped
...
>>> @echo
>>> def f(x):
...    " I'm f, don't mess with me! "
...    pass
>>> f.__name__
'f'
>>> f.func_doc
" I'm f, don't mess with me! "
>>> f(('spam', 'spam', 'spam!'))
f(('spam', 'spam', 'spam!'))

One more related answer: What does functools.wraps do?

drd
  • 565
  • 2
  • 8