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-2.0.0.tar.gz (32.6 kB view details)

Uploaded Source

Built Distribution

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

Uploaded Python 3

File details

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

File metadata

  • Download URL: async_object-2.0.0.tar.gz
  • Upload date:
  • Size: 32.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.18

File hashes

Hashes for async_object-2.0.0.tar.gz
Algorithm Hash digest
SHA256 0168dc4489ea3bf6620837d9af1382d36524b435d0ebdea2affcdbc802b5f9f3
MD5 0cc1ef9b8ac5be5e64e13f449e941534
BLAKE2b-256 5ee1bf32038dce05cfcf528c5222c227403318a948c6a458dbf0a9ae67f2220e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for async_object-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fa527d7af21b274972ee171347370099ae2f3a8353a6f3bbaa4958919ac364c2
MD5 2fbb90cd561149f8b2d405fc2b733017
BLAKE2b-256 767ec72fe3e97116b6f6bbbbf517f9777efc62ee113f08d11b2ea3790768a7ad

See more details on using hashes here.

Supported by

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