Edit: I wanted to explain why implementing this as a decorator isn't a great idea. On a fundamental level, a decorator is just a modification of a function's behavior with another function. However, typically the desired net result of said function should not be substantially modified, because that makes it harder to read and identify what's actually happening. In the ideal case, the reader should be able to basically ignore the decorator for the immediate concerns. For example, creating a decorator to log the runtime or success of a function's execution would not inherently modify the shape of the input, or its expected result.
In this particular case, it could be confusing to a future reader (and to your IDE's Language server) that the nominally returned result from the function is fundamentally different than the actual resulting value -- the returned value is a tuple[str, str]
containing (url, headers)
, where as the actual result after passing through is some arbitrary value from a JSON decode. I mean, sure you can indicate this type on the function's result type, but that's still directly contradicting what would be returned in the function itself.
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)