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############################################################################## 

2# 

3# Copyright (c) 2004 Zope Foundation and Contributors. 

4# All Rights Reserved. 

5# 

6# This software is subject to the provisions of the Zope Public License, 

7# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. 

8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED 

9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 

10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 

11# FOR A PARTICULAR PURPOSE. 

12# 

13############################################################################## 

14"""Adapter management 

15""" 

16import itertools 

17import weakref 

18 

19from zope.interface import implementer 

20from zope.interface import providedBy 

21from zope.interface import Interface 

22from zope.interface import ro 

23from zope.interface.interfaces import IAdapterRegistry 

24 

25from zope.interface._compat import _normalize_name 

26from zope.interface._compat import STRING_TYPES 

27from zope.interface._compat import _use_c_impl 

28 

29__all__ = [ 

30 'AdapterRegistry', 

31 'VerifyingAdapterRegistry', 

32] 

33 

34# In the CPython implementation, 

35# ``tuple`` and ``list`` cooperate so that ``tuple([some list])`` 

36# directly allocates and iterates at the C level without using a 

37# Python iterator. That's not the case for 

38# ``tuple(generator_expression)`` or ``tuple(map(func, it))``. 

39## 

40# 3.8 

41# ``tuple([t for t in range(10)])`` -> 610ns 

42# ``tuple(t for t in range(10))`` -> 696ns 

43# ``tuple(map(lambda t: t, range(10)))`` -> 881ns 

44## 

45# 2.7 

46# ``tuple([t fon t in range(10)])`` -> 625ns 

47# ``tuple(t for t in range(10))`` -> 665ns 

48# ``tuple(map(lambda t: t, range(10)))`` -> 958ns 

49# 

50# All three have substantial variance. 

51## 

52# On PyPy, this is also the best option. 

53## 

54# PyPy 2.7.18-7.3.3 

55# ``tuple([t fon t in range(10)])`` -> 128ns 

56# ``tuple(t for t in range(10))`` -> 175ns 

57# ``tuple(map(lambda t: t, range(10)))`` -> 153ns 

58## 

59# PyPy 3.7.9 7.3.3-beta 

60# ``tuple([t fon t in range(10)])`` -> 82ns 

61# ``tuple(t for t in range(10))`` -> 177ns 

62# ``tuple(map(lambda t: t, range(10)))`` -> 168ns 

63# 

64 

65class BaseAdapterRegistry(object): 

66 """ 

67 A basic implementation of the data storage and algorithms required 

68 for a :class:`zope.interface.interfaces.IAdapterRegistry`. 

69 

70 Subclasses can set the following attributes to control how the data 

71 is stored; in particular, these hooks can be helpful for ZODB 

72 persistence. They can be class attributes that are the named (or similar) type, or 

73 they can be methods that act as a constructor for an object that behaves 

74 like the types defined here; this object will not assume that they are type 

75 objects, but subclasses are free to do so: 

76 

77 _sequenceType = list 

78 This is the type used for our two mutable top-level "byorder" sequences. 

79 Must support mutation operations like ``append()`` and ``del seq[index]``. 

80 These are usually small (< 10). Although at least one of them is 

81 accessed when performing lookups or queries on this object, the other 

82 is untouched. In many common scenarios, both are only required when 

83 mutating registrations and subscriptions (like what 

84 :meth:`zope.interface.interfaces.IComponents.registerUtility` does). 

85 This use pattern makes it an ideal candidate to be a 

86 :class:`~persistent.list.PersistentList`. 

87 _leafSequenceType = tuple 

88 This is the type used for the leaf sequences of subscribers. 

89 It could be set to a ``PersistentList`` to avoid many unnecessary data 

90 loads when subscribers aren't being used. Mutation operations are directed 

91 through :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`; if you use 

92 a mutable type, you'll need to override those. 

93 _mappingType = dict 

94 This is the mutable mapping type used for the keyed mappings. 

95 A :class:`~persistent.mapping.PersistentMapping` 

96 could be used to help reduce the number of data loads when the registry is large 

97 and parts of it are rarely used. Further reductions in data loads can come from 

98 using a :class:`~BTrees.OOBTree.OOBTree`, but care is required 

99 to be sure that all required/provided 

100 values are fully ordered (e.g., no required or provided values that are classes 

101 can be used). 

102 _providedType = dict 

103 This is the mutable mapping type used for the ``_provided`` mapping. 

104 This is separate from the generic mapping type because the values 

105 are always integers, so one might choose to use a more optimized data 

106 structure such as a :class:`~BTrees.OIBTree.OIBTree`. 

107 The same caveats regarding key types 

108 apply as for ``_mappingType``. 

109 

110 It is possible to also set these on an instance, but because of the need to 

111 potentially also override :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`, 

112 this may be less useful in a persistent scenario; using a subclass is recommended. 

113 

114 .. versionchanged:: 5.3.0 

115 Add support for customizing the way internal data 

116 structures are created. 

117 .. versionchanged:: 5.3.0 

118 Add methods :meth:`rebuild`, :meth:`allRegistrations` 

119 and :meth:`allSubscriptions`. 

120 """ 

