Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to subclass requests in python through inheritance

I would like to specialize / subclass the requests package to add some method with custom functionality.

I tried to do this:

# concrete_requests.py
import requests

class concreteRequests(requests):
    def __init__(self):
        super(concreteRequests, self).__init__() 
        self.session()

    def login(self):
        payload = {'user': 'foo', 'pass': 'bar'}
        self.get('loginUrl', headers=header, data=payload)
        # more login stuff...

# my_class.py
class MyClass:
    def __init__():
        self.requests = concreteRequests()
        self.requests.login()

This way I could still benefit from self.requests members + my concrete implementation. So I could do: self.requests.get(...) or print(self.requests.post(...).status_code) and so on.

I guess that this line super(concreteRequests, self).__init__() can be stupidly useless since requests doesn't have any class declaration inside just imports...

So, the requests package can be subclassed/specialized through inheritance ?

like image 716
Jeflopo Avatar asked May 19 '15 13:05

Jeflopo


2 Answers

requests is a python module not a class. You can only subclass classes.

So basically you should just leverage its methods/functions inside your own custom class.

import requests

class MyRequests:
    def __init__(self, username, passwd):
        self.username = username
        self.passwd = passwd

    def get(self, *args, **kwargs):
        # do your thing
        resp = requests.get(...)
        # do more processing

What I wrote above is just an example to get you going.

like image 159
rantanplan Avatar answered Oct 17 '22 13:10

rantanplan


A good way is to subclass a Session object from requests. A basic example:

from requests import Session

class MyClient(Session):
    """Specialized client that inherits the requests api."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def get_google(self):
        return self.get("http://google.com")

MyClient includes the Session (request) api for for free, and anything else you want to add to it.

A real-world example: suppose a client requires an auth header specified at runtime (in this case the auth requires a current timestamp). Here is an example client that subclasses Session and of subclassed AuthBase that achieves that (this code requires values set for api_key, secret_key, passphrase):

import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase
from requests import Session

class MyClient(Session):
    """Client with specialized auth required by api."""
    def __init__(self, api_key, secret_key, passphrase, *args, **kwargs):
        # allow passing args to `Session.__init__`
        super().__init__(*args, **kwargs)
        
        # `self.auth` callable that creates timestamp when request is made
        self.auth = MyAuth(api_key, secret_key, passphrase)


class MyAuth(AuthBase):
    """Auth includes current timestamp when called.
       https://docs.python-requests.org/en/master/user/advanced/#custom-authentication    
    """

    def __init__(self, api_key, secret_key, passphrase):
        self.api_key = api_key
        self.secret_key = secret_key
        self.passphrase = passphrase

    def __call__(self, request):

        timestamp = str(time.time())
        message = timestamp + request.method + request.path_url + (request.body or "")
        message = message.encode("utf-8")
        hmac_key = base64.b64decode(self.secret_key)
        signature = hmac.new(hmac_key, message, hashlib.sha256)
        signature_b64 = base64.b64encode(signature.digest())
        request.headers.update(
            {
                "ACCESS-SIGN": signature_b64,
                "ACCESS-TIMESTAMP": timestamp,
                "ACCESS-KEY": self.api_key,
                "ACCESS-PASSPHRASE": self.passphrase,
                "Content-Type": "application/json",
            }
        )
        return request
like image 34
anon01 Avatar answered Oct 17 '22 14:10

anon01