backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 19 Oct 2009 15:16:41 +0200
changeset 3720 5376aaadd16b
parent 3678 29f74716fd70 (current diff)
parent 3719 7547fe738e54 (diff)
child 3721 a02f7df5bbec
backport stable
__init__.py
cwctl.py
cwvreg.py
dbapi.py
devtools/devctl.py
devtools/testlib.py
entities/__init__.py
entities/wfobjs.py
entity.py
goa/appobjects/components.py
goa/db.py
hooks/integrity.py
hooks/metadata.py
hooks/security.py
hooks/syncschema.py
req.py
rset.py
schema.py
selectors.py
server/__init__.py
server/checkintegrity.py
server/hook.py
server/hooksmanager.py
server/migractions.py
server/pool.py
server/repository.py
server/sources/__init__.py
server/sources/ldapuser.py
server/sources/native.py
server/ssplanner.py
server/test/unittest_migractions.py
server/test/unittest_msplanner.py
server/test/unittest_rql2sql.py
sobjects/notification.py
test/unittest_entity.py
view.py
web/facet.py
web/formfields.py
web/formwidgets.py
web/uicfg.py
web/views/__init__.py
web/views/actions.py
web/views/autoform.py
web/views/baseviews.py
web/views/bookmark.py
web/views/csvexport.py
web/views/editcontroller.py
web/views/editforms.py
web/views/facets.py
web/views/forms.py
web/views/magicsearch.py
web/views/navigation.py
web/views/owl.py
web/views/plots.py
web/views/primary.py
web/views/schema.py
web/views/sparql.py
web/views/startup.py
web/views/tableview.py
web/views/urlpublishing.py
web/views/xmlrss.py
--- a/common/migration.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/common/migration.py	Mon Oct 19 15:16:41 2009 +0200
@@ -92,7 +92,9 @@
 
     def __init__(self, config, interactive=True, verbosity=1):
         self.config = config
-        self.config.init_log(logthreshold=logging.ERROR, debug=True)
+        if config:
+            # no config on shell to a remote instance
+            self.config.init_log(logthreshold=logging.ERROR, debug=True)
         # 0: no confirmation, 1: only main commands confirmed, 2 ask for everything
         self.verbosity = verbosity
         self.need_wrap = True
@@ -144,7 +146,7 @@
         ctx['versions_map'] = vmap
         if self.config.accept_mode('Any') and 'cubicweb' in vmap:
             migrdir = self.config.migration_scripts_dir()
-            self.process_script(join(migrdir, 'bootstrapmigration_repository.py'))
+            self.cmd_process_script(join(migrdir, 'bootstrapmigration_repository.py'))
         for cube, fromversion, toversion in toupgrade:
             if cube == 'cubicweb':
                 migrdir = self.config.migration_scripts_dir()
@@ -159,7 +161,7 @@
                     if prevversion is not None and version != prevversion:
                         self.cube_upgraded(cube, prevversion)
                     prevversion = version
-                    self.process_script(script)
+                    self.cmd_process_script(script)
                 self.cube_upgraded(cube, toversion)
             else:
                 self.cube_upgraded(cube, toversion)
@@ -261,7 +263,7 @@
                     context[attr[4:]] = getattr(self, attr)
         return context
 
-    def process_script(self, migrscript, funcname=None, *args, **kwargs):
+    def cmd_process_script(self, migrscript, funcname=None, *args, **kwargs):
         """execute a migration script
         in interactive mode,  display the migration script path, ask for
         confirmation and execute it if confirmed
--- a/cwctl.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/cwctl.py	Mon Oct 19 15:16:41 2009 +0200
@@ -685,10 +685,16 @@
 
 
 class ShellCommand(Command):
-    """Run an interactive migration shell. This is a python shell with
-    enhanced migration commands predefined in the namespace. An additional
-    argument may be given corresponding to a file containing commands to
-    execute in batch mode.
+    """Run an interactive migration shell on an instance. This is a python shell
+    with enhanced migration commands predefined in the namespace. An additional
+    argument may be given corresponding to a file containing commands to execute
+    in batch mode.
+
+    By default it will connect to a local instance using an in memory
+    connection, unless -P option is specified, in which case you will be
+    connected through pyro. In the later case, you won't have access to
+    repository internals (session, etc...) so most migration commands won't be
+    available.
 
     <instance>
       the identifier of the instance to connect.
@@ -698,46 +704,87 @@
     options = (
         ('system-only',
          {'short': 'S', 'action' : 'store_true',
-          'default': False,
           'help': 'only connect to the system source when the instance is '
           'using multiple sources. You can\'t use this option and the '
-          '--ext-sources option at the same time.'}),
+          '--ext-sources option at the same time.',
+          'group': 'local'
+         }),
 
         ('ext-sources',
          {'short': 'E', 'type' : 'csv', 'metavar': '<sources>',
-          'default': None,
           'help': "For multisources instances, specify to which sources the \
 repository should connect to for upgrading. When unspecified or 'all' given, \
 will connect to all defined sources. If 'migration' is given, appropriate \
 sources for migration will be automatically selected.",
+          'group': 'local'
           }),
 
         ('force',
          {'short': 'f', 'action' : 'store_true',
-          'default' : False,
-          'help': 'don\'t check instance is up to date.'}
-         ),
+          'help': 'don\'t check instance is up to date.',
+          'group': 'local'
+          }),
+
+        ('pyro',
+         {'short': 'P', 'action' : 'store_true',
+          'help': 'connect to a running instance through Pyro.',
+          'group': 'remote',
+          }),
+        ('pyro-ns-host',
+         {'short': 'H', 'type' : 'string', 'metavar': '<host[:port]>',
+          'help': 'Pyro name server host. If not set, will be detected by '
+          'using a broadcast query.',
+          'group': 'remote'
+          }),
         )
 
     def run(self, args):
         appid = pop_arg(args, 99, msg="No instance specified !")
-        config = cwcfg.config_for(appid)
-        if self.config.ext_sources:
-            assert not self.config.system_only
-            sources = self.config.ext_sources
-        elif self.config.system_only:
-            sources = ('system',)
+        if self.config.pyro:
+            from cubicweb import AuthenticationError
+            from cubicweb.dbapi import connect
+            from cubicweb.server.utils import manager_userpasswd
+            from cubicweb.server.migractions import ServerMigrationHelper
+            while True:
+                try:
+                    login, pwd = manager_userpasswd(msg=None)
+                    cnx = connect(appid, login=login, password=pwd,
+                                  host=self.config.pyro_ns_host, mulcnx=False)
+                except AuthenticationError, ex:
+                    print ex
+                except (KeyboardInterrupt, EOFError):
+                    print
+                    sys.exit(0)
+                else:
+                    break
+            cnx.load_appobjects()
+            repo = cnx._repo
+            mih = ServerMigrationHelper(None, repo=repo, cnx=cnx,
+                                         # hack so it don't try to load fs schema
+                                        schema=1)
         else:
-            sources = ('all',)
-        config.set_sources_mode(sources)
-        config.repairing = self.config.force
-        mih = config.migration_handler()
-        if args:
-            for arg in args:
-                mih.process_script(arg)
-        else:
-            mih.interactive_shell()
-        mih.shutdown()
+            config = cwcfg.config_for(appid)
+            if self.config.ext_sources:
+                assert not self.config.system_only
+                sources = self.config.ext_sources
+            elif self.config.system_only:
+                sources = ('system',)
+            else:
+                sources = ('all',)
+            config.set_sources_mode(sources)
+            config.repairing = self.config.force
+            mih = config.migration_handler()
+        try:
+            if args:
+                for arg in args:
+                    mih.cmd_process_script(arg)
+            else:
+                mih.interactive_shell()
+        finally:
+            if not self.config.pyro:
+                mih.shutdown()
+            else:
+                cnx.close()
 
 
 class RecompileInstanceCatalogsCommand(InstanceCommand):
--- a/cwvreg.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/cwvreg.py	Mon Oct 19 15:16:41 2009 +0200
@@ -29,7 +29,7 @@
 
 
 def use_interfaces(obj):
-    """return interfaces used by the given object by searchinf for implements
+    """return interfaces used by the given object by searching for implements
     selectors, with a bw compat fallback to accepts_interfaces attribute
     """
     from cubicweb.selectors import implements
@@ -62,16 +62,28 @@
         pass
 
     @deprecated('[3.6] select object, then use obj.render()')
-    def render(self, __oid, req, __fallback_oid=None, rset=None, **kwargs):
-        """select object, or fallback object if specified and the first one
-        isn't selectable, then render it
+    def render(self, __oid, req, __fallback_oid=None, rset=None, initargs=None,
+               **kwargs):
+        """Select object with the given id (`__oid`) then render it.  If the
+        object isn't selectable, try to select fallback object if
+        `__fallback_oid` is specified.
+
+        If specified `initargs` is expected to be a dictionnary containing
+        arguments that should be given to selection (hence to object's __init__
+        as well), but not to render(). Other arbitrary keyword arguments will be
+        given to selection *and* to render(), and so should be handled by
+        object's call or cell_call method..
         """
+        if initargs is None:
+            initargs = kwargs
+        else:
+            initargs.update(kwargs)
         try:
-            obj = self.select(__oid, req, rset=rset, **kwargs)
+            obj = self.select(__oid, req, rset=rset, **initargs)
         except NoSelectableObject:
             if __fallback_oid is None:
                 raise
-            obj = self.select(__fallback_oid, req, rset=rset, **kwargs)
+            obj = self.select(__fallback_oid, req, rset=rset, **initargs)
         return obj.render(**kwargs)
 
     @deprecated('[3.6] use select_or_none and test for obj.cw_propval("visible")')
@@ -305,9 +317,11 @@
         self.register_objects(searchpath, force_reload=False)
         # map lowered entity type names to their actual name
         self.case_insensitive_etypes = {}
-        for etype in self.schema.entities():
-            etype = str(etype)
+        for eschema in self.schema.entities():
+            etype = str(eschema)
             self.case_insensitive_etypes[etype.lower()] = etype
+            clear_cache(eschema, 'ordered_relations')
+            clear_cache(eschema, 'meta_attributes')
 
     def _set_schema(self, schema):
         """set instance'schema"""
@@ -379,7 +393,7 @@
             implemented_interfaces = set()
             if 'Any' in self.get('etypes', ()):
                 for etype in self.schema.entities():
-                    if etype.is_final():
+                    if etype.final:
                         continue
                     cls = self['etypes'].etype_class(etype)
                     for iface in cls.__implements__:
--- a/dbapi.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/dbapi.py	Mon Oct 19 15:16:41 2009 +0200
@@ -448,7 +448,7 @@
         return self._repo.get_schema()
 
     def load_appobjects(self, cubes=_MARKER, subpath=None, expand=True,
-                      force_reload=None):
+                        force_reload=None):
         config = self.vreg.config
         if cubes is _MARKER:
             cubes = self._repo.get_cubes()
--- a/devtools/devctl.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/devtools/devctl.py	Mon Oct 19 15:16:41 2009 +0200
@@ -144,13 +144,13 @@
         if etype not in libschema:
             add_msg(w, etype)
             add_msg(w, '%s_plural' % etype)
-            if not eschema.is_final():
+            if not eschema.final:
                 add_msg(w, 'This %s' % etype)
                 add_msg(w, 'New %s' % etype)
             if eschema.description and not eschema.description in done:
                 done.add(eschema.description)
                 add_msg(w, eschema.description)
-        if eschema.is_final():
+        if eschema.final:
             continue
         for rschema, targetschemas, role in eschema.relation_definitions(True):
             for tschema in targetschemas:
