Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / wrapt / decorators.py @ 745

History | View | Annotate | Download (19.7 KB)

1
"""This module implements decorators for implementing other decorators
2
as well as some commonly used decorators.
3

4
"""
5

    
6
import sys
7

    
8
PY2 = sys.version_info[0] == 2
9
PY3 = sys.version_info[0] == 3
10

    
11
if PY3:
12
    string_types = str,
13

    
14
    import builtins
15
    exec_ = getattr(builtins, "exec")
16
    del builtins
17

    
18
else:
19
    string_types = basestring,
20

    
21
    def exec_(_code_, _globs_=None, _locs_=None):
22
        """Execute code in a namespace."""
23
        if _globs_ is None:
24
            frame = sys._getframe(1)
25
            _globs_ = frame.f_globals
26
            if _locs_ is None:
27
                _locs_ = frame.f_locals
28
            del frame
29
        elif _locs_ is None:
30
            _locs_ = _globs_
31
        exec("""exec _code_ in _globs_, _locs_""")
32

    
33
from functools import partial
34
from inspect import getargspec, ismethod, isclass, formatargspec
35
from collections import namedtuple
36
from threading import Lock, RLock
37

    
38
try:
39
    from inspect import signature
40
except ImportError:
41
    pass
42

    
43
from .wrappers import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy,
44
    CallableObjectProxy)
45

    
46
# Adapter wrapper for the wrapped function which will overlay certain
47
# properties from the adapter function onto the wrapped function so that
48
# functions such as inspect.getargspec(), inspect.getfullargspec(),
49
# inspect.signature() and inspect.getsource() return the correct results
50
# one would expect.
51

    
52
class _AdapterFunctionCode(CallableObjectProxy):
53

    
54
    def __init__(self, wrapped_code, adapter_code):
55
        super(_AdapterFunctionCode, self).__init__(wrapped_code)
56
        self._self_adapter_code = adapter_code
57

    
58
    @property
59
    def co_argcount(self):
60
        return self._self_adapter_code.co_argcount
61

    
62
    @property
63
    def co_code(self):
64
        return self._self_adapter_code.co_code
65

    
66
    @property
67
    def co_flags(self):
68
        return self._self_adapter_code.co_flags
69

    
70
    @property
71
    def co_kwonlyargcount(self):
72
        return self._self_adapter_code.co_kwonlyargcount
73

    
74
    @property
75
    def co_varnames(self):
76
        return self._self_adapter_code.co_varnames
77

    
78
class _AdapterFunctionSurrogate(CallableObjectProxy):
79

    
80
    def __init__(self, wrapped, adapter):
81
        super(_AdapterFunctionSurrogate, self).__init__(wrapped)
82
        self._self_adapter = adapter
83

    
84
    @property
85
    def __code__(self):
86
        return _AdapterFunctionCode(self.__wrapped__.__code__,
87
                self._self_adapter.__code__)
88

    
89
    @property
90
    def __defaults__(self):
91
        return self._self_adapter.__defaults__
92

    
93
    @property
94
    def __kwdefaults__(self):
95
        return self._self_adapter.__kwdefaults__
96

    
97
    @property
98
    def __signature__(self):
99
        if 'signature' not in globals():
100
            return self._self_adapter.__signature__
101
        else:
102
            # Can't allow this to fail on Python 3 else it falls
103
            # through to using __wrapped__, but that will be the
104
            # wrong function we want to derive the signature
105
            # from. Thus generate the signature ourselves.
106

    
107
            return signature(self._self_adapter)
108

    
109
    if PY2:
110
        func_code = __code__
111
        func_defaults = __defaults__
112

    
113
class _BoundAdapterWrapper(BoundFunctionWrapper):
114

    
115
    @property
116
    def __func__(self):
117
        return _AdapterFunctionSurrogate(self.__wrapped__.__func__,
118
                self._self_parent._self_adapter)
119

    
120
    if PY2:
121
        im_func = __func__
122

    
123
class AdapterWrapper(FunctionWrapper):
124

    
125
    __bound_function_wrapper__ = _BoundAdapterWrapper
126

    
127
    def __init__(self, *args, **kwargs):
128
        adapter = kwargs.pop('adapter')
129
        super(AdapterWrapper, self).__init__(*args, **kwargs)
130
        self._self_surrogate = _AdapterFunctionSurrogate(
131
                self.__wrapped__, adapter)
132
        self._self_adapter = adapter
133

    
134
    @property
135
    def __code__(self):
136
        return self._self_surrogate.__code__
137

    
138
    @property
139
    def __defaults__(self):
140
        return self._self_surrogate.__defaults__
141

    
142
    @property
143
    def __kwdefaults__(self):
