[Distutils] Best practices to package a desktop application

PJ Eby pje at telecommunity.com
Thu Dec 29 20:47:44 CET 2011

On Thu, Dec 29, 2011 at 10:04 AM, Jonathan Ballet <jon at multani.info> wrote:

> Hi,
> I hav a desktop application which currently runs only on Linux using
> pyGtk, and is packaged using setuptools/distribute.
> It ships with a number of non-Python files:
> * documentation (README, Changelog, Authors, etc.)
> * .desktop file
> * application specific's icons
> * one man file
> * translation files
> * and one executable script
> The setup.py file looks like this so far:
>    setup(
>        name='myapp',
>        ...
>        packages=["myapp"],
>        package_dir={"myapp": "myapp/"},
>        data_files=[
>            ('share/myapp', ['README', 'Changelog', 'Authors']),
>            ('share/applications', ['myapp.desktop']),
>            ('share/pixmaps', glob.glob('myapp/pixmaps/*')),
>            ('share/man/man1', ['myapp.1']),
>            ('share/locale/fr/LC_MESSAGES', ['mo/fr/myapp.mo']),
>            ... numerous other translation files ...
>        ],
>        entry_points={'console_scripts': ['myapp=myapp:run']},
>    )
> but I somehow have a bad feeling about this, except for the entry point
> (which does a great job).
> When I run ``python setup.py install --prefix="local"``, everything gets
> installed into ``site-packages/myapp/``, Python files and data files,
> which seems to be a feature of setuptools/distribute over plain
> Distutils, whereas it seems to be "cleaner" to split things as described
> by the ``data_files`` setting (and AFAIK, Distutils does just that).
> To be clear, I end up with
>    install/lib/python2.7/site-packages/myapp.egg/myapp.py
>    ...
>    install/lib/python2.7/site-packages/myapp.egg/share/myapp/README
>    ...
>    install/lib/python2.7/site-packages/myapp.egg/share/pixmaps/myapp.png
>    ...
>  install/lib/python2.7/site-packages/myapp.egg/share/locale/fr/LC_MESSAGES/myapp.mo
>    ...
> Whereas I "think" I would like to have this instead, which looks more
> organized to me:
>    install/lib/python2.7/site-packages/myapp.egg/myapp.py
>    ...
>    install/share/myapp/README
>    ...
>    install/share/pixmaps/myapp.png
>    ...
>    install/share/locale/fr/LC_MESSAGES/myapp.mo
>    ...
> Actually, someone reported this "bug" to me, as he was expecting the
> latter whereas he got the former.
> Also, the application currently uses a custom method to find the
> location of those data files (mainly the icons which are used at
> run-time). It does a "terrible" job of trying several well-known
> locations until it either reaches the file or fails with a laconic
> error. I guess using ``pkg_resources`` data-access API would be much
> better, right?

Well, it'd certainly be One Obvious Way to do it.  ;-)

The way I look at it is, if the "data" file is really just a code constant
(i.e., not user-modifiable, nor modified by the program), then it's not
really data, it's part of the code.  A "resource" rather than data.  That's
what the pkg_resources API is for: resources.

Now, if it's data or configuration -- something the user or program will
touch -- then resources don't cut it.  You need a way to set that stuff up.
 Same for documentation, unless it's a program-served resource.

Setuptools doesn't handle those so well; eggs and easy_install were
designed for application *plugins* more than applications; it was assumed
that when you install the self-contained .egg, the application platform
would provide documentation browsing and configuration support.  So, there
was no real provision for installing things to multiple places; even
scripts were a bit of an afterthought.

Finally, I'm also concerned about Linux distribution packagers, and I
> would like to package the application so that it also useful and easy
> for them to repackage and distribute it.

If you actually build a system package (e.g. via bdist_rpm), you'll notice
that your package will actually be installed the way you want: the data
files will go to the right place.  That's because "setup.py install --root
/somepath" or "setup.py install --single-version-externally-managed" will
use distutils conventions for installation.  So, don't worry about the
system packagers, you're doing it right (enough) with your current

After reading setuptools/distribute and distutils documentations, I'm
> still not what is the best way to proceed. Any hints would be greatly
> appreciated.

Based on what you're trying to accomplish, I'd suggest:

1. Use pkg_resources to access constant "resource" files that are not
2. Leave configuration, data, or documentation files as 'data_files' in
your setup.py
3. If your package doesn't need easy_install support (i.e., it has no
dependencies to download), add a setup.cfg with:

single_version_externally_managed = 1
record = RECORD

And tell people to install it using "setup.py install".  This will install
it in a flat (no .egg subdirectory) fashion, and with data files to the
share/ location you expect.

If you *do* need easy_install support, then it's a bit more complex, so I'm
not going to spell that out unless you first say that you do need it.  ;-)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/distutils-sig/attachments/20111229/dc9be7f1/attachment.html>

More information about the Distutils-SIG mailing list