
Every so often, there's a perennial debate on the distutils-sig about script filenames. Should they have .py extensions or not? What about .pyw apps on Windows? What about Mac OS? and so on.
It occurred to me this morning that we now have a new tool for resolving this issue in setuptools' "entry points" feature. For various reasons, it's common practice to write scripts as Python modules with a "main" function of some kind. These modules are then run directly with '-m', or use "if __name__=='__main__'", or have a short script that imports the main function and runs it.
So, if these "main" functions were simply declared as entry points in the project's setup script, then EasyInstall could automatically generate stub scripts for them, in a platform-appropriate fashion, with no need for '-m', "__name__=='__main__'", or fiddling with file extensions. For example, if PyUnit were distributed as an egg, with the following entry points:
[distutils.console_apps] unittest = unittest:main
Then EasyInstall could create a 'unittest' script with a #! line on Unix-like OSes, and a 'unittest.py', 'unittest.bat', or 'unittest.exe' on Windows. In each case, the generated program would simply load and run the entry point with no arguments. Similarly, there could be a "distutils.gui_apps" entry point group, which could be handled differently according to the target platform. (For example, by creating desktop or menu shortcuts on Windows.) And, tools that create standalone applications or installers could use this information to create other kinds of wrappers around the same entry points.
In order for this to work well, there are obviously some details to be worked out. Unix-like OSes are pretty obvious: #! line and no extension should work fine for all app types' executables. But adding desktop icons or menu items for GUI apps is very platform-specific (e.g. Gnome vs. KDE). So, whatever mechanism is used needs to be extensible and configurable. Ideally, it should be possible for the platform's Python installation to provide the necessary hooks to do this, because I personally don't want to have to write and maintain all that code. :)
As for Mac OS, I have almost no experience with it, so I'm not sure what GUI applications there need. Does everything need py2app? If you have a wx-based app, would you just make a #! script? Bob Ippolito previously mentioned that you don't "install" applications there, that people just drag applications wherever they want them rather than using shortcuts, so at least that part isn't a problem. :)
On Windows, I'd say that applications are pretty much always better as .exe's, whether console or GUI. The .py/.pyw form is dependent on a single global consistent version of Python, but it's possible and reasonable to have multiple Python versions installed. An .exe also has a lot more control over how Python is initialized, and that can be particularly important for applications. On the other hand, in the short run I can also see using .bat or .cmd files for console apps, and .pyw for GUI apps, just to have something that works and wait for the path management use cases for various .exe options to work themselves out.
Anyway, my idea here is that when using setuptools, you would define entry points instead of creating scripts and listing them in setup(). Then, using either EasyInstall or "setup.py install" would automatically create platform-appropriate scripts.
Some of the open questions:
* What groups (if any) should exist besides "console_apps" and "gui_apps"?
* How can we allow easy control of installation options on the target system? (e.g., I only want Windows Programs Menu items, you only want desktop shortcuts for KDE, etc., but we can't have six billion command line options, and all of this stuff can't go into setuptools anyway)
* Should launchers hardcode their sys.path? This would prevent breakage caused by installing new and incompatible versions of their dependencies, but require an explicit upgrade action to update them, and make the installation process more complex.
Thoughts, anyone?