121 

122 # List of methods copied from lookup sub-objects: 

123 _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter', 

124 'adapter_hook', 'lookupAll', 'names', 

125 'subscriptions', 'subscribers') 

126 

127 # All registries maintain a generation that can be used by verifying 

128 # registries 

129 _generation = 0 

130 

131 def __init__(self, bases=()): 

132 

133 # The comments here could be improved. Possibly this bit needs 

134 # explaining in a separate document, as the comments here can 

135 # be quite confusing. /regebro 

136 

137 # {order -> {required -> {provided -> {name -> value}}}} 

138 # Here "order" is actually an index in a list, "required" and 

139 # "provided" are interfaces, and "required" is really a nested 

140 # key. So, for example: 

141 # for order == 0 (that is, self._adapters[0]), we have: 

142 # {provided -> {name -> value}} 

143 # but for order == 2 (that is, self._adapters[2]), we have: 

144 # {r1 -> {r2 -> {provided -> {name -> value}}}} 

145 # 

146 self._adapters = self._sequenceType() 

147 

148 # {order -> {required -> {provided -> {name -> [value]}}}} 

149 # where the remarks about adapters above apply 

150 self._subscribers = self._sequenceType() 

151 

152 # Set, with a reference count, keeping track of the interfaces 

153 # for which we have provided components: 

154 self._provided = self._providedType() 

155 

156 # Create ``_v_lookup`` object to perform lookup. We make this a 

157 # separate object to to make it easier to implement just the 

158 # lookup functionality in C. This object keeps track of cache 

159 # invalidation data in two kinds of registries. 

160 

161 # Invalidating registries have caches that are invalidated 

162 # when they or their base registies change. An invalidating 

163 # registry can only have invalidating registries as bases. 

164 # See LookupBaseFallback below for the pertinent logic. 

165 

166 # Verifying registies can't rely on getting invalidation messages, 

167 # so have to check the generations of base registries to determine 

168 # if their cache data are current. See VerifyingBasePy below 

169 # for the pertinent object. 

170 self._createLookup() 

171 

172 # Setting the bases causes the registries described above 

173 # to be initialized (self._setBases -> self.changed -> 

174 # self._v_lookup.changed). 

175 

176 self.__bases__ = bases 

177 

178 def _setBases(self, bases): 

179 """ 

180 If subclasses need to track when ``__bases__`` changes, they 

181 can override this method. 

182 

183 Subclasses must still call this method. 

184 """ 

185 self.__dict__['__bases__'] = bases 

186 self.ro = ro.ro(self) 

187 self.changed(self) 

188 

189 __bases__ = property(lambda self: self.__dict__['__bases__'], 

190 lambda self, bases: self._setBases(bases), 

191 ) 

192 

193 def _createLookup(self): 

194 self._v_lookup = self.LookupClass(self) 

195 for name in self._delegated: 

196 self.__dict__[name] = getattr(self._v_lookup, name) 

197 

198 # Hooks for subclasses to define the types of objects used in 

199 # our data structures. 

200 # These have to be documented in the docstring, instead of local 

201 # comments, because Sphinx autodoc ignores the comment and just writes 

202 # "alias of list" 

203 _sequenceType = list 

204 _leafSequenceType = tuple 

205 _mappingType = dict 

206 _providedType = dict 

207 

208 def _addValueToLeaf(self, existing_leaf_sequence, new_item): 

209 """ 

210 Add the value *new_item* to the *existing_leaf_sequence*, which may 

211 be ``None``. 

212 

213 Subclasses that redefine `_leafSequenceType` should override this method. 

214 

215 :param existing_leaf_sequence: 

216 If *existing_leaf_sequence* is not *None*, it will be an instance 

217 of `_leafSequenceType`. (Unless the object has been unpickled 

218 from an old pickle and the class definition has changed, in which case 

219 it may be an instance of a previous definition, commonly a `tuple`.) 

220 

221 :return: 

222 This method returns the new value to be stored. It may mutate the 

223 sequence in place if it was not ``None`` and the type is mutable, but 

224 it must also return it. 

225 

226 .. versionadded:: 5.3.0 

227 """ 

228 if existing_leaf_sequence is None: 

229 return (new_item,) 

230 return existing_leaf_sequence + (new_item,) 

231 

232 def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove): 

233 """ 

234 Remove the item *to_remove* from the (non-``None``, non-empty) 

235 *existing_leaf_sequence* and return the mutated sequence. 

236 

237 If there is more than one item that is equal to *to_remove* 

238 they must all be removed. 

239 

240 Subclasses that redefine `_leafSequenceType` should override 

241 this method. Note that they can call this method to help 

242 in their implementation; this implementation will always 

243 return a new tuple constructed by iterating across 

244 the *existing_leaf_sequence* and omitting items equal to *to_remove*. 

245 

246 :param existing_leaf_sequence: 

247 As for `_addValueToLeaf`, probably an instance of 

248 `_leafSequenceType` but possibly an older type; never `None`. 

249 :return: 

250 A version of *existing_leaf_sequence* with all items equal to 

251 *to_remove* removed. Must not return `None`. However, 

252 returning an empty 

253 object, even of another type such as the empty tuple, ``()`` is 

254 explicitly allowed; such an object will never be stored. 

255 

256 .. versionadded:: 5.3.0 

257 """ 

258 return tuple([v for v in existing_leaf_sequence if v != to_remove]) 

259 

260 def changed(self, originally_changed): 

261 self._generation += 1 

262 self._v_lookup.changed(originally_changed) 

263 

264 def register(self, required, provided, name, value): 

265 if not isinstance(name, STRING_TYPES): 

266 raise ValueError('name is not a string') 

267 if value is None: 

268 self.unregister(required, provided, name, value) 

269 return 

270 

271 required = tuple([_convert_None_to_Interface(r) for r in required]) 

272 name = _normalize_name(name) 

273 order = len(required) 

274 byorder = self._adapters 

275 while len(byorder) <= order: 

276 byorder.append(self._mappingType()) 

277 components = byorder[order] 

278 key = required + (provided,) 

279 

280 for k in key: 

281 d = components.get(k) 

282 if d is None: 

283 d = self._mappingType() 

284 components[k] = d 

285 components = d 

286 

287 if components.get(name) is value: 

288 return 

289 

290 components[name] = value 

291 

292 n = self._provided.get(provided, 0) + 1 

293 self._provided[provided] = n 

294 if n == 1: 

295 self._v_lookup.add_extendor(provided) 

296 

297 self.changed(self) 

298 

299 def _find_leaf(self, byorder, required, provided, name): 

300 # Find the leaf value, if any, in the *byorder* list 

301 # for the interface sequence *required* and the interface 

302 # *provided*, given the already normalized *name*. 

303 # 

304 # If no such leaf value exists, returns ``None`` 

305 required = tuple([_convert_None_to_Interface(r) for r in required]) 

306 order = len(required) 

307 if len(byorder) <= order: 

308 return None 

309 

310 components = byorder[order] 

311 key = required + (provided,) 

312 

313 for k in key: 

314 d = components.get(k) 

315 if d is None: 

316 return None 

317 components = d 

318 

319 return components.get(name) 

320 

321 def registered(self, required, provided, name=u''): 

322 return self._find_leaf( 

323 self._adapters, 

324 required, 

325 provided, 

326 _normalize_name(name) 

327 ) 

328 

329 @classmethod 

330 def _allKeys(cls, components, i, parent_k=()): 

331 if i == 0: 

332 for k, v in components.items(): 

333 yield parent_k + (k,), v 

334 else: 

335 for k, v in components.items(): 

336 new_parent_k = parent_k + (k,) 

337 for x, y in cls._allKeys(v, i - 1, new_parent_k): 

338 yield x, y 

339 

340 def _all_entries(self, byorder): 

341 # Recurse through the mapping levels of the `byorder` sequence, 

342 # reconstructing a flattened sequence of ``(required, provided, name, value)`` 

