merge
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 12 Aug 2009 18:06:23 +0200
changeset 2786 971d7c545505
parent 2785 34dfdbefcfd1 (current diff)
parent 2784 f395115070c1 (diff)
child 2787 ece5bbed3cd2
merge
server/session.py
--- a/common/mixins.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/common/mixins.py	Wed Aug 12 18:06:23 2009 +0200
@@ -168,7 +168,7 @@
     @property
     def state(self):
         try:
-            return self.related('in_state', entities=True)[0].get_value('name')
+            return self.in_state[0].name
         except IndexError:
             self.warning('entity %s has no state', self)
             return None
@@ -259,10 +259,10 @@
     __implements__ = (IEmailable,)
 
     def get_email(self):
-        emails = self.related('primary_email', 'subject', entities=True) or \
-                 self.related('use_email', 'subject', entities=True)
-        if emails:
-            return emails[0].get_value('address')
+        if getattr(self, 'primary_email', None):
+            return self.primary_email[0].address
+        if getattr(self, 'use_email', None):
+            return self.use_email[0].address
         return None
 
     @classmethod
@@ -283,8 +283,7 @@
         NOTE: the dictionary keys should match the list returned by the
         `allowed_massmail_keys` method.
         """
-        return dict( (attr, self.cwgetattr(attr))
-                     for attr in self.allowed_massmail_keys() )
+        return dict( (attr, getattr(self, attr)) for attr in self.allowed_massmail_keys() )
 
 
 
--- a/entities/authobjs.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/entities/authobjs.py	Wed Aug 12 18:06:23 2009 +0200
@@ -44,7 +44,7 @@
         try:
             return self._groups
         except AttributeError:
-            self._groups = set(g.cwdb.name for g in self.cwdb.in_group)
+            self._groups = set(g.name for g in self.in_group)
             return self._groups
 
     @property
@@ -52,8 +52,7 @@
         try:
             return self._properties
         except AttributeError:
-            self._properties = dict((p.cwdb.pkey, p.cwdb.value)
-                                    for p in self.cwdb.reverse_for_user)
+            self._properties = dict((p.pkey, p.value) for p in self.reverse_for_user)
             return self._properties
 
     def property_value(self, key):
@@ -64,8 +63,7 @@
         except KeyError:
             pass
         except ValueError:
-            self.warning('incorrect value for property %s of user %s', key,
-                         self.cwdb.login)
+            self.warning('incorrect value for eproperty %s of user %s', key, self.login)
         return self.vreg.property_value(key)
 
     def matching_groups(self, groups):
@@ -124,17 +122,16 @@
 
     def name(self):
         """construct a name using firstname / surname or login if not defined"""
-        surname = self.get_value('surname')
-        firstname = self.get_value('firstname')
-        if firstname and surname:
+
+        if self.firstname and self.surname:
             return self.req._('%(firstname)s %(surname)s') % {
-                'firstname': firstname, 'surname' : surname}
-        if firstname or surname:
-            return surname or firstname
-        return self.get_value('login')
+                'firstname': self.firstname, 'surname' : self.surname}
+        if self.firstname:
+            return self.firstname
+        return self.login
 
     def dc_title(self):
-        return self.get_value('login')
+        return self.login
 
     dc_long_title = name
 
--- a/entity.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/entity.py	Wed Aug 12 18:06:23 2009 +0200
@@ -132,20 +132,14 @@
         return super(_metaentity, mcs).__new__(mcs, name, bases, classdict)
 
 
-_CWDB_CLASSES = {}
-
-def cwdb___init__(self, entity):
-    self.entity = entity
+class Entity(AppObject, dict):
+    """an entity instance has e_schema automagically set on
+    the class and instances has access to their issuing cursor.
 
-class Entity(AppObject, dict):
-    """an entity instance has e_schema automagically set on the class.
-
-    Also its special cwdb attribute provides access to persistent attributes and
-    relation using properties which are set for each attribute and relation
-    according to entity's type schema.
-
-    Beware that among attributes, 'eid' is *NEITHER* stored in the dict
-    containment (which acts as a cache for other attributes dynamically fetched)
+    A property is set for each attribute and relation on each entity's type
+    class. Becare that among attributes, 'eid' is *NEITHER* stored in the
+    dict containment (which acts as a cache for other attributes dynamically
+    fetched)
 
     :type e_schema: `cubicweb.schema.EntitySchema`
     :ivar e_schema: the entity's schema
@@ -183,30 +177,24 @@
         etype = cls.id
         assert etype != 'Any', etype
         cls.e_schema = eschema = cls.schema.eschema(etype)
-        cwdbclsdict = {'__init__': cwdb___init__}
         for rschema, _ in eschema.attribute_definitions():
-            rtype = rschema.type
-            if rtype == 'eid':
+            if rschema.type == 'eid':
                 continue
-            setattr(cls, rtype, DeprecatedAttribute(rtype))
-            cwdbclsdict[rtype] = Attribute(rtype)
+            setattr(cls, rschema.type, Attribute(rschema.type))
         mixins = []
-        for rschema, _, role in eschema.relation_definitions():
-            if (rschema, role) in MI_REL_TRIGGERS:
-                mixin = MI_REL_TRIGGERS[(rschema, role)]
+        for rschema, _, x in eschema.relation_definitions():
+            if (rschema, x) in MI_REL_TRIGGERS:
+                mixin = MI_REL_TRIGGERS[(rschema, x)]
                 if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
                     mixins.append(mixin)
                 for iface in getattr(mixin, '__implements__', ()):
                     if not interface.implements(cls, iface):
                         interface.extend(cls, iface)
-            rtype = rschema.type
-            if role == 'object':
-                attr = 'reverse_%s' % rtype
+            if x == 'subject':
+                setattr(cls, rschema.type, SubjectRelation(rschema))
             else:
-                attr = rtype
-            setattr(cls, attr, DeprecatedRelation(rtype, role))
-            cwdbclsdict[attr] = Relation(rtype, role)
-        _CWDB_CLASSES[etype] = type(etype + 'CWDB', (object,), cwdbclsdict)
+                attr = 'reverse_%s' % rschema.type
+                setattr(cls, attr, ObjectRelation(rschema))
         if mixins:
             cls.__bases__ = tuple(mixins + [p for p in cls.__bases__ if not p is object])
             cls.debug('plugged %s mixins on %s', mixins, etype)
@@ -311,8 +299,6 @@
         else:
             self.eid = None
         self._is_saved = True
-        if self.id != 'Any':
-            self.cwdb = _CWDB_CLASSES[self.id](self)
 
     def __repr__(self):
         return '<Entity %s %s %s at %s>' % (
@@ -324,15 +310,6 @@
     def __hash__(self):
         return id(self)
 
-    def cwgetattr(self, attr, default=_marker):
-        """return attribute from either self.cwdb or self"""
-        try:
-            return getattr(self.cwdb, attr)
-        except AttributeError:
-            if default is _marker:
-                return getattr(self, attr)
-            return getattr(self, attr, default)
-
     def pre_add_hook(self):
         """hook called by the repository before doing anything to add the entity
         (before_add entity hooks have not been called yet). This give the
@@ -415,7 +392,7 @@
         etype = str(self.e_schema)
         path = etype.lower()
         if mainattr != 'eid':
-            value = getattr(self.cwdb, mainattr)
+            value = getattr(self, mainattr)
             if value is None or unicode(value) == u'':
                 mainattr = 'eid'
                 path += '/eid'
@@ -436,7 +413,7 @@
 
     def attr_metadata(self, attr, metadata):
         """return a metadata for an attribute (None if unspecified)"""
-        value = self.cwgetattr('%s_%s' % (attr, metadata), None)
+        value = getattr(self, '%s_%s' % (attr, metadata), None)
         if value is None and metadata == 'encoding':
             value = self.vreg.property_value('ui.encoding')
         return value
@@ -448,17 +425,14 @@
         """
         attr = str(attr)
         if value is _marker:
-            value = self.cwgetattr(attr)
+            value = getattr(self, attr)
         if isinstance(value, basestring):
             value = value.strip()
         if value is None or value == '': # don't use "not", 0 is an acceptable value
             return u''
         if attrtype is None:
             attrtype = self.e_schema.destination(attr)
-        try:
-            props = self.e_schema.rproperties(attr)
-        except KeyError:
-            props = {}
+        props = self.e_schema.rproperties(attr)
         if attrtype == 'String':
             # internalinalized *and* formatted string such as schema
             # description...
@@ -499,20 +473,13 @@
         assert self.has_eid()
         execute = self.req.execute
         for rschema in self.e_schema.subject_relations():
-            # skip final, meta or composite relation
-            if rschema.is_final() or rschema.meta or self.e_schema.subjrproperty(rschema, 'composite'):
+            if rschema.is_final() or rschema.meta:
                 continue
-            # skip relation with card in ?1 else we either change the copied
-            # object (inlined relation) or inserting some inconsistency
-            if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
+            # skip already defined relations
+            if getattr(self, rschema.type):
                 continue
-            # skip if we're told to do so
             if rschema.type in self.skip_copy_for:
                 continue
-            # skip already defined relations
-            if self.related(rschema.type, 'subject'):
-                continue
-            # special case for in_state
             if rschema.type == 'in_state':
                 # if the workflow is defining an initial state (XXX AND we are
                 # not in the managers group? not done to be more consistent)
@@ -520,26 +487,32 @@
                 if execute('Any S WHERE S state_of ET, ET initial_state S,'
                            'ET name %(etype)s', {'etype': str(self.e_schema)}):
                     continue
+            # skip composite relation
+            if self.e_schema.subjrproperty(rschema, 'composite'):
+                continue
+            # skip relation with card in ?1 else we either change the copied
+            # object (inlined relation) or inserting some inconsistency
+            if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
+                continue
             rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
-                rschema, rschema)
+                rschema.type, rschema.type)
             execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
             self.clear_related_cache(rschema.type, 'subject')
         for rschema in self.e_schema.object_relations():
-            # skip meta or composite
-            if rschema.meta or self.e_schema.objrproperty(rschema, 'composite'):
+            if rschema.meta:
+                continue
+            # skip already defined relations
+            if getattr(self, 'reverse_%s' % rschema.type):
+                continue
+            # skip composite relation
+            if self.e_schema.objrproperty(rschema, 'composite'):
                 continue
             # skip relation with card in ?1 else we either change the copied
             # object (inlined relation) or inserting some inconsistency
             if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1':
                 continue
-            # skip if we're told to do so
-            if rschema.type in self.skip_copy_for:
-                continue
-            # skip already defined relations
-            if self.related(rschema.type, 'object'):
-                continue
             rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
-                rschema, rschema)
+                rschema.type, rschema.type)
             execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
             self.clear_related_cache(rschema.type, 'object')
 
@@ -904,9 +877,9 @@
             yielded = False
             for rschema, target in containers:
                 if target == 'object':
-                    targets = self.related(rschema.type, 'subject', entities=True)
+                    targets = getattr(self, rschema.type)
                 else:
-                    targets = self.related(rschema.type, 'object', entities=True)
+                    targets = getattr(self, 'reverse_%s' % rschema)
                 for entity in targets:
                     if entity.eid in _done:
                         continue
@@ -942,73 +915,61 @@
                 words += tokenize(value)
 
         for rschema, role in self.e_schema.fulltext_relations():
-            for entity in self.related(rschema.type, role, entities=True):
-                words += entity.get_words()
+            if role == 'subject':
+                for entity in getattr(self, rschema.type):
+                    words += entity.get_words()
+            else: # if role == 'object':
+                for entity in getattr(self, 'reverse_%s' % rschema.type):
+                    words += entity.get_words()
         return words
 
 
 # attribute and relation descriptors ##########################################
 
-
 class Attribute(object):
     """descriptor that controls schema attribute access"""
 
-    def __init__(self, rtype):
-        assert rtype != 'eid'
-        self._rtype = rtype
+    def __init__(self, attrname):
+        assert attrname != 'eid'
+        self._attrname = attrname
+
+    def __get__(self, eobj, eclass):
+        if eobj is None:
+            return self
+        return eobj.get_value(self._attrname)
+
+    def __set__(self, eobj, value):
+        # XXX bw compat
+        # would be better to generate UPDATE queries than the current behaviour
+        eobj.warning("deprecated usage, don't use 'entity.attr = val' notation)")
+        eobj[self._attrname] = value
 
-    def __get__(self, cwdbobj, eclass):
-        if cwdbobj is None:
-            return self
-        return cwdbobj.entity.get_value(self._rtype)
+
+class Relation(object):
+    """descriptor that controls schema relation access"""
+    _role = None # for pylint
+
+    def __init__(self, rschema):
+        self._rschema = rschema
+        self._rtype = rschema.type
+
+    def __get__(self, eobj, eclass):
+        if eobj is None:
+            raise AttributeError('%s cannot be only be accessed from instances'
+                                 % self._rtype)
+        return eobj.related(self._rtype, self._role, entities=True)
 
     def __set__(self, eobj, value):
         raise NotImplementedError
 
 
-class Relation(Attribute):
+class SubjectRelation(Relation):
     """descriptor that controls schema relation access"""
-
-    def __init__(self, rtype, role):
-        super(Relation, self).__init__(rtype)
-        self._role = role
-
-    def __get__(self, cwdbobj, eclass):
-        if cwdbobj is None:
-            raise AttributeError('%s cannot be only be accessed from instances'
-                                 % self._rtype)
-        return cwdbobj.entity.related(self._rtype, self._role, entities=True)
-
-
-class DeprecatedAttribute(Attribute):
-    """descriptor that controls schema attribute access"""
+    _role = 'subject'
 
-    def __get__(self, eobj, eclass):
-        rtype = self._rtype
-        warn('entity.%s is deprecated, use entity.cwdb.%s' % (rtype, rtype),
-             DeprecationWarning, stacklevel=2)
-        if eobj is None:
-            return self
-        return eobj.get_value(self._rtype)
-
-    def __set__(self, eobj, value):
-        # XXX bw compat
-        # would be better to generate UPDATE queries than the current behaviour
-        eobj.warning("deprecated usage, don't use 'entity.attr = val' notation)")
-        eobj[self._rtype] = value
-
-
-class DeprecatedRelation(Relation):
+class ObjectRelation(Relation):
     """descriptor that controls schema relation access"""
-
-    def __get__(self, eobj, eclass):
-        rtype = self._rtype
-        warn('entity.[reverse_]%s is deprecated, use entity.cwdb.[reverse_]%s'
-             % (rtype, rtype), DeprecationWarning, stacklevel=2)
-        if eobj is None:
-            raise AttributeError('%s cannot be only be accessed from instances'
-                                 % self._rtype)
-        return eobj.related(rtype, self._role, entities=True)
+    _role = 'object'
 
 from logging import getLogger
 from cubicweb import set_log_methods
--- a/server/hooks.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/server/hooks.py	Wed Aug 12 18:06:23 2009 +0200
@@ -67,7 +67,7 @@
         if self.entity.eid in session.transaction_data.get('pendingeids', ()):
             # entity have been created and deleted in the same transaction
             return
-        if not self.entity.related('created_by'):
+        if not self.entity.created_by:
             session.add_relation(self.entity.eid, 'created_by', session.user.eid)
 
 
--- a/server/repository.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/server/repository.py	Wed Aug 12 18:06:23 2009 +0200
@@ -687,7 +687,7 @@
         if props is not None:
             self.set_session_props(sessionid, props)
         user = session.user
-        return user.eid, user.cwdb.login, user.groups, user.properties
+        return user.eid, user.login, user.groups, user.properties
 
     def set_session_props(self, sessionid, props):
         """this method should be used by client to:
