Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct type hinting for COM Objects in python?

Tags:

I am using COM Objects within python to expose programmable interfaces to 3rd party software. This is achieved through using Dispatch from win32com.client. My project has also been making use of type hinting from python.3.7, however I am unsure as to how you would define the type of these COM objects for the purposes of type hinting. The question relates to all the COM objects I have, with a real example being a Microsoft Direct X Recordset: Dispatch("ADODB.Recordset").

from win32com.client import Dispatch
def create_my_com_object_instance(input_arg: Dict[str, Union[str, float, int]]) -> <type_of_com_object_here>:
    my_instance = Dispatch("ADODB.Recordset")
    # Set attributes/call methods of this instance here ... 
    return my_instance

In the above code snippet I would replace 'type_of_com_object_here' with the COM object type.

My first thought was just to call type() on the instance and use the returned type:

x = Dispatch("ADODB.Recordset")
x
Out[1]: <win32com.gen_py.Microsoft ActiveX Data Objects 6.1 Library._Recordset instance at 0x83848456>
type(x)
Out[2]: win32com.gen_py.B691E011-1797-432E-907A-4D8C69339129x0x6x1._Recordset._Recordset
x.__class__
Out[3]: win32com.gen_py.B691E011-1797-432E-907A-4D8C69339129x0x6x1._Recordset._Recordset

This does not return a suitable type for defining the COM Object. I believe that I could create an abstract base class using TypeVar('T') and Generic[], however I am unsure if there is a more pythonic/better alternative available.

Thanks

like image 472
Tom Pick Avatar asked Feb 19 '20 11:02

Tom Pick


1 Answers

I'm afraid you're unfortunately running into one of the limits of typing in Python. In particular:

  1. As far as I'm aware, there are no type stubs for the pywin32 library in either Typeshed or as a 3rd party module. This means there is no existing canonical reference for what that type ought to be.
  2. Dynamically dispatching to arbitrary objects is going to strain the limits of what's possible to express using just type hints (assuming that Dispatch really does dispatch to arbitrary objects).

So, I recommend one of the following three approaches:

  1. If you want to assume absolutely nothing about the returned type and treat it as just an opaque blob, have the return type be just object. This is the most restrictive and type safe option, since a PEP 484 compliant type checker will let the caller only use methods that are present in all Python objects, such as __str__ or __eq__.

  2. If you want to assume absolutely nothing about the returned type but also impose no restrictions on its use, have the return type be Any, the dynamic type. This is the most permissive and unsafe operation: you are saying the output is some dynamic type and telling the type checker to let the caller use the returned object however they please.

    This is the "default option", so to speak: since there are no type stubs available for pywin32, the type checker will fall back to assuming Dispatch(...) has a return type of Any.

  3. If you know for certain the returned type will always have some specific methods or attributes available, you have the return type be a Protocol. Protocols basically let you do structural subtyping: any class that happens to implement the protocol methods and attributes is considered a subtype of that protocol, even if the class isn't inheriting from the protocol in any way. Protocols are basically like Go's interfaces, if you're familiar with Go.

    You may want to create a dedicated create_adodb_recordset(...) function here, so you can more fully customize the return type.

like image 184
Michael0x2a Avatar answered Sep 18 '22 11:09

Michael0x2a