343 # tuples that can be used to reconstruct the sequence with the appropriate 

344 # registration methods. 

345 # 

346 # Locally reference the `byorder` data; it might be replaced while 

347 # this method is running (see ``rebuild``). 

348 for i, components in enumerate(byorder): 

349 # We will have *i* levels of dictionaries to go before 

350 # we get to the leaf. 

351 for key, value in self._allKeys(components, i + 1): 

352 assert len(key) == i + 2 

353 required = key[:i] 

354 provided = key[-2] 

355 name = key[-1] 

356 yield (required, provided, name, value) 

357 

358 def allRegistrations(self): 

359 """ 

360 Yields tuples ``(required, provided, name, value)`` for all 

361 the registrations that this object holds. 

362 

363 These tuples could be passed as the arguments to the 

364 :meth:`register` method on another adapter registry to 

365 duplicate the registrations this object holds. 

366 

367 .. versionadded:: 5.3.0 

368 """ 

369 for t in self._all_entries(self._adapters): 

370 yield t 

371 

372 def unregister(self, required, provided, name, value=None): 

373 required = tuple([_convert_None_to_Interface(r) for r in required]) 

374 order = len(required) 

375 byorder = self._adapters 

376 if order >= len(byorder): 

377 return False 

378 components = byorder[order] 

379 key = required + (provided,) 

380 

381 # Keep track of how we got to `components`: 

382 lookups = [] 

383 for k in key: 

384 d = components.get(k) 

385 if d is None: 

386 return 

387 lookups.append((components, k)) 

388 components = d 

389 

390 old = components.get(name) 

391 if old is None: 

392 return 

393 if (value is not None) and (old is not value): 

394 return 

395 

396 del components[name] 

397 if not components: 

398 # Clean out empty containers, since we don't want our keys 

399 # to reference global objects (interfaces) unnecessarily. 

400 # This is often a problem when an interface is slated for 

401 # removal; a hold-over entry in the registry can make it 

402 # difficult to remove such interfaces. 

403 for comp, k in reversed(lookups): 

404 d = comp[k] 

405 if d: 

406 break 

407 else: 

408 del comp[k] 

409 while byorder and not byorder[-1]: 

410 del byorder[-1] 

411 n = self._provided[provided] - 1 

412 if n == 0: 

413 del self._provided[provided] 

414 self._v_lookup.remove_extendor(provided) 

415 else: 

416 self._provided[provided] = n 

417 

418 self.changed(self) 

419 

420 def subscribe(self, required, provided, value): 

421 required = tuple([_convert_None_to_Interface(r) for r in required]) 

422 name = u'' 

423 order = len(required) 

424 byorder = self._subscribers 

425 while len(byorder) <= order: 

426 byorder.append(self._mappingType()) 

427 components = byorder[order] 

428 key = required + (provided,) 

429 

430 for k in key: 

431 d = components.get(k) 

432 if d is None: 

433 d = self._mappingType() 

434 components[k] = d 

435 components = d 

436 

437 components[name] = self._addValueToLeaf(components.get(name), value) 

438 

439 if provided is not None: 

440 n = self._provided.get(provided, 0) + 1 

441 self._provided[provided] = n 

442 if n == 1: 

443 self._v_lookup.add_extendor(provided) 

444 

445 self.changed(self) 

446 

447 def subscribed(self, required, provided, subscriber): 

448 subscribers = self._find_leaf( 

449 self._subscribers, 

450 required, 

451 provided, 

452 u'' 

453 ) or () 

454 return subscriber if subscriber in subscribers else None 

455 

456 def allSubscriptions(self): 

457 """ 

458 Yields tuples ``(required, provided, value)`` for all the 

459 subscribers that this object holds. 

460 

461 These tuples could be passed as the arguments to the 

462 :meth:`subscribe` method on another adapter registry to 

463 duplicate the registrations this object holds. 

464 

465 .. versionadded:: 5.3.0 

466 """ 

467 for required, provided, _name, value in self._all_entries(self._subscribers): 

468 for v in value: 

469 yield (required, provided, v) 

470 

471 def unsubscribe(self, required, provided, value=None): 

472 required = tuple([_convert_None_to_Interface(r) for r in required]) 

473 order = len(required) 

474 byorder = self._subscribers 

475 if order >= len(byorder): 

476 return 

477 components = byorder[order] 

478 key = required + (provided,) 

