6

Via

import win32com.client
wordapp = win32com.client.gencache.EnsureDispatch('Word.Application')

I can get a Word Application object documented e.g. here. However, ipython's autocompletion is not aware of that API, is there any way to add that?

fuglede
  • 17,388
  • 2
  • 54
  • 99
Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • 1
    You will have to use the documentation or a COM browser to get familiar with the API. The python object is only Proxy object, if you `dir` it it will only show the `__special__` methods. In your case there is good documentation available, so it shouldn't be too hard. –  Nov 18 '14 at 10:08
  • @Dabrion Although that throws the more general question whether there is a way to automagically link a well-formatted documentation to `dir`... I'll have to think about this some more – Tobias Kienzler Nov 18 '14 at 10:10
  • I dont think so, it seems infeasible to try all possible methods names. So this responsibility lies with the com object, which should provide a method list when you get the handle. I remember this functionality (com browser?). It has been some time that worked with Windows stuff.. so I am afraid have to refer you to the docs for details. –  Nov 18 '14 at 10:18
  • @Dabrion Oh no, brute force would be a bad idea. I meant, some smart logic that uses e.g. http://msdn.microsoft.com/en-us/library/bb244515.aspx to obtain the actually available names. I know little of COM, so the browser you mentioned is something I have to read up on first... – Tobias Kienzler Nov 18 '14 at 10:21

2 Answers2

10

Quick solution

Perhaps the simplest way to achieve code completion in IPython (tested with 6.2.1, see the answer below for a snippet that works with 7.1) and Jupyter is to run the following snippet:

from IPython.utils.generics import complete_object
import win32com.client

@complete_object.when_type(win32com.client.DispatchBaseClass)
def complete_dispatch_base_class(obj, prev_completions):
    try:
        ole_props = set(obj._prop_map_get_).union(set(obj._prop_map_put_))
        return list(ole_props) + prev_completions
    except AttributeError:
        pass

enter image description here

enter image description here

Short story long

With some more details being outlined in this guide, win32com ships with a script, makepy.py for generating Python types corresponding to the type library of a given COM object.

In the case of Word 2016, we would proceed as follows:

C:\Users\username\AppData\Local\Continuum\Anaconda3\pkgs\pywin32-221-py36h9c10281_0\Lib\site-packages\win32com\client>python makepy.py -i "Microsoft Word 16.0 Object Library"

Microsoft Word 16.0 Object Library
 {00020905-0000-0000-C000-000000000046}, lcid=0, major=8, minor=7
 >>> # Use these commands in Python code to auto generate .py support
 >>> from win32com.client import gencache
 >>> gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}', 0, 8, 7)

The location of makepy.py will of course depend on your Python distribution. The script combrowse.py, available in the same directory, can be used to find the names of available type libraries.

With that in place, win32com.client will automatically make use of the generated types, rather than the raw IPyDispatch, and at this point, auto-completion is available in e.g. IPython or Jupyter, given that the COM object of interest actually publishes its available properties and methods (which is not a requirement).

Now, in your case, by invoking EnsureDispatch instead of Dispatch, the makepy part of the process is performed automatically, so you really should be able to obtain code completion in IPython for the published methods:

enter image description here

Note, though, that while this does give code completion for methods, the same will not be true for properties. It is possible to inspect those using the _prop_map_get_ attribute. For example, wordapp.Selection.Range.Font._prop_map_get_ gives all properties available on fonts.

If using IPython is not a strong requirement, note also that the PythonWin shell (located around \pkgs\pywin32\Lib\site-packages\pythonwin\Pythonwin.exe) has built-in code completion support for both properties and methods.

enter image description here

This, by itself, suggests that the same is achievable in IPython.

Concretely, the logic for auto-completion, which in turn relies on _prop_map_get_, can be found in scintilla.view.CScintillaView._AutoComplete. On the other hand, code completion in IPython 6.2.1 is handled by core.completer.IPCompleter. The API for adding custom code completers is provided by IPython.utils.generics.complete_object, as illustrated in the first solution above. One gotcha is that with complete_object being based on simplegeneric, only one completer may be provided for any given type. Luckily, all types generated by makepy will inherit from win32com.client.DispatchBaseClass.

If this turns out to ever be an issue, one can also circumvent complete_object entirely and simply manually patch IPython by adding the following five lines to core.completer.Completion.attr_matches:

try:
    ole_props = set(obj._prop_map_get_).union(set(obj._prop_map_put_))
    words += list(ole_props)
except AttributeError:
    pass

Conversely, IPython bases its code-completion on __dir__, so one could also patch gencache, which is where the code generation ultimately happens, to include something to like

def __dir__(self):
    return list(set(self._prop_map_get_).union(set(self._prop_map_put_)))

to each generated DispatchBaseClass.

fuglede
  • 17,388
  • 2
  • 54
  • 99
5

fuglede's answer is great, just want to update it for the newest versions of IPython (7.1+). Since IPython.utils.generics has changes from using simplegeneric to using functools, the @complete_object.when_type method should be changed to @complete_object.register. So his initial code should changed to:

from IPython.utils.generics import complete_object
import win32com.client

@complete_object.register(win32com.client.DispatchBaseClass)
def complete_dispatch_base_class(obj, prev_completions):
    try:
        ole_props = set(obj._prop_map_get_).union(set(obj._prop_map_put_))
        return list(ole_props) + prev_completions
    except AttributeError:
        pass
EngTurtle
  • 51
  • 1
  • 2
  • I tried it in Spyder's IPython console and it executes the code without error, but I get no code completion. I tried to enable greedy or jedi code completion, but no change. Do I miss something? – MiB_Coder Feb 20 '20 at 16:53