views:

118

answers:

1

I'm running a py2exe-compiled python program from one server machine on a number of client machines (mapped to a network drive on every machine, say W:).

For Windows XP and later machines, have so far had zero problems with Python picking up W:\python23.dll (yes, I'm using Python 2.3.5 for W98 compatibility and all that). It will then use W:\zlib.pyd to decompress W:\library.zip containing all the .pyc files like os and such, which are then imported and the program runs no problems.

The issue I'm getting is on some Windows 98 SE machines (note: SOME Windows 98 SE machines, others seem to work with no apparent issues). What happens is, the program runs from W:, the W:\python23.dll is, I assume, found (since I'm getting Python ImportErrors, we'd need to be able to execute a Python import statement), but a couple of things don't work:

1) If W:\library.zip contains the only copy of the .pyc files, I get ZipImportError: can't decompress data; zlib not available (nonsense, considering W:\zlib.pyd IS available and works fine with the XP and higher machines on the same network).

2) If the .pyc files are actually bundled INSIDE the python exe by py2exe, OR put in the same directory as the .exe, OR put into a named subdirectory which is then set as part of the PYTHONPATH variable (e.g W:\pylib), I get ImportError: no module named os (os is the first module imported, before sys and anything else).

Come to think of it, sys.path wouldn't be available to search if os was imported before it maybe? I'll try switching the order of those imports but my question still stands: Why is this a sporadic issue, working on some networks but not on others? And how would I force Python to find the files that are bundled inside the very executable I run? I have immediate access to the working Windows 98 SE machine, but I only get access to the non-working one (a customer of mine) every morning before their store opens.

Thanks in advance!


EDIT: Okay, big step forward. After debugging with PY2EXE_VERBOSE, the problem occurring on the specific W98SE machine is that it's not using the right path syntax when looking for imports. Firstly, it doesn't seem to read the PYTHONPATH environment variable (there may be a py2exe-specific one I'm not aware of, like PY2EXE_VERBOSE).

Secondly, it only looks in one place before giving up (if the files are bundled inside the EXE, it looks there. If not, it looks in library.zip).

EDIT 2: In fact, according to this, there is a difference between the sys.path in the Python interpreter and that of Py2exe executables. Specifically, sys.path contains only a single entry: the full pathname of the shared code archive. Blah. No fallbacks? Not even the current working directory? I'd try adding W:\ to PATH, but py2exe doesn't conform to any sort of standards for locating system libraries, so it won't work.

Now for the interesting bit. The path it tries to load atexit, os, etc. from is:

W:\\library.zip\<module>.<ext>

Note the single slash after library.zip, but the double slash after the drive letter (someone correct me if this is intended and should work). It looks like if this is a string literal, then since the slash isn't doubled, it's read as an (invalid) escape sequence and the raw character is printed (giving W:\library.zipos.pyd, W:\library.zipos.dll, ... instead of with a slash); if it is NOT a string literal, the double slash might not be normpath'd automatically (as it should be) and so the double slash confuses the module loader. Like I said, I can't just set PYTHONPATH=W:\\library.zip\\ because it ignores that variable.

It may be worth using sys.path.append at the start of my program but hard-coding module paths is an absolute LAST resort, especially since the problem occurs in ONE configuration of an outdated OS.

Any ideas? I have one, which is to normpath the sys.path.. pity I need os for that. Another is to just append os.getenv('PATH') or os.getenv('PYTHONPATH') to sys.path... again, needing the os module. The site module also fails to initialise, so I can't use a .pth file.

I also recently tried the following code at the start of the program:

for pth in sys.path:
    fErr.write(pth)
    fErr.write(' to ')
    pth.replace('\\\\','\\') # Fix Windows 98 pathing issues
    fErr.write(pth)
    fErr.write('\n')

But it can't load linecache.pyc, or anything else for that matter; it can't actually execute those commands from the looks of things. Is there any way to use built-in functionality which doesn't need linecache to modify the sys.path dynamically? Or am I reduced to hard-coding the correct sys.path?

A: 

This isn't a direct answer, but possibly some help. Are you familiar with the -v option in Python. Type python -h to learn more. Note the equivalent to the PYTHONVERBOSE environment variable for py2exe'd scripts is PY2EXE_VERBOSE, as described almost nowhere except in this post by its author. Apparently it can take values of 1 or 2, basically like -v and -vv, though that's slightly different from how PYTHONVERBOSE works.

Note also about your sys.path idea: whether you have imported sys already or not would have no effect on whether you could import os. That is, the Python path (visible in sys.path) is always available, in the sense that it reflects an internal feature of the interpreter which is there whether you've imported the sys module or not.

As with a number of other modules, sys is built-in, so it should always be importable even if your app is almost totally crippled. If it will help, you may be able to use sys.builtin_module_names to see what they are for your version of Python. If the interpreter is running at all that information would be available, so the following may be the smallest useful program you can make to see what you've got:

import sys
print sys.builtin_module_names

Also, I'd advise against trying anything fancy like bundling the .pyc files inside the .exe. You've already got enough working against you being stuck supporting Win98, and if I were you I'd go for the simplest approach that would let me get the job done and move on to more interesting areas. If you could just install Python normally and run from source, you should definitely consider it! :)

Edited to include link to PY2EXE_VERBOSE info per comment by darvids0n.

Peter Hansen
Unfortunately, Peter, it's hard-coded (in an application that I cannot modify) to run this py2exe executable on a command-line (passing in parameters and so forth), so I can't install Python on the client's computer. Thanks for your ideas though, I'll check out PYTHONVERBOSE and sys.builtin_module_names on the target system :)
darvids0n
@darvids0n, It's possible that the calling program doesn't require an actual *exe*, in which case you might try just making a "trampoline" .bat file which invokes Python with the name of your script and the appropriate arguments. Just another thought, FWIW.
Peter Hansen
Peter, just double-checked and the call is actually to a .exe, not just the name of the program. A patchwork solution is to write a C++ exe to execute a .bat on a command-line (horrible, horrible way to do it, but it will work). Hopefully I can solve all this in the near future by completely rewriting the Python program in C++, though, which is my end goal (faster, and it seems more stable as well).
darvids0n
I quickly Googled py2exe's verbose setting and PY2EXE_VERBOSE=1 prints the result of import statements, PY2EXE_VERBOSE=2 prints every try for every import statement. I'll try using this at the target site, should give me a big insight as to where it's looking and where it isn't. Thanks for your help :D
darvids0n
Good find! I never knew py2exe had some extra settings like that. I'll add a link to my answer for posterity.
Peter Hansen