diff --git a/comtypes/test/test_typeannotator.py b/comtypes/test/test_typeannotator.py index 1417aa37..33ec7b4a 100644 --- a/comtypes/test/test_typeannotator.py +++ b/comtypes/test/test_typeannotator.py @@ -70,13 +70,13 @@ def test_disp_interface(self): " def ham(self) -> hints.Incomplete: ...\n" " pass # @property # dispprop\n" " pass # avoid using a keyword for def except(self) -> hints.Incomplete: ...\n" # noqa - " def bacon(self, *args: hints.Any, **kwargs: hints.Any, /) -> hints.Incomplete: ...\n" # noqa + " def bacon(self, *args: hints.Any, **kwargs: hints.Any) -> hints.Incomplete: ...\n" # noqa " def _get_spam(self, arg1: hints.Incomplete = ..., /) -> hints.Incomplete: ...\n" # noqa - " def _set_spam(self, arg1: hints.Incomplete = ..., **kwargs: hints.Any, /) -> hints.Incomplete: ...\n" # noqa + " def _set_spam(self, arg1: hints.Incomplete = ..., /, **kwargs: hints.Any) -> hints.Incomplete: ...\n" # noqa " spam = hints.named_property('spam', _get_spam, _set_spam)\n" " pass # avoid using a keyword for def raise(self, foo: hints.Incomplete, bar: hints.Incomplete = ..., /) -> hints.Incomplete: ...\n" # noqa " def _get_def(self, arg1: hints.Incomplete = ..., /) -> hints.Incomplete: ...\n" # noqa - " def _set_def(self, arg1: hints.Incomplete = ..., **kwargs: hints.Any, /) -> hints.Incomplete: ...\n" # noqa + " def _set_def(self, arg1: hints.Incomplete = ..., /, **kwargs: hints.Any) -> hints.Incomplete: ...\n" # noqa " pass # avoid using a keyword for def = hints.named_property('def', _get_def, _set_def)\n" # noqa " def egg(self) -> hints.Incomplete: ..." # noqa ) diff --git a/comtypes/tools/codegenerator/typeannotator.py b/comtypes/tools/codegenerator/typeannotator.py index c0dd6938..9dc54f7a 100644 --- a/comtypes/tools/codegenerator/typeannotator.py +++ b/comtypes/tools/codegenerator/typeannotator.py @@ -277,13 +277,23 @@ class DispMethodAnnotator(_MethodAnnotator[typedesc.DispMethod]): def getvalue(self, name: str) -> str: inargs = [] has_optional = False + # NOTE: Since named parameters are not yet implemented, all arguments + # for the dispmethod (called via `Invoke`) are marked as + # positional-only parameters, introduced in PEP570. + # See also `automation.IDispatch.Invoke`. + # See https://github.com/enthought/comtypes/issues/371 for _, argname, default in self.inarg_specs: if keyword.iskeyword(argname): inargs = ["*args: hints.Any", "**kwargs: hints.Any"] break if default is None: if has_optional: + # Required parameter follows an optional one. # probably propput or propputref + # TODO: After named parameters are supported, + # the positional-only parameter markers + # will be removed. + inargs.append("/") # HACK: Something that goes into this conditional branch # should be a special callback. inargs.append("**kwargs: hints.Any") @@ -292,15 +302,14 @@ def getvalue(self, name: str) -> str: else: inargs.append(f"{argname}: hints.Incomplete = ...") has_optional = True + else: + # TODO: After named parameters are supported, the positional-only + # parameter markers will be removed. + if inargs: + inargs.append("/") out = _to_outtype(self.method.returns) - # NOTE: Since named parameters are not yet implemented, all arguments - # for the dispmethod (called via `Invoke`) are marked as positional-only - # parameters, introduced in PEP570. See also `automation.IDispatch.Invoke`. - # See https://github.com/enthought/comtypes/issues/371 - # TODO: After named parameters are supported, the positional-only parameter - # markers will be removed. if inargs: - content = f"def {name}(self, {', '.join(inargs)}, /) -> {out}: ..." + content = f"def {name}(self, {', '.join(inargs)}) -> {out}: ..." else: content = f"def {name}(self) -> {out}: ..." if keyword.iskeyword(name):