Last active
August 29, 2015 14:07
-
-
Save dcollien/59b437b30b142bdad4dc to your computer and use it in GitHub Desktop.
Automatic Argument Conversion Decorator
This file contains 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
import calendar | |
from datetime import datetime | |
import types | |
import json | |
from functools import wraps | |
def convert_arg(type_name, value): | |
converter = None | |
if not isinstance(type_name, basestring): | |
converter = type_name | |
type_name = type_name.__name__ | |
if isinstance(value, basestring): | |
if type_name == 'list': | |
try: | |
value = json_decode(value) | |
except ValueError: | |
value = [value] | |
elif type_name == 'tuple': | |
value = (value,) | |
elif type_name == 'datetime': | |
try: | |
value = float(value) | |
except ValueError: | |
value = None | |
else: | |
value = datetime.fromtimestamp(value) | |
elif type_name == 'dict': | |
value = json.loads(value) | |
elif type_name == 'int': | |
value = int(value) | |
elif type_name == 'long': | |
value = long(value) | |
elif type_name == 'float': | |
value = float(value) | |
elif type_name == 'bool': | |
value = (value.lower() == 'true') or (value.lower() == 'yes') or (value.lower() == '1') | |
elif converter is not None: | |
value = converter(value) | |
elif isinstance(value, int) or isinstance(value, float): | |
if type_name == 'datetime': | |
value = datetime.fromtimestamp(float(value)) | |
if value is not None and type(value).__name__ != type_name and not converter: | |
raise TypeError("Expecting <type '" + type_name + "'>") | |
return value | |
def arg_types(**params): | |
def _decorator(func): | |
if isinstance(func, staticmethod): | |
func = func.__func__ | |
@wraps(func) | |
def _inner(*args, **kwargs): | |
argSpec = getargspec(func) | |
argNames = argSpec.args | |
numArgs = len(args) | |
newArgs = [] | |
i = 0 | |
for argName in argNames: | |
if i < numArgs: | |
value = args[i] | |
i += 1 | |
else: | |
value = kwargs[argName] | |
if argName in params: | |
try: | |
value = convert_arg(params[argName], value) | |
except TypeError as err: | |
raise TypeError(func.__name__ + ": Argument '" + argName + "' given incorrect type. " + str(err)) | |
except ValueError as err: | |
raise ValueError(func.__name__ + ": Cannot convert " + str(type(value).__name__) + " argument '" + argName + "' to " + str(params[argName]) + '. ' + str(err)) | |
newArgs.append(value) | |
return func(*newArgs) | |
return _inner | |
return _decorator | |
def test_arg_types(): | |
class Dummy(object): | |
def __init__(self, arg): | |
self.arg = arg | |
def __str__(self): | |
return self.arg | |
@arg_types(date=datetime, number=int, isAwesome=bool, dummy=Dummy, things='list', stuff=dict) | |
def testfunc(date, number, isAwesome, dummy=None, things=[], stuff={}): | |
assert type(date) == datetime | |
assert str(date) == '2014-09-08 15:46:49+00:00' | |
assert type(number) == int | |
assert number == 42 | |
assert type(isAwesome) == bool and isAwesome | |
assert isinstance(dummy, Dummy) and str(dummy) == 'test' | |
assert things[0] == 'a thing' | |
assert stuff['foo'] == 'bar' | |
testfunc('1410191209', '42', 'true', 'test', stuff='{"foo": "bar"}', things='a thing') | |
testfunc(1410191209, 42, 'yes', 'test', stuff={"foo": "bar"}, things='["a thing", "of Things"]') | |
print 'All Tests Passed' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment