Hide keyboard shortcuts

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# -*- coding: utf-8 -*- 

2"""Proxy/PromiseProxy implementation. 

3 

4This module contains critical utilities that needs to be loaded as 

5soon as possible, and that shall not load any third party modules. 

6 

7Parts of this module is Copyright by Werkzeug Team. 

8""" 

9from __future__ import absolute_import, unicode_literals 

10 

11import operator 

12import sys 

13from functools import reduce 

14from importlib import import_module 

15from types import ModuleType 

16 

17from .five import PY3, bytes_if_py2, items, string, string_t 

18 

19__all__ = ('Proxy', 'PromiseProxy', 'try_import', 'maybe_evaluate') 

20 

21__module__ = __name__ # used by Proxy class body 

22 

23 

24def _default_cls_attr(name, type_, cls_value): 

25 # Proxy uses properties to forward the standard 

26 # class attributes __module__, __name__ and __doc__ to the real 

27 # object, but these needs to be a string when accessed from 

28 # the Proxy class directly. This is a hack to make that work. 

29 # -- See Issue #1087. 

30 

31 def __new__(cls, getter): 

32 instance = type_.__new__(cls, cls_value) 

33 instance.__getter = getter 

34 return instance 

35 

36 def __get__(self, obj, cls=None): 

37 return self.__getter(obj) if obj is not None else self 

38 

39 return type(bytes_if_py2(name), (type_,), { 

40 '__new__': __new__, '__get__': __get__, 

41 }) 

42 

43 

44def try_import(module, default=None): 

45 """Try to import and return module. 

46 

47 Returns None if the module does not exist. 

48 """ 

49 try: 

50 return import_module(module) 

51 except ImportError: 

52 return default 

53 

54 

55class Proxy(object): 

56 """Proxy to another object.""" 

57 

58 # Code stolen from werkzeug.local.Proxy. 

59 __slots__ = ('__local', '__args', '__kwargs', '__dict__') 

60 

61 def __init__(self, local, 

62 args=None, kwargs=None, name=None, __doc__=None): 

63 object.__setattr__(self, '_Proxy__local', local) 

64 object.__setattr__(self, '_Proxy__args', args or ()) 

65 object.__setattr__(self, '_Proxy__kwargs', kwargs or {}) 

66 if name is not None: 

67 object.__setattr__(self, '__custom_name__', name) 

68 if __doc__ is not None: 

69 object.__setattr__(self, '__doc__', __doc__) 

70 

71 @_default_cls_attr('name', str, __name__) 

72 def __name__(self): 

73 try: 

74 return self.__custom_name__ 

75 except AttributeError: 

76 return self._get_current_object().__name__ 

77 

78 @_default_cls_attr('qualname', str, __name__) 

79 def __qualname__(self): 

80 try: 

81 return self.__custom_name__ 

82 except AttributeError: 

83 return self._get_current_object().__qualname__ 

84 

85 @_default_cls_attr('module', str, __module__) 

86 def __module__(self): 

87 return self._get_current_object().__module__ 

88 

89 @_default_cls_attr('doc', str, __doc__) 

90 def __doc__(self): 

91 return self._get_current_object().__doc__ 

92 

93 def _get_class(self): 

94 return self._get_current_object().__class__ 

95 

96 @property 

97 def __class__(self): 

98 return self._get_class() 

99 

100 def _get_current_object(self): 

101 """Get current object. 

102 

103 This is useful if you want the real 

104 object behind the proxy at a time for performance reasons or because 

105 you want to pass the object into a different context. 

106 """ 

107 loc = object.__getattribute__(self, '_Proxy__local') 

108 if not hasattr(loc, '__release_local__'): 

109 return loc(*self.__args, **self.__kwargs) 

110 try: # pragma: no cover 

111 # not sure what this is about 

112 return getattr(loc, self.__name__) 

113 except AttributeError: # pragma: no cover 

114 raise RuntimeError('no object bound to {0.__name__}'.format(self)) 

115 

116 @property 

117 def __dict__(self): 

118 try: 

119 return self._get_current_object().__dict__ 

120 except RuntimeError: # pragma: no cover 

121 raise AttributeError('__dict__') 

122 

123 def __repr__(self): 

124 try: 

125 obj = self._get_current_object() 

126 except RuntimeError: # pragma: no cover 

127 return '<{0} unbound>'.format(self.__class__.__name__) 

128 return repr(obj) 

129 

130 def __bool__(self): 

131 try: 

132 return bool(self._get_current_object()) 

133 except RuntimeError: # pragma: no cover 

134 return False 

135 __nonzero__ = __bool__ # Py2 

136 

137 def __dir__(self): 

138 try: 

139 return dir(self._get_current_object()) 

140 except RuntimeError: # pragma: no cover 

141 return [] 

142 

143 def __getattr__(self, name): 

144 if name == '__members__': 

145 return dir(self._get_current_object()) 

146 return getattr(self._get_current_object(), name) 

147 

148 def __setitem__(self, key, value): 

149 self._get_current_object()[key] = value 

150 

151 def __delitem__(self, key): 

152 del self._get_current_object()[key] 

153 

154 def __setslice__(self, i, j, seq): 

155 self._get_current_object()[i:j] = seq 

156 

157 def __delslice__(self, i, j): 

158 del self._get_current_object()[i:j] 

159 

160 def __setattr__(self, name, value): 

161 setattr(self._get_current_object(), name, value) 

162 

163 def __delattr__(self, name): 

164 delattr(self._get_current_object(), name) 

165 

166 def __str__(self): 

167 return str(self._get_current_object()) 

168 

169 def __lt__(self, other): 

170 return self._get_current_object() < other 

171 

172 def __le__(self, other): 

173 return self._get_current_object() <= other 

174 

175 def __eq__(self, other): 

176 return self._get_current_object() == other 

177 

178 def __ne__(self, other): 

179 return self._get_current_object() != other 

180 

181 def __gt__(self, other): 

182 return self._get_current_object() > other 

183 

184 def __ge__(self, other): 

185 return self._get_current_object() >= other 

186 

187 def __hash__(self): 

188 return hash(self._get_current_object()) 

189 

190 def __call__(self, *a, **kw): 

191 return self._get_current_object()(*a, **kw) 

192 

193 def __len__(self): 

194 return len(self._get_current_object()) 

195 

196 def __getitem__(self, i): 

197 return self._get_current_object()[i] 

198 

199 def __iter__(self): 

200 return iter(self._get_current_object()) 

201 

202 def __contains__(self, i): 

203 return i in self._get_current_object() 

204 

205 def __getslice__(self, i, j): 

206 return self._get_current_object()[i:j] 

207 

208 def __add__(self, other): 

209 return self._get_current_object() + other 

210 

211 def __sub__(self, other): 

212 return self._get_current_object() - other 

213 

214 def __mul__(self, other): 

215 return self._get_current_object() * other 

216 

217 def __floordiv__(self, other): 

218 return self._get_current_object() // other 

219 

220 def __mod__(self, other): 

221 return self._get_current_object() % other 

222 

223 def __divmod__(self, other): 

224 return self._get_current_object().__divmod__(other) 

225 

226 def __pow__(self, other): 

227 return self._get_current_object() ** other 

228 

229 def __lshift__(self, other): 

230 return self._get_current_object() << other 

231 

232 def __rshift__(self, other): 

233 return self._get_current_object() >> other 

234 

235 def __and__(self, other): 

236 return self._get_current_object() & other 

237 

238 def __xor__(self, other): 

239 return self._get_current_object() ^ other 

240 

241 def __or__(self, other): 

242 return self._get_current_object() | other 

243 

244 def __div__(self, other): 

