Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross-platform support for `data_files` with `setup.py` + `pip`

What would be a cross-platform way of shipping data_files with setup.py (compatible with pip)?

From the official documentation, one needs to write:

setup(...,
    data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
                ('config', ['cfg/data.cfg']),
                ('/etc/init.d', ['init-script'])]
    )

and 'bitmaps', etc. are the sub-directories where those files should go (relative to sys.prefix).

However, this is sub-optimal for cross-platform installations where the standard sub-dir will depend on the system. Additionally, installing the package in developer mode will not place the files where they will later be after the installation, making this process of finding/using resources ultimately hard / annoying to debug.

I have looked into appdirs, but it seems difficult to make it work properly during installation, e.g. if using the user directory for data this gets actually tied to my development environment.

The reason I am asking this is because I have a small Python package that implements a simple GUI and I would like to ship an icon with it.

For the record, I am OK with processing setup.py with setuptools.

like image 759
norok2 Avatar asked Oct 17 '22 15:10

norok2


1 Answers

As suggested in the comments, for bundling resource files, I'd rather use package_data and place the files under some package dir. Example:

project
├── pkg1
│   ├── __init__.py
│   └── icons
│       └── image.png
└── pkg2
    └── __init__.py

Packaging in the setup.py script:

from setuptools import setup


setup(
    ...
    package_data={'pkg1': ['icons/image.png']},
)

Update for accessing resource files in code:

Python 3.7 introduced importlib.resources which replaces the old pkg_resources functionality and offers a modern resource machinery that utilizes pathlib:

filepath = importlib_resources.path('pkg1', 'icons/image.png')

For Python 3.6 and older, there's a backport named importlib_resources. The version-agnostic example is thus:

import sys

if sys.version_info >= (3, 7):
    from importlib import resources as importlib_resources
else:
    import importlib_resources

filepath = importlib_resources.path('pkg1', 'icons/image.png')

Use importlib_resources instead of pkg_resources where possible.

Original answer, for the history only

To reference the resource files in code, use pkg_resources:

import pkg_resources

filepath = pkg_resources.resource_filename('pkg1', 'icons/image.png')

The cross-platform support is thus handled by pkg_resources. Refer to ResourceManager API for available resource access functions.

like image 168
hoefling Avatar answered Oct 21 '22 03:10

hoefling