Pyinstaller and win32com

Pyinstaller is a wonderful way of making a single executable that can be run on any windows machine without requiring Python or associated libraries (e.g. wxPython for GUI toolkit) to be installed. It can make things much easier for users. Instead of installing a whole lot of libraries they just put a folder somewhere (even the Desktop is OK) and start using the executable. In my case I needed a simple toolkit for reading, editing, and manipulating secured MS Access databases and reporting results in either HTML or Excel spreadsheets. Python is much nicer to write code in than VB. But I had some major problems with getting win32com working.

If using a script with win32com inside a pyinstaller (made with 1.5 in this case) executable, there can be major problems with win32com.client.Dispatch(). I used two different approaches to get MS Access and Excel working. Using win32com.client.gencache.EnsureDispatch() seemed to work for both initially but that was incorrect. Using either approach (EnsureDispatch() or Dispatch() without the clsctx argument) there was a problem finding the module within PyInstaller. See EnsureDispatch and EnsureModule not working on win32 for a description of the problem even if the solution was not one I was able to use successfully.

Example error:

dbengine = win32com.client.gencache.EnsureDispatch(r'DAO.DBEngine.36')
File "C:\Program Files\exampledev\build\pyi.win32\admin\outPYZ1.pyz/win32com.client.gencache", line 536, in EnsureDispatch
File "C:\Program Files\exampledev\build\pyi.win32\admin\outPYZ1.pyz/win32com.client.gencache", line 520, in EnsureModule
File "C:\Program Files\exampledev\build\pyi.win32\admin\outPYZ1.pyz/win32com.client.gencache", line 287, in MakeModuleForTypelib
File "C:\Program Files\exampledev\build\pyi.win32\admin\outPYZ1.pyz/win32com.client.makepy", line 286, in GenerateFromTypeLibSpec
File "C:\Program Files\exampledev\build\pyi.win32\admin\outPYZ1.pyz/win32com.client.gencache", line 550, in AddModuleToCache
File "C:\Program Files\exampledev\build\pyi.win32\admin\outPYZ1.pyz/win32com.client.gencache", line 629, in _GetModule
File "C:\pyinstaller-1.5\iu.py", line 455, in importHook
ImportError: No module named win32com.gen_py.00025E01-0000-0000-C000-000000000046x0x5x0

For MS Access, I looked in C:\Python26\Lib\site-packages\win32com\genpy folder and found one the modules I had earlier generated by makepy (details on making it later). It started with the following details which made it clear it was the correct version:

# Created by makepy.py version 0.5.00
# By python version 2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)]
# From type library 'dao360.dll'
# On Tue Jun 21 21:47:34 2011
"""Microsoft DAO 3.6 Object Library"""

I saved and renamed the module to dao36_from_genpy.py and the starting point was:
dao36_from_genpy.DBEngine()
instead of win32com.client.Dispatch(r'DAO.DBEngine.36')

The idea came from py2exe/pyinstaller and DispatchWithEvents

Returning to using makepy.py:

“There are a couple of different ways to run makepy. Start Pythonwin, and from the menu select Tools->Com Makepy Utility. You should see a list of registered typelibs. Select “Microsoft Word x.y Object Library” and hit Ok. This can also be done programatically by initiating Word with
win32com.client.gencache.EnsureDispatch(‘Word.Application’) win32com.client.constants – AttributeError

For Excel, the solution was to use win32com.client.Dispatch(“Excel.Application”, clsctx = pythoncom.CLSCTX_LOCAL_SERVER). Remember to import pythoncom of course. I wasn’t able to get the other approach working very quickly because instead of a single module there was a folder, an extensive __init__.py, and a lot of submodules e.g. Workbook.py.

For more on Pyinstaller see Pyinstaller 1.5 with Python 2.6 (Round 3)

Pyinstaller 1.5 with Python 2.6 (Round 3)

Round 1 with Pyinstaller 1.2 and Python 2.4 was here: PyInstaller1.2
Round 2 with Pyinstaller 1.3 and Python 2.5 was here: PyInstaller Round 2

Overview:

  1. Put latest copy of pyinstaller under the appropriate python folder e.g. Python26
  2. Run Configure.py e.g.

    C:\Python26\python.exe C:\Python26\Pyinstaller-1.5\Configure.py

  3. Make a dev folder and put main script in there and a copy of the most recent spec file made for any other project. Use as a starting point.
  4. Make a bat file to produce the executable using the spec file to configure the end result e.g.

    "C:\Python26\python.exe" "C:\Python26\pyinstaller-1.5\Build.py" "myproj.spec"
    pause

    Include the pause so any errors can be spotted and fixed

  5. Make another batch script which runs the executable and has the pause command at the end so you can spot any errors and fix them (so screen doesn’t flash on and off without giving you a chance to read the error messages if any). NB no python.exe involved – just the exe you made e.g.

    myproj.exe
    pause

  6. Once testing is complete, remake the exe with live spec settings i.e. set console to False and debug to False.
  7. Distribute 🙂

Mistakes to avoid:

  • Testing the exe with a batch file is good. Having the batch file try to run the exe with python, not so good (in fact, it is dumb 😉 ). Just run the exe on its own. Otherwise spurious error about encoding problem with line 1 of the exe. See Why this SyntaxError
  • If making a script which execs everything else it needs (reason: to keep things flexible), the main script e.g. “exec_script2exec.py” MUST import anything needed by scripts it is likely to call otherwise they will not appear in the exe. Pyinstaller can’t read minds or predict the future 😉 . So import wx and import win32com.client are probably required for my MS Access project. Otherwise will get an error about no module called wx etc.

Escaping Ubuntu Unity (for now)

Don’t get me wrong – I’m really looking forwards to what Unity will become (if Canonical plays its cards right). But it is a mixed experience for users at the moment. A friend of mine upgraded his laptop from Maverick to Natty and had some serious problems. Unity had worked at first (although he preferred the old interface) but then it stopped. The launcher and the panel vanished and he needed some help escaping Unity and using the Ubuntu Classic (Gnome 2) interface. Unfortunately, he wasn’t able to simply log in and change to Ubuntu Classic as per HowTo: Disable Ubuntu Unity Interface.

The first thing I suggested was that he follow the instructions at Missing top and side panels in Unity, Natty Troubleshooting. Great! It worked and now he could interact with the system much easier. The sharks were backing off 😉 (See http://xkcd.com/349/).

But how could he get the system to let him log in using his name and password? For that I needed the control panel. The easiest way to get that seemed to be via the terminal with the command:

gnome-control-center

Once there I could select the default log in session:

Change default log in session

Success!

While I’m at it I should add a couple of opinions about Unity. Why not? Everyone else is 😉

  1. Canonical needs to give users the option of Ubuntu Classic (Gnome 2) for a few releases more than intended. Depending on how Unity works with dual widescreen monitors I may need Gnome 2 for quite a while.
  2. Users need to have a few configuration options easily available – e.g. how to remove the overlay scroll bars**
  3. Unity could be awesome and I think it will be a great option within a couple of releases. See a reasonably balanced review here – Riding the Narwhal: Ars reviews Unity in Ubuntu 11.04 I am already trying out Oneiric Unity on my notebook so I’m not a hater.

**
At the moment you have to do the following:
sudo su
echo "export LIBOVERLAY_SCROLLBAR=0" > /etc/X11/Xsession.d/80overlayscrollbars

from How To Disable The Overlay Scrollbars In Ubuntu 11.04 [Quick Tip]