I'm attempting dynamic rewrite of pywin32 win32com.client module prior to import, the below seems to work - but I'm not happy with the 6 lines of code for importing (after the last comment). Can anyone recommend a more concise way of creating / importing the resulting module (package)?
import sys, ast, types
import os.path
import importlib.util
import win32com
# import ast
_win32com_client_spec = importlib.util.find_spec("win32com.client")
_win32com_client_ast = ast.parse(open(_win32com_client_spec.origin).read())
(_win32com_client_dispatch_ast,) = (x for x in _win32com_client_ast.body if isinstance(x, ast.FunctionDef) and x.name == 'Dispatch')
# rename Dispatch
_win32com_client_dispatch_ast.name = '_OldDispatch'
# create new Dispatch
_my_dispatch_mod_ast = ast.parse("""
def Dispatch(*args, **kwds):
base_inst = _OldDispatch(*args, **kwds)
spec_inst = _OldDispatch(base_inst)
return spec_inst
""")
(_my_dispatch_ast,) = _my_dispatch_mod_ast.body
# insert new Dispatch in module
_win32com_client_ast.body.insert(_win32com_client_ast.body.index(_win32com_client_dispatch_ast)+1, _my_dispatch_ast)
# import the package
_my_win32com_client_co = compile(_win32com_client_ast, '<patched win32com.client>', 'exec')
_my_win32com_client_mod = types.ModuleType('win32com.client')
_my_win32com_client_mod.__path__ = (os.path.dirname(_win32com_client_spec.origin),)
sys.modules[_my_win32com_client_mod.__name__] = _my_win32com_client_mod
win32com.client = _my_win32com_client_mod
exec(_my_win32com_client_co, _my_win32com_client_mod.__dict__)
further reading of the documentation shows using the
ModuleSpecobject is preferable to usingtypes.ModuleType.ref: https://docs.python.org/3/library/importlib.html#importlib.util.module_from_spec
the resulting code implicitly handles the
__path__for the package, and allows to reuse the spec object throughout. Beware if using the spec origin to show patching as below, the file attribute should be set explicitly as below.The module should be imported prior to being exec'd, this seems to be important for submodule imports to work as found in
gencachemodules e.g.import win32com.client.CLSISToClass: