0

Can the __contains__ function by adapted to Python asyncio, where the method becomes a coroutine? For example

class A():
    async def __contains__(self, a):
        return True

async def main():
    a = A()
    print(2 in a)

Of course this generates the warning

RuntimeWarning: coroutine 'A.__contains__' was never awaited

I assume there needs to be some special version of in for asyncio, though I've tried several permutations of using await and in with no luck.

My real use case of contains is a wrapper around a database search to check if there is an entity conflict. One case is to check if a user's screen name already exists in a database table, such as 'mike' in Users

Of course I could make my own contains and do something like User.contains('mike') but I prefer the beauty of in.

Mike
  • 58,961
  • 76
  • 175
  • 221
  • 1
    Have you tried `print(await (2 in a))`? – mkrieger1 Apr 09 '23 at 20:23
  • 2
    Yes I did. `print(await (2 in a))` `TypeError: object bool can't be used in 'await' expression` – Mike Apr 09 '23 at 20:31
  • I don't know much about async, but this got me curious and I found this question: [How to set class attribute with await in \_\_init\_\_](/q/33128325/4518341), where the [top answer](/a/33134213/4518341) says *"Most magic methods aren't designed to work with `async def`/`await`"*. Does that help? I also found this: [python async special class methods \_\_delete\_\_](/q/43268390/4518341) – wjandrea Apr 09 '23 at 20:49
  • Sort of, there are a few magic methods that can be adapted, such as `iter` with `aiter` and `async with` using with `aenter` and `aexit`. I was even able to to adapt `__getitem__` myself. I was hoping there would be something for `__contains__` – Mike Apr 09 '23 at 20:54
  • 2
    The thing that calls `__contains__` would need to do the await, but that thing is the implementation of the `in` byte code. `in` isn't going to know when its in a coroutine or the target object is waitable. Its the same with other things implemented with magic methods. Python is a hybrid language - async is bolted on a prodedural language - so its not a natural fit for all types of polymorphism. – tdelaney Apr 09 '23 at 21:12

1 Answers1

1

No - the built-in in operator is naturally synchronous, and making it make use of an asynchronous iterator needs changes on the language side.

Just see, for an analogy, that when the language introduced support to asynchronous context managers and asynchronous iterators, there where syntax changes introducing the async with and async for versions.

If you want a hacky toy, not suitable for production anyway, you might play along calling (synchronously) loop._run_once() inside your __contains__ code, where you would yield to the async loop with an await. It is an internal structure, so if there is a custom event-loop running this will simply fail - and also, it is not designed to be re-entrant, so if it is called from an asynchronous function, _run_once will be on the call stack already: you results might range from it doing nothing, to a lot of estate information both on your async loop and tasks to be corrupted. As a hacky toy, it could be a valid experience though.

jsbueno
  • 99,910
  • 10
  • 151
  • 209