{ "info": { "author": "Eric Reinecke", "author_email": "reinecke.eric@gmail.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2" ], "description": "IOU\n===\n\nPromise-like data encapsulation aimed at asynchronous behavior\n\nTutorial\n--------\nVery simply put, IOUs give you a way of describing *what* to do with data while\nallowing you to think about *when* the data will be available secondarily. An\nIOU is simply a wrapper for a peice of data that will be provided in the future\nsaying \"when we get this data, do these other things.\"\n\nHere is an example of setting up data dependency with IOUs:\n```python\nfrom __future__ import print_function\nfrom iou import IOU\n\niou1 = IOU()\niou1.add_fulfilled_handler(lambda x:print(\"original:\", x))\nplus_seven_iou = iou1.add_fulfilled_handler(lambda x:x+7)\n\nplus_seven_iou.add_fulfilled_handler(lambda x:print(\"plus seven:\", x))\n\niou1.fulfill(10) # fulfill the original iou with a value of 10\n```\n\nThe output of this would be:\n\n```\noriginal: 10\nplus seven: 17\n```\n\nSo, what happened here exactly? We created our intial IOU `iou1`. This is a\nwrapper for some value that might be provided in the future. We then use\n`iou1.add_fulfilled_handler` to say when `iou1` is fulfilled, call the\nhandler function provided with the value the IOU was fulfilled with.\n\nWe then call `iou.add_fulfilled_handler` with a simple lambda that adds 7 to\nwhatever value it's called with. In the first call to\n`iou1.add_fulfilled_handler` we didn't do anything with the return value.\nIn the second call though, you'll notice we assign\n`plus_seven_iou` with the result of `iou1.add_fulfilled_handler`. Any time\nyou add a fulfilled handler to an IOU, you receive another IOU that will be \nfulfilled with the result of that handler. So, `plus_seven_iou` represents an\nIOU that will be fulfilled with the value of `lambda x:x+7` when called with\nthe value of `iou1`.\n\nNext, we add a print handler to `plus_seven_iou to` print it's value.\n\nFinally, we fulfill the original IOU, `iou1` with the value `10`. By doing this,\nwe cause the entire chain of IOUs to evaluate.\n\nAnother way to put it would be like this:\n\n1. create `iou1`\n2. make a handler that will print the value `iou1` id fulfilled with\n3. make an IOU (`plus_seven_iou`) that will be fulfilled with the value of `iou1` plus 7\n4. make a handler that will print the value `plus_seven_iou` is fulfilled with\n5. fulfill `iou1` with a value of `10`, causing the chain of fulfilled handlers to get executed\n\nThat was maybe a less useful example. But let's imagine you have code that looks\nsomething like:\n```python\n# Fetch the person object from the server for \"ereinecke\"\nperson = person_from_server(\"ereinecke\")\n\n# Push the first and last name for person to UI fields\nfirst_name_text_field.set_text(person.first_name)\nlast_name_text_field.set_text(person.last_name)\n```\n\nIf `person_from_server` takes some amount of time, you could end up blocking\nyour UI until you get the result.\n\nWhat if you made a version of your sever client that made requests on another\nthread and then fulfilled IOUs for the requests on the main thread:\n\n```python\n# IOU-Aware variant of person_from_server\nperson_iou = iou_for_person_from_server(\"ereinecke\")\n\n# Tell the IOU to set the text fields on completion\nperson_iou.add_fulfilled_handler(\n lambda p:first_name_text_field.set_text(p.first_name))\nperson_iou.add_fulfilled_handler(\n lambda p:last_name_text_field.set_text(p.last_name))\n```\n\nThis allows for the implementation of non-blocking APIs fairly simply. See the\n`iou.httpreactor` sub-module for an example of a non-blocking http request API!\n\n### Rejection\nJust like IOUs can be *fulfilled*, they can also be *rejected*. That is to say,\nif there is an error in generating the value to fulfill the IOU with, it will be\nrejected with the appropriate exception. When this happens, none of the\nfulfilled handlers will be be called, instead the rejected handlers will be\ncalled with the exception. Also, the value of the IOU will be set to the\nexception as well. Rejection handlers can be added similarly to fulfilled\nhandlers, but using `add_rejected_handler` instead. This method also returns an\nIOU to be fulfilled with the result of the rejected handler.\n\n### Chaining\nThere is one special cases in which you'll be given `None` when using \n`add_fulfilled_handler`. This is when the handler is another IOU. In \nthis case, the handler IOU is *chained* to the IOU you added it to.\nThis means that the chained IOU will be settled with the same result\nas the IOU it's a handler for.\n\nHere is an example:\n\n```\nfrom __future__ import print_function\nfrom iou import IOU\niou1 = IOU()\niou2 = IOU()\niou2.add_fulfilled_handler(print)\niou2.add_rejected_handler(lambda e:print(\"exception:\", e))\niou1.add_fulfilled_handler(iou2)\niou1.reject(Exception(\"bad stuff happened\"))\n```\n\nWhen this is run, the output would be:\n\n`exception: bad stuff happened`\n\nAnother place where chaining happens is if the handler's *result* is an IOU, when\nthis happens, the IOU you were given when you registered the handler is chained\nto the resultant IOU. This is to day, the IOU that was returned by the handler\nwill also fulfill the IOU you got when you registered the handler. This \nfacilitates the `then()` behavior of other promise systems.\n\nHere is a theorretical example:\n```python\ncon = MyConnectionToServer('database.mycompany.com')\nlogin_iou = con.login('username', 'password') # returns an IOU to a session object\n\n# We don't actually use the session object, but want to wait until the connection\n# is established before calling user_for_username\nuser_iou = connection_iou.add_fulfilled_handler(\n lambda _:con.user_for_username('ereinecke'))\n\n# Log the name of the user and delete him\nuser_iou.add_fulfilled_handler(lambda user:print(\"I am deleting:\", user.name))\nuser_delete_iou = user_iou.add_fulfilled_handler(lambda user:user.delete())\n\n# once the deletion is complete, logout\nuser_delete_iou.add_fulfilled_handler(lambda _:con.logout())\n```\n\nWhat happens when you add an IOU as a rejected handler? Weird stuff. I'm open\nto ideas about what to do here, [let me know!](https://github.com/reinecke/IOU/issues/new)\n\nThere is much more to the implementation of the IOUs, I hope to document this\nin the near future.\n\nhttpreactor\n-----------\nThe httpreactor module within the IOU package provides a proof-of-concept\nimplementation of an API that provides an asynchronous wrapper around the\nrequests module that is IOU aware.\n\nHere is an example of how to use it to fetch http://www.python.org:\n\n```python\nfrom IOU import httpreactor\n\n# create the reactor to handle HTTP requests and start it up\nreactor = httpreactor.IOUHTTPReactor()\nreactor.start()\n\n# create a request and submit it to the reactor\nrequest = httpreactor.IOUHTTPReactorTask('http://www.python.org')\nrequest_result_iou = reactor.submit_task(request)\n\n# When the request completes, log the HTTP status code\nrequest_result_iou.add_fulfilled_handler(lambda x:print(x.status_code))\n```\n\nNote that none of the calls are blocking :)\n\nProject Status\n--------------\nCurrently at proof-of-concept stage. The main TODOs are:\n- Finish out documentation\n- Create an IOU that joins to other IOUs?\n- Clarify threaded behavior\n\nTo expand more, it could be interesting to have the IOU system able to execute\nfulfillment/rejection handlers using a thread pool. This way the system could\nhave a mechanisim to somewhat auto-parallelize.\n\nAcknowledgements\n----------------\nThe design of the IOU system was largely influenced by Keith Rarick's blog\npost [Asynchronous Programming in Python](http://xph.us/2009/12/10/asynchronous-programming-in-python.html).\n\nThanks also to [PIX System, LLC.](http://www.pixsystem.com) for encouraging me\nto chase this effort on company time and supporting sharing to the open source\necosystem.\n", "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/reinecke/IOU", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "iou", "package_url": "https://pypi.org/project/iou/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/iou/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/reinecke/IOU" }, "release_url": "https://pypi.org/project/iou/0.1.0/", "requires_dist": null, "requires_python": null, "summary": "Promise-like data encapsulation for asynchronous behavior", "version": "0.1.0" }, "last_serial": 1449504, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "3010907064780d2eefb7f6f324f70f05", "sha256": "dc95476404994256b1e7baf4cd63a9b5df9f3ebc151e02b9ecab64c2158ac159" }, "downloads": -1, "filename": "iou-0.1.0.tar.gz", "has_sig": false, "md5_digest": "3010907064780d2eefb7f6f324f70f05", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10025, "upload_time": "2015-03-05T18:27:01", "url": "https://files.pythonhosted.org/packages/8e/1a/f8e29b196bdd09e8b90bd0f0a23b185566f3ca8072a55f945b5c2a8ffeca/iou-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "3010907064780d2eefb7f6f324f70f05", "sha256": "dc95476404994256b1e7baf4cd63a9b5df9f3ebc151e02b9ecab64c2158ac159" }, "downloads": -1, "filename": "iou-0.1.0.tar.gz", "has_sig": false, "md5_digest": "3010907064780d2eefb7f6f324f70f05", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10025, "upload_time": "2015-03-05T18:27:01", "url": "https://files.pythonhosted.org/packages/8e/1a/f8e29b196bdd09e8b90bd0f0a23b185566f3ca8072a55f945b5c2a8ffeca/iou-0.1.0.tar.gz" } ] }