245 return self._get_current_object().__div__(other) 

246 

247 def __truediv__(self, other): 

248 return self._get_current_object().__truediv__(other) 

249 

250 def __neg__(self): 

251 return -(self._get_current_object()) 

252 

253 def __pos__(self): 

254 return +(self._get_current_object()) 

255 

256 def __abs__(self): 

257 return abs(self._get_current_object()) 

258 

259 def __invert__(self): 

260 return ~(self._get_current_object()) 

261 

262 def __complex__(self): 

263 return complex(self._get_current_object()) 

264 

265 def __int__(self): 

266 return int(self._get_current_object()) 

267 

268 def __float__(self): 

269 return float(self._get_current_object()) 

270 

271 def __oct__(self): 

272 return oct(self._get_current_object()) 

273 

274 def __hex__(self): 

275 return hex(self._get_current_object()) 

276 

277 def __index__(self): 

278 return self._get_current_object().__index__() 

279 

280 def __coerce__(self, other): 

281 return self._get_current_object().__coerce__(other) 

282 

283 def __enter__(self): 

284 return self._get_current_object().__enter__() 

285 

286 def __exit__(self, *a, **kw): 

287 return self._get_current_object().__exit__(*a, **kw) 

288 

289 def __reduce__(self): 

290 return self._get_current_object().__reduce__() 

291 

292 if not PY3: # pragma: no cover 

293 def __cmp__(self, other): 

294 return cmp(self._get_current_object(), other) # noqa 

295 

296 def __long__(self): 

297 return long(self._get_current_object()) # noqa 

298 

299 def __unicode__(self): 

300 try: 

301 return string(self._get_current_object()) 

302 except RuntimeError: # pragma: no cover 

303 return repr(self) 

304 

305 

306class PromiseProxy(Proxy): 

307 """Proxy that evaluates object once. 

308 

309 :class:`Proxy` will evaluate the object each time, while the 

310 promise will only evaluate it once. 

311 """ 

312 

313 __slots__ = ('__pending__', '__weakref__') 

314 

315 def _get_current_object(self): 

316 try: 

317 return object.__getattribute__(self, '__thing') 

318 except AttributeError: 

319 return self.__evaluate__() 

320 

321 def __then__(self, fun, *args, **kwargs): 

322 if self.__evaluated__(): 

323 return fun(*args, **kwargs) 

324 from collections import deque 

325 try: 

326 pending = object.__getattribute__(self, '__pending__') 

327 except AttributeError: 

328 pending = None 

329 if pending is None: 

330 pending = deque() 

331 object.__setattr__(self, '__pending__', pending) 

332 pending.append((fun, args, kwargs)) 

333 

334 def __evaluated__(self): 

335 try: 

336 object.__getattribute__(self, '__thing') 

337 except AttributeError: 

338 return False 

339 return True 

340 

341 def __maybe_evaluate__(self): 

342 return self._get_current_object() 

343 

344 def __evaluate__(self, 

345 _clean=('_Proxy__local', 

346 '_Proxy__args', 

347 '_Proxy__kwargs')): 

348 try: 

349 thing = Proxy._get_current_object(self) 

350 except Exception: 

351 raise 

352 else: 

353 object.__setattr__(self, '__thing', thing) 

354 for attr in _clean: 

355 try: 

356 object.__delattr__(self, attr) 

357 except AttributeError: # pragma: no cover 

358 # May mask errors so ignore 

359 pass 

360 try: 

361 pending = object.__getattribute__(self, '__pending__') 

362 except AttributeError: 

363 pass 

364 else: 

365 try: 

366 while pending: 

367 fun, args, kwargs = pending.popleft() 

368 fun(*args, **kwargs) 

369 finally: 

370 try: 

371 object.__delattr__(self, '__pending__') 

372 except AttributeError: # pragma: no cover 

373 pass 

374 return thing 

375 

376 