144
        return self._self_surrogate.__kwdefaults__
145

    
146
    if PY2:
147
        func_code = __code__
148
        func_defaults = __defaults__
149

    
150
    @property
151
    def __signature__(self):
152
        return self._self_surrogate.__signature__
153

    
154
class AdapterFactory(object):
155
    def __call__(self, wrapped):
156
        raise NotImplementedError()
157

    
158
class DelegatedAdapterFactory(AdapterFactory):
159
    def __init__(self, factory):
160
        super(DelegatedAdapterFactory, self).__init__()
161
        self.factory = factory
162
    def __call__(self, wrapped):
163
        return self.factory(wrapped)
164

    
165
adapter_factory = DelegatedAdapterFactory
166

    
167
# Decorator for creating other decorators. This decorator and the
168
# wrappers which they use are designed to properly preserve any name
169
# attributes, function signatures etc, in addition to the wrappers
170
# themselves acting like a transparent proxy for the original wrapped
171
# function so the wrapper is effectively indistinguishable from the
172
# original wrapped function.
173

    
174
def decorator(wrapper=None, enabled=None, adapter=None):
175
    # The decorator should be supplied with a single positional argument
176
    # which is the wrapper function to be used to implement the
177
    # decorator. This may be preceded by a step whereby the keyword
178
    # arguments are supplied to customise the behaviour of the
179
    # decorator. The 'adapter' argument is used to optionally denote a
180
    # separate function which is notionally used by an adapter
181
    # decorator. In that case parts of the function '__code__' and
182
    # '__defaults__' attributes are used from the adapter function
183
    # rather than those of the wrapped function. This allows for the
184
    # argument specification from inspect.getargspec() to be overridden
185
    # with a prototype for a different function than what was wrapped.
186
    # The 'enabled' argument provides a way to enable/disable the use
187
    # of the decorator. If the type of 'enabled' is a boolean, then it
188
    # is evaluated immediately and the wrapper not even applied if
189
    # it is False. If not a boolean, it will be evaluated when the
190
    # wrapper is called for an unbound wrapper, and when binding occurs
191
    # for a bound wrapper. When being evaluated, if 'enabled' is callable
192
    # it will be called to obtain the value to be checked. If False,
193
    # the wrapper will not be called and instead the original wrapped
194
    # function will be called directly instead.
195

    
196
    if wrapper is not None:
197
        # Helper function for creating wrapper of the appropriate
198
        # time when we need it down below.
199

    
200
        def _build(wrapped, wrapper, enabled=None, adapter=None):
201
            if adapter:
202
                if isinstance(adapter, AdapterFactory):
203
                    adapter = adapter(wrapped)
204

    
205
                if not callable(adapter):
206
                    ns = {}
207
                    if not isinstance(adapter, string_types):
208
                        adapter = formatargspec(*adapter)
209
                    exec_('def adapter{0}: pass'.format(adapter), ns, ns)
210
                    adapter = ns['adapter']
211

    
212
                return AdapterWrapper(wrapped=wrapped, wrapper=wrapper,
213
                        enabled=enabled, adapter=adapter)
214

    
215
            return FunctionWrapper(wrapped=wrapped, wrapper=wrapper,
216
                    enabled=enabled)
217

    
218
        # The wrapper has been provided so return the final decorator.
219
        # The decorator is itself one of our function wrappers so we
220
        # can determine when it is applied to functions, instance methods
221
        # or class methods. This allows us to bind the instance or class
222
        # method so the appropriate self or cls attribute is supplied
223
        # when it is finally called.
224

    
225
        def _wrapper(wrapped, instance, args, kwargs):
226
            # We first check for the case where the decorator was applied
227
            # to a class type.
228
            #
229
            #     @decorator
230
            #     class mydecoratorclass(object):
231
            #         def __init__(self, arg=None):
232
            #             self.arg = arg
233
            #         def __call__(self, wrapped, instance, args, kwargs):
234
            #             return wrapped(*args, **kwargs)
235
            #
236
            #     @mydecoratorclass(arg=1)
237
            #     def function():
238
            #         pass
239
            #
240
            # In this case an instance of the class is to be used as the
241
            # decorator wrapper function. If args was empty at this point,
242
            # then it means that there were optional keyword arguments
243
            # supplied to be used when creating an instance of the class
244
            # to be used as the wrapper function.
245

    
246
            if instance is None and isclass(wrapped) and not args:
247
                # We still need to be passed the target function to be
248
                # wrapped as yet, so we need to return a further function
249
                # to be able to capture it.
250

    
251
                def _capture(target_wrapped):
252
                    # Now have the target function to be wrapped and need
253
                    # to create an instance of the class which is to act
254
                    # as the decorator wrapper function. Before we do that,
255
                    # we need to first check that use of the decorator
256
                    # hadn't been disabled by a simple boolean. If it was,
257
                    # the target function to be wrapped is returned instead.
258
                    
259
                    _enabled = enabled
260
                    if type(_enabled) is bool:
261
                        if not _enabled:
262
                            return target_wrapped
263
                        _enabled = None
264

    
265
                    # Now create an instance of the class which is to act
266
                    # as the decorator wrapper function. Any arguments had
267
                    # to be supplied as keyword only arguments so that is
268
                    # all we pass when creating it.
269

    
270
                    target_wrapper = wrapped(**kwargs)
271

    
272
                    # Finally build the wrapper itself and return it.
273

    
274
                    return _build(target_wrapped, target_wrapper,
275
                            _enabled, adapter)
276

    
277
                return _capture
278

    
279
            # We should always have the target function to be wrapped at
280
            # this point as the first (and only) value in args.
281

    
282
            target_wrapped = args[0]
283

    
284
            # Need to now check that use of the decorator hadn't been
285
            # disabled by a simple boolean. If it was, then target
286
            # function to be wrapped is returned instead.
287

    
288
            _enabled = enabled
289
            if type(_enabled) is bool:
290
                if not _enabled:
291
                    return target_wrapped
292
                _enabled = None
293

    
294
            # We now need to build the wrapper, but there are a couple of
295
            # different cases we need to consider.
296

    
297
            if instance is None:
298
                if isclass(wrapped):
299
                    # In this case the decorator was applied to a class
300
                    # type but optional keyword arguments were not supplied
301
                    # for initialising an instance of the class to be used
302
                    # as the decorator wrapper function.
303
                    #
304
                    #     @decorator
305
                    #     class mydecoratorclass(object):
306
                    #         def __init__(self, arg=None):
307
                    #             self.arg = arg
308
                    #         def __call__(self, wrapped, instance,
309
                    #                 args, kwargs):
310
                    #             return wrapped(*args, **kwargs)
311
                    #
312
                    #     @mydecoratorclass
313
                    #     def function():
314
                    #         pass
315
                    #
316
                    # We still need to create an instance of the class to
317
                    # be used as the decorator wrapper function, but no
318
                    # arguments are pass.
319

    
320
                    target_wrapper = wrapped()
321

    
322
                else:
323
                    # In this case the decorator was applied to a normal
324
                    # function, or possibly a static method of a class.
325
                    #
326
                    #     @decorator
327
                    #     def mydecoratorfuntion(wrapped, instance,
328
                    #             args, kwargs):
329
                    #         return wrapped(*args, **kwargs)
330
                    #
331
                    #     @mydecoratorfunction
332
                    #     def function():
333
                    #         pass
334
                    #
335
                    # That normal function becomes the decorator wrapper
336
                    # function.
337

    
338
                    target_wrapper = wrapper
339

    
340
            else:
341
                if isclass(instance):
342
                    # In this case the decorator was applied to a class
343
                    # method.
344
                    #
345
                    #     class myclass(object):
346
                    #         @decorator
347
                    #         @classmethod
348
                    #         def decoratorclassmethod(cls, wrapped,
349
                    #                 instance, args, kwargs):
350
                    #             return wrapped(*args, **kwargs)
351
                    #
352
                    #     instance = myclass()
353
                    #
354
                    #     @instance.decoratorclassmethod
355
                    #     def function():
356
                    #         pass
357
                    #
358
                    # This one is a bit strange because binding was actually
359
                    # performed on the wrapper created by our decorator
360
                    # factory. We need to apply that binding to the decorator
361
                    # wrapper function which which the decorator factory
362
                    # was applied to.
363

    
364
                    target_wrapper = wrapper.__get__(None, instance)
365

    
366
                else:
367
                    # In this case the decorator was applied to an instance
368
                    # method.
369
                    #
370
                    #     class myclass(object):
371
                    #         @decorator
372
                    #         def decoratorclassmethod(self, wrapped,
373
                    #                 instance, args, kwargs):
374
                    #             return wrapped(*args, **kwargs)
375
                    #
376
                    #     instance = myclass()
377
                    #
378
                    #     @instance.decoratorclassmethod
379
                    #     def function():
380
                    #         pass
381
                    #
382
                    # This one is a bit strange because binding was actually
383
                    # performed on the wrapper created by our decorator
384
                    # factory. We need to apply that binding to the decorator
385
                    # wrapper function which which the decorator factory
386
                    # was applied to.
387

    
388
                    target_wrapper = wrapper.__get__(instance, type(instance))
389

    
390
            # Finally build the wrapper itself and return it.
