{ "info": { "author": "Holger Krekel", "author_email": "holger.krekel@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "execution locals: killing global state (including thread locals)\n===================================================================\n\nThis module provides execution locals aka \"xlocal\" objects which implement\na more restricted variant of \"thread locals\". An execution local allows to\nmanage attributes on a per-execution basis in a manner similar to how real\nlocals work:\n\n- Invoked functions cannot change the binding for the invoking function\n- existence of a binding is local to a code block (and everything it calls)\n\nAttribute bindings for an xlocal objects will not leak outside a\ncontext-managed code block and they will not leak to other threads of\ngreenlets. By contrast, both process-globals and so called \"thread\nlocals\" do not implement the above properties.\n\nLet's look at a basic example::\n\n # content of example.py\n\n from xlocal import xlocal\n\n xcurrent = xlocal()\n\n def output():\n print \"hello world\", xcurrent.x\n\n if __name__ == \"__main__\":\n with xcurrent(x=1):\n output()\n\nIf we execute this module, the ``output()`` function will see \na ``xcurrent.x==1`` binding::\n\n $ python example.py\n hello world 1\n\nHere is what happens in detail: ``xcurrent(x=1)`` returns a context manager which \nsets/resets the ``x`` attribute on the ``xcurrent`` object. While remaining\nin the same thread/greenlet, all code triggered by the with-body (in this case\njust the ``output()`` function) can access ``xcurrent.x``. Outside the with-\nbody ``xcurrent.x`` would raise an AttributeError. It is also not allowed\nto directly set ``xcurrent`` attributes; you always have to explicitely mark their\nlife-cycle with a with-statement. This means that invoked code:\n\n- cannot rebind xlocal state of its invoking functions (no side effects, yay!)\n- xlocal state does not leak outside the with-context (lifecylcle control)\n\nAnother module may now reuse the example code::\n\n # content of example_call.py\n import example\n \n with example.xcurrent(x=3):\n example.output()\n\nwhich when running ...::\n\n $ python example_call.py\n hello world 3\n\nwill cause the ``example.output()`` function to print the ``xcurrent.x`` binding\nas defined at the invoking ``with xcurrent(x=3)`` statement.\n\nOther threads or greenlets will never see this ``xcurrent.x`` binding; they may even \nset and read their own distincit ``xcurrent.x`` object. This means that all \nthreads/greenlets can concurrently call into a function which will always\nsee the execution specific ``x`` attribute.\n\nUsage in frameworks and libraries invoking \"handlers\"\n-----------------------------------------------------------\n\nWhen invoking plugin code or handler code to perform work, you may not\nwant to pass around all state that might ever be needed. Instead of using\na global or thread local you can safely pass around such state in \nexecution locals. Here is a pseudo example::\n\n xcurrent = xlocal()\n\n def with_xlocal(func, **kwargs):\n with xcurrent(**kwargs):\n func()\n\n def handle_request(request):\n func = gethandler(request) # some user code\n spawn(with_xlocal(func, request=request))\n\n``handle_request`` will run a user-provided handler function in a newly\nspawned execution unit (for example spawn might map to\n``threading.Thread(...).start()`` or to ``gevent.spawn(...)``). The\ngeneric ``with_xlocal`` helper wraps the execution of the handler\nfunction so that it will see a ``xcurrent.request`` binding. Multiple\nspawns may execute concurrently and ``xcurrent.request`` will\ncarry the execution-specific request object in each of them.\n\n\nIssues worth noting\n---------------------------------------\n\nIf a method memorizes an attribute of an execution local, for\nexample the above ``xcurrent.request``, then it will keep a reference to\nthe exact request object, not the per-execution one. If you want to\nkeep a per-execution local, you can do it this way for example::\n\n Class Renderer:\n @property\n def request(self):\n return xcurrent.request\n\nthis means that Renderer instances will have an execution-local\n``self.request`` object even if the life-cycle of the instance crosses\nexecution units.\n\nAnother issue is that if you spawn new execution units, they will not \nimplicitely inherit execution locals. Instead you have to wrap\nyour spawning function to explicitely set execution locals, similar to\nwhat we did in the above \"invoking handlers\" section.\n\nCopyright / inspiration\n-------------------------------------\n\nThis code is based on discussions with Armin Ronacher and others\nin response to a `tweet of mine `_. It extracts and refines some ideas found in Armin's \"werzeug.local\" module\nand friends.\n\n:copyright: (c) 2012 by Holger Krekel, partially Armin Ronacher\n:license: BSD, see LICENSE for more details.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://bitbucket.org/hpk42/xlocal/", "keywords": null, "license": "UNKNOWN", "maintainer": null, "maintainer_email": null, "name": "xlocal", "package_url": "https://pypi.org/project/xlocal/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/xlocal/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://bitbucket.org/hpk42/xlocal/" }, "release_url": "https://pypi.org/project/xlocal/0.5/", "requires_dist": null, "requires_python": null, "summary": "execution locals: killing global state (including thread locals)", "version": "0.5" }, "last_serial": 703268, "releases": { "0.5": [ { "comment_text": "", "digests": { "md5": "b5450d4841523f92229e08b0844db854", "sha256": "8ee1c1551294e366a3c307985ea2129e72717556a5736063c0e089c5fb868405" }, "downloads": -1, "filename": "xlocal-0.5.zip", "has_sig": false, "md5_digest": "b5450d4841523f92229e08b0844db854", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10995, "upload_time": "2012-11-16T13:43:34", "url": "https://files.pythonhosted.org/packages/4f/3b/e7836d2a24926abff5d8bf2f485a0d6f90c2a931c3eaf67a2f45fc605d1f/xlocal-0.5.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b5450d4841523f92229e08b0844db854", "sha256": "8ee1c1551294e366a3c307985ea2129e72717556a5736063c0e089c5fb868405" }, "downloads": -1, "filename": "xlocal-0.5.zip", "has_sig": false, "md5_digest": "b5450d4841523f92229e08b0844db854", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10995, "upload_time": "2012-11-16T13:43:34", "url": "https://files.pythonhosted.org/packages/4f/3b/e7836d2a24926abff5d8bf2f485a0d6f90c2a931c3eaf67a2f45fc605d1f/xlocal-0.5.zip" } ] }