Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/wrapt/decorators.py: 46%

192 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1"""This module implements decorators for implementing other decorators 

2as well as some commonly used decorators. 

3 

4""" 

5 

6import sys 

7 

8PY2 = sys.version_info[0] == 2 

9 

10if PY2: 

11 string_types = basestring, 

12 

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

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

15 if _globs_ is None: 

16 frame = sys._getframe(1) 

17 _globs_ = frame.f_globals 

18 if _locs_ is None: 

19 _locs_ = frame.f_locals 

20 del frame 

21 elif _locs_ is None: 

22 _locs_ = _globs_ 

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

24 

25else: 

26 string_types = str, 

27 

28 import builtins 

29 

30 exec_ = getattr(builtins, "exec") 

31 del builtins 

32 

33from functools import partial 

34from inspect import isclass 

35from threading import Lock, RLock 

36 

37from .arguments import formatargspec 

38 

39try: 

40 from inspect import signature 

41except ImportError: 

42 pass 

43 

44from .wrappers import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy, 

45 CallableObjectProxy) 

46 

47# Adapter wrapper for the wrapped function which will overlay certain 

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

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

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

51# one would expect. 

52 

53class _AdapterFunctionCode(CallableObjectProxy): 

54 

55 def __init__(self, wrapped_code, adapter_code): 

56 super(_AdapterFunctionCode, self).__init__(wrapped_code) 

57 self._self_adapter_code = adapter_code 

58 

59 @property 

60 def co_argcount(self): 

61 return self._self_adapter_code.co_argcount 

62 

63 @property 

64 def co_code(self): 

65 return self._self_adapter_code.co_code 

66 

67 @property 

68 def co_flags(self): 

69 return self._self_adapter_code.co_flags 

70 

71 @property 

72 def co_kwonlyargcount(self): 

73 return self._self_adapter_code.co_kwonlyargcount 

74 

75 @property 

76 def co_varnames(self): 

77 return self._self_adapter_code.co_varnames 

78 

79class _AdapterFunctionSurrogate(CallableObjectProxy): 

80 

81 def __init__(self, wrapped, adapter): 

82 super(_AdapterFunctionSurrogate, self).__init__(wrapped) 

83 self._self_adapter = adapter 

84 

85 @property 

86 def __code__(self): 

87 return _AdapterFunctionCode(self.__wrapped__.__code__, 

88 self._self_adapter.__code__) 

89 

90 @property 

91 def __defaults__(self): 

92 return self._self_adapter.__defaults__ 

93 

94 @property 

95 def __kwdefaults__(self): 

96 return self._self_adapter.__kwdefaults__ 

97 

98 @property 

99 def __signature__(self): 

100 if 'signature' not in globals(): 

101 return self._self_adapter.__signature__ 

102 else: 

103 return signature(self._self_adapter) 

104 

105 if PY2: 

106 func_code = __code__ 

107 func_defaults = __defaults__ 

108 

109class _BoundAdapterWrapper(BoundFunctionWrapper): 

110 

111 @property 

112 def __func__(self): 

113 return _AdapterFunctionSurrogate(self.__wrapped__.__func__, 

114 self._self_parent._self_adapter) 

115 

116 @property 

117 def __signature__(self): 

118 if 'signature' not in globals(): 

119 return self.__wrapped__.__signature__ 

120 else: 

121 return signature(self._self_parent._self_adapter) 

122 

123 if PY2: 

124 im_func = __func__ 

125 

126class AdapterWrapper(FunctionWrapper): 

127 

128 __bound_function_wrapper__ = _BoundAdapterWrapper 

129 

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

131 adapter = kwargs.pop('adapter') 

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

133 self._self_surrogate = _AdapterFunctionSurrogate( 

134 self.__wrapped__, adapter) 

135 self._self_adapter = adapter 

136 

137 @property 

138 def __code__(self): 

139 return self._self_surrogate.__code__ 

140 

141 @property 

142 def __defaults__(self): 

143 return self._self_surrogate.__defaults__ 

144 

145 @property 

146 def __kwdefaults__(self): 

147 return self._self_surrogate.__kwdefaults__ 

148 

149 if PY2: 

150 func_code = __code__ 

151 func_defaults = __defaults__ 

152 

153 @property 

154 def __signature__(self): 

155 return self._self_surrogate.__signature__ 

156 

157class AdapterFactory(object): 

158 def __call__(self, wrapped): 

159 raise NotImplementedError() 

160 

161class DelegatedAdapterFactory(AdapterFactory): 

162 def __init__(self, factory): 

163 super(DelegatedAdapterFactory, self).__init__() 

164 self.factory = factory 

165 def __call__(self, wrapped): 

166 return self.factory(wrapped) 

167 

168adapter_factory = DelegatedAdapterFactory 

169 

170# Decorator for creating other decorators. This decorator and the 

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

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

173# themselves acting like a transparent proxy for the original wrapped 

174# function so the wrapper is effectively indistinguishable from the 

