def testProxyHostComps(self): pers_comps = BaseComponents(BASE, 'persistent', (BASE,)) host_comps = BaseComponents(BASE, 'example.com', (BASE,)) host_sm = HSM('example.com', 'siteman', host_comps, pers_comps) host_site = MockSite(host_sm) host_site.__name__ = host_sm.__name__ setSite(host_site) new_comps = BaseComponents(BASE, 'sub_site', (pers_comps,)) new_site = MockSite(new_comps) new_site.__name__ = new_comps.__name__ interface.alsoProvides(new_site, IFoo) threadSiteSubscriber(new_site, None) cur_site = getSite() # It should implement the static and dynamic # ifaces assert_that(cur_site, validly_provides(IFoo)) assert_that(cur_site, validly_provides(IMock)) # It should have the marker property assert_that(cur_site.getSiteManager(), has_property('host_components', host_comps)) assert_that(ro.ro(cur_site.getSiteManager()), contains( # The first entry is synthesized has_property('__name__', new_comps.__name__), pers_comps, # The host comps appear after all the bases # in the ro of the new site host_comps, BASE))
def changed(self, originally_changed): """We, or something we depend on, have changed """ try: del self._v_attrs except AttributeError: pass implied = self._implied implied.clear() ancestors = ro(self) try: if Interface not in ancestors: ancestors.append(Interface) except NameError: pass # defining Interface itself self.__sro__ = tuple(ancestors) self.__iro__ = tuple([ancestor for ancestor in ancestors if isinstance(ancestor, InterfaceClass) ]) for ancestor in ancestors: # We directly imply our ancestors: implied[ancestor] = () # Now, advise our dependents of change: for dependent in tuple(self.dependents.keys()): dependent.changed(originally_changed)
def changed(self, originally_changed): """We, or something we depend on, have changed """ try: del self._v_attrs except AttributeError: pass implied = self._implied implied.clear() ancestors = ro(self) try: if Interface not in ancestors: ancestors.append(Interface) except NameError: pass # defining Interface itself self.__sro__ = tuple(ancestors) self.__iro__ = tuple([ ancestor for ancestor in ancestors if isinstance(ancestor, InterfaceClass) ]) for ancestor in ancestors: # We directly imply our ancestors: implied[ancestor] = () # Now, advise our dependents of change: for dependent in self.dependents.keys(): dependent.changed(originally_changed)
def _setBases(self, bases): """ If subclasses need to track when ``__bases__`` changes, they can override this method. Subclasses must still call this method. """ self.__dict__['__bases__'] = bases self.ro = ro.ro(self) self.changed(self)
def test_ro(self, stdlib_class=stdlib_class, iface=iface): from zope.interface import ro from zope.interface import implementedBy from zope.interface import Interface self.assertEqual(tuple(ro.ro(iface, strict=True)), iface.__sro__) implements = implementedBy(stdlib_class) sro = implements.__sro__ self.assertIs(sro[-1], Interface) # Check that we got the strict C3 resolution order, unless we # know we cannot. Note that 'Interface' is virtual base that doesn't # necessarily appear at the same place in the calculated SRO as in the # final SRO. strict = stdlib_class not in self.NON_STRICT_RO isro = ro.ro(implements, strict=strict) isro.remove(Interface) isro.append(Interface) self.assertEqual(tuple(isro), sro)
def objectImplements(self): """ All interfaces and classes implemented by an object. This is a KeywordIndex on the catalog. """ dottednames = set() # Add the highest five classes in resolution order. 5 is # an arbitrary number; essentially, we only care about indexing # Zenoss classes, and our inheritance tree isn't that deep. Past # 5 we index a bunch of ObjectManager, Persistent, etc., which # we'll never use, and enact a significant performance penalty # when inserting keywords into the index. for kls in ro.ro(self._context.__class__)[:5]: dottednames.add('%s.%s' % (kls.__module__, kls.__name__)) return list(dottednames)
def test_simple_ro(self): # Check that resolution order is what we think. See # site.py # This simulates the layout in the database and global # site manager. class GSM(object): pass # DB class Root(GSM): pass class DS(Root): pass # global sites class Base(GSM): pass class S1(Base): pass class S2(Base): pass # DB sites class PS1(S1, DS): pass class PS2(S2, PS1): pass assert_that(ro.ro(PS2), is_([PS2, S2, PS1, S1, Base, DS, Root, GSM, object]))
def get_all_host_sites(): """ The order in which sites are accessed is top-down breadth-first, that is, the shallowest to the deepest nested sites. This allows you to assume that your parent sites have already been updated. :returns: A list of sites :rtype: list """ sites = component.getUtility(IEtcNamespace, name='hostsites') sites = list(sites.values()) # The easyiest way to go top-down is to again use the resolution order; # we just have to watch out for duplicates and non-persistent components site_to_ro = {site: ro.ro(site.getSiteManager()) for site in sites} # This should be a plain, directed acyclic tree (single root) that is now # linearized. # Transform from the site manager back into the site object itself site_to_site_ro = {} for site, managers in site_to_ro.items(): site_to_site_ro[site] = [getattr(x, '__parent__', None) for x in managers] # Ok, now, go through the dictionary, walking from the top to the bottom, # one at a time, thus producing the correct order # (Because our datastructure looks like this: # site1: [site1, ds, base, GSM] # site2: [site2, site1, base, GSM] # site3: [site3, ds, base, GSM]) ordered = list() while site_to_site_ro: for site, managers in dict(site_to_site_ro).items(): if not managers: site_to_site_ro.pop(site) continue base_site = managers.pop() if base_site in sites and base_site not in ordered: # Ie., it's a real one we haven't seen before ordered.append(base_site) return ordered
def objectImplements(self): """ All interfaces and classes implemented by an object. This is a KeywordIndex on the catalog. """ dottednames = set() # Add the highest five classes in resolution order. 5 is # an arbitrary number; essentially, we only care about indexing # Zenoss classes, and our inheritance tree isn't that deep. Past # 5 we index a bunch of ObjectManager, Persistent, etc., which # we'll never use, and enact a significant performance penalty # when inserting keywords into the index. for kls in ro.ro(self._context.__class__): # @TODO review. had some issues with picking only the top 5 # instead we get anything from Products or Zenpacks if kls.__module__.startswith("Products") or kls.__module__.startswith("ZenPacks"): dottednames.add('%s.%s' % (kls.__module__, kls.__name__)) return list(dottednames)
def objectImplements(self): """ All interfaces and classes implemented by an object. This is a KeywordIndex on the catalog. """ dottednames = set() # Add the highest five classes in resolution order. 5 is # an arbitrary number; essentially, we only care about indexing # Zenoss classes, and our inheritance tree isn't that deep. Past # 5 we index a bunch of ObjectManager, Persistent, etc., which # we'll never use, and enact a significant performance penalty # when inserting keywords into the index. for kls in ro.ro(self._context.__class__): # @TODO review. had some issues with picking only the top 5 # instead we get anything from Products or Zenpacks if kls.__module__.startswith( "Products") or kls.__module__.startswith("ZenPacks"): dottednames.add('%s.%s' % (kls.__module__, kls.__name__)) return list(dottednames)
def test_site_sync(self): for site in _SITES: assert_that(_find_site_components((site.__name__,)), is_(not_none())) with mock_db_trans() as conn: for site in _SITES: assert_that(_find_site_components((site.__name__,)), is_(not_none())) ds = conn.root()['nti.dataserver'] assert ds is not None sites = ds['++etc++hostsites'] for site in _SITES: assert_that(sites, does_not(has_key(site.__name__))) synchronize_host_policies() synchronize_host_policies() assert_that(self._events, has_length(len(_SITES))) # These were put in in order # assert_that( self._events[0][0].__parent__, # has_property('__name__', EVAL.__name__)) # XXX These two lines are cover only. get_host_site(DEMO.__name__) get_host_site('DNE', True) assert_that(calling(get_host_site).with_args('dne'), raises(LookupError)) with mock_db_trans() as conn: for site in _SITES: assert_that(_find_site_components((site.__name__,)), is_(not_none())) ds = conn.root()['nti.dataserver'] assert ds is not None sites = ds['++etc++hostsites'] assert_that(sites, has_key(EVAL.__name__)) assert_that(sites[EVAL.__name__], verifiably_provides(ISite)) # If we ask the demoalpha persistent site for an ITestSyteSync, # it will find us, because it goes to the demo global site assert_that(sites[DEMOALPHA.__name__].getSiteManager().queryUtility(ITestSiteSync), is_(ASync)) # However, if we put something in the demo *persistent* site, it # will find that sites[DEMO.__name__].getSiteManager().registerUtility(OtherSync()) assert_that(sites[DEMOALPHA.__name__].getSiteManager().queryUtility(ITestSiteSync), is_(OtherSync)) # Verify the resolution order too def _name(x): if x.__name__ == '++etc++site': return 'P' + str(x.__parent__.__name__) return x.__name__ assert_that([_name(x) for x in ro.ro(sites[DEMOALPHA.__name__].getSiteManager())], is_([u'Pdemo-alpha.nextthoughttest.com', u'demo-alpha.nextthoughttest.com', u'Pdemo.nextthoughttest.com', u'demo.nextthoughttest.com', u'Peval.nextthoughttest.com', u'eval.nextthoughttest.com', u'Pdataserver2', u'PNone', 'base'])) # including if we ask to travers from top to bottom names = list() def func(): names.append(_name(component.getSiteManager())) run_job_in_all_host_sites(func) # Note that PDemo and Peval-alpha are arbitrary, they both # descend from eval; # TODO: why aren't we maintaining alphabetical order? # we should be, but sometimes we don't assert_that(names, is_(any_of( [u'Peval.nextthoughttest.com', u'Pdemo.nextthoughttest.com', u'Peval-alpha.nextthoughttest.com', u'Pdemo-alpha.nextthoughttest.com'], [u'Peval.nextthoughttest.com', u'Peval-alpha.nextthoughttest.com', u'Pdemo.nextthoughttest.com', u'Pdemo-alpha.nextthoughttest.com']))) # And that it's what we get back if we ask for it assert_that(get_site_for_site_names((DEMOALPHA.__name__,)), is_(same_instance(sites[DEMOALPHA.__name__]))) # No new sites created assert_that(self._events, has_length(len(_SITES)))
def clean(self): for base in self.__bases__: base.unsubscribe(self) self.__bases__ = [self.registry.get(base) for base in self.spec().__bases__] for base in self.__bases__: base.subscribe(self) self.selfImplied, self.multImplied = adapterImplied(self.adapters) implied = {} ancestors = ro(self) # Collect implied data in reverse order to have more specific data # override less-specific data. ancestors.reverse() for ancestor in ancestors: for key, v in ancestor.selfImplied.iteritems(): # key is specification or ('s', specification) subscription = isinstance(key, tuple) and key[0] == 's' if subscription: # v is tuple of subs implied[key] = implied.get(key, ()) + v else: oldbyname = implied.get(key) if not oldbyname: implied[key] = oldbyname = {} # v is name -> object oldbyname.update(v) for key, v in ancestor.multImplied.iteritems(): # key is (specification, order) # or ('s', specification, order) subscription = key[0] == 's' if subscription: oldwithobs = implied.get(key) if not oldwithobs: oldwithobs = implied[key] = {} # v is {with -> tuple([object])} for with, objects in v.iteritems(): oldwithobs[with] = oldwithobs.get(with, ()) + objects else: oldbyname = implied.get(key) if not oldbyname: implied[key] = oldbyname = {} # v is {name -> {with -> ?}} for name, withobs in v.iteritems(): oldwithobs = oldbyname.get(name) if not oldwithobs: oldwithobs = oldbyname[name] = {} # withobs is {with -> object} oldwithobs.update(withobs) # Now flatten with mappings to tuples for key, v in implied.iteritems(): if isinstance(key, tuple): if key[0] == 's': # subscriptions if isinstance(v, dict): implied[key] = v.items() else: byname = v for name, value in byname.iteritems(): if isinstance(value, dict): # We have {with -> value} # convert it to sorted [(with, value] byname[name] = orderwith(value) self.get = implied.get
def _callFUT(self, ob): from zope.interface.ro import ro return ro(ob)
def synchronize_host_policies(): """ Called within a transaction with a site being the current application site, find any :mod:`z3c.baseregistry` components that should be persistent sites, and register them in the database. As a prerequisite, :func:`install_sites_folder` must have been done, and we must be in that site. """ # TODO: We will ultimately need to deal with removing and renaming # of these # Resolution order: The actual ISite __parent__ order is not # important, so we can keep them flat to mirror the GSM IComponents # registrations. What matters is the __bases__ of the site managers. # Now, if the global IComponents are themselves flat, then it doesn't matter; # however, if you have a hierarchy (and we do) then it matters critically, because # we need to pick up persistent utilities from these objects, as well as # the global components, in the right order. For example, if we have this # global hierarchy: # GSM # \ # S1 # \ # S2 # and in the database we have the nti.dataserver and root persistent site managers, # then when we create the persistent sites for S1 and S2 (PS1 and PS2) we want # the resolution order to be: # PS2 -> S2 -> PS1 -> S1 -> DS -> Root -> GSM # That is, we need to get the persistent components mixed in between the # global components. # Fortunately this is very easy to achieve. The code in zope.interface.ro handles # this. # We just need to ensure: # PS1.__bases__ = (S1, DS) # PS2.__bases__ = (S2, PS1) sites = component.getUtility(IEtcNamespace, name='hostsites') ds_folder = sites.__parent__ assert IMainApplicationFolder.providedBy(ds_folder) ds_site_manager = ds_folder.getSiteManager() # Ok, find everything that is globally registered global_sm = component.getGlobalSiteManager() all_global_named_utilities = list(global_sm.getUtilitiesFor(IComponents)) for name, comp in all_global_named_utilities: # The sites must be registered the same as their internal name assert name == comp.__name__ all_global_utilities = [x[1] for x in all_global_named_utilities] # Now, get the resolution order of each site; this is an easy way # to do a kind of topological sort. site_ros = [ro.ro(x) for x in all_global_utilities] # Next, start creating persistent sites in the database, walking from the top # of the resolution order (the end of the list) # towards the root; the first one we put in the DB gets the DS as its # base, otherwise it gets the previous one we put in. for site_ro in site_ros: site_ro = reversed(site_ro) secondary_comps = ds_site_manager for comps in site_ro: name = comps.__name__ logger.debug("Checking host policy for site %s", name) if name.endswith('base') or name.startswith('base'): # The GSM or the base global objects # TODO: better way to do this...marker interface? continue # pragma: no cover if name in sites: logger.debug("Host policy for %s already in place", name) # Ok, we've already put one in for this level. # We need to make it our next choice going forward secondary_comps = sites[name].getSiteManager() else: # Great, create the site logger.info("Installing site policy %s", name) site = HostPolicyFolder() # should fire object created event sites[name] = site site_policy = HostPolicySiteManager(site) site_policy.__bases__ = (comps, secondary_comps) # should fire INewLocalSite site.setSiteManager(site_policy) secondary_comps = site_policy
def _setBases(self, bases): self.__dict__['__bases__'] = bases self.ro = ro.ro(self) self.changed(self)
def _callFUT(self, ob, **kwargs): from zope.interface.ro import ro return ro(ob, **kwargs)