Skip to content

Commit

Permalink
Merge branch 'main' into fix_issue_524_syntaxerror_and_more
Browse files Browse the repository at this point in the history
  • Loading branch information
junkmd committed Apr 25, 2024
2 parents 9a38a74 + b046b46 commit 9b82e11
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 80 deletions.
14 changes: 12 additions & 2 deletions comtypes/hints.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ if sys.version_info >= (3, 8):
else:
from typing_extensions import Protocol
if sys.version_info >= (3, 10):
from typing import Concatenate, ParamSpec
from typing import Concatenate, ParamSpec, TypeAlias
else:
from typing_extensions import Concatenate, ParamSpec
from typing_extensions import Concatenate, ParamSpec, TypeAlias
if sys.version_info >= (3, 11):
from typing import Self
else:
Expand All @@ -34,6 +34,16 @@ from comtypes.automation import IDispatch as IDispatch, VARIANT as VARIANT
from comtypes.server import IClassFactory as IClassFactory
from comtypes.typeinfo import ITypeInfo as ITypeInfo

Incomplete: TypeAlias = Any
"""The type symbol is used temporarily until the COM library parsers or
code generators are enhanced to annotate detailed type hints.
"""

Hresult: TypeAlias = int
"""The value returned when calling a method with no `[out]` or `[out, retval]`
arguments and with `HRESULT` as its return type in its COM method definition.
"""

class _MethodTypeDesc(Protocol):
arguments: List[Tuple[Any, str, List[str], Optional[Any]]]
idlflags: List[str]
Expand Down
129 changes: 66 additions & 63 deletions comtypes/tools/codegenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,45 +208,42 @@ def _to_arg_definition(
default: _DefValType,
) -> str:
if default is not None:
elms = (idlflags, type_name, arg_name, default)
code = " (%r, %s, '%s', %r)" % elms
code = f" ({idlflags!r}, {type_name}, '{arg_name}', {default!r})"
if len(code) > 80:
code = (
" (\n"
" %r,\n"
" %s,\n"
" '%s',\n"
" %r\n"
f" {idlflags!r},\n"
f" {type_name},\n"
f" '{arg_name}',\n"
f" {default!r}\n"
" )"
) % elms
)
else:
elms = (idlflags, type_name, arg_name)
code = " (%r, %s, '%s')" % elms
code = f" ({idlflags!r}, {type_name}, '{arg_name}')"
if len(code) > 80:
code = (
" (\n"
" %r,\n"
" %s,\n"
" '%s',\n"
f" {idlflags!r},\n"
f" {type_name},\n"
f" '{arg_name}',\n"
" )"
) % elms
)
return code


class ComMethodGenerator(object):
def __init__(self, m: typedesc.ComMethod, isdual: bool) -> None:
self._m = m
self._isdual = isdual
self._stream = io.StringIO()
self.data: List[str] = []
self._to_type_name = TypeNamer()

def generate(self):
# () -> str
def generate(self) -> str:
if not self._m.arguments:
self._make_noargs()
else:
self._make_withargs()
return self._stream.getvalue()
return "\n".join(self.data)

def _get_common_elms(self) -> Tuple[List[_IdlFlagType], str, str]:
idlflags: List[_IdlFlagType] = []
Expand All @@ -261,26 +258,30 @@ def _get_common_elms(self) -> Tuple[List[_IdlFlagType], str, str]:
return (idlflags, type_name, self._m.name)

def _make_noargs(self) -> None:
elms = self._get_common_elms()
code = " COMMETHOD(%r, %s, '%s')," % elms
flags, type_name, member_name = self._get_common_elms()
code = f" COMMETHOD({flags!r}, {type_name}, '{member_name}'),"
if len(code) > 80:
code = (
" COMMETHOD(\n"
" %r,\n"
" %s,\n"
" '%s',\n"
f" {flags!r},\n"
f" {type_name},\n"
f" '{member_name}',\n"
" ),"
) % elms
print(code, file=self._stream)
)
self.data.append(code)

def _make_withargs(self) -> None:
flags, type_name, member_name = self._get_common_elms()
code = (
" COMMETHOD(\n" " %r,\n" " %s,\n" " '%s',"
) % self._get_common_elms()
print(code, file=self._stream)
" COMMETHOD(\n"
f" {flags!r},\n"
f" {type_name},\n"
f" '{member_name}',"
)
self.data.append(code)
arglist = [_to_arg_definition(*i) for i in self._iter_args()]
print(",\n".join(arglist), file=self._stream)
print(" ),", file=self._stream)
self.data.append(",\n".join(arglist))
self.data.append(" ),")