@@ -201,7 +201,7 @@
             for subjschema in rschema.subjects():
                 if not subjschema in libsubjects:
                     add_msg(w, rtype, subjschema.type)
-        if not (schema.rschema(rtype).is_final() or rschema.symetric):
+        if not (schema.rschema(rtype).final or rschema.symetric):
             if rschema not in no_context_rtypes:
                 libobjects = librschema and librschema.objects() or ()
                 for objschema in rschema.objects():
--- a/devtools/fill.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/devtools/fill.py	Mon Oct 19 15:16:41 2009 +0200
@@ -347,7 +347,7 @@
         queries = []
         #   1/ skip final relations and explictly ignored relations
         rels = [rschema for rschema in self.schema.relations()
-                if not (rschema.is_final() or rschema in ignored_relations)]
+                if not (rschema.final or rschema in ignored_relations)]
         # for each relation
         #   2/ take each possible couple (subj, obj)
         #   3/ analyze cardinality of relation
@@ -397,9 +397,12 @@
             restrictions = ', '.join(c.restriction for c in constraints)
             q += ', %s' % restrictions
             # restrict object eids if possible
+            # XXX the attempt to restrict below in completely wrong
+            # disabling it for now
             objeids = select(restrictions, self.cursor)
         else:
             objeids = oedict.get(obj, frozenset())
+##         objeids = oedict.get(obj, frozenset())
         if subjcard in '?1' or objcard in '?1':
             for subjeid, objeid in used:
                 if subjcard in '?1' and subjeid in subjeids:
--- a/devtools/testlib.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/devtools/testlib.py	Mon Oct 19 15:16:41 2009 +0200
@@ -45,7 +45,6 @@
         file('/tmp/toto.html', 'w').write(data)
         webbrowser.open('file:///tmp/toto.html')
 
-
 def line_context_filter(line_no, center, before=3, after=None):
     """return true if line are in context
 
@@ -715,7 +714,7 @@
     # compute how many entities by type we need to be able to satisfy relation constraint
     relmap = {}
     for rschema in schema.relations():
-        if rschema.is_final():
+        if rschema.final:
             continue
         for subj, obj in rschema.iter_rdefs():
             card = rschema.rproperty(subj, obj, 'cardinality')
@@ -759,6 +758,7 @@
     def post_populate(self, cursor):
         pass
 
+
     @nocoverage
     def auto_populate(self, how_many):
         """this method populates the database with `how_many` entities
@@ -779,9 +779,9 @@
             rset = cu.execute('%s X' % etype)
             edict[str(etype)] = set(row[0] for row in rset.rows)
         existingrels = {}
-        ignored_relations = SYSTEM_RELATIONS | set(self.ignored_relations)
+        ignored_relations = SYSTEM_RELATIONS + self.ignored_relations
         for rschema in self.schema.relations():
-            if rschema.is_final() or rschema in ignored_relations:
+            if rschema.final or rschema in ignored_relations:
                 continue
             rset = cu.execute('DISTINCT Any X,Y WHERE X %s Y' % rschema)
             existingrels.setdefault(rschema.type, set()).update((x, y) for x, y in rset)
--- a/doc/book/en/annexes/rql/language.rst	Thu Oct 15 11:20:26 2009 +0200
+++ b/doc/book/en/annexes/rql/language.rst	Mon Oct 19 15:16:41 2009 +0200
@@ -11,13 +11,9 @@
 
 ::
 
-     DISTINCT, INSERT, SET, DELETE,
-     WHERE, AND, OR, NOT, EXISTS,
-     IN, LIKE, UNION, WITH, BEING,
-     TRUE, FALSE, NULL, TODAY, NOW,
-     LIMIT, OFFSET,
-     HAVING, GROUPBY, ORDERBY, ASC, DESC
-
+  AND, ASC, BEING, DELETE, DESC, DISTINCT, EXISTS, FALSE, GROUPBY,
+  HAVING, ILIKE, IN, INSERT, LIKE, LIMIT, NOT, NOW, NULL, OFFSET,
+  OR, ORDERBY, SET, TODAY, TRUE, UNION, WHERE, WITH
 
 Variables and Typing
 ~~~~~~~~~~~~~~~~~~~~
@@ -76,7 +72,7 @@
 ````````````````````
 ::
 
-     =, <, <=, >=, >, ~=, IN, LIKE
+     =, <, <=, >=, >, ~=, IN, LIKE, ILIKE
 
 * The operator `=` is the default operator.
 
@@ -88,6 +84,8 @@
      Any X WHERE X name ~= 'Th%'
      Any X WHERE X name LIKE '%lt'
 
+* The operator `ILIKE` is the case insensitive version of `LIKE`.
+
 * The operator `IN` provides a list of possible values:
   ::
 
--- a/entities/__init__.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/entities/__init__.py	Mon Oct 19 15:16:41 2009 +0200
@@ -63,7 +63,7 @@
 
     def dc_description(self, format='text/plain'):
         """return a suitable description for this entity"""
-        if self.e_schema.has_subject_relation('description'):
+        if 'description' in self.e_schema.subjrels:
             return self.printable_value('description', format=format)
         return u''
 
--- a/entities/wfobjs.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/entities/wfobjs.py	Mon Oct 19 15:16:41 2009 +0200
@@ -446,15 +446,12 @@
             kwargs['comment'] = comment
             if commentformat is not None:
                 kwargs['comment_format'] = commentformat
-        args = [('wf_info_for', 'E')]
-        kwargs['E'] = self.eid
+        kwargs['wf_info_for'] = self
         if treid is not None:
-            args.append( ('by_transition', 'T') )
-            kwargs['T'] = treid
+            kwargs['by_transition'] = self.req.entity_from_eid(treid)
         if tseid is not None:
-            args.append( ('to_state', 'S') )
-            kwargs['S'] = tseid
-        return self._cw.create_entity('TrInfo', *args, **kwargs)
+            kwargs['to_state'] = self.req.entity_from_eid(tseid)
+        return self._cw.create_entity('TrInfo', **kwargs)
 
     def fire_transition(self, tr, comment=None, commentformat=None):
         """change the entity's state by firing transition of the given name in
--- a/entity.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/entity.py	Mon Oct 19 15:16:41 2009 +0200
@@ -139,7 +139,7 @@
         _fetchattrs = []
         for attr in fetchattrs:
             try:
-                rschema = eschema.subject_relation(attr)
+                rschema = eschema.subjrels[attr]
             except KeyError:
                 cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
                             attr, cls.__regid__)
@@ -150,7 +150,7 @@
             selection.append(var)
             restriction = '%s %s %s' % (mainvar, attr, var)
             restrictions.append(restriction)
-            if not rschema.is_final():
+            if not rschema.final:
                 # XXX this does not handle several destination types
                 desttype = rschema.objects(eschema.type)[0]
                 card = rschema.rproperty(eschema, desttype, 'cardinality')[0]
@@ -184,7 +184,7 @@
             needcheck = not cls.e_schema.has_unique_values(mainattr)
         else:
             for rschema in cls.e_schema.subject_relations():
-                if rschema.is_final() and rschema != 'eid' and cls.e_schema.has_unique_values(rschema):
+                if rschema.final and rschema != 'eid' and cls.e_schema.has_unique_values(rschema):
                     mainattr = str(rschema)
                     needcheck = False
                     break
@@ -383,7 +383,7 @@
         assert self.has_eid()
         execute = self._cw.execute
         for rschema in self.e_schema.subject_relations():
-            if rschema.is_final() or rschema.meta:
+            if rschema.final or rschema.meta:
                 continue
             # skip already defined relations
             if getattr(self, rschema.type):
@@ -431,7 +431,7 @@
     def to_complete_relations(self):
         """by default complete final relations to when calling .complete()"""
         for rschema in self.e_schema.subject_relations():
-            if rschema.is_final():
+            if rschema.final:
                 continue
             if len(rschema.objects(self.e_schema)) > 1:
                 # ambigous relations, the querier doesn't handle
@@ -591,14 +591,14 @@
             if targettypes is None:
                 targettypes = rschema.objects(self.e_schema)
             else:
-                restriction += 'E is IN (%s)' % ','.join(targettypes)
+                restriction += ', X is IN (%s)' % ','.join(targettypes)
             card = greater_card(rschema, (self.e_schema,), targettypes, 0)
         else:
             restriction = 'E eid %%(x)s, X %s E' % rtype
             if targettypes is None:
                 targettypes = rschema.subjects(self.e_schema)
             else:
-                restriction += 'E is IN (%s)' % ','.join(targettypes)
+                restriction += ', X is IN (%s)' % ','.join(targettypes)
             card = greater_card(rschema, targettypes, (self.e_schema,), 1)
         if len(targettypes) > 1:
             fetchattrs_list = []
@@ -706,7 +706,7 @@
         else raise `KeyError`
         """
         res = self._related_cache['%s_%s' % (rtype, role)][entities]
-        if limit is not None:
+        if limit is not None and limit < len(res):
             if entities:
                 res = res[:limit]
             else:
--- a/goa/__init__.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/__init__.py	Mon Oct 19 15:16:41 2009 +0200
@@ -65,7 +65,7 @@
             edef.name = edef.name.encode()
             assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
             eschema = super(CubicWebSchema, self).add_entity_type(edef)
-            if not eschema.is_final():
+            if not eschema.final:
                 # automatically add the eid relation to non final entity types
                 rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Bytes',
                                               cardinality='?1', uid=True)
