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

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

Parent Directory | Revision Log

version 331, Fri Feb 15 23:36:20 2002 UTC version 469, Sat Jul 13 21:02:40 2002 UTC
Line 1 
Line 1 
 """Basic, in-memory implementation of the Service-Element-Feature pattern"""  """Basic binding tools"""
   
 from TW.API import *  from once import Once, New, WeakBinding
 from TW.SEF.Interfaces import *  import meta, modules
 from TW.SEF.Interfaces import __all__ as allInterfaces  
   
 from types import TupleType  from weakref import ref, WeakValueDictionary
 from Interface import Standard  
   from peak.naming.names import toName, Syntax, CompoundName
   from peak.naming.interfaces import NameNotFoundException
   from peak.util.EigenData import EigenRegistry
   
   from Interface import Interface
   from peak.api import config, NOT_FOUND
   
   
 __all__ = [  __all__ = [
     'Base','App','Service','TypeService','DynamicBinding','StaticBinding',      'Component','AutoCreated','Provider','CachingProvider',
     'StructuralFeature', 'Field', 'Collection', 'Reference', 'Sequence',      'bindTo', 'requireBinding', 'bindToNames', 'bindToParent', 'bindToSelf',
     'Classifier','PrimitiveType','Enumeration','DataType','Element'      'getRootComponent', 'getParentComponent', 'lookupComponent',
       'acquireComponent', 'globalLookup'
 ]  ]
   
   
 # We export the interfaces too, so people don't have to dig for them...  InterfaceClass = Interface.__class__
   
   
   
 __all__ += allInterfaces  
   
   
   
   
 class DynamicBindingMC(Meta.AssertInterfaces):  
   
     def __get__(self, obj, typ=None):  
         if obj is None: return self  
         newOb = self()  
         newOb._setSEFparent(obj)  
         obj.__dict__[newOb._componentName] = newOb  
         return newOb  
   
   
 class DynamicBinding(object):  
     __metaclass__ = DynamicBindingMC  
   
   
   
   
   
 class Base(object):  
   
     """Base for all S-E-F classes"""  
   
     __metaclasses__  = Meta.AssertInterfaces, Meta.ClassInit  def Provider(callable):
     __implements__ = ISEF      return lambda foundIn, forObj: callable(forObj)
     _sefParent     = None  
   
     def getService(self,name=None):  
   
         if name:  def CachingProvider(callable, weak=False):
             if not isinstance(name,TupleType):  
                 name = tuple(name.split('.'))  
   
             if hasattr(self,name[0]):      def provider(foundIn, forObj):
                 o = getattr(self,name[0])  
                 if len(name)==1:          fid = id(foundIn)
                     return o          utility = provider.cache.get(fid)
   
           if utility is None:
               utility = provider.cache[fid] = callable(foundIn)
   
           return utility
   
       if weak:
           provider.cache = WeakValueDictionary()
                 else:                  else:
                     return o.getService(name[1:])          provider.cache = {}
   
       return provider
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   def getParentComponent(component):
   
       """Return parent of 'component', or 'None' if root or non-component"""
   
       try:
           gpc = component.getParentComponent
       except AttributeError:
           pass
             else:              else:
                 parent = self.getSEFparent()          return gpc()
                 if parent is not None:  
                     return parent.getService(name)  
   def getRootComponent(component):
   
       """Return the root component of the tree 'component' belongs to"""
   
       next = component
   
       while next is not None:
           component = next
           next = getParentComponent(component)
   
       return component
   
   
   def globalLookup(name, component=None):
   
       """Lookup 'name' in global 'InitialContext', w/'component' in environ"""
   
       from peak.naming.api import InitialContext
   
       return InitialContext(RELATIVE_TO_COMPONENT=component).lookup(name)
   
   
   
   
   
   
   
   
   
   def acquireComponent(component, name):
   
       """Acquire 'name' relative to 'component', w/fallback to globalLookup()"""
   
       target = component
   
       while target is not None:
   
           ob = getattr(target, name, NOT_FOUND)
   
           if ob is not NOT_FOUND:
               return ob
   
           target = getParentComponent(target)
   
         else:          else:
             return self.getSEFparent()          return globalLookup(name, component)
   
   
   
   
   
   
     def _setSEFparent(self,parent):  
         from weakref import ref  
         self.getSEFparent = ref(parent)  
   
     def getSEFparent(self):  
         return None  
   
     def _componentName(self):  
         return self.__class__.__name__  
   
     _componentName = property(_componentName)  
   
   
 class App(Base):  
   
     """Application class"""  
   
     def newElement(self,elementType,*args,**kw):  
         element = apply(getattr(self,elementType),args,kw)  # XXX won't do dotted names  
         element._fData = {}  
         element._setSEFparent(self)  
         return element  
   
   
 class Service(DynamicBinding, Base):  
   
     """Instance (as opposed to class)"""  
   
     __implements__ = IService  
   
   
 class TypeService(Service):  
   
     """Service that supports a (possibly abstract) class"""  
   
     __implements__ = ITypeService  
   
   
 class StaticBinding(object):  
     pass  
   
   ComponentNameSyntax = Syntax(
       direction = 1,
       separator = '/',
   )
   
   
   def ComponentName(nameStr):
       return CompoundName(nameStr, ComponentNameSyntax)
   
   
   _getFirstPathComponent = dict( (
       ('',   getRootComponent),
       ('.',  lambda x:x),
       ('..', getParentComponent),
   ) ).get
   
   
   _getNextPathComponent = dict( (
       ('',   lambda x:x),
       ('.',  lambda x:x),
       ('..', getParentComponent),
   ) ).get
   
   
   
