Skip to content

Commit

Permalink
Trial and error: For Python 3.13.
Browse files Browse the repository at this point in the history
  • Loading branch information
junkmd committed Sep 23, 2024
1 parent a3a8733 commit ab44f93
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 102 deletions.
70 changes: 50 additions & 20 deletions comtypes/_meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# comtypes._meta helper module
from ctypes import POINTER, c_void_p, cast
import sys

import comtypes

################################################################
Expand Down Expand Up @@ -42,27 +44,55 @@ class _coclass_meta(type):
# POINTER(...) type gets a __ctypes_from_outparam__ method which
# will QueryInterface for the default interface: the first one on
# the coclass' _com_interfaces_ list.
def __new__(cls, name, bases, namespace):
klass = type.__new__(cls, name, bases, namespace)
if bases == (object,):

if sys.version_info >= (3, 13):

def __new__(cls, name, bases, namespace):
klass = type.__new__(cls, name, bases, namespace)
return klass

def __init__(self, name, bases, namespace):
if bases == (object,):
return self
# XXX We should insist that a _reg_clsid_ is present.
if "_reg_clsid_" in namespace:
clsid = namespace["_reg_clsid_"]
comtypes.com_coclass_registry[str(clsid)] = self
PTR = _coclass_pointer_meta(
"POINTER(%s)" % self.__name__,
(self, c_void_p),
{
"__ctypes_from_outparam__": _wrap_coclass,
"from_param": classmethod(_coclass_from_param),
},
)
from ctypes import _pointer_type_cache # type: ignore

_pointer_type_cache[self] = PTR

else:

def __new__(cls, name, bases, namespace):
klass = type.__new__(cls, name, bases, namespace)
if bases == (object,):
return klass
# XXX We should insist that a _reg_clsid_ is present.
if "_reg_clsid_" in namespace:
clsid = namespace["_reg_clsid_"]
comtypes.com_coclass_registry[str(clsid)] = klass
PTR = _coclass_pointer_meta(
"POINTER(%s)" % klass.__name__,
(klass, c_void_p),
{
"__ctypes_from_outparam__": _wrap_coclass,
"from_param": classmethod(_coclass_from_param),
},
)
from ctypes import _pointer_type_cache # type: ignore

_pointer_type_cache[klass] = PTR

return klass
# XXX We should insist that a _reg_clsid_ is present.
if "_reg_clsid_" in namespace:
clsid = namespace["_reg_clsid_"]
comtypes.com_coclass_registry[str(clsid)] = klass
PTR = _coclass_pointer_meta(
"POINTER(%s)" % klass.__name__,
(klass, c_void_p),
{
"__ctypes_from_outparam__": _wrap_coclass,
"from_param": classmethod(_coclass_from_param),
},
)
from ctypes import _pointer_type_cache

_pointer_type_cache[klass] = PTR

return klass


# will not work if we change the order of the two base classes!
Expand Down
259 changes: 177 additions & 82 deletions comtypes/_post_coinit/unknwn.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,96 +66,191 @@ class _cominterface_meta(type):
_com_shutting_down = False

# Creates also a POINTER type for the newly created class.
def __new__(cls, name, bases, namespace):
methods = namespace.pop("_methods_", None)
dispmethods = namespace.pop("_disp_methods_", None)
new_cls = type.__new__(cls, name, bases, namespace)
if sys.version_info >= (3, 13):

if methods is not None:
new_cls._methods_ = methods
if dispmethods is not None:
new_cls._disp_methods_ = dispmethods
def __new__(cls, name, bases, namespace):
methods = namespace.pop("_methods_", None)
dispmethods = namespace.pop("_disp_methods_", None)
new_cls = type.__new__(cls, name, bases, namespace)

# If we sublass a COM interface, for example:
#
# class IDispatch(IUnknown):
# ....
#
# then we need to make sure that POINTER(IDispatch) is a
# subclass of POINTER(IUnknown) because of the way ctypes
# typechecks work.
if bases == (object,):
_ptr_bases = (new_cls, _compointer_base)
else:
_ptr_bases = (new_cls, POINTER(bases[0]))
if methods is not None:
new_cls._methods_ = methods
if dispmethods is not None:
new_cls._disp_methods_ = dispmethods
return new_cls

# The interface 'new_cls' is used as a mixin.
p = type(_compointer_base)(
"POINTER(%s)" % new_cls.__name__,
_ptr_bases,
{"__com_interface__": new_cls, "_needs_com_addref_": None},
)
def __init__(self, name, bases, namespace):
# If we sublass a COM interface, for example:
#
# class IDispatch(IUnknown):
# ....
#
# then we need to make sure that POINTER(IDispatch) is a
# subclass of POINTER(IUnknown) because of the way ctypes
# typechecks work.
if bases == (object,):
_ptr_bases = (self, _compointer_base)
else:
_ptr_bases = (self, POINTER(bases[0]))

from ctypes import _pointer_type_cache
# The interface 'new_cls' is used as a mixin.
p = type(_compointer_base)(
"POINTER(%s)" % self.__name__,
_ptr_bases,
{"__com_interface__": self, "_needs_com_addref_": None},
)

_pointer_type_cache[new_cls] = p
from ctypes import _pointer_type_cache # type: ignore

if new_cls._case_insensitive_:
_pointer_type_cache[self] = p

@patcher.Patch(p)
class CaseInsensitive(object):
# case insensitive attributes for COM methods and properties
def __getattr__(self, name):
"""Implement case insensitive access to methods and properties"""
try:
fixed_name = self.__map_case__[name.lower()]
except KeyError:
if self._case_insensitive_:

@patcher.Patch(p)
class CaseInsensitive(object):
# case insensitive attributes for COM methods and properties
def __getattr__(self, name):
"""Implement case insensitive access to methods and properties"""
try:
fixed_name = self.__map_case__[name.lower()]
except KeyError:
raise AttributeError(name)
if fixed_name != name: # prevent unbounded recursion
return getattr(self, fixed_name)
raise AttributeError(name)
if fixed_name != name: # prevent unbounded recursion
return getattr(self, fixed_name)
raise AttributeError(name)

# __setattr__ is pretty heavy-weight, because it is called for
# EVERY attribute assignment. Settings a non-com attribute
# through this function takes 8.6 usec, while without this
# function it takes 0.7 sec - 12 times slower.
#
# How much faster would this be if implemented in C?
def __setattr__(self, name, value):
"""Implement case insensitive access to methods and properties"""
object.__setattr__(
self, self.__map_case__.get(name.lower(), name), value
)

@patcher.Patch(POINTER(p))
class ReferenceFix(object):
def __setitem__(self, index, value):
# We override the __setitem__ method of the
# POINTER(POINTER(interface)) type, so that the COM
# reference count is managed correctly.
#
# This is so that we can implement COM methods that have to
# return COM pointers more easily and consistent. Instead of
# using CopyComPointer in the method implementation, we can
# simply do:
#
# def GetTypeInfo(self, this, ..., pptinfo):
# if not pptinfo: return E_POINTER
# pptinfo[0] = a_com_interface_pointer
# return S_OK
if index != 0:
# CopyComPointer, which is in _ctypes, does only
# handle an index of 0. This code does what
# CopyComPointer should do if index != 0.
if bool(value):
value.AddRef()
super(POINTER(p), self).__setitem__(index, value)
return
from _ctypes import CopyComPointer

CopyComPointer(value, self)

return new_cls

# __setattr__ is pretty heavy-weight, because it is called for
# EVERY attribute assignment. Settings a non-com attribute
# through this function takes 8.6 usec, while without this
# function it takes 0.7 sec - 12 times slower.
#
# How much faster would this be if implemented in C?
def __setattr__(self, name, value):
"""Implement case insensitive access to methods and properties"""
object.__setattr__(
self, self.__map_case__.get(name.lower(), name), value
)

