What is wxPRE?
wxPRE is the wxPython Runtime Environment.
wxPRE is being planned at the moment, and there has been some progress but there is plenty more to do.
Aim
The aim is to create a Runtime Environment containing wxPython and Python. Inspired by the JRE
These wiki pages are used for the planning and will hopefully become the basis for the wxPRE.
Related Pages
VersionSelection may be an important part of wxPRE
py2exe and other related systems will be helpful
wxPRE requirements
wxPRE itself should contain everything needed to run wxPython apps, either as a standalone python source file, or as an application bundle of some sort.
It should be possible to create the following kinds of installers:
- A standalone wxPRE installer.
- An application installer that includes a bundled wxPRE
- A "light" application installer that relies on an already-installed wxPRE
Implementation questions
- How should applications be bundled? Possible in a zip file. In which directory?
- How will wxPRE installations be found? On Windows, could be through the registry.
- How will wxPRE handle multiple versions being installed?
- py2exe is Windows-only at the moment. How can wxPRE be made for other platforms? Is it neccessary?
Progress so far
So far a simple wxPRE script has been created that works successfully with py2exe.
A setup script has also been created that produces the py2exe-d wxPRE executable. It also uses InnoSetup to create a wxPRE installer. However there isn't yet a mechanism to produce application installers (with bundled wxPRE or lightweight).
Source code
wxPRE.py
"""wxPython Runtime Environment"""
import sys
import os
__version__ = "0.2.1"
def getversion():
"""returns wxPRE version, including the wx version if available"""
try:
import wx
return "%s-wx%s-mingw" % (__version__, wx.VERSION_STRING)
except ImportError:
return __version__
def addpathsfromdir(dirname):
"""looks in the directory for .pth files and adds them to sys.path"""
for direntry in os.listdir(dirname):
if os.path.splitext(direntry)[1] == '.pth':
pthfile = open(os.path.join(dirname, direntry), 'r')
pthdirs = [pthdir.strip() for pthdir in pthfile]
pthfile.close()
for pthdir in pthdirs:
if os.path.isdir(pthdir):
sys.path.append(pthdir)
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
arg = sys.argv[1]
if arg == "--version":
print sys.argv[0], getversion()
elif arg == "--test":
print repr(sys.path)
print repr(sys.prefix)
print repr(sys.executable)
print repr(getattr(sys, "frozen", None))
print repr(getattr(locals, "__file__", None))
print repr(sys.argv)
else:
filename = os.path.abspath(arg)
addpathsfromdir(os.path.dirname(filename))
addpathsfromdir(os.getcwd())
sys.argv = sys.argv[1:]
#make sure the current path is the required one
#and that imports can be made
path = os.path.dirname(filename)
if path is not os.getcwd():
os.chdir(path)
if path not in sys.path:
sys.path.append(path)
try:
execfile(filename)
except ImportError,detail:
print >>sys.stderr, "wxPRE ImportError: ",detail
print >>sys.stderr, "Try adding a .pth file here or in the scripts dir."
raise
else:
print >>sys.stderr, "Need to supply script filename for %s (version %s)" % (sys.argv[0], getversion())
setup.py
from distutils.core import setup, gen_usage, Command, grok_environment_error
from distutils.dist import Distribution
from distutils.errors import *
import os
import sys
try:
import py2exe
build_exe = py2exe.build_exe.py2exe
Distribution = py2exe.Distribution
except ImportError:
py2exe = None
build_exe = Command
import wxPRE
class InnoScript:
"""class that builds an InnoSetup script"""
def __init__(self, name, lib_dir, dist_dir, windows_exe_files = [], lib_files = [], version = "1.0"):
self.lib_dir = lib_dir
self.dist_dir = dist_dir
if not self.dist_dir.endswith(os.sep):
self.dist_dir += os.sep
self.name = name
self.version = version
self.windows_exe_files = [self.chop(p) for p in windows_exe_files]
self.lib_files = [self.chop(p) for p in lib_files]
def chop(self, pathname):
"""returns the path relative to self.dist_dir"""
assert pathname.startswith(self.dist_dir)
return pathname[len(self.dist_dir):]
def create(self, pathname=None):
"""creates the InnoSetup script"""
if pathname is None:
self.pathname = os.path.join(self.dist_dir, self.name + os.extsep + "iss")
else:
self.pathname = pathname
ofi = self.file = open(self.pathname, "w")
print >> ofi, "; WARNING: This script has been created by py2exe. Changes to this script"
print >> ofi, "; will be overwritten the next time py2exe is run!"
print >> ofi, r"[Setup]"
print >> ofi, r"AppName=%s" % self.name
print >> ofi, r"AppVerName=%s %s" % (self.name, self.version)
print >> ofi, r"DefaultDirName={pf}\%s" % self.name
print >> ofi, r"DefaultGroupName=%s" % self.name
print >> ofi, r"OutputBaseFilename=%s-%s-setup" % (self.name, self.version)
print >> ofi
print >> ofi, r"[Files]"
for path in self.windows_exe_files + self.lib_files:
print >> ofi, r'Source: "%s"; DestDir: "{app}\%s"; Flags: ignoreversion' % (path, os.path.dirname(path))
print >> ofi
print >> ofi, r"[Icons]"
for path in self.windows_exe_files:
print >> ofi, r'Name: "{group}\%s"; Filename: "{app}\%s"' % \
(self.name, path)
print >> ofi, 'Name: "{group}\Uninstall %s"; Filename: "{uninstallexe}"' % self.name
def compile(self, compilercmd="compile"):
"""compiles the script using InnoSetup"""
try:
import ctypes
except ImportError:
try:
import win32api
except ImportError:
os.startfile(self.pathname)
else:
print "Ok, using win32api."
win32api.ShellExecute(0, compilercmd, self.pathname, None, None, 0)
else:
print "Cool, you have ctypes installed."
res = ctypes.windll.shell32.ShellExecuteA(0, compilercmd, self.pathname, None, None, 0)
if res < 32:
raise RuntimeError, "ShellExecute failed, error %d" % res
###############################################################
class build_installer(build_exe):
"""distutils class that first builds the exe file(s), then creates a Windows installer using InnoSetup"""
def run(self):
# First, let py2exe do it's work.
build_exe.run(self)
lib_dir = self.lib_dir
dist_dir = self.dist_dir
# create the Installer, using the files py2exe has created.
script = InnoScript(self.distribution.metadata.name, lib_dir, dist_dir, self.windows_exe_files, self.lib_files, version=wxPRE.getversion())
print "*** creating the inno setup script***"
script.create()
print "*** compiling the inno setup script***"
script.compile("compil32")
# Note: By default the final setup.exe will be in an Output subdirectory.
class wxPREDistribution(Distribution):
"""A distribution class for wxPRE"""
def __init__(self, attrs):
baseattrs = {}
baseattrs['script_name'] = os.path.basename(sys.argv[0])
baseattrs['script_args'] = sys.argv[1:]
# package information
baseattrs['name'] = "wxPRE"
baseattrs['version'] = wxPRE.__version__
baseattrs['url'] = "http://wiki.wxpython.org/index.cgi/wxPRE"
baseattrs['author'] = "David Fraser and Stephen Emslie"
baseattrs['author_email'] = '[email protected]'
baseattrs['description'] = 'wxPython Runtime Environment'
baseattrs['long_description'] = wxPRE.__doc__
baseattrs['license'] = "wxWidgets license"
py2exeoptions = {}
py2exeoptions["packages"] = ["wx"]
py2exeoptions["compressed"] = True
py2exeoptions["dist_dir"] = "wxPRE-%s" % wxPRE.getversion()
baseattrs['options'] = {"py2exe": py2exeoptions}
if py2exe:
baseattrs['console'] = ["wxPRE.py"]
baseattrs['zipfile'] = "wxPRElibs.zip"
baseattrs['cmdclass'] = {"py2exe": build_installer}
baseattrs.update(attrs)
Distribution.__init__(self, baseattrs)
if __name__ == "__main__":
dist = wxPREDistribution({})
try:
ok = dist.parse_command_line()
except DistutilsArgError, msg:
raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg
if ok:
try:
dist.run_commands()
except KeyboardInterrupt:
raise SystemExit, "interrupted"
except (IOError, os.error), exc:
error = grok_environment_error(exc)
raise SystemExit, error
except (DistutilsError, CCompilerError), msg:
raise SystemExit, "error: " + str(msg)