Show
Ignore:
Timestamp:
11/12/09 14:47:53 (3 years ago)
Author:
ged
Message:

- Added a new collection which can resolve entities relative to the current

entity, for example "..other_module.Class" (based on patches from Johannes
Janssen, closes #93).

- Added a new entity option "resolve_root", which allows one to specify the

root module where your entities are defined. The string will be prepended
to all "absolute" entity paths. It can also be used on a per-entity basis.
This feature is based on a patch from Johannes Janssen, see #93.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/elixir/collection.py

    r490 r515  
    33''' 
    44import sys 
     5import re 
    56 
    6 # default entity collection 
    7 class EntityCollection(list): 
     7class BaseCollection(list): 
    88    def __init__(self, entities=None): 
    9         # _entities is a dict of entities keyed on their name. 
    10         self._entities = {} 
    119        list.__init__(self) 
    1210        if entities is not None: 
     
    1614        for e in entities: 
    1715            self.append(e) 
     16 
     17    def clear(self): 
     18        del self[:] 
     19 
     20    def resolve_absolute(self, key, full_path, entity=None, root=None): 
     21        if root is None: 
     22            root = entity._descriptor.resolve_root 
     23        if root: 
     24            full_path = '%s.%s' % (root, full_path) 
     25        module_path, classname = full_path.rsplit('.', 1) 
     26        module = sys.modules[module_path] 
     27        res = getattr(module, classname, None) 
     28        if res is None: 
     29            if entity is not None: 
     30                raise Exception("Couldn't resolve target '%s' <%s> in '%s'!" 
     31                                % (key, full_path, entity.__name__)) 
     32            else: 
     33                raise Exception("Couldn't resolve target '%s' <%s>!" 
     34                                % (key, full_path)) 
     35        return res 
     36 
     37    def __getattr__(self, key): 
     38        return self.resolve(key) 
     39 
     40# default entity collection 
     41class GlobalEntityCollection(BaseCollection): 
     42    def __init__(self, entities=None): 
     43        # _entities is a dict of entities keyed on their name. 
     44        self._entities = {} 
     45        super(GlobalEntityCollection, self).__init__(entities) 
    1846 
    1947    def append(self, entity): 
     
    3159        "source" entity when resolving relationship targets. 
    3260        ''' 
    33         path = key.rsplit('.', 1) 
    34         classname = path.pop() 
    35         if path: 
    36             # Do we have a fully qualified entity name? 
    37             module = sys.modules[path.pop()] 
    38             return getattr(module, classname, None) 
     61        # Do we have a fully qualified entity name? 
     62        if '.' in key: 
     63            return self.resolve_absolute(key, key, entity) 
    3964        else: 
    4065            # Otherwise we look in the entities of this collection 
     
    4267            if res is None: 
    4368                if entity: 
    44                     raise Exception("Couldn't resolve target '%s' in '%s'" \ 
     69                    raise Exception("Couldn't resolve target '%s' in '%s'" 
    4570                                    % (key, entity.__name__)) 
    4671                else: 
     
    5782    def clear(self): 
    5883        self._entities = {} 
    59         del self[:] 
     84        super(GlobalEntityCollection, self).clear() 
     85 
     86# backward compatible name 
     87EntityCollection = GlobalEntityCollection 
     88 
     89_leading_dots = re.compile('^([.]*).*$') 
     90 
     91class RelativeEntityCollection(BaseCollection): 
     92    # the entity=None does not make any sense with a relative entity collection 
     93    def resolve(self, key, entity): 
     94        ''' 
     95        Resolve a key to an Entity. The optional `entity` argument is the 
     96        "source" entity when resolving relationship targets. 
     97        ''' 
     98        full_path = key 
     99 
     100        if '.' not in key or key.startswith('.'): 
     101            # relative target 
     102 
     103            # any leading dot is stripped and with each dot removed, 
     104            # the entity_module is stripped of one more chunk (starting with 
     105            # the last one). 
     106            num_dots = _leading_dots.match(full_path).end(1) 
     107            full_path = full_path[num_dots:] 
     108            chunks = entity.__module__.split('.') 
     109            chunkstokeep = len(chunks) - num_dots 
     110            if chunkstokeep < 0: 
     111                raise Exception("Couldn't resolve relative target " 
     112                    "'%s' relative to '%s'" % (key, entity.__module__)) 
     113            entity_module = '.'.join(chunks[:chunkstokeep]) 
     114 
     115            if entity_module and entity_module is not '__main__': 
     116                full_path = '%s.%s' % (entity_module, full_path) 
     117 
     118            root = '' 
     119        else: 
     120            root = None 
     121        return self.resolve_absolute(key, full_path, entity, root=root) 
    60122 
    61123    def __getattr__(self, key): 
    62         return self.resolve(key) 
     124        raise NotImplementedError 
    63125 
    64