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

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

Parent Directory | Revision Log

version 351, Fri Mar 22 15:34:54 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 FunctionType  
 from Interface import Standard  
 from TW.Utils.Code import Function  
   
 __all__ = [  
     'Base','App','Service','Specialist','DynamicBinding','StaticBinding',  
     'StructuralFeature', 'Field', 'Collection', 'Reference', 'Sequence',  
     'Classifier','PrimitiveType','Enumeration','DataType','Element',  
     'bindTo', 'requireBinding', 'bindToNames', 'bindToParent', 'Specialist'  
 ]  
   
   
 # We export the interfaces too, so people don't have to dig for them...  
   
 __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  
   
   from weakref import ref, WeakValueDictionary
   
 class DynamicBinding(object):  from peak.naming.names import toName, Syntax, CompoundName
   from peak.naming.interfaces import NameNotFoundException
     __metaclass__ = DynamicBindingMC  from peak.util.EigenData import EigenRegistry
   
   
   
   
 class bindTo(Once):  
   
     """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  
         calling 'self.getService("path.to.service")' repeatedly.  
     """  
   
     singleValue = 1  
   
     def __init__(self,targetName):  
         self.targetNames = (targetName,)  
   
     def computeValue(self, obj, instanceDict, attrName):  
   
         instanceDict[attrName] = None   # recursion guard  
   
         parent = obj.getSEFparent()  
         if parent is None: parent = obj  
   
         obs = map(parent.getService, self.targetNames)  
   
         for newOb in obs:  
             if newOb is None:  
                 del instanceDict[attrName]  
                 raise NameError(  
                     "%s not found binding %s" % (self.targetName, attrName)  
                 )  
             elif self.singleValue:  
                 return newOb  
   
         return tuple(obs)  
   
   
   
 class bindToNames(bindTo):  
   
     """Automatically look up and cache a sequence of services by name  
   
         Usage::  
   
             class someClass(SEF.Service):  
   
                 thingINeed = SEF.bindToNames(  
                     "path.to.service1", "another.path", ...  
                 )  
   
         'someClass' can then refer to 'self.thingINeed' to get a tuple of  
         services instead of calling 'self.getService()' on a series of names.  
     """  
   
     singleValue = 0  
   
     def __init__(self,*targetNames):  
         self.targetNames = targetNames  
   
   from Interface import Interface
   from peak.api import config, NOT_FOUND
   
   
   __all__ = [
       'Component','AutoCreated','Provider','CachingProvider',
       'bindTo', 'requireBinding', 'bindToNames', 'bindToParent', 'bindToSelf',
       'getRootComponent', 'getParentComponent', 'lookupComponent',
       'acquireComponent', 'globalLookup'
   ]
   
   
   InterfaceClass = Interface.__class__
   
   
   
Line 121 
Line 38 
   
   
   
 class bindToParent(Once):  
   
     """Look up and cache a reference to the nth-level parent service  def Provider(callable):
       return lambda foundIn, forObj: callable(forObj)
   
         Usage::  
   
             class someClass(SEF.Service):  def CachingProvider(callable, weak=False):
   
                 grandPa = SEF.bindToParent(2)      def provider(foundIn, forObj):
   
        'someClass' can then refer to 'self.grandPa' instead of calling          fid = id(foundIn)
        'self.getSEFparent().getSEFparent()'.          utility = provider.cache.get(fid)
   
        Note that this binding creates a circular reference as soon as it          if utility is None:
        is retrieved from an instance.  The circular reference can be              utility = provider.cache[fid] = callable(foundIn)
        broken by deleting the attribute (e.g. 'del self.grandPa'), but of  
        course it will come back the next time you use the attribute.  
     """  
   
     def __init__(self,level=1):          return utility
         self.level = level  
   
     def computeValue(self, obj, instDict, attrName):      if weak:
           provider.cache = WeakValueDictionary()
         for step in range(self.level):      else:
             newObj = obj.getSEFparent()          provider.cache = {}
             if newObj is None: break  
             obj = newObj  
   
         return obj  
   
       return provider
   
   
   
Line 162 
Line 71 
   
   
   
 class requireBinding(Once):  
   
     """Placeholder for a binding that should be (re)defined by a subclass"""  
   
     def __init__(self,description=""):  
         self.description = description  
   
     def computeValue(self, obj, instanceDict, attrName):  
   
         raise NameError("Class %s must define %s; %s"  
             % (obj.__class__.__name__, attrName, self.description)  
         )  
   
   
   
   
   
   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)
   
   
   
