[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 713, Wed Nov 20 01:11:08 2002 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 weakref import WeakValueDictionary
 from peak.naming.interfaces import NameNotFoundException  
 from peak.util.EigenData import EigenRegistry  from peak.naming.names import toName, Syntax, Name
   from peak.util.EigenData import EigenRegistry, EigenCell
 from Interface import Interface  from peak.config.interfaces import IConfigKey, IPropertyMap
 from peak.api import config, NOT_FOUND  
   
   
 __all__ = [  __all__ = [
     'Component','AutoCreated','Provider','CachingProvider',      'Base', 'Component','AutoCreated',
     'bindTo', 'requireBinding', 'bindToNames', 'bindToParent', 'bindToSelf',      'bindTo', 'requireBinding', 'bindSequence', 'bindToParent', 'bindToSelf',
     'getRootComponent', 'getParentComponent', 'lookupComponent',      'getRootComponent', 'getParentComponent', 'lookupComponent',
     'acquireComponent', 'globalLookup'      'acquireComponent', 'globalLookup', 'findUtility', 'findUtilities',
       'bindToUtilities', 'bindToProperty', 'iterParents', 'Constant',
       'getComponentName', 'getComponentPath', 'Acquire', 'ComponentName',
 ]  ]
   
   
 InterfaceClass = Interface.__class__  
   
   
   
Line 39 
Line 39 
   
   
   
 def Provider(callable):  def getComponentPath(component, relativeTo=None):
     return lambda foundIn, forObj: callable(forObj)  
   
       """Get 'ComponentName' that would traverse from 'relativeTo' to 'component'
   
 def CachingProvider(callable, weak=False):      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'."""
   
     def provider(foundIn, forObj):      path = []; root=None
   
         fid = id(foundIn)      if relativeTo is None:
         utility = provider.cache.get(fid)          root = getRootComponent(component)
   
         if utility is None:      for c in iterParents(component):
             utility = provider.cache[fid] = callable(foundIn)  
   
         return utility          if c is root:
               path.append(''); break
   
     if weak:          elif c is relativeTo:
         provider.cache = WeakValueDictionary()              break
     else:  
         provider.cache = {}  
   
     return provider          path.append(getComponentName(c) or '*')
   
       path.reverse()
       return ComponentName(path)
   
   
   
Line 78 
Line 80 
   
   
   
   def Constant(provides, value, doc=None):
       """Supply a constant as a property or utility"""
       return Once(lambda s,d,a: value, provides=provides, doc=doc)
   
   
 def getParentComponent(component):  def getParentComponent(component):
Line 92 
Line 97 
         return gpc()          return gpc()
   
   
   def getComponentName(component):
   
       """Return name of 'component', or 'None' if root or non-component"""
   
       try:
           gcn = component.getComponentName
       except AttributeError:
           pass
       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 104 
Line 121 
   
     return component      return component
   
   def globalLookup(name, component=None, targetName=None):
   
 def globalLookup(name, component=None):      """Lookup 'name' in global 'InitialContext', relative to 'component'"""
   
     """Lookup 'name' in global 'InitialContext', w/'component' in environ"""  
   
     from peak.naming.api import InitialContext  
   
     return InitialContext(RELATIVE_TO_COMPONENT=component).lookup(name)  
   
   
       return naming.lookup(name, component,
           creationParent=component, creationName=targetName
       )
   
   
   
Line 121 
Line 135 
   
   
   
 def acquireComponent(component, name):  def acquireComponent(name, component=None, targetName=None):
   
     """Acquire 'name' relative to 'component', w/fallback to globalLookup()"""      """Acquire 'name' relative to 'component', w/fallback to globalLookup()"""
   
Line 137 
Line 151 
         target = getParentComponent(target)          target = getParentComponent(target)
   
     else:      else:
         return globalLookup(name, component)          return globalLookup(name, component, targetName)
   
   
   
Line 148 
Line 162 
   
   
   
   def iterParents(component=None):
   
       """Return iterator for all parents of 'component', including config roots
       """
   
       last = component
   
       for part in "..":
   
           while component is not None:
   
               yield component
   
               last      = component
               component = getParentComponent(component)
   
           component = config.getLocal(last)
   
   
   def findUtilities(iface, component=None):
   
       """Return iterator over all utilities providing 'iface' for 'component'"""
   
       forObj = component
   
       for component in iterParents(component):
   
           try:
               utility = component._getConfigData
           except AttributeError:
               continue
   
 ComponentNameSyntax = Syntax(          utility = utility(iface, forObj)
     direction = 1,  
     separator = '/',  
 )  
   
           if utility is not NOT_FOUND:
               yield utility
   
 def ComponentName(nameStr):  
     return CompoundName(nameStr, ComponentNameSyntax)  
   
   
 _getFirstPathComponent = dict( (  
     ('',   getRootComponent),  
     ('.',  lambda x:x),  
     ('..', getParentComponent),  
 ) ).get  
   
   
 _getNextPathComponent = dict( (  def findUtility(iface, component=None, default=NOT_GIVEN):
     ('',   lambda x:x),  
     ('.',  lambda x:x),  
     ('..', getParentComponent),  
 ) ).get  
   
       """Return first utility supporting 'iface' for 'component', or 'default'"""
   
       for u in findUtilities(iface, component):
           return u
   
       if default is NOT_GIVEN:
           raise exceptions.NameNotFound(iface, resolvedObj = component)
   
       return default
   
   
   
Line 203 
Line 229 
   
   
   
 def lookupComponent(component, name):  
   
     """Lookup 'name' relative to 'component'  
   
     'name' can be any name acceptable to the 'peak.naming' package, or an  
     Interface object.  Strings and 'CompoundName' names will be interpreted  
     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.  
   
   
   
   
   
   
   
   
   
   
   class ComponentName(Name):
   
       """Path between components
   
     Component Path Syntax      Component Path Syntax
   
         Paths are '/'-separated attribute names.  Path segments of '.' and          Paths are '"/"' separated attribute names.  Path segments of '"."' and
         '..' mean the same as they do in URLs.  A leading '/' (or a          '".."' mean the same as they do in URLs.  A leading '"/"' (or a
         CompoundName beginning with an empty path segment), will be treated          compound name beginning with an empty path segment), will be treated
         as an "absolute path" relative to the component's root component.          as an "absolute path" relative to the component's root component.
   
         Paths beginning with anything other than '/', './', or '../' are          Paths beginning with anything other than '"/"', '"./"', or '"../"' are
         acquired, which means that the first path segment will be looked          acquired, which means that the first path segment will be looked
         up using 'acquireComponent()' before processing the rest of the path.          up using 'acquireComponent()' before processing the rest of the path.
         (See 'acquireComponent()' for more details.)  If you do not want          (See 'acquireComponent()' for more details.)  If you do not want
Line 235 
Line 265 
         All path segments after the first are interpreted as attribute names          All path segments after the first are interpreted as attribute names
         to be looked up, beginning at the component referenced by the first          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.  '.' and '..' are interpreted the same as for the first
         path segment."""          path segment.
       """
   
     if isinstance(name, InterfaceClass):      isCompound = 1
         utility = component.acquireUtility(name)  
         if utility is None:  
             raise NameNotFoundException(name, resolvedObj = component)  
   
     parsedName = toName(name, ComponentName, 1)      syntax = Syntax(
           direction = 1,
           separator = '/',
       )
   
     # URL's and composite names must be handled globally  
   
   
   
   
   
   
   
   
   
   def lookupComponent(name, component=None):
   
       """Lookup 'name' relative to 'component'
   
       'name' can be any name acceptable to the 'peak.naming' package, or an
       Interface object.  Strings and compound names will be interpreted
       as paths relative to the starting component.  An empty name will return
       the starting component.  Interfaces and Properties will be looked up using
       'findUtility(name, component)'.  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, an 'exceptions.NameNotFound'
       error will be raised if the name cannot be found."""
   
       return _lookupComponent(component, name)
   
   
   
   _getFirstPathComponent = dict( (
       ('',   getRootComponent),
       ('.',  lambda x:x),
       ('..', getParentComponent),
   ) ).get
   
   
   _getNextPathComponent = dict( (
       ('',   lambda x:x),
       ('.',  lambda x:x),
       ('..', getParentComponent),
   ) ).get
   
   
   
   
   
   
   
   
   
   
   def _lookupComponent(component, name, targetName=None):
   
       if IConfigKey.isImplementedBy(name):
           return findUtility(name, component)
   
       parsedName = toName(name, ComponentName, 1)
   
     if not parsedName.isCompound:      if not parsedName.isCompound:
         return globalLookup(name, component)          # URL's and composite names must be handled globally
           return globalLookup(name, component, targetName)
   
     if not parsedName:      if not parsedName:  # empty name refers to self
         # empty name refers to self  
         return component          return component
   
     parts = iter(parsedName)      parts = iter(parsedName)
   
     attr = parts.next() # first part      attr = parts.next() # first part
     pc = _getFirstPathComponent(attr)      pc = _getFirstPathComponent(attr)
   
     if pc:      if pc:  ob = pc(component)
         ob = pc(component)      else:   ob = acquireComponent(attr, component, targetName)
     else:  
         ob = acquireComponent(component,attr)  
   
     resolved = []      resolved = []
     append = resolved.append      append = resolved.append
Line 269 
Line 353 
     try:      try:
         for attr in parts:          for attr in parts:
             pc = _getNextPathComponent(attr)              pc = _getNextPathComponent(attr)
             if pc:              if pc:  ob = pc(ob)
                 ob = pc(ob)              else:   ob = getattr(ob,attr)
             else:  
                 ob = getattr(ob,attr)  
             append(attr)              append(attr)
   
     except AttributeError:      except AttributeError:
   
         raise NameNotFoundException(          raise exceptions.NameNotFound(
             resolvedName = ComponentName(resolved),              resolvedName = ComponentName(resolved),
             remainingName = ComponentName([attr] + [a for a in parts]),              remainingName = ComponentName([attr] + [a for a in parts]),
             resolvedObj = ob              resolvedObj = ob
Line 295 
Line 377 
   
                 thingINeed = binding.bindTo("path/to/service")                  thingINeed = binding.bindTo("path/to/service")
   
                 getOtherThing = binding.bindTo("some/thing", weak=True)  
   
         'someClass' can then refer to 'self.thingINeed' instead of          'someClass' can then refer to 'self.thingINeed' instead of
         calling 'self.lookupComponent("path/to/service")' repeatedly.          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      singleValue = True
   
     def __init__(self,targetName,weak=False,provides=None):  
       def __init__(self,targetName,provides=None,doc=None):
   
         self.targetNames = (targetName,)          self.targetNames = (targetName,)
         self.weak = weak  
         self._provides=provides          self._provides=provides
           self.__doc__ = doc or ("binding.bindTo(%r)" % targetName)
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
     def computeValue(self, obj, instanceDict, attrName):      def computeValue(self, obj, instanceDict, attrName):
   
         names = self.targetNames          names = self.targetNames
         obs   = map(obj.lookupComponent, names)          obs   = [_lookupComponent(obj,n,attrName) for n in names]
   
         for name,newOb in zip(names, obs):          for name,newOb in zip(names, obs):
   
             if newOb is NOT_FOUND:              if newOb is NOT_FOUND:
   
                 del instanceDict[attrName]                  del instanceDict[attrName]
                 raise NameNotFoundError(attrName, resolvedName = name)                  raise exceptions.NameNotFound(attrName, resolvedName = name)
   
             if self.singleValue:              if self.singleValue:
   
                 if self.weak:  
                     return ref(newOb)  
                 else:  
                     return newOb                      return newOb
   
         if self.weak:  
             obs = map(ref,obs)  
   
         return tuple(obs)          return tuple(obs)
   
   
   class bindSequence(bindTo):
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 class bindToNames(bindTo):  
   
     """Automatically look up and cache a sequence of services by name      """Automatically look up and cache a sequence of services by name
   
Line 375 
Line 416 
   
             class someClass(binding.AutoCreated):              class someClass(binding.AutoCreated):
   
                 thingINeed = binding.bindToNames(                  thingINeed = binding.bindSequence(
                     "path/to/service", "another/path", ...                      "path/to/service", "another/path", ...
                 )                  )
   
         'someClass' can then refer to 'self.thingINeed' to get a tuple of          'someClass' can then refer to 'self.thingINeed' to get a tuple of
         services instead of calling 'self.lookupComponent()' on a series of          services instead of calling 'self.lookupComponent()' on a series of
         names.  As with 'bindTo()', a 'weak' keyword argument can be set to          names.
         indicate that the sequence should consist of weak references to the  
         named objects.  
     """      """
   
     singleValue = False      singleValue = False
   
     def __init__(self, *targetNames, **kw):      def __init__(self, *targetNames, **kw):
         self.targetNames = targetNames          self.targetNames = targetNames
         self.weak = kw.get('weak')  
         self._provides = kw.get('provides')          self._provides = kw.get('provides')
           self.__doc__ = kw.get('doc',("binding.bindSequence%s" % `targetNames`))
   
   
   def Acquire(key,doc=None):
   
       if not IConfigKey.isImplementedBy(key):
           raise exceptions.InvalidName("Not a configuration key:", key)
   
       return bindTo(key,key,doc)
   
   
   
Line 406 
Line 449 
   
   
   
   def bindToParent(level=1, name=None, provides=None, doc=None):
   
       """Look up and cache a reference to the nth-level parent component
 class bindToParent(WeakBinding):  
   
     """Look up and cache a weak ref to the nth-level parent component  
   
         Usage::          Usage::
   
Line 422 
Line 463 
        'self.getParentComponent().getParentComponent()'.         'self.getParentComponent().getParentComponent()'.
     """      """
   
     def __init__(self,level=1,provides=None):      def computeValue(obj, instDict, attrName):
         self.level = level  
         self._provides = provides  
   
     def computeValue(self, obj, instDict, attrName):  
   
         for step in range(self.level):          for step in range(level):
             newObj = obj.getParentComponent()              newObj = getParentComponent(obj)
             if newObj is None: break              if newObj is None: break
             obj = newObj              obj = newObj
   
         return obj          return obj
   
       return Once(computeValue, name=name, provides=provides, doc=doc)
   
   
 def bindToSelf(provides=None):  def bindToSelf(name=None, provides=None, doc=None):
   
     """Weak reference to the 'self' object      """Cached reference to the 'self' object
   
     This is just a shortcut for 'bindToParent(0)', and does pretty much what      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      you'd expect.  It's handy for objects that provide default support for
Line 446 
Line 485 
     can refer to 'self.delegateForInterfaceX.someMethod()', and have      can refer to 'self.delegateForInterfaceX.someMethod()', and have
     'delegateForInterfaceX' be a 'bindToSelf()' by default."""      'delegateForInterfaceX' be a 'bindToSelf()' by default."""
   
     return bindToParent(0,provides)      return bindToParent(0,name,provides,doc)
   
   
   
   
 class requireBinding(Once):  class requireBinding(Once):
   
     """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):      def __init__(self,description="",name=None,provides=None,doc=None):
         self.description = description          self.description = description
         self._provides = provides          self._provides = provides
           self.__doc__ = doc or ("binding.requireBinding: %s" % description)
           self.attrName = self.__name__ = name
   
     def computeValue(self, obj, instanceDict, attrName):      def computeValue(self, obj, instanceDict, attrName):
   
Line 464 
Line 507 
         )          )
   
   
   def bindToUtilities(iface, provides=None, doc=None):
   
       """Binding to a list of all 'iface' utilities above the component"""
   
       return Once(lambda s,d,a: list(findUtilities(iface,s)),
           provides=provides, doc=doc
       )
   
   
   def bindToProperty(propName, default=NOT_GIVEN, provides=None, doc=None):
   
       """Binding to property 'propName', defaulting to 'default' if not found
   
           If 'default' is not supplied, failure to locate the named property
           will result in a 'config.PropertyNotFound' exception.
       """
   
       propName = PropertyName(propName)
   
       return Once(lambda s,d,a: config.getProperty(propName,s,default),
           provides=provides, doc=doc
       )
   
   
   class Base(object):
   
       """Thing that can be composed into a component tree, w/binding & lookups"""
   
       __class_implements__ = IBindingFactory
       __implements__       = IBindingAPI
       __metaclass__        = ActiveDescriptors
   
       def __init__(self, parentComponent=None, componentName=None, **kw):
           self.setParentComponent(parentComponent,componentName)
           if kw:
               self.__dict__.update(kw)
   
       lookupComponent = _lookupComponent
   
       def setParentComponent(self, parentComponent, componentName=None):
           self.__parentCell.set(parentComponent)
           self.__componentName = componentName
   
       __componentName = None
   
       def getParentComponent(self):
           return self.__parentCell.get()
   
       def getComponentName(self):
           return self.__componentName
   
       def __parentCell(s,d,a):
           cell = EigenCell()
           cell.set(None)
           s.getParentComponent = cell.get
           return cell
   
       __parentCell = Once(__parentCell)
   
       def _getConfigData(self, configKey, forObj):
           return NOT_FOUND
   
       def _hasBinding(self,attr):
           return attr in self.__dict__
   
       def _getBinding(self,attr,default=None):
           return self.__dict__.get(attr,default)
   
       def _setBinding(self,attr,value):
           self.__dict__[attr]=value
   
       def _delBinding(self,attr):
           if attr in self.__dict__:
               del self.__dict__[attr]
   
   
   
   
 class Component(object):  
   
     """Thing that can be composed into a component tree, w/binding & lookups"""  
   
     __metaclasses__  = (  
         meta.AssertInterfaces, meta.ActiveDescriptors  
     )  
   
     # use the global lookupComponent function as a method  
   
     lookupComponent = lookupComponent  
   
     def setParentComponent(self,parent):  
         from weakref import ref  
         self.getParentComponent = ref(parent)  
   
     def getParentComponent(self):  
         return None  
   
     def _componentName(self, dict, name):  
         return self.__class__.__name__.split('.')[-1]  
   
     _componentName = Once(_componentName)  
   
     __instance_provides__ = New(EigenRegistry)  
   
     __class_provides__ = EigenRegistry()  
   
   
   
Line 531 
Line 609 
   
   
   
     def acquireUtility(self, iface, forObj=None, localLookup=True):  
   
         if forObj is None:  
             forObj=self  
   
         if localLookup:  
   
             provider = self.__instance_provides__.get(iface)  
   
             if provider is not None:  class Component(Base):
                 return provider(self,forObj)  
   
             attr = self.__class_provides__.get(iface)      """An configurable implementation (i.e., solution-domain) component"""
   
             if attr is not None:      __implements__ = IComponent
   
                 utility = getattr(self,attr)      def __instance_provides__(self,d,a):
           from peak.config.config_components import PropertyMap
           pm=PropertyMap()
           pm.setParentComponent(self)
           return pm
   
                 if utility is not NOT_FOUND:      __instance_provides__ = Once(__instance_provides__, provides=IPropertyMap)
                     return utility      __class_provides__    = EigenRegistry()
   
         parent = self.getParentComponent()  
   
         if parent is None:      def _getConfigData(self, configKey, forObj):
             parent = config.getLocal(self)  
   
         return parent.acquireUtility(iface,forObj)          attr = self._getBinding('__instance_provides__')
   
           if attr:
               value = attr.getValueFor(configKey, forObj)
   
     def registerProvider(self, ifaces, provider):              if value is not NOT_FOUND:
         self.__instance_provides__.register(ifaces, provider)                  return value
   
           attr = self.__class_provides__.get(configKey)
   
           if attr:
               return getattr(self, attr, NOT_FOUND)
   
           return NOT_FOUND
   
   
       def registerProvider(self, configKeys, provider):
           self.__instance_provides__.registerProvider(configKeys, provider)
   
   
   
   
   
 class AutoCreatable(type):  class AutoCreatable(OnceClass, ActiveDescriptors):
   
     """Metaclass for components which auto-create when used"""      """Metaclass for components which auto-create when used"""
   
     def __get__(self, obj, typ=None):      def computeValue(self,owner,_d,_a):
           return self(owner,_a)
   
         if obj is None:  
             return self  
   
         newOb = self(obj)  
   
         obj.__dict__[newOb._componentName] = newOb  
         return newOb  
   
   
 class AutoCreated(Component):  class AutoCreated(Component):
Line 592 
Line 668 
     """Component that auto-creates itself in instances of its containing class      """Component that auto-creates itself in instances of its containing class
     """      """
   
     __metaclasses__ = AutoCreatable,      __metaclass__ = AutoCreatable
   
     def __init__(self, parent=None):  
   
         super(AutoCreated,self).__init__()  
   
         if parent is not None:  
             self.setParentComponent(parent)  
   
   
   
   
   
   
   
   
   
   
   
   
   
 modules.setupModule()  
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   


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

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help