Last active
April 20, 2022 02:12
-
-
Save lucaswiman/4bbe3aab911f0bade43eb9fa6a7a0854 to your computer and use it in GitHub Desktop.
Using Wrapt to alter a method signature
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"id": "98e1a19f", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Collecting wrapt\n", | |
" Using cached wrapt-1.14.0-cp39-cp39-macosx_10_9_x86_64.whl (35 kB)\n", | |
"Installing collected packages: wrapt\n", | |
"Successfully installed wrapt-1.14.0\n", | |
"\u001b[33mWARNING: You are using pip version 21.2.4; however, version 22.0.4 is available.\n", | |
"You should consider upgrading via the '/Users/lucaswiman/personal/private-stuff/work/20220402--etl-blog-post/.venv/bin/python -m pip install --upgrade pip' command.\u001b[0m\n" | |
] | |
} | |
], | |
"source": [ | |
"!pip install wrapt" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"id": "881d2fc1", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import wrapt\n", | |
"import uuid\n", | |
"import inspect\n", | |
"from typing import *\n", | |
"\n", | |
"def get_argspec_including_annotations(\n", | |
" func: Callable,\n", | |
" added_annotations: Dict[str, type],\n", | |
" removed_args: Sequence[str] = (),\n", | |
" validate=False,\n", | |
") -> inspect.FullArgSpec:\n", | |
" signature = inspect.signature(func)\n", | |
" spec = inspect.getfullargspec(func)._asdict()\n", | |
" for name, annotation in added_annotations.items():\n", | |
" if name not in signature.parameters:\n", | |
" spec[\"kwonlyargs\"].append(name)\n", | |
" spec[\"annotations\"][name] = annotation\n", | |
" for name in removed_args:\n", | |
" if name in signature.parameters:\n", | |
" if name in spec[\"args\"]:\n", | |
" spec[\"args\"].remove(name)\n", | |
" if name in spec[\"kwonlyargs\"]:\n", | |
" spec[\"kwonlyargs\"].remove(name)\n", | |
" spec[\"annotations\"].pop(name, None)\n", | |
"\n", | |
" return inspect.FullArgSpec(**spec)\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"id": "76a5f833", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_fastapi_method_argspec(func: Callable) -> inspect.ArgSpec:\n", | |
" return get_argspec_including_annotations(func, {\"client_token\": uuid.UUID}, [\"job_context\"])\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 30, | |
"id": "39d7e9a3", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"@wrapt.decorator(adapter=wrapt.adapter_factory(get_fastapi_method_argspec))\n", | |
"def munge_types(wrapped, instance, args, kwargs):\n", | |
" client_token = kwargs.pop(\"client_token\", None)\n", | |
" if not client_token:\n", | |
" client_token = uuid.uuid4()\n", | |
" job_context = {\"client_token\": client_token}\n", | |
" signature = inspect.signature(wrapped)\n", | |
" bound = signature.bind(*args, **kwargs)\n", | |
" bound.apply_defaults()\n", | |
" \n", | |
" # Note: job_context can be positional or kwarg and it will do the right thing\n", | |
" bound.arguments[\"job_context\"] = job_context\n", | |
" print(f\"calling with {bound.args=}, {bound.kwargs=}\")\n", | |
" return wrapped(*bound.args, **bound.kwargs)\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 31, | |
"id": "c10658d6", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"@munge_types\n", | |
"def add(\n", | |
" x: int,\n", | |
" *,\n", | |
" y: int = 1,\n", | |
" job_context: Optional[\"JobContext\"] = None,\n", | |
") -> int:\n", | |
" print(f\"got {job_context=} for {x=}\")\n", | |
" return x + 1" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 32, | |
"id": "47a9e0f0", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"calling with bound.args=(1,), bound.kwargs={'y': 2, 'job_context': {'client_token': UUID('2f2a15e5-9b9d-4b2e-a434-46d9c3e2e9ae')}}\n", | |
"got job_context={'client_token': UUID('2f2a15e5-9b9d-4b2e-a434-46d9c3e2e9ae')} for x=1\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"2" | |
] | |
}, | |
"execution_count": 32, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add(1, y=2)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 33, | |
"id": "4959bf47", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"calling with bound.args=(1,), bound.kwargs={'y': 1, 'job_context': {'client_token': UUID('84476126-2c91-4f31-a6c0-b5924816d172')}}\n", | |
"got job_context={'client_token': UUID('84476126-2c91-4f31-a6c0-b5924816d172')} for x=1\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"2" | |
] | |
}, | |
"execution_count": 33, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add(1)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 35, | |
"id": "f8f5b4d4", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"{'x': int,\n", | |
" 'y': int,\n", | |
" 'job_context': typing.Optional[ForwardRef('JobContext')],\n", | |
" 'return': int}" | |
] | |
}, | |
"execution_count": 35, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"add.__annotations__" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 36, | |
"id": "4f3786f7", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=None, kwonlyargs=['y', 'client_token'], kwonlydefaults={'y': 1}, annotations={'return': <class 'int'>, 'x': <class 'int'>, 'y': <class 'int'>, 'client_token': <class 'uuid.UUID'>})" | |
] | |
}, | |
"execution_count": 36, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"inspect.getfullargspec(add)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "f56442cb", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.9.1" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment