[Subversion] / PEAK / src / peak / binding / components.py  

Diff of /PEAK/src/peak/binding/components.py

Parent Directory | Revision Log

version 469, Sat Jul 13 21:02:40 2002 UTC version 1576, Sun Dec 28 19:19:32 2003 UTC
Line 1 
Line 1 
 """Basic binding tools"""  """Basic binding tools"""
   
 from once import Once, New, WeakBinding  from __future__ import generators
 import meta, modules  from peak.api import *
   
 from weakref import ref, WeakValueDictionary  from once import *
   from interfaces import *
 from peak.naming.names import toName, Syntax, CompoundName  from types import ModuleType
 from peak.naming.interfaces import NameNotFoundException  from peak.naming.names import toName, AbstractName, COMPOUND_KIND, IName
 from peak.util.EigenData import EigenRegistry  from peak.naming.syntax import PathSyntax
   from peak.util.EigenData import AlreadyRead
 from Interface import Interface  from peak.config.interfaces import IConfigKey, IPropertyMap, \
 from peak.api import config, NOT_FOUND      IConfigurationRoot, NullConfigRoot
   from peak.config.registries import ImmutableConfig
   from peak.util.imports import importString
   
   
 __all__ = [  __all__ = [
     'Component','AutoCreated','Provider','CachingProvider',      'Base', 'Component', 'whenAssembled', 'Obtain', 'Require', 'Delegate',
     'bindTo', 'requireBinding', 'bindToNames', 'bindToParent', 'bindToSelf',      'bindTo', 'requireBinding', 'bindSequence', 'bindToParent', 'bindToSelf',
     'getRootComponent', 'getParentComponent', 'lookupComponent',      'getRootComponent', 'getParentComponent', 'lookupComponent',
     'acquireComponent', 'globalLookup'      'acquireComponent', 'notifyUponAssembly', 'PluginsFor', 'PluginKeys',
       'bindToUtilities', 'bindToProperty', 'Constant', 'delegateTo',
       'getComponentName', 'getComponentPath', 'Acquire', 'ComponentName',
 ]  ]
   
   from _once import BaseDescriptor
   
 InterfaceClass = Interface.__class__  class _proxy(BaseDescriptor):
   
       def __init__(self,attrName):
           self.attrName = attrName
   
       def usageError(self):
           raise AttributeError, self.attrName
   
       def computeValue(self,ob,d,a): raise AttributeError, a
   
   
   
   
   def getComponentPath(component, relativeTo=None):
   
       """Get 'ComponentName' that would traverse from 'relativeTo' to 'component'
   
       If 'relativeTo' is 'None' or not supplied, the path returned is relative
       to the root component of 'component'.  Note that if supplied, 'relativeTo'
       must be an ancestor (parent, parent's parent, etc.) of 'component'."""
   
       path = []; root=None
   
       if relativeTo is None:
           root = getRootComponent(component)
   
       c = component
   
       while 1:
   
           if c is root:
               path.append(''); break
   
           elif c is relativeTo:
               break
   
           path.append(getComponentName(c) or '*')
   
 def Provider(callable):          c = getParentComponent(c)
     return lambda foundIn, forObj: callable(forObj)  
   
           if c is None:
               break
   
       path.reverse()
       return ComponentName(path)
   
 def CachingProvider(callable, weak=False):  
   
     def provider(foundIn, forObj):  
   
         fid = id(foundIn)  
         utility = provider.cache.get(fid)  
   
         if utility is None:  
             utility = provider.cache[fid] = callable(foundIn)  
   
         return utility  
   
     if weak:  
         provider.cache = WeakValueDictionary()  
     else:  
         provider.cache = {}  
   
     return provider  
   
   
   def Constant(value, **kw):
       """DEPRECATED: Use 'Make(lambda: value)' instead"""
       return Make(lambda: value, **kw)
   
   
   class ModuleAsNode(object):
   
       protocols.advise(
           instancesProvide=[IBindingNode],
           asAdapterForTypes=[ModuleType],
       )
   
       def __init__(self,ob,protocol):
           self.module = ob
   
       def getParentComponent(self):
           m = '.'.join(self.module.__name__.split('.')[:-1])
           if m: return importString(m)
           return None
   
       def getComponentName(self):
           return self.module.__name__.split('.')[-1]
   
   
       # XXX it's not clear if we really need the below, since
       # XXX they are not currently used with an adaptation
   
       def _getConfigData(self, forObj, configKey):
           return NOT_FOUND
   
       def notifyUponAssembly(self,child):
           child.uponAssembly()
   
   
   
Line 86 
Line 127 
   
     try:      try:
         gpc = component.getParentComponent          gpc = component.getParentComponent
   
     except AttributeError:      except AttributeError:
         pass  
           component = adapt(component,IBindingNode,None)
   
           if component is not None:
               return component.getParentComponent()
   
     else:      else:
         return gpc()          return gpc()
   
   
   def getComponentName(component):
   
       """Return name of 'component', or 'None' if root or non-component"""
   
       try:
           gcn = component.getComponentName
   
       except AttributeError:
   
           component = adapt(component,IBindingNode,None)
   
           if component is not None:
               return component.getComponentName()
   
       else:
           return gcn()
   
   
   
   
   
   
   
 def getRootComponent(component):  def getRootComponent(component):
   
     """Return the root component of the tree 'component' belongs to"""      """Return the root component of the tree 'component' belongs to"""
Line 105 
Line 175 
     return component      return component
   
   
 def globalLookup(name, component=None):  
   
     """Lookup 'name' in global 'InitialContext', w/'component' in environ"""  
   
     from peak.naming.api import InitialContext  def notifyUponAssembly(parent,child):
   
       """Call 'child.uponAssembly()' as soon as 'parent' knows all its parents"""
   
       try:
           nua = parent.notifyUponAssembly
   
       except AttributeError:
   
           parent = getParentComponent(parent)
   
     return InitialContext(RELATIVE_TO_COMPONENT=component).lookup(name)          if parent is None:
               child.uponAssembly()
           else:
               notifyUponAssembly(parent,child)
   
       else:
           nua(child)
   
   
   
Line 123 
Line 205 
   
 def acquireComponent(component, name):  def acquireComponent(component, name):
   
     """Acquire 'name' relative to 'component', w/fallback to globalLookup()"""      """Acquire 'name' relative to 'component', w/fallback to naming.lookup()
   
       'name' is looked for as an attribute of 'component'.  If not found,
       the component's parent will be searched, and so on until the root component
       is reached.  If 'name' is still not found, and the root component
       implements 'config.IConfigurationRoot', the name will be looked up in the
       default naming context, if any.  Otherwise, a 'NameNotFound' error will be
       raised."""
   
     target = component      prev = target = component
   
     while target is not None:      while target is not None:
   
Line 134 
Line 223 
         if ob is not NOT_FOUND:          if ob is not NOT_FOUND:
             return ob              return ob
   
           prev = target
         target = getParentComponent(target)          target = getParentComponent(target)
   
     else:      else:
         return globalLookup(name, component)          return adapt(
               prev, IConfigurationRoot, NullConfigRoot
           ).nameNotFound(
               prev, name, component
           )
   
   
   