479 

480 # Keep track of how we got to `components`: 

481 lookups = [] 

482 for k in key: 

483 d = components.get(k) 

484 if d is None: 

485 return 

486 lookups.append((components, k)) 

487 components = d 

488 

489 old = components.get(u'') 

490 if not old: 

491 # this is belt-and-suspenders against the failure of cleanup below 

492 return # pragma: no cover 

493 len_old = len(old) 

494 if value is None: 

495 # Removing everything; note that the type of ``new`` won't 

496 # necessarily match the ``_leafSequenceType``, but that's 

497 # OK because we're about to delete the entire entry 

498 # anyway. 

499 new = () 

500 else: 

501 new = self._removeValueFromLeaf(old, value) 

502 # ``new`` may be the same object as ``old``, just mutated in place, 

503 # so we cannot compare it to ``old`` to check for changes. Remove 

504 # our reference to it now to avoid trying to do so below. 

505 del old 

506 

507 if len(new) == len_old: 

508 # No changes, so nothing could have been removed. 

509 return 

510 

511 if new: 

512 components[u''] = new 

513 else: 

514 # Instead of setting components[u''] = new, we clean out 

515 # empty containers, since we don't want our keys to 

516 # reference global objects (interfaces) unnecessarily. This 

517 # is often a problem when an interface is slated for 

518 # removal; a hold-over entry in the registry can make it 

519 # difficult to remove such interfaces. 

520 del components[u''] 

521 for comp, k in reversed(lookups): 

522 d = comp[k] 

523 if d: 

524 break 

525 else: 

526 del comp[k] 

527 while byorder and not byorder[-1]: 

528 del byorder[-1] 

529 

530 if provided is not None: 

531 n = self._provided[provided] + len(new) - len_old 

532 if n == 0: 

533 del self._provided[provided] 

534 self._v_lookup.remove_extendor(provided) 

535 else: 

536 self._provided[provided] = n 

537 

538 self.changed(self) 

539 

540 def rebuild(self): 

541 """ 

542 Rebuild (and replace) all the internal data structures of this 

543 object. 

544 

545 This is useful, especially for persistent implementations, if 

546 you suspect an issue with reference counts keeping interfaces 

547 alive even though they are no longer used. 

548 

549 It is also useful if you or a subclass change the data types 

550 (``_mappingType`` and friends) that are to be used. 

551 

552 This method replaces all internal data structures with new objects; 

553 it specifically does not re-use any storage. 

554 

555 .. versionadded:: 5.3.0 

556 """ 

557 

558 # Grab the iterators, we're about to discard their data. 

559 registrations = self.allRegistrations() 

560 subscriptions = self.allSubscriptions() 

561 

562 def buffer(it): 

563 # The generator doesn't actually start running until we 

564 # ask for its next(), by which time the attributes will change 

565 # unless we do so before calling __init__. 

566 try: 

567 first = next(it) 

568 except StopIteration: 

569 return iter(()) 

570 

571 return itertools.chain((first,), it) 

572 

573 registrations = buffer(registrations) 

574 subscriptions = buffer(subscriptions) 

575 

576 

577 # Replace the base data structures as well as _v_lookup. 

578 self.__init__(self.__bases__) 

579 # Re-register everything previously registered and subscribed. 

580 # 

581 # XXX: This is going to call ``self.changed()`` a lot, all of 

582 # which is unnecessary (because ``self.__init__`` just 

583 # re-created those dependent objects and also called 

584 # ``self.changed()``). Is this a bottleneck that needs fixed? 

585 # (We could do ``self.changed = lambda _: None`` before 

586 # beginning and remove it after to disable the presumably expensive 

587 # part of passing that notification to the change of objects.) 

588 for args in registrations: 

589 self.register(*args) 

590 for args in subscriptions: 

591 self.subscribe(*args) 

592 

593 # XXX hack to fake out twisted's use of a private api. We need to get them 

594 # to use the new registed method. 

595 def get(self, _): # pragma: no cover 

596 class XXXTwistedFakeOut: 

597 selfImplied = {} 

598 return XXXTwistedFakeOut 

599 

600 

601_not_in_mapping = object() 

602 

603@_use_c_impl 

604class LookupBase(object): 

605 

606 def __init__(self): 

607 self._cache = {} 

608 self._mcache = {} 

609 self._scache = {} 

610 

611 def changed(self, ignored=None): 

