Skip to main content

async-object let you write classes with async def __init__

Project description

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"

Project details


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.

Supported by

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