##// END OF EJS Templates
fix Python2 infinite recursion on __name__
MinRK -
Show More
@@ -1,230 +1,230 b''
1 1 """Dependency utilities
2 2
3 3 Authors:
4 4
5 5 * Min RK
6 6 """
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2013 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 from types import ModuleType
15 15
16 16 from IPython.parallel.client.asyncresult import AsyncResult
17 17 from IPython.parallel.error import UnmetDependency
18 18 from IPython.parallel.util import interactive
19 19 from IPython.utils import py3compat
20 20 from IPython.utils.py3compat import string_types
21 21 from IPython.utils.pickleutil import can, uncan
22 22
23 23 class depend(object):
24 24 """Dependency decorator, for use with tasks.
25 25
26 26 `@depend` lets you define a function for engine dependencies
27 27 just like you use `apply` for tasks.
28 28
29 29
30 30 Examples
31 31 --------
32 32 ::
33 33
34 34 @depend(df, a,b, c=5)
35 35 def f(m,n,p)
36 36
37 37 view.apply(f, 1,2,3)
38 38
39 39 will call df(a,b,c=5) on the engine, and if it returns False or
40 40 raises an UnmetDependency error, then the task will not be run
41 41 and another engine will be tried.
42 42 """
43 43 def __init__(self, f, *args, **kwargs):
44 44 self.f = f
45 45 self.args = args
46 46 self.kwargs = kwargs
47 47
48 48 def __call__(self, f):
49 49 return dependent(f, self.f, *self.args, **self.kwargs)
50 50
51 51 class dependent(object):
52 52 """A function that depends on another function.
53 53 This is an object to prevent the closure used
54 54 in traditional decorators, which are not picklable.
55 55 """
56 56
57 57 def __init__(self, f, df, *dargs, **dkwargs):
58 58 self.f = f
59 59 name = getattr(f, '__name__', 'f')
60 60 if py3compat.PY3:
61 61 self.__name__ = name
62 62 else:
63 63 self.func_name = name
64 64 self.df = df
65 65 self.dargs = dargs
66 66 self.dkwargs = dkwargs
67 67
68 68 def check_dependency(self):
69 69 if self.df(*self.dargs, **self.dkwargs) is False:
70 70 raise UnmetDependency()
71 71
72 72 def __call__(self, *args, **kwargs):
73 73 return self.f(*args, **kwargs)
74 74
75 75 if not py3compat.PY3:
76 76 @property
77 77 def __name__(self):
78 return self.__name__
78 return self.func_name
79 79
80 80 @interactive
81 81 def _require(*modules, **mapping):
82 82 """Helper for @require decorator."""
83 83 from IPython.parallel.error import UnmetDependency
84 84 from IPython.utils.pickleutil import uncan
85 85 user_ns = globals()
86 86 for name in modules:
87 87 try:
88 88 exec('import %s' % name, user_ns)
89 89 except ImportError:
90 90 raise UnmetDependency(name)
91 91
92 92 for name, cobj in mapping.items():
93 93 user_ns[name] = uncan(cobj, user_ns)
94 94 return True
95 95
96 96 def require(*objects, **mapping):
97 97 """Simple decorator for requiring local objects and modules to be available
98 98 when the decorated function is called on the engine.
99 99
100 100 Modules specified by name or passed directly will be imported
101 101 prior to calling the decorated function.
102 102
103 103 Objects other than modules will be pushed as a part of the task.
104 104 Functions can be passed positionally,
105 105 and will be pushed to the engine with their __name__.
106 106 Other objects can be passed by keyword arg.
107 107
108 108 Examples
109 109 --------
110 110
111 111 In [1]: @require('numpy')
112 112 ...: def norm(a):
113 113 ...: return numpy.linalg.norm(a,2)
114 114
115 115 In [2]: foo = lambda x: x*x
116 116 In [3]: @require(foo)
117 117 ...: def bar(a):
118 118 ...: return foo(1-a)
119 119 """
120 120 names = []
121 121 for obj in objects:
122 122 if isinstance(obj, ModuleType):
123 123 obj = obj.__name__
124 124
125 125 if isinstance(obj, string_types):
126 126 names.append(obj)
127 127 elif hasattr(obj, '__name__'):
128 128 mapping[obj.__name__] = obj
129 129 else:
130 130 raise TypeError("Objects other than modules and functions "
131 131 "must be passed by kwarg, but got: %s" % type(obj)
132 132 )
133 133
134 134 for name, obj in mapping.items():
135 135 mapping[name] = can(obj)
136 136 return depend(_require, *names, **mapping)
137 137
138 138 class Dependency(set):
139 139 """An object for representing a set of msg_id dependencies.
140 140
141 141 Subclassed from set().
142 142
143 143 Parameters
144 144 ----------
145 145 dependencies: list/set of msg_ids or AsyncResult objects or output of Dependency.as_dict()
146 146 The msg_ids to depend on
147 147 all : bool [default True]
148 148 Whether the dependency should be considered met when *all* depending tasks have completed
149 149 or only when *any* have been completed.
150 150 success : bool [default True]
151 151 Whether to consider successes as fulfilling dependencies.
152 152 failure : bool [default False]
153 153 Whether to consider failures as fulfilling dependencies.
154 154
155 155 If `all=success=True` and `failure=False`, then the task will fail with an ImpossibleDependency
156 156 as soon as the first depended-upon task fails.
157 157 """
158 158
159 159 all=True
160 160 success=True
161 161 failure=True
162 162
163 163 def __init__(self, dependencies=[], all=True, success=True, failure=False):
164 164 if isinstance(dependencies, dict):
165 165 # load from dict
166 166 all = dependencies.get('all', True)
167 167 success = dependencies.get('success', success)
168 168 failure = dependencies.get('failure', failure)
169 169 dependencies = dependencies.get('dependencies', [])
170 170 ids = []
171 171
172 172 # extract ids from various sources:
173 173 if isinstance(dependencies, string_types + (AsyncResult,)):
174 174 dependencies = [dependencies]
175 175 for d in dependencies:
176 176 if isinstance(d, string_types):
177 177 ids.append(d)
178 178 elif isinstance(d, AsyncResult):
179 179 ids.extend(d.msg_ids)
180 180 else:
181 181 raise TypeError("invalid dependency type: %r"%type(d))
182 182
183 183 set.__init__(self, ids)
184 184 self.all = all
185 185 if not (success or failure):
186 186 raise ValueError("Must depend on at least one of successes or failures!")
187 187 self.success=success
188 188 self.failure = failure
189 189
190 190 def check(self, completed, failed=None):
191 191 """check whether our dependencies have been met."""
192 192 if len(self) == 0:
193 193 return True
194 194 against = set()
195 195 if self.success:
196 196 against = completed
197 197 if failed is not None and self.failure:
198 198 against = against.union(failed)
199 199 if self.all:
200 200 return self.issubset(against)
201 201 else:
202 202 return not self.isdisjoint(against)
203 203
204 204 def unreachable(self, completed, failed=None):
205 205 """return whether this dependency has become impossible."""
206 206 if len(self) == 0:
207 207 return False
208 208 against = set()
209 209 if not self.success:
210 210 against = completed
211 211 if failed is not None and not self.failure:
212 212 against = against.union(failed)
213 213 if self.all:
214 214 return not self.isdisjoint(against)
215 215 else:
216 216 return self.issubset(against)
217 217
218 218
219 219 def as_dict(self):
220 220 """Represent this dependency as a dict. For json compatibility."""
221 221 return dict(
222 222 dependencies=list(self),
223 223 all=self.all,
224 224 success=self.success,
225 225 failure=self.failure
226 226 )
227 227
228 228
229 229 __all__ = ['depend', 'require', 'dependent', 'Dependency']
230 230
General Comments 0
You need to be logged in to leave comments. Login now