612 self._cache.clear() 

613 self._mcache.clear() 

614 self._scache.clear() 

615 

616 def _getcache(self, provided, name): 

617 cache = self._cache.get(provided) 

618 if cache is None: 

619 cache = {} 

620 self._cache[provided] = cache 

621 if name: 

622 c = cache.get(name) 

623 if c is None: 

624 c = {} 

625 cache[name] = c 

626 cache = c 

627 return cache 

628 

629 def lookup(self, required, provided, name=u'', default=None): 

630 if not isinstance(name, STRING_TYPES): 

631 raise ValueError('name is not a string') 

632 cache = self._getcache(provided, name) 

633 required = tuple(required) 

634 if len(required) == 1: 

635 result = cache.get(required[0], _not_in_mapping) 

636 else: 

637 result = cache.get(tuple(required), _not_in_mapping) 

638 

639 if result is _not_in_mapping: 

640 result = self._uncached_lookup(required, provided, name) 

641 if len(required) == 1: 

642 cache[required[0]] = result 

643 else: 

644 cache[tuple(required)] = result 

645 

646 if result is None: 

647 return default 

648 

649 return result 

650 

651 def lookup1(self, required, provided, name=u'', default=None): 

652 if not isinstance(name, STRING_TYPES): 

653 raise ValueError('name is not a string') 

654 cache = self._getcache(provided, name) 

655 result = cache.get(required, _not_in_mapping) 

656 if result is _not_in_mapping: 

657 return self.lookup((required, ), provided, name, default) 

658 

659 if result is None: 

660 return default 

661 

662 return result 

663 

664 def queryAdapter(self, object, provided, name=u'', default=None): 

665 return self.adapter_hook(provided, object, name, default) 

666 

667 def adapter_hook(self, provided, object, name=u'', default=None): 

668 if not isinstance(name, STRING_TYPES): 

669 raise ValueError('name is not a string') 

670 required = providedBy(object) 

671 cache = self._getcache(provided, name) 

672 factory = cache.get(required, _not_in_mapping) 

673 if factory is _not_in_mapping: 

674 factory = self.lookup((required, ), provided, name) 

675 

676 if factory is not None: 

677 if isinstance(object, super): 

678 object = object.__self__ 

679 result = factory(object) 

680 if result is not None: 

681 return result 

682 

683 return default 

684 

685 def lookupAll(self, required, provided): 

686 cache = self._mcache.get(provided) 

687 if cache is None: 

688 cache = {} 

689 self._mcache[provided] = cache 

690 

691 required = tuple(required) 

692 result = cache.get(required, _not_in_mapping) 

693 if result is _not_in_mapping: 

694 result = self._uncached_lookupAll(required, provided) 

695 cache[required] = result 

696 

697 return result 

698 

699 

700 def subscriptions(self, required, provided): 

701 cache = self._scache.get(provided) 

702 if cache is None: 

703 cache = {} 

704 self._scache[provided] = cache 

705 

706 required = tuple(required) 

707 result = cache.get(required, _not_in_mapping) 

708 if result is _not_in_mapping: 

709 result = self._uncached_subscriptions(required, provided) 

710 cache[required] = result 

711 

712 return result 

713 

714 

715@_use_c_impl 

716class VerifyingBase(LookupBaseFallback): 

717 # Mixin for lookups against registries which "chain" upwards, and 

718 # whose lookups invalidate their own caches whenever a parent registry 

719 # bumps its own '_generation' counter. E.g., used by 

720 # zope.component.persistentregistry 

721 

722 def changed(self, originally_changed): 

723 LookupBaseFallback.changed(self, originally_changed) 

724 self._verify_ro = self._registry.ro[1:] 

725 self._verify_generations = [r._generation for r in self._verify_ro] 

726 

727 def _verify(self): 

728 if ([r._generation for r in self._verify_ro] 

729 != self._verify_generations): 

730 self.changed(None) 

731 

732 def _getcache(self, provided, name): 

733 self._verify() 

734 return LookupBaseFallback._getcache(self, provided, name) 

735 

736 def lookupAll(self, required, provided): 

737 self._verify() 

738 return LookupBaseFallback.lookupAll(self, required, provided) 

739 

740 def subscriptions(self, required, provided): 

741 self._verify() 

742 return LookupBaseFallback.subscriptions(self, required, provided) 

743 

744 

745class AdapterLookupBase(object): 

