|
1 | 1 | # -*- coding: utf-8 -*-
|
2 | 2 | """Provides the variant form decorator."""
|
3 | 3 |
|
4 |
| -import types |
| 4 | +import functools |
5 | 5 |
|
6 | 6 | __all__ = ['variants']
|
7 | 7 |
|
@@ -30,26 +30,60 @@ def myfunc(url):
|
30 | 30 | class VariantFunction:
|
31 | 31 | __doc__ = f.__doc__
|
32 | 32 |
|
| 33 | + def __init__(self): |
| 34 | + self._variants = set() |
| 35 | + |
33 | 36 | def __call__(self, *args, **kwargs):
|
34 | 37 | return f(*args, **kwargs)
|
35 | 38 |
|
| 39 | + def _add_variant(self, var_name, vfunc): |
| 40 | + self._variants.add(var_name) |
| 41 | + setattr(self, var_name, vfunc) |
| 42 | + |
36 | 43 | def variant(self, func_name):
|
37 | 44 | """Decorator to add a new variant form to the function."""
|
38 | 45 | def decorator(vfunc):
|
39 |
| - setattr(self.__class__, func_name, staticmethod(vfunc)) |
| 46 | + self._add_variant(func_name, vfunc) |
| 47 | + |
40 | 48 | return self
|
41 | 49 |
|
42 | 50 | return decorator
|
43 | 51 |
|
44 | 52 | def __get__(self, instance, owner):
|
45 | 53 | # This is necessary to bind instance methods
|
46 |
| - if instance is None: |
47 |
| - return self |
| 54 | + if instance is not None: |
| 55 | + return VariantMethod(self, instance) |
48 | 56 |
|
49 |
| - return types.MethodType(self, instance) |
| 57 | + return self |
50 | 58 |
|
51 | 59 | def __repr__(self):
|
52 |
| - return '<VariantFunction {}>'.format(self.__name__) |
| 60 | + return '<{} {}>'.format(self.__class__.__name__, self.__name__) |
| 61 | + |
| 62 | + class VariantMethod(VariantFunction): |
| 63 | + def __init__(self, variant_func, instance): |
| 64 | + self.__instance = instance |
| 65 | + self.__name__ = variant_func.__name__ |
| 66 | + |
| 67 | + # Convert existing variants to methods |
| 68 | + for vname in variant_func._variants: |
| 69 | + vfunc = getattr(variant_func, vname) |
| 70 | + vmethod = self._as_bound_method(vfunc) |
| 71 | + |
| 72 | + setattr(self, vname, vmethod) |
| 73 | + |
| 74 | + def __call__(self, *args, **kwargs): |
| 75 | + return f(self.__instance, *args, **kwargs) |
| 76 | + |
| 77 | + def _as_bound_method(self, vfunc): |
| 78 | + @functools.wraps(vfunc) |
| 79 | + def bound_method(*args, **kwargs): |
| 80 | + return vfunc(self.__instance, *args, **kwargs) |
| 81 | + |
| 82 | + return bound_method |
| 83 | + |
| 84 | + def _add_variant(self, var_name, vfunc): |
| 85 | + self._variants.add(var_name) |
| 86 | + setattr(self, var_name, self._as_bound_method(vfunc)) |
53 | 87 |
|
54 | 88 | f_out = VariantFunction()
|
55 | 89 | f_out.__name__ = f.__name__
|
|
0 commit comments