r6882 - in online-desktop/trunk/pyddm: . ddm



Author: otaylor
Date: 2007-11-08 14:50:28 -0600 (Thu, 08 Nov 2007)
New Revision: 6882

Modified:
   online-desktop/trunk/pyddm/ddm/AbstractModel.py
   online-desktop/trunk/pyddm/ddm/DataModel.py
   online-desktop/trunk/pyddm/ddm/TwistedModel.py
   online-desktop/trunk/pyddm/test-session.py
   online-desktop/trunk/pyddm/test.py
Log:
AbstractModel.py DataModel.py TwistedModel.py: Replace 'connected'
  'initialized' 'server-connected' with a single 'ready'. (Same
  semantics as in the C API ... it's emitted at the end of 
  startup initialization and when you need to restart your data
  fetching.)

AbstractModel.py: Allow passing either a resource_id or a resource to 
  query_resource.

DataModel.py: Map boolean resources to True/False, not 0/1 integers

DataModel.py: Async error on no connection, don't raise an exception.
 (pulls in pygobject dependency for main loop.)

TwistedModel.py test.py: Allow separately specifying the web and xmpp
  servers. This is most useful for dealing with the (unhandled) XMPP
  redirect ... so test.py --server=mugshot.org --xmpp-server=bn.mugshot.org

test.py test-session.py: Adapt to changes


Modified: online-desktop/trunk/pyddm/ddm/AbstractModel.py
===================================================================
--- online-desktop/trunk/pyddm/ddm/AbstractModel.py	2007-11-08 20:39:56 UTC (rev 6881)
+++ online-desktop/trunk/pyddm/ddm/AbstractModel.py	2007-11-08 20:50:28 UTC (rev 6882)
@@ -12,64 +12,39 @@
     """
 
     def __init__(self):
-        self.__initialized_handlers = []
-        self.__connected_handlers = []
-        self.__server_connected_handlers = []
-        self.__disconnected_handlers = []
+        self.__ready_handlers = []
         self.__added_handlers = []
         self.__removed_handlers = []
         self.__resources = {}
-        self.initialized = False
-        # self_id that is not None means that the model is connected
-        self.self_id = None
-        # connected is True only when we can talk to the server/ the network is present
-        self.connected = False
-        self.__handled_model_connected = False
-        self.__handled_server_connected = False
+        self.ready = False
+        self.global_resource = None
+        self.self_resource = None
 
-    def add_initialized_handler(self, handler):
-        """Add a handler that will be called when we initialize the model and find out if we can get connected to the server"""
-        self.__initialized_handlers.append(handler)
+    def add_ready_handler(self, handler):
+        """Add a handler that will be called a) when we become ready b) on reconnection"""
+        self.__ready_handlers.append(handler)
 
-    def remove_initialized_handler(self, handler):
-        """Remove a handler added with add_initialized_handler"""
-        self.__initialized_handlers.remove(handler)
+    def remove_ready_handler(self, handler):
+        """Remove a handler added with add_ready_handler"""
+        self.__ready_handlers.remove(handler)
 
-    def add_connected_handler(self, handler):
-        """Add a handler that will be called when we become connected to the model"""
-        self.__connected_handlers.append(handler)
-
-    def remove_connected_handler(self, handler):
-        """Remove a handler added with add_connected_handler"""
-        self.__connected_handlers.remove(handler)
-
-    def add_server_connected_handler(self, handler):
-        """Add a handler that will be called when we become connected to the server"""
-        self.__server_connected_handlers.append(handler)
-
-    def remove_server_connected_handler(self, handler):
-        """Remove a handler added with add_connected_handler"""
-        self.__server_connected_handlers.remove(handler)
-
-    def add_disconnected_handler(self, handler):
-        """Add a handler that will be called when we become disconnected from the server"""
-        self.__disconnected_handlers.append(handler)
-
-    def remove_disconnected_handler(self, handler):
-        """Remove a handler added with add_disconnected_handler"""
-        self.__disconnected_handlers.remove(handler)
-
     def add_added_handler(self, handler):
-        """Add a handler that will be called when a resource is added"""
+        """Add a handler that will be called when a resource is added.
+
+        This should never be used by a normal appplication, it's for ddm-viewer.
+        """
         self.__added_handlers.append(handler)
 
     def remove_added_handler(self, handler):
-        """Remove a handler added with add_added_handler"""
+        """Remove a handler added with add_added_handler """
         self.__added_handlers.remove(handler)
 
     ## of course, currently we never remove resources...
     def add_removed_handler(self, handler):
-        """Add a handler that will be called when a resource is removed"""
+        """Add a handler that will be called when a resource is removed
+
+        This should never be used by a normal appplication, it's for ddm-viewer.
+        """
         self.__removed_handlers.append(handler)
 
     def remove_removed_handler(self, handler):
@@ -97,11 +72,11 @@
         """
         raise NotImplementedException()
 
