35

Is there a way to pass an HTTP verb (PATCH/POST) to a function and dynamically use that verb for Python requests?

For example, I want this function to take a 'verb' variable which is only called internally and will either = post/patch.

def dnsChange(self, zID, verb):
    for record in config.NEW_DNS:
        ### LINE BELOW IS ALL THAT MATTERS TO THIS QUESTION 
        json = requests.verb(headers=self.auth, url=self.API + '/zones/' + str(zID) + '/dns_records', data={"type":record[0], "name":record[1], "content":record[2]})
        key = record[0] + "record with host " + record[1]
        result = json.loads(json.text)
        self.apiSuccess(result,key,value)

I realize I cannot requests.'verb' as I have above, it's meant to illustrate the question. Is there a way to do this or something similar? I'd like to avoid an:

if verb == 'post':
    json = requests.post(headers=self.auth, url=self.API + '/zones/' + str(zID) + '/dns_records', data={"type":record[0], "name":record[1], "content":record[2]}
else:
    json = requests.patch(headers=self.auth, url=self.API + '/zones/' + str(zID) + '/dns_records', data={"type":record[0], "name":record[1], "content":record[2]}

Thanks guys!

HectorOfTroy407
  • 1,737
  • 5
  • 21
  • 31

2 Answers2

66

Just use the requests.request() method. First argument is the HTTP verb that you want to use. requests.get(), requests.post(), etc. are just aliases to request('GET'), request('POST'): see the doc

Your code becomes:

verb = 'POST'
response = requests.request(
     verb,
     headers=self.auth,
     url=self.API + '/zones/' + str(zID) + '/dns_records',
     data={"type":record[0], "name":record[1], "content":record[2]}
)
Guillaume
  • 5,497
  • 3
  • 24
  • 42
19

With the request library, the requests.request method can be relied on directly (as Guillaume's answer suggested).

However, when encountering against libraries that don't have a generic method for methods that have similar calling signatures, getattr can be supplied with the name of the desired method as a string with a default value. Maybe like

action = getattr(requests, verb, None)
if action:
    action(headers=self.auth, url=self.API + '/zones/' + str(zID) + '/dns_records', data={"type":record[0], "name":record[1], "content":record[2]})
else:
    # handle invalid action as the default value was returned

For the default value it can be a proper action, or just leave it out and an exception will be raised; it's up to you how you want to handle it. I left it as None so you can deal with alternative case in the else section.

metatoaster
  • 17,419
  • 5
  • 55
  • 66
  • Thanks for pointing me in this directly. Majorly badass. "getattr(object, name[, default]) Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised." – HectorOfTroy407 Sep 05 '16 at 07:18
  • 1
    Your solution works but relying on `getattr()` is generally considered ugly. I suggest you see my solution, which is way better because it is the way the `requests` lib is supposed to be used. – Guillaume Feb 09 '17 at 10:23
  • 1
    @Guillaume Yeah, agree with you, I wrote the answer without regard to the underlying library so it was written as one that can work against libraries that don't implement a generic method like `request` does. I do wonder why have you waited more than a year to bring this up. – metatoaster Feb 09 '17 at 11:09