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

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

Parent Directory | Revision Log

version 334, Mon Feb 25 22:53:19 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',
     'bindTo', 'requireBinding',      '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 bindTo(object):  
   
     """Automatically look up and cache a relevant service  
   
         Usage::  
   
             class someClass(SEF.Service):  
   
                 thingINeed = SEF.bindTo("path.to.service")  
   
         'someClass' can then refer to 'self.thingINeed' instead of  def Provider(callable):
         calling 'self.getService("path.to.service")' repeatedly.      return lambda foundIn, forObj: callable(forObj)
     """  
   
     __implements__ = INamedDescriptor  
   
   def CachingProvider(callable, weak=False):
   
     def __init__(self,targetName):      def provider(foundIn, forObj):
         self.targetName = targetName  
   
           fid = id(foundIn)
           utility = provider.cache.get(fid)
   
     def copyWithName(self,newName):          if utility is None:
         from copy import copy              utility = provider.cache[fid] = callable(foundIn)
         newOb = copy(self)  
         newOb._componentName = newName  
   
           return utility
   
       if weak:
           provider.cache = WeakValueDictionary()
       else:
           provider.cache = {}
   
       return provider
   
   
   
Line 80 
Line 74 
   
   
   
     def __get__(self, obj, typ=None):  
   
         if obj is None: return self  
   
         d = obj.__dict__  
         n = self._componentName  
         t = self.targetName  
   
         if not n:  
             raise TypeError(  
                 "%s used in type which does not support NamedDescriptors"  
                 % self  
             )  
         d[n] = None  
   
         newOb = obj.getSEFparent()  
         if newOb is None: newOb = obj  
         newOb = newOb.getService(t)  
   
         if newOb is None:  
             del d[n]  
             raise NameError("%s not found binding %s" % (t, n))  
   
         d[n] = newOb  
         return newOb  
   
   def getParentComponent(component):
   
       """Return parent of 'component', or 'None' if root or non-component"""
   
       try:
           gpc = component.getParentComponent
       except AttributeError:
           pass
       else:
           return gpc()
   
   
   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)
   
   
 class requireBinding(bindTo):  
   
     """Placeholder for a binding that should be (re)defined by a subclass"""  
   
     def __init__(self,description=""):  
         self.description = description  
   
     def __get__(self, obj, typ=None):  
   
         if obj is None: return self  
   
         raise NameError("Class %s must define %s; %s"  
             % (obj.__class__.__name__, self._componentName, self.description)  
   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:
           return globalLookup(name, component)
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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 160 
Line 203 
   
   
   
   def lookupComponent(component, name):
   
       """Lookup 'name' relative to 'component'
   
 class Base(object):      '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()'.
   
     """Base for all S-E-F classes"""      Regardless of how the lookup is processed, a 'naming.NameNotFoundException'
       will be raised if the name cannot be found.
   
     __metaclasses__  = Meta.AssertInterfaces, Meta.ClassInit      Component Path Syntax
     __implements__ = ISEF  
     _sefParent     = None  
   
     def getService(self,name=None):          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.
   
         if name:          Paths beginning with anything other than '/', './', or '../' are
             if not isinstance(name,TupleType):          acquired, which means that the first path segment will be looked
                 name = tuple(name.split('.'))          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.
   
             if hasattr(self,name[0]):          All path segments after the first are interpreted as attribute names
                 o = getattr(self,name[0])          to be looked up, beginning at the component referenced by the first
                 if len(name)==1:          path segment.  '.' and '..' are interpreted the same as for the first
                     return o          path segment."""
                 else:  
                     return o.getService(name[1:])      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
       pc = _getFirstPathComponent(attr)
   
       if pc:
           ob = pc(component)
             else:              else:
                 parent = self.getSEFparent()          ob = acquireComponent(component,attr)
                 if parent is not None:  
                     return parent.getService(name)      resolved = []
       append = resolved.append
   
       try:
           for attr in parts:
               pc = _getNextPathComponent(attr)
               if pc:
                   ob = pc(ob)
         else:          else:
             return self.getSEFparent()                  ob = getattr(ob,attr)
               append(attr)
   
       except AttributeError:
   
     def _setSEFparent(self,parent):          raise NameNotFoundException(
         from weakref import ref              resolvedName = ComponentName(resolved),
         self.getSEFparent = ref(parent)              remainingName = ComponentName([attr] + [a for a in parts]),
               resolvedObj = ob
           )
   
     def getSEFparent(self):      return ob
         return None  
   
     def _componentName(self):  class bindTo(Once):
         return self.__class__.__name__  
   
     _componentName = property(_componentName)      """Automatically look up and cache a relevant component
   
           Usage::
   
     def __class_init__(thisClass, newClass, next):              class someClass(binding.Component):
   
         if __proceed__ is not None:                  thingINeed = binding.bindTo("path/to/service")
             __proceed__(thisClass, newClass, next)  
         else:  
             next().__class_init__(newClass,next)  
   
         for k,v in newClass.__dict__.items():                  getOtherThing = binding.bindTo("some/thing", weak=True)
             if hasattr(v,'copyWithName') and isNamedDescriptor(v):  
                 setattr(newClass, k, v.copyWithName(k))  
   
           '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 __init__(self,targetName,weak=False,provides=None):
   
           self.targetNames = (targetName,)
           self.weak = weak
           self._provides=provides
   
   
   
Line 233 
Line 326 
   
   
   
       def computeValue(self, obj, instanceDict, attrName):
   
           names = self.targetNames
           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:
                       return newOb
   
           if self.weak:
               obs = map(ref,obs)
   
           return tuple(obs)
   
   
   
 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  
   
   
   
   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
           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
   
       def __init__(self, *targetNames, **kw):
           self.targetNames = targetNames
           self.weak = kw.get('weak')
           self._provides = kw.get('provides')
   
   
   
   
 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):  class bindToParent(WeakBinding):
         return self.getSEFparent()._fData.has_key(self._componentName)  
   
       """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()'.
       """
   
 class Field(StructuralFeature):      def __init__(self,level=1,provides=None):
           self.level = level
           self._provides = provides
   
     __implements__ = IValue      def computeValue(self, obj, instDict, attrName):
   
     upperBound = 1          for step in range(self.level):
               newObj = obj.getParentComponent()
               if newObj is None: break
               obj = newObj
   
     def __call__(self):          return obj
         """Return the value of the feature"""  
         return self._getData(None)  
   
     def values(self):  
         """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):  def bindToSelf(provides=None):
         """Unset the value of the feature (like __delattr__)"""  
         if self._hasData():  
             self._delData()  
   
     def set(self,value):      """Weak reference to the 'self' object
         """Set the value of the feature to value"""  
         if self.isChangeable:  
             self._set(value)  
         else:  
             raise TypeError,("Read-only field %s" % self._componentName)  
   
     def _set(self,value):      This is just a shortcut for 'bindToParent(0)', and does pretty much what
         self.clear()      you'd expect.  It's handy for objects that provide default support for
         self._setData(value)      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 Collection(StructuralFeature):  
   
     __implements__ = ICollection  
   
   
     def set(self,value):  
         """Set the value of the feature to value"""  
         self._set(value)  
   
   
     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  
             self._setData(d)  
             self._notifyUnlink(oldItem)  
             self._notifyLink(newItem)  
         else:  
             raise ValueError    # XXX  
   
   
   
     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 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 _notifyLink(self,item):      def setParentComponent(self,parent):
         referencedEnd = self.referencedEnd          from weakref import ref
         if referencedEnd:          self.getParentComponent = ref(parent)
             otherEnd = getattr(item,referencedEnd)  
             otherEnd._link(self.getElement())  
   
     def _notifyUnlink(self,item):      def getParentComponent(self):
         referencedEnd = self.referencedEnd          return None
         if referencedEnd:  
             otherEnd = getattr(item,referencedEnd)  
             otherEnd._unlink(self.getElement())  
   
       def _componentName(self, dict, name):
           return self.__class__.__name__.split('.')[-1]
   
     def _set(self,value):      _componentName = Once(_componentName)
         self.clear()  
         self._setData(value)  
   
       __instance_provides__ = New(EigenRegistry)
   
     def _link(self,element):      __class_provides__ = EigenRegistry()
         d=self._getData([])  
         d.append(element)  
         self._setData(d)  
   
     def _unlink(self,element):  
         d=self._getData([])  
         d.remove(element)  
         self._setData(d)  
   
   
   
Line 489 
Line 531 
   
   
   
       def acquireUtility(self, iface, forObj=None, localLookup=True):
   
 class Reference(Collection):          if forObj is None:
               forObj=self
   
     __implements__ = IReference          if localLookup:
   
     upperBound = 1              provider = self.__instance_provides__.get(iface)
   
     def __call__(self):              if provider is not None:
         """Return the value of the feature"""                  return provider(self,forObj)
         return self._getData(None)  
   
     def _set(self,value):              attr = self.__class_provides__.get(iface)
         self.clear()  
         self._setData([value])  
   
               if attr is not None:
   
 class Sequence(Collection):                  utility = getattr(self,attr)
   
     __implements__ = ISequence                  if utility is not NOT_FOUND:
                       return utility
   
     isOrdered = 1          parent = self.getParentComponent()
   
     def insertBefore(self,oldItem,newItem):          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.334  
changed lines
  Added in v.469

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help