team-10/venv/Lib/site-packages/win32com/test/errorSemantics.py
2025-08-02 02:00:33 +02:00

243 lines
8.3 KiB
Python

# 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
# 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 AssertionError("Expecting this call to fail!")
except pythoncom.com_error as com_exc:
assert com_exc.hresult == winerror.E_UNEXPECTED, (
"Calling the object natively did not yield the correct scode",
str(com_exc),
)
exc = com_exc.excepinfo
assert exc and exc[-1] == winerror.E_UNEXPECTED, (
"The scode element of the exception tuple did not yield the correct scode",
str(com_exc),
)
assert exc[2] == "Not today", (
"The description in the exception tuple did not yield the correct string",
str(com_exc),
)
cap = CaptureWriter()
try:
cap.capture()
try:
com_server.Commit(0)
finally:
cap.release()
raise AssertionError("Expecting this call to fail!")
except pythoncom.com_error as com_exc:
assert com_exc.hresult == winerror.E_FAIL, (
"The hresult was not E_FAIL for an internal error",
str(com_exc),
)
assert com_exc.excepinfo[1] == "Python COM Server Internal Error", (
"The description in the exception tuple did not yield the correct string",
str(com_exc),
)
# Check we saw a traceback in stderr
assert cap.get_captured().find("Traceback") >= 0, (
f"Could not find a traceback in stderr: {cap.get_captured()!r}"
)
# Now do it all again, but using IDispatch
com_server = Dispatch(wrap(TestServer()))
try:
com_server.Clone()
raise AssertionError("Expecting this call to fail!")
except pythoncom.com_error as com_exc:
assert com_exc.hresult == winerror.DISP_E_EXCEPTION, (
"Calling the object via IDispatch did not yield the correct scode",
str(com_exc),
)
exc = com_exc.excepinfo
assert exc and exc[-1] == winerror.E_UNEXPECTED, (
"The scode element of the exception tuple did not yield the correct scode",
str(com_exc),
)
assert exc[2] == "Not today", (
"The description in the exception tuple did not yield the correct string",
str(com_exc),
)
cap.clear()
try:
cap.capture()
try:
com_server.Commit(0)
finally:
cap.release()
raise AssertionError("Expecting this call to fail!")
except pythoncom.com_error as com_exc:
assert com_exc.hresult == winerror.DISP_E_EXCEPTION, (
"Calling the object via IDispatch did not yield the correct scode",
str(com_exc),
)
exc = com_exc.excepinfo
assert exc and exc[-1] == winerror.E_FAIL, (
"The scode element of the exception tuple did not yield the correct scode",
str(com_exc),
)
assert exc[1] == "Python COM Server Internal Error", (
"The description in the exception tuple did not yield the correct string",
str(com_exc),
)
# Check we saw a traceback in stderr
assert cap.get_captured().find("Traceback") >= 0, (
f"Could not find a traceback in stderr: {cap.get_captured()!r}"
)
# And an explicit com_error
cap.clear()
try:
cap.capture()
try:
com_server.Commit(1)
finally:
cap.release()
raise AssertionError("Expecting this call to fail!")
except pythoncom.com_error as com_exc:
assert com_exc.hresult == winerror.DISP_E_EXCEPTION, (
"Calling the object via IDispatch did not yield the correct scode",
str(com_exc),
)
exc = com_exc.excepinfo
assert exc and exc[-1] == winerror.E_FAIL, (
"The scode element of the exception tuple did not yield the correct scode",
str(com_exc),
)
assert exc[1] == "source", (
"The source in the exception tuple did not yield the correct string",
str(com_exc),
)
assert exc[2] == "\U0001f600", (
"The description in the exception tuple did not yield the correct string",
str(com_exc),
)
assert exc[3] == "helpfile", (
"The helpfile in the exception tuple did not yield the correct string",
str(com_exc),
)
assert exc[4] == 1, (
"The help context in the exception tuple did not yield the correct string",
str(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 AssertionError("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 AssertionError("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")