-    def query_resource(self, resource_id, fetch):
+    def query_resource(self, resource, fetch):
         """Create a query object for the standard system method 'getResource'.
 
         Arguments:
-        method -- the ID of the resource to retrieve
+        resource -- a resource object or resource ID
         fetch -- the fetch string to use for retrieving data. (default '+')
 
         The query will return the resource object if found, otherwise
@@ -109,6 +84,11 @@
         until you call execute() on it.
         
         """
+
+        if isinstance(resource, Resource):
+            resource_id = resource.resource_id
+        else:
+            resource_id = resource
         
         return self.query(("http://mugshot.org/p/system";, "getResource"),
                           fetch,
@@ -130,6 +110,12 @@
         
         raise NotImplementedException()
 
+    def _reset(self):
+        self.__resources = {}
+        self.global_resource = self._ensure_resource("online-desktop:/o/global", "online-desktop:/p/o/global")
+        self.self_resource = None
+        self.global_resource.connect(self.__on_self_changed, "self")
+    
     def _get_resource(self, resource_id):
         return self.__resources[resource_id]
 
@@ -144,43 +130,10 @@
             
             return resource
 
-    def _on_initialized(self):
-        for handler in self.__initialized_handlers:
+    def _on_ready(self):
+        self.ready = True
+        for handler in self.__ready_handlers:
             handler()    
-        self.initialized = True
 
-    def _on_connected(self):
-        if not self.__handled_model_connected:
-      
-            # On reconnection, all previous state is irrelevant
-            self.__resources = {}
-        
-            for handler in self.__connected_handlers:
-                handler()
-
-            self.__handled_model_connected = True 
-
-        if self.connected:
-            self._on_server_connected()
-
-    def _on_server_connected(self):
-        if self.__handled_server_connected:
-            return
-
-        for handler in self.__server_connected_handlers:
-            handler()
-
-        self.__handled_server_connected = True
-
-    def _on_disconnected(self):
-        if not self.__handled_model_connected:
-            return
-       
-        for handler in self.__disconnected_handlers:
-            handler()
-
-        self.__handled_model_connected = False
-        self.__handled_server_connected = False
-
-    def _set_self_id(self, self_id):
-        self.self_id = self_id
+    def __on_self_changed(self, global_resource):
+        self.self_resource = global_resource.self;

Modified: online-desktop/trunk/pyddm/ddm/DataModel.py
===================================================================
--- online-desktop/trunk/pyddm/ddm/DataModel.py	2007-11-08 20:39:56 UTC (rev 6881)
+++ online-desktop/trunk/pyddm/ddm/DataModel.py	2007-11-08 20:50:28 UTC (rev 6882)
@@ -8,6 +8,9 @@
 from ddm.NotificationSet import *
 from ddm.Resource import *
 
+# For idle handling
+import gobject
+
 _logger = logging.getLogger('mugshot.DataModel')
 
 def _escape_byte(m):
@@ -54,89 +57,89 @@
     def __real_init(self, server_name):
         AbstractModel.__init__(self)
 
-        self.__web_base_url = None
-        
         self.server_name = server_name # server_name can be None for "whatever engine is running"
 
         bus = dbus.SessionBus()
         bus_proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
         bus_proxy.connect_to_signal("NameOwnerChanged",
-                                    self.__update_proxy,
+                                    self.__on_name_owner_changed,
                                     arg0=_make_bus_name(server_name))
+        self._proxy = None        
         self.__update_proxy()
 
         self.callback = _DBusCallback(self, bus)
 
+    def __on_name_owner_changed(self, name, old_owner, new_owner):
+        self._proxy = None
+        if new_owner != '':
+            self.__update_proxy()
+        else:
+            self.__go_offline()
+
     def __update_proxy(self, *args):
-        self._on_disconnected()
-
         bus = dbus.SessionBus()
         targetname = _make_bus_name(self.server_name)
         try:
             _logger.debug("Looking for engine %s", targetname)            
             self._proxy = bus.get_object(targetname, '/org/freedesktop/od/data_model')
         except dbus.DBusException:
+            # Probably means the engine couldn't be activated
             _logger.debug("Failed to get proxy for %s", targetname, exc_info=True)
+            self.__go_offline()
             return
         
-        _logger.debug("Found model, querying status")          
-        # Order matters ... we want the self_id to be there before we call on_connect
-        self._proxy.Get('org.freedesktop.od.Model', 'SelfId', reply_handler=self.__get_self_id_reply, error_handler=self.__on_dbus_error)
+        _logger.debug("Found new model, querying ready state")
 
-        self._proxy.Get('org.freedesktop.od.Model', 'WebBaseUrl', reply_handler=self.__get_web_base_url_reply, error_handler=self.__on_dbus_error)
-        
-        self._proxy.connect_to_signal("ConnectedChanged", self.__on_connected_changed, dbus_interface='org.freedesktop.od.Model')
-        self._proxy.Get('org.freedesktop.od.Model', 'Connected', reply_handler=self.__get_connected_reply, error_handler=self.__on_dbus_error)        
+        self._proxy.connect_to_signal("Ready", self.__on_ready, dbus_interface='org.freedesktop.od.Model')
+        self._proxy.Get('org.freedesktop.od.Model', 'Ready', reply_handler=self.__get_ready_reply, error_handler=self.__get_ready_error)
 
-    def __on_dbus_error(self, err):
-        _logger.error("Caught D-BUS error: %s", err)
+    def __get_ready_reply(self, ready):
+        _logger.debug("Got reply for ready state, Ready=%s", ready)
+        if ready:
+            self.__on_ready()
 
-    def __on_connected_changed(self, connected, self_id):
-        _logger.debug("Connected status changed: %s", connected)      
-        self.connected = connected  
-        if connected:
-            self.__on_connected(self_id)
-        else:
-            self.__on_disconnected()
+    def __get_ready_error(self, err):
+        # Probably means that the engine died
+        _logger.error("Caught D-BUS error asking for Ready: %s", err)
+        self.__go_offline()
 
-    def __on_connected(self, self_id):
-        if self_id == '':
-            self._set_self_id(None)
-        else:
-            self._set_self_id(self_id)
-        self._on_connected()
+    def __on_ready(self):
+        self._reset()
 
-    def __on_disconnected(self):
-        self._on_disconnected()
+        _logger.debug("Doing initial query")
 
-    def __get_web_base_url_reply(self, baseurl):
-        _logger.debug("Got base url %s", baseurl)
-        if baseurl == '':
-            baseurl = None
-        self.__web_base_url = baseurl
+        query = self.query_resource("online-desktop:/o/global", "self +;webBaseUrl;online")
+        query.add_handler(self.__on_initial_query_success)
+        query.add_error_handler(self.__on_initial_query_error)
+        query.execute()
+            
+    def __on_initial_query_success(self, resource):
+        self._on_ready()
+            
+    def __on_initial_query_error(self, code, message):
+        # Probably means that the engine died
+        _logger.error("Got an error response to the initial query: %s", message)
+        self.__go_offline()
 
-    def __get_self_id_reply(self, self_id):
-        _logger.debug("Got self id %s", self_id)  
-        if self_id == '':
-            self._set_self_id(None)
-        else:
-            self._set_self_id(self_id)
+    def __go_offline(self):
+        # Common handling if an error occurs in the initialization path or we lose the
+        # connection to the server; we change the state to be offline and if we were
+        # still in the "not yet ready" state, signal the end of initialization
+        #
+        if self.global_resource == None:
+            self._reset()
+        notifications = NotificationSet(self)
+        self.global_resource._update_property(("online-desktop:/p/o/global", "online"),
+                                              UPDATE_REPLACE, CARDINALITY_1, False,
+                                              notifications)
+        notifications.send()
 
-    def __get_connected_reply(self, connected):
-         self.connected = connected 
+        if not self.ready:
+            self._on_ready()
 
-         self._on_initialized() 
-         # this is a hack -- we pretend that we are connected so that we can get
-         # the information that has been cached by the mugshot client
-         if self.self_id != None:
-             self._on_connected()
-
     def _get_proxy(self):
         return self._proxy
 
-    def get_web_base_url(self):
-        return self.__web_base_url
-        
     def query(self, method, fetch=None, single_result=False, **kwargs):
         _logger.debug("doing query: %s fetch=%s, single_result=%s", method, fetch, single_result)
         return _DBusQuery(self, method, fetch, single_result, kwargs)
@@ -164,6 +167,8 @@
                 raise Exception("Resource-valued element points to a resource we don't know about: " + str(value))
         elif type_byte == ord('s') or type_byte == ord('u'):
             value = value.__str__()
+        elif type_byte == ord('b'):
+            value = bool(value)
             
         if cardinality_byte == ord('.'):
             cardinality = CARDINALITY_1
@@ -240,11 +245,14 @@
         _logger.error('Caught error: %s', err.message)
         self._on_error(ERROR_FAILED, err.message)
 
+    def __async_no_connection_error(self):
+        self._on_error(ERROR_NO_CONNECTION, "No connection to engine to data model engine")
+        return False
+
     def execute(self):
-        # FIXME: Would it be better to call the __on_error? Doing that sync could cause problems.
-        #   If we decide to continue raising an exception here, we should use a subclass
-        if not self.__model.connected and self.__model.self_id == None:
-            raise Exception("Not connected")
+        if self.__model._proxy == None:
+            self._on_error(ERROR_NO_CONNECTION, "No connection to data model engine")
+            return
 
         method_uri = self.__method[0] + "#" + self.__method[1]
         #_logger.debug("executing query method: '%s' fetch: '%s' params: '%s'", method_uri, self.__fetch, self._params)
@@ -270,11 +278,14 @@
         _logger.error('Caught error: %s', err.message)
         self._on_error(ERROR_FAILED, err.message)
 
+    def __async_no_connection_error(self):
+        self._on_error(ERROR_NO_CONNECTION, "No connection to engine")
+        return False
+
     def execute(self):
-        # FIXME: Would it be better to call the __on_error? Doing that sync could cause problems.
-        #   If we decide to continue raising an exception here, we should use a subclass
-        if not self.__model.connected and self.__model.self_id == None:
-            raise Exception("Not connected")
+        if self.__model._proxy == None:
+            gobject.idle_add(self.__async_no_connection_error)
+            return
 
         method_uri = self.__method[0] + "#" + self.__method[1]
         _logger.debug("executing update method: '%s' params: '%s'", method_uri, self._params)

Modified: online-desktop/trunk/pyddm/ddm/TwistedModel.py
===================================================================
--- online-desktop/trunk/pyddm/ddm/TwistedModel.py	2007-11-08 20:39:56 UTC (rev 6881)
+++ online-desktop/trunk/pyddm/ddm/TwistedModel.py	2007-11-08 20:50:28 UTC (rev 6882)
@@ -23,37 +23,66 @@
     
     """
     
-    def __init__(self, server="mugshot.org:5222", username=None, password=None):
+    def __init__(self, web_server="mugshot.org", xmpp_server=None, username=None, password=None):
         """Create a new TwistedModel instance that connects to the specified server.
 
         Arguments:
-        server: Server to connect to. The port may be specified by appending it after
-           a colon. If the port is not specified, a default value will be used (default='mugshot.org')
+        web_server: Web server to connect to. The port may be specified by appending it after
+           a colon. If the port is not specified, a default value will be used. (default='mugshot.org')
+        xmpp_server: XMPP server to connect to. The port may be specified by appending it after
+           a colon. If the port is not specified, a default value will be used.
+           If no xmpp server is specified, the web server hostname will be used.
         username: GUID of the user to connect as (if not specified, found from cookies.txt)
         password: Password of the user (if not specified, found from cookies.txt)
         
         """
         AbstractModel.__init__(self)
 
-        colon = server.find(":")
+        colon = web_server.find(":")
         if (colon >= 0):
-            self.server = server[:colon]
-            self.port = int(server[colon + 1:])
+            self.web_server = web_server[:colon]
+            self.web_port = int(web_server[colon + 1:])
         else:
-            self.server = server
-            if server == "mugshot.org":
-                self.port = 5222
+            self.web_server = web_server
+            if web_server == "mugshot.org":
+                self.web_port = 80
+            elif web_server == "dogfood.mugshot.org":
+                self.web_port = 9080
             else:
-                self.port = 21020
+                self.web_port = 8080
 
+        if self.web_port == 80:
+            web_port = ""
+        else:
+            web_port = ":" + `self.web_port`
+
+        self.web_base_url = "http://%s%s"; % (self.web_server, web_port)
+
+        self.xmpp_port = None
+        if xmpp_server != None:
+            colon = xmpp_server.find(":")
+            if (colon >= 0):
+                self.xmpp_server = xmpp_server[:colon]
+                self.xmpp_port = int(xmpp_server[colon + 1:])
+            else:
+                self.xmpp_server =xmpp_server
+        else:
+            self.xmpp_server = self.web_server
+
+        if self.xmpp_port == None:
+            if self.web_port == 80:
+                self.xmpp_port = 5122
+            else:
+                self.xmpp_port = 21020
+        
         if username == None and password == None:
-            username, password = _parse_cookies(self.server)
+            username, password = _parse_cookies(self.web_server)
         elif username != None or password != None:
             raise MustLoginException("Either both user and password must be specified or neither")
 
         # FIXME: Use a legal resource
         self.username = username
-        user_jid = jid.JID("%s %s/mugshot-python" % (_guid_to_jabber_id(username), self.server))
+        user_jid = jid.JID("%s %s/mugshot-python" % (_guid_to_jabber_id(username), self.web_server))
         self.__factory = client.basicClientFactory(user_jid, password)
         self._xmlstream = None
 
@@ -65,12 +94,12 @@
         presumbaly want to start the main loop itself.
         
         """
-        
+
         global reactor
         self.__factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self.__on_auth_succeeded)
         self.__factory.addBootstrap(client.BasicAuthenticator.AUTH_FAILED_EVENT, self.__on_auth_failed)
  
