From 40168a8cb99971dd0e4143ad49c508bdea0da9a4 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 25 Jul 2024 13:47:11 -0400 Subject: [PATCH 1/3] Ensure disp_class cannot be None in DispatchWithEvents --- com/win32com/client/__init__.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/com/win32com/client/__init__.py b/com/win32com/client/__init__.py index c0b1a8761..4e8426308 100644 --- a/com/win32com/client/__init__.py +++ b/com/win32com/client/__init__.py @@ -303,9 +303,9 @@ class object that derives from three classes: """ # Create/Get the object. disp = Dispatch(clsid) - if not disp.__class__.__dict__.get( - "CLSID" - ): # Eeek - no makepy support - try and build it. + disp_class = None + 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] @@ -315,11 +315,13 @@ class object that derives from three classes: # 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" - ) + pass else: disp_class = disp.__class__ + if disp_class is None: + raise TypeError( + "This COM object can not automate the makepy process - please run makepy manually for this object" + ) # 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. @@ -554,7 +556,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): @@ -564,7 +566,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): From ca9fe7dc8c5f9ec55cc93b7fc6725d045273b9e7 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Oct 2024 15:59:11 -0400 Subject: [PATCH 2/3] deduplicate code between DispatchWithEvents and WithEvents --- com/win32com/client/CLSIDToClass.py | 4 +- com/win32com/client/__init__.py | 97 ++++++++++++----------------- 2 files changed, 43 insertions(+), 58 deletions(-) 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 573a73430..008a9b38c 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,41 +335,14 @@ class object that derives from three classes: Visible changed: 1 >>> """ - # Create/Get the object. - disp = Dispatch(clsid) - disp_class = None - 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: - pass - else: - disp_class = disp.__class__ - if disp_class is None: - raise TypeError( - "This COM object can not automate the makepy process - please run makepy manually for this object" - ) - # 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) @@ -367,33 +373,10 @@ 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.") + disp, disp_class, events_class = __get_disp_and_event_classes(disp) result_class = type("COMEventClass", (events_class, user_event_class), {}) - instance = result_class(disp) # This only calls the first base class __init__. + # 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 From a89d4d876e30744e101b8f9359b383003665b4ea Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 18 Oct 2024 16:00:26 -0400 Subject: [PATCH 3/3] Mirror result_class line --- com/win32com/client/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/com/win32com/client/__init__.py b/com/win32com/client/__init__.py index 008a9b38c..60380a654 100644 --- a/com/win32com/client/__init__.py +++ b/com/win32com/client/__init__.py @@ -374,7 +374,11 @@ def WithEvents(disp, user_event_class): circular reference problems that the simple proxy doesn't deal with """ disp, disp_class, events_class = __get_disp_and_event_classes(disp) - result_class = type("COMEventClass", (events_class, user_event_class), {}) + 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__"):