[entity api] unify set_attributes / set_relations into a cw_set method. Closes #2423719
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 17 Jul 2012 12:24:46 +0200
changeset 8483 4ba11607d84a
parent 8469 82272decfa99
child 8492 8ec7cc28b501
[entity api] unify set_attributes / set_relations into a cw_set method. Closes #2423719 Allowing similar usage as create_entity/cw_instantiate. Update cw code base to remove deprecated calls.
doc/book/en/annexes/faq.rst
doc/book/en/devrepo/entityclasses/application-logic.rst
doc/book/en/devrepo/entityclasses/data-as-objects.rst
doc/book/en/devrepo/repo/hooks.rst
doc/book/en/devrepo/testing.rst
doc/book/en/tutorials/advanced/part02_security.rst
entities/authobjs.py
entities/sources.py
entities/test/unittest_base.py
entities/test/unittest_wfobjs.py
entity.py
hooks/test/unittest_hooks.py
hooks/test/unittest_syncschema.py
hooks/workflow.py
misc/migration/3.10.0_Any.py
misc/migration/3.11.0_Any.py
misc/migration/3.14.0_Any.py
misc/migration/3.15.0_Any.py
misc/scripts/chpasswd.py
misc/scripts/ldapuser2ldapfeed.py
server/hook.py
server/migractions.py
server/sources/datafeed.py
server/test/unittest_repository.py
server/test/unittest_storage.py
server/test/unittest_undo.py
sobjects/ldapparser.py
test/unittest_entity.py
web/test/unittest_reledit.py
web/test/unittest_urlrewrite.py
--- a/doc/book/en/annexes/faq.rst	Tue Jul 17 17:16:28 2012 +0200
+++ b/doc/book/en/annexes/faq.rst	Tue Jul 17 12:24:46 2012 +0200
@@ -364,7 +364,7 @@
     >>> crypted = crypt_password('joepass')
     >>> rset = rql('Any U WHERE U is CWUser, U login "joe"')
     >>> joe = rset.get_entity(0,0)
-    >>> joe.set_attributes(upassword=Binary(crypted))
+    >>> joe.cw_set(upassword=Binary(crypted))
 
 Please, refer to the script example is provided in the `misc/examples/chpasswd.py` file.
 
--- a/doc/book/en/devrepo/entityclasses/application-logic.rst	Tue Jul 17 17:16:28 2012 +0200
+++ b/doc/book/en/devrepo/entityclasses/application-logic.rst	Tue Jul 17 12:24:46 2012 +0200
@@ -38,7 +38,7 @@
 object was built.
 
 Setting an attribute or relation value can be done in the context of a
-Hook/Operation, using the obj.set_relations(x=42) notation or a plain
+Hook/Operation, using the obj.cw_set(x=42) notation or a plain
 RQL SET expression.
 
 In views, it would be preferable to encapsulate the necessary logic in
--- a/doc/book/en/devrepo/entityclasses/data-as-objects.rst	Tue Jul 17 17:16:28 2012 +0200
+++ b/doc/book/en/devrepo/entityclasses/data-as-objects.rst	Tue Jul 17 12:24:46 2012 +0200
@@ -47,16 +47,13 @@
   related to the current entity by the relation given in parameter
   and satisfying its constraints
 
-* `set_attributes(**kwargs)`, updates the attributes list with the corresponding
-  values given named parameters
+* :meth:`cw_set(**kwargs)`, updates entity's attributes and/or relation with the
+  corresponding values given named parameters. To set a relation where this
+  entity is the object of the relation, use `reverse_<relation>` as argument
+  name.  Values may be an entity, a list of entities, or None (meaning that all
+  relations of the given type from or to this object should be deleted).
 
-* `set_relations(**kwargs)`, add relations to the given object. To
-  set a relation where this entity is the object of the relation,
-  use `reverse_<relation>` as argument name.  Values may be an
-  entity, a list of entities, or None (meaning that all relations of
-  the given type from or to this object should be deleted).
-
-* `copy_relations(ceid)`, copies the relations of the entities having the eid
+* :meth:`copy_relations(ceid)`, copies the relations of the entities having the eid
   given in the parameters on the current entity
 
 * `delete()` allows to delete the entity
--- a/doc/book/en/devrepo/repo/hooks.rst	Tue Jul 17 17:16:28 2012 +0200
+++ b/doc/book/en/devrepo/repo/hooks.rst	Tue Jul 17 12:24:46 2012 +0200
@@ -206,10 +206,11 @@
 Reminder
 ~~~~~~~~
 
-You should never use the `entity.foo = 42` notation to update an
-entity. It will not do what you expect (updating the
-database). Instead, use the :meth:`set_attributes` and
-:meth:`set_relations` methods.
+You should never use the `entity.foo = 42` notation to update an entity. It will
+not do what you expect (updating the database). Instead, use the
+:meth:`~cubicweb.entity.Entity.cw_set` method or direct access to entity's
+:attr:`cw_edited` attribute if you're writing a hook for 'before_add_entity' or
+'before_update_entity' event.
 
 
 How to choose between a before and an after event ?
--- a/doc/book/en/devrepo/testing.rst	Tue Jul 17 17:16:28 2012 +0200
+++ b/doc/book/en/devrepo/testing.rst	Tue Jul 17 12:24:46 2012 +0200
@@ -70,13 +70,13 @@
 
         def test_cannot_create_cycles(self):
             # direct obvious cycle
-            self.assertRaises(ValidationError, self.kw1.set_relations,
+            self.assertRaises(ValidationError, self.kw1.cw_set,
                               subkeyword_of=self.kw1)
             # testing indirect cycles
             kw3 = self.execute('INSERT Keyword SK: SK name "kwgroup2", SK included_in C, '
                                'SK subkeyword_of K WHERE C name "classif1", K eid %s'
                                % self.kw1.eid).get_entity(0,0)
-            self.kw1.set_relations(subkeyword_of=kw3)
+            self.kw1.cw_set(subkeyword_of=kw3)
             self.assertRaises(ValidationError, self.commit)
 
 The test class defines a :meth:`setup_database` method which populates the
@@ -192,10 +192,10 @@
                                 description=u'cubicweb is beautiful')
             blog_entry_1 = req.create_entity('BlogEntry', title=u'hop',
                                              content=u'cubicweb hop')
-            blog_entry_1.set_relations(entry_of=cubicweb_blog)
+            blog_entry_1.cw_set(entry_of=cubicweb_blog)
             blog_entry_2 = req.create_entity('BlogEntry', title=u'yes',
                                              content=u'cubicweb yes')
-            blog_entry_2.set_relations(entry_of=cubicweb_blog)
+            blog_entry_2.cw_set(entry_of=cubicweb_blog)
             self.assertEqual(len(MAILBOX), 0)
             self.commit()
             self.assertEqual(len(MAILBOX), 2)
--- a/doc/book/en/tutorials/advanced/part02_security.rst	Tue Jul 17 17:16:28 2012 +0200
+++ b/doc/book/en/tutorials/advanced/part02_security.rst	Tue Jul 17 12:24:46 2012 +0200
@@ -196,7 +196,7 @@
 	    for eid in self.get_data():
 		entity = self.session.entity_from_eid(eid)
 		if entity.visibility == 'parent':
-		    entity.set_attributes(visibility=u'authenticated')
+		    entity.cw_set(visibility=u'authenticated')
 
     class SetVisibilityHook(hook.Hook):
 	__regid__ = 'sytweb.setvisibility'
@@ -215,7 +215,7 @@
 	    parent = self._cw.entity_from_eid(self.eidto)
 	    child = self._cw.entity_from_eid(self.eidfrom)
 	    if child.visibility == 'parent':
-		child.set_attributes(visibility=parent.visibility)
+		child.cw_set(visibility=parent.visibility)
 
 Notice:
 
@@ -344,7 +344,7 @@
 	    self.assertEquals(len(req.execute('Folder X')), 0) # restricted...
 	    # may_be_read_by propagation
 	    self.restore_connection()
-	    folder.set_relations(may_be_read_by=toto)
+	    folder.cw_set(may_be_read_by=toto)
 	    self.commit()
 	    photo1.clear_all_caches()
 	    self.failUnless(photo1.may_be_read_by)
--- a/entities/authobjs.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/entities/authobjs.py	Tue Jul 17 12:24:46 2012 +0200
@@ -101,7 +101,7 @@
                 kwargs['for_user'] = self
             self._cw.create_entity('CWProperty', **kwargs)
         else:
