Managing resources in a Python project

You may want to use pkg_resources library that comes with setuptools.

For example, I've made up a quick little package "proj" to illustrate the resource organization scheme I'd use:

proj/setup.py
proj/proj/__init__.py
proj/proj/code.py
proj/proj/resources/__init__.py
proj/proj/resources/images/__init__.py
proj/proj/resources/images/pic1.png
proj/proj/resources/images/pic2.png

Notice how I keep all resources in a separate subpackage.

"code.py" shows how pkg_resources is used to refer to the resource objects:

from pkg_resources import resource_string, resource_listdir

# Itemize data files under proj/resources/images:
print resource_listdir('proj.resources.images', '')
# Get the data file bytes:
print resource_string('proj.resources.images', 'pic2.png').encode('base64')

If you run it, you get:

['__init__.py', '__init__.pyc', 'pic1.png', 'pic2.png']
iVBORw0KGgoAAAANSUhE ...

If you need to treat a resource as a fileobject, use resource_stream().

The code accessing the resources may be anywhere within the subpackage structure of your project, it just needs to refer to subpackage containing the images by full name: proj.resources.images, in this case.

Here's "setup.py":

#!/usr/bin/env python

from setuptools import setup, find_packages

setup(name='proj',
      packages=find_packages(),
      package_data={'': ['*.png']})

Caveat: To test things "locally", that is w/o installing the package first, you'll have to invoke your test scripts from directory that has setup.py. If you're in the same directory as code.py, Python won't know about proj package. So things like proj.resources won't resolve.


The new way of doing this is with importlib. For Python versions older than 3.7 you can add a dependency to importlib_resources and do something like

from importlib_resources import files


def get_resource(module: str, name: str) -> str:
    """Load a textual resource file."""
    return files(module).joinpath(name).read_text(encoding="utf-8")

If your resources live inside the foo/resources sub-module, you would then use get_resource like so

resource_text = get_resource('foo.resources', 'myresource')