Line 121 
Line 191 
   
   
   
 class StructuralFeature(DynamicBinding, Base):  
   
     # XXX lowerBound = Eval("isRequired and 1 or 0")  
     # XXX lowerBound.copyIntoSubclasses = 1  
   
     isRequired    = 0  # XXX SubclassDefault(0)  
   
     upperBound    = None    # None means unbounded upper end  
   
     isOrdered     = 0  
     isChangeable  = 1       # default is to be changeable  
   
     referencedEnd = None    # and without an 'other end'  
     referencedType = None  
   
     def getElement(self):  
         return self.getSEFparent()  
   
     def getService(self,name=None):  
         return self.getSEFparent().getService(name)  
   
     def getReferencedType(self):  
         return self.getService(self.referencedType)  
   
     def _getData(self,default=()):  
         return self.getSEFparent()._fData.get(self._componentName,default)  
   
     def _setData(self,value):  
         self.getSEFparent()._fData[self._componentName]=value  
   
     def _delData(self):  
         del self.getSEFparent()._fData[self._componentName]  
   
     def _hasData(self):  def lookupComponent(component, name):
         return self.getSEFparent()._fData.has_key(self._componentName)  
   
       """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.
   
       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.
   
 class Field(StructuralFeature):          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.
   
     __implements__ = IValue          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."""
   
     upperBound = 1      if isinstance(name, InterfaceClass):
           utility = component.acquireUtility(name)
           if utility is None:
               raise NameNotFoundException(name, resolvedObj = component)
   
     def __call__(self):      parsedName = toName(name, ComponentName, 1)
         """Return the value of the feature"""  
         return self._getData(None)  
   
     def values(self):      # URL's and composite names must be handled globally
         """Return the value(s) of the feature as a sequence, even if it's a single value"""  
         v=self._getData(NOT_FOUND)  
         if v is NOT_FOUND: return ()  
         return v,  
   
     def clear(self):      if not parsedName.isCompound:
         """Unset the value of the feature (like __delattr__)"""          return globalLookup(name, component)
         if self._hasData():  
             self._delData()  
   
     def set(self,value):      if not parsedName:
         """Set the value of the feature to value"""          # empty name refers to self
         if self.isChangeable:          return component
             self._set(value)  
       parts = iter(parsedName)
   
       attr = parts.next() # first part
       pc = _getFirstPathComponent(attr)
   
       if pc:
           ob = pc(component)
         else:          else:
             raise TypeError,("Read-only field %s" % self._componentName)          ob = acquireComponent(component,attr)
   
     def _set(self,value):      resolved = []
         self.clear()      append = resolved.append
         self._setData(value)  
   
       try:
           for attr in parts:
               pc = _getNextPathComponent(attr)
               if pc:
                   ob = pc(ob)
               else:
                   ob = getattr(ob,attr)
               append(attr)
   
       except AttributeError:
   
           raise NameNotFoundException(
               resolvedName = ComponentName(resolved),
               remainingName = ComponentName([attr] + [a for a in parts]),
               resolvedObj = ob
           )
   
       return ob
   
   class bindTo(Once):
   
       """Automatically look up and cache a relevant component
   
           Usage::
   
               class someClass(binding.Component):
   
                   thingINeed = binding.bindTo("path/to/service")
   
 class Collection(StructuralFeature):                  getOtherThing = binding.bindTo("some/thing", weak=True)
   
     __implements__ = ICollection          'someClass' can then refer to 'self.thingINeed' instead of
           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
   
     def set(self,value):      def __init__(self,targetName,weak=False,provides=None):
         """Set the value of the feature to value"""  
         self._set(value)          self.targetNames = (targetName,)
           self.weak = weak
           self._provides=provides
   
   
     def addItem(self,item):  
         """Add the item to the collection/relationship, reject if multiplicity exceeded"""  
   
         ub = self.upperBound  
   
         if not ub or len(self)<ub:  
             self._notifyLink(item)  
             self._link(item)  
         else:  
             raise ValueError  
   
   
     def removeItem(self,item):  
         """Remove the item from the collection/relationship, if present"""  
         self._unlink(item)  
         self._notifyUnlink(item)  
   
   
     def replaceItem(self,oldItem,newItem):  
         d = self._getData([])  
         p = d.index(oldItem)  
         if p!=-1:  
             d[p]=newItem      def computeValue(self, obj, instanceDict, attrName):
             self._setData(d)  
             self._notifyUnlink(oldItem)          names = self.targetNames
             self._notifyLink(newItem)          obs   = map(obj.lookupComponent, names)
   
           for name,newOb in zip(names, obs):
   
               if newOb is NOT_FOUND:
   
                   del instanceDict[attrName]
                   raise NameNotFoundError(attrName, resolvedName = name)
   
               if self.singleValue:
   
                   if self.weak:
                       return ref(newOb)
         else:          else:
             raise ValueError    # XXX                      return newOb
   
           if self.weak:
               obs = map(ref,obs)
   
           return tuple(obs)
   
     def __call__(self):  
         """Return the value of the feature"""  
         return self._getData()  
   
   
     def values(self):  
         """Return the value(s) of the feature as a sequence, even if it's a single value"""  
         return self._getData()  
   
   
     def clear(self):  
         """Unset the value of the feature (like __delattr__)"""  
   
         referencedEnd = self.referencedEnd  
   
         d = self._getData()  
   
         if referencedEnd:  
             element = self.getElement()  
             for item in d:  
                 otherEnd = getattr(item,referencedEnd)  
                 otherEnd._unlink(element)  
   
         if d:  
             self._delData()  
   
   
     def __len__(self):  
         return len(self._getData())  
   
     def isEmpty(self):  
         return len(self._getData())==0  
   
     def isReferenced(self,item):  
         return item in self._getData()  
   
   
   
   
   
   class bindToNames(bindTo):
   
     def _notifyLink(self,item):      """Automatically look up and cache a sequence of services by name
         referencedEnd = self.referencedEnd  
         if referencedEnd:  
             otherEnd = getattr(item,referencedEnd)  
             otherEnd._link(self.getElement())  
   
     def _notifyUnlink(self,item):          Usage::
         referencedEnd = self.referencedEnd  
         if referencedEnd:  
             otherEnd = getattr(item,referencedEnd)  
             otherEnd._unlink(self.getElement())  
   
               class someClass(binding.AutoCreated):
   
     def _set(self,value):                  thingINeed = binding.bindToNames(
         self.clear()                      "path/to/service", "another/path", ...
         self._setData(value)                  )
   
           'someClass' can then refer to 'self.thingINeed' to get a tuple of
           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.
       """
   
     def _link(self,element):      singleValue = False
         d=self._getData([])  
         d.append(element)  
         self._setData(d)  
   
     def _unlink(self,element):      def __init__(self, *targetNames, **kw):
         d=self._getData([])          self.targetNames = targetNames
         d.remove(element)          self.weak = kw.get('weak')
         self._setData(d)          self._provides = kw.get('provides')
   
   
   
