merge stable
authorAlexandre Fayolle <alexandre.fayolle@logilab.fr>
Fri, 16 Oct 2009 18:44:28 +0200
branchstable
changeset 3713 a090324efefe
parent 3712 4b8d7838d74d (current diff)
parent 3710 5bfdb591050a (diff)
child 3714 3cb7dcd9ae77
merge
--- a/__init__.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/__init__.py	Fri Oct 16 18:44:28 2009 +0200
@@ -141,11 +141,12 @@
         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
--- a/common/migration.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/common/migration.py	Fri Oct 16 18:44:28 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
--- a/cwctl.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/cwctl.py	Fri Oct 16 18:44:28 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.process_script(arg)
+            else:
+                mih.interactive_shell()
+        finally:
+            if not self.config.pyro:
+                mih.shutdown()
+            else:
+                cnx.close()
 
 
 class RecompileInstanceCatalogsCommand(InstanceCommand):
--- a/cwvreg.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/cwvreg.py	Fri Oct 16 18:44:28 2009 +0200
@@ -65,16 +65,28 @@
             for appobject in appobjects:
                 appobject.vreg_initialization_completed()
 
-    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)
 
     def select_vobject(self, oid, *args, **kwargs):
@@ -374,6 +386,8 @@
             implemented_interfaces = set()
             if 'Any' in self.get('etypes', ()):
                 for etype in self.schema.entities():
+                    if etype.final:
+                        continue
                     cls = self['etypes'].etype_class(etype)
                     for iface in cls.__implements__:
                         implemented_interfaces.update(iface.__mro__)
--- a/dbapi.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/dbapi.py	Fri Oct 16 18:44:28 2009 +0200
@@ -446,7 +446,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/server/__init__.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/server/__init__.py	Fri Oct 16 18:44:28 2009 +0200
@@ -176,6 +176,10 @@
     # reloging using the admin user
     config._cubes = None # avoid assertion error
     repo, cnx = in_memory_cnx(config, login, 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/migractions.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/server/migractions.py	Fri Oct 16 18:44:28 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:
@@ -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):
@@ -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/utils.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/server/utils.py	Fri Oct 16 18:44:28 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/web/views/primary.py	Fri Oct 16 18:41:40 2009 +0200
+++ b/web/views/primary.py	Fri Oct 16 18:44:28 2009 +0200
@@ -187,7 +187,8 @@
         self.w(u'<div class="section">')
         if showlabel:
             self.w(u'<h4>%s</h4>' % self.req._(dispctrl['label']))
-        self.wview(dispctrl.get('vid', defaultvid), rset, dispctrl=dispctrl)
+        self.wview(dispctrl.get('vid', defaultvid), rset,
+                   initargs={'dispctrl': dispctrl})
         self.w(u'</div>')
 
     def _render_attribute(self, rschema, value, role='subject'):
@@ -202,12 +203,14 @@
 class RelatedView(EntityView):
     id = 'autolimited'
 
-    def call(self, dispctrl=None, **kwargs):
+    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 is not None:
-            limit = dispctrl.get('limit')
+        if 'dispctrl' in self.extra_kwargs:
+            limit = self.extra_kwargs['dispctrl'].get('limit')
+        else:
+            limit = None
         # if not too many entities, show them all in a list
         if limit is None or self.rset.rowcount <= limit:
             if self.rset.rowcount == 1: