{ "info": { "author": "Mikhail Korobov", "author_email": "kmike84@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Topic :: Database", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "===============\ntornado-slacker\n===============\n\nThis package provides an easy API for moving the work out of\nthe tornado process / event loop.\n\nCurrently implemented methods are:\n\n* execute the code in another server's http hook\n (django implementation is included);\n* execute the code in a separate thread (thread pool is used);\n* dummy immediate execution.\n\nAPI example::\n\n from django.contrib.auth.models import User\n from slacker import adisp\n from slacker import Slacker\n from slacker.workers import DjangoWorker\n\n AsyncUser = Slacker(User, DjangoWorker())\n\n @adisp.process\n def process_data():\n # all the django ORM is supported; the query will be executed\n # on remote end, this will not block the IOLoop\n\n users = yield AsyncUser.objects.filter(is_staff=True)[:5]\n print users\n\n(pep-342 syntax and adisp library are optional, callback-style code\nis also supported)\n\n\nInstallation\n============\n\n::\n\n pip install tornado-slacker\n\n\nSlackers and workers\n====================\n\nIn order to execute some code in non-blocking manner:\n\n1. Create a Slacker (configured with the desired worker) for some python object::\n\n from slacker import Slacker\n from slacker.workers import ThreadWorker\n\n class Foo(object):\n # ...\n\n worker = ThreadWorker()\n AsyncFoo = Slacker(Foo, worker)\n\n2. build a query (you can access attributes, do calls and slicing)::\n\n query = AsyncFoo('foo').do_blocking_operation(param1, param2)[0]\n\n3. execute the query::\n\n def callback(result):\n # ...\n\n query.proceed(callback)\n\n or, using pep-342 style::\n\n from slacker import adisp\n\n @adisp.process\n def handler():\n result = yield query\n # ...\n\nSlackers\n========\n\nSlackers are special objects that are collecting operations (attribute\naccess, calls, slicing) without actually executing them::\n\n >>> from slacker import Slacker\n >>> class Foo():\n ... pass\n ...\n >>> FooSlacker = Slacker(Foo)\n >>> FooSlacker.hello.world()\n __main__.Foo: [('hello',), ('world', (), {})]\n\n >>> FooSlacker(name='me').hello.world(1, y=3)[:3]\n __main__.Foo: [(None, (), {'name': 'me'}),\n ('hello',),\n ('world', (1,), {'y': 3}),\n (slice(None, 3, None), None)]\n\nCallables arguments must be picklable. Slackers also provide a\nmethod to apply the collected operations to a base object.\n\nAny picklable object (including top-level functions and classes) can\nbe wrapped into Slacker, e.g.::\n\n from slacker import adisp\n from slacker import Slacker\n from slacker.workers import ThreadWorker\n\n def task(param1, param2):\n # do something blocking and io-bound\n return results\n\n async_task = Slacker(task, ThreadWorker())\n\n # pep-342-style\n @adisp.process\n def process_data():\n results = yield async_task('foo', 'bar')\n print results\n\n # callback style\n def process_data2():\n async_task('foo', 'bar').proceed(on_result)\n\n def on_result(results):\n print results\n\n\nPython modules also can be Slackers::\n\n import shutil\n from slacker import Slacker\n from slacker.workers import ThreadWorker\n\n shutil_async = Slacker(shutil, ThreadWorker())\n op = shutil_async.copy('file1.txt', 'file2.txt')\n op.proceed()\n\nWorkers\n=======\n\nWorkers are classes that decides how and where the work should be done:\n\n* ``slacker.workers.DummyWorker`` executes code in-place (this\n is blocking);\n\n* ``slacker.workers.ThreadWorker`` executes code in a thread from\n a thread pool;\n\n* ``slacker.workers.HttpWorker`` pickles the slacker, makes an async\n http request with this data to a given server hook and expects it\n to execute the code and return pickled results;\n\n .. note::\n\n IOLoop blocks on any CPU activity and making http requests plus\n unpickling the returned result can cause a significant overhead\n here. So if the query is fast (e.g. database primary key or index\n lookup, say 10ms) then it may be better not to use tornado-slacker\n and call the query in 'blocking' way: the overall blocking time\n may be less than with 'async' approach because of reduced\n computations amount.\n\n It is also wise to return as little as possible if HttpWorker is used.\n\n\n* ``slacker.workers.DjangoWorker`` is just a HttpWorker with default\n values for use with bundled django remote server hook implementation\n (``slacker.django_backend``).\n\n In order to enable django hook, include 'slacker.django_backend.urls'\n into urls.py and add SLACKER_SERVER option with server address to\n settings.py.\n\n SLACKER_SERVER is '127.0.0.1:8000' by default so this should work for\n development server out of box.\n\n .. warning::\n\n Do not expose django server hook to public, this is insecure!\n The best way is to configure additional server instance to listen\n some local port (e.g. bind it to the default 127.0.0.1:8000 address).\n\n .. note::\n\n Django's QuerySet arguments like Q, F objects, aggregate and annotate\n functions (e.g. Count) are picklable so tornado-slacker can handle\n them fine::\n\n AsyncAuthor = Slacker(Author, DjangoWorker())\n\n # ...\n qs = AsyncAuthor.objects.filter(\n Q(name='vasia') or Q(is_great=True)\n ).values('name').annotate(average_rating=Avg('book__rating'))[:10]\n\n authors = yield qs\n\n Using slacker.Slacker is better than pickling queryset.query\n (as adviced at http://docs.djangoproject.com/en/dev/ref/models/querysets/#pickling-querysets)\n because this allows to pickle any ORM calls including ones that\n don't return QuerySets (http://docs.djangoproject.com/en/dev/ref/models/querysets/#methods-that-do-not-return-querysets)::\n\n yield AsyncUser.objects.create_superuser('foo')\n\n Moreover, slacker.Slacker adds transparent support for remote invocation\n of custom managers and model methods, returning just the model instance\n attributes, etc.\n\n\nParallel execution\n==================\n\nParallel task execution is supported by adisp library::\n\n def _task1(param1, param2):\n # do something blocking\n return results\n\n def _task2():\n # do something blocking\n return results\n\n # worker can be reused\n worker = ThreadWorker()\n task1 = Slacker(_task1, worker)\n task2 = Slacker(_task2, worker)\n\n @adisp.process\n def process_data():\n # this will execute task1 and task2 in parallel\n # and return the result after all data is ready\n res1, res2 = yield task1('foo', 'bar'), task2()\n print res1, res2\n\n.. note::\n\n this will fail with ``DjangoWorker`` and django development server\n because django development server is single-threaded\n\n\nContributing\n============\n\nIf you have any suggestions, bug reports or\nannoyances please report them to the issue tracker:\n\n* https://github.com/kmike/tornado-slacker/issues\n\nSource code:\n\n* https://bitbucket.org/kmike/tornado-slacker/\n* https://github.com/kmike/tornado-slacker/\n\nBoth hg and git pull requests are welcome!\n\nCredits\n=======\n\nInspiration:\n\n* https://github.com/satels/django-async-dbslayer/\n* https://bitbucket.org/david/django-roa/\n* http://tornadogists.org/654157/\n\nThird-party software:\n\n* `adisp `_ (tornado adisp implementation\n is taken from `brukva `_);\n* exception serialization utils are from\n `billiard `_ by Ask Solem.\n\nLicense\n=======\n\nThe license is MIT.\n\nBundled adisp library uses Simplified BSD License.\n\nslacker.serialization is under BSD License.", "description_content_type": null, "docs_url": null, "download_url": "https://bitbucket.org/kmike/tornado-slacker/get/tip.zip", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/kmike/tornado-slacker/", "keywords": null, "license": "MIT license", "maintainer": null, "maintainer_email": null, "name": "tornado-slacker", "package_url": "https://pypi.org/project/tornado-slacker/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/tornado-slacker/", "project_urls": { "Download": "https://bitbucket.org/kmike/tornado-slacker/get/tip.zip", "Homepage": "https://github.com/kmike/tornado-slacker/" }, "release_url": "https://pypi.org/project/tornado-slacker/0.1/", "requires_dist": null, "requires_python": null, "summary": "This package provides an easy API for moving the work out of the tornado process / event loop.", "version": "0.1" }, "last_serial": 655968, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "6691d19163597af80b43ee613a6fde23", "sha256": "759e5a97b8175f3c712a366af93b3365bee5a946eff42f2671d64f714b27eed6" }, "downloads": -1, "filename": "tornado-slacker-0.0.1.tar.gz", "has_sig": false, "md5_digest": "6691d19163597af80b43ee613a6fde23", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8921, "upload_time": "2011-04-29T01:59:59", "url": "https://files.pythonhosted.org/packages/f6/73/221c3d3aa46c385e374eb2021568b04f08a2f91798dfddeff771b4534f85/tornado-slacker-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "0fd08d57bfb6d80e29459b5025289f1a", "sha256": "37db2b1f12538517d8600eb0ec9e96a678953c8c82455c514931621253e3cc74" }, "downloads": -1, "filename": "tornado-slacker-0.0.2.tar.gz", "has_sig": false, "md5_digest": "0fd08d57bfb6d80e29459b5025289f1a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8415, "upload_time": "2011-04-29T11:20:05", "url": "https://files.pythonhosted.org/packages/0c/b5/92ab337be726f82d0d175035b90f486e582c93a8d0d01a3102b25970f0f9/tornado-slacker-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "9c60ce78c73234fe3719b53754300b1d", "sha256": "15fb3cc5f798ac2e60d62b977151955b8c53b4298f98445185d6d93143c27ca2" }, "downloads": -1, "filename": "tornado-slacker-0.0.3.tar.gz", "has_sig": false, "md5_digest": "9c60ce78c73234fe3719b53754300b1d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10348, "upload_time": "2011-04-29T11:21:18", "url": "https://files.pythonhosted.org/packages/3d/72/f710d35f780f9cfb667790f914691a2af8adb46d847a30315f34bb1bba33/tornado-slacker-0.0.3.tar.gz" } ], "0.1": [ { "comment_text": "", "digests": { "md5": "4c161c8c0b1e9605355a59412771468b", "sha256": "bec84659658a24b1c9076bb2e605786f278911f50db6cdb3949b6c598058f359" }, "downloads": -1, "filename": "tornado-slacker-0.1.tar.gz", "has_sig": false, "md5_digest": "4c161c8c0b1e9605355a59412771468b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13199, "upload_time": "2011-05-03T01:33:06", "url": "https://files.pythonhosted.org/packages/f4/c0/0c6623e4b11705911479ff2d0b1c96672a54e2a5f327b15ea9328666e762/tornado-slacker-0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "4c161c8c0b1e9605355a59412771468b", "sha256": "bec84659658a24b1c9076bb2e605786f278911f50db6cdb3949b6c598058f359" }, "downloads": -1, "filename": "tornado-slacker-0.1.tar.gz", "has_sig": false, "md5_digest": "4c161c8c0b1e9605355a59412771468b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13199, "upload_time": "2011-05-03T01:33:06", "url": "https://files.pythonhosted.org/packages/f4/c0/0c6623e4b11705911479ff2d0b1c96672a54e2a5f327b15ea9328666e762/tornado-slacker-0.1.tar.gz" } ] }