--- a/goa/appobjects/components.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/appobjects/components.py	Mon Oct 19 15:16:41 2009 +0200
@@ -67,7 +67,7 @@
     """
     req = self.req
     for eschema in eschemas:
-        if eschema.is_final() or not (eschema.has_perm(req, 'read') or
+        if eschema.final or not (eschema.has_perm(req, 'read') or
                                       eschema.has_local_role('read')):
             continue
         etype = eschema.type
--- a/goa/appobjects/dbmgmt.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/appobjects/dbmgmt.py	Mon Oct 19 15:16:41 2009 +0200
@@ -175,7 +175,7 @@
         # XXX should use unsafe_execute with all hooks deactivated
         # XXX step by catching datastore errors?
         for eschema in self.schema.entities():
-            if eschema.is_final() or eschema in self.skip_etypes:
+            if eschema.final or eschema in self.skip_etypes:
                 continue
             self.req.execute('DELETE %s X' % eschema)
             self.w(u'deleted all %s entities<br/>' % eschema)
--- a/goa/db.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/db.py	Mon Oct 19 15:16:41 2009 +0200
@@ -148,7 +148,7 @@
     def __initialize__(cls):
         super(Model, cls).__initialize__()
         cls._attributes = frozenset(rschema for rschema in cls.e_schema.subject_relations()
-                                    if rschema.is_final())
+                                    if rschema.final)
 
     def __init__(self, *args, **kwargs):
         # db.Model prototype:
@@ -163,7 +163,7 @@
             super(Model, self).__init__(None, None)
             # if Model instances are given in kwargs, turn them into db model
             for key, val in kwargs.iteritems():
-                if key in self.e_schema.subject_relations() and not self.e_schema.schema[key].is_final():
+                if key in self.e_schema.subject_relations() and not self.e_schema.schema[key].final:
                     if isinstance(kwargs, (list, tuple)):
                         val = [isinstance(x, Model) and x._dbmodel or x for x in val]
                     elif isinstance(val, Model):
--- a/goa/dbinit.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/dbinit.py	Mon Oct 19 15:16:41 2009 +0200
@@ -90,7 +90,7 @@
     eschema = schema.eschema('CWEType')
     execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description)})
     for eschema in schema.entities():
-        if eschema.is_final() or eschema == 'CWEType':
+        if eschema.final or eschema == 'CWEType':
             continue
         execute(rql, {'name': unicode(eschema),
                       'descr': unicode(eschema.description)})
--- a/goa/overrides/rqlannotation.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/overrides/rqlannotation.py	Mon Oct 19 15:16:41 2009 +0200
@@ -13,7 +13,7 @@
     def __init__(self, schema):
         self.schema = schema
         self.nfdomain = frozenset(eschema.type for eschema in schema.entities()
-                                  if not eschema.is_final())
+                                  if not eschema.final)
     def annotate(self, rqlst):
         rqlst.has_text_query = False
         rqlst.need_distinct = False
--- a/goa/rqlinterpreter.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/goa/rqlinterpreter.py	Mon Oct 19 15:16:41 2009 +0200
@@ -156,7 +156,7 @@
         myvar = self.rhs.name
         ovar = self.var.name
         rtype = self.rtype
-        if self.schema.rschema(rtype).is_final():
+        if self.schema.rschema(rtype).final:
             # should be detected by rql.stcheck: "Any C WHERE NOT X attr C" doesn't make sense
             #if self._not:
             #    raise NotImplementedError()
@@ -595,7 +595,7 @@
             # ok, we *may* process this Not node (not implemented error will be
             # raised later if we can't)
             extra[node.parent] = True
-        if rschema.is_final():
+        if rschema.final:
             self._visit_final_relation(rschema, node, constraints, extra)
         elif neged:
             self._visit_non_final_neged_relation(rschema, node, constraints)
--- a/hooks/integrity.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/hooks/integrity.py	Mon Oct 19 15:16:41 2009 +0200
@@ -63,8 +63,11 @@
     __abstract__ = True
     category = 'integrity'
 
+class UserIntegrityHook(IntegrityHook):
+    __select__ == IntegrityHook.__select__ & ~regular_session()
 
-class CheckCardinalityHook(IntegrityHook):
+
+class CheckCardinalityHook(UserIntegrityHook):
     """check cardinalities are satisfied"""
     __regid__ = 'checkcard'
     events = ('after_add_entity', 'before_delete_relation')
@@ -139,7 +142,7 @@
         pass
 
 
-class CheckConstraintHook(IntegrityHook):
+class CheckConstraintHook(UserIntegrityHook):
     """check the relation satisfy its constraints
 
     this is delayed to a precommit time operation since other relation which
@@ -155,7 +158,7 @@
             _CheckConstraintsOp(self._cw, constraints=constraints,
                                rdef=(self.eidfrom, self.rtype, self.eidto))
 
-class CheckAttributeConstraintHook(IntegrityHook):
+class CheckAttributeConstraintHook(UserIntegrityHook):
     """check the attribute relation satisfy its constraints
 
     this is delayed to a precommit time operation since other relation which
@@ -168,7 +171,7 @@
         schema = self._cw.vreg.schema
         entity = self.entity
         for attr in entity.edited_attributes:
-            if schema.rschema(attr).is_final():
+            if schema.rschema(attr).final:
                 constraints = [c for c in entity.e_schema.constraints(attr)
                                if isinstance(c, RQLVocabularyConstraint)]
                 if constraints:
@@ -176,7 +179,7 @@
                                         rdef=(entity.eid, attr, None))
 
 
-class CheckUniqueHook(IntegrityHook):
+class CheckUniqueHook(UserIntegrityHook):
     __regid__ = 'checkunique'
     events = ('before_add_entity', 'before_update_entity')
 
@@ -184,7 +187,7 @@
         entity = self.entity
         eschema = entity.e_schema
         for attr in entity.edited_attributes:
-            if eschema.subject_relation(attr).is_final() and \
+            if eschema.subject_relation(attr).final and \
                    eschema.has_unique_values(attr):
                 val = entity[attr]
                 if val is None:
@@ -253,7 +256,7 @@
             self.entity['name'] = newname
 
 
-class TidyHtmlFields(IntegrityHook):
+class TidyHtmlFields(UserIntegrityHook):
     """tidy HTML in rich text strings"""
     __regid__ = 'htmltidy'
     events = ('before_add_entity', 'before_update_entity')
--- a/hooks/metadata.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/hooks/metadata.py	Mon Oct 19 15:16:41 2009 +0200
@@ -80,14 +80,18 @@
         session = self._cw
         entity = self.entity
         try:
-            session.add_relation(entity.eid, 'is',
-                                 eschema_type_eid(session, entity.__regid__))
+            #session.add_relation(entity.eid, 'is',
+            #                     eschema_type_eid(session, entity.__regid__))
+            session.system_sql('INSERT INTO is_relation(eid_from,eid_to) VALUES (%s,%s)'
+                           % (entity.eid, eschema_type_eid(session, entity.id)))
         except IndexError:
             # during schema serialization, skip
             return
         for etype in entity.e_schema.ancestors() + [entity.e_schema]:
-            session.add_relation(entity.eid, 'is_instance_of',
-                                 eschema_type_eid(session, etype))
+            #session.add_relation(entity.eid, 'is_instance_of',
+            #                     eschema_type_eid(session, etype))
+            session.system_sql('INSERT INTO is_instance_of_relation(eid_from,eid_to) VALUES (%s,%s)'
+                               % (entity.eid, eschema_type_eid(session, etype)))
 
 
 class SetOwnershipHook(MetaDataHook):
--- a/hooks/security.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/hooks/security.py	Mon Oct 19 15:16:41 2009 +0200
@@ -25,8 +25,8 @@
     for attr in editedattrs:
         if attr in defaults:
             continue
-        rschema = eschema.subject_relation(attr)
-        if rschema.is_final(): # non final relation are checked by other hooks
+        rschema = eschema.subjrels[attr]
+        if rschema.final: # non final relation are checked by other hooks
             # add/delete should be equivalent (XXX: unify them into 'update' ?)
             rschema.check_perm(session, 'add', eid)
 
--- a/hooks/syncschema.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/hooks/syncschema.py	Mon Oct 19 15:16:41 2009 +0200
@@ -216,7 +216,7 @@
     def precommit_event(self):
         session = self.session
         rschema = self.rschema
-        if rschema.is_final() or not 'inlined' in self.values:
+        if rschema.final or not 'inlined' in self.values:
             return # nothing to do
         inlined = self.values['inlined']
         entity = self.entity
@@ -454,7 +454,7 @@
                 sysource.create_index(self.session, table, column)
             else:
                 sysource.drop_index(self.session, table, column)
-        if 'cardinality' in self.values and self.rschema.is_final():
+        if 'cardinality' in self.values and self.rschema.final:
             adbh = self.session.pool.source('system').dbhelper
             if not adbh.alter_column_support:
                 # not supported (and NOT NULL not set by yams in that case, so
@@ -816,7 +816,25 @@
         self._cw.execute('DELETE Workflow X WHERE NOT X workflow_of Y')
 
 
+<<<<<<< /home/syt/src/fcubicweb/cubicweb/hooks/syncschema.py
 class AfterAddCWETypeHook(DelCWETypeHook):
+=======
+def after_del_relation_type(session, rdefeid, rtype, rteid):
+
+
+# addition hooks ###############################################################
+
+def before_add_eetype(session, entity):
+    """before adding a CWEType entity:
+    * check that we are not using an existing entity type,
+    """
+    name = entity['name']
+    schema = session.schema
+    if name in schema and schema[name].eid is not None:
+        raise RepositoryError('an entity type %s already exists' % name)
+
+def after_add_eetype(session, entity):
+>>>>>>> /tmp/schemahooks.py~other.2drHhu
     """after adding a CWEType entity:
     * create the necessary table
     * set creation_date and modification_date by creating the necessary
@@ -974,10 +992,11 @@
     def __call__(self):
         session = self._cw
         subjschema, rschema, objschema = session.vreg.schema.schema_by_eid(self.eidfrom)
+        subjschema, rschema, objschema = session.schema.schema_by_eid(rdefeid)
         pendings = session.transaction_data.get('pendingeids', ())
         pendingrdefs = session.transaction_data.setdefault('pendingrdefs', set())
         # first delete existing relation if necessary
-        if rschema.is_final():
+        if rschema.final:
             rdeftype = 'CWAttribute'
             pendingrdefs.add((subjschema, rschema))
         else:
@@ -987,7 +1006,6 @@
                 session.execute('DELETE X %s Y WHERE X is %s, Y is %s'
                                 % (rschema, subjschema, objschema))
         execute = session.unsafe_execute
-        rteid = self.eidto
         rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R,'
                        'R eid %%(x)s' % rdeftype, {'x': rteid})
         lastrel = rset[0][0] == 0
@@ -995,7 +1013,7 @@
         # relations, but only if it's the last instance for this relation type
         # for other relations
 
-        if (rschema.is_final() or rschema.inlined):
+        if (rschema.final or rschema.inlined):
             rset = execute('Any COUNT(X) WHERE X is %s, X relation_type R, '
                            'R eid %%(x)s, X from_entity E, E name %%(name)s'
                            % rdeftype, {'x': rteid, 'name': str(subjschema)})
--- a/req.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/req.py	Mon Oct 19 15:16:41 2009 +0200
@@ -108,7 +108,8 @@
 
     # XXX move to CWEntityManager or even better as factory method (unclear
     # where yet...)
-    def create_entity(self, etype, **kwargs):
+
+    def create_entity(self, etype, _cw_unsafe=False, **kwargs):
         """add a new entity of the given type
 
         Example (in a shell session):
@@ -117,16 +118,21 @@
         create_entity('Person', works_for=c, firstname=u'John', lastname=u'Doe')
 
         """
+        if _cw_unsafe:
+            execute = self.unsafe_execute
+        else:
+            execute = self.execute
         rql = 'INSERT %s X' % etype
         relations = []
         restrictions = set()
         cachekey = []
         pending_relations = []
-        for attr, value in kwargs.iteritems():
+        for attr, value in kwargs.items():
             if isinstance(value, (tuple, list, set, frozenset)):
                 if len(value) == 1:
                     value = iter(value).next()
                 else:
+                    del kwargs[attr]
                     pending_relations.append( (attr, value) )
                     continue
             if hasattr(value, 'eid'): # non final relation
@@ -147,13 +153,13 @@
             rql = '%s: %s' % (rql, ', '.join(relations))
         if restrictions:
             rql = '%s WHERE %s' % (rql, ', '.join(restrictions))
-        created = self.execute(rql, kwargs, cachekey).get_entity(0, 0)
+        created = execute(rql, kwargs, cachekey).get_entity(0, 0)
         for attr, values in pending_relations:
             if attr.startswith('reverse_'):
                 restr = 'Y %s X' % attr[len('reverse_'):]
             else:
                 restr = 'X %s Y' % attr
-            self.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
+            execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % (
                 restr, ','.join(str(r.eid) for r in values)),
                          {'x': created.eid}, 'x')
         return created
--- a/rqlrewrite.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/rqlrewrite.py	Mon Oct 19 15:16:41 2009 +0200
@@ -32,7 +32,7 @@
     allpossibletypes = {}
     for solution in solutions:
         for varname, etype in solution.iteritems():
-            if not varname in newroot.defined_vars or eschema(etype).is_final():
+            if not varname in newroot.defined_vars or eschema(etype).final:
                 continue
             allpossibletypes.setdefault(varname, set()).add(etype)
     for varname in sorted(allpossibletypes):
@@ -289,7 +289,7 @@
         stinfo = self.varinfo['stinfo']
         for rel in stinfo['relations']:
             rschema = self.schema.rschema(rel.r_type)