175# original wrapped function. 

176 

177def decorator(wrapper=None, enabled=None, adapter=None, proxy=FunctionWrapper): 

178 # The decorator should be supplied with a single positional argument 

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

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

181 # arguments are supplied to customise the behaviour of the 

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

183 # separate function which is notionally used by an adapter 

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

185 # '__defaults__' attributes are used from the adapter function 

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

187 # argument specification from inspect.getfullargspec() and similar 

188 # functions to be overridden with a prototype for a different 

189 # function than what was wrapped. The 'enabled' argument provides a 

190 # way to enable/disable the use of the decorator. If the type of 

191 # 'enabled' is a boolean, then it is evaluated immediately and the 

192 # wrapper not even applied if it is False. If not a boolean, it will 

193 # be evaluated when the wrapper is called for an unbound wrapper, 

194 # and when binding occurs for a bound wrapper. When being evaluated, 

195 # if 'enabled' is callable it will be called to obtain the value to 

196 # be checked. If False, the wrapper will not be called and instead 

197 # the original wrapped function will be called directly instead. 

198 # The 'proxy' argument provides a way of passing a custom version of 

199 # the FunctionWrapper class used in decorating the function. 

200 

201 if wrapper is not None: 

202 # Helper function for creating wrapper of the appropriate 

203 # time when we need it down below. 

204 

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

206 if adapter: 

207 if isinstance(adapter, AdapterFactory): 

208 adapter = adapter(wrapped) 

209 

210 if not callable(adapter): 

211 ns = {} 

212 

213 # Check if the signature argument specification has 

214 # annotations. If it does then we need to remember 

215 # it but also drop it when attempting to manufacture 

216 # a standin adapter function. This is necessary else 

217 # it will try and look up any types referenced in 

218 # the annotations in the empty namespace we use, 

219 # which will fail. 

220 

221 annotations = {} 

222 

223 if not isinstance(adapter, string_types): 

224 if len(adapter) == 7: 

225 annotations = adapter[-1] 

226 adapter = adapter[:-1] 

227 adapter = formatargspec(*adapter) 

228 

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

230 adapter = ns['adapter'] 

231 

232 # Override the annotations for the manufactured 

233 # adapter function so they match the original 

234 # adapter signature argument specification. 

235 

236 if annotations: 

237 adapter.__annotations__ = annotations 

238 

239 return AdapterWrapper(wrapped=wrapped, wrapper=wrapper, 

240 enabled=enabled, adapter=adapter) 

241 

242 return proxy(wrapped=wrapped, wrapper=wrapper, enabled=enabled) 

243 

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

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

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

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

248 # method so the appropriate self or cls attribute is supplied 

249 # when it is finally called. 

250 

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

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

253 # to a class type. 

254 # 

255 # @decorator 

256 # class mydecoratorclass(object): 

257 # def __init__(self, arg=None): 

258 # self.arg = arg 

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

260 # return wrapped(*args, **kwargs) 

261 # 

262 # @mydecoratorclass(arg=1) 

263 # def function(): 

264 # pass 

265 # 

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

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

268 # then it means that there were optional keyword arguments 

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

270 # to be used as the wrapper function. 

271 

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

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

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

275 # to be able to capture it. 

276 

277 def _capture(target_wrapped): 

278 # Now have the target function to be wrapped and need 

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

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

281 # we need to first check that use of the decorator 

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

283 # the target function to be wrapped is returned instead. 

284 

285 _enabled = enabled 

286 if type(_enabled) is bool: 

287 if not _enabled: 

288 return target_wrapped 

289 _enabled = None 

290 

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

292 # as the decorator wrapper function. Any arguments had 

293 # to be supplied as keyword only arguments so that is 

294 # all we pass when creating it. 

295 

296 target_wrapper = wrapped(**kwargs) 

297 

298 # Finally build the wrapper itself and return it. 

299 

300 return _build(target_wrapped, target_wrapper, 

301 _enabled, adapter) 

302 

303 return _capture 

304 

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

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

307 

308 target_wrapped = args[0] 

309 

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

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

312 # function to be wrapped is returned instead. 

313 

314 _enabled = enabled 

315 if type(_enabled) is bool: 

316 if not _enabled: 

317 return target_wrapped 

318 _enabled = None 

319 

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

321 # different cases we need to consider. 

322 

323 if instance is None: 

324 if isclass(wrapped): 

325 # In this case the decorator was applied to a class 

326 # type but optional keyword arguments were not supplied 

327 # for initialising an instance of the class to be used 

328 # as the decorator wrapper function. 

329 # 

330 # @decorator 

331 # class mydecoratorclass(object): 

332 # def __init__(self, arg=None): 

333 # self.arg = arg 

334 # def __call__(self, wrapped, instance, 

335 # args, kwargs): 

336 # return wrapped(*args, **kwargs) 

337 # 

338 # @mydecoratorclass 

339 # def function(): 

340 # pass 

341 # 

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

343 # be used as the decorator wrapper function, but no 

344 # arguments are pass. 

345 

346 target_wrapper = wrapped() 

347 

348 else: 

349 # In this case the decorator was applied to a normal 

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

351 # 

352 # @decorator 

353 # def mydecoratorfuntion(wrapped, instance, 

354 # args, kwargs): 

355 # return wrapped(*args, **kwargs) 

356 # 

357 # @mydecoratorfunction 

358 # def function(): 

359 # pass 

360 # 

361 # That normal function becomes the decorator wrapper 

362 # function. 

363 

364 target_wrapper = wrapper 

365 

366 else: 

367 if isclass(instance): 

368 # In this case the decorator was applied to a class 

369 # method. 

370 # 

371 # class myclass(object): 

372 # @decorator 

373 # @classmethod 

374 # def decoratorclassmethod(cls, wrapped, 

375 # instance, args, kwargs): 

376 # return wrapped(*args, **kwargs) 

377 # 

378 # instance = myclass() 

379 # 

380 # @instance.decoratorclassmethod 

381 # def function(): 

382 # pass 

383 # 

384 # This one is a bit strange because binding was actually 

385 # performed on the wrapper created by our decorator 

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

387 # wrapper function that the decorator factory 

388 # was applied to. 

389 

390 target_wrapper = wrapper.__get__(None, instance) 

391 

392 else: 

393 # In this case the decorator was applied to an instance 

394 # method. 

395 # 

396 # class myclass(object): 

397 # @decorator 

398 # def decoratorclassmethod(self, wrapped, 

399 # instance, args, kwargs): 

400 # return wrapped(*args, **kwargs) 

401 # 

402 # instance = myclass() 

403 # 

404 # @instance.decoratorclassmethod 

405 # def function(): 

406 # pass 

407 # 

408 # This one is a bit strange because binding was actually 

409 # performed on the wrapper created by our decorator 

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

411 # wrapper function that the decorator factory 

412 # was applied to. 

413 

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

415 

416 # Finally build the wrapper itself and return it. 

417 

418 return _build(target_wrapped, target_wrapper, _enabled, adapter) 

419 

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

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

422 # other words, it is itself a universal decorator. The decorator 

423 # function is used as the adapter so that linters see a signature 

424 # corresponding to the decorator and not the wrapper it is being 

425 # applied to. 

426 

427 return _build(wrapper, _wrapper, adapter=decorator) 

428 

429 else: 

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

431 # collecting the optional keyword arguments. Return the 

432 # decorator again wrapped in a partial using the collected 

433 # arguments. 

434 

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

436 proxy=proxy) 

437 

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

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

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

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

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

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

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

445# synchronization primitive without creating a separate lock against the 

446# derived or supplied context. 

447 

448def synchronized(wrapped): 

449 # Determine if being passed an object which is a synchronization 

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

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

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

453 # extensible anyway as it allows custom synchronization mechanisms. 

454 

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

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

457 # decorator which accesses and locks it. When returning the new 

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

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

460 # synchronized statements with a 'with' statement. 

461 

462 lock = wrapped 

463 

464 @decorator 

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

466 # Execute the wrapped function while the original supplied 

467 # lock is held. 

468 

469 with lock: 

470 return wrapped(*args, **kwargs) 

471 

472 class _PartialDecorator(CallableObjectProxy): 

473 

474 def __enter__(self): 

475 lock.acquire() 

476 return lock 

477 

478 def __exit__(self, *args): 

479 lock.release() 

480 

481 return _PartialDecorator(wrapped=_synchronized) 

482 

483 # Following only apply when the lock is being created automatically 

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

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

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

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

488 

489 def _synchronized_lock(context): 

490 # Attempt to retrieve the lock for the specific context. 

491 

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

493 

494 if lock is None: 

495 # There is no existing lock defined for the context we 

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

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

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

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

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

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

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

503 # meta lock on this wrapper itself, to control the 

504 # creation and assignment of the lock attribute against 

505 # the context. 

506 

507 with synchronized._synchronized_meta_lock: 

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

509 # exists in case two threads were trying to create it 

510 # at the same time and were competing to create the 

511 # meta lock. 

512 

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

514 

515 if lock is None: 

516 lock = RLock() 

517 setattr(context, '_synchronized_lock', lock) 

518 

519 return lock 

520 

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

522 # Execute the wrapped function while the lock for the 

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

524 # wrapped function is used as the context. 

525 

526 with _synchronized_lock(instance if instance is not None else wrapped): 

527 return wrapped(*args, **kwargs) 

528 

529 class _FinalDecorator(FunctionWrapper): 

530 

531 def __enter__(self): 

532 self._self_lock = _synchronized_lock(self.__wrapped__) 

533 self._self_lock.acquire() 

534 return self._self_lock 

535 

536 def __exit__(self, *args): 

537 self._self_lock.release() 

538 

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

540 

541synchronized._synchronized_meta_lock = Lock()