{ "info": { "author": "Alberto Berti", "author_email": "alberto@metapensiero.it", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5" ], "description": ".. -*- coding: utf-8 -*-\n.. :Project: metapensiero.asyncio.transaction -- Handle coroutines from synchronous functions or methods (like special methods)\n.. :Created: dom 09 ago 2015 12:57:35 CEST\n.. :Author: Alberto Berti \n.. :License: GNU General Public License version 3 or later\n.. :Copyright: Copyright (C) 2015 Alberto Berti\n..\n\n==================================\n metapensiero.asyncio.transaction\n==================================\n\n :author: Alberto Berti\n :contact: alberto@metapensiero.it\n :license: GNU General Public License version 3 or later\n\nHandle coroutines from synchronous functions or methods (like special methods)\n------------------------------------------------------------------------------\n\nGoal\n~~~~\n\nThis package helps handling such cases when there is a side effect\nthat is coded as a coroutine and the code block that needs it cannot\nbe run as a coroutine.\n\nThe case is e. g. when a coroutine is called from an external package\nor a Python special method.\n\nThis package will give you the tools to ensure that even if the\ncomputation is not immediate, the execution of the coroutine(s) is\nguaranteed to happen in the context of the coroutine that is calling\nthe special method.\n\nA package that works in tandem with this is `metapensiero.signal`__\n\n__ https://pypi.python.org/pypi/metapensiero.signal\n\nInstallation\n~~~~~~~~~~~~\n\nTo install the package execute the following command::\n\n $ pip install metapensiero.asyncio.transaction\n\nUsage\n~~~~~\n\nGiven a scenario where a coroutine is called from the ``__setattr___``\nmethod, this is how to deal with the situation:\n\n.. code:: python\n\n import asyncio\n from metapensiero.async import transaction\n\n @asyncio.coroutine\n def publish(value):\n # do something async\n\n class Example:\n\n def __setattr__(self, name, value):\n trans = transaction.get()\n trans.add(publish(value))\n super().__setattr__(name, value)\n\n @asyncio.coroutine\n def external_coro():\n inst = Example()\n trans = transaction.begin()\n inst.foo = 'bar'\n yield from trans.end()\n\nIn python 3.5, the ``external_coro`` can be written as:\n\n.. code:: python\n\n async def external_coro():\n inst = Example()\n async with transaction.begin():\n inst.foo = 'bar'\n\nSo by taking advantage of the new ``__aenter__`` and ``__aexit__``\nmethods and awaitable classes.\n\nThe coroutines will be scheduled in the order they have been created.\n\nAt a certain point, you may want to ensure that all the remaining\ncoroutines are executed you may use the coroutine\n``transaction.wait_all()``, doing so will end all the remaining open\ntransactions.\n\nWhen your code add a coro to the transaction it can also pass a\ncallback as the ``cback`` keyword parameter, so it will be called when\nthe stashed coro completes (or is cancelled). This way you can use the\nreturn value of the coro like:\n\n.. code:: python\n\n import asyncio\n from metapensiero.asyncio import transaction\n\n results = []\n\n @asyncio.coroutine\n def stashed_coro():\n nonlocal results\n results.append('called stashed_coro')\n return 'result from stashed coro'\n\n class A:\n\n def __init__(self):\n tran = transaction.get()\n c = stashed_coro()\n tran.add(c, cback=self._init)\n\n def _init(self, stashed_task):\n nonlocal results\n results.append(stashed_task.result())\n\n @asyncio.coroutine\n def external_coro():\n tran = transaction.begin()\n # in py3.5\n # async with tran:\n # a = A()\n a = A()\n yield from tran.end()\n\n yield from asyncio.gather(\n external_coro()\n )\n\n assert len(results) == 2\n assert results == ['called stashed_coro', 'result from stashed coro']\n\n\nTesting\n~~~~~~~\n\nTo run the tests you should run the following at the package root::\n\n python setup.py test\n\n\nBuild status\n~~~~~~~~~~~~\n\n.. image:: https://travis-ci.org/azazel75/metapensiero.asyncio.transaction.svg?branch=master\n :target: https://travis-ci.org/azazel75/metapensiero.asyncio.transaction\n\n\n.. -*- coding: utf-8 -*-\n\nChanges\n-------\n\n0.1 (2015-12-15)\n~~~~~~~~~~~~~~~~\n\n- Adaptation from the rocky sources.\n- First draft documentation.\n\n0.2 (2015-12-16)\n~~~~~~~~~~~~~~~~\n\n- Document some details about testing.\n- Add some checks on end().\n\n0.3 (2015-12-26)\n~~~~~~~~~~~~~~~~\n\n- Fix packaging.\n\n0.4 (2015-12-27)\n~~~~~~~~~~~~~~~~\n\n- Documentation fixes.\n\n0.5 (2016-01-22)\n~~~~~~~~~~~~~~~~\n\n- Fix behavior when transaction.begin() is called from code not\n already running in a task.\n\n0.6 (2016-01-27)\n~~~~~~~~~~~~~~~~\n\n- support a callback function when adding coros to the transaction.\n\n0.7 (2016-02-18)\n~~~~~~~~~~~~~~~~\n\n- Add a wait() method to suspend the current task while the coros complete\n without closing the current transaction.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/azazel75/metapensiero.asyncio.transaction", "keywords": "", "license": "GPLv3+", "maintainer": null, "maintainer_email": null, "name": "metapensiero.asyncio.transaction", "package_url": "https://pypi.org/project/metapensiero.asyncio.transaction/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/metapensiero.asyncio.transaction/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/azazel75/metapensiero.asyncio.transaction" }, "release_url": "https://pypi.org/project/metapensiero.asyncio.transaction/0.7/", "requires_dist": null, "requires_python": null, "summary": "Handle coroutines from synchronous functions or methods (like special methods)", "version": "0.7" }, "last_serial": 1963387, "releases": { "0.6": [ { "comment_text": "", "digests": { "md5": "decb643528165dcc0b9bafffe0ea6baa", "sha256": "57b39958b64b8564adc5168680e3c889da9e014709486b8d323afbc6c7932cfc" }, "downloads": -1, "filename": "metapensiero.asyncio.transaction-0.6.tar.gz", "has_sig": false, "md5_digest": "decb643528165dcc0b9bafffe0ea6baa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5885, "upload_time": "2016-01-26T23:37:09", "url": "https://files.pythonhosted.org/packages/68/a0/f4256d93fb432496f35eb31f00e83419b0b1323d14a2968b8106916ecaa8/metapensiero.asyncio.transaction-0.6.tar.gz" } ], "0.7": [ { "comment_text": "", "digests": { "md5": "2eb4de1179e8b774bad5a6d238d121f6", "sha256": "e24f6c2ba032687eb867501e3557c9c19f936cf5579e8030b274d5bd5dbc0385" }, "downloads": -1, "filename": "metapensiero.asyncio.transaction-0.7.tar.gz", "has_sig": false, "md5_digest": "2eb4de1179e8b774bad5a6d238d121f6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6089, "upload_time": "2016-02-18T15:17:52", "url": "https://files.pythonhosted.org/packages/cd/e0/b6cee072f0a6fb2ebfedb7730ce7a1354e2969249ba35f9655d39033d6b1/metapensiero.asyncio.transaction-0.7.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "2eb4de1179e8b774bad5a6d238d121f6", "sha256": "e24f6c2ba032687eb867501e3557c9c19f936cf5579e8030b274d5bd5dbc0385" }, "downloads": -1, "filename": "metapensiero.asyncio.transaction-0.7.tar.gz", "has_sig": false, "md5_digest": "2eb4de1179e8b774bad5a6d238d121f6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6089, "upload_time": "2016-02-18T15:17:52", "url": "https://files.pythonhosted.org/packages/cd/e0/b6cee072f0a6fb2ebfedb7730ce7a1354e2969249ba35f9655d39033d6b1/metapensiero.asyncio.transaction-0.7.tar.gz" } ] }