def _iter_args(self) -> Iterator[Tuple[str, str, List[str], _DefValType]]:
for typ, arg_name, _f, _defval in self._m.arguments:
Expand Down Expand Up @@ -341,16 +342,15 @@ def _iter_args(self) -> Iterator[Tuple[str, str, List[str], _DefValType]]:
class DispMethodGenerator(object):
def __init__(self, m: typedesc.DispMethod) -> None:
self._m = m
self._stream = io.StringIO()
self.data: List[str] = []
self._to_type_name = TypeNamer()

def generate(self):
# () -> str
def generate(self) -> str:
if not self._m.arguments:
self._make_noargs()
else:
self._make_withargs()
return self._stream.getvalue()
return "\n".join(self.data)

def _get_common_elms(self) -> Tuple[List[_IdlFlagType], str, str]:
idlflags: List[_IdlFlagType] = []
Expand All @@ -362,26 +362,30 @@ def _get_common_elms(self) -> Tuple[List[_IdlFlagType], str, str]:
return (idlflags, type_name, self._m.name)

def _make_noargs(self) -> None:
elms = self._get_common_elms()
code = " DISPMETHOD(%r, %s, '%s')," % elms
flags, type_name, member_name = self._get_common_elms()
code = f" DISPMETHOD({flags!r}, {type_name}, '{member_name}'),"
if len(code) > 80:
code = (
" DISPMETHOD(\n"
" %r,\n"
" %s,\n"
" '%s',\n"
f" {flags!r},\n"
f" {type_name},\n"
f" '{member_name}',\n"
" ),"
) % elms
print(code, file=self._stream)
)
self.data.append(code)

def _make_withargs(self) -> None:
flags, type_name, member_name = self._get_common_elms()
code = (
" DISPMETHOD(\n" " %r,\n" " %s,\n" " '%s',"
) % self._get_common_elms()
print(code, file=self._stream)
" DISPMETHOD(\n"
f" {flags!r},\n"
f" {type_name},\n"
f" '{member_name}',"
)
self.data.append(code)
arglist = [_to_arg_definition(*i) for i in self._iter_args()]
print(",\n".join(arglist), file=self._stream)
print(" ),", file=self._stream)
self.data.append(",\n".join(arglist))
self.data.append(" ),")

def _iter_args(self) -> Iterator[Tuple[str, str, List[str], _DefValType]]:
for typ, arg_name, idlflags, default in self._m.arguments:
Expand All @@ -394,19 +398,18 @@ def __init__(self, m: typedesc.DispProperty) -> None:
self._m = m
self._to_type_name = TypeNamer()

def generate(self):
# () -> str
elms = self._get_common_elms()
code = " DISPPROPERTY(%r, %s, '%s')," % elms
def generate(self) -> str:
flags, type_name, member_name = self._get_common_elms()
code = f" DISPPROPERTY({flags!r}, {type_name}, '{member_name}'),"
if len(code) > 80:
code = (
" DISPPROPERTY(\n"
" %r,\n"
" %s,\n"
" '%s'\n"
f" {flags!r},\n"
f" {type_name},\n"
f" '{member_name}'\n"
" ),"
) % elms
return code + "\n"
)
return code

def _get_common_elms(self) -> Tuple[List[_IdlFlagType], str, str]:
idlflags: List[_IdlFlagType] = []
Expand Down Expand Up @@ -1149,11 +1152,11 @@ def ComInterfaceHead(self, head: typedesc.ComInterfaceHead) -> None:
print(" return item", file=self.stream)
print(" raise IndexError(index)", file=self.stream)

annotaions = typeannotator.ComInterfaceMembersAnnotator(head.itf).generate()
if annotaions:
annotations = typeannotator.ComInterfaceMembersAnnotator(head.itf).generate()
if annotations:
print(file=self.stream)
print(" if TYPE_CHECKING: # commembers", file=self.stream)
print(annotaions, file=self.stream, end="")
print(annotations, file=self.stream)

print(file=self.stream)
print(file=self.stream)
Expand Down Expand Up @@ -1277,11 +1280,11 @@ def DispInterfaceHead(self, head: typedesc.DispInterfaceHead) -> None:
print(" _idlflags_ = %s" % head.itf.idlflags, file=self.stream)
print(" _methods_ = []", file=self.stream)

annotaions = typeannotator.DispInterfaceMembersAnnotator(head.itf).generate()
if annotaions:
annotations = typeannotator.DispInterfaceMembersAnnotator(head.itf).generate()
if annotations:
print(file=self.stream)
print(" if TYPE_CHECKING: # dispmembers", file=self.stream)
print(annotaions, file=self.stream, end="")
print(annotations, file=self.stream)