-        reactor.connectTCP(self.server, self.port, self.__factory)
+        reactor.connectTCP(self.xmpp_server, self.xmpp_port, self.__factory)
         reactor.run()
         
 
@@ -88,16 +117,34 @@
         xmlstream.addObserver('/message', self.__on_message)
         
         self._xmlstream = xmlstream
-    
-        self.connected = True
-        self._on_initialized() 
-        self._on_connected()
 
+        self._reset()
+        
+        notifications = NotificationSet(self)        
+        self.global_resource._update_property(("online-desktop:/p/o/global", "online"),
+                                              UPDATE_REPLACE, CARDINALITY_1, True,
+                                              notifications)
+
+        self.global_resource._update_property(("online-desktop:/p/o/global", "webBaseUrl"),
+                                              UPDATE_REPLACE, CARDINALITY_1,  self.web_base_url,
+                                              notifications)
+
+        self_resource_id = self.web_base_url + "/o/user/" + self.username
+        self_resource = self._ensure_resource(self_resource_id,
+                                              "http://mugshot.org/p/o/user";)
+        
+        self.global_resource._update_property(("online-desktop:/p/o/global", "self"),
+                                              UPDATE_REPLACE, CARDINALITY_01, self_resource,
+                                              notifications)
+
+        notifications.send()
+        
+        self._on_ready()
+
     def __on_auth_failed(self, xmlstream):
         global reactor
         print 'Auth failed!'
