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.

pyscript.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. """Python ActiveX Scripting Implementation
  2. This module implements the Python ActiveX Scripting client.
  3. To register the implementation, simply "run" this Python program - ie
  4. either double-click on it, or run "python.exe pyscript.py" from the
  5. command line.
  6. """
  7. import re
  8. import pythoncom
  9. import win32api
  10. import win32com
  11. import win32com.client.dynamic
  12. import win32com.server.register
  13. import winerror
  14. from win32com.axscript import axscript
  15. from win32com.axscript.client import framework, scriptdispatch
  16. from win32com.axscript.client.framework import (
  17. SCRIPTTEXT_FORCEEXECUTION,
  18. SCRIPTTEXT_ISEXPRESSION,
  19. SCRIPTTEXT_ISPERSISTENT,
  20. Exception,
  21. RaiseAssert,
  22. trace,
  23. )
  24. PyScript_CLSID = "{DF630910-1C1D-11d0-AE36-8C0F5E000000}"
  25. debugging_attr = 0
  26. def debug_attr_print(*args):
  27. if debugging_attr:
  28. trace(*args)
  29. def ExpandTabs(text):
  30. return re.sub("\t", " ", text)
  31. def AddCR(text):
  32. return re.sub("\n", "\r\n", text)
  33. class AXScriptCodeBlock(framework.AXScriptCodeBlock):
  34. def GetDisplayName(self):
  35. return "PyScript - " + framework.AXScriptCodeBlock.GetDisplayName(self)
  36. # There is only ever _one_ ax object - it exists in the global namespace
  37. # for all script items.
  38. # It performs a search from all global/visible objects
  39. # down.
  40. # This means that if 2 sub-objects of the same name are used
  41. # then only one is ever reachable using the ax shortcut.
  42. class AXScriptAttribute:
  43. "An attribute in a scripts namespace."
  44. def __init__(self, engine):
  45. self.__dict__["_scriptEngine_"] = engine
  46. def __getattr__(self, attr):
  47. if attr[1] == "_" and attr[:-1] == "_":
  48. raise AttributeError(attr)
  49. rc = self._FindAttribute_(attr)
  50. if rc is None:
  51. raise AttributeError(attr)
  52. return rc
  53. def _Close_(self):
  54. self.__dict__["_scriptEngine_"] = None
  55. def _DoFindAttribute_(self, obj, attr):
  56. try:
  57. return obj.subItems[attr.lower()].attributeObject
  58. except KeyError:
  59. pass
  60. # Check out the sub-items
  61. for item in obj.subItems.values():
  62. try:
  63. return self._DoFindAttribute_(item, attr)
  64. except AttributeError:
  65. pass
  66. raise AttributeError(attr)
  67. def _FindAttribute_(self, attr):
  68. for item in self._scriptEngine_.subItems.values():
  69. try:
  70. return self._DoFindAttribute_(item, attr)
  71. except AttributeError:
  72. pass
  73. # All else fails, see if it is a global
  74. # (mainly b/w compat)
  75. return getattr(self._scriptEngine_.globalNameSpaceModule, attr)
  76. # raise AttributeError(attr)
  77. class NamedScriptAttribute:
  78. "An explicitely named object in an objects namespace"
  79. # Each named object holds a reference to one of these.
  80. # Whenever a sub-item appears in a namespace, it is really one of these
  81. # objects. Has a circular reference back to the item itself, which is
  82. # closed via _Close_()
  83. def __init__(self, scriptItem):
  84. self.__dict__["_scriptItem_"] = scriptItem
  85. def __repr__(self):
  86. return "<NamedItemAttribute" + repr(self._scriptItem_) + ">"
  87. def __getattr__(self, attr):
  88. # If a known subitem, return it.
  89. try:
  90. return self._scriptItem_.subItems[attr.lower()].attributeObject
  91. except KeyError:
  92. # Otherwise see if the dispatch can give it to us
  93. if self._scriptItem_.dispatchContainer:
  94. return getattr(self._scriptItem_.dispatchContainer, attr)
  95. raise AttributeError(attr)
  96. def __setattr__(self, attr, value):
  97. # XXX - todo - if a known item, then should call its default
  98. # dispatch method.
  99. attr = attr.lower()
  100. if self._scriptItem_.dispatchContainer:
  101. try:
  102. return setattr(self._scriptItem_.dispatchContainer, attr, value)
  103. except AttributeError:
  104. pass
  105. raise AttributeError(attr)
  106. def _Close_(self):
  107. self.__dict__["_scriptItem_"] = None
  108. class ScriptItem(framework.ScriptItem):
  109. def __init__(self, parentItem, name, dispatch, flags):
  110. framework.ScriptItem.__init__(self, parentItem, name, dispatch, flags)
  111. self.scriptlets = {}
  112. self.attributeObject = None
  113. def Reset(self):
  114. framework.ScriptItem.Reset(self)
  115. if self.attributeObject:
  116. self.attributeObject._Close_()
  117. self.attributeObject = None
  118. def Close(self):
  119. framework.ScriptItem.Close(self) # calls reset.
  120. self.dispatchContainer = None
  121. self.scriptlets = {}
  122. def Register(self):
  123. framework.ScriptItem.Register(self)
  124. self.attributeObject = NamedScriptAttribute(self)
  125. if self.dispatch:
  126. # Need to avoid the new Python "lazy" dispatch behaviour.
  127. try:
  128. engine = self.GetEngine()
  129. olerepr = clsid = None
  130. typeinfo = self.dispatch.GetTypeInfo()
  131. clsid = typeinfo.GetTypeAttr()[0]
  132. try:
  133. olerepr = engine.mapKnownCOMTypes[clsid]
  134. except KeyError:
  135. pass
  136. except pythoncom.com_error:
  137. typeinfo = None
  138. if olerepr is None:
  139. olerepr = win32com.client.dynamic.MakeOleRepr(
  140. self.dispatch, typeinfo, None
  141. )
  142. if clsid is not None:
  143. engine.mapKnownCOMTypes[clsid] = olerepr
  144. self.dispatchContainer = win32com.client.dynamic.CDispatch(
  145. self.dispatch, olerepr, self.name
  146. )
  147. # self.dispatchContainer = win32com.client.dynamic.Dispatch(self.dispatch, userName = self.name)
  148. # self.dispatchContainer = win32com.client.dynamic.DumbDispatch(self.dispatch, userName = self.name)
  149. # def Connect(self):
  150. # framework.ScriptItem.Connect(self)
  151. # def Disconnect(self):
  152. # framework.ScriptItem.Disconnect(self)
  153. class PyScript(framework.COMScript):
  154. # Setup the auto-registration stuff...
  155. _reg_verprogid_ = "Python.AXScript.2"
  156. _reg_progid_ = "Python"
  157. # _reg_policy_spec_ = default
  158. _reg_catids_ = [axscript.CATID_ActiveScript, axscript.CATID_ActiveScriptParse]
  159. _reg_desc_ = "Python ActiveX Scripting Engine"
  160. _reg_clsid_ = PyScript_CLSID
  161. _reg_class_spec_ = "win32com.axscript.client.pyscript.PyScript"
  162. _reg_remove_keys_ = [(".pys",), ("pysFile",)]
  163. _reg_threading_ = "both"
  164. def __init__(self):
  165. framework.COMScript.__init__(self)
  166. self.globalNameSpaceModule = None
  167. self.codeBlocks = []
  168. self.scriptDispatch = None
  169. def InitNew(self):
  170. framework.COMScript.InitNew(self)
  171. import imp
  172. self.scriptDispatch = None
  173. self.globalNameSpaceModule = imp.new_module("__ax_main__")
  174. self.globalNameSpaceModule.__dict__["ax"] = AXScriptAttribute(self)
  175. self.codeBlocks = []
  176. self.persistedCodeBlocks = []
  177. self.mapKnownCOMTypes = {} # Map of known CLSID to typereprs
  178. self.codeBlockCounter = 0
  179. def Stop(self):
  180. # Flag every pending script as already done
  181. for b in self.codeBlocks:
  182. b.beenExecuted = 1
  183. return framework.COMScript.Stop(self)
  184. def Reset(self):
  185. # Reset all code-blocks that are persistent, and discard the rest
  186. oldCodeBlocks = self.codeBlocks[:]
  187. self.codeBlocks = []
  188. for b in oldCodeBlocks:
  189. if b.flags & SCRIPTTEXT_ISPERSISTENT:
  190. b.beenExecuted = 0
  191. self.codeBlocks.append(b)
  192. return framework.COMScript.Reset(self)
  193. def _GetNextCodeBlockNumber(self):
  194. self.codeBlockCounter = self.codeBlockCounter + 1
  195. return self.codeBlockCounter
  196. def RegisterNamedItem(self, item):
  197. wasReg = item.isRegistered
  198. framework.COMScript.RegisterNamedItem(self, item)
  199. if not wasReg:
  200. # Insert into our namespace.
  201. # Add every item by name
  202. if item.IsVisible():
  203. self.globalNameSpaceModule.__dict__[item.name] = item.attributeObject
  204. if item.IsGlobal():
  205. # Global items means sub-items are also added...
  206. for subitem in item.subItems.values():
  207. self.globalNameSpaceModule.__dict__[
  208. subitem.name
  209. ] = subitem.attributeObject
  210. # Also add all methods
  211. for name, entry in item.dispatchContainer._olerepr_.mapFuncs.items():
  212. if not entry.hidden:
  213. self.globalNameSpaceModule.__dict__[name] = getattr(
  214. item.dispatchContainer, name
  215. )
  216. def DoExecutePendingScripts(self):
  217. try:
  218. globs = self.globalNameSpaceModule.__dict__
  219. for codeBlock in self.codeBlocks:
  220. if not codeBlock.beenExecuted:
  221. if self.CompileInScriptedSection(codeBlock, "exec"):
  222. self.ExecInScriptedSection(codeBlock, globs)
  223. finally:
  224. pass
  225. def DoRun(self):
  226. pass
  227. def Close(self):
  228. self.ResetNamespace()
  229. self.globalNameSpaceModule = None
  230. self.codeBlocks = []
  231. self.scriptDispatch = None
  232. framework.COMScript.Close(self)
  233. def GetScriptDispatch(self, name):
  234. # trace("GetScriptDispatch with", name)
  235. # if name is not None: return None
  236. if self.scriptDispatch is None:
  237. self.scriptDispatch = scriptdispatch.MakeScriptDispatch(
  238. self, self.globalNameSpaceModule
  239. )
  240. return self.scriptDispatch
  241. def MakeEventMethodName(self, subItemName, eventName):
  242. return (
  243. subItemName[0].upper()
  244. + subItemName[1:]
  245. + "_"
  246. + eventName[0].upper()
  247. + eventName[1:]
  248. )
  249. def DoAddScriptlet(
  250. self,
  251. defaultName,
  252. code,
  253. itemName,
  254. subItemName,
  255. eventName,
  256. delimiter,
  257. sourceContextCookie,
  258. startLineNumber,
  259. ):
  260. # Just store the code away - compile when called. (JIT :-)
  261. item = self.GetNamedItem(itemName)
  262. if (
  263. itemName == subItemName
  264. ): # Explicit handlers - eg <SCRIPT LANGUAGE="Python" for="TestForm" Event="onSubmit">
  265. subItem = item
  266. else:
  267. subItem = item.GetCreateSubItem(item, subItemName, None, None)
  268. funcName = self.MakeEventMethodName(subItemName, eventName)
  269. codeBlock = AXScriptCodeBlock(
  270. "Script Event %s" % funcName, code, sourceContextCookie, startLineNumber, 0
  271. )
  272. self._AddScriptCodeBlock(codeBlock)
  273. subItem.scriptlets[funcName] = codeBlock
  274. def DoProcessScriptItemEvent(self, item, event, lcid, wFlags, args):
  275. # trace("ScriptItemEvent", self, item, event, event.name, lcid, wFlags, args)
  276. funcName = self.MakeEventMethodName(item.name, event.name)
  277. codeBlock = function = None
  278. try:
  279. function = item.scriptlets[funcName]
  280. if type(function) == type(self): # ie, is a CodeBlock instance
  281. codeBlock = function
  282. function = None
  283. except KeyError:
  284. pass
  285. if codeBlock is not None:
  286. realCode = "def %s():\n" % funcName
  287. for line in framework.RemoveCR(codeBlock.codeText).split("\n"):
  288. realCode = realCode + "\t" + line + "\n"
  289. realCode = realCode + "\n"
  290. if not self.CompileInScriptedSection(codeBlock, "exec", realCode):
  291. return
  292. dict = {}
  293. self.ExecInScriptedSection(
  294. codeBlock, self.globalNameSpaceModule.__dict__, dict
  295. )
  296. function = dict[funcName]
  297. # cache back in scriptlets as a function.
  298. item.scriptlets[funcName] = function
  299. if function is None:
  300. # still no function - see if in the global namespace.
  301. try:
  302. function = self.globalNameSpaceModule.__dict__[funcName]
  303. except KeyError:
  304. # Not there _exactly_ - do case ins search.
  305. funcNameLook = funcName.lower()
  306. for attr in self.globalNameSpaceModule.__dict__.keys():
  307. if funcNameLook == attr.lower():
  308. function = self.globalNameSpaceModule.__dict__[attr]
  309. # cache back in scriptlets, to avoid this overhead next time
  310. item.scriptlets[funcName] = function
  311. if function is None:
  312. raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND)
  313. return self.ApplyInScriptedSection(codeBlock, function, args)
  314. def DoParseScriptText(
  315. self, code, sourceContextCookie, startLineNumber, bWantResult, flags
  316. ):
  317. code = framework.RemoveCR(code) + "\n"
  318. if flags & SCRIPTTEXT_ISEXPRESSION:
  319. name = "Script Expression"
  320. exec_type = "eval"
  321. else:
  322. name = "Script Block"
  323. exec_type = "exec"
  324. num = self._GetNextCodeBlockNumber()
  325. if num == 1:
  326. num = ""
  327. name = "%s %s" % (name, num)
  328. codeBlock = AXScriptCodeBlock(
  329. name, code, sourceContextCookie, startLineNumber, flags
  330. )
  331. self._AddScriptCodeBlock(codeBlock)
  332. globs = self.globalNameSpaceModule.__dict__
  333. if bWantResult: # always immediate.
  334. if self.CompileInScriptedSection(codeBlock, exec_type):
  335. if flags & SCRIPTTEXT_ISEXPRESSION:
  336. return self.EvalInScriptedSection(codeBlock, globs)
  337. else:
  338. return self.ExecInScriptedSection(codeBlock, globs)
  339. # else compile failed, but user chose to keep running...
  340. else:
  341. if flags & SCRIPTTEXT_FORCEEXECUTION:
  342. if self.CompileInScriptedSection(codeBlock, exec_type):
  343. self.ExecInScriptedSection(codeBlock, globs)
  344. else:
  345. self.codeBlocks.append(codeBlock)
  346. def GetNamedItemClass(self):
  347. return ScriptItem
  348. def ResetNamespace(self):
  349. if self.globalNameSpaceModule is not None:
  350. try:
  351. self.globalNameSpaceModule.ax._Reset_()
  352. except AttributeError:
  353. pass # ???
  354. globalNameSpaceModule = None
  355. def DllRegisterServer():
  356. klass = PyScript
  357. win32com.server.register._set_subkeys(
  358. klass._reg_progid_ + "\\OLEScript", {}
  359. ) # Just a CreateKey
  360. # Basic Registration for wsh.
  361. win32com.server.register._set_string(".pys", "pysFile")
  362. win32com.server.register._set_string("pysFile\\ScriptEngine", klass._reg_progid_)
  363. guid_wsh_shellex = "{60254CA5-953B-11CF-8C96-00AA00B8708C}"
  364. win32com.server.register._set_string(
  365. "pysFile\\ShellEx\\DropHandler", guid_wsh_shellex
  366. )
  367. win32com.server.register._set_string(
  368. "pysFile\\ShellEx\\PropertySheetHandlers\\WSHProps", guid_wsh_shellex
  369. )
  370. def Register(klass=PyScript):
  371. ret = win32com.server.register.UseCommandLine(
  372. klass, finalize_register=DllRegisterServer
  373. )
  374. return ret
  375. if __name__ == "__main__":
  376. Register()