{ "info": { "author": "Dieter Maurer", "author_email": "dieter@handshake.de", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Software Development", "Topic :: Utilities" ], "description": "dm.reuse\n========\n\nUtilities to reuse (slightly modified) objects in new contexts.\n\nCurrently, there is are two utilities: ``rebindFunction`` and\n``OverridingProxy``.\n\n``rebindFunction``\n++++++++++++++++++\n\n``rebindFunction`` allows to reuse the code of a function while changing\nname, globals, default arguments, properties and/or names used.\n\nLets look at a trivial example. Function ``f`` accesses global variables\n``i`` and ``j``.\n\nExamples\n--------\n\n>>> i = 1; j = 2\n>>> def f(): return i, j\n...\n>>> f()\n(1, 2)\n\nWe want to derive a new function `g` which binds `i` to `-1`:\n\n>>> from dm.reuse import rebindFunction\n>>> g=rebindFunction(f, i=-1)\n>>> g()\n(-1, 2)\n\nWe can specify the rebinds not only via keyword arguments but via\na dictionary as well:\n\n>>> g=rebindFunction(f, dict(i=-1, j=-2))\n>>> g()\n(-1, -2)\n\nUsually, the function name is taken over from the original function,\nbut it can be changed:\n\n>>> f.__name__\n'f'\n>>> g.__name__\n'f'\n>>> g=rebindFunction(f, dict(i=-1, j=-2), funcName='g')\n>>> g.__name__\n'g'\n>>> g()\n(-1, -2)\n\nThe originals function docstring is taken over, too -- unless\noverridden:\n\n>>> f.func_doc = 'some documentation'\n>>> g=rebindFunction(f, dict(i=-1, j=-2))\n>>> f.__doc__ is g.__doc__\nTrue\n>>> g=rebindFunction(f, dict(i=-1, j=-2), funcDoc='some new documentation')\n>>> g.__doc__\n'some new documentation'\n\nDefault values for arguments can be added, removed or changed.\nUnknown arguments are recognized:\n\n>>> def f(a1, a2=2): return a1, a2\n...\n>>> g=rebindFunction(f, argRebindDir=dict(a1=1))\n>>> g()\n(1, 2)\n\n>>> from dm.reuse import REQUIRED\n>>> g=rebindFunction(f, argRebindDir=dict(a2=REQUIRED))\n>>> g(1) #doctest: +IGNORE_EXCEPTION_DETAIL\nTraceback (most recent call last):\n ...\nTypeError: f() takes exactly 2 arguments (1 given)\n\n>>> g=rebindFunction(f, argRebindDir=dict(a2=10))\n>>> g(1)\n(1, 10)\n\n>>> g=rebindFunction(f, argRebindDir=dict(a3=10))\nTraceback (most recent call last):\n ...\nValueError: unknown arguments in `argRebindDir`: a3\n\nFinally, function properties can be rebound with `propRebindDir`.\nWe are careful, to give the new function a separate new property dict.\n\n>>> f.prop='p'\n>>> g=rebindFunction(f)\n>>> g.prop\n'p'\n>>> g=rebindFunction(f, propRebindDir=dict(prop='P', prop2='p2'))\n>>> g.prop, g.prop2\n('P', 'p2')\n>>> f.__dict__\n{'prop': 'p'}\n\nOccationally, functions use local imports which are not adequate\nin the new context. In order to provide control over them, names\nused inside the function code can be changed.\n\n>>> def f(a): import codecs; return codecs, a\n...\n>>> g=rebindFunction(f, nameRebindDir=dict(codecs='urllib'))\n>>> r = g(1)\n>>> r[0].__name__, r[1]\n('urllib', 1)\n\nThis way, references to global variables can be changed as well.\n\n>>> i1, i2 = 1, 2\n>>> def f(): return i1\n... \n>>> g=rebindFunction(f, nameRebindDir=dict(i1='i2'))\n>>> g()\n2\n\n\n``OverridingProxy``\n+++++++++++++++++++\n\nOccasionally, you must work with an object, whose overall functionality\nis mostly adequate but small aspects need to be changed.\nIn those cases, ``OverridingProxy`` might be of help. It allows\nto proxy an object. The proxy mostly behaves like the proxied\nobject but some attributes/methods are overridden.\n\nExamples\n--------\n\nWe set up a play object ``c`` and pretend that we must use it\nwith modified attribute ``attr1``. We achieve this with a proxy\nwhich behaves almost like ``c`` but has the attribute changed.\n\n>>> from dm.reuse.proxy import OverridingProxy, make_proxy\n>>> \n>>> class C(object):\n... attr1 = 1\n... attr2 = 2\n... def f(self): return self.attr1, self.attr2\n... def g(self): return self.f()\n... \n>>> c = C()\n>>> p = make_proxy(c, attr1='overridden attr1')\n>>> p.__class__ is C\nTrue\n>>> isinstance(p, C)\nTrue\n>>> p.attr1\n'overridden attr1'\n\nThe change is also effective when we call methods which use\nthe overrriden attribute.\n\n>>> p.f()\n('overridden attr1', 2)\n\nIf we assign a new value to a proxy attribute, then the\ncorresponding attribute on the proxied object is changed.\nAs a consequence, changing overridden attributes have\napparently no effect -- this is unintuitive and might be changed\nin the future.\n\n>>> p.attr2 = \"new attr2\"\n>>> p.f()\n('overridden attr1', 'new attr2')\n>>> p.attr1 = \"new attr1\"\n>>> c.attr1\n'new attr1'\n>>> p.f()\n('overridden attr1', 'new attr2')\n\nYou can use the method ``set_proxy_attribute`` to change\nthe proxies attributes rather than those of the proxied method.\n\n>>> p.set_proxy_attribute(\"attr1\", \"new overridden attr1\")\n>>> p.f()\n('new overridden attr1', 'new attr2')\n\nA proxy compares usually equal to its proxied object. Sometimes,\nthis works also for the inverse. But, two\nproxies for the same proxied object compare usually equal.\n\n>>> p == c\nTrue\n>>> c == p\nTrue\n>>> make_proxy(c) == p\nTrue\n\n\nUsually, you would not override simple attributes but methods.\nThis is easier to achieve with a custom proxy class.\nIn many cases, an overriding method will want to call the\noverridden method; this is possible via ``call_proxied_method``.\n\n>>> class MyProxy(OverridingProxy):\n... def g(self):\n... print (\"g called\")\n... return self.call_proxied_method(\"g\")\n... \n>>> p = make_proxy(c, MyProxy, attr1=\"overridden attr1\")\n>>> p.g()\ng called\n('overridden attr1', 'new attr2')\n\n\nThe proxies have limited support for special methods and\nthereby support e.g. subscription. Note that this support\nis incomplete and surprises are possible.\n\n>>> class MyDict(dict, C): pass\n...\n>>> md = MyDict(dict(a=1, b=2))\n>>> p = make_proxy(md, attr1=\"overridden attr1\")\n>>> p.f()\n('overridden attr1', 2)\n>>> p[\"a\"]\n1\n>>> class MySequence(C):\n... seq = 0, 1, 2\n... def __getitem__(self, i): return self.seq[i]\n...\n>>> c = MySequence()\n>>> p = make_proxy(c, seq=(10, 11, 12))\n>>> p[0]\n10\n>>> p = make_proxy(c)\n>>> p[0]\n0\n\n\nUsually, descriptors, too, see the modified state.\n\n>>> class WithProperty(C):\n... @property\n... def attr1_prop(self): return self.attr1\n...\n>>> c = WithProperty()\n>>> p = make_proxy(c, attr1=\"overridden attr1\")\n>>> p.attr1_prop\n'overridden attr1'\n\n\nHistory\n+++++++\n\n2.1\n added ``OverridingProxy``, allowing to reuse objects with\n small modifications\n\n2.0\n added partial support for Python 3 (keyword only arguments and\n annotations are not yet supported)\n\n dropped support for Python before 2.7\n\n1.1\n ``nameRebindDir`` support added", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://pypi.org/project/dm.reuse", "keywords": "reuse", "license": "BSD (see \"dm/reuse/LICENSE.txt\", for details)", "maintainer": "", "maintainer_email": "", "name": "dm.reuse", "package_url": "https://pypi.org/project/dm.reuse/", "platform": "", "project_url": "https://pypi.org/project/dm.reuse/", "project_urls": { "Homepage": "https://pypi.org/project/dm.reuse" }, "release_url": "https://pypi.org/project/dm.reuse/2.1.1/", "requires_dist": null, "requires_python": "", "summary": "Support for object reuse with slight modifications", "version": "2.1.1" }, "last_serial": 4620722, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "d3f994b1f08c1c153ed59bc6ef31da4b", "sha256": "b9fca6adf30ee11ef2e03203f26fa35a41a868c9c5850bc0bcd984100d69aaa7" }, "downloads": -1, "filename": "dm.reuse-1.0.tar.gz", "has_sig": false, "md5_digest": "d3f994b1f08c1c153ed59bc6ef31da4b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4386, "upload_time": "2007-10-03T11:34:19", "url": "https://files.pythonhosted.org/packages/df/84/b3a7882368598837cfeaa3fef6f3a17e09e4ee04179ed5a04656edc03c28/dm.reuse-1.0.tar.gz" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "e7672fb295e434265f245d5e5b9aab49", "sha256": "23144495720bc08c6c408af25403601226905e33ea099294090a05865082a8c2" }, "downloads": -1, "filename": "dm.reuse-1.1.tar.gz", "has_sig": false, "md5_digest": "e7672fb295e434265f245d5e5b9aab49", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4937, "upload_time": "2010-10-19T10:08:59", "url": "https://files.pythonhosted.org/packages/a1/4d/8a7358e154343614202f7f908d75a49b02a8f10ab39e8fe2d9b641afc4fc/dm.reuse-1.1.tar.gz" } ], "2.0": [ { "comment_text": "", "digests": { "md5": "1ee1d34645fbd23af91081592c35bbe3", "sha256": "0e7c92c5e00c0ca677c3d51219e9f99a8f8c8af7905fbb014d65c5d440ebfd12" }, "downloads": -1, "filename": "dm.reuse-2.0.tar.gz", "has_sig": false, "md5_digest": "1ee1d34645fbd23af91081592c35bbe3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5229, "upload_time": "2018-11-11T09:58:42", "url": "https://files.pythonhosted.org/packages/b2/ed/c2131dd185596b4b19f925894672aa2bc1ce9eea02006c954cc551d7e270/dm.reuse-2.0.tar.gz" } ], "2.1": [ { "comment_text": "", "digests": { "md5": "380f3d6a2450d6195666f535dea8b7c8", "sha256": "328d5808dd2d0a7a3d3b4f77ecfb88ba7ed786eb4078c249c651f7a49e44d414" }, "downloads": -1, "filename": "dm.reuse-2.1.tar.gz", "has_sig": false, "md5_digest": "380f3d6a2450d6195666f535dea8b7c8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7038, "upload_time": "2018-12-17T09:39:45", "url": "https://files.pythonhosted.org/packages/a4/88/899e53f296caa5381f050d453a26b96a91fdc58838e9017e434d82c7a039/dm.reuse-2.1.tar.gz" } ], "2.1.1": [ { "comment_text": "", "digests": { "md5": "75dda9f07d3af939d8126d9a9bd1c0a4", "sha256": "8ddda43eda3f65684242e0b077e887ebf3ba73126885b9b5fc522bd8a788ef47" }, "downloads": -1, "filename": "dm.reuse-2.1.1.tar.gz", "has_sig": false, "md5_digest": "75dda9f07d3af939d8126d9a9bd1c0a4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9708, "upload_time": "2018-12-20T13:00:47", "url": "https://files.pythonhosted.org/packages/0c/c6/dfae9b9985a4d4761fba729ed89d37acdbc5356fd3f17d4bff30fe34daa5/dm.reuse-2.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "75dda9f07d3af939d8126d9a9bd1c0a4", "sha256": "8ddda43eda3f65684242e0b077e887ebf3ba73126885b9b5fc522bd8a788ef47" }, "downloads": -1, "filename": "dm.reuse-2.1.1.tar.gz", "has_sig": false, "md5_digest": "75dda9f07d3af939d8126d9a9bd1c0a4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9708, "upload_time": "2018-12-20T13:00:47", "url": "https://files.pythonhosted.org/packages/0c/c6/dfae9b9985a4d4761fba729ed89d37acdbc5356fd3f17d4bff30fe34daa5/dm.reuse-2.1.1.tar.gz" } ] }