Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

errorSemantics.py 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. # errorSemantics.py
  2. # Test the Python error handling semantics. Specifically:
  3. #
  4. # * When a Python COM object is called via IDispatch, the nominated
  5. # scode is placed in the exception tuple, and the HRESULT is
  6. # DISP_E_EXCEPTION
  7. # * When the same interface is called via IWhatever, the
  8. # nominated scode is returned directly (with the scode also
  9. # reflected in the exception tuple)
  10. # * In all cases, the description etc end up in the exception tuple
  11. # * "Normal" Python exceptions resolve to an E_FAIL "internal error"
  12. import pythoncom
  13. import winerror
  14. from win32com.client import Dispatch
  15. from win32com.server.exception import COMException
  16. from win32com.server.util import wrap
  17. from win32com.test.util import CaptureWriter
  18. class error(Exception):
  19. def __init__(self, msg, com_exception=None):
  20. Exception.__init__(self, msg, str(com_exception))
  21. # Our COM server.
  22. class TestServer:
  23. _public_methods_ = ["Clone", "Commit", "LockRegion", "Read"]
  24. _com_interfaces_ = [pythoncom.IID_IStream]
  25. def Clone(self):
  26. raise COMException("Not today", scode=winerror.E_UNEXPECTED)
  27. def Commit(self, flags):
  28. # Testing unicode: 1F600 '😀'; GRINNING FACE
  29. # Use the 'name' just for fun!
  30. if flags == 0:
  31. # A non com-specific exception.
  32. raise Exception("\N{GRINNING FACE}")
  33. # An explicit com_error, which is a bit of an edge-case, but might happen if
  34. # a COM server itself calls another COM object and it fails.
  35. excepinfo = (
  36. winerror.E_UNEXPECTED,
  37. "source",
  38. "\N{GRINNING FACE}",
  39. "helpfile",
  40. 1,
  41. winerror.E_FAIL,
  42. )
  43. raise pythoncom.com_error(winerror.E_UNEXPECTED, "desc", excepinfo, None)
  44. def test():
  45. # Call via a native interface.
  46. com_server = wrap(TestServer(), pythoncom.IID_IStream)
  47. try:
  48. com_server.Clone()
  49. raise error("Expecting this call to fail!")
  50. except pythoncom.com_error as com_exc:
  51. if com_exc.hresult != winerror.E_UNEXPECTED:
  52. raise error(
  53. "Calling the object natively did not yield the correct scode", com_exc
  54. )
  55. exc = com_exc.excepinfo
  56. if not exc or exc[-1] != winerror.E_UNEXPECTED:
  57. raise error(
  58. "The scode element of the exception tuple did not yield the correct scode",
  59. com_exc,
  60. )
  61. if exc[2] != "Not today":
  62. raise error(
  63. "The description in the exception tuple did not yield the correct string",
  64. com_exc,
  65. )
  66. cap = CaptureWriter()
  67. try:
  68. cap.capture()
  69. try:
  70. com_server.Commit(0)
  71. finally:
  72. cap.release()
  73. raise error("Expecting this call to fail!")
  74. except pythoncom.com_error as com_exc:
  75. if com_exc.hresult != winerror.E_FAIL:
  76. raise error("The hresult was not E_FAIL for an internal error", com_exc)
  77. if com_exc.excepinfo[1] != "Python COM Server Internal Error":
  78. raise error(
  79. "The description in the exception tuple did not yield the correct string",
  80. com_exc,
  81. )
  82. # Check we saw a traceback in stderr
  83. if cap.get_captured().find("Traceback") < 0:
  84. raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
  85. # Now do it all again, but using IDispatch
  86. com_server = Dispatch(wrap(TestServer()))
  87. try:
  88. com_server.Clone()
  89. raise error("Expecting this call to fail!")
  90. except pythoncom.com_error as com_exc:
  91. if com_exc.hresult != winerror.DISP_E_EXCEPTION:
  92. raise error(
  93. "Calling the object via IDispatch did not yield the correct scode",
  94. com_exc,
  95. )
  96. exc = com_exc.excepinfo
  97. if not exc or exc[-1] != winerror.E_UNEXPECTED:
  98. raise error(
  99. "The scode element of the exception tuple did not yield the correct scode",
  100. com_exc,
  101. )
  102. if exc[2] != "Not today":
  103. raise error(
  104. "The description in the exception tuple did not yield the correct string",
  105. com_exc,
  106. )
  107. cap.clear()
  108. try:
  109. cap.capture()
  110. try:
  111. com_server.Commit(0)
  112. finally:
  113. cap.release()
  114. raise error("Expecting this call to fail!")
  115. except pythoncom.com_error as com_exc:
  116. if com_exc.hresult != winerror.DISP_E_EXCEPTION:
  117. raise error(
  118. "Calling the object via IDispatch did not yield the correct scode",
  119. com_exc,
  120. )
  121. exc = com_exc.excepinfo
  122. if not exc or exc[-1] != winerror.E_FAIL:
  123. raise error(
  124. "The scode element of the exception tuple did not yield the correct scode",
  125. com_exc,
  126. )
  127. if exc[1] != "Python COM Server Internal Error":
  128. raise error(
  129. "The description in the exception tuple did not yield the correct string",
  130. com_exc,
  131. )
  132. # Check we saw a traceback in stderr
  133. if cap.get_captured().find("Traceback") < 0:
  134. raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
  135. # And an explicit com_error
  136. cap.clear()
  137. try:
  138. cap.capture()
  139. try:
  140. com_server.Commit(1)
  141. finally:
  142. cap.release()
  143. raise error("Expecting this call to fail!")
  144. except pythoncom.com_error as com_exc:
  145. if com_exc.hresult != winerror.DISP_E_EXCEPTION:
  146. raise error(
  147. "Calling the object via IDispatch did not yield the correct scode",
  148. com_exc,
  149. )
  150. exc = com_exc.excepinfo
  151. if not exc or exc[-1] != winerror.E_FAIL:
  152. raise error(
  153. "The scode element of the exception tuple did not yield the correct scode",
  154. com_exc,
  155. )
  156. if exc[1] != "source":
  157. raise error(
  158. "The source in the exception tuple did not yield the correct string",
  159. com_exc,
  160. )
  161. if exc[2] != "\U0001F600":
  162. raise error(
  163. "The description in the exception tuple did not yield the correct string",
  164. com_exc,
  165. )
  166. if exc[3] != "helpfile":
  167. raise error(
  168. "The helpfile in the exception tuple did not yield the correct string",
  169. com_exc,
  170. )
  171. if exc[4] != 1:
  172. raise error(
  173. "The help context in the exception tuple did not yield the correct string",
  174. com_exc,
  175. )
  176. try:
  177. import logging
  178. except ImportError:
  179. logging = None
  180. if logging is not None:
  181. import win32com
  182. class TestLogHandler(logging.Handler):
  183. def __init__(self):
  184. self.reset()
  185. logging.Handler.__init__(self)
  186. def reset(self):
  187. self.num_emits = 0
  188. self.last_record = None
  189. def emit(self, record):
  190. self.num_emits += 1
  191. self.last_record = self.format(record)
  192. return
  193. print("--- record start")
  194. print(self.last_record)
  195. print("--- record end")
  196. def testLogger():
  197. assert not hasattr(win32com, "logger")
  198. handler = TestLogHandler()
  199. formatter = logging.Formatter("%(message)s")
  200. handler.setFormatter(formatter)
  201. log = logging.getLogger("win32com_test")
  202. log.addHandler(handler)
  203. win32com.logger = log
  204. # Now throw some exceptions!
  205. # Native interfaces
  206. com_server = wrap(TestServer(), pythoncom.IID_IStream)
  207. try:
  208. com_server.Commit(0)
  209. raise RuntimeError("should have failed")
  210. except pythoncom.error as exc:
  211. # `excepinfo` is a tuple with elt 2 being the traceback we captured.
  212. message = exc.excepinfo[2]
  213. assert message.endswith("Exception: \U0001F600\n")
  214. assert handler.num_emits == 1, handler.num_emits
  215. assert handler.last_record.startswith(
  216. "pythoncom error: Unexpected exception in gateway method 'Commit'"
  217. )
  218. handler.reset()
  219. # IDispatch
  220. com_server = Dispatch(wrap(TestServer()))
  221. try:
  222. com_server.Commit(0)
  223. raise RuntimeError("should have failed")
  224. except pythoncom.error as exc:
  225. # `excepinfo` is a tuple with elt 2 being the traceback we captured.
  226. message = exc.excepinfo[2]
  227. assert message.endswith("Exception: \U0001F600\n")
  228. assert handler.num_emits == 1, handler.num_emits
  229. handler.reset()
  230. if __name__ == "__main__":
  231. test()
  232. if logging is not None:
  233. testLogger()
  234. from win32com.test.util import CheckClean
  235. CheckClean()
  236. print("error semantic tests worked")