takluyver / pynsist

Build Windows installers for Python applications
https://pynsist.readthedocs.io/
Other
896 stars 119 forks source link

Use site.addsitedir instead of sys.path.insert #137

Closed fakeidentity closed 6 years ago

fakeidentity commented 6 years ago

The application I'm using pynsist with requires pywin32. pywin32 uses a .pth file to add modules organised under its /win32 directory (like win32console.pyd) to the path.

.pth files are apparently something that the built in site module handles: https://docs.python.org/3/library/site.html

I can include the necessary pywin32 files with pynsist easy enough. Either by extracting the pywin32 installer and copying the appropriate stuff to a pynsist_pkgs directory, OR simply adding pypiwin32 (which seems very very unofficial and may or not be dead) to the pypi_wheels installer.cfg option.

Personally, I'm using pypiwin32. After pynsist has built/installed the app, the pypiwin32.pth file (and the /win32 directory it references) end up in /pkgs - where they should be.

The problem is that python only looks for .pth files within a site-packages directory, and simply inserting a path at the start of sys.path isn't enough to fool it. So despite all the files being in the right place, when I attempt to import win32console in my pynsist app, it fails with ImportError: DLL load failed: The specified module could not be found. ... because the .pth file isn't parsed and ultimately I suppose it doesn't know where to look for win32console.

Apologies for being long-winded but I hope that explains the issue well enough. I wanted to include something for google to find for other people trying to get pywin32 working with pynsist :)

The solution I found is to replace sys.path.insert(0, pkgdir) with site.addsitedir(pkgdir) in the .launch.py script(s) that pynsist creates. Suddenly the .pth files in /pkgs are used and I can import win32console and whatever else.

For now I'm using the extra_preamble option, where the contents of the preamble are simply import site; site.addsitedir(pkgdir). Since the preamble is just inserted into the launch script, pkgdir is already assigned - so this works well.

But since /pkgs does function precisely as an additional site-packages directory, it seems appropriate to use site.addsitedir, no?

takluyver commented 6 years ago

Thanks for the detailed description. I think you're right, it should call site.addsitedir().

I'd like it to still do sys.path.insert() first, so that the pkgs/ directory is the first one on sys.path rather than the last. This way, if you try to launch the app with a separately installed Python, it uses the app's bundled modules over any that you've installed elsewhere.

Do you want to make a PR?

takluyver commented 6 years ago

Fixed in #138