jeudi 10 septembre 2020

Extend class functionality in a script runtime

I have a web application in which I want to support different incoming APIs, but business logic of processing requests is the same for all of the incoming APIs.

Right now business logic works with a single implemented API and works directly with gunicorn's Request class.

In order for business logic to not rely on the format of incoming http requests / or how data is received in general, to reduce code duplication, to not implement different functions which do the same but on different fields, in other words, to keep it dry, the common interface were implemented which is used by business logic for data access, for e.g.

class IMyAppRequest(Request):

    def __init__(self):
        self._username = None
        self._address = None

    @property
    def username(self):
        raise
        
    @property
    def address(self):
        raise

And classes which implement this interface dynamically calculating username and address fields.

class TypeARequest(IMyAppRequest):

    @property
    def address(self):
        if self._address is not None:
            return self._address
            
        self._address = self.body["geolocation"]["addr"]
        
        return self._address
class TypeBRequest(IMyAppRequest):

    @property
    def address(self):
        if self._address is not None:
            return self._address
        
        self._address = self.body["country"]["city"]["full_address"]
        
        return self._address

And now I want to implement a middleware which wraps/replaces incoming gunicorn's Request with classes above.

How do you properly do it? Basically, I want drop-in replacement of a parent class with child one. I can't instantiate child class initially because I have to have a full http request parsed before I can determine which type it is. Networking/request parsing incorporated in gunicorn's Request class itself, so the Request class is already created while the request is not parsed.

I can't go with simple inheritance because it won't solve the issue. Right now i'm using a dependency injection(?):

class IMyAppRequest:

    def __init__(self, http_request):
        self.request = http_request
    
        self._username = None
        self._address = None

    @property
    def username(self):
        raise
        
    @property
    def address(self):
        raise
        
    def __getattr__(self, item):
        return getattr(self.request, item)
        
    def __setattr(self, key, value):
        return setattr(self.request, key, value)

where http_request is gunicorn's Request instance.

That way I can simply wrap the request in middleware depending on request's type. But is it how you do it? Or it's completely wrong? I couldn't find something related in source codes of libraries I'm using/python stdlib, and I can't trust myself in this question that this implementation is ok.

Aucun commentaire:

Enregistrer un commentaire