-            if rschema.is_final() or (rschema.inlined and
+            if rschema.final or (rschema.inlined and
                                       not rel in stinfo['rhsrelations']):
                 self.select.remove_node(rel)
                 rel.children[0].name = selectvar
--- a/rset.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/rset.py	Mon Oct 19 15:16:41 2009 +0200
@@ -396,7 +396,7 @@
         etype = self.description[row][col]
         try:
             eschema = self.vreg.schema.eschema(etype)
-            if eschema.is_final():
+            if eschema.final:
                 raise NotAnEntity(etype)
         except KeyError:
             raise NotAnEntity(etype)
@@ -461,14 +461,14 @@
                 if outerselidx is None:
                     continue
                 if x == 'subject':
-                    rschema = eschema.subject_relation(attr)
-                    if rschema.is_final():
+                    rschema = eschema.subjrels[attr]
+                    if rschema.final:
                         entity[attr] = rowvalues[outerselidx]
                         continue
                     tetype = rschema.objects(etype)[0]
                     card = rschema.rproperty(etype, tetype, 'cardinality')[0]
                 else:
-                    rschema = eschema.object_relation(attr)
+                    rschema = eschema.objrels[attr]
                     tetype = rschema.subjects(etype)[0]
                     card = rschema.rproperty(tetype, etype, 'cardinality')[1]
                 # only keep value if it can't be multivalued
@@ -541,7 +541,7 @@
         locate_query_col = col
         rqlst = self.syntax_tree()
         etype = self.description[row][col]
-        if self.vreg.schema.eschema(etype).is_final():
+        if self.vreg.schema.eschema(etype).final:
             # final type, find a better one to locate the correct subquery
             # (ambiguous if possible)
             for i in xrange(len(rqlst.children[0].selection)):
@@ -550,7 +550,7 @@
                 coletype = self.description[row][i]
                 if coletype is None:
                     continue
-                if not self.vreg.schema.eschema(coletype).is_final():
+                if not self.vreg.schema.eschema(coletype).final:
                     etype = coletype
                     locate_query_col = i
                     if len(self.column_types(i)) > 1:
--- a/schema.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/schema.py	Mon Oct 19 15:16:41 2009 +0200
@@ -239,7 +239,7 @@
     """return system entity types only: skip final, schema and application entities
     """
     for eschema in schema.entities():
-        if eschema.is_final() or eschema.schema_entity():
+        if eschema.final or eschema.schema_entity():
             continue
         yield eschema.type
 
@@ -301,7 +301,7 @@
         may_need_has_text, has_has_text = False, False
         need_has_text = None
         for rschema in self.subject_relations():
-            if rschema.is_final():
+            if rschema.final:
                 if rschema == 'has_text':
                     has_has_text = True
                 elif self.rproperty(rschema, 'fulltextindexed'):
@@ -433,7 +433,7 @@
 
     def rql_expression(self, expression, mainvars=None, eid=None):
         """rql expression factory"""
-        if self.is_final():
+        if self.final:
             return ERQLExpression(expression, mainvars, eid)
         return RRQLExpression(expression, mainvars, eid)
 
@@ -473,7 +473,7 @@
         edef.name = bw_normalize_etype(edef.name)
         assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name)
         eschema = super(CubicWebSchema, self).add_entity_type(edef)
-        if not eschema.is_final():
+        if not eschema.final:
             # automatically add the eid relation to non final entity types
             rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Int',
                                           cardinality='11', uid=True)
--- a/schemaviewer.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/schemaviewer.py	Mon Oct 19 15:16:41 2009 +0200
@@ -47,7 +47,7 @@
                                            klass='titleUnderline'),))
         layout.append(esection)
         eschemas = [eschema for eschema in schema.entities()
-                    if not (eschema.is_final() or eschema in skiptypes)]
+                    if not (eschema.final or eschema in skiptypes)]
         for eschema in sorted(eschemas):
             esection.append(self.visit_entityschema(eschema, skiptypes))
         if display_relations:
@@ -55,7 +55,7 @@
             rsection = Section(children=(title,))
             layout.append(rsection)
             relations = [rschema for rschema in schema.relations()
-                         if not (rschema.is_final() or rschema.type in skiptypes)]
+                         if not (rschema.final or rschema.type in skiptypes)]
             keys = [(rschema.type, rschema) for rschema in relations]
             for key, rschema in sorted(keys):
                 relstr = self.visit_relationschema(rschema)
@@ -143,7 +143,7 @@
         data.append(Section(children=rels, klass='rels'))
         data.append(Section(children=t_vars, klass='vars'))
         layout.append(Section(children=data, klass='entityAttributes'))
-        if eschema.is_final(): # stop here for final entities
+        if eschema.final: # stop here for final entities
             return layout
         _ = self.req._
         if self.req.user.matching_groups('managers'):
--- a/selectors.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/selectors.py	Mon Oct 19 15:16:41 2009 +0200
@@ -684,9 +684,9 @@
         eschema = eclass.e_schema
         try:
             if self.role == 'object':
-                rschema = eschema.object_relation(self.rtype)
+                rschema = eschema.objrels[self.rtype]
             else:
-                rschema = eschema.subject_relation(self.rtype)
+                rschema = eschema.subjrels[self.rtype]
         except KeyError:
             return 0
         if self.target_etype is not None:
@@ -901,7 +901,7 @@
     """
     def score(self, cls, req, etype):
         eschema = req.vreg.schema.eschema(etype)
-        if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
+        if not (eschema.final or eschema.is_subobject(strict=True)) \
                and eschema.has_perm(req, 'add'):
             return 1
         return 0
--- a/server/__init__.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/__init__.py	Mon Oct 19 15:16:41 2009 +0200
@@ -176,6 +176,10 @@
     # reloging using the admin user
     config._cubes = None # avoid assertion error
     repo, cnx = in_memory_cnx(config, login, password=pwd)
+    # trigger vreg initialisation of entity classes
+    config.cubicweb_appobject_path = set(('entities',))
+    config.cube_appobject_path = set(('entities',))
+    repo.vreg.set_schema(repo.schema)
     assert len(repo.sources) == 1, repo.sources
     handler = config.migration_handler(schema, interactive=False,
                                        cnx=cnx, repo=repo)
--- a/server/checkintegrity.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/checkintegrity.py	Mon Oct 19 15:16:41 2009 +0200
@@ -83,7 +83,7 @@
     repo.do_fti = True  # ensure full-text indexation is activated
     etypes = set()
     for eschema in schema.entities():
-        if eschema.is_final():
+        if eschema.final:
             continue
         indexable_attrs = tuple(eschema.indexable_attributes()) # generator
         if not indexable_attrs:
@@ -161,7 +161,7 @@
                 print >> sys.stderr
     print 'Checking entities tables'
     for eschema in schema.entities():
-        if eschema.is_final():
+        if eschema.final:
             continue
         table = SQL_PREFIX + eschema.type
         column = SQL_PREFIX +  'eid'
@@ -193,7 +193,7 @@
     """check all relations registered in the repo system table"""
     print 'Checking relations'
     for rschema in schema.relations():
-        if rschema.is_final() or rschema in PURE_VIRTUAL_RTYPES:
+        if rschema.final or rschema in PURE_VIRTUAL_RTYPES:
             continue
         if rschema.inlined:
             for subjtype in rschema.subjects():
--- a/server/hook.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/hook.py	Mon Oct 19 15:16:41 2009 +0200
@@ -245,12 +245,12 @@
         eschema = self.schema.eschema(self._cw.describe(self.eidfrom)[0])
         execute = self._cw.unsafe_execute
         for rel in self.subject_relations:
-            if eschema.has_subject_relation(rel):
+            if rel in eschema.subjrels:
                 execute('SET R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
                         'X %s R, NOT R %s P' % (self.rtype, rel, self.rtype),
                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
         for rel in self.object_relations:
-            if eschema.has_object_relation(rel):
+            if rel in eschema.objrels:
                 execute('SET R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
                         'R %s X, NOT R %s P' % (self.rtype, rel, self.rtype),
                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
@@ -269,12 +269,12 @@
         eschema = self.schema.eschema(self._cw.describe(self.eidfrom)[0])
         execute = self._cw.unsafe_execute
         for rel in self.subject_relations:
-            if eschema.has_subject_relation(rel):
+            if rel in eschema.subjrels:
                 execute('DELETE R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
                         'X %s R' % (self.rtype, rel),
                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
         for rel in self.object_relations:
-            if eschema.has_object_relation(rel):
+            if rel in eschema.objrels:
                 execute('DELETE R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
                         'R %s X' % (self.rtype, rel),
                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
@@ -328,10 +328,15 @@
         """return the index of  the lastest instance which is not a
         LateOperation instance
         """
-        for i, op in enumerate(self.session.pending_operations):
+        # faster by inspecting operation in reverse order for heavy transactions
+        i = None
+        for i, op in enumerate(reversed(self.session.pending_operations)):
             if isinstance(op, (LateOperation, SingleLastOperation)):
-                return i
-        return None
+                continue
+            return -i or None
+        if i is None:
+            return None
+        return -(i + 1)
 
     def handle_event(self, event):
         """delegate event handling to the opertaion"""
@@ -395,10 +400,15 @@
         """return the index of  the lastest instance which is not a
         SingleLastOperation instance
         """
-        for i, op in enumerate(self.session.pending_operations):
+        # faster by inspecting operation in reverse order for heavy transactions
+        i = None
+        for i, op in enumerate(reversed(self.session.pending_operations)):
             if isinstance(op, SingleLastOperation):
-                return i
-        return None
+                continue
+            return -i or None
+        if i is None:
+            return None
+        return -(i + 1)
 
 
 class SingleOperation(Operation):
@@ -418,10 +428,9 @@
 
     def equivalent_index(self, operations):
         """return the index of the equivalent operation if any"""
-        equivalents = [i for i, op in enumerate(operations)
-                       if op.__class__ is self.__class__]
-        if equivalents:
-            return equivalents[0]
+        for i, op in enumerate(reversed(operations)):
+            if op.__class__ is self.__class__:
+                return -(i+1)
         return None
 
 
--- a/server/migractions.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/migractions.py	Mon Oct 19 15:16:41 2009 +0200
@@ -54,6 +54,7 @@
     def __init__(self, config, schema, interactive=True,
                  repo=None, cnx=None, verbosity=1, connect=True):
         MigrationHelper.__init__(self, config, interactive, verbosity)
+        # no config on shell to a remote instance
         if not interactive:
             assert cnx
             assert repo
@@ -61,7 +62,8 @@
             assert repo
             self._cnx = cnx
             self.repo = repo
-            self.session.data['rebuild-infered'] = False
+            if config is not None:
+                self.session.data['rebuild-infered'] = False
         elif connect:
             self.repo_connect()
         if not schema:
@@ -93,7 +95,7 @@
                 self.backup_database(askconfirm=False)
         super(ServerMigrationHelper, self).migrate(vcconf, toupgrade, options)
 
-    def process_script(self, migrscript, funcname=None, *args, **kwargs):
+    def cmd_process_script(self, migrscript, funcname=None, *args, **kwargs):
         """execute a migration script
         in interactive mode,  display the migration script path, ask for
         confirmation and execute it if confirmed
@@ -103,7 +105,7 @@
                 if self.execscript_confirm(migrscript):
                     sqlexec(open(migrscript).read(), self.session.system_sql)
             else:
