ManifoldAPI raises ManifoldException if anything goes wrong
authorThierry Parmentelat <thierry.parmentelat@inria.fr>
Fri, 17 May 2013 11:21:33 +0000 (13:21 +0200)
committerThierry Parmentelat <thierry.parmentelat@inria.fr>
Fri, 17 May 2013 11:21:33 +0000 (13:21 +0200)
SliceView catches this at the highest level (and logs out user)
ManifoldProxy catches this too and forwards manifoldresult to js

manifold/manifoldapi.py
manifold/manifoldproxy.py
manifold/manifoldresult.py
manifold/metadata.py
trash/sliceview.py
unfold/page.py

index 2548287..14eef3b 100644 (file)
@@ -3,7 +3,7 @@ import xmlrpclib
 
 from myslice.config import Config
 
-from manifoldresult import ManifoldResult, ManifoldCode
+from manifoldresult import ManifoldResult, ManifoldCode, ManifoldException
 
 debug=False
 debug=True
@@ -24,6 +24,13 @@ class ManifoldAPI:
 
     def __repr__ (self): return "ManifoldAPI[%s]"%self.url
 
+    # a one-liner to give a hint of what the return value looks like
+    def _print_result (self, result):
+        if not result:                        print "[no/empty result]"
+        elif isinstance (result,str):         print "result is '%s'"%result
+        elif isinstance (result,list):        print "result is a %d-elts list"%len(result)
+        else:                                 print "[dont know how to display result]"
+
     # xxx temporary code for scaffolding a ManifolResult on top of an API that does not expose error info
     # as of march 2013 we work with an API that essentially either returns the value, or raises 
     # an xmlrpclib.Fault exception with always the same 8002 code
@@ -32,29 +39,30 @@ class ManifoldAPI:
     # a SESSION_EXPIRED code
     def __getattr__(self, methodName):
         def func(*args, **kwds):
-            if (debug): 
-                print "entering ManifoldAPI.%s"%methodName,
-                print "args",args,
-                print "kwds",kwds
             try:
+                if debug: print "====> ManifoldAPI.%s"%methodName,"args",args,"kwds",kwds
                 result=getattr(self.server, methodName)(self.auth, *args, **kwds)
+                if debug:
+                    print '<==== backend call %s(*%s,**%s) returned'%(methodName,args,kwds),
+                    print '.ctd. Authmethod=',self.auth['AuthMethod'], self.url,'->',
+                    self._print_result(result)
                 ### attempt to cope with old APIs and new APIs
                 if isinstance (result, dict) and 'code' in result:
-                    # this sounds like a result from a new API, leave it untouched
-                    # XXX jordan : we need to wrap it into a ResultValue structure
-                    # XXX this is not good until we merge both repos
-                    if result['code'] != 2:
+                    if debug: print "taken as new API"
+                    # this sounds like a result from a new API
+                    # minimal treatment is required, but we do want to turn this into a 
+                    # class instance
+                    if result['code'] != 2: # in the manifold world, this can be either
+                                            # 0 (ok) 1 (partial result) or 2 which means error
+                        if debug: print "OK (new API)"
                         return ManifoldResult(code=result['code'], value=result['value'])
                     else:
-                        return ManifoldResult(code=result['code'], output=result['description'])
+                        if debug: print "KO (new API) - raising ManifoldException"
+                        raise ManifoldException(ManifoldResult(code=result['code'], output=result['description']))
                 else:
-                    if debug:
-                        print '<=== backend call', methodName, args, kwds
-                        print '.... ctd', 'Authmethod=',self.auth['AuthMethod'], self.url,'->',
-                        if not result:                        print "[no/empty result]"
-                        elif isinstance (result,str):         print "result is '%s'"%result
-                        elif isinstance (result,list):        print "result is a %d-elts list"%len(result)
-                        else:                                 print "[dont know how to display result]"
+                    if debug: print "taken as old API"
+                    # we're talking to an old API
+                    # so if we make it here it should mean success
                     return ManifoldResult (code=ManifoldCode.SUCCESS, value=result)
             except xmlrpclib.Fault, error:
                 ### xxx this is very rough for now
@@ -62,15 +70,17 @@ class ManifoldAPI:
                 # in some less unpolite way than this anonymous exception, we assume it's a problem with the session
                 # that needs to be refreshed
                 if error.faultCode == 8002:
+                    if debug: print "KO (old API - 8002) - raising ManifoldException"
                     reason="most likely your session has expired"
                     reason += " (the manifold API has no unambiguous error reporting mechanism yet)"
-                    return ManifoldResult (code=ManifoldCode.SESSION_EXPIRED, output=reason)
+                    raise ManifoldException ( ManifoldResult (code=ManifoldCode.SESSION_EXPIRED, output=reason))
                 else:
+                    if debug: print "KO (old API - other) - raising ManifoldException"
                     reason="xmlrpclib.Fault with faultCode = %s (not taken as session expired)"%error.faultCode
-                    return ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output=reason)
+                    raise ManifoldException ( ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output=reason))
             except Exception,error:
-                print "ManifoldAPI: unexpected exception",error
-                return ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output="%s"%error)
+                if debug: print "KO (unexpected exception)",error
+                raise ManifoldException ( ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output="%s"%error) )
         return func
 
     def send_manifold_query (self, query):
index 1182955..99de784 100644 (file)
@@ -6,6 +6,7 @@ from django.http import HttpResponse, HttpResponseForbidden
 #from manifold.manifoldquery import ManifoldQuery
 from manifold.core.query import Query
 from manifold.manifoldapi import ManifoldAPI
+from manifold.manifoldresult import ManifoldException
 
 debug=False
 debug=True
@@ -73,8 +74,13 @@ with the query passed using POST"""
                 
         # actually forward
         manifold_api= ManifoldAPI(auth=manifold_api_session_auth)
-        if debug: print 'manifoldproxy.proxy: sending to backend', manifold_query
-        answer=manifold_api.send_manifold_query (manifold_query)
+        if debug: print '===> manifoldproxy.proxy: sending to backend', manifold_query
+        # for the benefit of the python code, manifoldAPI raises an exception if something is wrong
+        # however in this case we want to propagate the complete manifold result to the js world
+        try:
+            answer=manifold_api.send_manifold_query (manifold_query)
+        except ManifoldException, manifold_result:
+            answer=manifold_result
         print "="*80
         print "ANSWER IN PROXY", answer
         print answer.ok_value()
@@ -89,7 +95,7 @@ with the query passed using POST"""
                 else: print "result is other (type=%s) : %s"%(type(value),value)
         json_answer=json.dumps(answer)
         # if in debug mode we save this so we can use offline mode later
-        if (debug):
+        if debug:
             with (file(offline_filename,"w")) as f:
                 f.write(json_answer)
         # this is an artificial delay added for debugging purposes only
index 1c812f0..a16a9a7 100644 (file)
@@ -21,11 +21,16 @@ class ManifoldResult (dict):
         for k in ['code','value','output']:
             self[k]=d[k]
 
+    # raw accessors
+    def code (self): return self['code']
+    def output (self): return self['output']
+
     # this returns None if there's a problem, the value otherwise
     def ok_value (self):
         if self['code']==ManifoldCode.SUCCESS:
             return self['value']
 
+    # both data in a single string
     def error (self):
         return "code=%s -- %s"%(self['code'],self['output'])
     
@@ -40,3 +45,11 @@ class ManifoldResult (dict):
             result += " [output=%s]"%self['output']
         result += "]]"
         return result
+
+# probably simpler to use a single class and transport the whole result there
+# instead of a clumsy set of derived classes 
+class ManifoldException (Exception):
+    def __init__ (self, manifold_result):
+        self.manifold_result=manifold_result
+    def __repr__ (self):
+        return "Manifold Exception %s"%(self.manifold_result.error())
index 0924ded..59d600b 100644 (file)
@@ -16,9 +16,6 @@ class MetaData:
         self.auth=auth
         self.hash_by_object={}
 
-        # XXX Retrieve all metadata the first time we instanciate the class
-        self.fetch()
-
     def fetch (self):
         offline_filename="offline-metadata.json"
         if work_offline:
@@ -39,14 +36,14 @@ class MetaData:
             'object': 'local:object', # proposed to replace metadata:table
             'fields':     fields 
         })
-#old#        rows_result = manifold_api.Get('metadata:table', [], [], fields)
         rows = rows_result.ok_value()
-        if not rows:
-            print "Failed to retrieve metadata",rows_result.error()
-            rows=[]
+# API errors will be handled by the outer logic
+#        if not rows:
+#            print "Failed to retrieve metadata",rows_result.error()
+#            rows=[]
         self.hash_by_object = dict ( [ (row['table'], row) for row in rows ] )
         # save for next time we use offline mode
-        if debug:
+        if debug and rows:
             with file(offline_filename,'w') as f:
                 f.write(json.dumps(self.hash_by_object))
 
index fcbaac7..9865f77 100644 (file)
@@ -4,6 +4,7 @@ from django.template import RequestContext
 from django.shortcuts import render_to_response
 
 from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseRedirect
 
 from unfold.page import Page
 #from manifold.manifoldquery import ManifoldQuery
@@ -20,8 +21,9 @@ from plugins.querycode.querycode import QueryCode
 from plugins.quickfilter.quickfilter import QuickFilter
 from plugins.messages.messages import Messages
 
-from myslice.viewutils import quickfilter_criterias
+from manifold.manifoldresult import ManifoldException
 
+from myslice.viewutils import quickfilter_criterias
 from myslice.viewutils import topmenu_items, the_user
 
 # XXX JORDAN
@@ -32,18 +34,34 @@ debug = True
 
 @login_required
 def slice_view (request, slicename=tmp_default_slice):
-    
+    # xxx Thierry - ugly hack
+    # fetching metadata here might fail - e.g. with an expired session..
+    # let's catch this early on and log out our user if needed
+    # it should of course be handled in a more generic way
+    try:
+        return _slice_view(request,slicename)
+    except ManifoldException, manifold_result:
+        # xxx needs a means to display this message to user...
+        from django.contrib.auth import logout
+        logout(request)
+        return HttpResponseRedirect ('/')
+    except Exception, e:
+        # xxx we need to sugarcoat this error message in some error template...
+        print "Unexpected exception",e
+        # return ...
+
+def _slice_view (request, slicename):
+
     page = Page(request)
     page.expose_js_metadata()
 
-
     # TODO The query to run is embedded in the URL
     main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename)
 
     # Get default fields from metadata unless specified
     if not main_query.fields:
-        md_fields = page.get_metadata()
-        md_fields = md_fields.details_by_object('slice')
+        metadata = page.get_metadata()
+        md_fields = metadata.details_by_object('slice')
         if debug:
             print "METADATA", md_fields
         # TODO Get default fields
index b49dce1..a97ec6d 100644 (file)
@@ -105,7 +105,7 @@ class Page:
         manifold_api_session_auth = session['manifold']['auth']
         metadata=MetaData (manifold_api_session_auth)
         metadata.fetch()
-            # store it for next time
+        # store it for next time
         manifold['metadata']=metadata
         if debug: print "Page.get_metadata: return new value"
         return metadata