{ "info": { "author": "Alexander Sosedkin", "author_email": "monk@unboiled.info", "bugtrack_url": null, "classifiers": [], "description": "hacks, a Python plugin library that doesn't play by the rules\n=============================================================\n\nIn one sentence\n---------------\n`hacks` aims to be a plugin library bringing aspect-oriented approach \nto Python programs.\n\n\nAbout\n-----\nWhat is `hacks` about? Oh, let me see...\n\n`hacks` is about shooting a complete stranger's leg from around the corner.\nStealthily. Modularly. Painfully.\n\n`hacks` is about patching objects that you're allowed to patch,\nand then patching everything else.\n\n`hacks` is about hooking into other code from afar.\n\n`hacks` is about modifying and extending functions\nwithout even having a reference to them.\n\n`hacks` is about extending classes you'll never see\nand existing instances of them that you'll never meet.\nAs you see fit. With null sweat. And that feeling of impending doom.\n\nAnd `hacks` is about cleaning up all that mess with a single dedent.\n\n\nUsage\n-----\n```python\nimport hacks\n\n# Hacks is usable as a simple call dispatcher:\n\n\ndef main_code():\n N = 2\n for i in range(N):\n # call everyone hooked into 'explicit_call'\n hacks.call.explicit_call(i)\n# ...\n\n\n@hacks.into('explicit_call') # knows nothing about main_code\ndef plugin1(i):\n print(i)\n\n\nclass Plugin2:\n\n @hacks.into('explicit_call')\n @hacks.stealing # that's a pity that they forgot to expose N\n def method(self, i, N=hacks.steal):\n print(i, 'of', N)\n\n# ...\nwith hacks.use(plugin1, Plugin2):\n main_code() # prints '0', '0 of 2', '1', '1 of 2'\nmain_code() # print nothing\n\n\n# Hacks can be used to modify @hacks.friendly objects, e.g. wrap functions:\n\n@hacks.friendly # name not specified, pass 'greeter' to @hacks.around\ndef greeter():\n return 'Hello!'\n# ...\n\n\n@hacks.around('greeter')\ndef reverse(func):\n def func_reversed():\n return func()[::-1]\n return func_reversed\n\n\n# ...\nwith hacks.use(reverse): # reverses all 'printer's\n print(greeter()) # Prints '!olleH'\nprint(greeter()) # Prints 'Hello!'\n\n\n# There is special support for extending classes and mutating existing\n# instances to use the updated versions transparently:\n\n@hacks.friendly_class # name not specified, pass 'Clock' to @hacks.up\nclass Clock:\n def __init__(self, text):\n self._text = text\n def tick(self):\n return self._text\n\n\n# ...\n@hacks.up('Clock')\ndef tockify(clock_class):\n class TockingClock:\n def tick(self):\n return self._text + '-tock'\n return TockingClock\n\n# ...\nticker = Clock('tick')\nprint(ticker.tick()) # prints 'tick'\n\nwith hacks.use(tockify): # makes all 'clock's tock\n print(ticker.tick()) # prints 'tick-tock'\n # Yes, ticker has transparently changed its class to TockingClock.\n\nprint(ticker.tick()) # prints 'tick'\n# And now it changed back to a plain old Clock!\n```\n\nThere's more: initializers, deinitializers, modifying custom objects and\nmaking non-cooperating objects cooperate.\n\nBut the most great feature is hacks.use, enabling and disabling hacks\ngracefully without altering their behaviour outside that block or thread.\nIt can be nested, it can be overridden, it poisons the call stack\nand it cleans up after itself at the end of the block. Ain't that nice?\n\nPlease see `tests` directory for more powerful usage examples.\n\n\nMeh, extending code with `hacks` is too explicit!\n-------------------------------------------------\nAh, the object of your interest is not decorated?\nNo problems, monkeypatch it with @hacks_friendly:\n```python\nsys.stdin = hacks_friendly(sys.stdin)\n```\nThat alone should have near-zero visible effect.\nNow change, wrap and extend it however you want with `hacks.use(...)`\nwithout worrying about other threads or cleaning up after yourself.\n\n\nMeh, extending code with `hacks` is too implicit!\n-------------------------------------------------\nWhen you decorate something with `@hacks.friendly`/`friendly_class`,\nyou kind of set it free from all contracts.\nYou never know what it will be on next access.\nBut hey, any decorator is a custom modification, what did you expect?\n\nBy the way, that burden of responsibility didn't simply vanish,\nit just spread onto the person who `@hacks.up` and `around` your code.\n\nIf you're opposed to `stealing`, simply don't `steal`.\n\n\nAnd how exactly is that a plugin system?\n----------------------------------------\nIt's usable as one.\n\nWrite some code.\nDecorate it with `@hacks.friendly` and `@hacks.friendly_class`\npaired with nice names.\nAdd explicit calls to plugins with `hacks.call` where appropriate.\n\nThink of a new aspect, like logging.\nWrite the logging code that `@hacks.into` explicit plugin calls.\nWrap around entire functions to add more logging.\nModify classes if you want to.\n\nThen execute your code `with hacks.use(logging):`.\nEnjoy your pluggable, replaceable, separated logging\nwithout polluting the original code too much.\n\nWrite more powerful and game-changing modifications\nwithout touching the main code.\nStack, nest and apply multiple hacks... um, plugins.\nDefine their 'scopes' flexibly.\nDon't break original object's contracts without a good reason.\n\nWhat else do you need from a plugin system?\n\nSetuptools integration for plugins autodiscovery, right.\nIt is planned for future releases though. Pull requests are welcome!\n\n\nSo what is the plugin interface? Plugins need a rigid interface!\n----------------------------------------------------------------\nNot in Pythonland.\n\nIf you disagree with me, you must be an interesting and strange person\nthat should probably have a look at https://github.com/pytest-dev/pluggy\n\n\nChoosing names requires forces you to design an interface!\n----------------------------------------------------------\nReally? How unbearable.\nJust hook all hacks to `'x'`, my true anarchist.\nGood luck differentiating the objects though!\n\n\nInstallation (approximate)\n--------------------------\nFrom pypi:\n\n pip3 install hacks\n\nFrom Github:\n\n pip3 install git+https://github.com/t184256/hacks.git\n\nFrom local source:\n\n pip3 install -r requirements.txt\n python3 setup.py install\n\n\nMore\n----\n`hacks` gave life and purpose to a\ngeneric object proxying library called `mutants`.\nCheck it out: https://github.com/t184256/mutants\n\n\nLicense\n-------\n`hacks` is distributed under the terms of the MIT License;\nsee [LICENSE.txt](LICENSE.txt).", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/t184256/hacks", "keywords": "plugins", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "hacks", "package_url": "https://pypi.org/project/hacks/", "platform": null, "project_url": "https://pypi.org/project/hacks/", "project_urls": { "Homepage": "https://github.com/t184256/hacks" }, "release_url": "https://pypi.org/project/hacks/0.0.12/", "requires_dist": null, "requires_python": "", "summary": "hacks, a Python plugin library that doesn't play by the rules", "version": "0.0.12" }, "last_serial": 2576917, "releases": { "0.0.0": [], "0.0.1": [ { "comment_text": "", "digests": { "md5": "1656465c81117bfabb0430dbe40e4d0b", "sha256": "de04ae89e3f6f519e68d2799fda8c7ddaff77148670614dbe601b17f7da47fa8" }, "downloads": -1, "filename": "hacks-0.0.1.tar.gz", "has_sig": false, "md5_digest": "1656465c81117bfabb0430dbe40e4d0b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6928, "upload_time": "2016-09-14T08:52:00", "url": "https://files.pythonhosted.org/packages/57/86/9640a5aa21077cd40f54326cbf2c8d44ee4723ddb242e0502247262135bb/hacks-0.0.1.tar.gz" } ], "0.0.1.dev2": [ { "comment_text": "", "digests": { "md5": "820f38723741e97353d4a0ddedba4596", "sha256": "1138545684b28025a8a03dbb3304c1236f5f00df2626a6688094b100394464f6" }, "downloads": -1, "filename": "hacks-0.0.1.dev2.tar.gz", "has_sig": false, "md5_digest": "820f38723741e97353d4a0ddedba4596", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6930, "upload_time": "2016-09-13T12:42:49", "url": "https://files.pythonhosted.org/packages/d5/6a/549c0e6a43abbddb4ded175cf072bf40f1f8f39ac73b53acc590563c2b9b/hacks-0.0.1.dev2.tar.gz" } ], "0.0.1.dev7": [ { "comment_text": "", "digests": { "md5": "13583b7803c2475461efa7fa8064db76", "sha256": "b4edb87431ed749309d30762d02c3a35f3f1841251b7d68b3ae24e559cd22357" }, "downloads": -1, "filename": "hacks-0.0.1.dev7.tar.gz", "has_sig": false, "md5_digest": "13583b7803c2475461efa7fa8064db76", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7767, "upload_time": "2016-09-14T08:01:16", "url": "https://files.pythonhosted.org/packages/0f/60/ce542e1c78329a815670c53390a798f0f77edf19eeb4cdef5cf1ac2574ab/hacks-0.0.1.dev7.tar.gz" } ], "0.0.10": [ { "comment_text": "", "digests": { "md5": "9176d2d65b92933e380dd6cd132362a8", "sha256": "15dfb7d16a34edc48860a0e1b5c2562d16aa59f7ad0651d456664f79d48af0a7" }, "downloads": -1, "filename": "hacks-0.0.10.tar.gz", "has_sig": false, "md5_digest": "9176d2d65b92933e380dd6cd132362a8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16646, "upload_time": "2016-09-30T11:18:46", "url": "https://files.pythonhosted.org/packages/d3/69/cdec2183ee4f0b8465075ed86370c23e92bd2252883198e78030db3a2910/hacks-0.0.10.tar.gz" } ], "0.0.11": [ { "comment_text": "", "digests": { "md5": "1cd6275a9cefec9c82717f81f9600502", "sha256": "3244d5ec2d4de04e5cc7ab9c0cd03d9629e9493f5fd0b890a9d110488252a0e1" }, "downloads": -1, "filename": "hacks-0.0.11.tar.gz", "has_sig": false, "md5_digest": "1cd6275a9cefec9c82717f81f9600502", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17522, "upload_time": "2016-10-04T04:45:26", "url": "https://files.pythonhosted.org/packages/37/2c/f44983e7a838e009ae73e15bc01961b3d0c5b04df00327601538e99a6636/hacks-0.0.11.tar.gz" } ], "0.0.12": [ { "comment_text": "", "digests": { "md5": "9268fc7fe709373fc5efdb752e5af9f0", "sha256": "dee1bf71376ccf216346dbdc4f5d5c0f1e9a4093de22454069ad46a5bf9c75f7" }, "downloads": -1, "filename": "hacks-0.0.12.tar.gz", "has_sig": false, "md5_digest": "9268fc7fe709373fc5efdb752e5af9f0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18062, "upload_time": "2017-01-16T10:40:25", "url": "https://files.pythonhosted.org/packages/4a/a0/9a1ec841b0507de8ce0431c7774b4f419031e9d8b4e772fbea9e44b65637/hacks-0.0.12.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "0f84007dd76a0fb4a4326101e6847bf8", "sha256": "e1d895568a39691e47247bac7092ee637a1ea980c9e3e36fffb4674f11bf3218" }, "downloads": -1, "filename": "hacks-0.0.2.tar.gz", "has_sig": false, "md5_digest": "0f84007dd76a0fb4a4326101e6847bf8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7859, "upload_time": "2016-09-14T08:53:19", "url": "https://files.pythonhosted.org/packages/fc/a9/eb9d9cf6f234b9e521a0ef3d892cae277884fafe878e75d77fdd235ba0bc/hacks-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "5a0bd4b585a8805e4ebbc4ab95ffec9e", "sha256": "ff7ea8817b72262d9863dc7dd18b1f3ec5498e95aab0081b9391c030f21c4050" }, "downloads": -1, "filename": "hacks-0.0.3.tar.gz", "has_sig": false, "md5_digest": "5a0bd4b585a8805e4ebbc4ab95ffec9e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9304, "upload_time": "2016-09-15T13:06:30", "url": "https://files.pythonhosted.org/packages/da/48/631d21ee2374d9a72e19f52388fd5cf78a88478a1083e57dbbf33c62f58b/hacks-0.0.3.tar.gz" } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "6421cf6fccfd0cb5b54b2fb9ec596338", "sha256": "774604d0122804e674ea26cd907e786e0fac844138ae2a1da9a6681781957c80" }, "downloads": -1, "filename": "hacks-0.0.4.tar.gz", "has_sig": false, "md5_digest": "6421cf6fccfd0cb5b54b2fb9ec596338", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9701, "upload_time": "2016-09-19T12:06:01", "url": "https://files.pythonhosted.org/packages/ae/4b/4c0244ffc87cdf3a58ac16f1134fafd7a72951e142a374df5a66434a6d0f/hacks-0.0.4.tar.gz" } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "74f0288ca957e5e4deb2534acdf90519", "sha256": "5dd2386f376a3d478d667a727ddfadf7f4e36aea83d2896e2252e38c57b5029a" }, "downloads": -1, "filename": "hacks-0.0.5.tar.gz", "has_sig": false, "md5_digest": "74f0288ca957e5e4deb2534acdf90519", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10082, "upload_time": "2016-09-20T19:01:45", "url": "https://files.pythonhosted.org/packages/04/46/f5e944a1a96e8d3a0c48047867081a92567f15fd922422ad745e1999def0/hacks-0.0.5.tar.gz" } ], "0.0.6": [ { "comment_text": "", "digests": { "md5": "0a6f9e92eb7ad6cc2225e8a414ac5eab", "sha256": "1c8381be2904755392f66f7029f15f79857bec56ed32f2068a5a9b94a6833ead" }, "downloads": -1, "filename": "hacks-0.0.6.tar.gz", "has_sig": false, "md5_digest": "0a6f9e92eb7ad6cc2225e8a414ac5eab", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15310, "upload_time": "2016-09-26T12:38:05", "url": "https://files.pythonhosted.org/packages/10/12/ab3acf2f11c8f8ca6af1e251b924c01a68c75684ca4ef072360360298cd9/hacks-0.0.6.tar.gz" } ], "0.0.7": [ { "comment_text": "", "digests": { "md5": "0ad9b15b0cdd8678f7bc251f62ad04b4", "sha256": "bfe63edd6c70c162955e222221335e471336f2b792dc18b17131c152a4ffe070" }, "downloads": -1, "filename": "hacks-0.0.7.tar.gz", "has_sig": false, "md5_digest": "0ad9b15b0cdd8678f7bc251f62ad04b4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15870, "upload_time": "2016-09-30T08:50:19", "url": "https://files.pythonhosted.org/packages/66/19/34dcf6725658298ef3678b57f6f8f7420e0c99b68e0cbd78c8ca3d8aa9fe/hacks-0.0.7.tar.gz" } ], "0.0.8": [ { "comment_text": "", "digests": { "md5": "e97a21cfae419f07d041bdc59a56bf24", "sha256": "671014999d7a7bc1fc349b07ebc2b7f65d37837271098b787cf42ec660a3ef25" }, "downloads": -1, "filename": "hacks-0.0.8.tar.gz", "has_sig": false, "md5_digest": "e97a21cfae419f07d041bdc59a56bf24", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16470, "upload_time": "2016-09-30T10:52:43", "url": "https://files.pythonhosted.org/packages/e7/d8/384000f63f937d3ddc189b6448d5e10fa08a9abc82a9f9cfc16ebfe73d28/hacks-0.0.8.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9268fc7fe709373fc5efdb752e5af9f0", "sha256": "dee1bf71376ccf216346dbdc4f5d5c0f1e9a4093de22454069ad46a5bf9c75f7" }, "downloads": -1, "filename": "hacks-0.0.12.tar.gz", "has_sig": false, "md5_digest": "9268fc7fe709373fc5efdb752e5af9f0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18062, "upload_time": "2017-01-16T10:40:25", "url": "https://files.pythonhosted.org/packages/4a/a0/9a1ec841b0507de8ce0431c7774b4f419031e9d8b4e772fbea9e44b65637/hacks-0.0.12.tar.gz" } ] }