Name | async-object JSON |
Version |
2.0.0
JSON |
| download |
home_page | |
Summary | async-object let you write classes with async def __init__ |
upload_time | 2023-12-27 13:21:22 |
maintainer | |
docs_url | None |
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"
}