746 

747 def __init__(self, registry): 

748 self._registry = registry 

749 self._required = {} 

750 self.init_extendors() 

751 super(AdapterLookupBase, self).__init__() 

752 

753 def changed(self, ignored=None): 

754 super(AdapterLookupBase, self).changed(None) 

755 for r in self._required.keys(): 

756 r = r() 

757 if r is not None: 

758 r.unsubscribe(self) 

759 self._required.clear() 

760 

761 

762 # Extendors 

763 # --------- 

764 

765 # When given an target interface for an adapter lookup, we need to consider 

766 # adapters for interfaces that extend the target interface. This is 

767 # what the extendors dictionary is about. It tells us all of the 

768 # interfaces that extend an interface for which there are adapters 

769 # registered. 

770 

771 # We could separate this by order and name, thus reducing the 

772 # number of provided interfaces to search at run time. The tradeoff, 

773 # however, is that we have to store more information. For example, 

774 # if the same interface is provided for multiple names and if the 

775 # interface extends many interfaces, we'll have to keep track of 

776 # a fair bit of information for each name. It's better to 

777 # be space efficient here and be time efficient in the cache 

778 # implementation. 

779 

780 # TODO: add invalidation when a provided interface changes, in case 

781 # the interface's __iro__ has changed. This is unlikely enough that 

782 # we'll take our chances for now. 

783 

784 def init_extendors(self): 

785 self._extendors = {} 

786 for p in self._registry._provided: 

787 self.add_extendor(p) 

788 

789 def add_extendor(self, provided): 

790 _extendors = self._extendors 

791 for i in provided.__iro__: 

792 extendors = _extendors.get(i, ()) 

793 _extendors[i] = ( 

794 [e for e in extendors if provided.isOrExtends(e)] 

795 + 

796 [provided] 

797 + 

798 [e for e in extendors if not provided.isOrExtends(e)] 

799 ) 

800 

801 def remove_extendor(self, provided): 

802 _extendors = self._extendors 

803 for i in provided.__iro__: 

804 _extendors[i] = [e for e in _extendors.get(i, ()) 

805 if e != provided] 

806 

807 

808 def _subscribe(self, *required): 

809 _refs = self._required 

810 for r in required: 

811 ref = r.weakref() 

812 if ref not in _refs: 

813 r.subscribe(self) 

814 _refs[ref] = 1 

815 

816 def _uncached_lookup(self, required, provided, name=u''): 

817 required = tuple(required) 

818 result = None 

819 order = len(required) 

820 for registry in self._registry.ro: 

821 byorder = registry._adapters 

822 if order >= len(byorder): 

823 continue 

824 

825 extendors = registry._v_lookup._extendors.get(provided) 

826 if not extendors: 

827 continue 

828 

829 components = byorder[order] 

830 result = _lookup(components, required, extendors, name, 0, 

831 order) 

832 if result is not None: 

833 break 

834 

835 self._subscribe(*required) 

836 

837 return result 

838 

839 def queryMultiAdapter(self, objects, provided, name=u'', default=None): 

840 factory = self.lookup([providedBy(o) for o in objects], provided, name) 

841 if factory is None: 

842 return default 

843 

844 result = factory(*[o.__self__ if isinstance(o, super) else o for o in objects]) 

845 if result is None: 

846 return default 

847 

848 return result 

849 

850 def _uncached_lookupAll(self, required, provided): 

851 required = tuple(required) 

852 order = len(required) 

853 result = {} 

854 for registry in reversed(self._registry.ro): 

855 byorder = registry._adapters 

856 if order >= len(byorder): 

857 continue 

858 extendors = registry._v_lookup._extendors.get(provided) 

859 if not extendors: 

860 continue 

861 components = byorder[order] 

862 _lookupAll(components, required, extendors, result, 0, order) 

863 

864 self._subscribe(*required) 

865 

866 return tuple(result.items()) 

867 

868 def names(self, required, provided): 

869 return [c[0] for c in self.lookupAll(required, provided)] 

870 

871 def _uncached_subscriptions(self, required, provided): 

872 required = tuple(required) 

873 order = len(required) 

874 result = [] 

875 for registry in reversed(self._registry.ro): 

876 byorder = registry._subscribers 

877 if order >= len(byorder): 

878 continue 

879 

880 if provided is None: 

881 extendors = (provided, ) 

882 else: 

