{ "info": { "author": "Plone Foundation", "author_email": "plone-developers@lists.sourceforge.net", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Plone", "Framework :: Plone :: 4.0", "Framework :: Plone :: 4.1", "Framework :: Plone :: 4.2", "Framework :: Plone :: 4.3", "Framework :: Plone :: 5.0", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License (GPL)", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "===============\r\nplone.app.async\r\n===============\r\n\r\nIntroduction\r\n============\r\nIntegration package for `zc.async`_ allowing asynchronous operations in\r\nPlone 3 and 4.\r\n\r\n.. contents::\r\n\r\nRepository\r\n=================\r\n- https://github.com/plone/plone.app.async\r\n\r\nInstallation\r\n============\r\nYou will typically run ``plone.app.async`` in a ZEO environment, where you\r\nwill have one or more *worker* instances carrying out jobs queued by your\r\nmain zope instances.\r\n\r\nFor the sake of simplicity it is assumed that you have one instance that can\r\nqueue new jobs, and one worker instance that consumes them, both operating on\r\na single database. In this case your buildout configuration will look similar\r\nto the following::\r\n\r\n [zeo]\r\n recipe = plone.recipe.zope2zeoserver\r\n file-storage = ${buildout:directory}/var/filestorage/Data.fs\r\n\r\n [instance]\r\n recipe = plone.recipe.zope2instance\r\n eggs = Plone plone.app.async\r\n zcml =\r\n zcml-additional =\r\n \r\n environment-vars =\r\n ZC_ASYNC_UUID ${buildout:directory}/var/instance-uuid.txt\r\n\r\n [worker]\r\n recipe = plone.recipe.zope2instance\r\n eggs = ${instance:eggs}\r\n zcml = ${instance:zcml}\r\n zcml-additional =\r\n \r\n environment-vars =\r\n ZC_ASYNC_UUID ${buildout:directory}/var/worker-uuid.txt\r\n\r\nThere are two important stanzas here:\r\n\r\n* Each instance has to set the ``ZC_ASYNC_UUID`` environment variable in order\r\n to integrate properly with `zc.async`_.\r\n\r\n* Each instance loads the ``single_db_instance.zcml`` configuration.\r\n The worker instance loads the ``single_db_worker.zcml`` configuration\r\n in order to setup the queue and configure itself as a dispatcher.\r\n\r\nFor more details please look at the `example buildout configurations`_ included in\r\nthe package.\r\n\r\n.. _`example buildout configurations`: https://github.com/plone/plone.app.async\r\n\r\n\r\nPlone 3\r\n-------\r\n\r\nUse zope.app.keyreference\r\n\r\n\r\nPlone 4\r\n-------\r\n\r\nUse five.intid\r\n\r\n\r\nCredits\r\n=======\r\nCode from Enfold's `plone.async.core`_ package has been used for setting up the queues.\r\n\r\nReferences\r\n==========\r\n* `zc.async`_ on PyPI\r\n* `plone.async.core`_ Subversion repository\r\n\r\n.. _zc.async: http://pypi.python.org/pypi/zc.async\r\n.. _plone.async.core: https://svn.enfoldsystems.com/public/plone.async.core\r\n\r\n\r\nUser Documentation\r\n==================\r\n\r\nBasic use\r\n---------\r\n\r\nAssuming your setup is done correctly, you can start by obtaining the\r\n``AsyncService`` utility::\r\n\r\n >>> from zope.component import getUtility\r\n >>> from plone.app.async.interfaces import IAsyncService\r\n >>> async = getUtility(IAsyncService)\r\n >>> async\r\n \r\n >>> folder = layer['test-folder']\r\n >>> portal = layer['portal']\r\n\r\nYou can already get the ``zc.async`` queues::\r\n\r\n >>> async.getQueues()\r\n \r\n\r\n >>> import zc.async.dispatcher\r\n >>> from plone.app.async.testing import _dispatcher_uuid\r\n >>> zc.async.dispatcher.get(_dispatcher_uuid)\r\n \r\n >>> queue = async.getQueues()['']\r\n >>> queue\r\n \r\n\r\nLet's define a simple function to be executed asynchronously. Note that the\r\nfirst argument **must** be a valid Zope object::\r\n\r\n >>> from plone.app.async.tests.funcs import *\r\n\r\nand queue it::\r\n\r\n >>> job = async.queueJob(addNumbers, folder, 40, 2)\r\n >>> len(queue)\r\n 1\r\n >>> job.status\r\n u'pending-status'\r\n\r\n\r\nIn real life the job would be executed by the worker. In the tests we need\r\nto commit in order to let the dispatcher become aware of the job and\r\nexecute it. Also we wait for the job to complete before continuing with the\r\ntest::\r\n\r\n >>> import transaction\r\n >>> from zc.async.testing import wait_for_result\r\n >>> transaction.commit()\r\n >>> wait_for_result(job)\r\n 42\r\n\r\nBatches of jobs\r\n----------------\r\n\r\nLet's now try some jobs that create persistent objects. First define\r\nthe tasks to be executed asynchronously::\r\n\r\n >>> from Products.CMFCore.utils import getToolByName\r\n\r\n\r\nQueue a job that creates a document and another that submits it::\r\n\r\n >>> job = async.queueJob(createDocument, folder,\r\n ... 'foo', 'title', 'description', 'body')\r\n >>> job2 = async.queueJob(submitObject, folder, 'foo')\r\n >>> transaction.commit()\r\n\r\nBecause by default the jobs are executed with the default quota set to 1,\r\n(i.e. only one job can be executed at a time), jobs are executed serially and\r\naccording to the order by which they were submitted. Hence, waiting for the\r\njob that submits the document implies that the one that created it has already \r\nbeen carried out::\r\n\r\n >>> wait_for_result(job2)\r\n >>> wt = getToolByName(folder, 'portal_workflow')\r\n >>> doc = folder['foo']\r\n >>> wt.getInfoFor(doc, 'review_state')\r\n 'pending'\r\n\r\nYou can also queue a *batch* of jobs to be executed serially as one job by use\r\nof ``queueSerialJobs``::\r\n\r\n >>> from plone.app.async.service import makeJob\r\n >>> job = async.queueSerialJobs(\r\n ... makeJob(createDocument, folder,\r\n ... 'bar', 'title', 'description', 'body'),\r\n ... makeJob(submitObject, folder, 'bar'))\r\n >>> transaction.commit()\r\n >>> res = wait_for_result(job)\r\n >>> res[0].result\r\n 'bar'\r\n >>> res[1].status\r\n u'completed-status'\r\n >>> doc = folder['bar']\r\n >>> wt.getInfoFor(doc, 'review_state')\r\n 'pending'\r\n\r\nIf you want to execute jobs in parallel, you can use ``queueParallelJobs``.\r\n\r\nSecurity and user permissions\r\n-----------------------------\r\n\r\nWhen a job is queued by some user, it is also executed by the same user, with\r\nthe same roles and permissions. So for instance::\r\n\r\n >>> job = async.queueJob(createDocument, portal,\r\n ... 'foo', 'title', 'description', 'body')\r\n >>> transaction.commit()\r\n\r\nwill fail as the user is not allowed to create content in the Plone root::\r\n\r\n >>> wait_for_result(job)\r\n <...Unauthorized...\r\n\r\nHandling failure and success\r\n----------------------------\r\n\r\nIf you need to act on the result of a job or handle a failure you can do\r\nso by adding callbacks. For instance::\r\n\r\n >>> from plone.app.async.tests import funcs\r\n >>> job = async.queueJob(addNumbers, folder, 40, 2)\r\n >>> c = job.addCallback(job_success_callback)\r\n >>> transaction.commit()\r\n >>> r = wait_for_result(job)\r\n >>> funcs.results\r\n ['Success: 42']\r\n\r\nFailures can be handled in the same way::\r\n\r\n >>> job = async.queueJob(failingJob, folder)\r\n >>> c = job.addCallbacks(failure=job_failure_callback)\r\n >>> transaction.commit()\r\n >>> r = wait_for_result(job)\r\n >>> funcs.results\r\n [...RuntimeError...\r\n\r\nIt is also possible to handle all successful/failed jobs (for instance if you\r\nwant to send an email upon failure) by subscribing to the respective event::\r\n\r\n >>> from zope.component import provideHandler\r\n >>> from plone.app.async.interfaces import IJobSuccess, IJobFailure\r\n >>> provideHandler(successHandler, [IJobSuccess])\r\n >>> provideHandler(failureHandler, [IJobFailure])\r\n >>> funcs.results = []\r\n >>> job1 = async.queueJob(addNumbers, folder, 40, 2)\r\n >>> job2 = async.queueJob(failingJob, folder)\r\n >>> transaction.commit()\r\n >>> r = wait_for_result(job2)\r\n >>> funcs.results\r\n [42, ...RuntimeError...FooBared...\r\n\r\nLet's clean up and unregister the success/failure handlers...::\r\n\r\n >>> from zope.component import getGlobalSiteManager\r\n >>> gsm = getGlobalSiteManager()\r\n >>> _ = gsm.unregisterHandler(successHandler, [IJobSuccess])\r\n >>> _ = gsm.unregisterHandler(failureHandler, [IJobFailure])\r\n >>> transaction.commit()\r\n\r\n\r\nChangelog\r\n=========\r\n\r\n\r\n1.7 (unreleased)\r\n----------------\r\n\r\n- Add zope.minmax dependency\r\n [vangheem]\r\n\r\n\r\n1.4 (2012-10-09)\r\n----------------\r\n- fix tests, and helpers mainly for plone.app.async testing layer consumers (collective.cron) [kiorky]\r\n- fix rst markup [kiorky]\r\n\r\n1.3 (2012-10-05)\r\n----------------\r\n\r\n- buildout infrastructure refresh [kiorky]\r\n- plone 4.3 compatibility [kiorky]\r\n- Switch tests from collective.testcaselayer to plone.app.testing [kiorky]\r\n- Merge expemimental work on UI & jobs back to master\r\n [kiorky]\r\n- Add plone UI to view jobs\r\n [davisagli]\r\n- Added support for queued/deferred tasks.\r\n [kiork,davisagli,do3cc]\r\n\r\n\r\n1.2 - 2012-04-26\r\n----------------\r\n\r\n- Fix includes to work correctly with dexterity and\r\n to include simplejson.\r\n [vangheem]\r\n\r\n- Change ping intervals to something more sane so it doesn't\r\n take so long for your workers to come back online after restart.\r\n [vangheem]\r\n\r\n\r\n1.1 - 2011-07-21\r\n----------------\r\n\r\n- Add MANIFEST.in.\r\n [WouterVH]\r\n\r\n- Change zcml:condition for zope.(app.)keyreference to use the plone-4\r\n feature.\r\n [vangheem]\r\n\r\n- Always use loadZCMLFile in testcase layers to not break under Zope 2.13.\r\n [stefan]\r\n\r\n- Avoid excessive ZODB growth by increasing the dispatcher ping intervals.\r\n https://mail.zope.org/pipermail/zope-dev/2011-June/043146.html\r\n [stefan]\r\n\r\n\r\n1.0 - 2011-01-03\r\n----------------\r\n\r\n- Conditionally include zope.app.keyreference ZCML.\r\n [vangheem]\r\n\r\n- Fix for async jobs started by anonymous user.\r\n [naro]\r\n\r\n- Add full set of example buildouts.\r\n [stefan]\r\n\r\n- Make tests pass under Plone 3 and 4. Exception representations have changed\r\n for some odd reason.\r\n [stefan]\r\n\r\n\r\n1.0a6 - 2010-10-14\r\n------------------\r\n\r\n- First public release.\r\n [ggozad]\r\n\r\n\r\n1.0a5 - 2010-10-14\r\n------------------\r\n\r\n- Instead of guessing where a userid may be coming from, record the path\r\n of the userfolder and use that to reinstate the user.\r\n [mj]\r\n\r\n\r\n1.0a4 - 2010-09-09\r\n------------------\r\n\r\n- Use multi-db setup in tests to keep testcaselayer working as expected.\r\n [stefan, ggozad]\r\n\r\n\r\n1.0a3 - 2010-09-01\r\n------------------\r\n\r\n- Separate helper function from test setup so it can be used in non-test code.\r\n [witsch]\r\n\r\n\r\n1.0a2 - 2010-08-30\r\n------------------\r\n\r\n- Made separate zcml configurations for single/multi and instance/worker.\r\n [stefan, ggozad]\r\n\r\n\r\n1.0a1 - 2010-08-25\r\n------------------\r\n\r\n- zc.async integration for Plone. Initial release.\r\n [ggozad, stefan]", "description_content_type": null, "docs_url": null, "download_url": "http://pypi.python.org/pypi/plone.app.async", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://plone.org/products/plone.app.async", "keywords": "plone async asynchronous", "license": "GPL version 2", "maintainer": "", "maintainer_email": "", "name": "plone.app.async", "package_url": "https://pypi.org/project/plone.app.async/", "platform": "Any", "project_url": "https://pypi.org/project/plone.app.async/", "project_urls": { "Download": "http://pypi.python.org/pypi/plone.app.async", "Homepage": "http://plone.org/products/plone.app.async" }, "release_url": "https://pypi.org/project/plone.app.async/1.7.0/", "requires_dist": null, "requires_python": null, "summary": "Integration package for zc.async allowing asynchronous operations in Plone", "version": "1.7.0" }, "last_serial": 1977008, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "5579d4c044bd4c3cdfcf388e32149478", "sha256": "65de537315c048c6fa4b96502744f885ff507fe8078a8dbcd83040c43eca579f" }, "downloads": -1, "filename": "plone.app.async-1.0.zip", "has_sig": false, "md5_digest": "5579d4c044bd4c3cdfcf388e32149478", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45518, "upload_time": "2011-01-03T16:27:14", "url": "https://files.pythonhosted.org/packages/6b/c5/6af5aedecfad5b9a3b9935cd3ea84cd8a1663e45184c1e23fc0570ef23aa/plone.app.async-1.0.zip" } ], "1.0a6": [ { "comment_text": "", "digests": { "md5": "29a1ce35a2c9e8517ae820e117a72d60", "sha256": "1f0b34f1fd12aa417ca04e3a23bbf58229a552fb5944bf5e7f65897ab3d2391c" }, "downloads": -1, "filename": "plone.app.async-1.0a6.zip", "has_sig": false, "md5_digest": "29a1ce35a2c9e8517ae820e117a72d60", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 40189, "upload_time": "2010-10-15T13:05:59", "url": "https://files.pythonhosted.org/packages/f6/d9/44029f7d227f6cb3721c499a4d5fc6a603ff4e77d63d06a87836232da273/plone.app.async-1.0a6.zip" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "dfd5e5d39f882c34801351d1e3294440", "sha256": "75fe28109ac787b2bd41e90b6305177a2bbdb826431803ad5c76a4a5ee36e51d" }, "downloads": -1, "filename": "plone.app.async-1.1.zip", "has_sig": false, "md5_digest": "dfd5e5d39f882c34801351d1e3294440", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45965, "upload_time": "2011-07-21T16:40:55", "url": "https://files.pythonhosted.org/packages/f4/cc/7b72f84bf537c094c000cc369ee62901464ee4a868e69302ca341edc2a3d/plone.app.async-1.1.zip" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "ab09e8161215a3c2d09e87a3829c9c96", "sha256": "91fcdc34731635b27f386cccf41316cf23cbc9951944809d0355e92d9a11d80f" }, "downloads": -1, "filename": "plone.app.async-1.2.zip", "has_sig": false, "md5_digest": "ab09e8161215a3c2d09e87a3829c9c96", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 46401, "upload_time": "2012-04-26T16:31:20", "url": "https://files.pythonhosted.org/packages/a3/47/cdeb3dabb5991888e8a9583926937487c4664afc8bc1fcb644809c17d63c/plone.app.async-1.2.zip" } ], "1.3": [ { "comment_text": "", "digests": { "md5": "561ec6f0910faf61ed9839dfc0ba29a0", "sha256": "28951d25bfc1eb98d932ae5953ed2eb6339a790272ce41c1802a4f9db803d67a" }, "downloads": -1, "filename": "plone.app.async-1.3.zip", "has_sig": false, "md5_digest": "561ec6f0910faf61ed9839dfc0ba29a0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 58372, "upload_time": "2012-10-05T19:11:28", "url": "https://files.pythonhosted.org/packages/01/e4/2430fc628e6f346e71ea75b40636568b556f8e39530576574872b3e741c8/plone.app.async-1.3.zip" } ], "1.4": [ { "comment_text": "", "digests": { "md5": "79a2041b506b18c0d95c14d595c6cccd", "sha256": "41f8270aa694b34c682f5f10eaccb7807e5c8b88428a6daf268f42c9b9c623ba" }, "downloads": -1, "filename": "plone.app.async-1.4.zip", "has_sig": false, "md5_digest": "79a2041b506b18c0d95c14d595c6cccd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 58933, "upload_time": "2012-10-09T16:59:16", "url": "https://files.pythonhosted.org/packages/a5/47/ace795ddfc875f131c5a4eaa05799c16f497fa8de6830a3762dbd7426e44/plone.app.async-1.4.zip" } ], "1.5": [ { "comment_text": "", "digests": { "md5": "8ceda631ce10766a69b2f4b655662234", "sha256": "befb42edc640076c1c94e888aa4cbf3b6bd799eca2f2f43a799f0167bcf81198" }, "downloads": -1, "filename": "plone.app.async-1.5.zip", "has_sig": false, "md5_digest": "8ceda631ce10766a69b2f4b655662234", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 58988, "upload_time": "2012-10-09T17:04:32", "url": "https://files.pythonhosted.org/packages/81/f6/1145c8a81dea5a3f2351886311423db20f8048191fa00d9dd700c08fc4ae/plone.app.async-1.5.zip" } ], "1.6": [ { "comment_text": "", "digests": { "md5": "2742675f78b3862537001f21113a4def", "sha256": "ef24b9e450d0f42b7498096afe767e5817372bb246603c7307facdd0078b169c" }, "downloads": -1, "filename": "plone.app.async-1.6.zip", "has_sig": false, "md5_digest": "2742675f78b3862537001f21113a4def", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 59025, "upload_time": "2012-10-09T17:05:34", "url": "https://files.pythonhosted.org/packages/65/0e/29fb9bc97d96694624b5c1a8566dbf6dbbf49ca09e00d4c4eccd6dec196e/plone.app.async-1.6.zip" } ], "1.7.0": [ { "comment_text": "", "digests": { "md5": "44fdfaccbea9fad8a4e56a5c1bfdf04d", "sha256": "dadbc2f9e9679126788875772e78be24cb1e1a296d126b89570476d3ae15b07a" }, "downloads": -1, "filename": "plone.app.async-1.7.0.zip", "has_sig": false, "md5_digest": "44fdfaccbea9fad8a4e56a5c1bfdf04d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 59163, "upload_time": "2016-02-25T22:09:57", "url": "https://files.pythonhosted.org/packages/ac/5f/17b0cb74f0444179dc18f8b20c2fc3b43e70dc6aff46064272ca5fb9a024/plone.app.async-1.7.0.zip" } ], "1.7.dev0": [ { "comment_text": "", "digests": { "md5": "1c0df0bad20d4e9df403553e4cb4d1ae", "sha256": "5f5bb949c2effd1c66b55fee86c0ef8aa8db6650bd9d80c61aa0d9e3a1c3cc70" }, "downloads": -1, "filename": "plone.app.async-1.7.dev0.zip", "has_sig": false, "md5_digest": "1c0df0bad20d4e9df403553e4cb4d1ae", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 59559, "upload_time": "2016-02-25T22:09:07", "url": "https://files.pythonhosted.org/packages/b6/e8/582f0cf3d14ab51ae6d76377deebbbda2fa639e782efe76c2bd6f1c9956f/plone.app.async-1.7.dev0.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "44fdfaccbea9fad8a4e56a5c1bfdf04d", "sha256": "dadbc2f9e9679126788875772e78be24cb1e1a296d126b89570476d3ae15b07a" }, "downloads": -1, "filename": "plone.app.async-1.7.0.zip", "has_sig": false, "md5_digest": "44fdfaccbea9fad8a4e56a5c1bfdf04d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 59163, "upload_time": "2016-02-25T22:09:57", "url": "https://files.pythonhosted.org/packages/ac/5f/17b0cb74f0444179dc18f8b20c2fc3b43e70dc6aff46064272ca5fb9a024/plone.app.async-1.7.0.zip" } ] }