git.net

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

How to handle async and inheritance?


Stephen Rosen wrote at 2020-6-30 11:59 -0400:
>Hi all,
>
>I'm looking at a conflict between code sharing via inheritance and async
>usage. I would greatly appreciate any guidance, ideas, or best practices
>which might help.
>
>I'll speak here in terms of a toy example, but, if anyone wants to look at
>the real code, I'm working on webargs. [1] Specifically, we have a `Parser`
>class and an `AsyncParser` subclass, and the two have a lot of code
>duplication to handle async/await. [2]
>
>
>I've got an inheritance structure like this:
>
>class MyAbstractType: ...
>class ConcreteType(MyAbstractType): ...
>class AsyncConcreteType(MyAbstractType): ...
>
>One of my goals, of course, is to share code between ConcreteType and
>AsyncConcreteType via their parent.
>But the trouble is that there are functions defined like this:
>
>class MyAbstractType:
>    def foo(self):
>        x = self.bar()
>        y = self.baz(x)
>        ... # some code here, let's say 20 lines
>
>class AsyncConcreteType(MyAbstractType):
>    async def foo(self):
>        x = await self.bar()
>        y = self.baz(x)
>        ... # the same 20 lines as above, but with an `await` added
>every-other line
>
>
>I'm aware that I'm looking at "function color" and that my scenario is
>pitting two language features -- inheritance and async -- against one
>another. But I don't see a clean way out if we want to support an
>"async-aware" version of a class with synchronous methods.
>
>What I tried already, which I couldn't get to work, was to either fiddle
>with things like `inspect` to see if the current function is async or to
>use a class variable to indicate that the current class is the async
>version. The idea was to write something like
>
>class MyAbstractType:
>    _use_async_calls = False
>    def foo(self):
>        x = self._await_if_i_am_async(self.bar)
>        y = self.baz(x)
>        ...
>
>and that way, the async subclass just needs to change signatures to be
>async with little stubs and set the flag,
>
>class AsyncConcreteType(MyAbstractType):
>    _use_async_calls = True
>    async def foo(self):
>        return super().foo()
>    async def bar(self):
>        return super().bar()

As far as I understand (I am far from an `async` expert),
`async` functions need to be specially compiled. This implies
that there cannot be a runtime switch which makes a given function
asynchronous or synchronous at runtime.
You would need to have 2 functions, one asynchronous and one synchronous.
Then a runtime switch may select the appropriate function.

You likely can generate one of those function kinds from the other
one by an `ast` (= "Abstract Syntax Tree") transformation.