~ubuntu-branches/ubuntu/utopic/cinder/utopic

« back to all changes in this revision

Viewing changes to cinder/taskflow/decorators.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, James Page, Adam Gandelman, Chuck Short
  • Date: 2013-09-08 21:09:46 UTC
  • mfrom: (1.1.18)
  • Revision ID: package-import@ubuntu.com-20130908210946-3dbzq1jy5uji4wad
Tags: 1:2013.2~b3-0ubuntu1
[ James Page ]
* d/control: Switch ceph-common -> python-ceph inline with upstream
  refactoring of Ceph RBD driver, move to Suggests of python-cinder.
  (LP: #1190791). 

[ Adam Gandelman ]
* debian/patches/avoid_paramiko_vers_depends.patch: Dropped, no longer
  required.
* Add minimum requirement python-greenlet (>= 0.3.2).
* Add minimum requirement python-eventlet (>= 0.12.0).
* Add minimum requirement python-paramiko (>= 1.8).

[ Chuck Short ]
* New upstream release.
* debian/patches/skip-sqlachemy-failures.patch: Skip testfailures
  with sqlalchemy 0.8 until they are fixed upstream.
* debian/control: Add python-babel to build-depends.
* debian/control: Add python-novaclient to build-depends.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
4
 
 
5
#    Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
 
6
#
 
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
8
#    not use this file except in compliance with the License. You may obtain
 
9
#    a copy of the License at
 
10
#
 
11
#         http://www.apache.org/licenses/LICENSE-2.0
 
12
#
 
13
#    Unless required by applicable law or agreed to in writing, software
 
14
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
15
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
16
#    License for the specific language governing permissions and limitations
 
17
#    under the License.
 
18
 
 
19
import collections
 
20
import functools
 
21
import inspect
 
22
import types
 
23
 
 
24
# These arguments are ones that we will skip when parsing for requirements
 
25
# for a function to operate (when used as a task).
 
26
AUTO_ARGS = ('self', 'context', 'cls')
 
27
 
 
28
 
 
29
def is_decorated(functor):
 
30
    if not isinstance(functor, (types.MethodType, types.FunctionType)):
 
31
        return False
 
32
    return getattr(extract(functor), '__task__', False)
 
33
 
 
34
 
 
35
def extract(functor):
 
36
    # Extract the underlying functor if its a method since we can not set
 
37
    # attributes on instance methods, this is supposedly fixed in python 3
 
38
    # and later.
 
39
    #
 
40
    # TODO(harlowja): add link to this fix.
 
41
    assert isinstance(functor, (types.MethodType, types.FunctionType))
 
42
    if isinstance(functor, types.MethodType):
 
43
        return functor.__func__
 
44
    else:
 
45
        return functor
 
46
 
 
47
 
 
48
def _mark_as_task(functor):
 
49
    setattr(functor, '__task__', True)
 
50
 
 
51
 
 
52
def _get_wrapped(function):
 
53
    """Get the method at the bottom of a stack of decorators."""
 
54
 
 
55
    if hasattr(function, '__wrapped__'):
 
56
        return getattr(function, '__wrapped__')
 
57
 
 
58
    if not hasattr(function, 'func_closure') or not function.func_closure:
 
59
        return function
 
60
 
 
61
    def _get_wrapped_function(function):
 
62
        if not hasattr(function, 'func_closure') or not function.func_closure:
 
63
            return None
 
64
 
 
65
        for closure in function.func_closure:
 
66
            func = closure.cell_contents
 
67
 
 
68
            deeper_func = _get_wrapped_function(func)
 
69
            if deeper_func:
 
70
                return deeper_func
 
71
            elif hasattr(closure.cell_contents, '__call__'):
 
72
                return closure.cell_contents
 
73
 
 
74
    return _get_wrapped_function(function)
 
75
 
 
76
 
 
77
def _take_arg(a):
 
78
    if a in AUTO_ARGS:
 
79
        return False
 
80
    # In certain decorator cases it seems like we get the function to be
 
81
    # decorated as an argument, we don't want to take that as a real argument.
 
82
    if isinstance(a, collections.Callable):
 
83
        return False
 
84
    return True
 
85
 
 
86
 
 
87
def wraps(fn):
 
88
    """This will not be needed in python 3.2 or greater which already has this
 
89
    built-in to its functools.wraps method.
 
90
    """
 
91
 
 
92
    def wrapper(f):
 
93
        f = functools.wraps(fn)(f)
 
94
        f.__wrapped__ = getattr(fn, '__wrapped__', fn)
 
95
        return f
 
96
 
 
97
    return wrapper
 
98
 
 
99
 
 
100
def locked(f):
 
101
 
 
102
    @wraps(f)
 
103
    def wrapper(self, *args, **kwargs):
 
104
        with self._lock:
 
105
            return f(self, *args, **kwargs)
 
106
 
 
107
    return wrapper
 
108
 
 
109
 
 
110
def task(*args, **kwargs):
 
111
    """Decorates a given function and ensures that all needed attributes of
 
112
    that function are set so that the function can be used as a task.
 
113
    """
 
114
 
 
115
    def decorator(f):
 
116
        w_f = extract(f)
 
117
 
 
118
        def noop(*args, **kwargs):
 
119
            pass
 
120
 
 
121
        # Mark as being a task
 
122
        _mark_as_task(w_f)
 
123
 
 
124
        # By default don't revert this.
 
125
        w_f.revert = kwargs.pop('revert_with', noop)
 
126
 
 
127
        # Associate a name of this task that is the module + function name.
 
128
        w_f.name = "%s.%s" % (f.__module__, f.__name__)
 
129
 
 
130
        # Sets the version of the task.
 
131
        version = kwargs.pop('version', (1, 0))
 
132
        f = _versionize(*version)(f)
 
133
 
 
134
        # Attach any requirements this function needs for running.
 
135
        requires_what = kwargs.pop('requires', [])
 
136
        f = _requires(*requires_what, **kwargs)(f)
 
137
 
 
138
        # Attach any optional requirements this function needs for running.
 
139
        optional_what = kwargs.pop('optional', [])
 
140
        f = _optional(*optional_what, **kwargs)(f)
 
141
 
 
142
        # Attach any items this function provides as output
 
143
        provides_what = kwargs.pop('provides', [])
 
144
        f = _provides(*provides_what, **kwargs)(f)
 
145
 
 
146
        @wraps(f)
 
147
        def wrapper(*args, **kwargs):
 
148
            return f(*args, **kwargs)
 
149
 
 
150
        return wrapper
 
151
 
 
152
    # This is needed to handle when the decorator has args or the decorator
 
153
    # doesn't have args, python is rather weird here...
 
154
    if kwargs or not args:
 
155
        return decorator
 
156
    else:
 
157
        if isinstance(args[0], collections.Callable):
 
158
            return decorator(args[0])
 
159
        else:
 
160
            return decorator
 
161
 
 
162
 
 
163
def _versionize(major, minor=None):
 
164
    """A decorator that marks the wrapped function with a major & minor version
 
165
    number.
 
166
    """
 
167
 
 
168
    if minor is None:
 
169
        minor = 0
 
170
 
 
171
    def decorator(f):
 
172
        w_f = extract(f)
 
173
        w_f.version = (major, minor)
 
174
 
 
175
        @wraps(f)
 
176
        def wrapper(*args, **kwargs):
 
177
            return f(*args, **kwargs)
 
178
 
 
179
        return wrapper
 
180
 
 
181
    return decorator
 
182
 
 
183
 
 
184
def _optional(*args, **kwargs):
 
185
    """Attaches a set of items that the decorated function would like as input
 
186
    to the functions underlying dictionary.
 
187
    """
 
188
 
 
189
    def decorator(f):
 
190
        w_f = extract(f)
 
191
 
 
192
        if not hasattr(w_f, 'optional'):
 
193
            w_f.optional = set()
 
194
 
 
195
        w_f.optional.update([a for a in args if _take_arg(a)])
 
196
 
 
197
        @wraps(f)
 
198
        def wrapper(*args, **kwargs):
 
199
            return f(*args, **kwargs)
 
200
 
 
201
        return wrapper
 
202
 
 
203
    # This is needed to handle when the decorator has args or the decorator
 
204
    # doesn't have args, python is rather weird here...
 
205
    if kwargs or not args:
 
206
        return decorator
 
207
    else:
 
208
        if isinstance(args[0], collections.Callable):
 
209
            return decorator(args[0])
 
210
        else:
 
211
            return decorator
 
212
 
 
213
 
 
214
def _requires(*args, **kwargs):
 
215
    """Attaches a set of items that the decorated function requires as input
 
216
    to the functions underlying dictionary.
 
217
    """
 
218
 
 
219
    def decorator(f):
 
220
        w_f = extract(f)
 
221
 
 
222
        if not hasattr(w_f, 'requires'):
 
223
            w_f.requires = set()
 
224
 
 
225
        if kwargs.pop('auto_extract', True):
 
226
            inspect_what = _get_wrapped(f)
 
227
            f_args = inspect.getargspec(inspect_what).args
 
228
            w_f.requires.update([a for a in f_args if _take_arg(a)])
 
229
 
 
230
        w_f.requires.update([a for a in args if _take_arg(a)])
 
231
 
 
232
        @wraps(f)
 
233
        def wrapper(*args, **kwargs):
 
234
            return f(*args, **kwargs)
 
235
 
 
236
        return wrapper
 
237
 
 
238
    # This is needed to handle when the decorator has args or the decorator
 
239
    # doesn't have args, python is rather weird here...
 
240
    if kwargs or not args:
 
241
        return decorator
 
242
    else:
 
243
        if isinstance(args[0], collections.Callable):
 
244
            return decorator(args[0])
 
245
        else:
 
246
            return decorator
 
247
 
 
248
 
 
249
def _provides(*args, **kwargs):
 
250
    """Attaches a set of items that the decorated function provides as output
 
251
    to the functions underlying dictionary.
 
252
    """
 
253
 
 
254
    def decorator(f):
 
255
        w_f = extract(f)
 
256
 
 
257
        if not hasattr(f, 'provides'):
 
258
            w_f.provides = set()
 
259
 
 
260
        w_f.provides.update([a for a in args if _take_arg(a)])
 
261
 
 
262
        @wraps(f)
 
263
        def wrapper(*args, **kwargs):
 
264
            return f(*args, **kwargs)
 
265
 
 
266
        return wrapper
 
267
 
 
268
    # This is needed to handle when the decorator has args or the decorator
 
269
    # doesn't have args, python is rather weird here...
 
270
    if kwargs or not args:
 
271
        return decorator
 
272
    else:
 
273
        if isinstance(args[0], collections.Callable):
 
274
            return decorator(args[0])
 
275
        else:
 
276
            return decorator