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. -- https://mail.python.org/mailman/listinfo/python-list