-                return super(ServerMigrationHelper, self).process_script(
+                return super(ServerMigrationHelper, self).cmd_process_script(
                     migrscript, funcname, *args, **kwargs)
             self.commit()
         except:
@@ -233,7 +235,10 @@
 
     @property
     def session(self):
-        return self.repo._get_session(self.cnx.sessionid)
+        if self.config is not None:
+            return self.repo._get_session(self.cnx.sessionid)
+        # no access to session on remote instance
+        return None
 
     def commit(self):
         if hasattr(self, '_cnx'):
@@ -255,7 +260,8 @@
                         'sql': self.sqlexec,
                         'rql': self.rqlexec,
                         'rqliter': self.rqliter,
-                        'schema': self.repo.schema,
+                        'schema': self.repo.get_schema(),
+                        'cnx': self.cnx,
                         'fsschema': self.fs_schema,
                         'session' : self.session,
                         'repo' : self.repo,
@@ -268,8 +274,7 @@
     @cached
     def group_mapping(self):
         """cached group mapping"""
-        self.session.set_pool()
-        return ss.group_mapping(self.session)
+        return ss.group_mapping(self._cw)
 
     def exec_event_script(self, event, cubepath=None, funcname=None,
                           *args, **kwargs):
@@ -289,7 +294,7 @@
             self.confirm = yes
             self.execscript_confirm = yes
             try:
-                return self.process_script(apc, funcname, *args, **kwargs)
+                return self.cmd_process_script(apc, funcname, *args, **kwargs)
             finally:
                 self.confirm = confirm
                 self.execscript_confirm = execscript_confirm
@@ -306,7 +311,7 @@
             return
         newrschema = self.fs_schema[ertype]
         teid = self.repo.schema[ertype].eid
-        if 'update' in newrschema.ACTIONS or newrschema.is_final():
+        if 'update' in newrschema.ACTIONS or newrschema.final:
             # entity type
             exprtype = u'ERQLExpression'
         else:
@@ -643,7 +648,7 @@
             # XXX (syt) plz explain: if we're adding an entity type, it should
             # not be there...
             eschema = instschema[etype]
-            if eschema.is_final():
+            if eschema.final:
                 instschema.del_entity_type(etype)
         else:
             eschema = self.fs_schema.eschema(etype)
@@ -686,7 +691,7 @@
                         if role == 'subject':
                             subjschema = spschema
                             objschema = tschema
-                            if rschema.final and instspschema.has_subject_relation(rschema):
+                            if rschema.final and rschema in instspschema.subjrels:
                                 # attribute already set, has_rdef would check if
                                 # it's of the same type, we don't want this so
                                 # simply skip here
@@ -852,7 +857,7 @@
         """unregister an existing relation definition"""
         rschema = self.repo.schema.rschema(rtype)
         # unregister the definition from CWAttribute or CWRelation
-        if rschema.is_final():
+        if rschema.final:
             etype = 'CWAttribute'
         else:
             etype = 'CWRelation'
@@ -981,7 +986,6 @@
 
          :rtype: `Workflow`
         """
-        self.session.set_pool() # ensure pool is set
         wf = self.cmd_create_entity('Workflow', name=unicode(name),
                                     **kwargs)
         if not isinstance(wfof, (list, tuple)):
@@ -1001,7 +1005,6 @@
 
     # XXX remove once cmd_add_[state|transition] are removed
     def _get_or_create_wf(self, etypes):
-        self.session.set_pool() # ensure pool is set
         if not isinstance(etypes, (list, tuple)):
             etypes = (etypes,)
         rset = self.rqlexec('Workflow X WHERE X workflow_of ET, ET name %(et)s',
@@ -1041,16 +1044,14 @@
         """set or add (if `reset` is False) groups and conditions for a
         transition
         """
-        self.session.set_pool() # ensure pool is set
-        tr = self.session.entity_from_eid(treid)
+        tr = self._cw.entity_from_eid(treid)
         tr.set_transition_permissions(requiredgroups, conditions, reset)
         if commit:
             self.commit()
 
     @deprecated('[3.5] use entity.fire_transition("transition") or entity.change_state("state")')
     def cmd_set_state(self, eid, statename, commit=False):
-        self.session.set_pool() # ensure pool is set
-        self.session.entity_from_eid(eid).change_state(statename)
+        self._cw.entity_from_eid(eid).change_state(statename)
         if commit:
             self.commit()
 
@@ -1074,11 +1075,18 @@
 
     # other data migration commands ###########################################
 
-    def cmd_create_entity(self, etype, *args, **kwargs):
+    @property
+    def _cw(self):
+        session = self.session
+        if session is not None:
+            session.set_pool()
+            return session
+        return self.cnx.request()
+
+    def cmd_create_entity(self, etype, **kwargs):
         """add a new entity of the given type"""
         commit = kwargs.pop('commit', False)
-        self.session.set_pool()
-        entity = self.session.create_entity(etype, *args, **kwargs)
+        entity = self._cw.create_entity(etype, **kwargs)
         if commit:
             self.commit()
         return entity
@@ -1114,7 +1122,6 @@
         if not isinstance(rql, (tuple, list)):
             rql = ( (rql, kwargs), )
         res = None
-        self.session.set_pool()
         for rql, kwargs in rql:
             if kwargs:
                 msg = '%s (%s)' % (rql, kwargs)
@@ -1122,7 +1129,7 @@
                 msg = rql
             if not ask_confirm or self.confirm('execute rql: %s ?' % msg):
                 try:
-                    res = self.session.execute(rql, kwargs, cachekey)
+                    res = self._cw.execute(rql, kwargs, cachekey)
                 except Exception, ex:
                     if self.confirm('error: %s\nabort?' % ex):
                         raise
@@ -1211,9 +1218,8 @@
         if self.ask_confirm:
             if not self._h.confirm('execute rql: %s ?' % msg):
                 raise StopIteration
-        self._h.session.set_pool()
         try:
-            rset = self._h.session.execute(rql, kwargs)
+            rset = self._h._cw.execute(rql, kwargs)
         except Exception, ex:
             if self._h.confirm('error: %s\nabort?' % ex):
                 raise
--- a/server/msplanner.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/msplanner.py	Mon Oct 19 15:16:41 2009 +0200
@@ -334,7 +334,7 @@
             for i, sol in enumerate(self._solutions):
                 vartype = sol[varname]
                 # skip final variable
-                if eschema(vartype).is_final():
+                if eschema(vartype).final:
                     break
                 for source in repo.sources:
                     if source.support_entity(vartype):
@@ -381,7 +381,7 @@
             # process non final relations only
             # note: don't try to get schema for 'is' relation (not available
             # during bootstrap)
-            if not rel.is_types_restriction() and not rschema(rel.r_type).is_final():
+            if not rel.is_types_restriction() and not rschema(rel.r_type).final:
                 # nothing to do if relation is not supported by multiple sources
                 # or if some source has it listed in its cross_relations
                 # attribute
@@ -457,7 +457,7 @@
             repo = self._repo
             rschema = self._schema.rschema
             for rel in self.plan.rqlst.main_relations:
-                if not rschema(rel.r_type).is_final():
+                if not rschema(rel.r_type).final:
                     # nothing to do if relation is not supported by multiple sources
                     if len(repo.rel_type_sources(rel.r_type)) < 2:
                         continue
@@ -730,7 +730,7 @@
                             # its lhs/rhs variable isn't in "terms", and the
                             # other end *is* in "terms", mark it have to be
                             # selected
-                            if source.uri != 'system' and not rschema(rel.r_type).is_final():
+                            if source.uri != 'system' and not rschema(rel.r_type).final:
                                 lhs, rhs = rel.get_variable_parts()
                                 try:
                                     lhsvar = lhs.variable
@@ -1003,7 +1003,7 @@
         for varname, mapping in step.outputmap.iteritems():
             if varname in inputmap and \
                    not (mapping == inputmap[varname] or
-                        self._schema.eschema(solutions[0][varname]).is_final()):
+                        self._schema.eschema(solutions[0][varname]).final):
                 self._conflicts.append((varname, inputmap[varname]))
         inputmap.update(step.outputmap)
         self.plan.add_step(step)
@@ -1359,7 +1359,7 @@
                 return False
         if not self.final and not relation in self.terms:
             rschema = self.schema.rschema(relation.r_type)
-            if not rschema.is_final():
+            if not rschema.final:
                 for term in relation.get_nodes((VariableRef, Constant)):
                     term = getattr(term, 'variable', term)
                     termsources = sorted(set(x[0] for x in self.ppi._term_sources(term)))
@@ -1370,7 +1370,7 @@
     def visit_relation(self, node, newroot, terms):
         if not node.is_types_restriction():
             if node in self.skip and self.solindices.issubset(self.skip[node]):
-                if not self.schema.rschema(node.r_type).is_final():
+                if not self.schema.rschema(node.r_type).final:
                     # can't really skip the relation if one variable is selected and only
                     # referenced by this relation
                     for vref in node.iget_nodes(VariableRef):
@@ -1403,7 +1403,7 @@
                 vref.unregister_reference()
             raise
         ored = node.ored()
-        if rschema.is_final() or rschema.inlined:
+        if rschema.final or rschema.inlined:
             vrefs = node.children[1].get_nodes(VariableRef)
             if not vrefs:
                 if not ored:
--- a/server/querier.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/querier.py	Mon Oct 19 15:16:41 2009 +0200
@@ -389,7 +389,7 @@
                     # add constant values to entity def
                     value = rhs.eval(self.args)
                     eschema = edef.e_schema
-                    attrtype = eschema.subject_relation(rtype).objects(eschema)[0]
+                    attrtype = eschema.subjrels[rtype].objects(eschema)[0]
                     if attrtype == 'Password' and isinstance(value, unicode):
                         value = value.encode('UTF8')
                     edef[rtype] = value
--- a/server/repository.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/repository.py	Mon Oct 19 15:16:41 2009 +0200
@@ -90,6 +90,7 @@
     def commit_event(self):
         pass
 
+
 def del_existing_rel_if_needed(session, eidfrom, rtype, eidto):
     """delete existing relation when adding a new one if card is 1 or ?
 
@@ -105,6 +106,7 @@
         return
     ensure_card_respected(session.unsafe_execute, session, eidfrom, rtype, eidto)
 
+
 def ensure_card_respected(execute, session, eidfrom, rtype, eidto):
     card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
     # one may be tented to check for neweids but this may cause more than one
@@ -122,6 +124,7 @@
         execute('DELETE X %s Y WHERE NOT X eid %%(x)s, Y eid %%(y)s' % rtype,
                 {'x': eidfrom, 'y': eidto}, 'y')
 
+
 class Repository(object):
     """a repository provides access to a set of persistent storages for
     entities and relations
@@ -984,8 +987,8 @@
         # XXX use entity.keys here since edited_attributes is not updated for
         # inline relations
         for attr in entity.keys():
-            rschema = eschema.subject_relation(attr)
-            if not rschema.is_final(): # inlined relation
+            rschema = eschema.subjrels[attr]
+            if not rschema.final: # inlined relation
                 relations.append((attr, entity[attr]))
         entity.set_defaults()
         entity.check(creation=True)
@@ -1003,7 +1006,7 @@
             rtype = str(rschema)
             if rtype in schema.VIRTUAL_RTYPES:
                 continue
-            if rschema.is_final():
+            if rschema.final:
                 entity.setdefault(rtype, None)
             else:
                 entity.set_related_cache(rtype, 'subject', session.empty_rset())
@@ -1043,8 +1046,8 @@
         for attr in edited_attributes:
             if attr == 'eid':
                 continue
-            rschema = eschema.subject_relation(attr)
-            if rschema.is_final():
+            rschema = eschema.subjrels[attr]
+            if rschema.final:
                 if eschema.rproperty(attr, 'fulltextindexed'):
                     need_fti_update = True
                 only_inline_rels = False
--- a/server/rqlannotation.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/rqlannotation.py	Mon Oct 19 15:16:41 2009 +0200
@@ -201,7 +201,7 @@
         for rel in select.iget_nodes(Relation):
             if rel.neged(strict=True) and not rel.is_types_restriction():
                 rschema = getrschema(rel.r_type)
-                if not rschema.is_final():
+                if not rschema.final:
                     # if one of the relation's variable is ambiguous but not
                     # invariant, an intersection will be necessary
                     for vref in rel.get_nodes(VariableRef):
@@ -221,7 +221,7 @@
     def __init__(self, schema):
         self.schema = schema
         self.nfdomain = frozenset(eschema.type for eschema in schema.entities()
-                                  if not eschema.is_final())
+                                  if not eschema.final)
 
     def annotate(self, rqlst):
         """add information to the rql syntax tree to help sources to do their
@@ -234,7 +234,7 @@
           syntax tree or because a solution for this variable has been removed
           due to security filtering)
         """
-        assert rqlst.TYPE == 'select', rqlst
+        #assert rqlst.TYPE == 'select', rqlst
         rqlst.has_text_query = self._annotate_union(rqlst)
 
     def _annotate_union(self, union):
@@ -288,7 +288,7 @@
         # set domains for each variable
         for varname, var in rqlst.defined_vars.iteritems():
             if var.stinfo['uidrels'] or \
-                   self.eschema(rqlst.solutions[0][varname]).is_final():
+                   self.eschema(rqlst.solutions[0][varname]).final:
                 ptypes = var.stinfo['possibletypes']
             else:
                 ptypes = set(self.nfdomain)
@@ -304,7 +304,7 @@
             lhs, rhs = rel.get_variable_parts()
             if isinstance(lhs, VariableRef) or isinstance(rhs, VariableRef):
                 rschema = self.rschema(rel.r_type)
-                if rschema.inlined or rschema.is_final():
+                if rschema.inlined or rschema.final:
                     self.not_invariants.add(lhs.variable)
                 self.set_rel_constraint(lhs, rel, rschema.subjects)
                 self.set_rel_constraint(rhs, rel, rschema.objects)
--- a/server/schemaserial.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/schemaserial.py	Mon Oct 19 15:16:41 2009 +0200
@@ -64,7 +64,7 @@
     entity type tables and columns
     """
     for etype in schema.entities():
