{"id":18456300,"url":"https://github.com/martmists-gh/pymixin","last_synced_at":"2025-09-20T16:31:33.481Z","repository":{"id":106860068,"uuid":"409879901","full_name":"Martmists-GH/pymixin","owner":"Martmists-GH","description":"Python code injection framework","archived":false,"fork":false,"pushed_at":"2021-10-17T10:11:44.000Z","size":20,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-13T14:24:20.754Z","etag":null,"topics":["bytecode","bytecode-manipulation","framework","mixin","mixins","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Martmists-GH.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-24T07:52:44.000Z","updated_at":"2024-11-11T06:14:12.000Z","dependencies_parsed_at":"2023-03-22T01:50:03.577Z","dependency_job_id":null,"html_url":"https://github.com/Martmists-GH/pymixin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martmists-GH%2Fpymixin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martmists-GH%2Fpymixin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martmists-GH%2Fpymixin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Martmists-GH%2Fpymixin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Martmists-GH","download_url":"https://codeload.github.com/Martmists-GH/pymixin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233674874,"owners_count":18712392,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bytecode","bytecode-manipulation","framework","mixin","mixins","python","python3"],"created_at":"2024-11-06T08:11:06.958Z","updated_at":"2025-09-20T16:31:28.175Z","avatar_url":"https://github.com/Martmists-GH.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PyMixin\n\nPyMixin is a python library designed to help inject code in a compatible way.\nIt is heavily inspired by the SpongePowered Mixin project.\n\n## Features\n\n### Redirect function calls\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e from math import log, log10\n\u003e\u003e\u003e \n\u003e\u003e\u003e def test_function(n: int) -\u003e int:\n...     return log(n)\n... \n\u003e\u003e\u003e @redirect(method=test_function, at=At(value=AtValue.INVOKE, target=log))\n... def real_input(n: int) -\u003e int:\n...     # Call log10 instead of log\n...     return log10(n)\n... \n\u003e\u003e\u003e test_function(10)\n1.0\n```\n\n### Change constants\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e from random import randint\n\u003e\u003e\u003e \n\u003e\u003e\u003e def percent(n: int) -\u003e float:\n...     return n / 100\n... \n\u003e\u003e\u003e @modify_const(method=percent, at=At(value=AtValue.LOAD, target=100))\n... def random_denominator():\n...     return randint(0, 100)\n... \n\u003e\u003e\u003e percent(10)\n0.2777777777777778\n\u003e\u003e\u003e percent(10)\n0.35714285714285715\n```\n\n### Inject callbacks\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e \n\u003e\u003e\u003e def internal_message_handler(data: bytes):\n...     return  # Dummy implementation\n... \n\u003e\u003e\u003e @inject(method=internal_message_handler, at=At(value=AtValue.HEAD))\n... def log_message(data: bytes, callback_info: CallbackInfo):\n...     print(\"Received data:\", data)\n... \n\u003e\u003e\u003e internal_message_handler(b\"Hello world\")\nReceived data: b'Hello world'\n```\n\n### Cancel functions\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e \n\u003e\u003e\u003e def process(body: str):\n...     if body == \"Hello\":\n...         print(\"World\")\n...     else:\n...         print(\"Invalid body\")\n... \n\u003e\u003e\u003e @inject(method=process, at=At(value=AtValue.HEAD), cancellable=True)\n... def cancel_if_bad(body: str, callback_info: CallbackInfo):\n...     if body != \"Hello\":\n...         callback_info.cancel()\n... \n\u003e\u003e\u003e process(\"Hello\")\nWorld\n\u003e\u003e\u003e process(\"World\")\n\u003e\u003e\u003e \n```\n\n### Modify returned value\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e \n\u003e\u003e\u003e def return_n_squared(n):\n...     return n * n\n... \n\u003e\u003e\u003e @inject(method=return_n_squared, at=At(value=AtValue.RETURN), cancellable=True)  # Warning: injects at EVERY return by default\n... def return_n_cubed_instead(callback_info: CallbackInfo):\n...     n = (callback_info.return_value**0.5)\n...     callback_info.set_return(n**3)\n... \n\u003e\u003e\u003e return_n_squared(10)\n1000.0\n```\n\n### Overwrite functions\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e \n\u003e\u003e\u003e def spam_a():\n...     while True:\n...         print(\"a\")\n... \n\u003e\u003e\u003e @overwrite(method=spam_a)\n... def replacement():\n...     print(\"b\")\n... \n\u003e\u003e\u003e spam_a()\nb\n```\n\n### A note on decorators\n\nOften in python, a function is wrapped with a decorator. This means the value of a function is no longer the same.\nTo resolve this, we added `mixin.unwrap`, to get the original function back (assuming `functools.wraps`) was used.\n\n```python\n\u003e\u003e\u003e from mixin import *\n\u003e\u003e\u003e from functools import wraps\n\u003e\u003e\u003e \n\u003e\u003e\u003e def with_print(func):\n...     @wraps(func)\n...     def inner(*args, **kwargs):\n...         print(\"args\", args, kwargs)\n...         return func(*args, **kwargs)\n...     return inner\n... \n\u003e\u003e\u003e @with_print\n... def test(n):\n...     return n*2\n... \n\u003e\u003e\u003e @inject(method=unwrap(test), at=At(value=AtValue.HEAD))\n... def log_n(n, callback_info):\n...     print(\"N:\", n)\n... \n\u003e\u003e\u003e test(10)\nargs (10,) {}\nN: 10\n20\n\n```\n\n## Installing\n\nTo install PyMixin, you can just use pip:\n\n```shell\npip install pymixin\n```\n\n## License\n\nPyMixin is licensed under MIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartmists-gh%2Fpymixin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartmists-gh%2Fpymixin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartmists-gh%2Fpymixin/lists"}