Object-oriented URL loading and HTTP client with requests in Python I have an API which I want to consume in Python 3.x using the requests module.
Right now, my solution is to create Requests objects for each URL like the LoginRequest, etc. (see below).

I have one request per API URL and need to write the __init__ method for each class depending on the parameters that are requested for the API. I find it tedious and I am not sure that's a good design. 

Could you suggest a better design? (e.g. not using OOP, with metaclass, ... ?)

class BaseRequest:
    USER_AGENT = 'My App 1.0'
    headers = {'User-Agent': USER_AGENT}
    url = None
    data = None
    params = None

    def __init__(self, url, data=None, headers=None, params=None):
        self.url = url
        if data:
            data.update(self.data or {})
            self.data = data
        if headers:
            headers.update(self.headers or {})
            self.headers = headers
        if params:
            params.update(self.params or {})
            self.params = params

    def process(self):
        r = requests.post(self.url, data=self.data, headers=self.headers, params=self.params)
        try:
            j = r.json()
        except:
            print(self.url, self.params, self.data)
            print(r.text)
            exit()
        return j


class AuthenticatedRequest(BaseRequest):
    API_KEY = 'blah'
    APP_ID = 'OfficialClient'
    data = {'appkey': API_KEY, 'appid': APP_ID}


class LoginRequest(AuthenticatedRequest):
    url = 'https://my.api.com/login'

    def __init__(self, email, password):
        super().__init__(self.url, data=dict(email=email, password=password, action='login'))

# now let's play with the API !
user = LoginRequest(email, password).process()
GetUserInfoDetailsRequest(...).process()
It makes sense to model the API like this if things are more
complicated, e.g. ability to pass around requests and responses as
objects might come in handy.

The last example shows why this alone isn't that user-friendly though:
In addition to mentioning the specific request (with the Request
suffix always around) you also have to call the process method.

I'd suggest an additional wrapper to make things easier in the normal
case, i.e. have login function or method that does this for you.



The code as-is looks good to me with the exception of the process
method - firstly you can just write return r.json(), secondly the
exit call in there is ... questionable.  I most certainly wouldn't
want to have a library call that on my program ever.

A minor thing I've also noticed is that the subclasses of BaseRequest
can't override any values via the constructor.  I'm guessing that's
deliberate, I just usually expect to be able to do that (so in the base
class call self.headers.update(headers) instead of the other way
round).