-        if etype.is_final():
+        if etype.final:
             continue
         try:
             sql = 'ALTER TABLE %s RENAME TO cw_%s' % (
@@ -76,7 +76,7 @@
         for rschema in etype.subject_relations():
             if rschema == 'has_text':
                 continue
-            if rschema.is_final() or rschema.inlined:
+            if rschema.final or rschema.inlined:
                 sql = 'ALTER TABLE cw_%s RENAME %s TO cw_%s' % (
                     etype, rschema, rschema)
                 print sql
@@ -326,7 +326,7 @@
         raise Exception("can't decode %s [was %s]" % (erschema.description, e))
     return {
         'name': type_,
-        'final': erschema.is_final(),
+        'final': erschema.final,
         'description': desc,
         }
 
@@ -340,7 +340,7 @@
 
 def rschema_relations_values(rschema):
     values = _ervalues(rschema)
-    values['final'] = rschema.is_final()
+    values['final'] = rschema.final
     values['symetric'] = rschema.symetric
     values['inlined'] = rschema.inlined
     if HAS_FULLTEXT_CONTAINER:
@@ -402,7 +402,7 @@
         # don't serialize infered relations
         if _props.get('infered'):
             continue
-        gen = genmap[rschema.is_final()]
+        gen = genmap[rschema.final]
         for rql, values in gen(rschema, subjtype, objtype, _props):
             yield rql, values
 
--- a/server/sources/__init__.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/sources/__init__.py	Mon Oct 19 15:16:41 2009 +0200
@@ -168,7 +168,7 @@
             wsupport = self.support_relations[rtype]
         except KeyError:
             rschema = self.schema.rschema(rtype)
-            if not rschema.is_final() or rschema == 'has_text':
+            if not rschema.final or rschema == 'has_text':
                 return False
             for etype in rschema.subjects():
                 try:
@@ -225,7 +225,7 @@
         # delete relations referencing one of those eids
         eidcolum = SQL_PREFIX + 'eid'
         for rschema in self.schema.relations():
-            if rschema.is_final() or rschema.type in VIRTUAL_RTYPES:
+            if rschema.final or rschema.type in VIRTUAL_RTYPES:
                 continue
             if rschema.inlined:
                 column = SQL_PREFIX + rschema.type
--- a/server/sources/ldapuser.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/sources/ldapuser.py	Mon Oct 19 15:16:41 2009 +0200
@@ -632,7 +632,7 @@
             return ''
         lhs, rhs = relation.get_parts()
         # attribute relation
-        if self.source.schema.rschema(rtype).is_final():
+        if self.source.schema.rschema(rtype).final:
             # dunno what to do here, don't pretend anything else
             if lhs.name != self._mainvarname:
                 if lhs.name in self.mainvars:
--- a/server/sources/native.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/sources/native.py	Mon Oct 19 15:16:41 2009 +0200
@@ -71,7 +71,7 @@
         try:
             sql.append('%s %s' % (name, typemap[ttype]))
         except KeyError:
-            # assert not schema(ttype).is_final()
+            # assert not schema(ttype).final
             sql.append('%s %s' % (name, typemap['Int']))
     return ','.join(sql), varmap
 
--- a/server/sources/pyrorql.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/sources/pyrorql.py	Mon Oct 19 15:16:41 2009 +0200
@@ -303,7 +303,7 @@
             needtranslation = []
             rows = rset.rows
             for i, etype in enumerate(descr[0]):
-                if (etype is None or not self.schema.eschema(etype).is_final()
+                if (etype is None or not self.schema.eschema(etype).final
                     or uidtype(union, i, etype, args)):
                     needtranslation.append(i)
             if needtranslation:
@@ -487,7 +487,7 @@
             raise
         if node.optional in ('left', 'both'):
             lhs += '?'
-        if node.r_type == 'eid' or not self.source.schema.rschema(node.r_type).is_final():
+        if node.r_type == 'eid' or not self.source.schema.rschema(node.r_type).final:
             self.need_translation = True
             self.current_operator = node.operator()
             if isinstance(node.children[0], Constant):
--- a/server/sources/rql2sql.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/sources/rql2sql.py	Mon Oct 19 15:16:41 2009 +0200
@@ -60,7 +60,7 @@
                 newivar = _new_var(newselect, vref.name)
                 newselect.selection.append(VariableRef(newivar))
                 _fill_to_wrap_rel(vref.variable, newselect, towrap, schema)
-        elif rschema.is_final():
+        elif rschema.final:
             towrap.add( (var, rel) )
 
 def rewrite_unstable_outer_join(select, solutions, unstable, schema):
@@ -616,7 +616,7 @@
             return ''
         lhs, rhs = relation.get_parts()
         rschema = self.schema.rschema(rtype)
-        if rschema.is_final():
+        if rschema.final:
             if rtype == 'eid' and lhs.variable._q_invariant and \
                    lhs.variable.stinfo['constnode']:
                 # special case where this restriction is already generated by
@@ -1096,7 +1096,7 @@
         except KeyError:
             etype = self._state.solution[var.name]
             # XXX this check should be moved in rql.stcheck
-            if self.schema.eschema(etype).is_final():
+            if self.schema.eschema(etype).final:
                 raise BadRQLQuery(var.stmt.root)
             table = var.name
             sql = '%s.%seid' % (table, SQL_PREFIX)
@@ -1211,7 +1211,7 @@
         """return the table alias used by the given relation"""
         if relation in self._state.done:
             return relation._q_sqltable
-        assert not self.schema.rschema(relation.r_type).is_final(), relation.r_type
+        assert not self.schema.rschema(relation.r_type).final, relation.r_type
         rid = 'rel_%s%s' % (relation.r_type, self._state.count)
         # relation's table is belonging to the root scope if it is the principal
         # table of one of it's variable and if that variable belong's to parent
--- a/server/sqlutils.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/sqlutils.py	Mon Oct 19 15:16:41 2009 +0200
@@ -229,8 +229,8 @@
         attrs = {}
         eschema = entity.e_schema
         for attr, value in entity.items():
-            rschema = eschema.subject_relation(attr)
-            if rschema.is_final():
+            rschema = eschema.subjrels[attr]
+            if rschema.final:
                 atype = str(entity.e_schema.destination(attr))
                 if atype == 'Boolean':
                     value = self.dbhelper.boolean_value(value)
--- a/server/ssplanner.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/ssplanner.py	Mon Oct 19 15:16:41 2009 +0200
@@ -75,8 +75,8 @@
                 if reverse:
                     rdefs[i] = rtype, RelationsStep.REVERSE_RELATION
                 else:
-                    rschema = eschema.subject_relation(rtype)
-                    if rschema.is_final() or rschema.inlined:
+                    rschema = eschema.subjrels[rtype]
+                    if rschema.final or rschema.inlined:
                         rdefs[i] = rtype, RelationsStep.FINAL
                     else:
                         rdefs[i] = rtype, RelationsStep.RELATION
@@ -147,7 +147,7 @@
                 selected_index[rhs.as_string('utf-8')] = index
                 index += 1
             rschema = getrschema(relation.r_type)
-            if rschema.is_final() or rschema.inlined:
+            if rschema.final or rschema.inlined:
                 attrrelations.append(relation)
             else:
                 relations.append(relation)
--- a/server/test/unittest_migractions.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/test/unittest_migractions.py	Mon Oct 19 15:16:41 2009 +0200
@@ -154,7 +154,7 @@
         self.assertEquals([str(rs) for rs in self.schema['Folder2'].object_relations()],
                           ['filed_under2', 'identity'])
         self.assertEquals(sorted(str(e) for e in self.schema['filed_under2'].subjects()),
-                          sorted(str(e) for e in self.schema.entities() if not e.is_final()))
+                          sorted(str(e) for e in self.schema.entities() if not e.final))
         self.assertEquals(self.schema['filed_under2'].objects(), ('Folder2',))
         eschema = self.schema.eschema('Folder2')
         for cstr in eschema.constraints('name'):
@@ -183,7 +183,7 @@
         self.schema.rebuild_infered_relations()
         self.failUnless('filed_under2' in self.schema)
         self.assertEquals(sorted(str(e) for e in self.schema['filed_under2'].subjects()),
-                          sorted(str(e) for e in self.schema.entities() if not e.is_final()))
+                          sorted(str(e) for e in self.schema.entities() if not e.final))
         self.assertEquals(self.schema['filed_under2'].objects(), ('Folder2',))
         self.mh.cmd_drop_relation_type('filed_under2')
         self.failIf('filed_under2' in self.schema)
--- a/server/test/unittest_msplanner.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/test/unittest_msplanner.py	Mon Oct 19 15:16:41 2009 +0200
@@ -226,7 +226,7 @@
                     self.ldap: {'Y': s[0], 'X': s[0]}}, True)
 
     def test_complex_aggregat(self):
-        solindexes = set(range(len([e for e in self.schema.entities() if not e.is_final()])))
+        solindexes = set(range(len([e for e in self.schema.entities() if not e.final])))
         self._test('Any MAX(X)',
                    {self.system: {'X': solindexes}}, False)
 