Line 150 
Line 244 
   
   
   
   class ComponentName(AbstractName):
   
       """Path between components
   
       Component Path Syntax
   
           Paths are '"/"' separated attribute names.  Path segments of '"."' and
           '".."' mean the same as they do in URLs.  A leading '"/"' (or a
           compound name beginning with an empty path segment), will be treated
           as an "absolute path" relative to the component's root component.
   
           Paths beginning with anything other than '"/"', '"./"', or '"../"' are
           acquired, which means that the first path segment will be looked
           up using 'acquireComponent()' before processing the rest of the path.
           (See 'acquireComponent()' for more details.)  If you do not want
           a name to be acquired, simply prefix it with './' so it is relative
           to the starting object.
   
           All path segments after the first are interpreted as attribute names
           to be looked up, beginning at the component referenced by the first
           path segment.  '.' and '..' are interpreted the same as for the first
           path segment.
       """
   
       nameKind = COMPOUND_KIND
   
       syntax = PathSyntax(
           direction = 1,
           separator = '/',
       )
   
       protocols.advise(
           instancesProvide=[IComponentKey]
       )
   
   
   
   
 ComponentNameSyntax = Syntax(  
     direction = 1,  
     separator = '/',  
       def findComponent(self, component, default=NOT_GIVEN):
   
           if not self:  # empty name refers to self
               return component
   
           parts = iter(self)
           attr = parts.next()                 # first part
           pc = _getFirstPathComponent(attr)
   
   
           if pc:  ob = pc(component)
           else:   ob = acquireComponent(component, attr)
   
           resolved = []
           append = resolved.append
   
           try:
               for attr in parts:
                   pc = _getNextPathComponent(attr)
                   if pc:  ob = pc(ob)
                   else:   ob = getattr(ob,attr)
                   append(attr)
   
           except AttributeError:
   
               if default is not NOT_GIVEN:
                   return default
   
               raise exceptions.NameNotFound(
                   resolvedName = ComponentName(resolved),
                   remainingName = ComponentName([attr] + [a for a in parts]),
                   resolvedObj = ob
 )  )
   
           return ob
   
   
   
   
 def ComponentName(nameStr):  
     return CompoundName(nameStr, ComponentNameSyntax)  
   
   
 _getFirstPathComponent = dict( (  _getFirstPathComponent = dict( (
Line 186 
Line 340 
 ) ).get  ) ).get
   
   
   def lookupComponent(component, name, default=NOT_GIVEN, adaptTo=None,
       creationName=None, suggestParent=True):
   
       """Lookup 'name' as a component key relative to 'component'
   
       'name' can be any object that implements or is adaptable to 'IComponentKey'.
       Such objects include 'peak.naming' names, interface objects, property
       names, and any custom objects you may create that implement 'IComponentKey'.
       Strings will be converted to a URL, or to a 'ComponentName' if they have
       no URL prefix.  If the key cannot be found, an 'exceptions.NameNotFound'
       error will be raised unless a 'default' other than 'NOT_GIVEN' is provided.
       """
   
       result = adapt(name, IComponentKey).findComponent( component, default )
   
       if adaptTo is not None:
           result = adapt(result,adaptTo)
   
       if suggestParent:
           suggestParentComponent(component,creationName,result)
   
       return result
   
   
   
   
   
   # Declare that strings should be converted to names (with a default class
   # of ComponentName), in order to use them as component keys
   #
   protocols.declareAdapter(
       lambda ob, proto: toName(ob, ComponentName, 1),
       provides = [IComponentKey],
       forTypes = [str, unicode],
   )
   
   
   class ConfigFinder(object):
   
       """Look up utilities or properties"""
   
       __slots__ = 'ob'
   
       protocols.advise(
           instancesProvide = [IComponentKey],
           asAdapterForProtocols = [IConfigKey]
       )
   
 def lookupComponent(component, name):      def __init__(self, ob, proto):
           self.ob = ob
   
     """Lookup 'name' relative to 'component'      def findComponent(self, component, default=NOT_GIVEN):
           return config.lookup(component, self.ob, default)
   
     'name' can be any name acceptable to the 'peak.naming' package, or an      def __repr__(self):
     Interface object.  Strings and 'CompoundName' names will be interpreted          return repr(self.ob)
     as paths relative to the starting component.  An empty name will return  
     the starting component.  Interfaces will be lookedup using  
     'component.acquireUtility()'.  All other kinds of names, including URL  
     strings and 'CompositeName' instances, will be looked up using  
     'binding.globalLookup()'.  
   
     Regardless of how the lookup is processed, a 'naming.NameNotFoundException'  
     will be raised if the name cannot be found.  
   
     Component Path Syntax  
   
         Paths are '/'-separated attribute names.  Path segments of '.' and  
         '..' mean the same as they do in URLs.  A leading '/' (or a  
         CompoundName beginning with an empty path segment), will be treated  
         as an "absolute path" relative to the component's root component.  
   
         Paths beginning with anything other than '/', './', or '../' are  
         acquired, which means that the first path segment will be looked  
         up using 'acquireComponent()' before processing the rest of the path.  
         (See 'acquireComponent()' for more details.)  If you do not want  
         a name to be acquired, simply prefix it with './' so it is relative  
         to the starting object.  
   
         All path segments after the first are interpreted as attribute names  
         to be looked up, beginning at the component referenced by the first  
         path segment.  '.' and '..' are interpreted the same as for the first  
         path segment."""  
   
     if isinstance(name, InterfaceClass):  
         utility = component.acquireUtility(name)  
         if utility is None:  
             raise NameNotFoundException(name, resolvedObj = component)  
   
     parsedName = toName(name, ComponentName, 1)  
   
     # URL's and composite names must be handled globally  
   
     if not parsedName.isCompound:  
         return globalLookup(name, component)  
   
     if not parsedName:  
         # empty name refers to self  
         return component  
   
     parts = iter(parsedName)  
   
     attr = parts.next() # first part  class PluginKeys(object):
     pc = _getFirstPathComponent(attr)      """Component key that finds the keys of plugins matching a given key
   
     if pc:      Usage::
         ob = pc(component)  
     else:  
         ob = acquireComponent(component,attr)  
   
     resolved = []          # get a sorted list of the keys to all 'foo.bar' plugins
     append = resolved.append          pluginNames = binding.Obtain( binding.PluginKeys('foo.bar') )
   
     try:          # get an unsorted list of the keys to all 'foo.bar' plugins
         for attr in parts:          pluginNames = binding.Obtain(
             pc = _getNextPathComponent(attr)              binding.PluginKeys('foo.bar', sortBy=None)
             if pc:          )
                 ob = pc(ob)  
             else:  
                 ob = getattr(ob,attr)  
             append(attr)  
   
     except AttributeError:      'sortBy' is either a false value or a callable that will be applied to
       each key to get a value for sorting purposes.  If set to a false value,
       the keys will be in the same order as yielded by 'config.iterKeys()'.
       'sortBy' defaults to 'str', which means the keys will be sorted based
       on their string form.
       """
   
         raise NameNotFoundException(      protocols.advise(
             resolvedName = ComponentName(resolved),          instancesProvide = [IComponentKey],
             remainingName = ComponentName([attr] + [a for a in parts]),  
             resolvedObj = ob  
         )          )
   
     return ob      def __init__(self, configKey, sortBy=str):
           self.configKey = adapt(configKey, IConfigKey)
           self.sortBy = sortBy
   
 class bindTo(Once):  
   
     """Automatically look up and cache a relevant component      def findComponent(self, component, default=NOT_GIVEN):
   
         Usage::          keys = config.iterKeys(component, self.configKey)
   
             class someClass(binding.Component):          if self.sortBy:
               sortBy = self.sortBy
               keys = [(sortBy(k),k) for k in keys]
               keys.sort()
               return [k for (sortedBy,k) in keys]
   
                 thingINeed = binding.bindTo("path/to/service")          return list(keys)
   
                 getOtherThing = binding.bindTo("some/thing", weak=True)  class PluginsFor(PluginKeys):
   
         'someClass' can then refer to 'self.thingINeed' instead of      """Component key that finds plugins matching a configuration key
         calling 'self.lookupComponent("path/to/service")' repeatedly.  
         It can also do 'self.getOtherThing()' to get '"some/thing"'.  
         (The 'weak' argument, if true, means to bind to a weak reference.)  
     """  
   
     singleValue = True      Usage::
   
     def __init__(self,targetName,weak=False,provides=None):          # get a list of 'my.plugins.X' plugins, sorted by property name
           myPlugins = binding.Obtain( binding.PluginsFor('my.plugins') )
   
         self.targetNames = (targetName,)          # get an unsorted list of all 'foo.bar' plugins
         self.weak = weak          myPlugins = binding.Obtain(
         self._provides=provides              binding.PluginsFor('foo.bar', sortKeys=False)
           )
   
       This key type works similarly to 'PluginKeys()', except that it returns the
       plugins themselves, rather than their configuration keys.
   
       'sortBy' is either a false value or a callable that will be applied to
       each plugin's key to get a value for sorting purposes.  If set to a false
       value,  plugins will be in the same order as their keys are yielded by
       'config.iterKeys()'.  'sortBy' defaults to 'str', which means the plugins
       will be sorted based on the string form of the keys used to retrieve them.
       """
   
       def findComponent(self, component, default=NOT_GIVEN):
           keys = super(PluginsFor,self).findComponent(component)
           return [adapt(k,IComponentKey).findComponent(component) for k in keys]
   
   
   
Line 326 
Line 489 
   
   
   
     def computeValue(self, obj, instanceDict, attrName):  
   
         names = self.targetNames  class Obtain(Attribute):
         obs   = map(obj.lookupComponent, names)      """'Obtain(componentKey,[default=value])' - finds/caches a needed component
   
         for name,newOb in zip(names, obs):      Usage examples::
   
             if newOb is NOT_FOUND:          class someClass(binding.Component):
   
                 del instanceDict[attrName]              thingINeed = binding.Obtain("path/to/service")
                 raise NameNotFoundError(attrName, resolvedName = name)              otherThing = binding.Obtain(IOtherThing)
               aProperty  = binding.Obtain(PropertyName('some.prop'), default=42)
   
       'someClass' instances can then refer to their attributes, such as
       'self.thingINeed', instead of repeatedly calling
       'self.lookupComponent(someKey)'.
   
       The initial argument to the 'Obtain' constructor must be adaptable to
       'binding.IComponentKey'.  If a 'default' keyword argument is supplied,
       it will be used as the default in case the specified component key is not
       found.
   
       XXX need to document IComponentKey translations somewhere... probably
           w/IComponentKey"""
   
       default = NOT_GIVEN
       targetName = None
   
       def __init__(self,targetName,**kw):
           self.targetName = adapt(targetName, IComponentKey)
           super(Obtain,self).__init__(**kw)
   
             if self.singleValue:      def computeValue(self, obj, instanceDict, attrName):
           return self.targetName.findComponent(obj, self.default)
   
                 if self.weak:      def __repr__(self):
                     return ref(newOb)          if self.__doc__:
               return "binding.Obtain(%r):\n\n%s" % (self.targetName,self.__doc__)
                 else:                  else:
                     return newOb              return "binding.Obtain(%r)" % (self.targetName,)
   
         if self.weak:  bindTo = Obtain     # XXX DEPRECATED
             obs = map(ref,obs)  
   
         return tuple(obs)  def bindSequence(*targetNames, **kw):
       """DEPRECATED: use binding.Obtain([key1,key2,...])"""
       return Obtain(targetNames, **kw)
   
   
   class SequenceFinder(object):
   
       """Look up sequences of component keys"""
   
       __slots__ = 'ob'
   
       protocols.advise(
           instancesProvide = [IComponentKey],
           asAdapterForProtocols = [protocols.sequenceOf(IComponentKey)]
       )
   
       def __init__(self, ob, proto):
           self.ob = ob
   
       def findComponent(self, component, default=NOT_GIVEN):
           return tuple([ob.findComponent(component, default) for ob in self.ob])
   
   
   def whenAssembled(func, **kw):
       """DEPRECATED: use 'Make(func, uponAssembly=True)'"""
       kw['uponAssembly'] = True
       return Make(func, **kw)
   
   
   
