From 784508ce5cb0e349d349e148f1c37edb09358477 Mon Sep 17 00:00:00 2001 From: junkmd Date: Tue, 14 Jan 2025 22:53:37 +0900 Subject: [PATCH 1/6] `__init__` -> `__new__` --- comtypes/server/register.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/comtypes/server/register.py b/comtypes/server/register.py index ed47b68a..3011cee3 100644 --- a/comtypes/server/register.py +++ b/comtypes/server/register.py @@ -275,16 +275,18 @@ class RegistryEntries(object): IDL library name of the type library containing the coclass. """ - def __init__( - self, - cls: Type, + def __new__( + cls, + typ: Type, *, frozen: Optional[str] = None, frozendllhandle: Optional[int] = None, - ) -> None: - self._cls = cls + ): + self = super(RegistryEntries, cls).__new__(cls) + self._cls = typ self._frozen = frozen self._frozendllhandle = frozendllhandle + return self def __iter__(self) -> Iterator[_Entry]: # that's the only required attribute for registration From 9261f6bdb1b676cb0cb0b044d0821184af8478fe Mon Sep 17 00:00:00 2001 From: junkmd Date: Tue, 14 Jan 2025 22:53:37 +0900 Subject: [PATCH 2/6] Make `RegistryEntries` a dynamic subtype resolving class. --- comtypes/server/register.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/comtypes/server/register.py b/comtypes/server/register.py index 3011cee3..033337ad 100644 --- a/comtypes/server/register.py +++ b/comtypes/server/register.py @@ -43,7 +43,7 @@ import winreg from ctypes import WinDLL, WinError from ctypes.wintypes import HKEY, LONG, LPCWSTR -from typing import Iterator, List, Optional, Tuple, Type, Union +from typing import Any, Iterator, List, Optional, Tuple, Type, Union from comtypes import CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER from comtypes.hresult import TYPE_E_CANTLOADLIBRARY, TYPE_E_REGISTRYACCESS @@ -275,6 +275,13 @@ class RegistryEntries(object): IDL library name of the type library containing the coclass. """ + def __new__(cls, typ: Type, **kwargs: Any): + if not kwargs: + return InterpRegistryEntries.__new__(InterpRegistryEntries, typ) + return FrozenRegistryEntries.__new__(FrozenRegistryEntries, typ, **kwargs) + + +class FrozenRegistryEntries(RegistryEntries): def __new__( cls, typ: Type, @@ -282,7 +289,7 @@ def __new__( frozen: Optional[str] = None, frozendllhandle: Optional[int] = None, ): - self = super(RegistryEntries, cls).__new__(cls) + self = object.__new__(cls) self._cls = typ self._frozen = frozen self._frozendllhandle = frozendllhandle @@ -297,6 +304,19 @@ def __iter__(self) -> Iterator[_Entry]: ) +class InterpRegistryEntries(RegistryEntries): + def __new__(cls, typ: Type): + self = object.__new__(cls) + self._cls = typ + return self + + def __iter__(self) -> Iterator[Tuple[int, str, str, str]]: + # that's the only required attribute for registration + reg_clsid = str(self._cls._reg_clsid_) + yield from _iter_reg_entries(self._cls, reg_clsid) + yield from _iter_ctx_entries(self._cls, reg_clsid) + + def _get_full_classname(cls: Type) -> str: """Return . for 'cls'.""" modname = cls.__module__ @@ -350,7 +370,10 @@ def _iter_reg_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]: def _iter_ctx_entries( - cls: Type, reg_clsid: str, frozen: Optional[str], frozendllhandle: Optional[int] + cls: Type, + reg_clsid: str, + frozen: Optional[str] = None, + frozendllhandle: Optional[int] = None, ) -> Iterator[_Entry]: clsctx: int = getattr(cls, "_reg_clsctx_", 0) localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER) From 7ad90f1e2c96d2d926c29c0178fb3dab8841b104 Mon Sep 17 00:00:00 2001 From: junkmd Date: Tue, 14 Jan 2025 22:53:37 +0900 Subject: [PATCH 3/6] Small fix. --- comtypes/server/register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/comtypes/server/register.py b/comtypes/server/register.py index 033337ad..4b28427e 100644 --- a/comtypes/server/register.py +++ b/comtypes/server/register.py @@ -277,8 +277,8 @@ class RegistryEntries(object): def __new__(cls, typ: Type, **kwargs: Any): if not kwargs: - return InterpRegistryEntries.__new__(InterpRegistryEntries, typ) - return FrozenRegistryEntries.__new__(FrozenRegistryEntries, typ, **kwargs) + return InterpRegistryEntries(typ) + return FrozenRegistryEntries(typ, **kwargs) class FrozenRegistryEntries(RegistryEntries): From b1ca18f2c5a9093818588b41c6ec3cce6befbb5f Mon Sep 17 00:00:00 2001 From: junkmd Date: Tue, 14 Jan 2025 22:53:37 +0900 Subject: [PATCH 4/6] Remove `_iter_ctx_entries`. --- comtypes/server/register.py | 54 ++++++++++++--------------- comtypes/test/test_server_register.py | 14 ++----- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/comtypes/server/register.py b/comtypes/server/register.py index 4b28427e..e7ccb46b 100644 --- a/comtypes/server/register.py +++ b/comtypes/server/register.py @@ -45,9 +45,9 @@ from ctypes.wintypes import HKEY, LONG, LPCWSTR from typing import Any, Iterator, List, Optional, Tuple, Type, Union +import comtypes.server.inprocserver # noqa from comtypes import CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER from comtypes.hresult import TYPE_E_CANTLOADLIBRARY, TYPE_E_REGISTRYACCESS -from comtypes.server.inprocserver import _clsid_to_class from comtypes.server.localserver import run as run_localserver from comtypes.server.w_getopt import w_getopt from comtypes.typeinfo import ( @@ -286,7 +286,7 @@ def __new__( cls, typ: Type, *, - frozen: Optional[str] = None, + frozen: str, frozendllhandle: Optional[int] = None, ): self = object.__new__(cls) @@ -299,9 +299,16 @@ def __iter__(self) -> Iterator[_Entry]: # that's the only required attribute for registration reg_clsid = str(self._cls._reg_clsid_) yield from _iter_reg_entries(self._cls, reg_clsid) - yield from _iter_ctx_entries( - self._cls, reg_clsid, self._frozen, self._frozendllhandle - ) + cls = self._cls + clsctx: int = getattr(cls, "_reg_clsctx_", 0) + localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER) + inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER) + if localsvr_ctx and self._frozendllhandle is None: + yield from _iter_frozen_local_ctx_entries(cls, reg_clsid) + if inprocsvr_ctx and self._frozen == "dll": + yield from _iter_inproc_ctx_entries(cls, reg_clsid, self._frozendllhandle) + yield from _iter_inproc_threading_model_entries(cls, reg_clsid) + yield from _iter_tlib_entries(cls, reg_clsid) class InterpRegistryEntries(RegistryEntries): @@ -314,7 +321,18 @@ def __iter__(self) -> Iterator[Tuple[int, str, str, str]]: # that's the only required attribute for registration reg_clsid = str(self._cls._reg_clsid_) yield from _iter_reg_entries(self._cls, reg_clsid) - yield from _iter_ctx_entries(self._cls, reg_clsid) + cls = self._cls + clsctx: int = getattr(cls, "_reg_clsctx_", 0) + localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER) + inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER) + if localsvr_ctx: + yield from _iter_interp_local_ctx_entries(cls, reg_clsid) + if inprocsvr_ctx: + yield from _iter_inproc_ctx_entries(cls, reg_clsid, None) + # only for non-frozen inproc servers the PythonPath/PythonClass is needed. + yield from _iter_inproc_python_entries(cls, reg_clsid) + yield from _iter_inproc_threading_model_entries(cls, reg_clsid) + yield from _iter_tlib_entries(cls, reg_clsid) def _get_full_classname(cls: Type) -> str: @@ -369,30 +387,6 @@ def _iter_reg_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]: yield (HKCR, f"{reg_novers_progid}\\CLSID", "", reg_clsid) # 3a -def _iter_ctx_entries( - cls: Type, - reg_clsid: str, - frozen: Optional[str] = None, - frozendllhandle: Optional[int] = None, -) -> Iterator[_Entry]: - clsctx: int = getattr(cls, "_reg_clsctx_", 0) - localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER) - inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER) - - if localsvr_ctx and frozendllhandle is None: - if frozen is None: - yield from _iter_interp_local_ctx_entries(cls, reg_clsid) - else: - yield from _iter_frozen_local_ctx_entries(cls, reg_clsid) - if inprocsvr_ctx and frozen in (None, "dll"): - yield from _iter_inproc_ctx_entries(cls, reg_clsid, frozendllhandle) - # only for non-frozen inproc servers the PythonPath/PythonClass is needed. - if frozendllhandle is None or not _clsid_to_class: - yield from _iter_inproc_python_entries(cls, reg_clsid) - yield from _iter_inproc_threading_model_entries(cls, reg_clsid) - yield from _iter_tlib_entries(cls, reg_clsid) - - def _iter_interp_local_ctx_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]: exe = sys.executable exe = f'"{exe}"' if " " in exe else exe diff --git a/comtypes/test/test_server_register.py b/comtypes/test/test_server_register.py index b14c371d..f236bb0f 100644 --- a/comtypes/test/test_server_register.py +++ b/comtypes/test/test_server_register.py @@ -442,7 +442,7 @@ class Cls: self.assertEqual(expected, list(RegistryEntries(Cls, frozen="windows_exe"))) @mock.patch.object(register, "_get_serverdll", return_value=SERVERDLL) - def test_inproc_dll_nonempty_clsid_to_class(self, get_serverdll): + def test_inproc_dll(self, get_serverdll): reg_clsid = GUID.create_new() reg_clsctx = comtypes.CLSCTX_INPROC_SERVER @@ -457,10 +457,8 @@ class Cls: (HKCR, inproc_srv_sub, "", self.SERVERDLL), ] - with mock.patch.dict(comtypes.server.inprocserver._clsid_to_class): - comtypes.server.inprocserver._clsid_to_class.update({5678: Cls}) - entries = RegistryEntries(Cls, frozen="dll", frozendllhandle=1234) - self.assertEqual(expected, list(entries)) + entries = RegistryEntries(Cls, frozen="dll", frozendllhandle=1234) + self.assertEqual(expected, list(entries)) get_serverdll.assert_called_once_with(1234) @mock.patch.object(register, "_get_serverdll", return_value=SERVERDLL) @@ -476,15 +474,9 @@ class Cls: clsid_sub = rf"CLSID\{reg_clsid}" inproc_srv_sub = rf"{clsid_sub}\InprocServer32" - full_classname = f"{__name__}.Cls" expected = [ (HKCR, clsid_sub, "", ""), (HKCR, inproc_srv_sub, "", self.SERVERDLL), - # 'PythonClass' and 'PythonPath' are not required for - # frozen inproc servers. This may be bugs but they do - # not affect the server behavior. - (HKCR, inproc_srv_sub, "PythonClass", full_classname), - (HKCR, inproc_srv_sub, "PythonPath", os.path.dirname(__file__)), (HKCR, inproc_srv_sub, "ThreadingModel", reg_threading), ] self.assertEqual( From 0b851d61da7d572b668f75ead86729382c2d6305 Mon Sep 17 00:00:00 2001 From: junkmd Date: Tue, 14 Jan 2025 22:53:38 +0900 Subject: [PATCH 5/6] Make `RegistryEntries` abstract class. --- comtypes/server/register.py | 78 +++++++++++---------------- comtypes/test/test_server_register.py | 44 ++++++++------- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/comtypes/server/register.py b/comtypes/server/register.py index e7ccb46b..d20b712e 100644 --- a/comtypes/server/register.py +++ b/comtypes/server/register.py @@ -37,13 +37,14 @@ """ import _ctypes +import abc import logging import os import sys import winreg from ctypes import WinDLL, WinError from ctypes.wintypes import HKEY, LONG, LPCWSTR -from typing import Any, Iterator, List, Optional, Tuple, Type, Union +from typing import Iterable, Iterator, List, Optional, Tuple, Type, Union import comtypes.server.inprocserver # noqa from comtypes import CLSCTX_INPROC_SERVER, CLSCTX_LOCAL_SERVER @@ -113,6 +114,11 @@ def __init__(self) -> None: self._frozen = getattr(sys, "frozen", None) self._frozendllhandle = getattr(sys, "frozendllhandle", None) + def _generate_reg_entries(self, cls: Type) -> Iterable[_Entry]: + if self._frozen is None: + return InterpRegistryEntries(cls) + return FrozenRegistryEntries(cls, self._frozen, self._frozendllhandle) + def nodebug(self, cls: Type) -> None: """Delete logging entries from the registry.""" clsid = cls._reg_clsid_ @@ -168,13 +174,7 @@ def register(self, cls: Type, executable: Optional[str] = None) -> None: self._register(cls, executable) def _register(self, cls: Type, executable: Optional[str] = None) -> None: - table = sorted( - RegistryEntries( - cls, - frozen=self._frozen, - frozendllhandle=self._frozendllhandle, - ) - ) + table = sorted(self._generate_reg_entries(cls)) _debug("Registering %s", cls) for hkey, subkey, valuename, value in table: _debug("[%s\\%s]", _explain(hkey), subkey) @@ -210,12 +210,7 @@ def unregister(self, cls: Type, force: bool = False) -> None: def _unregister(self, cls: Type, force: bool = False) -> None: # If force==False, we only remove those entries that we # actually would have written. It seems ATL does the same. - table = [ - t[:2] - for t in RegistryEntries( - cls, frozen=self._frozen, frozendllhandle=self._frozendllhandle - ) - ] + table = [t[:2] for t in self._generate_reg_entries(cls)] # only unique entries table = list(set(table)) table.sort() @@ -253,7 +248,7 @@ def _get_serverdll(handle: Optional[int]) -> str: return _ctypes.__file__ -class RegistryEntries(object): +class RegistryEntries(abc.ABC): """Iterator of tuples containing registry entries. The tuples must be (key, subkey, name, value). @@ -275,64 +270,55 @@ class RegistryEntries(object): IDL library name of the type library containing the coclass. """ - def __new__(cls, typ: Type, **kwargs: Any): - if not kwargs: - return InterpRegistryEntries(typ) - return FrozenRegistryEntries(typ, **kwargs) + @abc.abstractmethod + def __iter__(self) -> Iterator[_Entry]: ... class FrozenRegistryEntries(RegistryEntries): - def __new__( - cls, - typ: Type, - *, + def __init__( + self, + cls: Type, frozen: str, frozendllhandle: Optional[int] = None, - ): - self = object.__new__(cls) - self._cls = typ + ) -> None: + self._cls = cls self._frozen = frozen self._frozendllhandle = frozendllhandle - return self def __iter__(self) -> Iterator[_Entry]: # that's the only required attribute for registration reg_clsid = str(self._cls._reg_clsid_) yield from _iter_reg_entries(self._cls, reg_clsid) - cls = self._cls - clsctx: int = getattr(cls, "_reg_clsctx_", 0) + clsctx: int = getattr(self._cls, "_reg_clsctx_", 0) localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER) inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER) if localsvr_ctx and self._frozendllhandle is None: - yield from _iter_frozen_local_ctx_entries(cls, reg_clsid) + yield from _iter_frozen_local_ctx_entries(self._cls, reg_clsid) if inprocsvr_ctx and self._frozen == "dll": - yield from _iter_inproc_ctx_entries(cls, reg_clsid, self._frozendllhandle) - yield from _iter_inproc_threading_model_entries(cls, reg_clsid) - yield from _iter_tlib_entries(cls, reg_clsid) + yield from _iter_inproc_ctx_entries(reg_clsid, self._frozendllhandle) + yield from _iter_inproc_threading_model_entries(self._cls, reg_clsid) + yield from _iter_tlib_entries(self._cls, reg_clsid) class InterpRegistryEntries(RegistryEntries): - def __new__(cls, typ: Type): - self = object.__new__(cls) - self._cls = typ - return self + def __init__(self, cls: Type) -> None: + self._cls = cls - def __iter__(self) -> Iterator[Tuple[int, str, str, str]]: + def __iter__(self) -> Iterator[_Entry]: # that's the only required attribute for registration reg_clsid = str(self._cls._reg_clsid_) yield from _iter_reg_entries(self._cls, reg_clsid) - cls = self._cls - clsctx: int = getattr(cls, "_reg_clsctx_", 0) + clsctx: int = getattr(self._cls, "_reg_clsctx_", 0) localsvr_ctx = bool(clsctx & CLSCTX_LOCAL_SERVER) inprocsvr_ctx = bool(clsctx & CLSCTX_INPROC_SERVER) if localsvr_ctx: - yield from _iter_interp_local_ctx_entries(cls, reg_clsid) + yield from _iter_interp_local_ctx_entries(self._cls, reg_clsid) if inprocsvr_ctx: - yield from _iter_inproc_ctx_entries(cls, reg_clsid, None) + yield from _iter_inproc_ctx_entries(reg_clsid, None) # only for non-frozen inproc servers the PythonPath/PythonClass is needed. - yield from _iter_inproc_python_entries(cls, reg_clsid) - yield from _iter_inproc_threading_model_entries(cls, reg_clsid) - yield from _iter_tlib_entries(cls, reg_clsid) + yield from _iter_inproc_python_entries(self._cls, reg_clsid) + yield from _iter_inproc_threading_model_entries(self._cls, reg_clsid) + yield from _iter_tlib_entries(self._cls, reg_clsid) def _get_full_classname(cls: Type) -> str: @@ -403,7 +389,7 @@ def _iter_frozen_local_ctx_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry def _iter_inproc_ctx_entries( - cls: Type, reg_clsid: str, frozendllhandle: Optional[int] + reg_clsid: str, frozendllhandle: Optional[int] ) -> Iterator[_Entry]: # Register InprocServer32 only when run from script or from # py2exe dll server, not from py2exe exe server. diff --git a/comtypes/test/test_server_register.py b/comtypes/test/test_server_register.py index f236bb0f..90beec5c 100644 --- a/comtypes/test/test_server_register.py +++ b/comtypes/test/test_server_register.py @@ -9,7 +9,12 @@ import comtypes.server.inprocserver from comtypes import GUID from comtypes.server import register -from comtypes.server.register import Registrar, RegistryEntries, _get_serverdll +from comtypes.server.register import ( + FrozenRegistryEntries, + InterpRegistryEntries, + Registrar, + _get_serverdll, +) HKCR = winreg.HKEY_CLASSES_ROOT MULTI_SZ = winreg.REG_MULTI_SZ @@ -203,7 +208,7 @@ def test_frozen(self, GetModuleFileName): self.assertEqual(260, maxsize) -class Test_NonFrozen_RegistryEntries(ut.TestCase): +class Test_InterpRegistryEntries(ut.TestCase): def test_reg_clsid(self): reg_clsid = GUID.create_new() @@ -211,7 +216,7 @@ class Cls: _reg_clsid_ = reg_clsid expected = [(HKCR, rf"CLSID\{reg_clsid}", "", "")] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_reg_desc(self): reg_clsid = GUID.create_new() @@ -222,7 +227,7 @@ class Cls: _reg_desc_ = reg_desc expected = [(HKCR, rf"CLSID\{reg_clsid}", "", reg_desc)] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_reg_novers_progid(self): reg_clsid = GUID.create_new() @@ -233,7 +238,7 @@ class Cls: _reg_novers_progid_ = reg_novers_progid expected = [(HKCR, rf"CLSID\{reg_clsid}", "", "Lib Server")] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_progid(self): reg_clsid = GUID.create_new() @@ -249,7 +254,7 @@ class Cls: (HKCR, reg_progid, "", "Lib Server 1"), (HKCR, rf"{reg_progid}\CLSID", "", str(reg_clsid)), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_reg_progid_reg_desc(self): reg_clsid = GUID.create_new() @@ -267,7 +272,7 @@ class Cls: (HKCR, reg_progid, "", "description for testing"), (HKCR, rf"{reg_progid}\CLSID", "", str(reg_clsid)), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_reg_progid_reg_novers_progid(self): reg_clsid = GUID.create_new() @@ -290,7 +295,7 @@ class Cls: (HKCR, rf"{reg_novers_progid}\CurVer", "", "Lib.Server.1"), (HKCR, rf"{reg_novers_progid}\CLSID", "", str(reg_clsid)), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_local_server(self): reg_clsid = GUID.create_new() @@ -306,7 +311,7 @@ class Cls: (HKCR, clsid_sub, "", ""), (HKCR, local_srv_sub, "", f"{sys.executable} {__file__}"), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_inproc_server(self): reg_clsid = GUID.create_new() @@ -325,7 +330,7 @@ class Cls: (HKCR, inproc_srv_sub, "PythonClass", full_classname), (HKCR, inproc_srv_sub, "PythonPath", os.path.dirname(__file__)), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_inproc_server_reg_threading(self): reg_clsid = GUID.create_new() @@ -347,7 +352,7 @@ class Cls: (HKCR, inproc_srv_sub, "PythonPath", os.path.dirname(__file__)), (HKCR, inproc_srv_sub, "ThreadingModel", reg_threading), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_reg_typelib(self): reg_clsid = GUID.create_new() @@ -362,7 +367,7 @@ class Cls: (HKCR, rf"CLSID\{reg_clsid}", "", ""), (HKCR, rf"CLSID\{reg_clsid}\Typelib", "", libid), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) def test_all_entries(self): reg_clsid = GUID.create_new() @@ -403,10 +408,10 @@ class Cls: (HKCR, inproc_srv_sub, "ThreadingModel", reg_threading), (HKCR, rf"{clsid_sub}\Typelib", "", libid), ] - self.assertEqual(expected, list(RegistryEntries(Cls))) + self.assertEqual(expected, list(InterpRegistryEntries(Cls))) -class Test_Frozen_RegistryEntries(ut.TestCase): +class Test_FrozenRegistryEntries(ut.TestCase): SERVERDLL = r"my\target\server.dll" # We do not test the scenario where `frozen` is `'dll'` but @@ -424,7 +429,7 @@ class Cls: # In such cases, the server does not start because the # InprocServer32/LocalServer32 keys are not registered. expected = [(HKCR, rf"CLSID\{reg_clsid}", "", "")] - entries = RegistryEntries(Cls, frozen="dll", frozendllhandle=1234) + entries = FrozenRegistryEntries(Cls, frozen="dll", frozendllhandle=1234) self.assertEqual(expected, list(entries)) def test_local_windows_exe(self): @@ -439,7 +444,9 @@ class Cls: (HKCR, rf"CLSID\{reg_clsid}", "", ""), (HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", sys.executable), ] - self.assertEqual(expected, list(RegistryEntries(Cls, frozen="windows_exe"))) + self.assertEqual( + expected, list(FrozenRegistryEntries(Cls, frozen="windows_exe")) + ) @mock.patch.object(register, "_get_serverdll", return_value=SERVERDLL) def test_inproc_dll(self, get_serverdll): @@ -457,7 +464,7 @@ class Cls: (HKCR, inproc_srv_sub, "", self.SERVERDLL), ] - entries = RegistryEntries(Cls, frozen="dll", frozendllhandle=1234) + entries = FrozenRegistryEntries(Cls, frozen="dll", frozendllhandle=1234) self.assertEqual(expected, list(entries)) get_serverdll.assert_called_once_with(1234) @@ -480,6 +487,7 @@ class Cls: (HKCR, inproc_srv_sub, "ThreadingModel", reg_threading), ] self.assertEqual( - expected, list(RegistryEntries(Cls, frozen="dll", frozendllhandle=1234)) + expected, + list(FrozenRegistryEntries(Cls, frozen="dll", frozendllhandle=1234)), ) get_serverdll.assert_called_once_with(1234) From d3154e844817e07e1dbdc6dedd2639ec6c223793 Mon Sep 17 00:00:00 2001 From: junkmd Date: Tue, 14 Jan 2025 22:53:38 +0900 Subject: [PATCH 6/6] Reduce conditional branches for retrieving server dll. --- comtypes/server/register.py | 24 +++++++++--------------- comtypes/test/test_server_register.py | 3 --- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/comtypes/server/register.py b/comtypes/server/register.py index d20b712e..ccffc289 100644 --- a/comtypes/server/register.py +++ b/comtypes/server/register.py @@ -241,11 +241,10 @@ def _unregister(self, cls: Type, force: bool = False) -> None: _debug("Done") -def _get_serverdll(handle: Optional[int]) -> str: +def _get_serverdll(handle: int) -> str: """Return the pathname of the dll hosting the COM object.""" - if handle is not None: - return GetModuleFileName(handle, 260) - return _ctypes.__file__ + assert isinstance(handle, int) + return GetModuleFileName(handle, 260) class RegistryEntries(abc.ABC): @@ -295,7 +294,9 @@ def __iter__(self) -> Iterator[_Entry]: if localsvr_ctx and self._frozendllhandle is None: yield from _iter_frozen_local_ctx_entries(self._cls, reg_clsid) if inprocsvr_ctx and self._frozen == "dll": - yield from _iter_inproc_ctx_entries(reg_clsid, self._frozendllhandle) + assert self._frozendllhandle is not None + frozen_dll = _get_serverdll(self._frozendllhandle) + yield from _iter_inproc_ctx_entries(reg_clsid, frozen_dll) yield from _iter_inproc_threading_model_entries(self._cls, reg_clsid) yield from _iter_tlib_entries(self._cls, reg_clsid) @@ -314,7 +315,7 @@ def __iter__(self) -> Iterator[_Entry]: if localsvr_ctx: yield from _iter_interp_local_ctx_entries(self._cls, reg_clsid) if inprocsvr_ctx: - yield from _iter_inproc_ctx_entries(reg_clsid, None) + yield from _iter_inproc_ctx_entries(reg_clsid, _ctypes.__file__) # only for non-frozen inproc servers the PythonPath/PythonClass is needed. yield from _iter_inproc_python_entries(self._cls, reg_clsid) yield from _iter_inproc_threading_model_entries(self._cls, reg_clsid) @@ -388,17 +389,10 @@ def _iter_frozen_local_ctx_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry yield (HKCR, rf"CLSID\{reg_clsid}\LocalServer32", "", f"{exe}") -def _iter_inproc_ctx_entries( - reg_clsid: str, frozendllhandle: Optional[int] -) -> Iterator[_Entry]: +def _iter_inproc_ctx_entries(reg_clsid: str, dllfile: str) -> Iterator[_Entry]: # Register InprocServer32 only when run from script or from # py2exe dll server, not from py2exe exe server. - yield ( - HKCR, - rf"CLSID\{reg_clsid}\InprocServer32", - "", - _get_serverdll(frozendllhandle), - ) + yield (HKCR, rf"CLSID\{reg_clsid}\InprocServer32", "", dllfile) def _iter_inproc_python_entries(cls: Type, reg_clsid: str) -> Iterator[_Entry]: diff --git a/comtypes/test/test_server_register.py b/comtypes/test/test_server_register.py index 90beec5c..dc3a2b2c 100644 --- a/comtypes/test/test_server_register.py +++ b/comtypes/test/test_server_register.py @@ -195,9 +195,6 @@ def test_calls_cls_unregister(self): class Test_get_serverdll(ut.TestCase): - def test_nonfrozen(self): - self.assertEqual(_ctypes.__file__, _get_serverdll(None)) - @mock.patch.object(register, "GetModuleFileName") def test_frozen(self, GetModuleFileName): handle, dll_path = 1234, r"path\to\frozen.dll"