Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

"""This module implements decorators for implementing other decorators 

as well as some commonly used decorators. 

 

""" 

 

import sys 

 

PY2 = sys.version_info[0] == 2 

PY3 = sys.version_info[0] == 3 

 

if PY3: 

    string_types = str, 

 

    import builtins 

    exec_ = getattr(builtins, "exec") 

    del builtins 

 

else: 

    string_types = basestring, 

 

    def exec_(_code_, _globs_=None, _locs_=None): 

        """Execute code in a namespace.""" 

        if _globs_ is None: 

            frame = sys._getframe(1) 

            _globs_ = frame.f_globals 

            if _locs_ is None: 

                _locs_ = frame.f_locals 

            del frame 

        elif _locs_ is None: 

            _locs_ = _globs_ 

        exec("""exec _code_ in _globs_, _locs_""") 

 

from functools import partial 

from inspect import getargspec, ismethod, isclass, formatargspec 

from collections import namedtuple 

from threading import Lock, RLock 

 

try: 

    from inspect import signature 

except ImportError: 

    pass 

 

from .wrappers import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy, 

    CallableObjectProxy) 

 

# Adapter wrapper for the wrapped function which will overlay certain 

# properties from the adapter function onto the wrapped function so that 

# functions such as inspect.getargspec(), inspect.getfullargspec(), 

# inspect.signature() and inspect.getsource() return the correct results 

# one would expect. 

 

class _AdapterFunctionCode(CallableObjectProxy): 

 

    def __init__(self, wrapped_code, adapter_code): 

        super(_AdapterFunctionCode, self).__init__(wrapped_code) 

        self._self_adapter_code = adapter_code 

 

    @property 

    def co_argcount(self): 

        return self._self_adapter_code.co_argcount 

 

    @property 

    def co_code(self): 

        return self._self_adapter_code.co_code 

 

    @property 

    def co_flags(self): 

        return self._self_adapter_code.co_flags 

 

    @property 

    def co_kwonlyargcount(self): 

        return self._self_adapter_code.co_kwonlyargcount 

 

    @property 

    def co_varnames(self): 

        return self._self_adapter_code.co_varnames 

 

class _AdapterFunctionSurrogate(CallableObjectProxy): 

 

    def __init__(self, wrapped, adapter): 

        super(_AdapterFunctionSurrogate, self).__init__(wrapped) 

        self._self_adapter = adapter 

 

    @property 

    def __code__(self): 

        return _AdapterFunctionCode(self.__wrapped__.__code__, 

                self._self_adapter.__code__) 

 

    @property 

    def __defaults__(self): 

        return self._self_adapter.__defaults__ 

 

    @property 

    def __kwdefaults__(self): 

        return self._self_adapter.__kwdefaults__ 

 

    @property 

    def __signature__(self): 

        if 'signature' not in globals(): 

            return self._self_adapter.__signature__ 

        else: 

            # Can't allow this to fail on Python 3 else it falls 

            # through to using __wrapped__, but that will be the 

            # wrong function we want to derive the signature 

            # from. Thus generate the signature ourselves. 

 

            return signature(self._self_adapter) 

 

    if PY2: 

        func_code = __code__ 

        func_defaults = __defaults__ 

 

class _BoundAdapterWrapper(BoundFunctionWrapper): 

 

    @property 

    def __func__(self): 

        return _AdapterFunctionSurrogate(self.__wrapped__.__func__, 

                self._self_parent._self_adapter) 

 

    if PY2: 

        im_func = __func__ 

 

class AdapterWrapper(FunctionWrapper): 

 

    __bound_function_wrapper__ = _BoundAdapterWrapper 

 

    def __init__(self, *args, **kwargs): 

        adapter = kwargs.pop('adapter') 

        super(AdapterWrapper, self).__init__(*args, **kwargs) 

        self._self_surrogate = _AdapterFunctionSurrogate( 

                self.__wrapped__, adapter) 

        self._self_adapter = adapter 

 

    @property 

    def __code__(self): 

        return self._self_surrogate.__code__ 

 

    @property 

    def __defaults__(self): 

        return self._self_surrogate.__defaults__ 

 

    @property 

    def __kwdefaults__(self): 

        return self._self_surrogate.__kwdefaults__ 

 

    if PY2: 

        func_code = __code__ 

        func_defaults = __defaults__ 

 

    @property 

    def __signature__(self): 

        return self._self_surrogate.__signature__ 

 

