Skip to content

Commit 6c1788a

Browse files
vasily-v-ryabovfusentasticusBence Balogh
authored
Restore Python2.7 support without 2to3 fixer + timestamping fix (#259)
* Restore Python2.7 support without 2to3 fixer * Fix faulty logic about timestamping * Amend release notes, add copyright for metaclass decorator * Fix IndexError for empty safearray Co-authored-by: fusentasticus <[email protected]> Co-authored-by: Bence Balogh <[email protected]>
1 parent 77cf6b8 commit 6c1788a

29 files changed

+340
-121
lines changed

CHANGES.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ Comtypes CHANGELOG
33

44
Release 1.1.11
55
-------------
6-
* Fix Python 3 compatibility. Thanks @kdschlosser.
7-
* Fix `tlib_mtime` in the absence of typelib.
6+
* Fix setuptools>=60.0.0 compatibility. Thanks @kdschlosser.
7+
* Fix timestamping for typelib. Thanks @fusentasticus.
88
* Fix 64bit inproc server.
9+
* Drop Python 2.6 support.
10+
* Fix IndexError for empty safearray. Thanks @BALOGHBence.
911

1012
Release 1.1.10
1113
-------------

appveyor.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
image: Visual Studio 2015
1+
image: Visual Studio 2022
22
build: off
33
max_jobs: 3
44

@@ -9,8 +9,6 @@ shallow_clone: true
99

1010
environment:
1111
matrix:
12-
- py: Python26
13-
- py: Python26-x64
1412
- py: Python27
1513
- py: Python27-x64
1614
- py: Python33
@@ -21,6 +19,10 @@ environment:
2119
- py: Python35-x64
2220
- py: Python36
2321
- py: Python36-x64
22+
- py: Python39
23+
- py: Python39-x64
24+
- py: Python310
25+
- py: Python310-x64
2426

2527
test_script:
2628
- C:\%py%\python.exe setup.py install

comtypes/GUID.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ def binary(obj):
88
def binary(obj):
99
return buffer(obj)
1010

11+
if sys.version_info >= (3, 0):
12+
text_type = str
13+
base_text_type = str
14+
else:
15+
text_type = unicode
16+
base_text_type = basestring
17+
1118
BYTE = c_byte
1219
WORD = c_ushort
1320
DWORD = c_ulong
@@ -32,10 +39,10 @@ class GUID(Structure):
3239

3340
def __init__(self, name=None):
3441
if name is not None:
35-
_CLSIDFromString(str(name), byref(self))
42+
_CLSIDFromString(text_type(name), byref(self))
3643

3744
def __repr__(self):
38-
return 'GUID("%s")' % str(self)
45+
return 'GUID("%s")' % text_type(self)
3946

4047
def __unicode__(self):
4148
p = c_wchar_p()
@@ -62,7 +69,7 @@ def __hash__(self):
6269
return hash(binary(self))
6370

6471
def copy(self):
65-
return GUID(str(self))
72+
return GUID(text_type(self))
6673

6774
def from_progid(cls, progid):
6875
"""Get guid from progid, ...
@@ -71,11 +78,11 @@ def from_progid(cls, progid):
7178
progid = progid._reg_clsid_
7279
if isinstance(progid, cls):
7380
return progid
74-
elif isinstance(progid, str):
81+
elif isinstance(progid, base_text_type):
7582
if progid.startswith("{"):
7683
return cls(progid)
7784
inst = cls()
78-
_CLSIDFromProgID(str(progid), byref(inst))
85+
_CLSIDFromProgID(text_type(progid), byref(inst))
7986
return inst
8087
else:
8188
raise TypeError("Cannot construct guid from %r" % progid)

comtypes/__init__.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
# comtypes version numbers follow semver (http://semver.org/) and PEP 440
66
__version__ = "1.1.11"
77

8+
if sys.version_info >= (3, 0):
9+
text_type = str
10+
else:
11+
text_type = unicode
12+
813
import logging
914
class NullHandler(logging.Handler):
1015
"""A Handler that does nothing."""
@@ -488,7 +493,7 @@ def _make_dispmethods(self, methods):
488493
if is_prop:
489494
self.__map_case__[name[5:].lower()] = name[5:]
490495

491-
for (name, nargs), methods in list(properties.items()):
496+
for (name, nargs), methods in properties.items():
492497
# methods contains [propget or None, propput or None, propputref or None]
493498
if methods[1] is not None and methods[2] is not None:
494499
# both propput and propputref.
@@ -655,7 +660,7 @@ def call_with_inout(self_, *args, **kw):
655660
return rescode
656661

657662
rescode = list(rescode)
658-
for outnum, o in list(outargs.items()):
663+
for outnum, o in outargs.items():
659664
rescode[outnum] = o.__ctypes_from_outparam__()
660665
return rescode
661666
return call_with_inout
@@ -670,7 +675,7 @@ def _make_methods(self, methods):
670675
except KeyError:
671676
raise AttributeError("this class must define an _iid_")
672677
else:
673-
iid = str(iid)
678+
iid = text_type(iid)
674679
## if iid in com_interface_registry:
675680
## # Warn when multiple interfaces are defined with identical iids.
676681
## # This would also trigger if we reload() a module that contains
@@ -779,7 +784,7 @@ def _make_methods(self, methods):
779784
self.__map_case__[name[5:].lower()] = name[5:]
780785

781786
# create public properties / attribute accessors
782-
for (name, doc, nargs), methods in list(properties.items()):
787+
for (name, doc, nargs), methods in properties.items():
783788
# methods contains [propget or None, propput or None, propputref or None]
784789
if methods[1] is not None and methods[2] is not None:
785790
# both propput and propputref.
@@ -891,11 +896,51 @@ def __repr__(self):
891896

892897
################################################################
893898

899+
def add_metaclass(metaclass):
900+
"""Class decorator from six.py for creating a class with a metaclass.
901+
902+
Copyright (c) 2010-2020 Benjamin Peterson
903+
904+
Permission is hereby granted, free of charge, to any person obtaining a copy of
905+
this software and associated documentation files (the "Software"), to deal in
906+
the Software without restriction, including without limitation the rights to
907+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
908+
the Software, and to permit persons to whom the Software is furnished to do so,
909+
subject to the following conditions:
910+
911+
The above copyright notice and this permission notice shall be included in all
912+
copies or substantial portions of the Software.
913+
914+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
915+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
916+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
917+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
918+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
919+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
920+
"""
921+
def wrapper(cls):
922+
orig_vars = cls.__dict__.copy()
923+
slots = orig_vars.get('__slots__')
924+
if slots is not None:
925+
if isinstance(slots, text_type):
926+
slots = [slots]
927+
for slots_var in slots:
928+
orig_vars.pop(slots_var)
929+
orig_vars.pop('__dict__', None)
930+
orig_vars.pop('__weakref__', None)
931+
if hasattr(cls, '__qualname__'):
932+
orig_vars['__qualname__'] = cls.__qualname__
933+
return metaclass(cls.__name__, cls.__bases__, orig_vars)
934+
return wrapper
935+
936+
################################################################
937+
894938
class _compointer_meta(type(c_void_p), _cominterface_meta):
895939
"metaclass for COM interface pointer classes"
896940
# no functionality, but needed to avoid a metaclass conflict
897941

898-
class _compointer_base(c_void_p, metaclass=_compointer_meta):
942+
@add_metaclass(_compointer_meta)
943+
class _compointer_base(c_void_p):
899944
"base class for COM interface pointer classes"
900945
def __del__(self, _debug=logger.debug):
901946
"Release the COM refcount we own."
@@ -1016,7 +1061,7 @@ def from_param(cls, value):
10161061
################################################################
10171062
# IDL stuff
10181063

1019-
class helpstring(str):
1064+
class helpstring(text_type):
10201065
"Specifies the helpstring for a COM method or property."
10211066

10221067
class defaultvalue(object):
@@ -1122,7 +1167,8 @@ def COMMETHOD(idlflags, restype, methodname, *argspec):
11221167
################################################################
11231168
# IUnknown, the root of all evil...
11241169

1125-
class IUnknown(object, metaclass=_cominterface_meta):
1170+
@add_metaclass(_cominterface_meta)
1171+
class IUnknown(object):
11261172
"""The most basic COM interface.
11271173
11281174
Each subclasses of IUnknown must define these class attributes:
@@ -1200,7 +1246,7 @@ def CoGetObject(displayname, interface):
12001246
interface = IUnknown
12011247
punk = POINTER(interface)()
12021248
# Do we need a way to specify the BIND_OPTS parameter?
1203-
_ole32.CoGetObject(str(displayname),
1249+
_ole32.CoGetObject(text_type(displayname),
12041250
None,
12051251
byref(interface._iid_),
12061252
byref(punk))
@@ -1379,6 +1425,7 @@ def CoCreateInstanceEx(clsid, interface=None,
13791425

13801426
from comtypes._meta import _coclass_meta
13811427

1382-
class CoClass(COMObject, metaclass=_coclass_meta):
1428+
@add_metaclass(_coclass_meta)
1429+
class CoClass(COMObject):
13831430
pass
13841431
################################################################

comtypes/_comobject.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from _ctypes import CopyComPointer
66
import logging
77
import os
8+
import sys
89

910
from comtypes import COMError, ReturnHRESULT, instancemethod, _encode_idl
1011
from comtypes.errorinfo import ISupportErrorInfo, ReportException, ReportError
@@ -21,6 +22,11 @@
2122
_warning = logger.warning
2223
_error = logger.error
2324

25+
if sys.version_info >= (3, 0):
26+
int_types = (int, )
27+
else:
28+
int_types = (int, long)
29+
2430
################################################################
2531
# COM object implementation
2632

@@ -51,7 +57,7 @@ def winerror(exc):
5157
return exc.hresult
5258
elif isinstance(exc, WindowsError):
5359
code = exc.winerror
54-
if isinstance(code, int):
60+
if isinstance(code, int_types):
5561
return code
5662
# Sometimes, a WindowsError instance has no error code. An access
5763
# violation raised by ctypes has only text, for example. In this
@@ -333,7 +339,10 @@ def run_sta(self):
333339
messageloop.run()
334340

335341
def run_mta(self):
336-
import queue
342+
if sys.version_info >= (3, 0):
343+
import queue
344+
else:
345+
import Queue as queue
337346
self._queue = queue.Queue()
338347
self._queue.get()
339348

comtypes/automation.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import array
33
import datetime
44
import decimal
5+
import sys
56

67
from ctypes import *
78
from ctypes import _Pointer
@@ -19,6 +20,15 @@ class _safearray(object):
1920
from ctypes.wintypes import DWORD, LONG, UINT, VARIANT_BOOL, WCHAR, WORD
2021

2122

23+
if sys.version_info >= (3, 0):
24+
int_types = (int, )
25+
str_types = (str, )
26+
base_text_type = str
27+
else:
28+
int_types = (int, long)
29+
str_types = (unicode, str)
30+
base_text_type = basestring
31+
2232
LCID = DWORD
2333
DISPID = LONG
2434
SCODE = LONG
@@ -219,7 +229,7 @@ def _set_value(self, value):
219229
if value is None:
220230
self.vt = VT_NULL
221231
elif (hasattr(value, '__len__') and len(value) == 0
222-
and not isinstance(value, str)):
232+
and not isinstance(value, base_text_type)):
223233
self.vt = VT_NULL
224234
# since bool is a subclass of int, this check must come before
225235
# the check for int
@@ -229,7 +239,7 @@ def _set_value(self, value):
229239
elif isinstance(value, (int, c_int)):
230240
self.vt = VT_I4
231241
self._.VT_I4 = value
232-
elif isinstance(value, int):
242+
elif isinstance(value, int_types):
233243
u = self._
234244
# try VT_I4 first.
235245
u.VT_I4 = value
@@ -264,7 +274,7 @@ def _set_value(self, value):
264274
elif isinstance(value, (float, c_double)):
265275
self.vt = VT_R8
266276
self._.VT_R8 = value
267-
elif isinstance(value, str):
277+
elif isinstance(value, str_types):
268278
self.vt = VT_BSTR
269279
# do the c_wchar_p auto unicode conversion
270280
self._.c_void_p = _SysAllocStringLen(value, len(value))
@@ -539,7 +549,10 @@ def ChangeType(self, typecode):
539549
VARIANT.empty = VARIANT()
540550
VARIANT.missing = v = VARIANT()
541551
v.vt = VT_ERROR
542-
v._.VT_I4 = 0x80020004
552+
if sys.version_info >= (3, 0):
553+
v._.VT_I4 = 0x80020004
554+
else:
555+
v._.VT_I4 = 0x80020004L
543556
del v
544557

545558
_carg_obj = type(byref(c_int()))
@@ -583,11 +596,18 @@ class IEnumVARIANT(IUnknown):
583596
def __iter__(self):
584597
return self
585598

586-
def __next__(self):
587-
item, fetched = self.Next(1)
588-
if fetched:
589-
return item
590-
raise StopIteration
599+
if sys.version_info >= (3, 0):
600+
def __next__(self):
601+
item, fetched = self.Next(1)
602+
if fetched:
603+
return item
604+
raise StopIteration
605+
else:
606+
def next(self):
607+
item, fetched = self.Next(1)
608+
if fetched:
609+
return item
610+
raise StopIteration
591611

592612
def __getitem__(self, index):
593613
self.Reset()

0 commit comments

Comments
 (0)