@patcher.Patch(POINTER(p))
class ReferenceFix(object):
def __setitem__(self, index, value):
# We override the __setitem__ method of the
# POINTER(POINTER(interface)) type, so that the COM
# reference count is managed correctly.
#
# This is so that we can implement COM methods that have to
# return COM pointers more easily and consistent. Instead of
# using CopyComPointer in the method implementation, we can
# simply do:
#
# def GetTypeInfo(self, this, ..., pptinfo):
# if not pptinfo: return E_POINTER
# pptinfo[0] = a_com_interface_pointer
# return S_OK
if index != 0:
# CopyComPointer, which is in _ctypes, does only
# handle an index of 0. This code does what
# CopyComPointer should do if index != 0.
if bool(value):
value.AddRef()
super(POINTER(p), self).__setitem__(index, value)
return
from _ctypes import CopyComPointer

CopyComPointer(value, self)

else:

def __new__(cls, name, bases, namespace):
methods = namespace.pop("_methods_", None)
dispmethods = namespace.pop("_disp_methods_", None)
new_cls = type.__new__(cls, name, bases, namespace)

if methods is not None:
new_cls._methods_ = methods
if dispmethods is not None:
new_cls._disp_methods_ = dispmethods

# If we sublass a COM interface, for example:
#
# class IDispatch(IUnknown):
# ....
#
# then we need to make sure that POINTER(IDispatch) is a
# subclass of POINTER(IUnknown) because of the way ctypes
# typechecks work.
if bases == (object,):
_ptr_bases = (new_cls, _compointer_base)
else:
_ptr_bases = (new_cls, POINTER(bases[0]))

# The interface 'new_cls' is used as a mixin.
p = type(_compointer_base)(
"POINTER(%s)" % new_cls.__name__,
_ptr_bases,
{"__com_interface__": new_cls, "_needs_com_addref_": None},
)

from ctypes import _pointer_type_cache # type: ignore

_pointer_type_cache[new_cls] = p

if new_cls._case_insensitive_:

@patcher.Patch(p)
class CaseInsensitive(object):
# case insensitive attributes for COM methods and properties
def __getattr__(self, name):
"""Implement case insensitive access to methods and properties"""
try:
fixed_name = self.__map_case__[name.lower()]
except KeyError:
raise AttributeError(name)
if fixed_name != name: # prevent unbounded recursion
return getattr(self, fixed_name)
raise AttributeError(name)

# __setattr__ is pretty heavy-weight, because it is called for
# EVERY attribute assignment. Settings a non-com attribute
# through this function takes 8.6 usec, while without this
# function it takes 0.7 sec - 12 times slower.
#
# How much faster would this be if implemented in C?
def __setattr__(self, name, value):
"""Implement case insensitive access to methods and properties"""
object.__setattr__(
self, self.__map_case__.get(name.lower(), name), value
)

@patcher.Patch(POINTER(p))
class ReferenceFix(object):
def __setitem__(self, index, value):
# We override the __setitem__ method of the
# POINTER(POINTER(interface)) type, so that the COM
# reference count is managed correctly.
#
# This is so that we can implement COM methods that have to
# return COM pointers more easily and consistent. Instead of
# using CopyComPointer in the method implementation, we can
# simply do:
#
# def GetTypeInfo(self, this, ..., pptinfo):
# if not pptinfo: return E_POINTER
# pptinfo[0] = a_com_interface_pointer
# return S_OK
if index != 0:
# CopyComPointer, which is in _ctypes, does only
# handle an index of 0. This code does what
# CopyComPointer should do if index != 0.
if bool(value):
value.AddRef()
super(POINTER(p), self).__setitem__(index, value)
return
from _ctypes import CopyComPointer

CopyComPointer(value, self)

return new_cls

def __setattr__(self, name, value):
if name == "_methods_":
Expand Down

0 comments on commit ab44f93

Please sign in to comment.