async-object


Nameasync-object JSON
Version 2.0.0 PyPI version JSON
download
home_page
Summaryasync-object let you write classes with async def __init__
upload_time2023-12-27 13:21:22
maintainer
docs_urlNone
author
requires_python>=3.9
license
keywords async asyncio init
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # async-object
[![Test](https://github.com/francis-clairicia/async-object/actions/workflows/test.yml/badge.svg)](https://github.com/francis-clairicia/async-object/actions/workflows/test.yml)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/francis-clairicia/async-object/main.svg)](https://results.pre-commit.ci/latest/github/francis-clairicia/async-object/main)

[![PyPI](https://img.shields.io/pypi/v/async-object)](https://pypi.org/project/async-object/)
[![PyPI - License](https://img.shields.io/pypi/l/async-object)](https://github.com/francis-clairicia/async-object/blob/main/LICENSE)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/async-object)

[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)

`async-object` let you write classes with `async def __init__`

## Installation
### From PyPI repository
```sh
pip install --user async-object
```

### From source
```sh
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:
```py
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.

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

Replace the `main` in the [Usage](#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:
```py
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](https://docs.python.org/3/glossary.html#term-method-resolution-order)).
- To avoid confusion with [awaitable objects](https://docs.python.org/3/glossary.html#term-awaitable), 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
```py
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`.
```py
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:
```sh
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`:
```ini
[mypy]
plugins = async_object.contrib.mypy.plugin
```

In `pyproject.toml`:
```toml
[tool.mypy]
plugins = ["async_object.contrib.mypy.plugin"]
```

For more information, see [the mypy documentation](https://mypy.readthedocs.io/en/stable/extending_mypy.html#configuring-mypy-to-use-plugins).

### What is permitted then ?
#### `__init__` method returning a coroutine is accepted
The error `The return type of "__init__" must be None` is discarded.
```py
class MyObject(AsyncObject):
    async def __init__(self, param: int) -> None:
        await super().__init__()
```

#### The class instanciation introspection is fixed
```py
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"
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "async-object",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "",
    "keywords": "async,asyncio,init",
    "author": "",
    "author_email": "FrankySnow9 <clairicia.rcj.francis@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/5e/e1/bf32038dce05cfcf528c5222c227403318a948c6a458dbf0a9ae67f2220e/async_object-2.0.0.tar.gz",
    "platform": null,
    "description": "# async-object\n[![Test](https://github.com/francis-clairicia/async-object/actions/workflows/test.yml/badge.svg)](https://github.com/francis-clairicia/async-object/actions/workflows/test.yml)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)\n[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/francis-clairicia/async-object/main.svg)](https://results.pre-commit.ci/latest/github/francis-clairicia/async-object/main)\n\n[![PyPI](https://img.shields.io/pypi/v/async-object)](https://pypi.org/project/async-object/)\n[![PyPI - License](https://img.shields.io/pypi/l/async-object)](https://github.com/francis-clairicia/async-object/blob/main/LICENSE)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/async-object)\n\n[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)\n\n`async-object` let you write classes with `async def __init__`\n\n## Installation\n### From PyPI repository\n```sh\npip install --user async-object\n```\n\n### From source\n```sh\ngit clone https://github.com/francis-clairicia/async-object.git\ncd async-object\npip install --user .\n```\n\n## Usage\nIt is simple, with `async-object` you can do this:\n```py\nfrom async_object import AsyncObject\n\n\nclass MyObject(AsyncObject):\n    async def __init__(self) -> None:\n        await super().__init__()\n\n        # Do some async stuff\n\n\nif __name__ == \"__main__\":\n    import asyncio\n\n    async def main() -> None:\n        instance = await MyObject()\n        assert isinstance(instance, MyObject)\n\n    asyncio.run(main())\n```\n\nThis example uses `asyncio`, but it is compatible with all runner libraries, since this package only uses the language syntax.\n\n## Description\n`async-object` provides a base class `AsyncObject` using `AsyncObjectMeta` metaclass.\n\n`AsyncObjectMeta` overrides the default `type` constructor in order to return a coroutine, which must be `await`-ed to get the instance.\n\n```py\nasync def main() -> None:\n    coroutine = MyObject()\n    print(coroutine)\n    instance = await coroutine\n    print(instance)\n```\n\nReplace the `main` in the [Usage](#usage) example by this one and run it. You should see something like this in your console:\n```\n<coroutine object AsyncObjectMeta.__call__ at 0x7ff1f28eb300>\n<__main__.MyObject object at 0x7ff1f21a4fd0>\n```\n\n### Arguments\nObviously, arguments can be given to `__init__` and `__new__`.\nThe inheritance logic with \"normal\" constructors is the same here:\n```py\nfrom typing_extensions import Self\n\nclass MyObjectOnlyNew(AsyncObject):\n    def __new__(cls, *args: Any, **kwargs: Any) -> Self:\n        self = super().__new__(cls)\n\n        print(args)\n        print(kwargs)\n\n        return self\n\n\nclass MyObjectOnlyInit(AsyncObject):\n    async def __init__(self, *args: Any, **kwargs: Any) -> None:\n        # await super().__init__()  # Optional if the base class is only AsyncObject (but useful in multiple inheritance context)\n\n        print(args)\n        print(kwargs)\n\n\nclass MyObjectBothNewAndInit(AsyncObject):\n    def __new__(cls, *args: Any, **kwargs: Any) -> Self:\n        self = super().__new__(cls)\n\n        print(args)\n        print(kwargs)\n\n        return self\n\n    async def __init__(self, *args: Any, **kwargs: Any) -> None:\n        # await super().__init__()\n\n        print(args)\n        print(kwargs)\n```\n\n### Inheritance\nTalking about inheritance, there are a few rules to follow:\n- `AsyncObject` or a subclass must appear at least once in the base classes declaration.\n- Non-`AsyncObject` classes can be used as base classes if they do not override `__init__` (in order not to break the [MRO](https://docs.python.org/3/glossary.html#term-method-resolution-order)).\n- To avoid confusion with [awaitable objects](https://docs.python.org/3/glossary.html#term-awaitable), overriding `__await__` is forbidden.\n\n### Abstract base classes\nThere is a metaclass `AsyncABCMeta` deriving from `AsyncObjectMeta` and `abc.ABCMeta` which allows you to declare abstract base classes\n```py\nimport abc\n\nfrom async_object import AsyncObject, AsyncABCMeta\n\n\nclass MyAbstractObject(AsyncObject, metaclass=AsyncABCMeta):\n    @abc.abstractmethod\n    def method(self) -> None:\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    async def async_method(self) -> None:\n        raise NotImplementedError\n\n\nclass MyObject(MyAbstractObject):\n    async def __init__(self) -> None:\n        pass\n\n    def method(self) -> None:\n        pass\n\n    async def async_method(self) -> None:\n        pass\n```\n\nN.B.: There is a shorthand `AsyncABC` like `abc.ABC`.\n```py\nimport abc\n\nfrom async_object import AsyncABC\n\n\nclass MyAbstractObject(AsyncABC):\n    @abc.abstractmethod\n    def method(self) -> None:\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    async def async_method(self) -> None:\n        raise NotImplementedError\n```\n\n## Static type checking: mypy integration\n`mypy` does not like having `async def` for `__init__`, and will not understand `await AsyncObject()`.\n\n`async-object` embeds a plugin which helps `mypy` to understand asynchronous constructors.\n\n### Installation\nFirstly, install the needed dependencies:\n```sh\npip install async-object[mypy]\n```\n\nTo register this plugin in your `mypy.ini`, `pyproject.toml`, or whatever, you must add `async_object.contrib.mypy.plugin` to the plugins list.\n\nIn `mypy.ini`:\n```ini\n[mypy]\nplugins = async_object.contrib.mypy.plugin\n```\n\nIn `pyproject.toml`:\n```toml\n[tool.mypy]\nplugins = [\"async_object.contrib.mypy.plugin\"]\n```\n\nFor more information, see [the mypy documentation](https://mypy.readthedocs.io/en/stable/extending_mypy.html#configuring-mypy-to-use-plugins).\n\n### What is permitted then ?\n#### `__init__` method returning a coroutine is accepted\nThe error `The return type of \"__init__\" must be None` is discarded.\n```py\nclass MyObject(AsyncObject):\n    async def __init__(self, param: int) -> None:\n        await super().__init__()\n```\n\n#### The class instanciation introspection is fixed\n```py\nasync def main() -> None:\n    coroutine = MyObject()\n    reveal_type(coroutine)  # Revealed type is \"typing.Coroutine[Any, Any, __main__.MyObject]\"\n    instance = await coroutine\n    reveal_type(instance)  # Revealed type is \"__main__.MyObject\"\n```\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "async-object let you write classes with async def __init__",
    "version": "2.0.0",
    "project_urls": {
        "Homepage": "https://github.com/francis-clairicia/async-object"
    },
    "split_keywords": [
        "async",
        "asyncio",
        "init"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "767ec72fe3e97116b6f6bbbbf517f9777efc62ee113f08d11b2ea3790768a7ad",
                "md5": "2fbb90cd561149f8b2d405fc2b733017",
                "sha256": "fa527d7af21b274972ee171347370099ae2f3a8353a6f3bbaa4958919ac364c2"
            },
            "downloads": -1,
            "filename": "async_object-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2fbb90cd561149f8b2d405fc2b733017",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 8407,
            "upload_time": "2023-12-27T13:21:20",
            "upload_time_iso_8601": "2023-12-27T13:21:20.823223Z",
            "url": "https://files.pythonhosted.org/packages/76/7e/c72fe3e97116b6f6bbbbf517f9777efc62ee113f08d11b2ea3790768a7ad/async_object-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5ee1bf32038dce05cfcf528c5222c227403318a948c6a458dbf0a9ae67f2220e",
                "md5": "0cc1ef9b8ac5be5e64e13f449e941534",
                "sha256": "0168dc4489ea3bf6620837d9af1382d36524b435d0ebdea2affcdbc802b5f9f3"
            },
            "downloads": -1,
            "filename": "async_object-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "0cc1ef9b8ac5be5e64e13f449e941534",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 32606,
            "upload_time": "2023-12-27T13:21:22",
            "upload_time_iso_8601": "2023-12-27T13:21:22.600695Z",
            "url": "https://files.pythonhosted.org/packages/5e/e1/bf32038dce05cfcf528c5222c227403318a948c6a458dbf0a9ae67f2220e/async_object-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-27 13:21:22",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "francis-clairicia",
    "github_project": "async-object",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "async-object"
}
        
Elapsed time: 4.64551s