print(file=self.stream)
print(file=self.stream)
Expand Down Expand Up @@ -1323,7 +1326,7 @@ def make_ComMethod(self, m: typedesc.ComMethod, isdual: bool) -> None:
if __debug__ and m.doc:
self.imports.add("comtypes", "helpstring")
gen = ComMethodGenerator(m, isdual)
print(gen.generate(), file=self.stream, end="")
print(gen.generate(), file=self.stream)
self.last_item_class = False
for typ, _, _, default in m.arguments:
if isinstance(typ, typedesc.ComInterface):
Expand All @@ -1341,7 +1344,7 @@ def make_DispMethod(self, m: typedesc.DispMethod) -> None:
if __debug__ and m.doc:
self.imports.add("comtypes", "helpstring")
gen = DispMethodGenerator(m)
print(gen.generate(), file=self.stream, end="")
print(gen.generate(), file=self.stream)
self.last_item_class = False
for _, _, _, default in m.arguments:
if default is not None:
Expand All @@ -1353,7 +1356,7 @@ def make_DispProperty(self, prop: typedesc.DispProperty) -> None:
if __debug__ and prop.doc:
self.imports.add("comtypes", "helpstring")
gen = DispPropertyGenerator(prop)
print(gen.generate(), file=self.stream, end="")
print(gen.generate(), file=self.stream)
self.last_item_class = False


Expand Down
32 changes: 17 additions & 15 deletions comtypes/tools/typeannotator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import abc
import io
import keyword
from typing import (
Any,
Expand Down Expand Up @@ -55,7 +54,7 @@ def getvalue(self, name: str) -> str:

class _MethodsAnnotator(abc.ABC, Generic[_T_MTD]):
def __init__(self) -> None:
self.stream = io.StringIO()
self.data: List[str] = []

@abc.abstractmethod
def to_method_annotator(self, method: _T_MTD) -> _MethodAnnotator[_T_MTD]:
Expand Down Expand Up @@ -100,7 +99,7 @@ def generate(self, members: Iterable[_T_MTD]) -> str:
else:
self._define_member(f"pass # what does `{name}` behave?")
self._patch_dunder(name)
return self.stream.getvalue()
return "\n".join(f" {d}" for d in self.data)

def _patch_dunder(self, name: str) -> None:
if name == "Count":
Expand Down Expand Up @@ -141,7 +140,7 @@ def _define_normal_prop(
self._define_member(content)

def _define_member(self, content: str) -> None:
print(f" {content}", file=self.stream)
self.data.append(content)

def _gen_method(self, name: str, mth: _T_MTD) -> None:
self._define_member(self.to_method_annotator(mth).getvalue(name))
Expand Down Expand Up @@ -237,13 +236,13 @@ def getvalue(self, name: str) -> str:
# should be a special callback.
inargs.append("**kwargs: Any")
break
inargs.append(f"{argname}: Any")
inargs.append(f"{argname}: hints.Incomplete")
else:
inargs.append(f"{argname}: Any = ...")
inargs.append(f"{argname}: hints.Incomplete = ...")
has_optional = True
outargs = ["Any" for _ in self._iter_outarg_specs()]
outargs = ["hints.Incomplete" for _ in self._iter_outarg_specs()]
if not outargs:
out = "Any"
out = "hints.Hresult"
elif len(outargs) == 1:
out = outargs[0]
else:
Expand Down Expand Up @@ -280,11 +279,11 @@ def getvalue(self, name: str) -> str:
# should be a special callback.
inargs.append("**kwargs: Any")
break
inargs.append(f"{argname}: Any")
inargs.append(f"{argname}: hints.Incomplete")
else:
inargs.append(f"{argname}: Any = ...")
inargs.append(f"{argname}: hints.Incomplete = ...")
has_optional = True
out = "Any"
out = "hints.Incomplete"
in_ = ("self, " + ", ".join(inargs)) if inargs else "self"
return f"def {name}({in_}) -> {out}: ..."

Expand All @@ -297,7 +296,6 @@ def to_method_annotator(self, m: typedesc.DispMethod) -> DispMethodAnnotator:
class DispInterfaceMembersAnnotator(object):
def __init__(self, itf: typedesc.DispInterface):
self.itf = itf
self.stream = io.StringIO()

def _categorize_members(
self,
Expand All @@ -313,7 +311,11 @@ def _categorize_members(

def generate(self) -> str:
props, methods = self._categorize_members()
property_lines: List[str] = []
for mem in props:
print(" @property # dispprop", file=self.stream)
print(f" def {mem.name}(self) -> Any: ...", file=self.stream)
return self.stream.getvalue() + DispMethodsAnnotator().generate(methods)
property_lines.append("@property # dispprop")
out = "hints.Incomplete"
property_lines.append(f"def {mem.name}(self) -> {out}: ...")
dispprops = "\n".join(f" {p}" for p in property_lines)
dispmethods = DispMethodsAnnotator().generate(methods)
return "\n".join(d for d in (dispprops, dispmethods) if d)

0 comments on commit 9b82e11

Please sign in to comment.