# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['koil', 'koil.checker', 'koil.checker.defaults']

package_data = \
{'': ['*']}

extras_require = \
{'jupyter': ['jupyter>=1.0.0,<2.0.0']}

setup_kwargs = {
    'name': 'koil',
    'version': '0.1.66',
    'description': 'An abstract event loop to run in an async and sync context (especially jupyter and qt), with utility functions to allow both accesses',
    'long_description': '# koil\n\n[![codecov](https://codecov.io/gh/jhnnsrs/koil/branch/master/graph/badge.svg?token=UGXEA2THBV)](https://codecov.io/gh/jhnnsrs/koil)\n[![PyPI version](https://badge.fury.io/py/koil.svg)](https://pypi.org/project/koil/)\n![Maintainer](https://img.shields.io/badge/maintainer-jhnnsrs-blue)\n[![PyPI pyversions](https://img.shields.io/pypi/pyversions/koil.svg)](https://pypi.python.org/pypi/koil/)\n[![PyPI status](https://img.shields.io/pypi/status/koil.svg)](https://pypi.python.org/pypi/koil/)\n[![PyPI download day](https://img.shields.io/pypi/dm/koil.svg)](https://pypi.python.org/pypi/koil/)\n\n### DEVELOPMENT\n\n### koil is asyncio for humans\n\nkoil is an abstraction layer on top of asyncio to enable "sensible defaults" for\nprogrammers working with frameworks that are barely compatible with asyncio (originally developped to get around pyqt5)\n\n### Why an abstraction layer on top of asyncio\n\nasyncio is a magificent addition to the python framework and is facilitating asyncronous\nand concurrent programming in a simple way.\n\nHowever especially in scientific and datascience contexts the benefits of diving\ninto the concepts of asyncio might not outweigh the costs of learning these paradigms.\n\nAdditionally there are huge players in the python ecoystem that do not work seemlessly\nwith asyncio yet and involve workarounds (Django, PyQt). Koil tries to encapsulate\nthese workarounds in a simple utility library that wraps your api in a safe context\nthat can be used form this frameworks.\n\n### Example\n\nAsyncio Way\n\n```python\nimport asyncio\n\nasync def call_api(sleep):\n    await asyncio.sleep(4)\n    return 5\n\n\nprint(asyncio.run(call_api(3)))\n\n```\n\nKoil Way\n\n```python\nimport asyncio\nfrom koil import koil\n\n@koil\nasync def call_api(sleep):\n    await asyncio.sleep(4)\n    return 5\n\nprint(call_api(sleep)) # note the ommision of the asyncio.run call\n\n```\n\nOn paper not a lot has changed between asyncio and koil, however under the hood the following\nhappened\n\nWhen calling call_api for the first time, koil detected that we are not running in an asyncronous\nevent loop and therefore wrapped the call in asyncio.run.\n\nThis doesn\'t justify a new library and a perfomance decrease though. So lets see this scenario\n\n```python\nimport asyncio\nfrom koil import koil\nfrom PyQt5.QtCore import QSize, Qt\nfrom PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton\n\n@koil\nasync def call_api(interval):\n    await asyncio.sleep(interval)\n    return 5\n\n\nclass MainWindow(QMainWindow):\n    def __init__(self):\n        super().__init__()\n\n        self.setWindowTitle("My App")\n        button = QPushButton("Press Me!")\n        button.clicked.connect(self.button_clicked)\n        self.setCentralWidget(button)\n        self.sleep_task = None\n\n    def button_clicked(self):\n        if self.sleep_task: self.sleep_task.cancel()\n        self.sleep_task = call_api(interval, as_task=True)\n        self.sleep_task.resolve.connect(lambda x: self.button.setText(f"Slept {x} seconds"))\n\n\n\napp = QApplication(sys.argv)\n\nwindow = MainWindow()\nwindow.show()\n\napp.exec()\n\n```\n\nNow this is more koils problem set. Koil detected on first run (when creating the Koil object) that\nwe are running in an QtApplication instance and loaded our asyncornous task off to another thread\nto not interfer with our qt event loop. By passing as_task we also specified that we are do not\nwant to block the qt thread to wait for result in the other thread. Koil knowingly that we are in a Qt app\nallows us to now connect to signals of that specific task in the ui_thread.\n\nHere koil differnts itself of libraries like qasync that try to mimik the qt event loop in an asyncio event loop.\nWith qasync you adopt to the asyncio style and not the style you may want.\n\n### Jupyter notebook\n\nJupyter is in a unique position as it itself runs in an event loop but is mainly used by scientists\nthat are used to an syncronus interface.\n\nYour library\n\n```python\n\n@koil\nasync def call_api(interval):\n    await asyncio.sleep(interval)\n    return 5\n\n\n```\n\nBy default koil makes the assumption that if not specified differently it will use a synconrous\ninterface in juypter so.\n\n```python\n\ncall_api()\n\n```\n\nWould run your Api Task in another Thread and return the result synconrously back to you.\nHowever you can opt in to use a asyncronous api.\n\n```python\nfrom koil import Koil\n\nKoil(force_async=True) #at the top of your programm\n\n\nawait call_api()\n```\n\nMaking the call run in the same event loop of jupyter.\n\n### Considerations\n\nKoil is a library that doesnt want to exist. In general it provides convenience method\nto build asyncornous apis for a still synconrous world.\n\nTherefore when adding koil to your api make sure to not ditch your asyncrs api but extend\nthem through convenience methods. One such trick would be to leave your asyncornous functions\nuntouched and extend them with koiled versions.\n\n```python\nimport koil\n\nclass MyComplexApi:\n\n    def __init__(self):\n        ...\n\n    async def acall_endpoint(self, endpoint): #a as convention for asyncornous functions\n        ...\n\n\n    def call_endpoint(self, *args, *kwargs):\n        return koil(self.acall_endpoint)(*args, **kwargs)\n\n```\n\nLike this you are not experiencing any perfomance hints when running in a complete\nasyncornous context, but you enable unfamiliar users to use your application in a\nsyncronous world if they so desire.\n\nAlso it is wise to always call your pure async functions from other async functions.\n\n```python\nimport koil\nimport asyncio\n\nclass MyComplexApi:\n\n    def __init__(self):\n        self.connected = False\n        ...\n\n    async def aconnect(self):\n        await asyncio.sleep(3)\n        self.connected = True\n\n\n    async def acall_endpoint(self, endpoint):\n        if not self.connected:\n            await self.aconnect() # call asyncronous no koiled version for better performance\n\n        ...\n\n\n    def call_endpoint(self, *args, *kwargs):\n        return koil(self.acall_endpoint)(*args, **kwargs)\n\n```\n',
    'author': 'jhnnsrs',
    'author_email': 'jhnnsrs@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'extras_require': extras_require,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