883 extendors = registry._v_lookup._extendors.get(provided) 

884 if extendors is None: 

885 continue 

886 

887 _subscriptions(byorder[order], required, extendors, u'', 

888 result, 0, order) 

889 

890 self._subscribe(*required) 

891 

892 return result 

893 

894 def subscribers(self, objects, provided): 

895 subscriptions = self.subscriptions([providedBy(o) for o in objects], provided) 

896 if provided is None: 

897 result = () 

898 for subscription in subscriptions: 

899 subscription(*objects) 

900 else: 

901 result = [] 

902 for subscription in subscriptions: 

903 subscriber = subscription(*objects) 

904 if subscriber is not None: 

905 result.append(subscriber) 

906 return result 

907 

908class AdapterLookup(AdapterLookupBase, LookupBase): 

909 pass 

910 

911@implementer(IAdapterRegistry) 

912class AdapterRegistry(BaseAdapterRegistry): 

913 """ 

914 A full implementation of ``IAdapterRegistry`` that adds support for 

915 sub-registries. 

916 """ 

917 

918 LookupClass = AdapterLookup 

919 

920 def __init__(self, bases=()): 

921 # AdapterRegisties are invalidating registries, so 

922 # we need to keep track of our invalidating subregistries. 

923 self._v_subregistries = weakref.WeakKeyDictionary() 

924 

925 super(AdapterRegistry, self).__init__(bases) 

926 

927 def _addSubregistry(self, r): 

928 self._v_subregistries[r] = 1 

929 

930 def _removeSubregistry(self, r): 

931 if r in self._v_subregistries: 

932 del self._v_subregistries[r] 

933 

934 def _setBases(self, bases): 

935 old = self.__dict__.get('__bases__', ()) 

936 for r in old: 

937 if r not in bases: 

938 r._removeSubregistry(self) 

939 for r in bases: 

940 if r not in old: 

941 r._addSubregistry(self) 

942 

943 super(AdapterRegistry, self)._setBases(bases) 

944 

945 def changed(self, originally_changed): 

946 super(AdapterRegistry, self).changed(originally_changed) 

947 

948 for sub in self._v_subregistries.keys(): 

949 sub.changed(originally_changed) 

950 

951 

952class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): 

953 pass 

954 

955@implementer(IAdapterRegistry) 

956class VerifyingAdapterRegistry(BaseAdapterRegistry): 

957 """ 

958 The most commonly-used adapter registry. 

959 """ 

960 

961 LookupClass = VerifyingAdapterLookup 

962 

963def _convert_None_to_Interface(x): 

964 if x is None: 

965 return Interface 

966 else: 

967 return x 

968 

969def _lookup(components, specs, provided, name, i, l): 

970 # this function is called very often. 

971 # The components.get in loops is executed 100 of 1000s times. 

972 # by loading get into a local variable the bytecode 

973 # "LOAD_FAST 0 (components)" in the loop can be eliminated. 

974 components_get = components.get 

975 if i < l: 

976 for spec in specs[i].__sro__: 

977 comps = components_get(spec) 

978 if comps: 

979 r = _lookup(comps, specs, provided, name, i+1, l) 

980 if r is not None: 

981 return r 

982 else: 

983 for iface in provided: 

984 comps = components_get(iface) 

985 if comps: 

986 r = comps.get(name) 

987 if r is not None: 

988 return r 

989 

990 return None 

991 

992def _lookupAll(components, specs, provided, result, i, l): 

993 components_get = components.get # see _lookup above 

994 if i < l: 

995 for spec in reversed(specs[i].__sro__): 

996 comps = components_get(spec) 

997 if comps: 

998 _lookupAll(comps, specs, provided, result, i+1, l) 

999 else: 

1000 for iface in reversed(provided): 

1001 comps = components_get(iface) 

1002 if comps: 

1003 result.update(comps) 

1004 

1005def _subscriptions(components, specs, provided, name, result, i, l): 

1006 components_get = components.get # see _lookup above 

1007 if i < l: 

1008 for spec in reversed(specs[i].__sro__): 

1009 comps = components_get(spec) 

1010 if comps: 

1011 _subscriptions(comps, specs, provided, name, result, i+1, l) 

1012 else: 

1013 for iface in reversed(provided): 

1014 comps = components_get(iface) 

1015 if comps: 

1016 comps = comps.get(name) 

1017 if comps: 

1018 result.extend(comps)