--- a/server/session.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/server/session.py	Wed Aug 12 18:06:23 2009 +0200
@@ -48,7 +48,7 @@
 
     def __init__(self, user, repo, cnxprops=None, _id=None):
         super(Session, self).__init__(repo.vreg)
-        self.id = _id or make_uid(user.cwdb.login.encode('UTF8'))
+        self.id = _id or make_uid(user.login.encode('UTF8'))
         cnxprops = cnxprops or ConnectionProperties('inmemory')
         self.user = user
         self.repo = repo
--- a/test/unittest_rset.py	Wed Aug 12 14:05:36 2009 +0200
+++ b/test/unittest_rset.py	Wed Aug 12 18:06:23 2009 +0200
@@ -117,7 +117,7 @@
         rs.req = self.request()
         rs.vreg = self.vreg
         def test_filter(entity):
-            return entity.cwdb.login != 'nico'
+            return entity.login != 'nico'
 
         rs2 = rs.filtered_rset(test_filter)
         self.assertEquals(len(rs2), 2)
@@ -271,11 +271,11 @@
         self.assertEquals(pprelcachedict(e._related_cache),
                           [('created_by_subject', [5])])
         # first level of recursion
-        u = e.cwdb.created_by[0]
+        u = e.created_by[0]
         self.assertEquals(u['login'], 'admin')
         self.assertRaises(KeyError, u.__getitem__, 'firstname')
         # second level of recursion
-        s = u.cwdb.in_state[0]
+        s = u.in_state[0]
         self.assertEquals(s['name'], 'activated')
         self.assertRaises(KeyError, s.__getitem__, 'description')