Line 367 
Line 567 
   
   
   
 class bindToNames(bindTo):  
   
     """Automatically look up and cache a sequence of services by name  
   
         Usage::  
   
             class someClass(binding.AutoCreated):  
   
                 thingINeed = binding.bindToNames(  
                     "path/to/service", "another/path", ...  
                 )  
   
         'someClass' can then refer to 'self.thingINeed' to get a tuple of  class Delegate(Make):
         services instead of calling 'self.lookupComponent()' on a series of  
         names.  As with 'bindTo()', a 'weak' keyword argument can be set to  
         indicate that the sequence should consist of weak references to the  
         named objects.  
     """  
   
     singleValue = False      """Delegate attribute to the same attribute of another object
   
     def __init__(self, *targetNames, **kw):      Usage::
         self.targetNames = targetNames  
         self.weak = kw.get('weak')  
         self._provides = kw.get('provides')  
   
           class PasswordFile(binding.Component):
               shadow = binding.Obtain('config:etc.shadow/')
               checkPwd = changePwd = binding.Delegate('shadow')
   
       The above is equivalent to this longer version::
   
           class PasswordFile(binding.Component):
               shadow = binding.Obtain('config:etc.shadow/')
               checkPwd = binding.Obtain('shadow/checkPwd')
               changePwd = binding.Obtain('shadow/changePwd')
   
       Because 'Delegate' uses the attribute name being looked up, you do not
       need to create a separate binding for each attribute that is delegated,
       as you do when using 'Obtain()'."""
   
       delegateAttr = None
   
       def __init__(self, delegateAttr, **kw):
           def delegate(s,d,a):
               return getattr(getattr(s,delegateAttr),a)
           super(Delegate,self).__init__(delegate,delegateAttr=delegateAttr,**kw)
   
       def __repr__(self):
           if self.__doc__:
               return "binding.Delegate(%r):\n\n%s" % (
                   self.delegateAttr,self.__doc__
               )
           else:
               return "binding.Delegate(%r)" % (self.delegateAttr,)
   
   
   delegateTo = Delegate   # XXX DEPRECATED; backward compat.
   
   
   
   def Acquire(key, **kw):
       """DEPRECATED: use Obtain(key, offerAs=[key])"""
       key = adapt(key,IConfigKey)
       kw['offerAs'] = [key]   # XXX should check that kwarg wasn't supplied
       return Obtain(key,**kw)
   
   def bindToParent(level=1, **kw):
       """DEPRECATED: use binding.Obtain('..')"""
       return Obtain('/'.join(['..']*level), **kw)
   
   def bindToSelf(**kw):
       """DEPRECATED: use binding.Obtain('.')"""
       return Obtain('.', **kw)
   
   def bindToProperty(propName, default=NOT_GIVEN, **kw):
       """DEPRECATED: use binding.Obtain(PropertyName(propName))"""
       kw['default'] = default
       return binding.Obtain(PropertyName(propName), **kw)
   
   
   
