123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- # errorSemantics.py
-
- # Test the Python error handling semantics. Specifically:
- #
- # * When a Python COM object is called via IDispatch, the nominated
- # scode is placed in the exception tuple, and the HRESULT is
- # DISP_E_EXCEPTION
- # * When the same interface is called via IWhatever, the
- # nominated scode is returned directly (with the scode also
- # reflected in the exception tuple)
- # * In all cases, the description etc end up in the exception tuple
- # * "Normal" Python exceptions resolve to an E_FAIL "internal error"
-
- import pythoncom
- import winerror
- from win32com.client import Dispatch
- from win32com.server.exception import COMException
- from win32com.server.util import wrap
- from win32com.test.util import CaptureWriter
-
-
- class error(Exception):
- def __init__(self, msg, com_exception=None):
- Exception.__init__(self, msg, str(com_exception))
-
-
- # Our COM server.
- class TestServer:
- _public_methods_ = ["Clone", "Commit", "LockRegion", "Read"]
- _com_interfaces_ = [pythoncom.IID_IStream]
-
- def Clone(self):
- raise COMException("Not today", scode=winerror.E_UNEXPECTED)
-
- def Commit(self, flags):
- # Testing unicode: 1F600 '😀'; GRINNING FACE
- # Use the 'name' just for fun!
- if flags == 0:
- # A non com-specific exception.
- raise Exception("\N{GRINNING FACE}")
- # An explicit com_error, which is a bit of an edge-case, but might happen if
- # a COM server itself calls another COM object and it fails.
- excepinfo = (
- winerror.E_UNEXPECTED,
- "source",
- "\N{GRINNING FACE}",
- "helpfile",
- 1,
- winerror.E_FAIL,
- )
- raise pythoncom.com_error(winerror.E_UNEXPECTED, "desc", excepinfo, None)
-
-
- def test():
- # Call via a native interface.
- com_server = wrap(TestServer(), pythoncom.IID_IStream)
- try:
- com_server.Clone()
- raise error("Expecting this call to fail!")
- except pythoncom.com_error as com_exc:
- if com_exc.hresult != winerror.E_UNEXPECTED:
- raise error(
- "Calling the object natively did not yield the correct scode", com_exc
- )
- exc = com_exc.excepinfo
- if not exc or exc[-1] != winerror.E_UNEXPECTED:
- raise error(
- "The scode element of the exception tuple did not yield the correct scode",
- com_exc,
- )
- if exc[2] != "Not today":
- raise error(
- "The description in the exception tuple did not yield the correct string",
- com_exc,
- )
- cap = CaptureWriter()
- try:
- cap.capture()
- try:
- com_server.Commit(0)
- finally:
- cap.release()
- raise error("Expecting this call to fail!")
- except pythoncom.com_error as com_exc:
- if com_exc.hresult != winerror.E_FAIL:
- raise error("The hresult was not E_FAIL for an internal error", com_exc)
- if com_exc.excepinfo[1] != "Python COM Server Internal Error":
- raise error(
- "The description in the exception tuple did not yield the correct string",
- com_exc,
- )
- # Check we saw a traceback in stderr
- if cap.get_captured().find("Traceback") < 0:
- raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
-
- # Now do it all again, but using IDispatch
- com_server = Dispatch(wrap(TestServer()))
- try:
- com_server.Clone()
- raise error("Expecting this call to fail!")
- except pythoncom.com_error as com_exc:
- if com_exc.hresult != winerror.DISP_E_EXCEPTION:
- raise error(
- "Calling the object via IDispatch did not yield the correct scode",
- com_exc,
- )
- exc = com_exc.excepinfo
- if not exc or exc[-1] != winerror.E_UNEXPECTED:
- raise error(
- "The scode element of the exception tuple did not yield the correct scode",
- com_exc,
- )
- if exc[2] != "Not today":
- raise error(
- "The description in the exception tuple did not yield the correct string",
- com_exc,
- )
-
- cap.clear()
- try:
- cap.capture()
- try:
- com_server.Commit(0)
- finally:
- cap.release()
- raise error("Expecting this call to fail!")
- except pythoncom.com_error as com_exc:
- if com_exc.hresult != winerror.DISP_E_EXCEPTION:
- raise error(
- "Calling the object via IDispatch did not yield the correct scode",
- com_exc,
- )
- exc = com_exc.excepinfo
- if not exc or exc[-1] != winerror.E_FAIL:
- raise error(
- "The scode element of the exception tuple did not yield the correct scode",
- com_exc,
- )
- if exc[1] != "Python COM Server Internal Error":
- raise error(
- "The description in the exception tuple did not yield the correct string",
- com_exc,
- )
- # Check we saw a traceback in stderr
- if cap.get_captured().find("Traceback") < 0:
- raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
-
- # And an explicit com_error
- cap.clear()
- try:
- cap.capture()
- try:
- com_server.Commit(1)
- finally:
- cap.release()
- raise error("Expecting this call to fail!")
- except pythoncom.com_error as com_exc:
- if com_exc.hresult != winerror.DISP_E_EXCEPTION:
- raise error(
- "Calling the object via IDispatch did not yield the correct scode",
- com_exc,
- )
- exc = com_exc.excepinfo
- if not exc or exc[-1] != winerror.E_FAIL:
- raise error(
- "The scode element of the exception tuple did not yield the correct scode",
- com_exc,
- )
- if exc[1] != "source":
- raise error(
- "The source in the exception tuple did not yield the correct string",
- com_exc,
- )
- if exc[2] != "\U0001F600":
- raise error(
- "The description in the exception tuple did not yield the correct string",
- com_exc,
- )
- if exc[3] != "helpfile":
- raise error(
- "The helpfile in the exception tuple did not yield the correct string",
- com_exc,
- )
- if exc[4] != 1:
- raise error(
- "The help context in the exception tuple did not yield the correct string",
- com_exc,
- )
-
-
- try:
- import logging
- except ImportError:
- logging = None
- if logging is not None:
- import win32com
-
- class TestLogHandler(logging.Handler):
- def __init__(self):
- self.reset()
- logging.Handler.__init__(self)
-
- def reset(self):
- self.num_emits = 0
- self.last_record = None
-
- def emit(self, record):
- self.num_emits += 1
- self.last_record = self.format(record)
- return
- print("--- record start")
- print(self.last_record)
- print("--- record end")
-
- def testLogger():
- assert not hasattr(win32com, "logger")
- handler = TestLogHandler()
- formatter = logging.Formatter("%(message)s")
- handler.setFormatter(formatter)
- log = logging.getLogger("win32com_test")
- log.addHandler(handler)
- win32com.logger = log
- # Now throw some exceptions!
- # Native interfaces
- com_server = wrap(TestServer(), pythoncom.IID_IStream)
- try:
- com_server.Commit(0)
- raise RuntimeError("should have failed")
- except pythoncom.error as exc:
- # `excepinfo` is a tuple with elt 2 being the traceback we captured.
- message = exc.excepinfo[2]
- assert message.endswith("Exception: \U0001F600\n")
- assert handler.num_emits == 1, handler.num_emits
- assert handler.last_record.startswith(
- "pythoncom error: Unexpected exception in gateway method 'Commit'"
- )
- handler.reset()
-
- # IDispatch
- com_server = Dispatch(wrap(TestServer()))
- try:
- com_server.Commit(0)
- raise RuntimeError("should have failed")
- except pythoncom.error as exc:
- # `excepinfo` is a tuple with elt 2 being the traceback we captured.
- message = exc.excepinfo[2]
- assert message.endswith("Exception: \U0001F600\n")
- assert handler.num_emits == 1, handler.num_emits
- handler.reset()
-
-
- if __name__ == "__main__":
- test()
- if logging is not None:
- testLogger()
- from win32com.test.util import CheckClean
-
- CheckClean()
- print("error semantic tests worked")
|