There are several utilities — all with different procedures, limitations, and target operating systems — for getting a Python package and all of its dependencies and turning them into a single binary program that is easy to ship to customers:
- http://wiki.python.org/moin/Freeze
- http://www.pyinstaller.org/
- http://www.py2exe.org/
- http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html
My situation goes one step further: third-party developers will be wanting to write plug-ins, extensions, or add-ons for my application. It is, of course, a daunting question how users on platforms like Windows would most easily install plugins or addons in such a way that my app can easily discover that they have been installed. But beyond that basic question is another: how can a third-party developer bundle their extension with whatever libraries the extension itself needs (which might be binary modules, like lxml) in such a way that the plugin’s dependencies become available for import at the same time that the plugin becomes available.
How can this be approached? Will my application need its own plug-in area on disk and its own plug-in registry to make this tractable? Or are there general mechanisms, that I could avoid writing myself, that would allow an app that is distributed as a single executable to look around and find plugins that are also installed as single files?
You should be able to have a plugins directory that your application scans at runtime (or later) to import the code in question. Here’s an example that should work with regular .py or .pyc code that even works with plugins stored inside zip files (so users could just drop someplugin.zip in the ‘plugins’ directory and have it magically work):
So lets say I have a plugin named “foo.py” in a directory called ‘plugins’ (that is in the base dir of my app) that will add a new capability to my application. The contents might look like this:
I could initialize my plugins when I launch my app like so:
That should work as long as the plugin is either a .py or .pyc file (assuming it is byte-compiled for the platform in question). It can be standalone file or inside of a directory with an init.py or inside of a zip file with the same rules.
How do I know this works? It is how I implemented plugins in PyCI. PyCI is a web application but there’s no reason why this method wouldn’t work for a regular ol’ GUI. For the example above I chose to use an imaginary add_menu_entries() function in conjunction with a Plugin object variable that could be used to add a plugin’s methods to your GUI’s menus.
Hopefully this answer will help you build your own plugin system. If you want to see precisely how it is implemented I recommend you download the PyCI source code and look at plugin_utils.py and the Example plugin in the plugins_enabled directory.