Line 408 
Line 638 
   
   
   
 class bindToParent(WeakBinding):  
   
     """Look up and cache a weak ref to the nth-level parent component  
   
         Usage::  
   
             class someClass(binding.AutoCreated):  
   
                 grandPa = binding.bindToParent(2)  
   
        'someClass' can then refer to 'self.grandPa' instead of calling  
        'self.getParentComponent().getParentComponent()'.  
     """  
   
     def __init__(self,level=1,provides=None):  
         self.level = level  
         self._provides = provides  
   
     def computeValue(self, obj, instDict, attrName):  
   
         for step in range(self.level):  
             newObj = obj.getParentComponent()  
             if newObj is None: break  
             obj = newObj  
   
         return obj  
   
   
 def bindToSelf(provides=None):  
   
     """Weak reference to the 'self' object  
   
     This is just a shortcut for 'bindToParent(0)', and does pretty much what  
     you'd expect.  It's handy for objects that provide default support for  
     various interfaces in the absence of an object to delegate to.  The object  
     can refer to 'self.delegateForInterfaceX.someMethod()', and have  
     'delegateForInterfaceX' be a 'bindToSelf()' by default."""  
   
     return bindToParent(0,provides)  
   
   
 class requireBinding(Once):  class Require(Attribute):
   
     """Placeholder for a binding that should be (re)defined by a subclass"""      """Placeholder for a binding that should be (re)defined by a subclass"""
   
     def __init__(self,description="",provides=None):      description = ''
         self.description = description  
         self._provides = provides  
   
     def computeValue(self, obj, instanceDict, attrName):      def __init__(self, description="", **kw):
           kw['description'] = description
           super(Require,self).__init__(**kw)
   
   
       def computeValue(self, obj, instanceDict, attrName):
         raise NameError("Class %s must define %s; %s"          raise NameError("Class %s must define %s; %s"
             % (obj.__class__.__name__, attrName, self.description)              % (obj.__class__.__name__, attrName, self.description)
         )          )
   
       def __repr__(self):
           if self.__doc__:
               return "binding.Require(%r):\n\n%s" % (
                   self.description,self.__doc__
               )
           else:
               return "binding.Require(%r)" % (self.description,)
   
   requireBinding = Require    # XXX DEPRECATED
   
   
   
   def bindToUtilities(iface, **kw):
       """DEPRECATED: bind list of all 'iface' utilities above the component"""
   
       return Make(lambda self: list(config.iterValues(self,iface)), **kw)
   
   
   
