Changeset 515
- Timestamp:
- 11/12/09 14:47:53 (3 years ago)
- Location:
- elixir/trunk
- Files:
-
- 8 added
- 8 modified
-
AUTHORS (modified) (1 diff)
-
CHANGES (modified) (1 diff)
-
elixir/__init__.py (modified) (2 diffs)
-
elixir/collection.py (modified) (5 diffs)
-
elixir/options.py (modified) (1 diff)
-
setup.py (modified) (1 diff)
-
tests/db1 (added)
-
tests/db1/__init__.py (added)
-
tests/db1/a.py (added)
-
tests/db1/b.py (added)
-
tests/db1/c.py (added)
-
tests/db2 (added)
-
tests/db2/__init__.py (added)
-
tests/db2/a.py (added)
-
tests/test_abstract.py (modified) (1 diff)
-
tests/test_packages.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
elixir/trunk/AUTHORS
r504 r515 16 16 - Graham Higgins 17 17 - Jason R. Coombs 18 - Johannes Janssen 18 19 - Neil Blakey-Milner 19 20 - Paul Johnston 20 21 - Remi Jolin 21 22 - Robin Munn 23 - Stéphane Klein 22 24 - Valentin Lab 23 25 - some anonymous contributions I couldn't trace to someone in particular -
elixir/trunk/CHANGES
r511 r515 5 5 etc... This allows, among others, an entity to inherit from multiple abstract 6 6 classes (patch from Stéphane Klein, closes #89). 7 - Added a new collection which can resolve entities relative to the current 8 entity, for example "..other_module.Class" (based on patches from Johannes 9 Janssen, closes #93). 10 - Added a new entity option "resolve_root", which allows one to specify the 11 root module where your entities are defined. The string will be prepended 12 to all "absolute" entity paths. It can also be used on a per-entity basis. 13 This feature is based on a patch from Johannes Janssen, see #93. 7 14 8 15 Changes: -
elixir/trunk/elixir/__init__.py
r491 r515 36 36 Synonym 37 37 from elixir.statements import Statement 38 from elixir.collection import EntityCollection 38 from elixir.collection import EntityCollection, GlobalEntityCollection 39 39 40 40 … … 68 68 69 69 # default entity collection 70 entities = EntityCollection()70 entities = GlobalEntityCollection() 71 71 72 72 -
elixir/trunk/elixir/collection.py
r490 r515 3 3 ''' 4 4 import sys 5 import re 5 6 6 # default entity collection 7 class EntityCollection(list): 7 class BaseCollection(list): 8 8 def __init__(self, entities=None): 9 # _entities is a dict of entities keyed on their name.10 self._entities = {}11 9 list.__init__(self) 12 10 if entities is not None: … … 16 14 for e in entities: 17 15 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 41 class 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) 18 46 19 47 def append(self, entity): … … 31 59 "source" entity when resolving relationship targets. 32 60 ''' 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) 39 64 else: 40 65 # Otherwise we look in the entities of this collection … … 42 67 if res is None: 43 68 if entity: 44 raise Exception("Couldn't resolve target '%s' in '%s'" \69 raise Exception("Couldn't resolve target '%s' in '%s'" 45 70 % (key, entity.__name__)) 46 71 else: … … 57 82 def clear(self): 58 83 self._entities = {} 59 del self[:] 84 super(GlobalEntityCollection, self).clear() 85 86 # backward compatible name 87 EntityCollection = GlobalEntityCollection 88 89 _leading_dots = re.compile('^([.]*).*$') 90 91 class 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) 60 122 61 123 def __getattr__(self, key): 62 r eturn self.resolve(key)124 raise NotImplementedError 63 125 64 -
elixir/trunk/elixir/options.py
r513 r515 226 226 allowcoloverride=False, 227 227 order_by=None, 228 resolve_root=None, 228 229 mapper_options={}, 229 230 table_options={} -
elixir/trunk/setup.py
r491 r515 24 24 author="Gaetan de Menten, Daniel Haus and Jonathan LaCour", 25 25 author_email="sqlelixir@googlegroups.com", 26 #TODO: use maintainer field 26 27 url="http://elixir.ematia.de", 27 28 license = "MIT License", -
elixir/trunk/tests/test_abstract.py
r513 r515 12 12 13 13 def setup(): 14 # this is an ugly hack because globally defined entities of other tests 15 # (a.*, b.*, db1.* and db2.*) leak into this one because of nosetests 16 # habit of importing all modules before running the first test. 17 # test_abstract is usually the first test to be run, so it gets all the 18 # crap. 19 cleanup_all() 20 14 21 metadata.bind = 'sqlite://' 15 22 elixir.options_defaults['shortnames'] = True 23 24 def teardown(): 25 elixir.options_defaults['shortnames'] = False 16 26 17 27 class TestAbstractInheritance(object): -
elixir/trunk/tests/test_packages.py
r490 r515 5 5 import sys 6 6 7 import elixir 7 8 from elixir import * 8 9 9 10 def setup(): 10 11 metadata.bind = 'sqlite://' 11 sys.modules.pop('tests.a', None)12 sys.modules.pop('tests.b', None)12 for module in ('a', 'b', 'db1', 'db1.a', 'db1.b', 'db1.c', 'db2', 'db2.a'): 13 sys.modules.pop('tests.%s' % module, None) 13 14 14 15 … … 46 47 def test_ref_to_imported_entity_using_class(self): 47 48 from tests.a import A 48 from tests.b import B49 import tests.b 49 50 50 51 class C(Entity): … … 57 58 58 59 def test_ref_to_imported_entity_using_name(self): 59 from tests.a import A60 from tests.b import B60 import tests.a 61 import tests.b 61 62 62 63 class C(Entity): … … 68 69 assert 'a_id' in C.table.columns 69 70 71 def test_resolve_root(self): 72 import tests.a 73 import tests.b 74 75 class C(Entity): 76 using_options(resolve_root='tests') 77 78 name = Field(String(30)) 79 a = ManyToOne('a.A') 80 81 setup_all(True) 82 83 assert 'a_id' in C.table.columns 84 85 def test_relative_collection(self): 86 from elixir.collection import RelativeEntityCollection, \ 87 GlobalEntityCollection 88 89 elixir.entities = RelativeEntityCollection() 90 91 import db1 92 import db2 93 94 setup_all(True) 95 96 try: 97 assert len(elixir.entities) == 5 98 finally: 99 elixir.entities = GlobalEntityCollection()