--- a/server/test/unittest_rql2sql.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/test/unittest_rql2sql.py	Mon Oct 19 15:16:41 2009 +0200
@@ -1082,7 +1082,7 @@
         self.assertEquals(var_sols('FROM_ENTITYOBJECT'), set(('CWAttribute', 'CWRelation')))
         self.assertEquals(var_sols('FROM_ENTITYOBJECT'), delete.defined_vars['FROM_ENTITYOBJECT'].stinfo['possibletypes'])
         self.assertEquals(var_sols('ISOBJECT'),
-                          set(x.type for x in self.schema.entities() if not x.is_final()))
+                          set(x.type for x in self.schema.entities() if not x.final))
         self.assertEquals(var_sols('ISOBJECT'), delete.defined_vars['ISOBJECT'].stinfo['possibletypes'])
 
 
--- a/server/utils.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/server/utils.py	Mon Oct 19 15:16:41 2009 +0200
@@ -71,7 +71,8 @@
 def manager_userpasswd(user=None, msg=DEFAULT_MSG, confirm=False,
                        passwdmsg='password'):
     if not user:
-        print msg
+        if msg:
+            print msg
         while not user:
             user = raw_input('login: ')
         user = unicode(user, sys.stdin.encoding)
--- a/sobjects/notification.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/sobjects/notification.py	Mon Oct 19 15:16:41 2009 +0200
@@ -152,7 +152,7 @@
         for attr, oldvalue, newvalue in sorted(changes):
             # check current user has permission to see the attribute
             rschema = self.vreg.schema[attr]
-            if rschema.is_final():
+            if rschema.final:
                 if not rschema.has_perm(self.req, 'read', eid=self.rset[0][0]):
                     continue
             # XXX suppose it's a subject relation...
--- a/test/unittest_entity.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/test/unittest_entity.py	Mon Oct 19 15:16:41 2009 +0200
@@ -120,10 +120,10 @@
         Note = self.vreg['etypes'].etype_class('Note')
         peschema = Personne.e_schema
         seschema = Societe.e_schema
-        peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '1*')
-        peschema.subject_relation('connait').set_rproperty(peschema, peschema, 'cardinality', '11')
-        peschema.subject_relation('evaluee').set_rproperty(peschema, Note.e_schema, 'cardinality', '1*')
-        seschema.subject_relation('evaluee').set_rproperty(seschema, Note.e_schema, 'cardinality', '1*')
+        peschema.subjrels['travaille'].set_rproperty(peschema, seschema, 'cardinality', '1*')
+        peschema.subjrels['connait'].set_rproperty(peschema, peschema, 'cardinality', '11')
+        peschema.subjrels['evaluee'].set_rproperty(peschema, Note.e_schema, 'cardinality', '1*')
+        seschema.subjrels['evaluee'].set_rproperty(seschema, Note.e_schema, 'cardinality', '1*')
         # testing basic fetch_attrs attribute
         self.assertEquals(Personne.fetch_rql(user),
                           'Any X,AA,AB,AC ORDERBY AA ASC '
@@ -158,13 +158,13 @@
             self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA ASC '
                               'WHERE X is Personne, X nom AA, X connait AB?')
             # testing optional relation
-            peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '?*')
+            peschema.subjrels['travaille'].set_rproperty(peschema, seschema, 'cardinality', '?*')
             Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
             Societe.fetch_attrs = ('nom',)
             self.assertEquals(Personne.fetch_rql(user),
                               'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
             # testing relation with cardinality > 1
-            peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '**')
+            peschema.subjrels['travaille'].set_rproperty(peschema, seschema, 'cardinality', '**')
             self.assertEquals(Personne.fetch_rql(user),
                               'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB')
             # XXX test unauthorized attribute
--- a/test/unittest_schema.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/test/unittest_schema.py	Mon Oct 19 15:16:41 2009 +0200
@@ -88,7 +88,7 @@
         self.assertEqual(esociete.type, 'Societe')
         self.assertEqual(schema.has_relation('TEST'), 0)
         self.assertEqual(schema.has_relation('test'), 1)
-        self.assertEqual(eperson.subject_relation('test').type, 'test')
+        self.assertEqual(eperson.subjrels['test'].type, 'test')
         self.assertEqual(schema.has_relation('Concerne'), 0)
         self.assertEqual(schema.has_relation('concerne'), 1)
         self.assertEqual(schema.rschema('concerne').type, 'concerne')
--- a/view.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/view.py	Mon Oct 19 15:16:41 2009 +0200
@@ -213,7 +213,7 @@
         coltypes = rset.column_types(0)
         if len(coltypes) == 1:
             etype = iter(coltypes).next()
-            if not self._cw.schema.eschema(etype).is_final():
+            if not self._cw.schema.eschema(etype).final:
                 if len(rset) == 1:
                     entity = rset.get_entity(0, 0)
                     return entity.absolute_url(vid=self.__regid__)
--- a/web/facet.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/facet.py	Mon Oct 19 15:16:41 2009 +0200
@@ -92,7 +92,7 @@
             # constant restriction
             # XXX: X title LOWER(T) if it makes sense?
             return None
-        if rschema.is_final():
+        if rschema.final:
             if len(ovar.stinfo['relations']) == 1:
                 # attribute selection
                 return ovar
--- a/web/formfields.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/formfields.py	Mon Oct 19 15:16:41 2009 +0200
@@ -586,7 +586,7 @@
     if role == 'subject':
         targetschema = rschema.objects(eschema)[0]
         help = rschema.rproperty(eschema, targetschema, 'description')
-        if rschema.is_final():
+        if rschema.final:
             if rschema.rproperty(eschema, targetschema, 'internationalizable'):
                 kwargs.setdefault('internationalizable', True)
             def get_default(form, es=eschema, rs=rschema):
@@ -603,7 +603,7 @@
         kwargs.setdefault('label', (eschema.type, rschema.type))
     kwargs['eidparam'] = True
     kwargs.setdefault('help', help)
-    if rschema.is_final():
+    if rschema.final:
         if skip_meta_attr and rschema in eschema.meta_attributes():
             return None
         fieldclass = FIELDS[targetschema]
--- a/web/formwidgets.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/formwidgets.py	Mon Oct 19 15:16:41 2009 +0200
@@ -448,7 +448,7 @@
         # XXX entity form specific
         entity = form.edited_entity
         attrs['cubicweb:etype_to'] = entity.e_schema
-        etype_from = entity.e_schema.subject_relation(field.name).objects(entity.e_schema)[0]
+        etype_from = entity.e_schema.subjrels[field.name].objects(entity.e_schema)[0]
         attrs['cubicweb:etype_from'] = etype_from
         return name, values, attrs
 
--- a/web/uicfg.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/uicfg.py	Mon Oct 19 15:16:41 2009 +0200
@@ -125,7 +125,7 @@
     if rtag.get(sschema, rschema, oschema, role) is None:
         card = card_from_role(rschema.rproperty(sschema, oschema, 'cardinality'), role)
         composed = rschema.rproperty(sschema, oschema, 'composite') == neg_role(role)
-        if rschema.is_final():
+        if rschema.final:
             if rschema.meta or sschema.is_metadata(rschema) \
                     or oschema.type in ('Password', 'Bytes'):
                 section = 'hidden'
--- a/web/views/__init__.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/__init__.py	Mon Oct 19 15:16:41 2009 +0200
@@ -39,7 +39,7 @@
         if not isinstance(selected[i+1], nodes.VariableRef):
             return True
         # if this is not a final entity
-        if not schema.eschema(etype).is_final():
+        if not schema.eschema(etype).final:
             return True
         # if this is a final entity not linked to the main variable
         var = selected[i+1].variable
@@ -74,7 +74,7 @@
     if nb_rows == 0 :
         return 'noresult'
     # entity result set
-    if not schema.eschema(rset.description[0][0]).is_final():
+    if not schema.eschema(rset.description[0][0]).final:
         if need_table_view(rset, schema):
             return 'table'
         if nb_rows == 1:
--- a/web/views/actions.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/actions.py	Mon Oct 19 15:16:41 2009 +0200
@@ -40,7 +40,7 @@
                  'optional argument', DeprecationWarning)
             editableattrs = form.editable_attributes()
         for rschema, targetschemas, role in editableattrs:
-            if not rschema.is_final():
+            if not rschema.final:
                 return 1
         return 0
 
@@ -66,7 +66,7 @@
         if len(select.defined_vars) == 1 and len(select.solutions) == 1:
             rset._searched_etype = select.solutions[0].itervalues().next()
             eschema = req.schema.eschema(rset._searched_etype)
-            if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
+            if not (eschema.final or eschema.is_subobject(strict=True)) \
                    and eschema.has_perm(req, 'add'):
                 return 1
     return 0
@@ -270,7 +270,7 @@
         for role, rschemas in (('subject', eschema.subject_relations()),
                                ('object', eschema.object_relations())):
             for rschema in rschemas:
-                if rschema.is_final():
+                if rschema.final:
                     continue
                 # check the relation can be added as well
                 # XXX consider autoform_permissions_overrides?
--- a/web/views/baseviews.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/baseviews.py	Mon Oct 19 15:16:41 2009 +0200
@@ -115,7 +115,7 @@
     __regid__ = 'secondary'
     title = _('secondary')
 
-    def cell_call(self, row, col):
+    def cell_call(self, row, col, **kwargs):
         """the secondary view for an entity
         secondary = icon + view(oneline)
         """
@@ -128,7 +128,7 @@
     __regid__ = 'oneline'
     title = _('oneline')
 
-    def cell_call(self, row, col):
+    def cell_call(self, row, col, **kwargs):
         """the one line view for an entity: linked text view
         """
         entity = self.cw_rset.get_entity(row, col)
--- a/web/views/bookmark.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/bookmark.py	Mon Oct 19 15:16:41 2009 +0200
@@ -10,7 +10,7 @@
 from logilab.mtconverter import xml_escape
 
 from cubicweb import Unauthorized
-from cubicweb.selectors import implements
+from cubicweb.selectors import implements, one_line_rset
 from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, RawBoxItem
 from cubicweb.web import action, box, uicfg
 from cubicweb.web.views import primary
@@ -21,7 +21,7 @@
 
 class FollowAction(action.Action):
     __regid__ = 'follow'
-    __select__ = implements('Bookmark')
+    __select__ = one_line_rset() & implements('Bookmark')
 
     title = _('follow')
     category = 'mainactions'
--- a/web/views/csvexport.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/csvexport.py	Mon Oct 19 15:16:41 2009 +0200
@@ -45,7 +45,7 @@
             csvrow = []
             for colindex, val in enumerate(row):
                 etype = descr[rowindex][colindex]
-                if val is not None and not eschema(etype).is_final():
+                if val is not None and not eschema(etype).final:
                     # csvrow.append(val) # val is eid in that case
                     content = self._cw.view('textincontext', rset,
                                             row=rowindex, col=colindex)
--- a/web/views/editforms.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/editforms.py	Mon Oct 19 15:16:41 2009 +0200
@@ -171,7 +171,7 @@
         rschema = schema.rschema(rtype)
         lzone = self._build_landing_zone(landing_zone)
         # compute value, checking perms, build form