Line 477 
Line 695 
   
   
   
   class _Base(object):
   
       """Basic attribute management and "active class" support"""
   
       __metaclass__ = ActiveClass
   
       protocols.advise(
           instancesProvide = [IBindableAttrs]
       )
   
       def _setBinding(self, attr, value, useSlot=False):
   
           self._bindingChanging(attr,value,useSlot)
   
           if useSlot:
               getattr(self.__class__,attr).__set__(self,value)
   
           else:
               self.__dict__[attr] = value
   
   
       def _getBinding(self, attr, default=None, useSlot=False):
   
           if useSlot:
               val = getattr(self,attr,default)
   
           else:
               val = self.__dict__.get(attr,default)
   
           if val is not default:
   
               val = self._postGet(attr,val,useSlot)
   
               if val is NOT_FOUND:
                   return default
   
           return val
   
   
   
   
       def _getBindingFuncs(klass, attr, useSlot=False):
           if useSlot:
               d = getattr(klass,attr)
           else:
               d = _proxy(attr)
           return d.__get__, d.__set__, d.__delete__
   
       _getBindingFuncs = classmethod(_getBindingFuncs)
   
   
       def _delBinding(self, attr, useSlot=False):
   
           self._bindingChanging(attr, NOT_FOUND, useSlot)
   
           if useSlot:
               d = getattr(self.__class__,attr).__delete__
   
               try:
                   d(self)
               except AttributeError:
                   pass
   
           elif attr in self.__dict__:
               del self.__dict__[attr]
   
       def _hasBinding(self,attr,useSlot=False):
   
           if useSlot:
               return hasattr(self,attr)
           else:
               return attr in self.__dict__
   
   
       def _bindingChanging(self,attr,newval,isSlot=False):
           pass
   
   
       def _postGet(self,attr,value,isSlot=False):
           return value
   
   
 class Component(object):  class Component(_Base):
   
     """Thing that can be composed into a component tree, w/binding & lookups"""      """Thing that can be composed into a component tree, w/binding & lookups"""
   
     __metaclasses__  = (      protocols.advise(
         meta.AssertInterfaces, meta.ActiveDescriptors          classProvides = [IComponentFactory],
           instancesProvide = [IComponent]
       )
   
   
       def __init__(self, parentComponent=NOT_GIVEN, componentName=None, **kw):
   
           # Set up keywords first, so state is sensible
           if kw:
   
               klass = self.__class__
   
               for k,v in kw.iteritems():
                   if hasattr(klass,k):
                       setattr(self,k,v)
                   else:
                       raise TypeError(
                           "%s constructor has no keyword argument %s" %
                           (klass, k)
     )      )
   
     # use the global lookupComponent function as a method          # set our parent component and possibly invoke assembly events
           if parentComponent is not NOT_GIVEN or componentName is not None:
               self.setParentComponent(parentComponent,componentName)
   
     lookupComponent = lookupComponent      lookupComponent = lookupComponent
   
     def setParentComponent(self,parent):  
         from weakref import ref  
         self.getParentComponent = ref(parent)  
   
   
   
   
   
   
       def fromZConfig(klass, section):
   
           """Classmethod: Create an instance from a ZConfig 'section'"""
   
           # ZConfig uses unicode for keys and defaults unsupplied values to None
           data = dict([(str(k),v) for k,v in section.__dict__.items()
               if v is not None])
   
           if not hasattr(klass,'_name') and '_name' in data:
               del data['_name']
   
           if not hasattr(klass,'_matcher') and '_matcher' in data:
               del data['_matcher']
   
           return klass(**data)
   
       fromZConfig = classmethod(fromZConfig)
   
   
       def setParentComponent(self, parentComponent, componentName=None,
           suggest=False):
   
           pc = self.__parentSetting
   
           if pc is NOT_GIVEN:
               self.__parentSetting = parentComponent
               self.__componentName = componentName
               self.__parentComponent  # lock and invoke assembly events
               return
   
           elif suggest:
               return
   
           raise AlreadyRead(
               "Component %r already has parent %r; tried to set %r"
               % (self,pc,parentComponent)
           )
   
       __parentSetting = NOT_GIVEN
       __componentName = None
   
       def __parentComponent(self,d,a):
   
           parent = self.__parentSetting
           if parent is NOT_GIVEN:
               parent = self.__parentSetting = None
   
           d[a] = parent
           if parent is None:
               self.uponAssembly()
           elif (self.__class__.__attrsToBeAssembled__
               or self._getBinding('__objectsToBeAssembled__')):
                   notifyUponAssembly(parent,self)
   
           return parent
   
       __parentComponent = Make(__parentComponent, suggestParent=False)
   
   
     def getParentComponent(self):      def getParentComponent(self):
         return None          return self.__parentComponent
   
     def _componentName(self, dict, name):      def getComponentName(self):
         return self.__class__.__name__.split('.')[-1]          return self.__componentName
   
     _componentName = Once(_componentName)      __instance_offers__ = Make(
           'peak.config.config_components:PropertyMap', offerAs=[IPropertyMap]
       )
   
     __instance_provides__ = New(EigenRegistry)  
   
     __class_provides__ = EigenRegistry()  
   
   
   
