pythonasynchronoustornadomongoenginemotorengine

abstract method async and sync implementation in Python


Let's say I have BaseClass which contains some logic in main_function() which is common for both SyncClass and AsyncClass. Let's say those 2 classes have their unique implementation of get_data() and the former gets in synchronous manner, while the latter in asynchronous manner. I have written something like this and it seems to work:

class BaseClass:
    def get_data():
        pass

    @gen.coroutine
    def main_function():
        # some logic
        try:
            data = yield self.get_data()
        except:
            data = self.get_data()
        # some more logic

class SyncClass(BaseClass):
    def get_data():
        //makes sync call to Mongo and gets data (using Mongoengine)

class AsyncClass(BaseClass):
    @gen.coroutine
    def get_data():
        //makes async call to Mongo and gets data (using Motorengine)

I used this code as a workaround, because I already had those methods for get_data() implemented that way. Is there some more elegant solution? There are 2 parts of my code that concern me:

try:
    data = yield self.get_data()
except:
    data = self.get_data()

I would prefer not to use try/except here.

The other thing is: I have @gen.coroutine in the AsyncClass while the same function isn't decorated with @gen.coroutine in the BaseClass.

Thank you!


Solution

  • Synchronous and asynchronous methods have different interfaces (that's what it means to be asynchronous). AsyncClass.get_data returns a Future; SyncClass.get_data does not. If this were in a statically-typed language, these two methods would not be able to implement the same abstract method from the base class. Of course, Python is more flexible and does not restrict you in this way, but the caller still needs to either know which kind of method it's dealing with or be prepared to find out via try/except or isinstance checks, etc. (note that try/except is dangerous in this case, because yield in a tornado coroutine will accept things like lists and dicts)

    In general, you cannot transparently switch between them as you're hoping to do here. Remember that any function that may call yield self.get_data() also needs to be decorated with @coroutine, so once one part of your system is asynchronous, it starts to spread. It's usually best to embrace this trend and just make things asynchronous.