Question
· Apr 18, 2022

How to handle returned exception error object by Embedded Python method?

How can I get the Python error object(exception return value) from the embedded python method?
I have an embedded Python method like as below;

ClassMethod test2() As XXX [ Language = python ]
{
   try:
    a=1/0
   
   except Exception as ex:
    print("Exception: %s" % str(ex))
    return ex
}

I'm not sure what kind of return type to set for this classmethod.   "As XXX" <--
I tried to set "ClassMethod test2() As %Exception.PythonException [ Language = python ]"
However, I got the following return value when I run the method in the IRIS terminal.

USER>set st= ##class(User.PythonTest).test2()
Exception: division by zero
USER>zw st
st=4@%SYS.Python ; division by zero ; <OREF>
USER>

How can I return the python error object(ex)?

Please let me know if you know a good way to raise or return errors in the Embedded Python method.

Product version: IRIS 2021.2
$ZV: IRIS for Windows (x86-64) 2021.2 (Build 651U) Mon Jan 31 2022 17:39:05 EST
Discussion (9)1
Log in or sign up to continue

If the goal is to have the method return an instance of %Exception.PythonException, the method could create one with "_New(str) method, supplying the "ex" string as the "str" parameter. The "except" clause would then return the instance.

Not sure of the syntax, but it's likely something like this:

newex = iris.cls('%Exception.PythonException')._New(ex) 
return newex

If, instead of returning that exception, the method would rather "raise" the exception,  there are some helpful hints here:

https://stackoverflow.com/questions/2052390/manually-raising-throwing-an...

@Jean Millette 
Thank you for your response.
I can receive a %Exception.PythonException object with your code!
However, it looks not to set the details of the exception.
Your StackOverflow link looks very useful, thank you very much.
In addition, print(repr(ex)) is very nice, too!

ClassMethod test2() As %Exception.PythonException [ Language = python ]
{
try:
 import iris
 a=1/0
 return ""
except Exception as ex:
 print(repr(ex))
 newex = iris.cls('%Exception.PythonException')._New(ex) 
 return newex
}
USER>s st= ##Class(User.PythonTest).test2()
ZeroDivisionError('division by zero')

USER>zw st
st=10@%Exception.PythonException ; <OREF>
+----------------- general information ---------------
| oref value: 10
| class name: %Exception.PythonException
| reference count: 3
+----------------- attribute values ------------------
| Code = "" <Set>
| Data = "" <Set>
| ExceptionObject = ""
| InnerException = "" <Set>
| Location = "" <Set>
| Name = "11@%SYS.Python" <Set>
| iStack = $lb("e^^^1")
+-----------------------------------------------------

USER>w st
10@%Exception.PythonException
USER>w st.DisplayString()
11@%SYS.Python
USER>w $SYSTEM.Status.GetErrorText(st.AsStatus())
エラー #5002: Cache エラー:11@%SYS.Python
USER>

I could get it like as follows. 
Thank you very much.

 newex = iris.cls('%Exception.PythonException')._New()
 newex.Code=str(ex.args)
 newex = iris.cls('%Exception.PythonException')._New(repr(ex),'','',str(ex.args)) 
 return newex 

===
+----------------- attribute values ------------------
| Code = "" <Set>
| Data = "('division by zero',)" <Set>
| ExceptionObject = ""
| InnerException = "" <Set>
| Location = "" <Set>
| Name = "ZeroDivisionError('division by zero')" <Set>

:

The "repr(ex)" Python method also prints a bit more information about the exception:

        except Exception as ex:
            print("Exception: %s" % str(ex))
            print(repr(ex))

The "repr(ex)" Python method also prints a bit more information about the exception (e.g., the type of Error):

Exception: division by zero
ZeroDivisionError('division by zero')

In Embedded Python, exceptions flow back-and-forth between ObjectScript and Python as you'd expect. 

If your Python code raises an exception, but does not handle it, then the exception is passed to the ObjectScript side.  And vice-versa.  In your example, I'm not sure why you're handling the exception in python and then returning an exception object to the caller.  Why not just not catch the exception and let it get passed along to the ObjectScript side?

@Bob Kuszewski 
Thank you for your response.

I tried your advice.
I could receive a %Exception.PythonException object on an exception.
I didn't set any return type on the python method.
It looks better to catch the exception on the ObjectScript side, I'll use properly depending on the situation.

ClassMethod test3() [ Language = python ]
{
  a=1/0
}

ClassMethod test4() As %Status
{
 set status=$$$OK
 try {
  do ..test3()
 }
 catch ex {
  set status=ex.AsStatus()
 }
 return status
}
USER>s st= ##Class(User.PythonTest).test4()
ZeroDivisionError('division by zero')
%Exception.PythonException
 
USER>w $SYSTEM.Status.GetErrorText(st)
エラー #5002: Cache エラー:<SYNTAX>ztest4+3^User.PythonTest.1^2^  Do ..test3() *      <class 'ZeroDivisionError'>: division by zero -
USER>