123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- """Python ActiveX Scripting Implementation
-
- This module implements the Python ActiveX Scripting client.
-
- To register the implementation, simply "run" this Python program - ie
- either double-click on it, or run "python.exe pyscript.py" from the
- command line.
- """
-
- import re
-
- import pythoncom
- import win32api
- import win32com
- import win32com.client.dynamic
- import win32com.server.register
- import winerror
- from win32com.axscript import axscript
- from win32com.axscript.client import framework, scriptdispatch
- from win32com.axscript.client.framework import (
- SCRIPTTEXT_FORCEEXECUTION,
- SCRIPTTEXT_ISEXPRESSION,
- SCRIPTTEXT_ISPERSISTENT,
- Exception,
- RaiseAssert,
- trace,
- )
-
- PyScript_CLSID = "{DF630910-1C1D-11d0-AE36-8C0F5E000000}"
-
- debugging_attr = 0
-
-
- def debug_attr_print(*args):
- if debugging_attr:
- trace(*args)
-
-
- def ExpandTabs(text):
- return re.sub("\t", " ", text)
-
-
- def AddCR(text):
- return re.sub("\n", "\r\n", text)
-
-
- class AXScriptCodeBlock(framework.AXScriptCodeBlock):
- def GetDisplayName(self):
- return "PyScript - " + framework.AXScriptCodeBlock.GetDisplayName(self)
-
-
- # There is only ever _one_ ax object - it exists in the global namespace
- # for all script items.
- # It performs a search from all global/visible objects
- # down.
- # This means that if 2 sub-objects of the same name are used
- # then only one is ever reachable using the ax shortcut.
- class AXScriptAttribute:
- "An attribute in a scripts namespace."
-
- def __init__(self, engine):
- self.__dict__["_scriptEngine_"] = engine
-
- def __getattr__(self, attr):
- if attr[1] == "_" and attr[:-1] == "_":
- raise AttributeError(attr)
- rc = self._FindAttribute_(attr)
- if rc is None:
- raise AttributeError(attr)
- return rc
-
- def _Close_(self):
- self.__dict__["_scriptEngine_"] = None
-
- def _DoFindAttribute_(self, obj, attr):
- try:
- return obj.subItems[attr.lower()].attributeObject
- except KeyError:
- pass
- # Check out the sub-items
- for item in obj.subItems.values():
- try:
- return self._DoFindAttribute_(item, attr)
- except AttributeError:
- pass
- raise AttributeError(attr)
-
- def _FindAttribute_(self, attr):
- for item in self._scriptEngine_.subItems.values():
- try:
- return self._DoFindAttribute_(item, attr)
- except AttributeError:
- pass
- # All else fails, see if it is a global
- # (mainly b/w compat)
- return getattr(self._scriptEngine_.globalNameSpaceModule, attr)
-
-
- # raise AttributeError(attr)
-
-
- class NamedScriptAttribute:
- "An explicitely named object in an objects namespace"
-
- # Each named object holds a reference to one of these.
- # Whenever a sub-item appears in a namespace, it is really one of these
- # objects. Has a circular reference back to the item itself, which is
- # closed via _Close_()
- def __init__(self, scriptItem):
- self.__dict__["_scriptItem_"] = scriptItem
-
- def __repr__(self):
- return "<NamedItemAttribute" + repr(self._scriptItem_) + ">"
-
- def __getattr__(self, attr):
- # If a known subitem, return it.
- try:
- return self._scriptItem_.subItems[attr.lower()].attributeObject
- except KeyError:
- # Otherwise see if the dispatch can give it to us
- if self._scriptItem_.dispatchContainer:
- return getattr(self._scriptItem_.dispatchContainer, attr)
- raise AttributeError(attr)
-
- def __setattr__(self, attr, value):
- # XXX - todo - if a known item, then should call its default
- # dispatch method.
- attr = attr.lower()
- if self._scriptItem_.dispatchContainer:
- try:
- return setattr(self._scriptItem_.dispatchContainer, attr, value)
- except AttributeError:
- pass
- raise AttributeError(attr)
-
- def _Close_(self):
- self.__dict__["_scriptItem_"] = None
-
-
- class ScriptItem(framework.ScriptItem):
- def __init__(self, parentItem, name, dispatch, flags):
- framework.ScriptItem.__init__(self, parentItem, name, dispatch, flags)
- self.scriptlets = {}
- self.attributeObject = None
-
- def Reset(self):
- framework.ScriptItem.Reset(self)
- if self.attributeObject:
- self.attributeObject._Close_()
- self.attributeObject = None
-
- def Close(self):
- framework.ScriptItem.Close(self) # calls reset.
- self.dispatchContainer = None
- self.scriptlets = {}
-
- def Register(self):
- framework.ScriptItem.Register(self)
- self.attributeObject = NamedScriptAttribute(self)
- if self.dispatch:
- # Need to avoid the new Python "lazy" dispatch behaviour.
- try:
- engine = self.GetEngine()
- olerepr = clsid = None
- typeinfo = self.dispatch.GetTypeInfo()
- clsid = typeinfo.GetTypeAttr()[0]
- try:
- olerepr = engine.mapKnownCOMTypes[clsid]
- except KeyError:
- pass
- except pythoncom.com_error:
- typeinfo = None
- if olerepr is None:
- olerepr = win32com.client.dynamic.MakeOleRepr(
- self.dispatch, typeinfo, None
- )
- if clsid is not None:
- engine.mapKnownCOMTypes[clsid] = olerepr
- self.dispatchContainer = win32com.client.dynamic.CDispatch(
- self.dispatch, olerepr, self.name
- )
-
-
- # self.dispatchContainer = win32com.client.dynamic.Dispatch(self.dispatch, userName = self.name)
- # self.dispatchContainer = win32com.client.dynamic.DumbDispatch(self.dispatch, userName = self.name)
-
- # def Connect(self):
- # framework.ScriptItem.Connect(self)
- # def Disconnect(self):
- # framework.ScriptItem.Disconnect(self)
-
-
- class PyScript(framework.COMScript):
- # Setup the auto-registration stuff...
- _reg_verprogid_ = "Python.AXScript.2"
- _reg_progid_ = "Python"
- # _reg_policy_spec_ = default
- _reg_catids_ = [axscript.CATID_ActiveScript, axscript.CATID_ActiveScriptParse]
- _reg_desc_ = "Python ActiveX Scripting Engine"
- _reg_clsid_ = PyScript_CLSID
- _reg_class_spec_ = "win32com.axscript.client.pyscript.PyScript"
- _reg_remove_keys_ = [(".pys",), ("pysFile",)]
- _reg_threading_ = "both"
-
- def __init__(self):
- framework.COMScript.__init__(self)
- self.globalNameSpaceModule = None
- self.codeBlocks = []
- self.scriptDispatch = None
-
- def InitNew(self):
- framework.COMScript.InitNew(self)
- import imp
-
- self.scriptDispatch = None
- self.globalNameSpaceModule = imp.new_module("__ax_main__")
- self.globalNameSpaceModule.__dict__["ax"] = AXScriptAttribute(self)
-
- self.codeBlocks = []
- self.persistedCodeBlocks = []
- self.mapKnownCOMTypes = {} # Map of known CLSID to typereprs
- self.codeBlockCounter = 0
-
- def Stop(self):
- # Flag every pending script as already done
- for b in self.codeBlocks:
- b.beenExecuted = 1
- return framework.COMScript.Stop(self)
-
- def Reset(self):
- # Reset all code-blocks that are persistent, and discard the rest
- oldCodeBlocks = self.codeBlocks[:]
- self.codeBlocks = []
- for b in oldCodeBlocks:
- if b.flags & SCRIPTTEXT_ISPERSISTENT:
- b.beenExecuted = 0
- self.codeBlocks.append(b)
- return framework.COMScript.Reset(self)
-
- def _GetNextCodeBlockNumber(self):
- self.codeBlockCounter = self.codeBlockCounter + 1
- return self.codeBlockCounter
-
- def RegisterNamedItem(self, item):
- wasReg = item.isRegistered
- framework.COMScript.RegisterNamedItem(self, item)
- if not wasReg:
- # Insert into our namespace.
- # Add every item by name
- if item.IsVisible():
- self.globalNameSpaceModule.__dict__[item.name] = item.attributeObject
- if item.IsGlobal():
- # Global items means sub-items are also added...
- for subitem in item.subItems.values():
- self.globalNameSpaceModule.__dict__[
- subitem.name
- ] = subitem.attributeObject
- # Also add all methods
- for name, entry in item.dispatchContainer._olerepr_.mapFuncs.items():
- if not entry.hidden:
- self.globalNameSpaceModule.__dict__[name] = getattr(
- item.dispatchContainer, name
- )
-
- def DoExecutePendingScripts(self):
- try:
- globs = self.globalNameSpaceModule.__dict__
- for codeBlock in self.codeBlocks:
- if not codeBlock.beenExecuted:
- if self.CompileInScriptedSection(codeBlock, "exec"):
- self.ExecInScriptedSection(codeBlock, globs)
- finally:
- pass
-
- def DoRun(self):
- pass
-
- def Close(self):
- self.ResetNamespace()
- self.globalNameSpaceModule = None
- self.codeBlocks = []
- self.scriptDispatch = None
- framework.COMScript.Close(self)
-
- def GetScriptDispatch(self, name):
- # trace("GetScriptDispatch with", name)
- # if name is not None: return None
- if self.scriptDispatch is None:
- self.scriptDispatch = scriptdispatch.MakeScriptDispatch(
- self, self.globalNameSpaceModule
- )
- return self.scriptDispatch
-
- def MakeEventMethodName(self, subItemName, eventName):
- return (
- subItemName[0].upper()
- + subItemName[1:]
- + "_"
- + eventName[0].upper()
- + eventName[1:]
- )
-
- def DoAddScriptlet(
- self,
- defaultName,
- code,
- itemName,
- subItemName,
- eventName,
- delimiter,
- sourceContextCookie,
- startLineNumber,
- ):
- # Just store the code away - compile when called. (JIT :-)
- item = self.GetNamedItem(itemName)
- if (
- itemName == subItemName
- ): # Explicit handlers - eg <SCRIPT LANGUAGE="Python" for="TestForm" Event="onSubmit">
- subItem = item
- else:
- subItem = item.GetCreateSubItem(item, subItemName, None, None)
- funcName = self.MakeEventMethodName(subItemName, eventName)
-
- codeBlock = AXScriptCodeBlock(
- "Script Event %s" % funcName, code, sourceContextCookie, startLineNumber, 0
- )
- self._AddScriptCodeBlock(codeBlock)
- subItem.scriptlets[funcName] = codeBlock
-
- def DoProcessScriptItemEvent(self, item, event, lcid, wFlags, args):
- # trace("ScriptItemEvent", self, item, event, event.name, lcid, wFlags, args)
- funcName = self.MakeEventMethodName(item.name, event.name)
- codeBlock = function = None
- try:
- function = item.scriptlets[funcName]
- if type(function) == type(self): # ie, is a CodeBlock instance
- codeBlock = function
- function = None
- except KeyError:
- pass
- if codeBlock is not None:
- realCode = "def %s():\n" % funcName
- for line in framework.RemoveCR(codeBlock.codeText).split("\n"):
- realCode = realCode + "\t" + line + "\n"
- realCode = realCode + "\n"
- if not self.CompileInScriptedSection(codeBlock, "exec", realCode):
- return
- dict = {}
- self.ExecInScriptedSection(
- codeBlock, self.globalNameSpaceModule.__dict__, dict
- )
- function = dict[funcName]
- # cache back in scriptlets as a function.
- item.scriptlets[funcName] = function
- if function is None:
- # still no function - see if in the global namespace.
- try:
- function = self.globalNameSpaceModule.__dict__[funcName]
- except KeyError:
- # Not there _exactly_ - do case ins search.
- funcNameLook = funcName.lower()
- for attr in self.globalNameSpaceModule.__dict__.keys():
- if funcNameLook == attr.lower():
- function = self.globalNameSpaceModule.__dict__[attr]
- # cache back in scriptlets, to avoid this overhead next time
- item.scriptlets[funcName] = function
-
- if function is None:
- raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND)
- return self.ApplyInScriptedSection(codeBlock, function, args)
-
- def DoParseScriptText(
- self, code, sourceContextCookie, startLineNumber, bWantResult, flags
- ):
- code = framework.RemoveCR(code) + "\n"
- if flags & SCRIPTTEXT_ISEXPRESSION:
- name = "Script Expression"
- exec_type = "eval"
- else:
- name = "Script Block"
- exec_type = "exec"
- num = self._GetNextCodeBlockNumber()
- if num == 1:
- num = ""
- name = "%s %s" % (name, num)
- codeBlock = AXScriptCodeBlock(
- name, code, sourceContextCookie, startLineNumber, flags
- )
- self._AddScriptCodeBlock(codeBlock)
- globs = self.globalNameSpaceModule.__dict__
- if bWantResult: # always immediate.
- if self.CompileInScriptedSection(codeBlock, exec_type):
- if flags & SCRIPTTEXT_ISEXPRESSION:
- return self.EvalInScriptedSection(codeBlock, globs)
- else:
- return self.ExecInScriptedSection(codeBlock, globs)
-
- # else compile failed, but user chose to keep running...
- else:
- if flags & SCRIPTTEXT_FORCEEXECUTION:
- if self.CompileInScriptedSection(codeBlock, exec_type):
- self.ExecInScriptedSection(codeBlock, globs)
- else:
- self.codeBlocks.append(codeBlock)
-
- def GetNamedItemClass(self):
- return ScriptItem
-
- def ResetNamespace(self):
- if self.globalNameSpaceModule is not None:
- try:
- self.globalNameSpaceModule.ax._Reset_()
- except AttributeError:
- pass # ???
- globalNameSpaceModule = None
-
-
- def DllRegisterServer():
- klass = PyScript
- win32com.server.register._set_subkeys(
- klass._reg_progid_ + "\\OLEScript", {}
- ) # Just a CreateKey
- # Basic Registration for wsh.
- win32com.server.register._set_string(".pys", "pysFile")
- win32com.server.register._set_string("pysFile\\ScriptEngine", klass._reg_progid_)
- guid_wsh_shellex = "{60254CA5-953B-11CF-8C96-00AA00B8708C}"
- win32com.server.register._set_string(
- "pysFile\\ShellEx\\DropHandler", guid_wsh_shellex
- )
- win32com.server.register._set_string(
- "pysFile\\ShellEx\\PropertySheetHandlers\\WSHProps", guid_wsh_shellex
- )
-
-
- def Register(klass=PyScript):
- ret = win32com.server.register.UseCommandLine(
- klass, finalize_register=DllRegisterServer
- )
- return ret
-
-
- if __name__ == "__main__":
- Register()
|