Line 201 
Line 121 
   
   
   
   def acquireComponent(component, name):
   
       """Acquire 'name' relative to 'component', w/fallback to globalLookup()"""
   
 class Base(object):      target = component
   
     """Base for all S-E-F classes"""      while target is not None:
   
     __metaclasses__  = (          ob = getattr(target, name, NOT_FOUND)
         Meta.AssertInterfaces, Meta.ClassInit, Meta.ActiveDescriptors  
     )  
   
     __implements__ = ISEF          if ob is not NOT_FOUND:
     _sefParent     = None              return ob
   
     def getService(self,name=None):          target = getParentComponent(target)
   
         if name:  
             if not isinstance(name,tuple):  
                 name = tuple(name.split('.'))  
   
             if hasattr(self,name[0]):  
                 o = getattr(self,name[0])  
                 if len(name)==1:  
                     return o  
                 else:                  else:
                     return o.getService(name[1:])          return globalLookup(name, component)
             else:  
                 parent = self.getSEFparent()  
                 if parent is not None:  
                     return parent.getService(name)  
         else:  
             return self.getSEFparent()  
   
   
     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 Service(DynamicBinding, Base):  
   
     """Instance (as opposed to class)"""  
   
     __implements__ = IService  
   
   
 class App(Service):  
   
     """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 StaticBinding(object):  
     pass  
   
   
   
Line 281 
Line 162 
   
   
   
   ComponentNameSyntax = Syntax(
       direction = 1,
       separator = '/',
   )
   
   
   def ComponentName(nameStr):
       return CompoundName(nameStr, ComponentNameSyntax)
   
   
 class Specialist(Service):  _getFirstPathComponent = dict( (
       ('',   getRootComponent),
       ('.',  lambda x:x),
       ('..', getParentComponent),
   ) ).get
   
     """Service for finding/creating objects of a specific type family  
   
         Specialists are the high-level building blocks of TransWarp  _getNextPathComponent = dict( (
         applications, providing "well-known" locations for retrieving or      ('',   lambda x:x),
         creating objects that play a given role in an application.  They also      ('.',  lambda x:x),
         serve as a focal point for operations that deal with objects of a      ('..', getParentComponent),
         particular type, but which deal with zero or more than one instance  ) ).get
         of that type (and thus can't be instance methods).  
   
         Why not just use a class?  Because a Specialist serves as a "placeful"  
         implementation of storage and lifecycle management for its objects,  
         while classes in the general sense are neither placeful nor provide  
         storage/lifecycle management.  
   
         This basic Specialist implementation supplies retrieval, creation, and  
         caching support for Elements based on Records (see the  
         TW.Database.DataModel package).  It should be straightforward to create  
         mixin classes, however, that implement other storage methodologies such  
         as ZODB-backed BTrees, etc.  
   
         Also, most application Specialists will probably add in a few  
         application-specific methods, and Zope 3 applications will probably  
         also want to define more specific interfaces than 'ISpecialist' so  
         they can use views to create user interfaces for their Specialists.  
   
         Configuring a Specialist  
   
             Specialists are specified as subclasses of 'SEF.Specialist'.  Here  
             are a few of the attributes you can specify in subclasses:  
   
              * 'isAbstract' -- if true, the Specialist will not create new  
                 items.  The default is false.  
   
              * 'elementClass' -- the class used for retrieved or created  
                 elements.  Can be specified using 'SEF.bindTo' functions.  
   
              * 'keyGenerator' -- an object with a 'next()' method, which will  
                 be called to generate the "next" key value for a newly created  
                 item, if a key is not supplied by the caller.  
   
              * 'cache' -- A 'OnceClass' that implements the 'TW.Caching.ICache'  
                interface, such as any of the 'TW.Caching' cache classes, or  
                the 'TW.Database.Transactions.TransactionalCache' class.  
                The default is 'TW.Caching.WeakCache'.  
   
             Note that not all subclasses of Specialist may use or honor these  
             attributes, since it is always possible to override the methods  
             in this base class that use them.  
   
         Record Management Support  
   
             To support retrieving DataModel Records from RecordManagers, there  
             are some additional attributes which you must specify:  
   
              * 'recordManager' -- the RecordManager which records will be  
                 retrieved from; can be specified using a 'SEF.bindTo' function.  
   
              * 'requiredType' -- The name of the RecordType which records  
                 retrieved from 'recordManager' must have, to be considered  
                 valid for retrieval.  
   
              * 'keyField' -- name of the record field which corresponds to the  
                 application key.  
   
              * 'keyConstants' -- An optional sequence of '(key,value)' pairs  
                 which will be passed to 'recordManager.getItem()' along with  
                 the key field.  This helps support multi-field keys, which is  
                 especially useful with CORE and WarpCORE "object names".  
   
             Of course, if you are using a Specialist subclass/variant which  
             doesn't use RecordManagers, then you needn't supply these  
             class attributes.  
   
         Polymorphism (Sub-Specialist) Support  
   
             In many applications, there will be Specialists whose scope  def lookupComponent(component, name):
             includes more-specialized variants of themselves.  For example, a  
             task management application might have two specialists, 'Tasks'  
             and 'ToDos', where the to-do item is a more specialized kind of  
             task.  Semantically, the 'Tasks' specialist must also be able to  
             retrieve items that are actually managed by the 'ToDos' specialist.  
   
             The base 'Specialist' class includes support for this circumstance,      """Lookup 'name' relative to 'component'
             so long as the record management support is being used, the  
             specialists are using the same RecordManager, and the RecordTypes  
             are subclasses of each other.  To activate this support, supply  
             a 'subtypeSpecialists' class attribute which is a sequence of the  
             specialists which specialize in direct subtypes.  
             'SEF.bindToNames()' is an easy way to provide such a sequence,  
             e.g.::  
   
                 class Task(SEF.Element):      '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()'.
   
                 class ToDo(Task):      Regardless of how the lookup is processed, a 'naming.NameNotFoundException'
                     ...      will be raised if the name cannot be found.
   
                 class Project(Task):      Component Path Syntax
                     ...  
   
                 class Tasks(SEF.Specialist):          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.
   
                     elementClass = Task          Paths beginning with anything other than '/', './', or '../' are
                     subtypeSpecialists = SEF.bindToNames('ToDos', 'Projects')          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)
   
                 class ToDos(SEF.Specialist):      parsedName = toName(name, ComponentName, 1)
   
                     elementClass = ToDo      # 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
   
                 class Projects(SEF.Specialist):      parts = iter(parsedName)
   
                     elementClass = Project      attr = parts.next() # first part
       pc = _getFirstPathComponent(attr)
   
                     ...      if pc:
           ob = pc(component)
       else:
           ob = acquireComponent(component,attr)
   
             The subtype specialists will be asked to retrieve an object      resolved = []
             whenever the more-general specialist does not have a suitable      append = resolved.append
             item available in its cache.  The first non-None item returned  
             from one of the subtype specialists will be used.  If no subtype  
             specialist claims the item, the more-general specialist will  
             use its own element class to wrap the retrieved record.  
     """  
   
     __implements__ = ISpecialist      try:
           for attr in parts:
               pc = _getNextPathComponent(attr)
               if pc:
                   ob = pc(ob)
               else:
                   ob = getattr(ob,attr)
               append(attr)
   
     isAbstract     = 0      except AttributeError:
   
     elementClass   = requireBinding(          raise NameNotFoundException(
         "Class for elements managed by this specialist"              resolvedName = ComponentName(resolved),
               remainingName = ComponentName([attr] + [a for a in parts]),
               resolvedObj = ob
     )      )
   
     keyGenerator   = requireBinding("Object with a 'next()' method")      return ob
   
     recordManager  = requireBinding("RecordManager to get records from")  
     requiredType   = requireBinding("RecordType name that records must have")  
     keyField       = requireBinding("Name of record key field")  
     keyConstants   = ()         # key/value pairs to be passed to recordType  
   
     subtypeSpecialists = ()     # specialists to check for subtypes  
   
     from TW.Caching import WeakCache as cache  class bindTo(Once):
   
       """Automatically look up and cache a relevant component
   
           Usage::
   
               class someClass(binding.Component):
   
                   thingINeed = binding.bindTo("path/to/service")
   
                   getOtherThing = binding.bindTo("some/thing", weak=True)
   
           '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
   
   
   
   
   
   
     def __getitem__(self, key, default=NOT_GIVEN):  
   
         """Retrieve element designated by 'key'; raise KeyError if not found"""  
   
         item = self.cache.get(key, NOT_GIVEN)  
   
         if item is NOT_GIVEN:  
             item = self._retrieveItem(key)  
             self.cache[key] = item  
   
         if item is NOT_FOUND:  
   
             if default is NOT_GIVEN:  
                 raise KeyError,key  
   
             item = default  
   
         return item  
   
   
     def getItem(self, key, default=None):  
   
         """Retrieve element designated by 'key', or 'default' if not found"""      def computeValue(self, obj, instanceDict, attrName):
   
         return self.__getitem__(key,default)          names = self.targetNames
           obs   = map(obj.lookupComponent, names)
   
           for name,newOb in zip(names, obs):
   
     def newItem(self, key=None):              if newOb is NOT_FOUND:
   
         """Create element with key 'key' (or a new key if 'key' is None)"""                  del instanceDict[attrName]
                   raise NameNotFoundError(attrName, resolvedName = name)
   
         if self.isAbstract:              if self.singleValue:
             raise TypeError("Abstract specialists can't create new items")  
   
         if key is None:                  if self.weak:
             key = self.keyGenerator.next()                      return ref(newOb)
                   else:
                       return newOb
   
         item = self.cache[key] = self._newItem(key)          if self.weak:
         return item              obs = map(ref,obs)
   
           return tuple(obs)
   
     def _retrieveItem(self,key):  
   
         """The heavy lifting for retrieval; redefine for non-RM subclasses"""  
   
         record = self.recordManager.getItem(  
             self.keyConstants + ((self.keyField,key),)  
         )  
   
         # record doesn't exist or is wrong type, ditch it...  
   
         if not record.hasType(self.requiredType):  
   
             if not record.exists():  
                 record.invalidate()  
   
             return None  
   
   
         # Check sub-specialists so that we get most-specific type  
   
         for sub in self.subtypeSpecialists:  
   
             item = sub.getItem(record[sub.keyField])  
   
             if item is not None:  
                 return item  
   
   
         return self._wrapElement(record)  
   
   
     def _wrapElement(self,record):  
   
         """Wrap 'record' in an Element"""  
   
         element = self.elementClass()  class bindToNames(bindTo):
         element._fData = record  
         element._setSEFparent(self)  
   
         return element      """Automatically look up and cache a sequence of services by name
   
     def _newItem(self,key):          Usage::
   
         """The heavy lifting for creation; redefine for non-RM subclasses"""              class someClass(binding.AutoCreated):
   
         record = self.recordManager.getItem(                  thingINeed = binding.bindToNames(
             self.keyConstants + ((self.keyField,key),)                      "path/to/service", "another/path", ...
         )          )
   
         if record.exists():          'someClass' can then refer to 'self.thingINeed' to get a tuple of
             raise KeyError(key, "key already exists")          services instead of calling 'self.lookupComponent()' on a series of
           names.  As with 'bindTo()', a 'weak' keyword argument can be set to
         record.addType(self.requiredType)          indicate that the sequence should consist of weak references to the
           named objects.
         item = self._wrapElement(record)      """
         self.cache[key] = item  
   
         return item  
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 class FeatureMC(Meta.ActiveDescriptor, type):  
   
     """Metaclass for Features that export methods to their Element      singleValue = False
   
         Example usage::      def __init__(self, *targetNames, **kw):
           self.targetNames = targetNames
           self.weak = kw.get('weak')
           self._provides = kw.get('provides')
   
             class aFeatureBase(SEF.Feature):  
   
                 hasFoo = 0  
   
                 def foo(self, anArg, someParam):  
                     feature = type(self).__feature__  
                     print feature.hasFoo  
   
                 # 'namingConvention' makes foo a method template  
                 foo.namingConvention = 'foo%(initcap)s'  
   
                 # 'installIf' determines whether template will be installed,  
                 #   based on subclass metadata  
                 foo.installIf = lambda feature,method: feature.hasFoo  
   
                 def bar(klass, something):  
                     # This will be a class method of the feature,  
                     # because it has no 'namingConvention'.  
                     ...  
   
             class anEl(SEF.Element):  
   
                 class myFeature(aFeatureBase):  
                     hasFoo = 1  
   
   
             el=anEl()  
             el.fooMyFeature(1,2)  
             el.__class__.myFeature.bar('spam')  
   
         In the above example, class 'anEl' receives a 'fooMyFeature()'  
         method from the 'myFeature' feature.  The feature and its class  
         methods/attributes are available via 'element.__class__.myFeature'.  
   
   
         The Basics  
   
             Features are immutable, singleton classes.  They are never  
             instantiated, and should never be modified after their creation.  
             Any methods defined in a feature are either class methods of the  
             feature class, or method templates for export to any Element class  
             which the feature is installed in.  
   
             Method templates automate the process of creating getters, setters,  
             and similarly patterned methods.  That is, instead of writing  
             'setFoo()' and 'getFoo()' methods for each feature of an element,  
             one need only define a 'foo' feature which inherits from a base  
             feature class with 'set()' and 'get()' templates.  
   
         Accessing Inner Methods  class bindToParent(WeakBinding):
   
             ...      """Look up and cache a weak ref to the nth-level parent component
   
         Dynamic Method Access          Usage::
   
             ...              class someClass(binding.AutoCreated):
   
         Defining Method Templates                  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):
   
       """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):
   
     def __get__(self, ob, typ=None):          raise NameError("Class %s must define %s; %s"
               % (obj.__class__.__name__, attrName, self.description)
         """Get the feature's value by delegating to 'ob.getX()'"""  
   
         if ob is None:  
             return self  
   
         return self.getMethod(ob,'get')()  
   
   
     def __set__(self, ob, val):  
         """Set the feature's value by delegating to 'ob.setX()'"""  
         return self.getMethod(ob,'set')(val)  
   
   
     def __delete__(self, ob):  
         """Delete the feature's value by delegating to 'ob.delattrX()'"""  
         return self.getMethod(ob,'delattr')()  
   
   
     def getMethod(self, ob, verb):  
         """Look up a method dynamically"""  
         return getattr(ob,self.methodNames[verb])  
   
   
     def activate(self,klass,attrName):  
   
         """Install the feature's getX/setX/etc. methods upon use in a class"""  
   
         if attrName != self.__name__:  
             raise TypeError(  
                 "Feature %s installed in %s as %s; must be named %s" %  
                 (self.__name__,klass,attrName,self.__name__)  
             )              )
   
         for verb,methodName in self.methodNames.items():  
             setattr(klass, methodName, getattr(self,verb))  
   
   
   
   
     def __init__(self, className, bases, classDict):  
   
         """Set up method templates, name mapping, etc."""  
   
         super(FeatureMC,self).__init__(className, bases, classDict)  
   
         items = []; d={}  
   
         for b in bases:  
             items.extend(getattr(b,'methodTemplates',d).items())  
   
         items.reverse()  
         mt = self.methodTemplates = dict(items)  
   
         for methodName, method in classDict.items():  
             if not isinstance(method,FunctionType): continue  
   
             nc = getattr(method,'namingConvention',None)  
             if nc:  
                 mt[methodName]=method  
                 setattr(self,methodName,staticmethod(method))  
             else:  
                 setattr(self,methodName,classmethod(method))  
   
         names = {  
             'name': className, 'upper': className.upper(),  
             'lower': className.lower(), 'capital': className.capitalize(),  
             'initCap': className[:1].upper()+className[1:]  
         }  
   
         mn = self.methodNames = {}  
         for methodName,method in mt.items():  
             verb       = getattr(method,'verb',methodName)  
             installIf  = getattr(method,'installIf',None)  
   
             if installIf is None or installIf(self,method):  
                 mn[verb] = method.namingConvention % names  
                 f=Function(method)  
                 f.co_names[f.co_names.index('__feature__')] = className  
                 setattr(self,verb,staticmethod(f.func()))  
   
 class StructuralFeature(Base):  
   
     __metaclasses__ = FeatureMC,  
   
     isRequired    = 0  
     lowerBound    = 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  
     defaultValue   = None  
   
     def get(self):  
         feature = self.__class__.__feature__  
         return self.__dict__.get(feature.__name__, feature.defaultValue)  
   
     get.namingConvention = 'get%(initCap)s'  
   
   
     def set(self,val):  
         feature = self.__class__.__feature__  
         self.__dict__[feature.__name__]=[val]  
   
     set.namingConvention = 'set%(initCap)s'  
   
   
     def delete(self):  
         feature = self.__class__.__feature__  
         del self.__dict__[feature.__name__]  
   
     delete.namingConvention = 'delete%(initCap)s'  
     delete.verb = 'delattr'  
   
   
 class Field(StructuralFeature):  
     __class_implements__ = IValue  
     upperBound = 1  
   
 class Collection(StructuralFeature):  
   
     __class_implements__ = ICollection  
   
     def get(self):  
         feature = self.__class__.__feature__  
         return self.__dict__.get(feature.__name__, [])  
   
     get.namingConvention = 'get%(initCap)s'  
   
   
     def add(self,item):  
   
         """Add the item to the collection/relationship"""  
   
         feature = self.__class__.__feature__  
         ub = feature.upperBound  
         value = feature.__get__(self)  
   
         if not ub or len(value)<ub:  
             feature._notifyLink(self,item)  
             feature._link(self,item)  
         else:  
             raise ValueError("Too many items")  
   
     add.namingConvention = 'add%(initCap)s'  
   
   
     def remove(self,item):  class Component(object):
         """Remove the item from the collection/relationship, if present"""  
         feature = self.__class__.__feature__  
         feature._unlink(self,item)  
         feature._notifyUnlink(self,item)  
   
     remove.namingConvention = 'remove%(initCap)s'      """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 replace(self,oldItem,newItem):      def _componentName(self, dict, name):
           return self.__class__.__name__.split('.')[-1]
   
         feature = self.__class__.__feature__      _componentName = Once(_componentName)
   
         d = feature.__get__(self)      __instance_provides__ = New(EigenRegistry)
         p = d.index(oldItem)  
   
         if p!=-1:      __class_provides__ = EigenRegistry()
             d[p]=newItem  
             feature._notifyUnlink(self,oldItem)  
             feature._notifyLink(self,newItem)  
         else:  
             raise ValueError(oldItem,"not found")  
   
     replace.namingConvention = 'replace%(initCap)s'  
   
   
     def delete(self):  
         """Unset the value of the feature (like __delattr__)"""  
   
         feature = self.__class__.__feature__  
         referencedEnd = feature.referencedEnd  
   
         d = feature.__get__(self)  
   
         if referencedEnd:  
   
             for item in d:  
                 otherEnd = getattr(item.__class__,referencedEnd)  
                 otherEnd._unlink(item,self)  
   
         super(Collection,feature).delete(self)  
   
   
   
   
   
   
       def acquireUtility(self, iface, forObj=None, localLookup=True):
   
           if forObj is None:
               forObj=self
   
           if localLookup:
   
     def _notifyLink(self,element,item):              provider = self.__instance_provides__.get(iface)
   
         referencedEnd = self.referencedEnd              if provider is not None:
                   return provider(self,forObj)
   
         if referencedEnd:              attr = self.__class_provides__.get(iface)
             otherEnd = getattr(item.__class__,referencedEnd)  
             otherEnd._link(item,element)  
   
               if attr is not None:
   
     def _notifyUnlink(self,element,item):                  utility = getattr(self,attr)
   
         referencedEnd = self.referencedEnd                  if utility is not NOT_FOUND:
                       return utility
   
         if referencedEnd:          parent = self.getParentComponent()
             otherEnd = getattr(item.__class__,referencedEnd)  
             otherEnd._unlink(item,element)  
   
           if parent is None:
               parent = config.getLocal(self)
   
     def _link(self,element,item):          return parent.acquireUtility(iface,forObj)
         d=self.__get__(element)  
         d.append(item)  
         self.__set__(element,d)  
   
     def _unlink(self,element,item):  
         d=self.__get__(element)  
         d.remove(item)  
         self.__set__(element,d)  
   
       def registerProvider(self, ifaces, provider):
           self.__instance_provides__.register(ifaces, provider)
   
   
   
Line 897 
Line 572 
   
   
   
   class AutoCreatable(type):
   
       """Metaclass for components which auto-create when used"""
   
       def __get__(self, obj, typ=None):
   
 class Reference(Collection):          if obj is None:
               return self
   
     __class_implements__ = IReference          newOb = self(obj)
   
     upperBound = 1          obj.__dict__[newOb._componentName] = newOb
           return newOb
   
     def __call__(self):  
         """Return the value of the feature"""  
         return self._getData(None)  
   
     def _set(self,value):  class AutoCreated(Component):
         self.clear()  
         self._setData([value])  
   
       """Component that auto-creates itself in instances of its containing class
       """
   
 class Sequence(Collection):      __metaclasses__ = AutoCreatable,
   
     __class_implements__ = ISequence      def __init__(self, parent=None):
   
     isOrdered = 1          super(AutoCreated,self).__init__()
   
     def insertBefore(self,oldItem,newItem):          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.351  
changed lines
  Added in v.469

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help