Python 3
I have a COM object via win32com.client.Dispatch with many methods used to automate the application. I'd like it so that every time any method of the COM object is called, python does something based on the returned value (logging actually).
My automation pseudocode is like this:
obj = win32com.client.DispatchEx("3rdParty.Application")
obj.methodA(Parameter)
obj.methodB()
obj.non_method_call
obj.methodN()
...
When any method call is made to obj, I would like what's intended by this pseudocode to happen:
x = obj.any_method(*args)
if x:
logger.debug(any_method.__name__ + ' was called at ' + datetime.now().strftime("%H:%M") + ' with parameters ' + str(*args)
else:
logger.error(obj.cerrmsg)
obj.logout
Note that the any_method.__name__
part would be nice, but is not crucial.
Can python do this, especially with a COM object, and without writing the logic for a finite set of methods?
Decorators sounded right because they modify functions/classes, but posts I've reviewed wouldn't work in this case (like going through the object's method dict), and then I heard they only work on methods defined in my own code. @Property was suggested, but I couldn't work out how to apply it in this case.
Advanced tricks welcome (getattr, metaclasses, functools, wraps, etc.) but please demonstrate.
I tested the approach I mentioned in my comment, against win32com.client and hit a basic problem:
The recursive nature of self.__getattribute__
means that trying to store an object and then use self.stored_obj
fails because it calls __getattribute__
recursively forever.
If you excuse the awful use of a global variable as a hackish way around this, and meaning you can only use this once at a time, it basically works.
i.e. it might be OK for debugging/tracing what's happening with your COM object, but it's not a nice or robust solution:
global _wrapped_object
class LogWrap:
def __init__(self, object):
global _wrapped_object
_wrapped_object = object
def __getattribute__(self, name):
global _wrapped_object
next_hop_attr = _wrapped_object.__getattr__(name)
if callable(next_hop_attr):
def logging_attr(*args, **kwargs):
retval = next_hop_attr(*args, **kwargs)
print("logging a call to {0} with args -{1}-".format(name, ','.join(args)))
print("Returned: {0}".format(retval))
return retval
return logging_attr
else:
return next_hop_attr
def __setattr__(self, name, value):
global _wrapped_object
_wrapped_object.__setattr__(name, value)
from win32com.client import Dispatch
ie = Dispatch('InternetExplorer.Application')
ie = LogWrap(ie)
ie.Visible = True
ie.Navigate2('google.com')
With example output:
>>> logging a call to Navigate2 with args -google.com-
Returned: None
>>>