-            prop.set_attributes(value=value)
+            prop.cw_set(value=value)
 
     def matching_groups(self, groups):
         """return the number of the given group(s) in which the user is
--- a/entities/sources.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/entities/sources.py	Tue Jul 17 12:24:46 2012 +0200
@@ -51,7 +51,7 @@
                     continue
                 raise
         cfgstr = unicode(generate_source_config(sconfig), self._cw.encoding)
-        self.set_attributes(config=cfgstr)
+        self.cw_set(config=cfgstr)
 
 
 class CWSource(_CWSourceCfgMixIn, AnyEntity):
@@ -181,5 +181,5 @@
     def write_log(self, session, **kwargs):
         if 'status' not in kwargs:
             kwargs['status'] = getattr(self, '_status', u'success')
-        self.set_attributes(log=u'<br/>'.join(self._logs), **kwargs)
+        self.cw_set(log=u'<br/>'.join(self._logs), **kwargs)
         self._logs = []
--- a/entities/test/unittest_base.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/entities/test/unittest_base.py	Tue Jul 17 12:24:46 2012 +0200
@@ -70,7 +70,7 @@
         email1 = self.execute('INSERT EmailAddress X: X address "maarten.ter.huurne@philips.com"').get_entity(0, 0)
         email2 = self.execute('INSERT EmailAddress X: X address "maarten@philips.com"').get_entity(0, 0)
         email3 = self.execute('INSERT EmailAddress X: X address "toto@logilab.fr"').get_entity(0, 0)
-        email1.set_relations(prefered_form=email2)
+        email1.cw_set(prefered_form=email2)
         self.assertEqual(email1.prefered.eid, email2.eid)
         self.assertEqual(email2.prefered.eid, email2.eid)
         self.assertEqual(email3.prefered.eid, email3.eid)
@@ -104,10 +104,10 @@
         e = self.execute('CWUser U WHERE U login "member"').get_entity(0, 0)
         self.assertEqual(e.dc_title(), 'member')
         self.assertEqual(e.name(), 'member')
-        e.set_attributes(firstname=u'bouah')
+        e.cw_set(firstname=u'bouah')
         self.assertEqual(e.dc_title(), 'member')
         self.assertEqual(e.name(), u'bouah')
-        e.set_attributes(surname=u'lôt')
+        e.cw_set(surname=u'lôt')
         self.assertEqual(e.dc_title(), 'member')
         self.assertEqual(e.name(), u'bouah lôt')
 
--- a/entities/test/unittest_wfobjs.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/entities/test/unittest_wfobjs.py	Tue Jul 17 12:24:46 2012 +0200
@@ -63,7 +63,7 @@
         # gnark gnark
         bar = wf.add_state(u'bar')
         self.commit()
-        bar.set_attributes(name=u'foo')
+        bar.cw_set(name=u'foo')
         with self.assertRaises(ValidationError) as cm:
             self.commit()
         self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already have a state of that name'})
@@ -86,7 +86,7 @@
         # gnark gnark
         biz = wf.add_transition(u'biz', (bar,), foo)
         self.commit()
-        biz.set_attributes(name=u'baz')
+        biz.cw_set(name=u'baz')
         with self.assertRaises(ValidationError) as cm:
             self.commit()
         self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already have a transition of that name'})
@@ -516,7 +516,7 @@
                           ['rest'])
         self.assertEqual(parse_hist(iworkflowable.workflow_history),
                           [('asleep', 'asleep', 'rest', None)])
-        user.set_attributes(surname=u'toto') # fulfill condition
+        user.cw_set(surname=u'toto') # fulfill condition
         self.commit()
         iworkflowable.fire_transition('rest')
         self.commit()
--- a/entity.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/entity.py	Tue Jul 17 12:24:46 2012 +0200
@@ -452,26 +452,13 @@
         return mainattr, needcheck
 
     @classmethod
-    def cw_instantiate(cls, execute, **kwargs):
-        """add a new entity of this given type
-
-        Example (in a shell session):
-
-        >>> companycls = vreg['etypes'].etype_class(('Company')
-        >>> personcls = vreg['etypes'].etype_class(('Person')
-        >>> c = companycls.cw_instantiate(session.execute, name=u'Logilab')
-        >>> p = personcls.cw_instantiate(session.execute, firstname=u'John', lastname=u'Doe',
-        ...                              works_for=c)
-
-        You can also set relation where the entity has 'object' role by
-        prefixing the relation by 'reverse_'.
-        """
-        rql = 'INSERT %s X' % cls.__regid__
+    def _cw_build_entity_query(cls, kwargs):
         relations = []
         restrictions = set()
         pending_relations = []
         eschema = cls.e_schema
         qargs = {}
+        attrcache = {}
         for attr, value in kwargs.items():
             if attr.startswith('reverse_'):
                 attr = attr[len('reverse_'):]
@@ -491,6 +478,9 @@
                     continue
             if rschema.final: # attribute
                 relations.append('X %s %%(%s)s' % (attr, attr))
+                attrcache[attr] = value
+            elif value is None:
+                pending_relations.append( (attr, role, value) )
             else:
                 rvar = attr.upper()
                 if role == 'object':
@@ -503,19 +493,51 @@
                 if hasattr(value, 'eid'):
                     value = value.eid
             qargs[attr] = value
+        rql = u''
         if relations:
-            rql = '%s: %s' % (rql, ', '.join(relations))
+            rql += ', '.join(relations)
         if restrictions:
-            rql = '%s WHERE %s' % (rql, ', '.join(restrictions))
-        created = execute(rql, qargs).get_entity(0, 0)
+            rql += ' WHERE %s' % ', '.join(restrictions)
+        return rql, qargs, pending_relations, attrcache
+
+    @classmethod
+    def _cw_handle_pending_relations(cls, eid, pending_relations, execute):
         for attr, role, values in pending_relations:
             if role == 'object':
                 restr = 'Y %s X' % attr
             else:
                 restr = 'X %s Y' % attr
+            if values is None:
+                execute('DELETE %s WHERE X eid %%(x)s' % restr, {'x': eid})
+                continue
             execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
                 restr, ','.join(str(getattr(r, 'eid', r)) for r in values)),
-                    {'x': created.eid}, build_descr=False)
+                    {'x': eid}, build_descr=False)
+
+    @classmethod
+    def cw_instantiate(cls, execute, **kwargs):
+        """add a new entity of this given type
+
+        Example (in a shell session):
+
+        >>> companycls = vreg['etypes'].etype_class(('Company')
+        >>> personcls = vreg['etypes'].etype_class(('Person')
+        >>> c = companycls.cw_instantiate(session.execute, name=u'Logilab')
+        >>> p = personcls.cw_instantiate(session.execute, firstname=u'John', lastname=u'Doe',
+        ...                              works_for=c)
+
+        You can also set relations where the entity has 'object' role by
+        prefixing the relation name by 'reverse_'. Also, relation values may be
+        an entity or eid, a list of entities or eids.
+        """
+        rql, qargs, pending_relations, attrcache = cls._cw_build_entity_query(kwargs)
+        if rql:
+            rql = 'INSERT %s X: %s' % (cls.__regid__, rql)
+        else:
+            rql = 'INSERT %s X' % (cls.__regid__)
+        created = execute(rql, qargs).get_entity(0, 0)
+        created.cw_attr_cache.update(attrcache)
+        cls._cw_handle_pending_relations(created.eid, pending_relations, execute)
         return created
 
     def __init__(self, req, rset=None, row=None, col=0):
@@ -1212,54 +1234,41 @@
 
     # raw edition utilities ###################################################
 
-    def set_attributes(self, **kwargs): # XXX cw_set_attributes
+    def cw_set(self, **kwargs):
+        """update this entity using given attributes / relation, working in the
+        same fashion as :meth:`cw_instantiate`.
+
+        Example (in a shell session):
+
+        >>> c = rql('Any X WHERE X is Company').get_entity(0, 0)
+        >>> p = rql('Any X WHERE X is Person').get_entity(0, 0)
+        >>> c.set(name=u'Logilab')
+        >>> p.set(firstname=u'John', lastname=u'Doe', works_for=c)
+
+        You can also set relations where the entity has 'object' role by
+        prefixing the relation name by 'reverse_'.  Also, relation values may be
+        an entity or eid, a list of entities or eids, or None (meaning that all
+        relations of the given type from or to this object should be deleted).
+        """
         _check_cw_unsafe(kwargs)
         assert kwargs
         assert self.cw_is_saved(), "should not call set_attributes while entity "\
                "hasn't been saved yet"
-        relations = ['X %s %%(%s)s' % (key, key) for key in kwargs]
-        # and now update the database
-        kwargs['x'] = self.eid
-        self._cw.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
-                         kwargs)
-        kwargs.pop('x')
+        rql, qargs, pending_relations, attrcache = self._cw_build_entity_query(kwargs)
+        if rql:
+            rql = 'SET ' + rql
+            qargs['x'] = self.eid
+            if ' WHERE ' in rql:
+                rql += ', X eid %(x)s'
+            else:
+                rql += ' WHERE X eid %(x)s'
+            self._cw.execute(rql, qargs)
         # update current local object _after_ the rql query to avoid
         # interferences between the query execution itself and the cw_edited /
         # skip_security machinery
-        self.cw_attr_cache.update(kwargs)
-
-    def set_relations(self, **kwargs): # XXX cw_set_relations
-        """add relations to the given object. To set a relation where this entity
-        is the object of the relation, use 'reverse_'<relation> as argument name.
-
-        Values may be an entity or eid, a list of entities or eids, or None
-        (meaning that all relations of the given type from or to this object
-        should be deleted).
-        """
-        # XXX update cache
-        _check_cw_unsafe(kwargs)
-        for attr, values in kwargs.iteritems():
-            if attr.startswith('reverse_'):
-                restr = 'Y %s X' % attr[len('reverse_'):]
-            else:
-                restr = 'X %s Y' % attr
-            if values is None:
-                self._cw.execute('DELETE %s WHERE X eid %%(x)s' % restr,
-                                 {'x': self.eid})
-                continue
-            if not isinstance(values, (tuple, list, set, frozenset)):
-                values = (values,)
-            eids = []
-            for val in values:
-                try:
-                    eids.append(str(val.eid))
-                except AttributeError:
-                    try:
-                        eids.append(str(typed_eid(val)))
-                    except (ValueError, TypeError):
-                        raise Exception('expected an Entity or eid, got %s' % val)
-            self._cw.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
-                    restr, ','.join(eids)), {'x': self.eid})
+        self.cw_attr_cache.update(attrcache)
+        self._cw_handle_pending_relations(self.eid, pending_relations, self._cw.execute)
+        # XXX update relation cache
 
     def cw_delete(self, **kwargs):
         assert self.has_eid(), self.eid
@@ -1274,6 +1283,21 @@
 
     # deprecated stuff #########################################################
 
+    @deprecated('[3.15] use cw_set() instead')
+    def set_attributes(self, **kwargs): # XXX cw_set_attributes
+        self.cw_set(**kwargs)
+
+    @deprecated('[3.15] use cw_set() instead')
+    def set_relations(self, **kwargs): # XXX cw_set_relations
+        """add relations to the given object. To set a relation where this entity
+        is the object of the relation, use 'reverse_'<relation> as argument name.
+
+        Values may be an entity or eid, a list of entities or eids, or None
+        (meaning that all relations of the given type from or to this object
+        should be deleted).
+        """
+        self.cw_set(**kwargs)
+
     @deprecated('[3.13] use entity.cw_clear_all_caches()')
     def clear_all_caches(self):
         return self.cw_clear_all_caches()
--- a/hooks/test/unittest_hooks.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/hooks/test/unittest_hooks.py	Tue Jul 17 12:24:46 2012 +0200
@@ -67,9 +67,9 @@
         entity = self.request().create_entity('Workflow', name=u'wf1',
                                               description_format=u'text/html',
                                               description=u'yo')
-        entity.set_attributes(name=u'wf2')
+        entity.cw_set(name=u'wf2')
         self.assertEqual(entity.description, u'yo')
-        entity.set_attributes(description=u'R&D<p>yo')
+        entity.cw_set(description=u'R&D<p>yo')
         entity.cw_attr_cache.pop('description')
         self.assertEqual(entity.description, u'R&amp;D<p>yo</p>')
 
--- a/hooks/test/unittest_syncschema.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/hooks/test/unittest_syncschema.py	Tue Jul 17 12:24:46 2012 +0200
@@ -294,7 +294,7 @@
     def test_change_fulltext_container(self):
         req = self.request()
         target = req.create_entity(u'EmailAddress', address=u'rick.roll@dance.com')
-        target.set_relations(reverse_use_email=req.user)
+        target.cw_set(reverse_use_email=req.user)
         self.commit()
         rset = req.execute('Any X WHERE X has_text "rick.roll"')
         self.assertIn(req.user.eid, [item[0] for item in rset])
--- a/hooks/workflow.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/hooks/workflow.py	Tue Jul 17 12:24:46 2012 +0200
@@ -335,7 +335,7 @@
             return
         entity = self._cw.entity_from_eid(self.eidfrom)
         try:
-            entity.set_attributes(modification_date=datetime.now())
+            entity.cw_set(modification_date=datetime.now())
         except RepositoryError, ex:
             # usually occurs if entity is coming from a read-only source
             # (eg ldap user)
--- a/misc/migration/3.10.0_Any.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/misc/migration/3.10.0_Any.py	Tue Jul 17 12:24:46 2012 +0200
@@ -34,5 +34,5 @@
 for x in rql('Any X,XK WHERE X pkey XK, '
              'X pkey ~= "boxes.%" OR '
              'X pkey ~= "contentnavigation.%"').entities():
-    x.set_attributes(pkey=u'ctxcomponents.' + x.pkey.split('.', 1)[1])
+    x.cw_set(pkey=u'ctxcomponents.' + x.pkey.split('.', 1)[1])
 
--- a/misc/migration/3.11.0_Any.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/misc/migration/3.11.0_Any.py	Tue Jul 17 12:24:46 2012 +0200
@@ -81,5 +81,5 @@
         rset = session.execute('Any V WHERE X is CWProperty, X value V, X pkey %(k)s',
                                {'k': pkey})
         timestamp = int(rset[0][0])
-        sourceentity.set_attributes(latest_retrieval=datetime.fromtimestamp(timestamp))
+        sourceentity.cw_set(latest_retrieval=datetime.fromtimestamp(timestamp))
         session.execute('DELETE CWProperty X WHERE X pkey %(k)s', {'k': pkey})
--- a/misc/migration/3.14.0_Any.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/misc/migration/3.14.0_Any.py	Tue Jul 17 12:24:46 2012 +0200
@@ -9,5 +9,5 @@
     expression = rqlcstr.value
     mainvars = guess_rrqlexpr_mainvars(expression)
     yamscstr = CONSTRAINTS[rqlcstr.type](expression, mainvars)
-    rqlcstr.set_attributes(value=yamscstr.serialize())
+    rqlcstr.cw_set(value=yamscstr.serialize())
     print 'updated', rqlcstr.type, rqlcstr.value.strip()
--- a/misc/migration/3.15.0_Any.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/misc/migration/3.15.0_Any.py	Tue Jul 17 12:24:46 2012 +0200
@@ -4,7 +4,7 @@
     config = source.dictconfig
     host = config.pop('host', u'ldap')
     protocol = config.pop('protocol', u'ldap')
-    source.set_attributes(url=u'%s://%s' % (protocol, host))
+    source.cw_set(url=u'%s://%s' % (protocol, host))
     source.update_config(skip_unknown=True, **config)
 
 commit()
--- a/misc/scripts/chpasswd.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/misc/scripts/chpasswd.py	Tue Jul 17 12:24:46 2012 +0200
@@ -42,7 +42,7 @@
 crypted = crypt_password(pass1)
 
 cwuser = rset.get_entity(0,0)
-cwuser.set_attributes(upassword=Binary(crypted))
+cwuser.cw_set(upassword=Binary(crypted))
 commit()
 
 print("password updated.")
--- a/misc/scripts/ldapuser2ldapfeed.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/misc/scripts/ldapuser2ldapfeed.py	Tue Jul 17 12:24:46 2012 +0200
@@ -70,7 +70,7 @@
 
 
 source_ent = rql('CWSource S WHERE S eid %(s)s', {'s': source.eid}).get_entity(0, 0)
-source_ent.set_attributes(type=u"ldapfeed", parser=u"ldapfeed")
+source_ent.cw_set(type=u"ldapfeed", parser=u"ldapfeed")
 
 
 commit()
--- a/server/hook.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/server/hook.py	Tue Jul 17 12:24:46 2012 +0200
@@ -152,7 +152,7 @@
 
   On those events, the entity has no `cw_edited` dictionary.
 
-.. note:: `self.entity.set_attributes(age=42)` will set the `age` attribute to
+.. note:: `self.entity.cw_set(age=42)` will set the `age` attribute to
   42. But to do so, it will generate a rql query that will have to be processed,
   hence may trigger some hooks, etc. This could lead to infinitely looping hooks.
 
--- a/server/migractions.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/server/migractions.py	Tue Jul 17 12:24:46 2012 +0200
@@ -1321,7 +1321,7 @@
         except Exception:
             self.cmd_create_entity('CWProperty', pkey=unicode(pkey), value=value)
         else:
-            prop.set_attributes(value=value)
+            prop.cw_set(value=value)
 
     # other data migration commands ###########################################
 
--- a/server/sources/datafeed.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/server/sources/datafeed.py	Tue Jul 17 12:24:46 2012 +0200
@@ -389,7 +389,7 @@
             attrs = dict( (k, v) for k, v in attrs.iteritems()
                           if v != getattr(entity, k))
             if attrs:
-                entity.set_attributes(**attrs)
+                entity.cw_set(**attrs)
                 self.notify_updated(entity)
 
 class DataFeedXMLParser(DataFeedParser):
--- a/server/test/unittest_repository.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/server/test/unittest_repository.py	Tue Jul 17 12:24:46 2012 +0200
@@ -521,7 +521,7 @@
         self.commit()
         self.assertEqual(len(c.reverse_fiche), 1)
 
-    def test_set_attributes_in_before_update(self):
+    def test_cw_set_in_before_update(self):
         # local hook
         class DummyBeforeHook(Hook):
             __regid__ = 'dummy-before-hook'
@@ -533,31 +533,31 @@
                 pendings = self._cw.transaction_data.setdefault('pending', set())
                 if self.entity.eid not in pendings:
                     pendings.add(self.entity.eid)
-                    self.entity.set_attributes(alias=u'foo')
+                    self.entity.cw_set(alias=u'foo')
         with self.temporary_appobjects(DummyBeforeHook):
             req = self.request()
             addr = req.create_entity('EmailAddress', address=u'a@b.fr')
-            addr.set_attributes(address=u'a@b.com')
+            addr.cw_set(address=u'a@b.com')
             rset = self.execute('Any A,AA WHERE X eid %(x)s, X address A, X alias AA',
                                 {'x': addr.eid})
             self.assertEqual(rset.rows, [[u'a@b.com', u'foo']])
 
-    def test_set_attributes_in_before_add(self):
+    def test_cw_set_in_before_add(self):
         # local hook
         class DummyBeforeHook(Hook):
             __regid__ = 'dummy-before-hook'
             __select__ = Hook.__select__ & is_instance('EmailAddress')
             events = ('before_add_entity',)
             def __call__(self):
-                # set_attributes is forbidden within before_add_entity()
-                self.entity.set_attributes(alias=u'foo')
+                # cw_set is forbidden within before_add_entity()
+                self.entity.cw_set(alias=u'foo')
         with self.temporary_appobjects(DummyBeforeHook):
             req = self.request()
             # XXX will fail with python -O
             self.assertRaises(AssertionError, req.create_entity,
                               'EmailAddress', address=u'a@b.fr')
 
-    def test_multiple_edit_set_attributes(self):
+    def test_multiple_edit_cw_set(self):
         """make sure cw_edited doesn't get cluttered
         by previous entities on multiple set
         """
@@ -663,7 +663,7 @@
         self.commit()
         rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
         self.assertEqual(rset.rows, [])
-        req.user.set_relations(use_email=toto)
+        req.user.cw_set(use_email=toto)
         self.commit()
         rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
         self.assertEqual(rset.rows, [[req.user.eid]])
@@ -673,11 +673,11 @@
         rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
         self.assertEqual(rset.rows, [])
         tutu = req.create_entity('EmailAddress', address=u'tutu@logilab.fr')
-        req.user.set_relations(use_email=tutu)
+        req.user.cw_set(use_email=tutu)
         self.commit()
         rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'tutu'})
         self.assertEqual(rset.rows, [[req.user.eid]])
-        tutu.set_attributes(address=u'hip@logilab.fr')
+        tutu.cw_set(address=u'hip@logilab.fr')
         self.commit()
         rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'tutu'})
         self.assertEqual(rset.rows, [])
@@ -789,7 +789,7 @@
             personnes.append(p)
         abraham = req.create_entity('Personne', nom=u'Abraham', prenom=u'John', sexe=u'M')
         for j in xrange(0, 2000, 100):
-            abraham.set_relations(personne_composite=personnes[j:j+100])
+            abraham.cw_set(personne_composite=personnes[j:j+100])
         t1 = time.time()
         self.info('creation: %.2gs', (t1 - t0))
         req.cnx.commit()
@@ -815,7 +815,7 @@
         t1 = time.time()
         self.info('creation: %.2gs', (t1 - t0))
         for j in xrange(100, 2000, 100):
-            abraham.set_relations(personne_composite=personnes[j:j+100])
+            abraham.cw_set(personne_composite=personnes[j:j+100])
         t2 = time.time()
         self.info('more relations: %.2gs', (t2-t1))
         req.cnx.commit()
@@ -835,7 +835,7 @@
         t1 = time.time()
         self.info('creation: %.2gs', (t1 - t0))
         for j in xrange(100, 2000, 100):
-            abraham.set_relations(personne_inlined=personnes[j:j+100])
+            abraham.cw_set(personne_inlined=personnes[j:j+100])
         t2 = time.time()
         self.info('more relations: %.2gs', (t2-t1))
         req.cnx.commit()
@@ -916,7 +916,7 @@
         p1 = req.create_entity('Personne', nom=u'Vincent')
         p2 = req.create_entity('Personne', nom=u'Florent')
         w = req.create_entity('Affaire', ref=u'wc')
-        w.set_relations(todo_by=[p1,p2])
+        w.cw_set(todo_by=[p1,p2])
         w.cw_clear_all_caches()
         self.commit()
         self.assertEqual(len(w.todo_by), 1)
@@ -927,9 +927,9 @@
         p1 = req.create_entity('Personne', nom=u'Vincent')
         p2 = req.create_entity('Personne', nom=u'Florent')
         w = req.create_entity('Affaire', ref=u'wc')
-        w.set_relations(todo_by=p1)
+        w.cw_set(todo_by=p1)
         self.commit()
-        w.set_relations(todo_by=p2)
+        w.cw_set(todo_by=p2)
         w.cw_clear_all_caches()
         self.commit()
         self.assertEqual(len(w.todo_by), 1)
--- a/server/test/unittest_storage.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/server/test/unittest_storage.py	Tue Jul 17 12:24:46 2012 +0200
@@ -99,7 +99,7 @@
         f1 = self.create_file()
         self.commit()
         self.assertEqual(file(expected_filepath).read(), 'the-data')
-        f1.set_attributes(data=Binary('the new data'))
+        f1.cw_set(data=Binary('the new data'))
         self.rollback()
         self.assertEqual(file(expected_filepath).read(), 'the-data')
         f1.cw_delete()
@@ -204,7 +204,7 @@
         # use self.session to use server-side cache
         f1 = self.session.create_entity('File', data=Binary('some data'),
                                         data_format=u'text/plain', data_name=u'foo')
-        # NOTE: do not use set_attributes() which would automatically
+        # NOTE: do not use cw_set() which would automatically
         #       update f1's local dict. We want the pure rql version to work
         self.execute('SET F data %(d)s WHERE F eid %(f)s',
                      {'d': Binary('some other data'), 'f': f1.eid})
@@ -218,7 +218,7 @@
         # use self.session to use server-side cache
         f1 = self.session.create_entity('File', data=Binary('some data'),
                                         data_format=u'text/plain', data_name=u'foo.txt')
-        # NOTE: do not use set_attributes() which would automatically
+        # NOTE: do not use cw_set() which would automatically
         #       update f1's local dict. We want the pure rql version to work
         self.commit()
         old_path = self.fspath(f1)
@@ -240,7 +240,7 @@
         # use self.session to use server-side cache
         f1 = self.session.create_entity('File', data=Binary('some data'),
                                         data_format=u'text/plain', data_name=u'foo.txt')
-        # NOTE: do not use set_attributes() which would automatically
+        # NOTE: do not use cw_set() which would automatically
         #       update f1's local dict. We want the pure rql version to work
         self.commit()
         old_path = self.fspath(f1)
@@ -265,7 +265,7 @@
         f = self.session.create_entity('Affaire', opt_attr=Binary('toto'))
         self.session.commit()
         self.session.set_cnxset()
-        f.set_attributes(opt_attr=None)
+        f.cw_set(opt_attr=None)
         self.session.commit()
 
     @tag('fs_importing', 'update')
--- a/server/test/unittest_undo.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/server/test/unittest_undo.py	Tue Jul 17 12:24:46 2012 +0200
@@ -203,7 +203,7 @@
         c.cw_delete()
         txuuid = self.commit()
         c2 = session.create_entity('Card', title=u'hip', content=u'hip')
-        p.set_relations(fiche=c2)
+        p.cw_set(fiche=c2)
         self.commit()
         self.assertUndoTransaction(txuuid, [
             "Can't restore object relation fiche to entity "
@@ -217,7 +217,7 @@
         session = self.session
         g = session.create_entity('CWGroup', name=u'staff')
         session.execute('DELETE U in_group G WHERE U eid %(x)s', {'x': self.toto.eid})
-        self.toto.set_relations(in_group=g)
+        self.toto.cw_set(in_group=g)
         self.commit()
         self.toto.cw_delete()
         txuuid = self.commit()
@@ -265,7 +265,7 @@
         email = self.request().create_entity('EmailAddress', address=u'tutu@cubicweb.org')
         prop = self.request().create_entity('CWProperty', pkey=u'ui.default-text-format',
                                             value=u'text/html')
-        tutu.set_relations(use_email=email, reverse_for_user=prop)
+        tutu.cw_set(use_email=email, reverse_for_user=prop)
         self.commit()
         with self.assertRaises(ValidationError) as cm:
             self.cnx.undo_transaction(txuuid)
@@ -278,7 +278,7 @@
         g = session.create_entity('CWGroup', name=u'staff')
         txuuid = self.commit()
         session.execute('DELETE U in_group G WHERE U eid %(x)s', {'x': self.toto.eid})
-        self.toto.set_relations(in_group=g)
+        self.toto.cw_set(in_group=g)
         self.commit()
         with self.assertRaises(ValidationError) as cm:
             self.cnx.undo_transaction(txuuid)
@@ -304,7 +304,7 @@
         c = session.create_entity('Card', title=u'hop', content=u'hop')
         p = session.create_entity('Personne', nom=u'louis', fiche=c)
         self.commit()
-        p.set_relations(fiche=None)
+        p.cw_set(fiche=None)
         txuuid = self.commit()
         self.assertUndoTransaction(txuuid)
         self.commit()
@@ -319,7 +319,7 @@
         c = session.create_entity('Card', title=u'hop', content=u'hop')
         p = session.create_entity('Personne', nom=u'louis', fiche=c)
         self.commit()
-        p.set_relations(fiche=None)
+        p.cw_set(fiche=None)
         txuuid = self.commit()
         c.cw_delete()
         self.commit()
@@ -339,7 +339,7 @@
         c = session.create_entity('Card', title=u'hop', content=u'hop')
         p = session.create_entity('Personne', nom=u'louis')
         self.commit()
-        p.set_relations(fiche=c)
+        p.cw_set(fiche=c)
         txuuid = self.commit()
         self.assertUndoTransaction(txuuid)
         self.commit()
@@ -354,7 +354,7 @@
         c = session.create_entity('Card', title=u'hop', content=u'hop')
         p = session.create_entity('Personne', nom=u'louis')
         self.commit()
-        p.set_relations(fiche=c)
+        p.cw_set(fiche=c)
         txuuid = self.commit()
         c.cw_delete()
         self.commit()
@@ -369,7 +369,7 @@
         c2 = session.create_entity('Card', title=u'hip', content=u'hip')
         p = session.create_entity('Personne', nom=u'louis', fiche=c1)
         self.commit()
-        p.set_relations(fiche=c2)
+        p.cw_set(fiche=c2)
         txuuid = self.commit()
         self.assertUndoTransaction(txuuid)
         self.commit()
@@ -385,7 +385,7 @@
         c2 = session.create_entity('Card', title=u'hip', content=u'hip')
         p = session.create_entity('Personne', nom=u'louis', fiche=c1)
         self.commit()
-        p.set_relations(fiche=c2)
+        p.cw_set(fiche=c2)
         txuuid = self.commit()
         c1.cw_delete()
         self.commit()
@@ -401,7 +401,7 @@
         p = session.create_entity('Personne', nom=u'toto')
         session.commit()
         self.session.set_cnxset()
-        p.set_attributes(nom=u'titi')
+        p.cw_set(nom=u'titi')
         txuuid = self.commit()
         self.assertUndoTransaction(txuuid)
         p.cw_clear_all_caches()
@@ -412,7 +412,7 @@
         p = session.create_entity('Personne', nom=u'toto')
         session.commit()
         self.session.set_cnxset()
-        p.set_attributes(nom=u'titi')
+        p.cw_set(nom=u'titi')
         txuuid = self.commit()
         p.cw_delete()
         self.commit()
--- a/sobjects/ldapparser.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/sobjects/ldapparser.py	Tue Jul 17 12:24:46 2012 +0200
@@ -81,7 +81,7 @@
             attrs = dict( (k, v) for k, v in attrs.iteritems()
                           if v != getattr(entity, k))
             if attrs:
-                entity.set_attributes(**attrs)
+                entity.cw_set(**attrs)
                 self.notify_updated(entity)
 
     def ldap2cwattrs(self, sdict, tdict=None):
@@ -110,7 +110,7 @@
         if entity.__regid__ == 'EmailAddress':
             return
         groups = [self._get_group(n) for n in self.source.user_default_groups]
-        entity.set_relations(in_group=groups)
+        entity.cw_set(in_group=groups)
         self._process_email(entity, sourceparams)
 
     def is_deleted(self, extid, etype, eid):
@@ -137,9 +137,9 @@
                 email = self.extid2entity(emailextid, 'EmailAddress',
                                           address=emailaddr)
                 if entity.primary_email:
-                    entity.set_relations(use_email=email)
+                    entity.cw_set(use_email=email)
                 else:
-                    entity.set_relations(primary_email=email)
+                    entity.cw_set(primary_email=email)
             elif self.sourceuris:
                 # pop from sourceuris anyway, else email may be removed by the
                 # source once import is finished
--- a/test/unittest_entity.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/test/unittest_entity.py	Tue Jul 17 12:24:46 2012 +0200
@@ -687,23 +687,23 @@
         self.assertEqual(card4.rest_path(), unicode(card4.eid))
 
 
-    def test_set_attributes(self):
+    def test_cw_set_attributes(self):
         req = self.request()
         person = req.create_entity('Personne', nom=u'di mascio', prenom=u'adrien')
         self.assertEqual(person.prenom, u'adrien')
         self.assertEqual(person.nom, u'di mascio')
-        person.set_attributes(prenom=u'sylvain', nom=u'thénault')
+        person.cw_set(prenom=u'sylvain', nom=u'thénault')
         person = self.execute('Personne P').get_entity(0, 0) # XXX retreival needed ?
         self.assertEqual(person.prenom, u'sylvain')
         self.assertEqual(person.nom, u'thénault')
 
-    def test_set_relations(self):
+    def test_cw_set_relations(self):
         req = self.request()
         person = req.create_entity('Personne', nom=u'chauvat', prenom=u'nicolas')
         note = req.create_entity('Note', type=u'x')
-        note.set_relations(ecrit_par=person)
+        note.cw_set(ecrit_par=person)
         note = req.create_entity('Note', type=u'y')
-        note.set_relations(ecrit_par=person.eid)
+        note.cw_set(ecrit_par=person.eid)
         self.assertEqual(len(person.reverse_ecrit_par), 2)
 
     def test_metainformation_and_external_absolute_url(self):
--- a/web/test/unittest_reledit.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/web/test/unittest_reledit.py	Tue Jul 17 12:24:46 2012 +0200
@@ -175,8 +175,8 @@
 
     def setup_database(self):
         super(ClickAndEditFormUICFGTC, self).setup_database()
-        self.tick.set_relations(concerns=self.proj)
-        self.proj.set_relations(manager=self.toto)
+        self.tick.cw_set(concerns=self.proj)
+        self.proj.cw_set(manager=self.toto)
 
     def test_with_uicfg(self):
         old_rctl = reledit_ctrl._tagdefs.copy()
--- a/web/test/unittest_urlrewrite.py	Tue Jul 17 17:16:28 2012 +0200
+++ b/web/test/unittest_urlrewrite.py	Tue Jul 17 12:24:46 2012 +0200
@@ -105,9 +105,9 @@
     def setup_database(self):
         req = self.request()
         self.p1 = self.create_user(req, u'user1')
-        self.p1.set_attributes(firstname=u'joe', surname=u'Dalton')
+        self.p1.cw_set(firstname=u'joe', surname=u'Dalton')
         self.p2 = self.create_user(req, u'user2')
-        self.p2.set_attributes(firstname=u'jack', surname=u'Dalton')
+        self.p2.cw_set(firstname=u'jack', surname=u'Dalton')
 
     def test_rgx_action_with_transforms(self):
         class TestSchemaBasedRewriter(SchemaBasedRewriter):