Line 529 
Line 900 
   
   
   
       def _configKeysMatching(self, configKey):
   
           """Iterable over defined keys that match 'configKey'
   
     def acquireUtility(self, iface, forObj=None, localLookup=True):          A key 'k' in the map is considered to "match" 'configKey' if any of
           'k.parentKeys()' are listed as keys in 'configKey.registrationKeys()'.
           You must not change the configuration map while iterating over the
           keys.  Also, keep in mind that only explicitly-registered keys are
           returned; for instance, load-on-demand rules will only show up as
           wildcard keys."""
   
         if forObj is None:          maps = [self.__class__.__class_offers__]
             forObj=self          attr = self._getBinding('__instance_offers__')
   
         if localLookup:          if attr:
               maps.append(attr)
   
             provider = self.__instance_provides__.get(iface)          yielded = {}
   
             if provider is not None:          for cMap in maps:
                 return provider(self,forObj)              for key in cMap._configKeysMatching(configKey):
                   if key in yielded:
                       continue
                   yield key
                   yielded[key] = 1
   
             attr = self.__class_provides__.get(iface)  
   
             if attr is not None:  
   
                 utility = getattr(self,attr)  
   
                 if utility is not NOT_FOUND:  
                     return utility  
   
         parent = self.getParentComponent()  
   
         if parent is None:  
             parent = config.getLocal(self)  
   
         return parent.acquireUtility(iface,forObj)  
   
   
     def registerProvider(self, ifaces, provider):  
         self.__instance_provides__.register(ifaces, provider)  
   
   
   
Line 569 
Line 941 
   
   
   
       def _getConfigData(self, forObj, configKey):
   
           attr = self._getBinding('__instance_offers__')
   
           if attr:
               value = attr.getValueFor(forObj, configKey)
   
 class AutoCreatable(type):              if value is not NOT_FOUND:
                   return value
   
     """Metaclass for components which auto-create when used"""          attr = self.__class__.__class_offers__.lookup(configKey)
   
     def __get__(self, obj, typ=None):          if attr:
               return getattr(self, attr, NOT_FOUND)
   
         if obj is None:          return NOT_FOUND
             return self  
   
         newOb = self(obj)  
   
         obj.__dict__[newOb._componentName] = newOb      def registerProvider(self, configKey, provider):
         return newOb          self.__instance_offers__.registerProvider(configKey, provider)
   
   
 class AutoCreated(Component):      def notifyUponAssembly(self,child):
   
     """Component that auto-creates itself in instances of its containing class          tba = self.__objectsToBeAssembled__
     """  
           if tba is None:
               child.uponAssembly()    # assembly has already occurred
           else:
               tba.append(child)       # save reference to child for callback
   
               if (len(tba)==1 and self.__parentSetting is not NOT_GIVEN
                   and len(tba)==1 and not self.__class__.__attrsToBeAssembled__
               ):
                   # Make sure our parent calls us, since we need to call a
                   # child now, but would not have been registered ourselves.
                   notifyUponAssembly(self.getParentComponent(),self)
   
   
   
   
       def uponAssembly(self):
           """Don't override this unless you can handle the reentrancy issues!"""
           tba = self.__objectsToBeAssembled__
   
           if tba is None:
               return
   
     __metaclasses__ = AutoCreatable,          self.__objectsToBeAssembled__ = None
   
     def __init__(self, parent=None):          try:
               while tba:
                   ob = tba.pop()
                   try:
                       ob.uponAssembly()
                   except:
                       tba.append(ob)
                       raise
   
               for attr in self.__class__.__attrsToBeAssembled__:
                   getattr(self,attr)
   
           except:
               self.__objectsToBeAssembled__ = tba
               raise
   
         super(AutoCreated,self).__init__()      __objectsToBeAssembled__ = Make(list)
   
         if parent is not None:  
             self.setParentComponent(parent)  
   
       def __attrsToBeAssembled__(klass,d,a):
           aa = {}
           map(aa.update, getInheritedRegistries(klass, '__attrsToBeAssembled__'))
   
           for attrName, descr in klass.__class_descriptors__.items():
               notify = getattr(descr,'uponAssembly',False)
               if notify: aa[attrName] = True
   
           return aa
   
       __attrsToBeAssembled__ = classAttr(Make(__attrsToBeAssembled__))
   
   
       def __class_offers__(klass,d,a):
   
           return ImmutableConfig(
               baseMaps = getInheritedRegistries(klass, '__class_offers__'),
               items = [(adapt(key,IConfigKey), attrName)
                   for attrName, descr in klass.__class_descriptors__.items()
                       for key in getattr(descr,'offerAs',())
               ]
           )
   
   
       __class_offers__ = classAttr(Make(__class_offers__))
   
   
   
   
   
 modules.setupModule()  Base = Component    # XXX backward compatibility; deprecated
   
   
   


Generate output suitable for use with a patch program
Legend:
Removed from v.469  
changed lines
  Added in v.1576

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help