enthought / comtypes

A pure Python, lightweight COM client and server framework, based on the ctypes Python FFI package.
Other
290 stars 96 forks source link

comtypes.client.GetModule fails on QWidget based tlb #588

Closed asamitov closed 1 month ago

asamitov commented 1 month ago

Latest comtypes 1.4.4 and 1.4.5 fails on call comtypes.client.GetModule("some.tlb") if tlb is based on QWidget which contains the method https://doc.qt.io/qt-6/qwidget.html#raise

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python312\Lib\site-packages\comtypes\client\_generate.py", line 128, in GetModule
    return ModuleGenerator(tlib, pathname).generate()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\site-packages\comtypes\client\_generate.py", line 245, in generate
    return [_create_module(name, code) for (name, code) in codebases][-1]
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\site-packages\comtypes\client\_generate.py", line 217, in _create_module
    return _my_import(modulename)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\site-packages\comtypes\client\_generate.py", line 28, in _my_import
    return importlib.import_module(fullname)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python312\Lib\importlib\__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 991, in exec_module
  File "<frozen importlib._bootstrap_external>", line 1129, in get_code
  File "<frozen importlib._bootstrap_external>", line 1059, in source_to_code
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "C:\Python312\Lib\site-packages\comtypes\gen\_EC1E025D_12CC_4153_AC7C_DF848BE241D1_0_1_0.py", line 232
    def raise(self) -> hints.Incomplete: ...
        ^^^^^
SyntaxError: invalid syntax

There is no issue on comtypes 1.1.7 where doesn't use typing.TYPE_CHECKING.

Is it possible set option to disable typing.TYPE_CHECKING in the latest comtypes version?

forderud commented 1 month ago

I'm also struggling with the same regression.

junkmd commented 1 month ago

@asamitov @forderud

Thank you for the bug report.

Python raises a SyntaxError if proper syntax is not used, even within a TYPE_CHECKING block that is not executed at runtime. This contrasts with the fact that NameError or AttributeError would not occur within a TYPE_CHECKING block.

Type annotations are important for modern Python programming. However, we should avoid making this package non-functional.

I have rewritten typeannotator so that if a Python reserved word is used as a member name, it is replaced with pass and a comment. This should prevent SyntaxError from occurring at runtime.

https://github.com/junkmd/comtypes/tree/avoid_keywords_named_methods_syntax_error (diff: https://github.com/enthought/comtypes/compare/b2da379...35f3336)

I do not have a COM type library to reproduce your situation, so please install the above changes in your environment and test it.

Also, if you have a method to reproduce the situation, please let me know.

junkmd commented 1 month ago

Is it possible set option to disable typing.TYPE_CHECKING in the latest comtypes version?

That option is not provided in comtypes. There are no plans to implement the option at the moment.

asamitov commented 1 month ago

Thank @junkmd for so fast response. I can confirm that the provided fix solves this issue.

https://github.com/junkmd/comtypes/tree/avoid_keywords_named_methods_syntax_error (diff: b2da379...35f3336)

I'm preparing a small reproducer for you. Will attach soon.

junkmd commented 1 month ago

You're welcome. Thank you for your fast feedback as well.

I'm preparing a small reproducer for you. Will attach soon.

I greatly appreciate this offer.

I have come up with some ideas for unit tests to prevent regression.

I would like to review your reproducer and consider whether it can also be used for these tests.

asamitov commented 1 month ago

Here is 7zip archive with precompiled binaries to reproduce: simple_bin.zip Python code to reproduce:

import comtypes.client
comtypes.client.gen_dir = None
comtypes.gen.__path__ = ['']
comtypes.client.GetModule("simpleax.tlb")

and source code to local build: simple_source.zip

Reproducer is based on https://doc.qt.io/qt-6/activeqt-activeqt-simple-example.html without code modification, only modified CMake to registrar the target as a post-build event and generate tlb.

Note: I have to change file extension from 7z to zip to be able upload to github.

junkmd commented 1 month ago

Here is 7zip archive with precompiled binaries to reproduce: simple_bin.zip Python code to reproduce:

import comtypes.client
comtypes.client.gen_dir = None
comtypes.gen.__path__ = ['']
comtypes.client.GetModule("simpleax.tlb")

Thank you.

By using your reproducer, I confirmed that the error indeed occurs in my environment as well.

Applying my patch replaces the line where the error occurred with pass and a comment, as shown below.

        pass  # avoid using a keyword for def raise(self) -> hints.Incomplete: ...

I am currently writing unit tests using typedesc to prevent the regression.

I will submit a PR. @asamitov (and @forderud), any feedback to that PR would be appreciated (even just a reaction is okay).

asamitov commented 1 month ago

Thank @junkmd for so rapid response.