Reputation: 738
I've spent the past couple of days trying to figure out how to build a dep-free application out of a bunch of Python3.4 scripts I have. These include an import of wand
, which requires ImageMagick
to be installed on the machine.
So far so good, the app ran from source code works fine both my dev environments (Debian8/Win10), finding all the necessary dependencies for the Wand module to properly operate.
Troubles come when I try to package the app with PyInstaller.
The build process works as expected, fetching the ImageMagick resources (.dll
s etc) and including them in the output directory alongside the other binaries, but when I run the app on machines without the IM library installed the import fails.
So I went digging into the Wand source code, and realized that dependecies are looked for in global system paths (i.e. C:\ProgramFiles). As a workaround, in my code I simply overridden the os.environ['MAGICK_HOME']
variable with './'
instructing the module to fetch the dependencies in place. And it worked, up to a point.
Now I can start the app: imports won't fail, the GUI is drawn and functionality is not affected. Not until I trigger any Wand-involving action at least, because whenever I do the app crashes raising the following exception:
wand.exceptions.ConfigureError: RegistryKeyLookupFailed `CoderModulesPath' @ error/module.c/GetMagickModulePath/666
It seems clear that the lack of a proper installation system-wide results in broken registry calls (now testing on win), but unfortunately I'm not quite familiar with CTypes (or whatever is being used to bind to the ImageMagick library) and I'm not sure how to proceed on properly building a no-deps app.
As always the first question popping into mind is: am I missing something obvious here?
If not, what would be your suggestions?
Thanks
Upvotes: 2
Views: 1173
Reputation: 17314
What set me on the right path was looking through wand's api.py
:
magick_home = os.environ.get('MAGICK_HOME')
magick_suffix = os.environ.get('WAND_MAGICK_LIBRARY_SUFFIX')
if system == 'Windows':
# ImageMagick installers normally install coder and filter DLLs in
# subfolders, we need to add those folders to PATH, otherwise loading
# the DLL later will fail.
...
Two things are needed:
.spec
's Analysis
's binaries
so they get packaged (or use pyinstaller --add-binary
)sys._MEIPASS
to get pyinstaller's _internal directory, set environment variables MAGICK_HOME
, MAGICK_CODER_MODULE_PATH
and MAGICK_CODER_FILTER_PATH
and add them all to PATH
Armed with this information, I updated my pyinstaller .spec file to read those environment variables and registry entries itself (that would have been the sane solution) run the private library_paths()
from wand
for the DLL locations and diff PATH
before/after to extract the regkey for the coder and filter paths. See here
This of course means the machine running pyinsaller needs to have the ImageMagick DLLs installed in a place wand
can find them.
Upvotes: 0
Reputation: 24439
This is more of a comment, and not really an answer, but I would suggest the following...
MAGICK_HOME
It's wise to set the MAGICK_HOME
environment variable. But I would suggest using os.path
to resolve the systems absolute path; as opposed to relative.
os.environ['MAGICK_HOME'] = os.path.abspath('.')
Very minor, but helps.
.dll's etc
Remember that IM is it's own ecosystem. It's true that wand
only needs MagickWand
& MagickCore
libraries to run, but IM needs all the delegates/coders to be useful. Ensure everything is present, and double check QuickStart.txt.
On your build system, it might be worth checking identify
utility to see other paths that should be included included during PyInstallers bundle stage.
identify -list Configure | grep PATH
On my Mac, I receive the following...
CODER_PATH /usr/local/lib/ImageMagick-6.9.5/modules-Q16/coders
CONFIGURE_PATH /usr/local/etc/ImageMagick-6/
DOCUMENTATION_PATH /usr/local/share/doc/ImageMagick-6
EXECUTABLE_PATH /usr/local/bin
FILTER_PATH /usr/local/lib/ImageMagick-6.9.5/modules-Q16/filters
INCLUDE_PATH /usr/local/include/ImageMagick-6
LIBRARY_PATH /usr/local/lib/ImageMagick-6.9.5
SHARE_PATH /usr/local/share/ImageMagick-6
SHAREARCH_PATH /usr/local/lib/ImageMagick-6.9.5/config-Q16
File policy.xml
under CONFIGURE_PATH
would be ideal to include to ensure your application protects against ImageTragick
Upvotes: 2