1############################################################################## 2# 3# Copyright (c) 2006 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"""Basic components support 15""" 16from collections import defaultdict 17 18try: 19 from zope.event import notify 20except ImportError: # pragma: no cover 21 def notify(*arg, **kw): pass 22 23from zope.interface.interfaces import ISpecification 24from zope.interface.interfaces import ComponentLookupError 25from zope.interface.interfaces import IAdapterRegistration 26from zope.interface.interfaces import IComponents 27from zope.interface.interfaces import IHandlerRegistration 28from zope.interface.interfaces import ISubscriptionAdapterRegistration 29from zope.interface.interfaces import IUtilityRegistration 30from zope.interface.interfaces import Registered 31from zope.interface.interfaces import Unregistered 32 33from zope.interface.interface import Interface 34from zope.interface.declarations import implementedBy 35from zope.interface.declarations import implementer 36from zope.interface.declarations import implementer_only 37from zope.interface.declarations import providedBy 38from zope.interface.adapter import AdapterRegistry 39from zope.interface._compat import CLASS_TYPES 40from zope.interface._compat import STRING_TYPES 41 42__all__ = [ 43 # Components is public API, but 44 # the *Registration classes are just implementations 45 # of public interfaces. 46 'Components', 47] 48 49class _UnhashableComponentCounter(object): 50 # defaultdict(int)-like object for unhashable components 51 52 def __init__(self, otherdict): 53 # [(component, count)] 54 self._data = [item for item in otherdict.items()] 55 56 def __getitem__(self, key): 57 for component, count in self._data: 58 if component == key: 59 return count 60 return 0 61 62 def __setitem__(self, component, count): 63 for i, data in enumerate(self._data): 64 if data[0] == component: 65 self._data[i] = component, count 66 return 67 self._data.append((component, count)) 68 69 def __delitem__(self, component): 70 for i, data in enumerate(self._data): 71 if data[0] == component: 72 del self._data[i] 73 return 74 raise KeyError(component) # pragma: no cover 75 76def _defaultdict_int(): 77 return defaultdict(int) 78 79class _UtilityRegistrations(object): 80 81 def __init__(self, utilities, utility_registrations): 82 # {provided -> {component: count}} 83 self._cache = defaultdict(_defaultdict_int) 84 self._utilities = utilities 85 self._utility_registrations = utility_registrations 86 87 self.__populate_cache() 88 89 def __populate_cache(self): 90 for ((p, _), data) in iter(self._utility_registrations.items()): 91 component = data[0] 92 self.__cache_utility(p, component) 93 94 def __cache_utility(self, provided, component): 95 try: 96 self._cache[provided][component] += 1 97 except TypeError: 98 # The component is not hashable, and we have a dict. Switch to a strategy 99 # that doesn't use hashing. 100 prov = self._cache[provided] = _UnhashableComponentCounter(self._cache[provided]) 101 prov[component] += 1 102 103 def __uncache_utility(self, provided, component): 104 provided = self._cache[provided] 105 # It seems like this line could raise a TypeError if component isn't 106 # hashable and we haven't yet switched to _UnhashableComponentCounter. However, 107 # we can't actually get in that situation. In order to get here, we would 108 # have had to cache the utility already which would have switched 109 # the datastructure if needed. 110 count = provided[component] 111 count -= 1 112 if count == 0: 113 del provided[component] 114 else: 115 provided[component] = count 116 return count > 0 117 118 def _is_utility_subscribed(self, provided, component): 119 try: 120 return self._cache[provided][component] > 0 121 except TypeError: 122 # Not hashable and we're still using a dict 123 return False 124 125 def registerUtility(self, provided, name, component, info, factory): 126 subscribed = self._is_utility_subscribed(provided, component) 127 128 self._utility_registrations[(provided, name)] = component, info, factory 129 self._utilities.register((), provided, name, component) 130 131 if not subscribed: 132 self._utilities.subscribe((), provided, component) 133 134 self.__cache_utility(provided, component) 135 136 def unregisterUtility(self, provided, name, component): 137 del self._utility_registrations[(provided, name)] 138 self._utilities.unregister((), provided, name) 139 140 subscribed = self.__uncache_utility(provided, component) 141 142 if not subscribed: 143 self._utilities.unsubscribe((), provided, component) 144 145 146@implementer(IComponents) 147class Components(object): 148 149 _v_utility_registrations_cache = None 150 151 def __init__(self, name='', bases=()): 152 # __init__ is used for test cleanup as well as initialization. 153 # XXX add a separate API for test cleanup. 154 assert isinstance(name, STRING_TYPES) 155 self.__name__ = name 156 self._init_registries() 157 self._init_registrations() 158 self.__bases__ = tuple(bases) 159 self._v_utility_registrations_cache = None 160 161 def __repr__(self): 162 return "<%s %s>" % (self.__class__.__name__, self.__name__) 163 164 def __reduce__(self): 165 # Mimic what a persistent.Persistent object does and elide 166 # _v_ attributes so that they don't get saved in ZODB. 167 # This allows us to store things that cannot be pickled in such 168 # attributes. 169 reduction = super(Components, self).__reduce__() 170 # (callable, args, state, listiter, dictiter) 171 # We assume the state is always a dict; the last three items 172 # are technically optional and can be missing or None. 173 filtered_state = {k: v for k, v in reduction[2].items() 174 if not k.startswith('_v_')} 175 reduction = list(reduction) 176 reduction[2] = filtered_state 177 return tuple(reduction) 178 179 def _init_registries(self): 180 # Subclasses have never been required to call this method 181 # if they override it, merely to fill in these two attributes. 182 self.adapters = AdapterRegistry() 183 self.utilities = AdapterRegistry() 184 185 def _init_registrations(self): 186 self._utility_registrations = {} 187 self._adapter_registrations = {} 188 self._subscription_registrations = [] 189 self._handler_registrations = [] 190 191 @property 192 def _utility_registrations_cache(self): 193 # We use a _v_ attribute internally so that data aren't saved in ZODB, 194 # because this object cannot be pickled. 195 cache = self._v_utility_registrations_cache 196 if (cache is None 197 or cache._utilities is not self.utilities 198 or cache._utility_registrations is not self._utility_registrations): 199 cache = self._v_utility_registrations_cache = _UtilityRegistrations( 200 self.utilities, 201 self._utility_registrations) 202 return cache 203 204 def _getBases(self): 205 # Subclasses might override 206 return self.__dict__.get('__bases__', ()) 207 208 def _setBases(self, bases): 209 # Subclasses might override 210 self.adapters.__bases__ = tuple([ 211 base.adapters for base in bases]) 212 self.utilities.__bases__ = tuple([ 213 base.utilities for base in bases]) 214 self.__dict__['__bases__'] = tuple(bases) 215 216 __bases__ = property( 217 lambda self: self._getBases(), 218 lambda self, bases: self._setBases(bases), 219 ) 220 221 def registerUtility(self, component=None, provided=None, name=u'', 222 info=u'', event=True, factory=None): 223 if factory: 224 if component: 225 raise TypeError("Can't specify factory and component.") 226 component = factory() 227 228 if provided is None: 229 provided = _getUtilityProvided(component) 230 231 if name == u'': 232 name = _getName(component) 233 234 reg = self._utility_registrations.get((provided, name)) 235 if reg is not None: 236 if reg[:2] == (component, info): 237 # already registered 238 return 239 self.unregisterUtility(reg[0], provided, name) 240 241 self._utility_registrations_cache.registerUtility( 242 provided, name, component, info, factory) 243 244 if event: 245 notify(Registered( 246 UtilityRegistration(self, provided, name, component, info, 247 factory) 248 )) 249 250 def unregisterUtility(self, component=None, provided=None, name=u'', 251 factory=None): 252 if factory: 253 if component: 254 raise TypeError("Can't specify factory and component.") 255 component = factory() 256 257 if provided is None: 258 if component is None: 259 raise TypeError("Must specify one of component, factory and " 260 "provided") 261 provided = _getUtilityProvided(component) 262 263 old = self._utility_registrations.get((provided, name)) 264 if (old is None) or ((component is not None) and 265 (component != old[0])): 266 return False 267 268 if component is None: 269 component = old[0] 270 271 # Note that component is now the old thing registered 272 self._utility_registrations_cache.unregisterUtility( 273 provided, name, component) 274 275 notify(Unregistered( 276 UtilityRegistration(self, provided, name, component, *old[1:]) 277 )) 278 279 return True 280 281 def registeredUtilities(self): 282 for ((provided, name), data 283 ) in iter(self._utility_registrations.items()): 284 yield UtilityRegistration(self, provided, name, *data) 285 286 def queryUtility(self, provided, name=u'', default=None): 287 return self.utilities.lookup((), provided, name, default) 288 289 def getUtility(self, provided, name=u''): 290 utility = self.utilities.lookup((), provided, name) 291 if utility is None: 292 raise ComponentLookupError(provided, name) 293 return utility 294 295 def getUtilitiesFor(self, interface): 296 for name, utility in self.utilities.lookupAll((), interface): 297 yield name, utility 298 299 def getAllUtilitiesRegisteredFor(self, interface): 300 return self.utilities.subscriptions((), interface) 301 302 def registerAdapter(self, factory, required=None, provided=None, 303 name=u'', info=u'', event=True): 304 if provided is None: 305 provided = _getAdapterProvided(factory) 306 required = _getAdapterRequired(factory, required) 307 if name == u'': 308 name = _getName(factory) 309 self._adapter_registrations[(required, provided, name) 310 ] = factory, info 311 self.adapters.register(required, provided, name, factory) 312 313 if event: 314 notify(Registered( 315 AdapterRegistration(self, required, provided, name, 316 factory, info) 317 )) 318 319 320 def unregisterAdapter(self, factory=None, 321 required=None, provided=None, name=u'', 322 ): 323 if provided is None: 324 if factory is None: 325 raise TypeError("Must specify one of factory and provided") 326 provided = _getAdapterProvided(factory) 327 328 if (required is None) and (factory is None): 329 raise TypeError("Must specify one of factory and required") 330 331 required = _getAdapterRequired(factory, required) 332 old = self._adapter_registrations.get((required, provided, name)) 333 if (old is None) or ((factory is not None) and 334 (factory != old[0])): 335 return False 336 337 del self._adapter_registrations[(required, provided, name)] 338 self.adapters.unregister(required, provided, name) 339 340 notify(Unregistered( 341 AdapterRegistration(self, required, provided, name, 342 *old) 343 )) 344 345 return True 346 347 def registeredAdapters(self): 348 for ((required, provided, name), (component, info) 349 ) in iter(self._adapter_registrations.items()): 350 yield AdapterRegistration(self, required, provided, name, 351 component, info) 352 353 def queryAdapter(self, object, interface, name=u'', default=None): 354 return self.adapters.queryAdapter(object, interface, name, default) 355 356 def getAdapter(self, object, interface, name=u''): 357 adapter = self.adapters.queryAdapter(object, interface, name) 358 if adapter is None: 359 raise ComponentLookupError(object, interface, name) 360 return adapter 361 362 def queryMultiAdapter(self, objects, interface, name=u'', 363 default=None): 364 return self.adapters.queryMultiAdapter( 365 objects, interface, name, default) 366 367 def getMultiAdapter(self, objects, interface, name=u''): 368 adapter = self.adapters.queryMultiAdapter(objects, interface, name) 369 if adapter is None: 370 raise ComponentLookupError(objects, interface, name) 371 return adapter 372 373 def getAdapters(self, objects, provided): 374 for name, factory in self.adapters.lookupAll( 375 list(map(providedBy, objects)), 376 provided): 377 adapter = factory(*objects) 378 if adapter is not None: 379 yield name, adapter 380 381 def registerSubscriptionAdapter(self, 382 factory, required=None, provided=None, 383 name=u'', info=u'', 384 event=True): 385 if name: 386 raise TypeError("Named subscribers are not yet supported") 387 if provided is None: 388 provided = _getAdapterProvided(factory) 389 required = _getAdapterRequired(factory, required) 390 self._subscription_registrations.append( 391 (required, provided, name, factory, info) 392 ) 393 self.adapters.subscribe(required, provided, factory) 394 395 if event: 396 notify(Registered( 397 SubscriptionRegistration(self, required, provided, name, 398 factory, info) 399 )) 400 401 def registeredSubscriptionAdapters(self): 402 for data in self._subscription_registrations: 403 yield SubscriptionRegistration(self, *data) 404 405 def unregisterSubscriptionAdapter(self, factory=None, 406 required=None, provided=None, name=u'', 407 ): 408 if name: 409 raise TypeError("Named subscribers are not yet supported") 410 if provided is None: 411 if factory is None: 412 raise TypeError("Must specify one of factory and provided") 413 provided = _getAdapterProvided(factory) 414 415 if (required is None) and (factory is None): 416 raise TypeError("Must specify one of factory and required") 417 418 required = _getAdapterRequired(factory, required) 419 420 if factory is None: 421 new = [(r, p, n, f, i) 422 for (r, p, n, f, i) 423 in self._subscription_registrations 424 if not (r == required and p == provided) 425 ] 426 else: 427 new = [(r, p, n, f, i) 428 for (r, p, n, f, i) 429 in self._subscription_registrations 430 if not (r == required and p == provided and f == factory) 431 ] 432 433 if len(new) == len(self._subscription_registrations): 434 return False 435 436 437 self._subscription_registrations[:] = new 438 self.adapters.unsubscribe(required, provided, factory) 439 440 notify(Unregistered( 441 SubscriptionRegistration(self, required, provided, name, 442 factory, '') 443 )) 444 445 return True 446 447 def subscribers(self, objects, provided): 448 return self.adapters.subscribers(objects, provided) 449 450 def registerHandler(self, 451 factory, required=None, 452 name=u'', info=u'', 453 event=True): 454 if name: 455 raise TypeError("Named handlers are not yet supported") 456 required = _getAdapterRequired(factory, required) 457 self._handler_registrations.append( 458 (required, name, factory, info) 459 ) 460 self.adapters.subscribe(required, None, factory) 461 462 if event: 463 notify(Registered( 464 HandlerRegistration(self, required, name, factory, info) 465 )) 466 467 def registeredHandlers(self): 468 for data in self._handler_registrations: 469 yield HandlerRegistration(self, *data) 470 471 def unregisterHandler(self, factory=None, required=None, name=u''): 472 if name: 473 raise TypeError("Named subscribers are not yet supported") 474 475 if (required is None) and (factory is None): 476 raise TypeError("Must specify one of factory and required") 477 478 required = _getAdapterRequired(factory, required) 479 480 if factory is None: 481 new = [(r, n, f, i) 482 for (r, n, f, i) 483 in self._handler_registrations 484 if r != required 485 ] 486 else: 487 new = [(r, n, f, i) 488 for (r, n, f, i) 489 in self._handler_registrations 490 if not (r == required and f == factory) 491 ] 492 493 if len(new) == len(self._handler_registrations): 494 return False 495 496 self._handler_registrations[:] = new 497 self.adapters.unsubscribe(required, None, factory) 498 499 notify(Unregistered( 500 HandlerRegistration(self, required, name, factory, '') 501 )) 502 503 return True 504 505 def handle(self, *objects): 506 self.adapters.subscribers(objects, None) 507 508 def rebuildUtilityRegistryFromLocalCache(self, rebuild=False): 509 """ 510 Emergency maintenance method to rebuild the ``.utilities`` 511 registry from the local copy maintained in this object, or 512 detect the need to do so. 513 514 Most users will never need to call this, but it can be helpful 515 in the event of suspected corruption. 516 517 By default, this method only checks for corruption. To make it 518 actually rebuild the registry, pass `True` for *rebuild*. 519 520 :param bool rebuild: If set to `True` (not the default), 521 this method will actually register and subscribe utilities 522 in the registry as needed to synchronize with the local cache. 523 524 :return: A dictionary that's meant as diagnostic data. The keys 525 and values may change over time. When called with a false *rebuild*, 526 the keys ``"needed_registered"`` and ``"needed_subscribed"`` will be 527 non-zero if any corruption was detected, but that will not be corrected. 528 529 .. versionadded:: 5.3.0 530 """ 531 regs = dict(self._utility_registrations) 532 utils = self.utilities 533 needed_registered = 0 534 did_not_register = 0 535 needed_subscribed = 0 536 did_not_subscribe = 0 537 538 539 # Avoid the expensive change process during this; we'll call 540 # it once at the end if needed. 541 assert 'changed' not in utils.__dict__ 542 utils.changed = lambda _: None 543 544 if rebuild: 545 register = utils.register 546 subscribe = utils.subscribe 547 else: 548 register = subscribe = lambda *args: None 549 550 try: 551 for (provided, name), (value, _info, _factory) in regs.items(): 552 if utils.registered((), provided, name) != value: 553 register((), provided, name, value) 554 needed_registered += 1 555 else: 556 did_not_register += 1 557 558 if utils.subscribed((), provided, value) is None: 559 needed_subscribed += 1 560 subscribe((), provided, value) 561 else: 562 did_not_subscribe += 1 563 finally: 564 del utils.changed 565 if rebuild and (needed_subscribed or needed_registered): 566 utils.changed(utils) 567 568 return { 569 'needed_registered': needed_registered, 570 'did_not_register': did_not_register, 571 'needed_subscribed': needed_subscribed, 572 'did_not_subscribe': did_not_subscribe 573 } 574 575def _getName(component): 576 try: 577 return component.__component_name__ 578 except AttributeError: 579 return u'' 580 581def _getUtilityProvided(component): 582 provided = list(providedBy(component)) 583 if len(provided) == 1: 584 return provided[0] 585 raise TypeError( 586 "The utility doesn't provide a single interface " 587 "and no provided interface was specified.") 588 589def _getAdapterProvided(factory): 590 provided = list(implementedBy(factory)) 591 if len(provided) == 1: 592 return provided[0] 593 raise TypeError( 594 "The adapter factory doesn't implement a single interface " 595 "and no provided interface was specified.") 596 597def _getAdapterRequired(factory, required): 598 if required is None: 599 try: 600 required = factory.__component_adapts__ 601 except AttributeError: 602 raise TypeError( 603 "The adapter factory doesn't have a __component_adapts__ " 604 "attribute and no required specifications were specified" 605 ) 606 elif ISpecification.providedBy(required): 607 raise TypeError("the required argument should be a list of " 608 "interfaces, not a single interface") 609 610 result = [] 611 for r in required: 612 if r is None: 613 r = Interface 614 elif not ISpecification.providedBy(r): 615 if isinstance(r, CLASS_TYPES): 616 r = implementedBy(r) 617 else: 618 raise TypeError("Required specification must be a " 619 "specification or class, not %r" % type(r) 620 ) 621 result.append(r) 622 return tuple(result) 623 624 625@implementer(IUtilityRegistration) 626class UtilityRegistration(object): 627 628 def __init__(self, registry, provided, name, component, doc, factory=None): 629 (self.registry, self.provided, self.name, self.component, self.info, 630 self.factory 631 ) = registry, provided, name, component, doc, factory 632 633 def __repr__(self): 634 return '%s(%r, %s, %r, %s, %r, %r)' % ( 635 self.__class__.__name__, 636 self.registry, 637 getattr(self.provided, '__name__', None), self.name, 638 getattr(self.component, '__name__', repr(self.component)), 639 self.factory, self.info, 640 ) 641 642 def __hash__(self): 643 return id(self) 644 645 def __eq__(self, other): 646 return repr(self) == repr(other) 647 648 def __ne__(self, other): 649 return repr(self) != repr(other) 650 651 def __lt__(self, other): 652 return repr(self) < repr(other) 653 654 def __le__(self, other): 655 return repr(self) <= repr(other) 656 657 def __gt__(self, other): 658 return repr(self) > repr(other) 659 660 def __ge__(self, other): 661 return repr(self) >= repr(other) 662 663@implementer(IAdapterRegistration) 664class AdapterRegistration(object): 665 666 def __init__(self, registry, required, provided, name, component, doc): 667 (self.registry, self.required, self.provided, self.name, 668 self.factory, self.info 669 ) = registry, required, provided, name, component, doc 670 671 def __repr__(self): 672 return '%s(%r, %s, %s, %r, %s, %r)' % ( 673 self.__class__.__name__, 674 self.registry, 675 '[' + ", ".join([r.__name__ for r in self.required]) + ']', 676 getattr(self.provided, '__name__', None), self.name, 677 getattr(self.factory, '__name__', repr(self.factory)), self.info, 678 ) 679 680 def __hash__(self): 681 return id(self) 682 683 def __eq__(self, other): 684 return repr(self) == repr(other) 685 686 def __ne__(self, other): 687 return repr(self) != repr(other) 688 689 def __lt__(self, other): 690 return repr(self) < repr(other) 691 692 def __le__(self, other): 693 return repr(self) <= repr(other) 694 695 def __gt__(self, other): 696 return repr(self) > repr(other) 697 698 def __ge__(self, other): 699 return repr(self) >= repr(other) 700 701@implementer_only(ISubscriptionAdapterRegistration) 702class SubscriptionRegistration(AdapterRegistration): 703 pass 704 705 706@implementer_only(IHandlerRegistration) 707class HandlerRegistration(AdapterRegistration): 708 709 def __init__(self, registry, required, name, handler, doc): 710 (self.registry, self.required, self.name, self.handler, self.info 711 ) = registry, required, name, handler, doc 712 713 @property 714 def factory(self): 715 return self.handler 716 717 provided = None 718 719 def __repr__(self): 720 return '%s(%r, %s, %r, %s, %r)' % ( 721 self.__class__.__name__, 722 self.registry, 723 '[' + ", ".join([r.__name__ for r in self.required]) + ']', 724 self.name, 725 getattr(self.factory, '__name__', repr(self.factory)), self.info, 726 ) 727