Changeset 363

Show
Ignore:
Timestamp:
07/14/08 21:51:36 (6 years ago)
Author:
ged
Message:

- Default "target entity resolving code" changed slightly. It now uses a global

collection keyed on the entity name. This means that entities can refer to
other entities in a different module simply with the target entity name
instead of its full path. The full path is only required when there is an
ambiguity (ie when there are two classes with the same name in two different
modules). Closes #9.

- minor cleanup of some test files
- changelog some earlier changes

Location:
elixir/trunk
Files:
6 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/CHANGES

    r362 r363  
    99  polymorphic or not). Concrete polymorphic inheritance requires SQLalchemy 
    1010  0.4.5 or later. 
     11- Moved the entity to string mapping and resolving code to the (newly created) 
     12  EntityCollection class (which stores lists of entities). This allows one to 
     13  provide a custom mapping method if needed. The default class also overrides 
     14  the __getattr__ method, providing and handy way to get at your entities. 
     15  See source:elixir/trunk/tests/test_collections.py#L58 
    1116- Added new "identity" option which can be used to set a custom polymorphic 
    1217  identity for an entity. It also accepts a callable so that you can generate 
     
    3035 
    3136Changes: 
     37- Default "target entity resolving code" changed slightly. It now uses a global 
     38  collection keyed on the entity name. This means that entities can refer to 
     39  other entities in a different module simply with the target entity name 
     40  instead of its full path. The full path is only required when there is an 
     41  ambiguity (ie when there are two classes with the same name in two different 
     42  modules). Closes #9. 
    3243- Added support for SQLAlchemy 0.5, and dropped support for version 0.3 and 
    3344  earlier. 
  • elixir/trunk/elixir/__init__.py

    r357 r363  
    7171class EntityCollection(list): 
    7272    def __init__(self): 
    73         # _entities is a dict of entities for each frame where there are 
    74         # entities defined. 
     73        # _entities is a dict of entities keyed on their name. 
    7574        self._entities = {} 
    76 #        self._entity_map = {} 
    7775        list.__init__(self) 
    7876 
    79     def map_entity(self, entity): 
    80         self.append(entity) 
     77    def append(self, entity): 
     78        ''' 
     79        Add an entity to the collection. 
     80        ''' 
     81        super(EntityCollection, self).append(entity) 
    8182 
    8283        key = entity.__name__ 
    83  
    84 #        if key in self._entity_map: 
    85 #            warnings.warn('An entity named `%s` is already registered!' % key) 
    86  
    87         # 3 is because map_entity is called by: 
    88         # EntityDescriptor::setup_options (which is called by) 
    89         # EntityMeta::__init__ 
    90         # which is called when the entity is defined 
    91         caller_frame = sys._getframe(3) 
    92         cid = entity._caller = id(caller_frame) 
    93         caller_entities = self._entities.setdefault(cid, {}) 
    94         caller_entities[key] = entity 
    95  
    96         # Append all entities which are currently visible by the entity. This 
    97         # will find more entities only if some of them where imported from 
    98         # another module. 
    99         for ent in [e for e in caller_frame.f_locals.values() 
    100                          if isinstance(e, EntityMeta)]: 
    101             caller_entities[ent.__name__] = ent 
    102  
    103 #        self._entity_map[key] = entity 
     84        mapped_entity = self._entities.get(key) 
     85        if mapped_entity: 
     86            if isinstance(mapped_entity, list): 
     87                mapped_entity.append(entity) 
     88            else: 
     89                self._entities[key] = [mapped_entity, entity] 
     90        else: 
     91            self._entities[key] = entity 
    10492 
    10593    def resolve(self, key, entity=None): 
     
    11098        path = rsplit(key, '.', 1) 
    11199        classname = path.pop() 
    112         #XXX: use eval()? 
    113  
    114100        if path: 
    115101            # Do we have a fully qualified entity name? 
     
    117103            return getattr(module, classname, None) 
    118104        else: 
    119             # If not, try the list of entities of the "caller" of the 
    120             # source class. Most of the time, this will be the module 
    121             # the class is defined in. But it could also be a method 
    122             # (inner classes). 
    123             caller_entities = self._entities[entity._caller] 
    124             return caller_entities[classname] 
     105            # Otherwise we look in the entities of this collection 
     106            res = self._entities[key] 
     107            if isinstance(res, list): 
     108                raise Exception("%s resolves to several entities, you should " 
     109                                "use the full path (including the full module " 
     110                                "name) to that entity.") 
     111            else: 
     112                return res 
    125113 
    126114    def clear(self): 
     
    129117 
    130118    def __getattr__(self, key): 
    131         print "GLOBALS", globals().keys() 
    132         print "LOCALS", locals().keys() 
    133119        return self.resolve(key) 
    134 #        return self._entity_map.get(key) 
    135120 
    136121entities = EntityCollection() 
  • elixir/trunk/elixir/entity.py

    r360 r363  
    9898        elixir.metadatas.add(self.metadata) 
    9999        if self.collection is not None: 
    100             self.collection.map_entity(self.entity) 
     100            self.collection.append(self.entity) 
    101101 
    102102        entity = self.entity 
  • elixir/trunk/tests/test_associable.py

    r349 r363  
    44 
    55from sqlalchemy import create_engine, and_ 
    6 from elixir     import * 
     6 
     7from elixir import * 
    78from elixir.ext.associable import associable 
    89 
    910 
    10 def setup(self): 
     11def setup(): 
    1112#    metadata.bind = create_engine('sqlite:///', echo=True) 
    1213    metadata.bind = 'sqlite:///' 
  • elixir/trunk/tests/test_collections.py

    r347 r363  
    11""" 
    2    test collections 
     2Test collections 
    33""" 
    44 
  • elixir/trunk/tests/test_packages.py

    r349 r363  
    11""" 
    2     simple test case 
     2Test spreading entities accross several modules 
    33""" 
    44 
    5 from elixir import * 
    65import sys 
    76 
    8 def setup(self): 
     7from elixir import * 
     8 
     9def setup(): 
    910    metadata.bind = 'sqlite:///' 
    1011 
     
    1314        cleanup_all(True) 
    1415 
    15     def test_packages(self): 
     16    def test_full_entity_path(self): 
    1617        # This is an ugly workaround because when nosetest is run globally (ie 
    1718        # either on the tests directory or in the "trunk" directory, it imports 
     
    2122        # and reimporting their modules does nothing because they were already 
    2223        # imported. 
     24 
    2325        sys.modules.pop('tests.a', None) 
    2426        sys.modules.pop('tests.b', None)