Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method annotations (all args and return-values are Any) #490

Merged
merged 3 commits into from
Feb 11, 2024

Conversation

junkmd
Copy link
Collaborator

@junkmd junkmd commented Jun 22, 2023

#400

Type annotations in the codebase of modules generated in the gen directory that can be interpreted by the static type checker are defined under the if TYPE_CHECKING: block.

Changes in generated modules

The IDictionary in the module generated by GetModule("scrrun.dll") is defined as shown below.

class IDictionary(comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0.IDispatch):
    """Scripting.Dictionary Interface"""
    _case_insensitive_ = True
    _iid_ = GUID('{42C642C1-97E1-11CF-978F-00A02463E06F}')
    _idlflags_ = ['hidden', 'dual', 'oleautomation']

    if TYPE_CHECKING:  # commembers
        def _get_Item(self, Key: Any) -> Any: ...
        def _set_Item(self, Key: Any, pRetItem: Any) -> Any: ...
        def _setref_Item(self, Key: Any, pRetItem: Any) -> Any: ...
        Item = hints.named_property('Item', _get_Item, hints.put_or_putref(_set_Item, _setref_Item))
        __call__ = hints.to_dunder_call(Item)
        __getitem__ = hints.to_dunder_getitem(Item)
        __setitem__ = hints.to_dunder_setitem(Item)
        def Add(self, Key: Any, Item: Any) -> Any: ...
        def _get_Count(self) -> Any: ...
        Count = hints.normal_property(_get_Count)
        __len__ = hints.to_dunder_len(Count)
        def Exists(self, Key: Any) -> Any: ...
        def Items(self) -> Any: ...
        def _set_Key(self, Key: Any, rhs: Any) -> Any: ...
        Key = hints.named_property('Key', fset=_set_Key)
        def Keys(self) -> Any: ...
        def Remove(self, Key: Any) -> Any: ...
        def RemoveAll(self) -> Any: ...
        def _get_CompareMode(self) -> Any: ...
        def _set_CompareMode(self, pcomp: Any) -> Any: ...
        CompareMode = hints.normal_property(_get_CompareMode, _set_CompareMode)
        def _NewEnum(self) -> Any: ...
        __iter__ = hints.to_dunder_iter(_NewEnum)
        def _get_HashVal(self, Key: Any) -> Any: ...
        HashVal = hints.named_property('HashVal', _get_HashVal)

The Picture in the module generated by GetModule("stdole2.tlb") is defined as shown below.

class Picture(IDispatch):
    _case_insensitive_ = True
    _iid_ = GUID('{7BF80981-BF32-101A-8BBB-00AA00300CAB}')
    _idlflags_ = []
    _methods_ = []

    if TYPE_CHECKING:  # dispmembers
        @property  # dispprop
        def Handle(self) -> Any: ...
        @property  # dispprop
        def hPal(self) -> Any: ...
        @property  # dispprop
        def Type(self) -> Any: ...
        @property  # dispprop
        def Width(self) -> Any: ...
        @property  # dispprop
        def Height(self) -> Any: ...
        def Render(self, hdc: Any, x: Any, y: Any, cx: Any, cy: Any, xSrc: Any, ySrc: Any, cxSrc: Any, cySrc: Any, prcWBounds: Any) -> Any: ...

Changes in hints.pyi

I have added a lot of type inference magic tools to hints.pyi!

The following type information is conveyed to the static type checker by helpers...

  • Type information about descriptor behavior that could not be expressed by the built-in property or _memberspec.named_property
  • Type information about the behavior of dunder methods patched to com interfaces by metaclasses

These tools use the functionality of the third-party typing_extensions, depending on the version of Python.
They are used in the stub file and under the if TYPE_CHECKING: block, so NOT need to install typing_extensions as runtime prerequisite.

Addition of typeannotator.py

Initially, type annotation stuffs were defined in codegenerator.py.
However, the amount of codebase I added grew so large that it became difficult to see the changes.
So I decided to add a new module with an appropriate name based on responsibility.

Why all args and return-values are Any?

Initially, I tried to do detailed type annotations for return values and arguments as well. However, the process of deriving Python-friendly type annotations from typedesc would require several hundred additional lines of codebase and possibly refactoring/adding functionality to typedesc and tlbparser.
I plan to add functionality for more detailed type annotations than Any after the 1.3.0 release, according to the feedback from the community.

Workarounds

Some COM libraries have implementations that are not compatible with the Python language specifications. These include having required arguments that come after optional arguments, using argument names that conflict with reserved words in Python.

Such method definitions causes SyntaxError even if under the if TYPE_CHECKING: block. To prevent those problems, using *args: Any or **kwargs: Any instead of using the arguments defined by the COM libraries.

# class CLASSNAME_Impl: comments

Since the beginning of this package, the codebase of the modules generated in the gen directory also generates comments, starting with # code template for CLASSNAME implementation, which would have been there to help with a simple wrapper class implementation.

Currently, the mix of comments and type annotations may confuse newcomers.

However, even if there are lack of informations on the behavior of some properties and default arguments, and they are not generated for classes that implement the dispatch method, there may be some projects that rely on the comments to do something.

Therefore, this PR does not eliminate this comment.
The future of the comment should be decided from the community feedback.

junkmd added a commit to junkmd/pywinauto that referenced this pull request Jun 22, 2023
junkmd added a commit to junkmd/pywinauto that referenced this pull request Jun 22, 2023
junkmd added a commit to junkmd/pywinauto that referenced this pull request Jun 23, 2023
junkmd added a commit to junkmd/pywinauto that referenced this pull request Jun 23, 2023
junkmd added a commit to junkmd/pywinauto that referenced this pull request Jun 24, 2023
@junkmd junkmd closed this Jun 24, 2023
@junkmd junkmd reopened this Jun 24, 2023
junkmd added a commit to junkmd/pywinauto that referenced this pull request Jun 24, 2023
@junkmd junkmd added the drop_py2 dev based on supporting only Python3, see #392 label Jun 25, 2023
@junkmd junkmd added this to the 1.3.0 milestone Jun 25, 2023
@junkmd junkmd marked this pull request as ready for review June 25, 2023 03:04
@junkmd
Copy link
Collaborator Author

junkmd commented Jun 25, 2023

@vasily-v-ryabov
Would you agree to include this in the 1.3.0 release?

The large amount of changes and using Python's latest type annotation feature.

Please feel free to ask questions, and any opinions would be appreciated.

@junkmd junkmd closed this Jul 21, 2023
@junkmd junkmd reopened this Jul 21, 2023
@junkmd junkmd closed this Jul 21, 2023
@junkmd junkmd reopened this Jul 21, 2023
junkmd added a commit to junkmd/pywinauto that referenced this pull request Jul 21, 2023
@junkmd junkmd modified the milestones: 1.3.0, 1.3.1 Jan 6, 2024
@junkmd junkmd mentioned this pull request Jan 6, 2024
@junkmd junkmd removed the drop_py2 dev based on supporting only Python3, see #392 label Feb 5, 2024
@junkmd junkmd changed the base branch from drop_py2 to main February 5, 2024 02:14
junkmd added a commit to junkmd/pywinauto that referenced this pull request Feb 6, 2024
@junkmd junkmd linked an issue Feb 10, 2024 that may be closed by this pull request
@junkmd junkmd self-assigned this Feb 11, 2024
@junkmd junkmd merged commit c6f8004 into enthought:main Feb 11, 2024
2 checks passed
@junkmd junkmd deleted the add_func_anno branch February 11, 2024 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add inline type annotations to dynamically generated modules
1 participant