pyscript.py 12 KB

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