Last active
November 6, 2017 16:27
-
-
Save devdave/de18ce1b0d385d83712af8edf109fd5c to your computer and use it in GitHub Desktop.
Messing around with annotations in python3
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
""" | |
Command line output | |
================================= | |
Test with both arguments supplied | |
Handling call | |
Parameter name: always_int | |
Parameter name: always_str | |
Original position arguments: ('123',) | |
Original keyword arguments: {'always_str': 345} | |
Transformed arguments: [123] | |
Transformed keyword arguments: {'always_str': '345'} | |
Completed | |
Test with only one positional argument supplied | |
Handling call | |
Parameter name: always_int | |
Parameter name: always_str | |
Original position arguments: ('123',) | |
Original keyword arguments: {} | |
Transformed arguments: [123] | |
Transformed keyword arguments: {'always_str': None} | |
Completed | |
Test with custom transformer | |
Handling call | |
Parameter name: bizarro | |
Original position arguments: ('Hello World',) | |
Original keyword arguments: {} | |
Transformed arguments: ['dlroW olleH'] | |
Transformed keyword arguments: {} | |
Completed | |
""" | |
from inspect import signature | |
def magic_decorator(func): | |
""" | |
Anytime I put the word "magic" in a identifier, its my way of putting up | |
a red flag that whatever is attached is possible toxic, dangerous, or dangerously toxic. | |
In this case, what magic_decorate does is really nifty but I am confident there is a gotcha in here | |
like the threat of inspect.signature.parameter.annotation changing behavior in the future. | |
That said.... | |
""" | |
#Grab the parameters of the function passed into the decorator | |
params = signature(func).parameters | |
#actual decorator logic | |
def decorator(*args, **kwargs): | |
#constants for noting where a function argument came from, either positional or keyword | |
POSITIONED = 1 | |
KW = 2 | |
new_args = [] | |
new_kwargs = {} | |
#*args is a tuple which is inconveniant | |
arg_list = [x for x in args] | |
print("Handling call") | |
for parameter_name, parameter_signature in params.items(): | |
print(f"\tParameter name: {parameter_name}") | |
#First step is to get the argument values passed in | |
source = None | |
if arg_list: | |
source = POSITIONED | |
original_value = arg_list.pop(0) | |
else: | |
source = KW | |
original_value = kwargs.get(parameter_signature.name, parameter_signature.empty) | |
#Handles cases where a value is not provided for a keyword argument | |
if original_value == parameter_signature.empty: | |
new_value = parameter_signature.default | |
elif parameter_signature.annotation != parameter_signature.empty: | |
#Here is the magic, in vanilla_func and goofy_func, the annotations are int/str/reversed_str | |
# and are provided as is via the inspect.Parameter.annotation property | |
new_value = parameter_signature.annotation(original_value) | |
else: | |
new_value = original_value | |
#Put the transformed (or untouched) arguments back to where they came from | |
if source == POSITIONED: | |
new_args.append(new_value) | |
else: | |
new_kwargs[parameter_signature.name] = new_value | |
print(f"\tOriginal position arguments: {args} \n\tOriginal keyword arguments: {kwargs}") | |
print(f"\tTransformed arguments: {new_args} \n\tTransformed keyword arguments: {new_kwargs}") | |
return_value = func(*new_args, **new_kwargs) | |
print("Completed") | |
return return_value | |
return decorator | |
@magic_decorator | |
def vanilla_func(always_int:int=None, always_str:str=None): | |
return always_int, always_str | |
def reversed_str(raw): | |
if not isinstance(raw, str): | |
raw = str(raw) | |
return raw[::-1] | |
@magic_decorator | |
def goofy_func(bizarro:reversed_str): | |
return bizarro | |
print("Test with both arguments supplied") | |
assert vanilla_func("123", always_str=345) == (123, "345",) | |
print("Test with only one positional argument supplied") | |
assert vanilla_func("123") == (123, None,) | |
print("Test with custom transformer") | |
assert goofy_func("Hello World") == "dlroW olleH" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment