diff --git a/com/win32com/client/CLSIDToClass.py b/com/win32com/client/CLSIDToClass.py index 88b742e0a..6ca110907 100644 --- a/com/win32com/client/CLSIDToClass.py +++ b/com/win32com/client/CLSIDToClass.py @@ -17,7 +17,9 @@ Access. """ -mapCLSIDToClass = {} +from __future__ import annotations + +mapCLSIDToClass: dict[str, type] = {} def RegisterCLSID(clsid, pythonClass): diff --git a/com/win32com/client/__init__.py b/com/win32com/client/__init__.py index 055b7c9cf..60380a654 100644 --- a/com/win32com/client/__init__.py +++ b/com/win32com/client/__init__.py @@ -5,6 +5,7 @@ # Note that if the unknown dispatch object then returns a known # dispatch object, the known class will be used. This contrasts # with dynamic.Dispatch behaviour, where dynamic objects are always used. +from __future__ import annotations import sys from itertools import chain @@ -263,7 +264,39 @@ def __setattr__(self, attr, val): setattr(self._obj_, attr, val) -def DispatchWithEvents(clsid, user_event_class): +def __get_disp_and_event_classes(dispatch): + # Create/Get the object. + disp = Dispatch(dispatch) + + if disp.__class__.__dict__.get("CLSID"): + return disp.__class__ + + # Eeek - no makepy support - try and build it. + error_msg = "This COM object can not automate the makepy process - please run makepy manually for this object" + try: + ti = disp._oleobj_.GetTypeInfo() + disp_clsid = ti.GetTypeAttr()[0] + tlb, index = ti.GetContainingTypeLib() + tla = tlb.GetLibAttr() + gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0) + # Get the class from the module. + disp_class = gencache.GetClassForProgID(str(disp_clsid)) + except pythoncom.com_error as error: + raise TypeError(error_msg) from error + + if disp_class is None: + raise TypeError(error_msg) + # Get the clsid + clsid = disp_class.CLSID + # Create a new class that derives from 2 classes: + # the event sink class and the user class. + events_class = getevents(clsid) + if events_class is None: + raise ValueError("This COM object does not support events.") + return disp, disp_class, events_class + + +def DispatchWithEvents(clsid, user_event_class) -> EventsProxy: """Create a COM object that can fire events to a user defined class. clsid -- The ProgID or CLSID of the object to create. user_event_class -- A Python class object that responds to the events. @@ -302,39 +335,14 @@ class object that derives from three classes: Visible changed: 1 >>> """ - # Create/Get the object. - disp = Dispatch(clsid) - if not disp.__class__.__dict__.get( - "CLSID" - ): # Eeek - no makepy support - try and build it. - try: - ti = disp._oleobj_.GetTypeInfo() - disp_clsid = ti.GetTypeAttr()[0] - tlb, index = ti.GetContainingTypeLib() - tla = tlb.GetLibAttr() - gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0) - # Get the class from the module. - disp_class = gencache.GetClassForProgID(str(disp_clsid)) - except pythoncom.com_error: - raise TypeError( - "This COM object can not automate the makepy process - please run makepy manually for this object" - ) - else: - disp_class = disp.__class__ - # If the clsid was an object, get the clsid - clsid = disp_class.CLSID - # Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class. - events_class = getevents(clsid) - if events_class is None: - raise ValueError("This COM object does not support events.") + disp, disp_class, events_class = __get_disp_and_event_classes(clsid) result_class = type( "COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__": _event_setattr_}, ) - instance = result_class( - disp._oleobj_ - ) # This only calls the first base class __init__. + # This only calls the first base class __init__. + instance = result_class(disp._oleobj_) events_class.__init__(instance, instance) if hasattr(user_event_class, "__init__"): user_event_class.__init__(instance) @@ -365,33 +373,14 @@ def WithEvents(disp, user_event_class): This is mainly useful where using DispatchWithEvents causes circular reference problems that the simple proxy doesn't deal with """ - disp = Dispatch(disp) - if not disp.__class__.__dict__.get( - "CLSID" - ): # Eeek - no makepy support - try and build it. - try: - ti = disp._oleobj_.GetTypeInfo() - disp_clsid = ti.GetTypeAttr()[0] - tlb, index = ti.GetContainingTypeLib() - tla = tlb.GetLibAttr() - gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0) - # Get the class from the module. - disp_class = gencache.GetClassForProgID(str(disp_clsid)) - except pythoncom.com_error: - raise TypeError( - "This COM object can not automate the makepy process - please run makepy manually for this object" - ) - else: - disp_class = disp.__class__ - # Get the clsid - clsid = disp_class.CLSID - # Create a new class that derives from 2 classes - the event sink - # class and the user class. - events_class = getevents(clsid) - if events_class is None: - raise ValueError("This COM object does not support events.") - result_class = type("COMEventClass", (events_class, user_event_class), {}) - instance = result_class(disp) # This only calls the first base class __init__. + disp, disp_class, events_class = __get_disp_and_event_classes(disp) + result_class = type( + "COMEventClass", + (events_class, user_event_class), + {}, + ) + # This only calls the first base class __init__. + instance = result_class(disp) if hasattr(user_event_class, "__init__"): user_event_class.__init__(instance) return instance @@ -556,7 +545,7 @@ def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *ar def __getattr__(self, attr): args = self._prop_map_get_.get(attr) if args is None: - raise AttributeError(f"'{repr(self)}' object has no attribute '{attr}'") + raise AttributeError(f"'{self!r}' object has no attribute '{attr}'") return self._ApplyTypes_(*args) def __setattr__(self, attr, value): @@ -566,7 +555,7 @@ def __setattr__(self, attr, value): try: args, defArgs = self._prop_map_put_[attr] except KeyError: - raise AttributeError(f"'{repr(self)}' object has no attribute '{attr}'") + raise AttributeError(f"'{self!r}' object has no attribute '{attr}'") self._oleobj_.Invoke(*(args + (value,) + defArgs)) def _get_good_single_object_(self, obj, obUserName=None, resultCLSID=None):