class AdapterFactory(object): 

    def __call__(self, wrapped): 

        raise NotImplementedError() 

 

class DelegatedAdapterFactory(AdapterFactory): 

    def __init__(self, factory): 

        super(DelegatedAdapterFactory, self).__init__() 

        self.factory = factory 

    def __call__(self, wrapped): 

        return self.factory(wrapped) 

 

adapter_factory = DelegatedAdapterFactory 

 

# Decorator for creating other decorators. This decorator and the 

# wrappers which they use are designed to properly preserve any name 

# attributes, function signatures etc, in addition to the wrappers 

# themselves acting like a transparent proxy for the original wrapped 

# function so the wrapper is effectively indistinguishable from the 

# original wrapped function. 

 

def decorator(wrapper=None, enabled=None, adapter=None): 

    # The decorator should be supplied with a single positional argument 

    # which is the wrapper function to be used to implement the 

    # decorator. This may be preceded by a step whereby the keyword 

    # arguments are supplied to customise the behaviour of the 

    # decorator. The 'adapter' argument is used to optionally denote a 

    # separate function which is notionally used by an adapter 

    # decorator. In that case parts of the function '__code__' and 

    # '__defaults__' attributes are used from the adapter function 

    # rather than those of the wrapped function. This allows for the 

    # argument specification from inspect.getargspec() to be overridden 

    # with a prototype for a different function than what was wrapped. 

    # The 'enabled' argument provides a way to enable/disable the use 

    # of the decorator. If the type of 'enabled' is a boolean, then it 

    # is evaluated immediately and the wrapper not even applied if 

    # it is False. If not a boolean, it will be evaluated when the 

    # wrapper is called for an unbound wrapper, and when binding occurs 

    # for a bound wrapper. When being evaluated, if 'enabled' is callable 

    # it will be called to obtain the value to be checked. If False, 

    # the wrapper will not be called and instead the original wrapped 

    # function will be called directly instead. 

 

    if wrapper is not None: 

        # Helper function for creating wrapper of the appropriate 

        # time when we need it down below. 

 

        def _build(wrapped, wrapper, enabled=None, adapter=None): 

            if adapter: 

                if isinstance(adapter, AdapterFactory): 

                    adapter = adapter(wrapped) 

 

                if not callable(adapter): 

                    ns = {} 

                    if not isinstance(adapter, string_types): 

                        adapter = formatargspec(*adapter) 

                    exec_('def adapter{0}: pass'.format(adapter), ns, ns) 

                    adapter = ns['adapter'] 

 

                return AdapterWrapper(wrapped=wrapped, wrapper=wrapper, 

                        enabled=enabled, adapter=adapter) 

 

            return FunctionWrapper(wrapped=wrapped, wrapper=wrapper, 

                    enabled=enabled) 

 

        # The wrapper has been provided so return the final decorator. 

        # The decorator is itself one of our function wrappers so we 

        # can determine when it is applied to functions, instance methods 

        # or class methods. This allows us to bind the instance or class 

        # method so the appropriate self or cls attribute is supplied 

        # when it is finally called. 

 

        def _wrapper(wrapped, instance, args, kwargs): 

            # We first check for the case where the decorator was applied 

            # to a class type. 

            # 

            #     @decorator 

            #     class mydecoratorclass(object): 

            #         def __init__(self, arg=None): 

            #             self.arg = arg 

            #         def __call__(self, wrapped, instance, args, kwargs): 

            #             return wrapped(*args, **kwargs) 

            # 

            #     @mydecoratorclass(arg=1) 

            #     def function(): 

            #         pass 

            # 

            # In this case an instance of the class is to be used as the 

            # decorator wrapper function. If args was empty at this point, 

            # then it means that there were optional keyword arguments 

            # supplied to be used when creating an instance of the class 

            # to be used as the wrapper function. 

 

            if instance is None and isclass(wrapped) and not args: 

                # We still need to be passed the target function to be 

                # wrapped as yet, so we need to return a further function 

                # to be able to capture it. 

 

                def _capture(target_wrapped): 

                    # Now have the target function to be wrapped and need 

                    # to create an instance of the class which is to act 

                    # as the decorator wrapper function. Before we do that, 

                    # we need to first check that use of the decorator 

                    # hadn't been disabled by a simple boolean. If it was, 

                    # the target function to be wrapped is returned instead. 

 

                    _enabled = enabled 

                    if type(_enabled) is bool: 

                        if not _enabled: 

                            return target_wrapped 

                        _enabled = None 

 

                    # Now create an instance of the class which is to act 

                    # as the decorator wrapper function. Any arguments had 

                    # to be supplied as keyword only arguments so that is 

                    # all we pass when creating it. 

 

                    target_wrapper = wrapped(**kwargs) 

 

                    # Finally build the wrapper itself and return it. 

 

                    return _build(target_wrapped, target_wrapper, 

                            _enabled, adapter) 

 

                return _capture 

 

            # We should always have the target function to be wrapped at 

            # this point as the first (and only) value in args. 

 

            target_wrapped = args[0] 

 

            # Need to now check that use of the decorator hadn't been 

            # disabled by a simple boolean. If it was, then target 

            # function to be wrapped is returned instead. 

 

            _enabled = enabled 

            if type(_enabled) is bool: 

                if not _enabled: 

                    return target_wrapped 

                _enabled = None 

 

            # We now need to build the wrapper, but there are a couple of 

            # different cases we need to consider. 

 

            if instance is None: 

                if isclass(wrapped): 

                    # In this case the decorator was applied to a class 

                    # type but optional keyword arguments were not supplied 

                    # for initialising an instance of the class to be used 

                    # as the decorator wrapper function. 

                    # 

                    #     @decorator 

                    #     class mydecoratorclass(object): 

                    #         def __init__(self, arg=None): 

                    #             self.arg = arg 

                    #         def __call__(self, wrapped, instance, 

                    #                 args, kwargs): 

                    #             return wrapped(*args, **kwargs) 

                    # 

                    #     @mydecoratorclass 

                    #     def function(): 

                    #         pass 

                    # 

                    # We still need to create an instance of the class to 

                    # be used as the decorator wrapper function, but no 

                    # arguments are pass. 

 

                    target_wrapper = wrapped() 

 

                else: 

                    # In this case the decorator was applied to a normal 

                    # function, or possibly a static method of a class. 

                    # 

                    #     @decorator 

                    #     def mydecoratorfuntion(wrapped, instance, 

                    #             args, kwargs): 

                    #         return wrapped(*args, **kwargs) 

                    # 

                    #     @mydecoratorfunction 

                    #     def function(): 

                    #         pass 

                    # 

                    # That normal function becomes the decorator wrapper 

                    # function. 

 

                    target_wrapper = wrapper 

 

            else: 

                if isclass(instance): 

                    # In this case the decorator was applied to a class 

                    # method. 

                    # 

                    #     class myclass(object): 

                    #         @decorator 

                    #         @classmethod 

                    #         def decoratorclassmethod(cls, wrapped, 

                    #                 instance, args, kwargs): 

                    #             return wrapped(*args, **kwargs) 

                    # 

                    #     instance = myclass() 

                    # 

                    #     @instance.decoratorclassmethod 

                    #     def function(): 

                    #         pass 

                    # 

                    # This one is a bit strange because binding was actually 

                    # performed on the wrapper created by our decorator 

                    # factory. We need to apply that binding to the decorator 

                    # wrapper function which which the decorator factory 

                    # was applied to. 

 

                    target_wrapper = wrapper.__get__(None, instance) 

 

                else: 

                    # In this case the decorator was applied to an instance 

                    # method. 

                    # 

                    #     class myclass(object): 

                    #         @decorator 

                    #         def decoratorclassmethod(self, wrapped, 

                    #                 instance, args, kwargs): 

                    #             return wrapped(*args, **kwargs) 

                    # 

                    #     instance = myclass() 

                    # 

                    #     @instance.decoratorclassmethod 

                    #     def function(): 

                    #         pass 

                    # 

                    # This one is a bit strange because binding was actually 

                    # performed on the wrapper created by our decorator 

                    # factory. We need to apply that binding to the decorator 

                    # wrapper function which which the decorator factory 

                    # was applied to. 

 

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

 

            # Finally build the wrapper itself and return it. 

 

            return _build(target_wrapped, target_wrapper, _enabled, adapter) 

 

        # We first return our magic function wrapper here so we can 

        # determine in what context the decorator factory was used. In 

        # other words, it is itself a universal decorator. 

 

        return _build(wrapper, _wrapper) 

 

    else: 

        # The wrapper still has not been provided, so we are just 

        # collecting the optional keyword arguments. Return the 

        # decorator again wrapped in a partial using the collected 

        # arguments. 

 

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

 