-        if rschema.is_final():
+        if rschema.final:
             onsubmit = ("return inlineValidateAttributeForm('%(rtype)s', '%(eid)s', '%(divid)s', "
                         "%(reload)s, '%(default)s');")
             form = self._build_form(
@@ -398,7 +398,7 @@
             form.form_add_hidden(eid_param('__cloned_eid', entity.eid),
                                  self.copying.eid)
         for rschema, role in form.editable_attributes():
-            if not rschema.is_final():
+            if not rschema.final:
                 # ensure relation cache is filed
                 rset = self.copying.related(rschema, role)
                 self.newentity.set_related_cache(rschema, role, rset)
--- a/web/views/facets.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/facets.py	Mon Oct 19 15:16:41 2009 +0200
@@ -100,8 +100,6 @@
             w(u'</fieldset>\n</form>\n')
         finally:
             rqlst.recover()
-            import cubicweb
-            cubicweb.info('after facets with rql: %s' % repr(rqlst))
 
     def display_bookmark_link(self, rset):
         eschema = self._cw.schema.eschema('Bookmark')
--- a/web/views/forms.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/forms.py	Mon Oct 19 15:16:41 2009 +0200
@@ -349,7 +349,7 @@
             return super(EntityFieldsForm, self).form_field_value(field, load_bytes)
         if attr == '__type':
             return entity.__regid__
-        if self._cw.schema.rschema(attr).is_final():
+        if self.schema.rschema(attr).final:
             attrtype = entity.e_schema.destination(attr)
             if attrtype == 'Password':
                 return entity.has_eid() and INTERNAL_FIELD_VALUE or ''
@@ -456,7 +456,7 @@
         if isinstance(rtype, basestring):
             rtype = self._cw.vreg.schema.rschema(rtype)
         done = None
-        assert not rtype.is_final(), rtype
+        assert not rtype.final, rtype
         if entity.has_eid():
             done = set(e.eid for e in getattr(entity, str(rtype)))
         result = []
--- a/web/views/magicsearch.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/magicsearch.py	Mon Oct 19 15:16:41 2009 +0200
@@ -279,7 +279,7 @@
         eschema = self._cw.schema.eschema(etype)
         rtype = self._get_attribute_name(word2, eschema)
         # expand shortcut if rtype is a non final relation
-        if not self._cw.schema.rschema(rtype).is_final():
+        if not self._cw.schema.rschema(rtype).final:
             return self._expand_shortcut(etype, rtype, word3)
         if '%' in word3:
             searchop = 'LIKE '
--- a/web/views/navigation.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/navigation.py	Mon Oct 19 15:16:41 2009 +0200
@@ -64,7 +64,7 @@
             def index_display(row):
                 entity = rset.get_entity(row, col)
                 return entity.printable_value(attrname, format='text/plain')
-        elif self._cw.schema.eschema(rset.description[0][col]).is_final():
+        elif self._cw.schema.eschema(rset.description[0][col]).final:
             def index_display(row):
                 return unicode(rset[row][col])
         else:
@@ -99,7 +99,7 @@
                 attrname = rel.r_type
                 if attrname == 'is':
                     continue
-                if not rschema(attrname).is_final():
+                if not rschema(attrname).final:
                     col = var.selected_index()
                     attrname = None
                 if col is None:
--- a/web/views/owl.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/owl.py	Mon Oct 19 15:16:41 2009 +0200
@@ -80,7 +80,7 @@
         """get a layout for a whole schema"""
         self.skiptypes = skiptypes
         entities = sorted(eschema for eschema in self._cw.schema.entities()
-                          if not eschema.is_final() or eschema in skiptypes)
+                          if not eschema.final or eschema in skiptypes)
         self.w(u'<!-- classes definition -->')
         for eschema in entities:
             self.visit_entityschema(eschema)
--- a/web/views/plots.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/plots.py	Mon Oct 19 15:16:41 2009 +0200
@@ -112,7 +112,8 @@
         for idx, (label, plot) in enumerate(zip(self.labels, self.plots)):
             plotid = '%s_%s' % (figid, idx)
             plotdefs.append('var %s = %s;' % (plotid, self.dump_plot(plot)))
-            plotdata.append("{label: '%s', data: %s}" % (label, plotid))
+            # XXX ugly but required in order to not crash my demo
+            plotdata.append("{label: '%s', data: %s}" % (label.replace(u'&', u''), plotid))
         req.html_headers.add_onload(self.onload %
                                     {'plotdefs': '\n'.join(plotdefs),
                                      'figid': figid,
@@ -177,7 +178,7 @@
 
         def _guess_vid(self, row):
             etype = self.cw_rset.description[row][0]
-            if self._cw.schema.eschema(etype).is_final():
+            if self._cw.schema.eschema(etype).final:
                 return 'final'
             return 'textincontext'
 
--- a/web/views/primary.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/primary.py	Mon Oct 19 15:16:41 2009 +0200
@@ -105,7 +105,7 @@
     def render_entity_attributes(self, entity, siderelations=None):
         for rschema, tschemas, role, dispctrl in self._section_def(entity, 'attributes'):
             vid = dispctrl.get('vid', 'reledit')
-            if rschema.is_final() or vid == 'reledit':
+            if rschema.final or vid == 'reledit':
                 value = entity.view(vid, rtype=rschema.type, role=role)
             else:
                 rset = self._relation_rset(entity, rschema, role, dispctrl)
@@ -176,11 +176,8 @@
 
     def _relation_rset(self, entity, rschema, role, dispctrl):
         try:
-            if dispctrl.get('limit'):
-                rset = entity.related(rschema.type, role,
-                                      limit=self.maxrelated+1)
-            else:
-                rset = entity.related(rschema.type, role)
+            dispctrl.setdefault('limit', self.maxrelated)
+            rset = entity.related(rschema.type, role, limit=dispctrl['limit']+1)
         except Unauthorized:
             return
         if 'filter' in dispctrl:
@@ -191,11 +188,11 @@
         self.w(u'<div class="section">')
         if showlabel:
             self.w(u'<h4>%s</h4>' % self._cw._(dispctrl['label']))
-        self.wview(dispctrl.get('vid', defaultvid), rset)
+                   initargs={'dispctrl': dispctrl})
         self.w(u'</div>')
 
     def _render_attribute(self, rschema, value, role='subject'):
-        if rschema.is_final():
+        if rschema.final:
             show_label = self.show_attr_label
         else:
             show_label = self.show_rel_label
@@ -205,12 +202,16 @@
 
 class RelatedView(EntityView):
     __regid__ = 'autolimited'
-    def call(self, title=None, **kwargs):
-        # if not too many entities, show them all in a list
-        maxrelated = self._cw.property_value('navigation.related-limit')
-        if title:
-            self.w(u'<div class="title"><span>%s</span></div>' % title)
-        if self.cw_rset.rowcount <= maxrelated:
+
+    def call(self, **kwargs):
+        # nb: rset retreived using entity.related with limit + 1 if any
+        # because of that, we known that rset.printable_rql() will return
+        # rql with no limit set anyway (since it's handled manually)
+        if 'dispctrl' in self.extra_kwargs:
+            limit = self.extra_kwargs['dispctrl'].get('limit')
+        else:
+            limit = None
+        if limit is None or self.cw_rset.rowcount <= limit:
             if self.cw_rset.rowcount == 1:
                 self.wview('incontext', self.cw_rset, row=0)
             elif 1 < self.cw_rset.rowcount <= 5:
@@ -222,7 +223,7 @@
         # else show links to display related entities
         else:
             rql = self.cw_rset.printable_rql()
-            self.cw_rset.limit(maxrelated)
+            self.cw_rset.limit(maxrelated) # remove extra entity
             self.w(u'<div>')
             self.wview('simplelist', self.cw_rset)
             self.w(u'[<a href="%s">%s</a>]' % (self._cw.build_url(rql=rql),
--- a/web/views/schema.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/schema.py	Mon Oct 19 15:16:41 2009 +0200
@@ -88,11 +88,11 @@
         schema = self._cw.schema
         # compute entities
         entities = sorted(eschema for eschema in schema.entities()
-                          if not (eschema.is_final() or eschema in skiptypes))
+                          if not (eschema.final or eschema in skiptypes))
         # compute relations
         if display_relations:
             relations = sorted(rschema for rschema in schema.relations()
-                               if not (rschema.is_final()
+                               if not (rschema.final
                                        or rschema in skiptypes
                                        or rschema in META_RTYPES))
         else:
@@ -337,7 +337,7 @@
         viewer = SchemaViewer(self._cw)
         layout = viewer.visit_relationschema(rschema)
         self.w(uilib.ureport_as_html(layout))
-        if not rschema.is_final():
+        if not rschema.final:
             msg = self._cw._('graphical schema for %s') % entity.name
             self.w(tags.img(src=entity.absolute_url(vid='schemagraph'),
                             alt=msg))
--- a/web/views/sparql.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/sparql.py	Mon Oct 19 15:16:41 2009 +0200
@@ -102,7 +102,7 @@
 
     def cell_binding(self, row, col, varname):
         celltype = self.cw_rset.description[row][col]
-        if self._cw.schema.eschema(celltype).is_final():
+        if self._cw.schema.eschema(celltype).final:
             cellcontent = self.view('cell', self.cw_rset, row=row, col=col)
             return E.binding(E.literal(cellcontent,
                                        datatype=xmlschema(celltype)),
--- a/web/views/startup.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/startup.py	Mon Oct 19 15:16:41 2009 +0200
@@ -130,7 +130,7 @@
         """
         req = self._cw
         for eschema in eschemas:
-            if eschema.is_final() or (not eschema.has_perm(req, 'read') and
+            if eschema.final or (not eschema.has_perm(req, 'read') and
                                       not eschema.has_local_role('read')):
                 continue
             etype = eschema.type
--- a/web/views/tableview.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/tableview.py	Mon Oct 19 15:16:41 2009 +0200
@@ -80,7 +80,7 @@
         eschema = self._cw.vreg.schema.eschema
         for i, etype in enumerate(self.cw_rset.description[0]):
             try:
-                if not eschema(etype).is_final():
+                if not eschema(etype).final:
                     return i
             except KeyError: # XXX possible?
                 continue
@@ -207,7 +207,7 @@
             # join, use the default non final subvid)
             if cellvids and colindex in cellvids:
                 column.append_renderer(cellvids[colindex], colindex)
-            elif coltype is not None and self._cw.schema.eschema(coltype).is_final():
+            elif coltype is not None and self._cw.schema.eschema(coltype).final:
                 column.append_renderer(self.finalview, colindex)
             else:
                 column.append_renderer(subvid or 'incontext', colindex)
@@ -236,7 +236,7 @@
         if val is None:
             return u''
         etype = self.cw_rset.description[row][col]
-        if self._cw.schema.eschema(etype).is_final():
+        if self._cw.schema.eschema(etype).final:
             entity, rtype = self.cw_rset.related_entity(row, col)
             if entity is None:
                 return val # remove_html_tags() ?
@@ -261,7 +261,7 @@
         :param cellvid: cell view (defaults to 'outofcontext')
         """
         etype, val = self.cw_rset.description[row][col], self.cw_rset[row][col]
-        if val is not None and not self._cw.schema.eschema(etype).is_final():
+        if val is not None and not self._cw.schema.eschema(etype).final:
             e = self.cw_rset.get_entity(row, col)
             e.view(cellvid or 'outofcontext', w=self.w)
         elif val is None:
--- a/web/views/urlpublishing.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/urlpublishing.py	Mon Oct 19 15:16:41 2009 +0200
@@ -158,7 +158,7 @@
             if len(parts) == 2:
                 attrname = parts.pop(0).lower()
                 try:
-                    cls.e_schema.subject_relation(attrname)
+                    cls.e_schema.subjrels[attrname]
                 except KeyError:
                     raise PathDontMatch()
             else:
--- a/web/views/xmlrss.py	Thu Oct 15 11:20:26 2009 +0200
+++ b/web/views/xmlrss.py	Mon Oct 19 15:16:41 2009 +0200
@@ -89,7 +89,7 @@
                 if '(' in tag:
                     attrs['expr'] = tag
                     tag = 'funccall'
-                if val is not None and not eschema(etype).is_final():
+                if val is not None and not eschema(etype).final:
                     attrs['eid'] = val
                     # csvrow.append(val) # val is eid in that case
                     val = self._cw.view('textincontext', rset,