Skip to main content

async-object

Test pre-commit pre-commit.ci status

PyPI PyPI - License PyPI - Python Version

Checked with mypy Code style: black Imports: isort

async-object let you write classes with async def __init__

Installation

From PyPI repository

pip install --user async-object

From source

git clone https://github.com/francis-clairicia/async-object.git
cd async-object
pip install --user .

Usage

It is simple, with async-object you can do this:

from async_object import AsyncObject


class MyObject(AsyncObject):
    async def __init__(self) -> None:
        await super().__init__()

        # Do some async stuff


if __name__ == "__main__":
    import asyncio

    async def main() -> None:
        instance = await MyObject()
        assert isinstance(instance, MyObject)

    asyncio.run(main())

This example uses asyncio, but it is compatible with all runner libraries, since this package only uses the language syntax.

Description

async-object provides a base class AsyncObject using AsyncObjectMeta metaclass.

AsyncObjectMeta overrides the default type constructor in order to return a coroutine, which must be await-ed to get the instance.

async def main() -> None:
    coroutine = MyObject()
    print(coroutine)
    instance = await coroutine
    print(instance)

Replace the main in the Usage example by this one and run it. You should see something like this in your console:

<coroutine object AsyncObjectMeta.__call__ at 0x7ff1f28eb300>
<__main__.MyObject object at 0x7ff1f21a4fd0>

Arguments

Obviously, arguments can be given to __init__ and __new__. The inheritance logic with "normal" constructors is the same here:

from typing_extensions import Self

class MyObjectOnlyNew(AsyncObject):
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        self = super().__new__(cls)

        print(args)
        print(kwargs)

        return self


class MyObjectOnlyInit(AsyncObject):
    async def __init__(self, *args: Any, **kwargs: Any) -> None:
        # await super().__init__()  # Optional if the base class is only AsyncObject (but useful in multiple inheritance context)

        print(args)
        print(kwargs)


class MyObjectBothNewAndInit(AsyncObject):
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        self = super().__new__(cls)

        print(args)
        print(kwargs)

        return self

    async def __init__(self, *args: Any, **kwargs: Any) -> None:
        # await super().__init__()

        print(args)
        print(kwargs)

Inheritance

Talking about inheritance, there are a few rules to follow:

  • AsyncObject or a subclass must appear at least once in the base classes declaration.
  • Non-AsyncObject classes can be used as base classes if they do not override __init__ (in order not to break the MRO).
  • To avoid confusion with awaitable objects, overriding __await__ is forbidden.

Abstract base classes

There is a metaclass AsyncABCMeta deriving from AsyncObjectMeta and abc.ABCMeta which allows you to declare abstract base classes

import abc

from async_object import AsyncObject, AsyncABCMeta


class MyAbstractObject(AsyncObject, metaclass=AsyncABCMeta):
    @abc.abstractmethod
    def method(self) -> None:
        raise NotImplementedError

    @abc.abstractmethod
    async def async_method(self) -> None:
        raise NotImplementedError


class MyObject(MyAbstractObject):
    async def __init__(self) -> None:
        pass

    def method(self) -> None:
        pass

    async def async_method(self) -> None:
        pass

N.B.: There is a shorthand AsyncABC like abc.ABC.

import abc

from async_object import AsyncABC


class MyAbstractObject(AsyncABC):
    @abc.abstractmethod
    def method(self) -> None:
        raise NotImplementedError

    @abc.abstractmethod
    async def async_method(self) -> None:
        raise NotImplementedError

Static type checking: mypy integration

mypy does not like having async def for __init__, and will not understand await AsyncObject().

async-object embeds a plugin which helps mypy to understand asynchronous constructors.

Installation

Firstly, install the needed dependencies:

pip install async-object[mypy]

To register this plugin in your mypy.ini, pyproject.toml, or whatever, you must add async_object.contrib.mypy.plugin to the plugins list.

In mypy.ini:

[mypy]
plugins = async_object.contrib.mypy.plugin

In pyproject.toml:

[tool.mypy]
plugins = ["async_object.contrib.mypy.plugin"]

For more information, see the mypy documentation.

What is permitted then ?

__init__ method returning a coroutine is accepted

The error The return type of "__init__" must be None is discarded.

class MyObject(AsyncObject):
    async def __init__(self, param: int) -> None:
        await super().__init__()

The class instanciation introspection is fixed

async def main() -> None:
    coroutine = MyObject()
    reveal_type(coroutine)  # Revealed type is "typing.Coroutine[Any, Any, __main__.MyObject]"
    instance = await coroutine
    reveal_type(instance)  # Revealed type is "__main__.MyObject"

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_object-3.0.0.tar.gz (103.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_object-3.0.0-py3-none-any.whl (8.4 kB view details)

Uploaded Python 3

File details

Details for the file async_object-3.0.0.tar.gz.

File metadata

  • Download URL: async_object-3.0.0.tar.gz
  • Upload date:
  • Size: 103.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for async_object-3.0.0.tar.gz
Algorithm Hash digest
SHA256 c98c25cc08a84c054aa02cc66d7e1a332224637bf3cb0a40365b1163b782f011
MD5 367a627c16deb54a605a34411b8615f2
BLAKE2b-256 3d6c9eb1b3f304b653b5ca224bb6c14e07b0892f2465a8c0632d1071dd630efc

See more details on using hashes here.

File details

Details for the file async_object-3.0.0-py3-none-any.whl.

File metadata

  • Download URL: async_object-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 8.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for async_object-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8b8258f3b61973c6ff20270754d4075e9e299b8db3b13feb5cffe27b3839af76
MD5 57bbefb64e23bc1523f9407b853abd97
BLAKE2b-256 aee288af38f2f4404eef7fdbbee1b41c32bbaa0e76963c8289205149379c5a10

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 files

2.0.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page