-
Notifications
You must be signed in to change notification settings - Fork 1
/
functions.py
139 lines (107 loc) · 3.95 KB
/
functions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""Utilities for working with functions"""
import sys
import weakref
from types import MethodType
from functools import partial
from operator import methodcaller
class Bindable(object):
def __get__(self, obj, cls=None):
if obj is None:
return self
return MethodType(self, obj)
class Function(Bindable):
def __init__(self, name=None):
# By default, name the function after its class
self.__name__ = name or type(self).__name__
class WrapperFunction(Function):
from functools import (update_wrapper, WRAPPER_ASSIGNMENTS)
def __init__(self, wrapped, assigned=WRAPPER_ASSIGNMENTS, *args, **kw):
self.update_wrapper(wrapped, assigned, *args, **kw)
# Python 2 does not add this, and Python 3 overwrites it with
# wrapped.__wrapped__ when updating self.__dict__
self.__wrapped__ = wrapped
# Python 2 cannot assign these unless they are guaranteed to exist
for name in {"__defaults__", "__code__"}.difference(assigned):
try:
value = getattr(wrapped, name)
except AttributeError:
continue
setattr(self, name, value)
try:
self.__kwdefaults__ = wrapped.__kwdefaults__
except AttributeError:
pass
def __get__(self, *pos, **kw):
binding = self.__wrapped__.__get__(*pos, **kw)
# Avoid Python 2's unbound methods, with __self__ = None
if binding is self.__wrapped__ or binding.__self__ is None:
return self
else:
return type(binding)(self, binding.__self__)
class decorator(WrapperFunction):
"""Decorator to help create other decorators
This can be used to create simple decorators that accept arguments:
@decorator
def arg_decorator(func, param): return implementation(func, param)
@arg_decorator("arg")
def func(): ...
is equivalent to
def func(): ...
func = implementation(func, "arg")
This can also be used to create wrapper decorators:
@decorator
def wrapper(func, *args): return implementation(func, *args)
class C:
@wrapper
def method(self, param): ...
C().method("arg")
is equivalent to
implementation(C.method, C(), "arg")"""
def __call__(self, *args, **kw):
return BindingPartial(self.__wrapped__, *args, **kw)
class BindingPartial(partial, Bindable):
pass
class weakmethod(object):
"""Decorator wrapper for methods that binds to objects using a weak
reference"""
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
if obj is None:
return self
return WeakBinding(self.func, obj)
class WeakBinding(object):
def __init__(self, func, obj):
self.__func__ = func
self.ref = weakref.ref(obj)
@property
def __self__(self):
obj = self.ref()
if obj is None:
raise ReferenceError("weakly bound instance to method {0!r} "
"no longer exists".format(self.__func__))
return obj
def __call__(self, *args, **kw):
obj = self.__self__
return self.__func__.__get__(obj, type(obj))(*args, **kw)
def __repr__(self):
return "<{0} of {1} to {2}>".format(
type(self).__name__, self.__func__, self.ref())
@decorator
def attributes(f, *args, **kw):
"""Decorator to add arbitrary attributes to a function object
Example:
@attributes(key="value")
def function(): ...
"""
f.__dict__.update(*args, **kw)
return f
def dummy(*args, **kw):
pass
def setitem(dict, key):
"""Decorator that adds the definition to a dictionary with a given key"""
def decorator(define):
dict[key] = define
return define
return decorator
instantiated = methodcaller("__call__")