-        self.connected = False
-        self._on_initialized() 
+        
         reactor.stop()
 
     def __get_resource_id(self, resource_element):

Modified: online-desktop/trunk/pyddm/test-session.py
===================================================================
--- online-desktop/trunk/pyddm/test-session.py	2007-11-08 20:39:56 UTC (rev 6881)
+++ online-desktop/trunk/pyddm/test-session.py	2007-11-08 20:50:28 UTC (rev 6882)
@@ -45,22 +45,26 @@
 def on_global_query_failure(code, message):
     print message
     
-def on_connect():
-    print "Connected"
+def online_changed(resource):
+    print "Online: ", resource.online
+    
+def on_ready():
+    print "Ready"
 
-    query = model.query_resource(model.self_id, "+;contacts +;contacters +;lovedAccounts +;email;aim")
-    query.add_handler(on_self_query_success)
-    query.add_error_handler(on_self_query_failure)
-    query.execute()
+    if model.self_resource != None:
+        query = model.query_resource(model.self_resource, "+;contacts +;contacters +;lovedAccounts +;email;aim")
+        query.add_handler(on_self_query_success)
+        query.add_error_handler(on_self_query_failure)
+        query.execute()
 
-    query = model.query_resource("online-desktop:/o/global", "onlineBuddies +")
+    query = model.query_resource(model.global_resource, "onlineBuddies +")
     query.add_handler(on_global_query_success)
     query.add_error_handler(on_global_query_failure)
     query.execute()
+    
+    model.global_resource.connect(online_changed, "online")
+    online_changed(model.global_resource)
 
-def on_disconnect():
-    print "Disconnected"
-
 parser = OptionParser()
 parser.add_option("-s", "--server", default="localinstance.mugshot.org:8080", help="Mugshot server to connect to (default localinstance.mugshot.org:8080)")
 (options, args) = parser.parse_args()
@@ -69,10 +73,9 @@
     sys.exit(1)
 
 model = DataModel(options.server)
-model.add_connected_handler(on_connect)
-model.add_disconnected_handler(on_disconnect)
-if model.connected:
-    on_connect()
+model.add_ready_handler(on_ready)
+if model.ready:
+    on_ready()
 
 loop = gobject.MainLoop()
 loop.run()

Modified: online-desktop/trunk/pyddm/test.py
===================================================================
--- online-desktop/trunk/pyddm/test.py	2007-11-08 20:39:56 UTC (rev 6881)
+++ online-desktop/trunk/pyddm/test.py	2007-11-08 20:50:28 UTC (rev 6882)
@@ -22,18 +22,25 @@
 def on_query_failure(code, message):
     print message
 
-def on_connect():
-    print "Connected"
+def online_changed(resource):
+    print "Online: ", resource.online
+    
+def on_ready():
+    print "Ready"
 
     # FIXME: having to include the port means won't work on non-debug servers