377def maybe_evaluate(obj): 

378 """Attempt to evaluate promise, even if obj is not a promise.""" 

379 try: 

380 return obj.__maybe_evaluate__() 

381 except AttributeError: 

382 return obj 

383 

384# ############# Module Generation ########################## 

385 

386# Utilities to dynamically 

387# recreate modules, either for lazy loading or 

388# to create old modules at runtime instead of 

389# having them litter the source tree. 

390 

391# import fails in python 2.5. fallback to reduce in stdlib 

392 

393 

394MODULE_DEPRECATED = """ 

395The module %s is deprecated and will be removed in a future version. 

396""" 

397 

398DEFAULT_ATTRS = {'__file__', '__path__', '__doc__', '__all__'} 

399 

400# im_func is no longer available in Py3. 

401# instead the unbound method itself can be used. 

402if sys.version_info[0] == 3: # pragma: no cover 

403 def fun_of_method(method): 

404 return method 

405else: 

406 def fun_of_method(method): # noqa 

407 return method.im_func 

408 

409 

410def getappattr(path): 

411 """Get attribute from current_app recursively. 

412 

413 Example: ``getappattr('amqp.get_task_consumer')``. 

414 

415 """ 

416 from celery import current_app 

417 return current_app._rgetattr(path) 

418 

419 

420def _compat_periodic_task_decorator(*args, **kwargs): 

421 from celery.task import periodic_task 

422 return periodic_task(*args, **kwargs) 

423 

424 

425COMPAT_MODULES = { 

426 'celery': { 

427 'execute': { 

428 'send_task': 'send_task', 

429 }, 

430 'decorators': { 

431 'task': 'task', 

432 'periodic_task': _compat_periodic_task_decorator, 

433 }, 

434 'log': { 

435 'get_default_logger': 'log.get_default_logger', 

436 'setup_logger': 'log.setup_logger', 

437 'setup_logging_subsystem': 'log.setup_logging_subsystem', 

438 'redirect_stdouts_to_logger': 'log.redirect_stdouts_to_logger', 

439 }, 

440 'messaging': { 

441 'TaskConsumer': 'amqp.TaskConsumer', 

442 'establish_connection': 'connection', 

443 'get_consumer_set': 'amqp.TaskConsumer', 

444 }, 

445 'registry': { 

446 'tasks': 'tasks', 

447 }, 

448 }, 

449 'celery.task': { 

450 'control': { 

451 'broadcast': 'control.broadcast', 

452 'rate_limit': 'control.rate_limit', 

453 'time_limit': 'control.time_limit', 

454 'ping': 'control.ping', 

455 'revoke': 'control.revoke', 

456 'discard_all': 'control.purge', 

457 'inspect': 'control.inspect', 

458 }, 

459 'schedules': 'celery.schedules', 

460 'chords': 'celery.canvas', 

461 } 

462} 

463 

464#: We exclude these from dir(celery) 

465DEPRECATED_ATTRS = set(COMPAT_MODULES['celery'].keys()) | {'subtask'} 

466 

467 

468class class_property(object): 

469 

470 def __init__(self, getter=None, setter=None): 

471 if getter is not None and not isinstance(getter, classmethod): 

472 getter = classmethod(getter) 

473 if setter is not None and not isinstance(setter, classmethod): 

474 setter = classmethod(setter) 

475 self.__get = getter 

476 self.__set = setter 

477 

478 info = getter.__get__(object) # just need the info attrs. 

479 self.__doc__ = info.__doc__ 

480 self.__name__ = info.__name__ 

481 self.__module__ = info.__module__ 

482 

483 def __get__(self, obj, type=None): 

484 if obj and type is None: 

485 type = obj.__class__ 

486 return self.__get.__get__(obj, type)() 

487 

488 def __set__(self, obj, value): 

489 if obj is None: 

490 return self 

491 return self.__set.__get__(obj)(value) 

492 

493 def setter(self, setter): 

494 return self.__class__(self.__get, setter) 

495 

496 

497def reclassmethod(method): 

498 return classmethod(fun_of_method(method)) 

499 

500 

501class LazyModule(ModuleType): 

502 _compat_modules = () 

503 _all_by_module = {} 

504 _direct = {} 

505 _object_origins = {} 

506 

507 def __getattr__(self, name): 

508 if name in self._object_origins: 

509 module = __import__(self._object_origins[name], None, None, [name]) 

510 for item in self._all_by_module[module.__name__]: 

511 setattr(self, item, getattr(module, item)) 

512 return getattr(module, name) 

513 elif name in self._direct: # pragma: no cover 

514 module = __import__(self._direct[name], None, None, [name]) 

515 setattr(self, name, module) 

516 return module 

517 return ModuleType.__getattribute__(self, name) 

518 

519 def __dir__(self): 

520 return [ 

521 attr for attr in set(self.__all__) | DEFAULT_ATTRS 

522 if attr not in DEPRECATED_ATTRS 

523 ] 

524 

525 def __reduce__(self): 

526 return import_module, (self.__name__,) 

527 

528 

529def create_module(name, attrs, cls_attrs=None, pkg=None, 

530 base=LazyModule, prepare_attr=None): 

531 fqdn = '.'.join([pkg.__name__, name]) if pkg else name 

532 cls_attrs = {} if cls_attrs is None else cls_attrs 

533 pkg, _, modname = name.rpartition('.') 

534 cls_attrs['__module__'] = pkg 

535 

536 attrs = { 

537 attr_name: (prepare_attr(attr) if prepare_attr else attr) 

538 for attr_name, attr in items(attrs) 

539 } 

540 module = sys.modules[fqdn] = type( 

541 bytes_if_py2(modname), (base,), cls_attrs)(bytes_if_py2(name)) 

542 module.__dict__.update(attrs) 

543 return module 

544 

545 

546def recreate_module(name, compat_modules=None, by_module=None, direct=None, 

547 base=LazyModule, **attrs): 

548 compat_modules = compat_modules or () 

549 by_module = by_module or {} 

550 direct = direct or {} 

551 old_module = sys.modules[name] 

552 origins = get_origins(by_module) 

553 compat_modules = COMPAT_MODULES.get(name, ()) 

554 

555 _all = tuple(set(reduce( 

556 operator.add, 

557 [tuple(v) for v in [compat_modules, origins, direct, attrs]], 

558 ))) 

559 if sys.version_info[0] < 3: 

560 _all = [s.encode() for s in _all] 

561 cattrs = { 

562 '_compat_modules': compat_modules, 

563 '_all_by_module': by_module, '_direct': direct, 

564 '_object_origins': origins, 

565 '__all__': _all, 

566 } 

567 new_module = create_module(name, attrs, cls_attrs=cattrs, base=base) 

568 new_module.__dict__.update({ 

569 mod: get_compat_module(new_module, mod) for mod in compat_modules 

570 }) 

571 return old_module, new_module 

572 

573 

574def get_compat_module(pkg, name): 

575 def prepare(attr): 

576 if isinstance(attr, string_t): 

577 return Proxy(getappattr, (attr,)) 

578 return attr 

579 

580 attrs = COMPAT_MODULES[pkg.__name__][name] 

581 if isinstance(attrs, string_t): 

582 fqdn = '.'.join([pkg.__name__, name]) 

583 module = sys.modules[fqdn] = import_module(attrs) 

584 return module 

585 attrs[bytes_if_py2('__all__')] = list(attrs) 

586 return create_module(name, dict(attrs), pkg=pkg, prepare_attr=prepare) 

587 

588 

589def get_origins(defs): 

590 origins = {} 

591 for module, attrs in items(defs): 

592 origins.update({attr: module for attr in attrs}) 

593 return origins