Canonical Voices

Posts tagged with 'py2exe'

mandel

So far using py2exe has not been walk in the park with several issues so far and this time it could not be different…. The interesting issue brought to me by py2exe this time was during the runtime of a pyqt application in which the following was being print to stderr:

QObject::moveToThread: Current thread (0x21a3410) is not the object's thread (0x19af0d0).

Funny enough that error would not happen if the application was not froze (WTF!). After some help from ralsina and some googling we found the following:

In my case I was pretty scared that the actual issue was related to the that the operation of the QObject was taking place in a twisted deferred callback and that qtreactor might be doing something naughty. At the end it turned out that the issue was related to the use of a jpg image which requires to use an image plugin in Qt… I fixed the issue as per this. Nevertheless I have made the required changes so that such a hack is hidden in the Windows code and that the Qt UI can be used in Kubuntu without dirty code so if everything goes as planned, SSO should have a buggy t UI that runs on Kubuntu.

Read more
mandel

The following bug I have been faced with has made me loose more time that I would have expected and therefore I think is a good idea to describe it so that the rest of the internet can take advantage of my wasted time and also to keep a record of my stupidity.

After building the .exe file of the ubuntu-sso port to windows I was getting the following error at runtime:

Cannot mix incompatible Qt library (version 0x40701) with this library (version 0x40702)

Usually this means that you have two different version of Qt installed are you are mixing the libraries (.dll in this case because I was dealing with Windows). My initial reaction was to look at my Qt setup in the machine and compared the version installed in the system and that used by PyQt. Let me tell you that is a waste of time. The real reason behind this runtime error was the fact that I was using the qtreactor and I had PyQt and PySide installed in my system.

When not freezing the application, the fact that you have both packages installed is not a problem what so ever, but with py2exe it is. Py2exe bundles all the dependencies you app has and due to the fact hat qtreactor does the following:

try:
    from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
    from PyQt4.QtCore import QEventLoop
except ImportError:
    from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
    from PySide.QtCore import QEventLoop

both, PySide and PyQt were included in the frozen app. The problem arises due to this fact. When py2exe adds both libs, it copies the Qt dlls you depend on, and if PySide and PYQt depend on different versions (which is what was happening in my system) you might run into the issue of getting dlls from different versions because py2exe will override the already copied dlls without telling you.

In summary if you get the above runtime error, take a look to see if PySide and PyQt have ben included in your frozen app and if they depend in different versions of Qt.

Read more
mandel

One of the things we wanted to achieve for the Windows port of Ubuntu One was to deploy to the users systems .exe files rather than requiring them to have python and all the different dependencies installed in their machine. There are different reasons we wanted to do this, but this post is not related to that. The goal of this post is to explain what to do when you are using py2exe and you depend on a package such as lazr.restfulclient.

Why lazr.restfulclient?

There are different reasons why I’m using lazr.restfulclient as an example:

  • It is a dependency we do have on Ubuntu One, and therefore I already have done the work with it.
  • It uses two features of setuptools that do not play well with py2exe:
    • It uses namespaced packages.
    • I uses pkg_resources to load resources used for the client.

Working around the use of namedspaced packages

This is actually a fairly easy thing to solve and it is well documented in the py2exe wiki, nevertheless I’d like to show it in this post so that the inclusion of the lazr.restfulclient is complete.

The main issue with namedspaced packages is that you have to tell the module finder from py2exe where to find those packages, which in our example are lazr.authentication, lazr.restfulclient and lazr.uri. A way to do that would be the following:

import lazr
try:
    import py2exe.mf as modulefinder
except ImportError:
    import modulefinder
 
for p in lazr.__path__:
        modulefinder.AddPackagePath(__name__, p)

Adding the lazr resources

This is a more problematic issue to solve since we have to work around a limitation found in py2exe. The lazr.restfulcient tries to load a resource from the py2exe library.zip but as the zipfile is reserved for compiled files, and therefore the module fails. In py2exe there is no way to state that those resource files have to be copied to the library.zip which would mean that an error is raised at runtime when trying to use the lib but not at build time.

The best way (if not the only one) to solve this is to extend the py2exe command to copy the resource files to the folders that are zipped before they are embedded, that way pkg_resource will be able to load the file with no problems.

import os
import glob
import lazr.restfulclient
from py2exe.build_exe import py2exe as build_exe
 
class LazrMediaCollector(build_exe):
    """Extension that copies lazr missing data."""
 
    def copy_extensions(self, extensions):
        """Copy the missing extensions."""
        build_exe.copy_extensions(self, extensions)
 
        # Create the media subdir where the
        # Python files are collected.
        media = os.path.join('lazr', 'restfulclient')
        full = os.path.join(self.collect_dir, media)
        if not os.path.exists(full):
            self.mkpath(full)
 
        # Copy the media files to the collection dir.
        # Also add the copied file to the list of compiled
        # files so it will be included in zipfile.
        for f in glob.glob(lazr.restfulclient.__path__[0] + '/*.txt'):
            name = os.path.basename(f)
            self.copy_file(f, os.path.join(full, name))
            self.compiled_files.append(os.path.join(media, name))

In order to use the above command class to perform the compilation you simply have to tell setup tools which command class to use.

cmdclass = {'py2exe' : LazrMediaCollector}

With the above done, you can use the usual ‘python setup.py install py2exe’. Now, the question for the Internet, can this be done with Pyinstaller?

Read more