Line 326 
Line 406 
   
   
   
 class Reference(Collection):  
   
     __implements__ = IReference  
   
     upperBound = 1  class bindToParent(WeakBinding):
   
     def __call__(self):      """Look up and cache a weak ref to the nth-level parent component
         """Return the value of the feature"""  
         return self._getData(None)  
   
     def _set(self,value):          Usage::
         self.clear()  
         self._setData([value])  
   
               class someClass(binding.AutoCreated):
   
 class Sequence(Collection):                  grandPa = binding.bindToParent(2)
   
     __implements__ = ISequence         'someClass' can then refer to 'self.grandPa' instead of calling
          'self.getParentComponent().getParentComponent()'.
       """
   
     isOrdered = 1      def __init__(self,level=1,provides=None):
           self.level = level
           self._provides = provides
   
     def insertBefore(self,oldItem,newItem):      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):
   
       """Placeholder for a binding that should be (re)defined by a subclass"""
   
       def __init__(self,description="",provides=None):
           self.description = description
           self._provides = provides
   
       def computeValue(self, obj, instanceDict, attrName):
   
           raise NameError("Class %s must define %s; %s"
               % (obj.__class__.__name__, attrName, self.description)
           )
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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()
   
   
   
   
   
   
   
   
   
   
   
   
   
   
       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:
                   return provider(self,forObj)
   
               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)
   
   
   
   
   
   
   
   
   
   
   class AutoCreatable(type):
   
       """Metaclass for components which auto-create when used"""
   
       def __get__(self, obj, typ=None):
   
           if obj is None:
               return self
   
           newOb = self(obj)
   
           obj.__dict__[newOb._componentName] = newOb
           return newOb
   
   
   class AutoCreated(Component):
   
       """Component that auto-creates itself in instances of its containing class
       """
   
       __metaclasses__ = AutoCreatable,
   
       def __init__(self, parent=None):
   
           super(AutoCreated,self).__init__()
   
           if parent is not None:
               self.setParentComponent(parent)
   
         d = self._getData([])  
   
         ub = self.upperBound  
         if ub and len(d)>=ub:  
             raise ValueError    # XXX  
   
         i = -1  
         if d: i = d.index(element)  
   
         if i!=-1:  
             d.insert(i,newItem)  
             self._setData(d)  
             self._notifyLink(newItem)  
         else:  
             raise ValueError    # XXX  
   
   
   
 class Classifier(StaticBinding, Base):  
     """Basis for all flavors"""  
   
 class PrimitiveType(Classifier):  
     """A primitive type (e.g. Boolean, String, etc.)"""  
   
 class Enumeration(DynamicBinding, Classifier):  
     """An enumerated type"""  
   
 class DataType(Classifier):  
     """A complex datatype"""  
   
 class Element(DataType):  
     """An element in its own right"""  
     __implements__ = IElement  
   
   
 setupModule()  modules.setupModule()
   
   
   


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

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help