Hi Jussi,

Il giorno Tue, 20 Jul 2010 10:49:51 +0300
Jussi Rasinmäki <[email protected]> ha scritto:

> Hi Antonio,
> 
> > Yes, in my experience packging an ETS app with pyinstaller is but an
> > easy task. I'm pretty sure it can't be done with a "stock Makespec
> > and Build commands".
> 
> I think the changes in ETS related to namespace package imports
> referenced in the thread you linked to have helped in this. At least
> pyinstaller seemed to happily import ETS packages when I enabled debug
> messages in pyinstaller's iu.py. Or then I interpreted the debug
> messages incorrectly...

Yes, pyinstaller correctly imports ets packages like any other python
program does.
The problem is that pynstaller seems to be not able to collect all
the stuffs needed by the frozen environment so you need a hook or
some other trick in the spec file.

> The work by my colleague I referred to was mainly about this stuff,
> how to collect needed ETS packages for pyinstaller. So it would seem
> that it's not needed anymore. However, it went roughly like this:

I disagree.
I still had no time to give your colleague's code a try but I think
you can't get a working app without it. 


> import enthought
> base =
> os.path.abspath(os.path.join(os.path.dirname(enthought.__file__),
> '..', '..')) ...
> for item in os.listdir(base):
>     if item.lower().startswith('traits-'):
>         traits = os.path.join(base, item)
>         print 'added traits from', item
> ... etc for the other needed ETS packages used with PKG below
> basefiles = [os.path.join(HOMEPATH,'support/_mountzlib.py'),
>               os.path.join(HOMEPATH,'support/useUnicode.py'),
>               os.path.join(base, 'pkg_resources.py'),
>               'unpackMetadata.py',
>               '../yasso.py',
>               '../modelcall.py',
>               ]
> a = Analysis(basefiles,
>              pathex=[''],
>              hookspath=[''],
>              excludes=['enthought', 'wx'])
> pyz = PYZ(a.pure)
> wx = PKG(Tree(wx), name='wx.pkg')
> traits = PKG(Tree(traits), name='traits.pkg')
> traitsgui = PKG(Tree(traitsgui), name='traitsgui.pkg')
> traitswx = PKG(Tree(traitswx), name='traitswx.pkg')
> enable = PKG(Tree(enable), name='enable.pkg')
> enthought = PKG(Tree(enthought), name='enthought.pkg')
> chaco = PKG(Tree(chaco), name='chaco.pkg')

Exclude enthought from the analysis stage and manually set all needed
packages. Including the entire package tree you automatically get code
for both wx and qt backends. The package gets a little fatter but it is
OK.

> exe = EXE(wx,
>           traitswx,
>           traitsgui,
>           traits,
>           enable,
>           enthought,
>           chaco,
>           pyz,
>           a.scripts, #+[("v", "", "OPTION")],
>           a.binaries+libfiles,
>           a.zipfiles,
>           name=os.path.join('dist', exename),
>           debug= False,
>           strip=False,
>           upx=False,
>           icon='yasso.ico',
>           console=False )
> 
> There was also a hook-enthought.py:
> hiddenimports = ['uuid', 'colorsys', 'wx', 'wx.html']

OK.
But if one wants to use the qt backend the last two items should be
changed accordingly.

> --------------------------
> Then at runtime the unpackMetadata.py was the key:
> import carchive
> import sys
> import os
> 
> this = carchive.CArchive(sys.executable)
> archives = os.environ['_MEIPASS2']
> pkgs = ['wx', 'traits', 'traitswx', 'chaco', 'enthought', 'enable',
> 'traitsgui'] for pkg in pkgs:
>     mp = this.openEmbedded('%s.pkg' % pkg)
>     targetdir = os.path.join(archives,pkg)
>     os.mkdir(targetdir)
>     print "Extracting %s ..." % pkg,
>     for fnm in mp.contents():
>         try:
>             stuff = mp.extract(fnm)[1]
>             outnm = os.path.join(targetdir, fnm)
>             dirnm = os.path.dirname(outnm)
>             if not os.path.exists(dirnm):
>                 os.makedirs(dirnm)
>             open(outnm, 'wb').write(stuff)
>         except Exception, mex:
>             print mex
>     mp = None
>     sys.path.insert(0, targetdir)
>     if pkg == 'traitsgui':
>         imagepath = os.path.join(targetdir, 'enthought', 'traits',
> 'ui', 'image', 'library')
>         os.environ['TRAITS_IMAGES'] = imagepath
>         os.putenv('TRAITS_IMAGES', imagepath)
>     print "ok"
> 
> from resource_path_override import ResourcePathOverride
> resource_override = ResourcePathOverride(archives)
> 
> -------------------------------------
> And then the custom ResourcePathOverride:
> import sys
> import os
> """This class will extend the original resource_path function so that
> it will always point to the archive_path instead of whatever was
> frozen into it. WARNING: Do not create an instance of this class
> before the archives have been extracted!
> """
> class ResourcePathOverride(object):
>     resource_path_orig = None
>     archive_path = None
> 
>     def __init__(self, archive_path):
>         import enthought.resource.resource_path #here, so that you can
> import the module freely (at least in theory)
>         self.resource_path_orig =
> enthought.resource.resource_path.resource_path self.archive_path =
> archive_path enthought.resource.resource_path.resource_path =
> self.resource_path
> 
>     def resource_path(self, level = 2):
>         path = self.resource_path_orig(level+1)
>         path = path.rsplit(''.join((os.path.sep, 'enthought',
> os.path.sep)), 1) root = path[0].rsplit(os.path.sep, 1)
>         path = os.path.join(self.archive_path,
> self.get_pkg_name(root[1]), 'enthought', path[1])
>         return path
> 
>     def get_pkg_name(self, path):
>         """This function must mirror the list in yasso.spec
>         """
>         if path.lower().startswith('traits-'):
>             return 'traits'
>         if path.lower().startswith('traitsgui'):
>             return 'traitsgui'
>         if path.lower().startswith('traitsbackendwx'):
>             return 'traitswx'
>         if path.lower().startswith('enable'):
>             return 'enable'
>         if path.lower().startswith('enthought'):
>             return 'enthought'
>         if path.lower().startswith('chaco'):
>             return 'chaco'

This part is black magic.
I wonder what Giovanni thinks about this solution and, most of all, if
there are plans for an official pkg_resources support in pyinstaller.

> > The graphical toolkit is imported only when actually needed and this
> > import is quite hard to detect for pyinstaller.
> 
> Any ideas how to force this? The GUI is invoked using
> configure_traits: ...
> yasso = Yasso()
> 
> if __name__ == '__main__':
>     yasso.configure_traits()
> 
> Jussi
> 

Yes, at this point ets looks for available backend and loads it before
actually setting up the GUI.

I you use a stock spec file you are not able to collect all needed
stuffs so the GUI is never painted.


ciao


-- 
Antonio Valentino

-- 
You received this message because you are subscribed to the Google Groups 
"PyInstaller" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/pyinstaller?hl=en.

Reply via email to