[Distutils] pyz VS zip

Christian Tismer tismer@appliedbiometrics.com
Wed, 11 Aug 1999 13:39:03 +0200


This is a multi-part message in MIME format.
--------------1CB7761F268640217756441D
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit



"James C. Ahlstrom" wrote:
> 
> > Darrell wrote:
> 
> > Has anyone tried to work with zips from python ?
> 
> We don't use compression within Python, although we
> like the idea of a .pyl Python library file.  The
> reason is we use a commercial installer (Wise) to
> distribute programs, and it supplies compression
> itself.

Since I wanted to wait for Grodon's next release and I had to
create an installer ASAP, here is what I did before my
translate-"Learning Python"-vacation.

The idea is very simple in this case:
- use the WISE installer to distribute files
- create a new "pyrun.exe" with slightly different defaults
- don't ship .pyc but .py in this case (since we are in alpha mode)
- do not change python15.dll at all

The "pyrun.exe" has one simple main feature:
It does not try to go interactive, but by default calls
a function via "-c" .
By modification of the environment, the default path is in
the executable's directory, and in /lib below.
All the rest has to be done by scripts from there.
If and only if called without parameters, an argument of
-c runapp()
is supplied.

This has some advantages:
Users may double-click on pyrun.exe, and it will simply run
the application, without having to create default parameters,
links and such.
The Python executable is still usable as a standard interpreter,
if renamed into something different from "pyrun".
If pyrun.exe is given any parameters, it will use them.
This makes it possible to let the Unwise uninstaller use
Python itself to clean up a tree of directories with .pyc files.

Find attached my minimum python.c program, and some scripts which
I use in the executable's directory:

site.py: 
  Installs a function abspath to make file paths absolute.
  Putsit together with runapp in module __main__.
eraser.py:
  Used for unwise.exe as a file cleanup. It works, since it can
  remove its own .pyc files after creating it. If run multiple times,
  use it with its own file tree at the last call.

This file has to be on top of the directory with these scripts
and the pyrun.exe, dll's, lib etc.:

app.py
  The real application bootstrap file. It has to be adjusted according
  to the application. The complete Python installation sits in a
  subdirectory and can be replaced as a whole without interference
  with the application.

For Wise users, I also attached our simple installation script
which installs Python with a redaction server for one of our
customers. Some of Wise's text editing features are used
to patch one or two Python scripts which take some path variables.

ciao - chris

p.s.: posted twice - first version was too long, zipped the .wse file.

-- 
Christian Tismer             :^)   <mailto:tismer@appliedbiometrics.com>
Applied Biometrics GmbH      :     Have a break! Take a ride on Python's
Kaiserin-Augusta-Allee 101   :    *Starship* http://starship.python.net
10553 Berlin                 :     PGP key -> http://wwwkeys.pgp.net
PGP Fingerprint       E182 71C7 1A9D 66E9 9D15  D3CC D4D7 93E2 1FAE F6DF
     we're tired of banana software - shipped green, ripens at home
--------------1CB7761F268640217756441D
Content-Type: application/x-unknown-content-type-cfile;
 name="python.c"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="python.c"

LyogTWluaW1hbCBtYWluIHByb2dyYW0gLS0gZXZlcnl0aGluZyBpcyBsb2FkZWQgZnJvbSB0
aGUgbGlicmFyeSAqLwoKI2luY2x1ZGUgIlB5dGhvbi5oIg0KI2luY2x1ZGUgIndpbmRvd3Mu
aCIKCmV4dGVybiBETF9FWFBPUlQoaW50KSBQeV9NYWluKCk7CgppbnQKbWFpbihhcmdjLCBh
cmd2KQoJaW50IGFyZ2M7CgljaGFyICoqYXJndjsKew0KCS8qIENUIDk5MDcyMiBoYWNrIHRv
IGFsbG93IHRvIGlnbm9yZSB0aGUgcmVnaXN0cnkgKi8NCgljaGFyIGRyaXZlW19NQVhfRFJJ
VkVdLCBkaXJbX01BWF9ESVJdLCBmbmFtZVtfTUFYX0ZOQU1FXSwgZXh0W19NQVhfRVhUXTsN
CgljaGFyIHBhdGhbX01BWF9QQVRIXTsNCgljaGFyIGJ1ZlsyMCtfTUFYX1BBVEhdOw0KCWNo
YXIgKiBkZWZhdWx0YXJnWzNdOw0KCV9zcGxpdHBhdGgoIGFyZ3ZbMF0sIGRyaXZlLCBkaXIs
IGZuYW1lLCBleHQgKTsNCglDaGFyTG93ZXIoZm5hbWUpOw0KCWlmIChzdHJjbXAoZm5hbWUs
ICJweXJ1biIpID09IDApIHsNCgkJX21ha2VwYXRoKHBhdGgsIGRyaXZlLCBkaXIsIE5VTEws
IE5VTEwpOw0KCQlzcHJpbnRmKGJ1ZiwgIlBZVEhPTlBBVEg9JXMiLCBwYXRoKTsNCgkJX3B1
dGVudihidWYpOw0KCQlpZiAoYXJnYyA9PSAxKSB7DQoJCQlkZWZhdWx0YXJnWzBdID0gYXJn
dlswXTsNCgkJCWRlZmF1bHRhcmdbMV0gPSAiLWMiIDsNCgkJCWRlZmF1bHRhcmdbMl0gPSAi
cnVuYXBwKCkiIDsNCgkJCWFyZ2MgPSAzOw0KCQkJYXJndiA9IGRlZmF1bHRhcmcgOw0KCQl9
DQoJfQ0KCXJldHVybiBQeV9NYWluKGFyZ2MsIGFyZ3YpOwp9Cg==

--------------1CB7761F268640217756441D
Content-Type: text/plain; charset=us-ascii;
 name="site.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="site.py"

# sitecustomize for an application.
# CT 990722 changes for pyrun.exe
# CT 990613
# better: this is an override for site.py
# by this trick, we only need "." in the path at
# startup time. All the rest can be done in site.py.
"""
Assumptions:
The executable and its helper dlls are in the same directory.
Above this directory, a file named "app.py" must exist.

Actions:
The path is cleared out totally.
Only the executable's path, and the lib path are valid.
The proper version of the interpreter is checked, also
the existance and correctness of "app.py".

If an error is detected, a message box will show up
if possible. The application will end with exit status 2.

If everything is ok, "app.py" is run. It is then responsible
to modify the path further and to actually start the application.
"""

print "test"
# globals:
PROGDIR = ''   # Python program directory
ROOTDIR = ''   # root directory where app.py is found

DLLVERSION = "1.5.42b1"

import sys # not os, yet!

path = sys.executable
while path and path[-1] != "\\":
    path = path[:-1]

#print path , " do isser"

sys.path=[path+".",path+"lib",path+"lib/plat-win"]

del path
#print sys.path

import os # but now :-)

def abspath(somedir, root=None):
    """make an absolute path from somedir.
    The directory must exist. It is built from ROOTDIR with somedir
    appended. It is tried to chdir into that, with no error checking,
    to make sure that the directory exists
    """
    if not root: root=ROOTDIR
    here = os.getcwd()
    target = os.path.join(root, somedir)
    os.chdir(target)
    os.chdir(here)
    return target
    
def checkpath(somedir):
    try:
        return abspath(somedir)
    except os.error:
        return None

def init_app():
    prog = sys.executable
    progdir = os.path.split(sys.executable)[0]
    appdir = os.path.split(progdir)[0]
    sys.path = [appdir,
                progdir, 
                abspath("lib", progdir),
                abspath("lib/plat-win", progdir),
                ]
    # reload os, since we have an absolute path now
    reload(os)
    # now we believe we have "string"
    global string
    import string
    vers = string.split(sys.version, None, 1)[0]
    if vers != DLLVERSION:
        raise SystemError, "wrong system dll version"
    global ROOTDIR
    ROOTDIR=appdir
    global PROGDIR
    PROGDIR=progdir
    def runapp(filename=os.path.join(ROOTDIR, "app.py")):
        execfile(filename)
    return runapp
    
try:
    runapp = init_app() # gives a function object, bound to the filename
except SystemExit:
    sys.exit(0)
except:
    err, reason, tb = sys.exc_info()
    del tb
    txt = "Problem encountered:\n"
    txt = txt+str(err)+"\n"
    txt = txt+str(reason)+"\n"
    head = "PNS Application Error"
    try:
        import win32api
        win32api.MessageBox(0,txt, head)
    except:
        print head
        print txt
    sys.exit(2)

import __main__
__main__.abspath = abspath
__main__.runapp = runapp
# put this into the cmdline: -c runapp()

# 990722 done by default from pyrun.exe


--------------1CB7761F268640217756441D
Content-Type: text/plain; charset=us-ascii;
 name="eraser.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="eraser.py"

# cleanup certain files recursively
# CT 990722

import os, sys, string

args = sys.argv[1:]
path = ""
types = []
if args:
    path = args[0]
    types = args[1:]
    
if not path: path = "."
if not types: types = [".pyc", ".pyo"]

types = map(string.lower, types)

def eraser(types, d, files):
    for namex in files:
        namex = string.lower(namex)
        name, ext = os.path.splitext(namex)
        if string.lower(ext) in types:
            try:
                fname = os.path.join(d, namex)
                print "removing", fname,
                os.unlink(fname)
                print " - done."
            except os.error:
                print " - cannot remove!"

                
os.path.walk(path, eraser, types)


--------------1CB7761F268640217756441D
Content-Type: text/plain; charset=us-ascii;
 name="app.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="app.py"

import sys, os, string

if "" in sys.path:sys.path.remove("")
print sys.path
paths = ["medusa", "medusa/RsScriptHandler"]

for path in paths:
    print path
    sys.path.insert(0, abspath(path))
    print "after"
print "schnaudiwaudi"    
from RsHttpServer import * # let it happen


--------------1CB7761F268640217756441D
Content-Type: application/x-winzip;
 name="RsGmServer.zip"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
 filename="RsGmServer.zip"

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
--------------1CB7761F268640217756441D--