3

I have a class called Client which uses the requests module to interact with a service. It has methods like:

def get_resource(self, url, headers):
    response = requests.get(url, headers, auth=self.auth)
    return response

Now I want to call some methods before and after each call to the requests module. Something like:

def get_resource(self, url, headers):
    self.add_request_header(headers)
    response = requests.get(url, headers, auth=self.auth)
    self.process_response_headers()
    return response

I'm having trouble finding a way to do this without having to rewrite all Client methods. The most straightforward way is to change the calls to the request module to calls to self and add the calls to the methods there.

def get_resource(self, url, headers):
    response = self.__get(url, headers, auth=self.auth)
    return response

def __get(self, headers, auth):
    self.add_request_header(headers)
    response = requests.get(url, headers, auth=self.auth)
    self.process_response_headers()
    return response

But this requires me to change all the call sites and duplicate functionality of the request module. I've tried to use decorators to add these method calls to the request module functions, but got stuck with how to pass in self to the decorator.

I'm sure there's an elegant way to do this in Python.

MvdD
  • 22,082
  • 8
  • 65
  • 93

2 Answers2

0

You can use monkey patching. read this : Python: Monkeypatching a method of an object

    import requests
    def get(self, url, params=None, **kwargs):
        self.add_request_header(self.headers)
        response = requests.get(url, self.headers, auth=self.auth)
        self.process_response_headers()
    setattr(requests.Session, 'get', requests.Session.get)
    s = requests.Session()
Rahul Raut
  • 1,099
  • 8
  • 15
  • I think the problem with this is that self refers to the Session object, while `add_request_header` is a method on `Client` – MvdD Sep 17 '18 at 15:21
0

I think in this case decorators will not be as good as it sounds and OOP is a better approach to your problem. You could use a base class Client:

class Client(object):
    def __init__(self, auth):
        self.auth = auth

    def add_request_header(self, headers):
        pass

    def process_response_headers(self):
        pass

    def get_resource(self, url, headers):
        self.add_request_header(headers)
        response = requests.get(url, headers, auth=self.auth)
        self.process_response_headers()
        return response

And create another subclasses with other implementations of add_request_header and/or process_response_headers so later you just need to instantiate the class that better suites your case

edilio
  • 1,778
  • 14
  • 13
  • How is this different from the solution I gave in the question to which I was looking for an alternative? – MvdD Sep 17 '18 at 15:02
  • @MvdD It seems I am not understanding your problem correctly, could you give two examples of `add_request_header` and `process_response_headers`? – edilio Sep 17 '18 at 17:08