diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e5069f98c5..86f51b9458 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -58,6 +58,16 @@ jobs: python -m win32.scripts.pywin32_postinstall -install -destination "$UserSite" pywin32_postinstall -remove -destination "$UserSite" + # Compilation and registration of the PyCOMTest server dll + - name: Set up MSVC + uses: microsoft/setup-msbuild@v2 + - name: Build and register the PyCOMTest server dll + run: | + cd com/TestSources/PyCOMTest + msbuild .\PyCOMTest.sln -property:Configuration=Release + cd x64/Release + regsvr32 .\PyCOMTest.dll + - name: Run tests # Run the tests directly from the source dir so support files (eg, .wav files etc) # can be found - they aren't installed into the Python tree. diff --git a/.gitignore b/.gitignore index 1ce8634b32..8c5ee60106 100644 --- a/.gitignore +++ b/.gitignore @@ -18,15 +18,23 @@ arm64libs/ pywin32.egg-info/ PyWin32.kpf +# Visual Studio project files +*.sln +*.suo +*.vc*proj* +*.user + # COM test bits com/TestSources/Build com/TestSources/PyCOMTest/PyCOMTest.h -com/TestSources/PyCOMTest/PyCOMTest.sln com/TestSources/PyCOMTest/PyCOMTest.suo com/TestSources/PyCOMTest/PyCOMTest.tlb -com/TestSources/PyCOMTest/PyCOMTest.vc*proj* com/TestSources/PyCOMTest/PyCOMTest_i.c com/TestSources/PyCOMTest/.vs/ +com/TestSources/PyCOMTest/PyCOMTest/ +com/TestSources/PyCOMTest/x64/ +!com/TestSources/PyCOMTest/PyCOMTest.sln +!com/TestSources/PyCOMTest/PyCOMTest.vcxproj # SWIG generated files. com/win32comext/adsi/src/*.cpp @@ -50,12 +58,6 @@ win32/src/win32service_messages.h win32/src/win32evtlog_messages.h isapi/src/pyISAPI_messages.h -# Visual Studio project files -*.sln -*.suo -*.vcproj -*.user - # Eclipse + PyDev .project .pydevproject diff --git a/CHANGES.txt b/CHANGES.txt index 81eacd13b2..8955b85a63 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -13,6 +13,7 @@ https://mhammond.github.io/pywin32_installers.html . Coming in build 311, as yet unreleased -------------------------------------- +* Fixed a regression that broke special __dunder__ methods with CoClass. (#1870, #2493, @Avasam, @geppi) Build 310, released 2025/03/16 ------------------------------ diff --git a/com/TestSources/PyCOMTest/PyCOMTest.idl b/com/TestSources/PyCOMTest/PyCOMTest.idl index db67625015..2d7b42833b 100644 --- a/com/TestSources/PyCOMTest/PyCOMTest.idl +++ b/com/TestSources/PyCOMTest/PyCOMTest.idl @@ -26,7 +26,7 @@ typedef enum // Missing EnumTestAttributes2 uuid(6bcdcb60-5605-11d0-ae5f-cadd4c000000), version(1.1), // an extended character in the help string should stress things... - helpstring("Python COM Test Harness 1.0 Type Library, � pywin32 contributors") + helpstring("Python COM Test Harness 1.0 Type Library, © pywin32 contributors") ] library PyCOMTestLib { @@ -70,7 +70,7 @@ library PyCOMTestLib const long LongTest2 = 0x7FFFFFFFL; const unsigned char UCharTest = 255; const char CharTest = -1; - const LPWSTR StringTest = L"Hello Wo�ld"; + const LPWSTR StringTest = L"Hello Wo®ld"; }; enum TestAttributes3{ // Note missing the enum name. diff --git a/com/TestSources/PyCOMTest/PyCOMTest.sln b/com/TestSources/PyCOMTest/PyCOMTest.sln new file mode 100644 index 0000000000..c420a591f1 --- /dev/null +++ b/com/TestSources/PyCOMTest/PyCOMTest.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35027.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PyCOMTest", "PyCOMTest.vcxproj", "{8EB3046C-6CE8-4537-9B58-6EDD46E6D632}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Debug|x64.ActiveCfg = Debug|x64 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Debug|x64.Build.0 = Debug|x64 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Debug|x86.ActiveCfg = Debug|Win32 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Debug|x86.Build.0 = Debug|Win32 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Release|x64.ActiveCfg = Release|x64 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Release|x64.Build.0 = Release|x64 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Release|x86.ActiveCfg = Release|Win32 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E15B431A-88C2-47F5-B809-AAC25279BD21} + EndGlobalSection +EndGlobal diff --git a/com/TestSources/PyCOMTest/PyCOMTest.vcxproj b/com/TestSources/PyCOMTest/PyCOMTest.vcxproj new file mode 100644 index 0000000000..1c3d45f337 --- /dev/null +++ b/com/TestSources/PyCOMTest/PyCOMTest.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + {8EB3046C-6CE8-4537-9B58-6EDD46E6D632} + Win32Proj + 10.0.22000.0 + + + + DynamicLibrary + true + v143 + NotSet + + + DynamicLibrary + false + v143 + NotSet + + + DynamicLibrary + true + v142 + NotSet + + + DynamicLibrary + false + v142 + NotSet + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + + WIN32;_DEBUG;_WINDOWS;_USRDLL;PYCOMTEST_EXPORTS;/nologo /MDd /W3 /Gm /GX /ZI /Od /D _DEBUG /D WIN32 /D _WINDOWS /D _USRDLL /D _UNICODE /D _WINDLL /D _AFXDLL /D _MBCS /Yupreconn.h /FD /c;%(PreprocessorDefinitions) + Level3 + + + true + Windows + Connect.def + + + + + _DEBUG;_WINDOWS;_USRDLL;PYCOMTEST_EXPORTS;/nologo /MDd /W3 /Gm /GX /ZI /Od /D _DEBUG /D WIN32 /D _WINDOWS /D _USRDLL /D _UNICODE /D _WINDLL /D _AFXDLL /D _MBCS /Yupreconn.h /FD /c;%(PreprocessorDefinitions) + Level3 + + + true + Windows + Connect.def + + + + + WIN32;NDEBUG;_WINDOWS;_USRDLL;PYCOMTEST_EXPORTS;/nologo /MD /W3 /GX /O2 /D NDEBUG /D WIN32 /D _WINDOWS /D _USRDLL /D _UNICODE /D _WINDLL /D _AFXDLL /D _MBCS /Yupreconn.h /FD /c;%(PreprocessorDefinitions) + Level3 + + + true + Windows + true + true + Connect.def + + + + + NDEBUG;_WINDOWS;_USRDLL;PYCOMTEST_EXPORTS;/nologo /MD /W3 /GX /O2 /D NDEBUG /D WIN32 /D _WINDOWS /D _USRDLL /D _UNICODE /D _WINDLL /D _AFXDLL /D _MBCS /Yupreconn.h /FD /c;%(PreprocessorDefinitions) + Level3 + + + true + Windows + true + false + false + Connect.def + + + + + + + + + + + + %(Filename).h + %(Filename).tlb + %(Filename).h + %(Filename).tlb + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/com/win32com/client/__init__.py b/com/win32com/client/__init__.py index ae879590b5..d060fae00d 100644 --- a/com/win32com/client/__init__.py +++ b/com/win32com/client/__init__.py @@ -628,18 +628,7 @@ class CoClassBaseClass: def __init__(self, oobj=None): if oobj is None: oobj = pythoncom.new(self.CLSID) - dispobj = self.__dict__["_dispobj_"] = self.default_interface(oobj) - # See comments below re the special methods. - for maybe in [ - "__call__", - "__str__", - "__int__", - "__iter__", - "__len__", - "__bool__", - ]: - if hasattr(dispobj, maybe): - setattr(self, maybe, getattr(self, "__maybe" + maybe)) + self.__dict__["_dispobj_"] = self.default_interface(oobj) def __repr__(self): return f"" @@ -663,31 +652,29 @@ def __setattr__(self, attr, value): pass self.__dict__[attr] = value - # Special methods don't use __getattr__ etc, so explicitly delegate here. - # Note however, that not all are safe to let bubble up - things like - # `bool(ob)` will break if the object defines __int__ but then raises an - # attribute error - eg, see #1753. - # It depends on what the wrapped COM object actually defines whether these - # will exist on the underlying object, so __init__ explicitly checks if they - # do and if so, wires them up. + # Special methods don't use __getattr__ etc, so explicitly delegate here. + # Some wrapped objects might not have them, but that's OK - the attribute + # error can just bubble up. + # This was initially implemented to address #1699 which did cause a problem + # with bool() in #1753 because the code initially implemented __nonzero__ + # instead of __bool__, which was pointed out in the conclusion of #1870. + def __call__(self, *args, **kwargs): + return self.__dict__["_dispobj_"](*args, **kwargs) - def __maybe__call__(self, *args, **kwargs): - return self.__dict__["_dispobj_"].__call__(*args, **kwargs) + def __str__(self, *args): + return str(self.__dict__["_dispobj_"]) - def __maybe__str__(self, *args): - return self.__dict__["_dispobj_"].__str__(*args) + def __int__(self, *args): + return int(self.__dict__["_dispobj_"]) - def __maybe__int__(self, *args): - return self.__dict__["_dispobj_"].__int__(*args) + def __iter__(self): + return iter(self.__dict__["_dispobj_"]) - def __maybe__iter__(self): - return self.__dict__["_dispobj_"].__iter__() + def __len__(self): + return len(self.__dict__["_dispobj_"]) - def __maybe__len__(self): - return self.__dict__["_dispobj_"].__len__() - - def __maybe__bool__(self): - return self.__dict__["_dispobj_"].__bool__() + def __bool__(self): + return bool(self.__dict__["_dispobj_"]) # A very simple VARIANT class. Only to be used with poorly-implemented COM diff --git a/com/win32com/test/testArrays.py b/com/win32com/test/testArrays.py index aa4ad16af4..0576efe679 100644 --- a/com/win32com/test/testArrays.py +++ b/com/win32com/test/testArrays.py @@ -51,7 +51,7 @@ def _normalize_array(a): class ArrayTest(util.TestCase): def setUp(self): - self.arr = gencache.EnsureDispatch("PyCOMTest.ArrayTest") + self.arr = gencache.EnsureDispatch("PyCOMTest.ArrayTest", bForDemand=False) def tearDown(self): self.arr = None diff --git a/com/win32com/test/testPyComTest.py b/com/win32com/test/testPyComTest.py index f8b0c73733..d525741fc5 100644 --- a/com/win32com/test/testPyComTest.py +++ b/com/win32com/test/testPyComTest.py @@ -36,7 +36,9 @@ from win32com.client import gencache try: - gencache.EnsureModule("{6BCDCB60-5605-11D0-AE5F-CADD4C000000}", 0, 1, 1) + gencache.EnsureModule( + "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}", 0, 1, 1, bForDemand=False + ) except pythoncom.com_error: print("The PyCOMTest module can not be located or generated.") print(importMsg) diff --git a/com/win32com/test/testall.py b/com/win32com/test/testall.py index db30a0a8ee..991dbdf6c8 100644 --- a/com/win32com/test/testall.py +++ b/com/win32com/test/testall.py @@ -178,10 +178,11 @@ def testit(self): custom_test_cases = [ # Level 1 tests. - [], - # Level 2 tests. [ PyCOMTest, + ], + # Level 2 tests. + [ PippoTest, ], # Level 3 tests