-    query = model.query_resource("http://%s:8080/o/user/%s"; % (model.server,  model.username),
+    query = model.query_resource(model.self_resource,
                                  "+;contacts +;contacters +;lovedAccounts +")
     query.add_handler(on_query_success)
     query.add_error_handler(on_query_failure)
     query.execute()
 
+    model.global_resource.connect(online_changed, "online")
+    online_changed(model.global_resource)
+
 parser = OptionParser()
-parser.add_option("-s", "--server", default="localinstance.mugshot.org:21020", help="Mugshot server to connect to (default localinstance.mugshot.org:21020)")
+parser.add_option("-s", "--server", default="localinstance.mugshot.org:21020", help="Mugshot web server to connect to (default localinstance.mugshot.org)")
+parser.add_option("-x", "--xmpp-server", help="XMPP server to connect to (if not specified, derived from --server)")
 parser.add_option("-u", "--user", help="User to connect to the server as (default from Firefox cookies)")
 parser.add_option("-p", "--password", help="Password to use to connect to the server (default from Firefox cookies)")
 (options, args) = parser.parse_args()
@@ -41,7 +48,7 @@
     parser.print_usage()
     sys.exit(1)
 
-model = TwistedModel(options.server, options.user, options.password)
+model = TwistedModel(web_server=options.server, xmpp_server=options.xmpp_server, username=options.user, password=options.password)
 
-model.add_connected_handler(on_connect)
+model.add_ready_handler(on_ready)
 model.run()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]