391

    
392
            return _build(target_wrapped, target_wrapper, _enabled, adapter)
393

    
394
        # We first return our magic function wrapper here so we can
395
        # determine in what context the decorator factory was used. In
396
        # other words, it is itself a universal decorator.
397

    
398
        return _build(wrapper, _wrapper)
399

    
400
    else:
401
        # The wrapper still has not been provided, so we are just
402
        # collecting the optional keyword arguments. Return the
403
        # decorator again wrapped in a partial using the collected
404
        # arguments.
405

    
406
        return partial(decorator, enabled=enabled, adapter=adapter)
407

    
408
# Decorator for implementing thread synchronization. It can be used as a
409
# decorator, in which case the synchronization context is determined by
410
# what type of function is wrapped, or it can also be used as a context
411
# manager, where the user needs to supply the correct synchronization
412
# context. It is also possible to supply an object which appears to be a
413
# synchronization primitive of some sort, by virtue of having release()
414
# and acquire() methods. In that case that will be used directly as the
415
# synchronization primitive without creating a separate lock against the
416
# derived or supplied context.
417

    
418
def synchronized(wrapped):
419
    # Determine if being passed an object which is a synchronization
420
    # primitive. We can't check by type for Lock, RLock, Semaphore etc,
421
    # as the means of creating them isn't the type. Therefore use the
422
    # existence of acquire() and release() methods. This is more
423
    # extensible anyway as it allows custom synchronization mechanisms.
424

    
425
    if hasattr(wrapped, 'acquire') and hasattr(wrapped, 'release'):
426
        # We remember what the original lock is and then return a new
427
        # decorator which acceses and locks it. When returning the new
428
        # decorator we wrap it with an object proxy so we can override
429
        # the context manager methods in case it is being used to wrap
430
        # synchronized statements with a 'with' statement.
431

    
432
        lock = wrapped
433

    
434
        @decorator
435
        def _synchronized(wrapped, instance, args, kwargs):
436
            # Execute the wrapped function while the original supplied
437
            # lock is held.
438

    
439
            with lock:
440
                return wrapped(*args, **kwargs)
441

    
442
        class _PartialDecorator(CallableObjectProxy):
443

    
444
            def __enter__(self):
445
                lock.acquire()
446
                return lock
447

    
448
            def __exit__(self, *args):
449
                lock.release()
450

    
451
        return _PartialDecorator(wrapped=_synchronized)
452

    
453
    # Following only apply when the lock is being created automatically
454
    # based on the context of what was supplied. In this case we supply
455
    # a final decorator, but need to use FunctionWrapper directly as we
456
    # want to derive from it to add context manager methods in in case it is
457
    # being used to wrap synchronized statements with a 'with' statement.
458

    
459
    def _synchronized_lock(context):
460
        # Attempt to retrieve the lock for the specific context.
461

    
462
        lock = vars(context).get('_synchronized_lock', None)
463

    
464
        if lock is None:
465
            # There is no existing lock defined for the context we
466
            # are dealing with so we need to create one. This needs
467
            # to be done in a way to guarantee there is only one
468
            # created, even if multiple threads try and create it at
469
            # the same time. We can't always use the setdefault()
470
            # method on the __dict__ for the context. This is the
471
            # case where the context is a class, as __dict__ is
472
            # actually a dictproxy. What we therefore do is use a
473
            # meta lock on this wrapper itself, to control the
474
            # creation and assignment of the lock attribute against
475
            # the context.
476

    
477
            meta_lock = vars(synchronized).setdefault(
478
                    '_synchronized_meta_lock', Lock())
479

    
480
            with meta_lock:
481
                # We need to check again for whether the lock we want
482
                # exists in case two threads were trying to create it
483
                # at the same time and were competing to create the
484
                # meta lock.
485

    
486
                lock = vars(context).get('_synchronized_lock', None)
487

    
488
                if lock is None:
489
                    lock = RLock()
490
                    setattr(context, '_synchronized_lock', lock)
491

    
492
        return lock
493

    
494
    def _synchronized_wrapper(wrapped, instance, args, kwargs):
495
        # Execute the wrapped function while the lock for the
496
        # desired context is held. If instance is None then the
497
        # wrapped function is used as the context.
498

    
499
        with _synchronized_lock(instance or wrapped):
500
            return wrapped(*args, **kwargs)
501

    
502
    class _FinalDecorator(FunctionWrapper):
503

    
504
        def __enter__(self):
505
            self._self_lock = _synchronized_lock(self.__wrapped__)
506
            self._self_lock.acquire()
507
            return self._self_lock
508

    
509
        def __exit__(self, *args):
510
            self._self_lock.release()
511

    
512
    return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)