# Decorator for implementing thread synchronization. It can be used as a 

# decorator, in which case the synchronization context is determined by 

# what type of function is wrapped, or it can also be used as a context 

# manager, where the user needs to supply the correct synchronization 

# context. It is also possible to supply an object which appears to be a 

# synchronization primitive of some sort, by virtue of having release() 

# and acquire() methods. In that case that will be used directly as the 

# synchronization primitive without creating a separate lock against the 

# derived or supplied context. 

 

def synchronized(wrapped): 

    # Determine if being passed an object which is a synchronization 

    # primitive. We can't check by type for Lock, RLock, Semaphore etc, 

    # as the means of creating them isn't the type. Therefore use the 

    # existence of acquire() and release() methods. This is more 

    # extensible anyway as it allows custom synchronization mechanisms. 

 

    if hasattr(wrapped, 'acquire') and hasattr(wrapped, 'release'): 

        # We remember what the original lock is and then return a new 

        # decorator which acceses and locks it. When returning the new 

        # decorator we wrap it with an object proxy so we can override 

        # the context manager methods in case it is being used to wrap 

        # synchronized statements with a 'with' statement. 

 

        lock = wrapped 

 

        @decorator 

        def _synchronized(wrapped, instance, args, kwargs): 

            # Execute the wrapped function while the original supplied 

            # lock is held. 

 

            with lock: 

                return wrapped(*args, **kwargs) 

 

        class _PartialDecorator(CallableObjectProxy): 

 

            def __enter__(self): 

                lock.acquire() 

                return lock 

 

            def __exit__(self, *args): 

                lock.release() 

 

        return _PartialDecorator(wrapped=_synchronized) 

 

    # Following only apply when the lock is being created automatically 

    # based on the context of what was supplied. In this case we supply 

    # a final decorator, but need to use FunctionWrapper directly as we 

    # want to derive from it to add context manager methods in in case it is 

    # being used to wrap synchronized statements with a 'with' statement. 

 

    def _synchronized_lock(context): 

        # Attempt to retrieve the lock for the specific context. 

 

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

 

        if lock is None: 

            # There is no existing lock defined for the context we 

            # are dealing with so we need to create one. This needs 

            # to be done in a way to guarantee there is only one 

            # created, even if multiple threads try and create it at 

            # the same time. We can't always use the setdefault() 

            # method on the __dict__ for the context. This is the 

            # case where the context is a class, as __dict__ is 

            # actually a dictproxy. What we therefore do is use a 

            # meta lock on this wrapper itself, to control the 

            # creation and assignment of the lock attribute against 

            # the context. 

 

            meta_lock = vars(synchronized).setdefault( 

                    '_synchronized_meta_lock', Lock()) 

 

            with meta_lock: 

                # We need to check again for whether the lock we want 

                # exists in case two threads were trying to create it 

                # at the same time and were competing to create the 

                # meta lock. 

 

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

 

                if lock is None: 

                    lock = RLock() 

                    setattr(context, '_synchronized_lock', lock) 

 

        return lock 

 

    def _synchronized_wrapper(wrapped, instance, args, kwargs): 

        # Execute the wrapped function while the lock for the 

        # desired context is held. If instance is None then the 

        # wrapped function is used as the context. 

 

        with _synchronized_lock(instance or wrapped): 

            return wrapped(*args, **kwargs) 

 

    class _FinalDecorator(FunctionWrapper): 

 

        def __enter__(self): 

            self._self_lock = _synchronized_lock(self.__wrapped__) 

            self._self_lock.acquire() 

            return self._self_lock 

 

        def __exit__(self, *args): 

            self._self_lock.release() 

 

    return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)