The following guide will help you migrate common
pkg_resources APIs to
importlib_resources. Only a small number of the most common APIs are
importlib_resources, so projects that use other features
(e.g. entry points) will have to find other solutions.
importlib_resources primarily supports the following basic resource
Note that although the steps below provide a drop-in replacement for the
above methods, for many use-cases, a better approach is to use the
Traversable path from
resource_filename() is one of the more interesting APIs because it
guarantees that the return value names a file on the file system. This means
that if the resource is in a zip file,
pkg_resources will extract the
file and return the name of the temporary file it created. The problem is
pkg_resources also implicitly cleans up this temporary file,
without control over its lifetime by the programmer.
importlib_resources takes a different approach. Its equivalent API is the
files() function, which returns a Traversable object implementing a
subset of the
pathlib.Path interface suitable for reading the contents and
provides a wrapper for creating a temporary file on the system in a
context whose lifetime is managed by the user. Note though
that if the resource is already on the file system,
still returns a context manager, but nothing needs to get cleaned up.
Here’s an example from
path = pkg_resources.resource_filename('my.package', 'resource.dat')
The best way to convert this is with the following idiom:
ref = importlib_resources.files('my.package') / 'resource.dat' with importlib_resources.as_file(ref) as path: # Do something with path. After the with-statement exits, any # temporary file created will be immediately cleaned up.
That’s all fine if you only need the file temporarily, but what if you need it
to stick around for a while? One way of doing this is to use an
contextlib.ExitStack instance and manage the resource explicitly:
from contextlib import ExitStack file_manager = ExitStack() ref = importlib_resources.files('my.package') / 'resource.dat' path = file_manager.enter_context( importlib_resources.as_file(ref))
path will continue to exist until you explicitly call
file_manager.close(). What if you want the file to exist until the
process exits, or you can’t pass
file_manager around in your code? Use an
import atexit file_manager = ExitStack() atexit.register(file_manager.close) ref = importlib_resources.files('my.package') / 'resource.dat' path = file_manager.enter_context( importlib_resources.as_file(ref))
Assuming your Python interpreter exits gracefully, the temporary file will be cleaned up when Python exits.
pkg_resources.resource_stream() returns a readable file-like object opened
in binary mode. When you read from the returned file-like object, you get
with pkg_resources.resource_stream('my.package', 'resource.dat') as fp: my_bytes = fp.read()
The equivalent code in
importlib_resources is pretty straightforward:
ref = importlib_resources.files('my.package').joinpath('resource.dat') with ref.open('rb') as fp: my_bytes = fp.read()
In Python 2,
pkg_resources.resource_string() returns the contents of a
resource as a
str. In Python 3, this function is a misnomer; it actually
returns the contents of the named resource as
bytes. That’s why the
following example is often written for clarity as:
from pkg_resources import resource_string as resource_bytes contents = resource_bytes('my.package', 'resource.dat')
This can be easily rewritten like so:
ref = importlib_resources.files('my.package').joinpath('resource.dat') contents = f.read_bytes()
This function lists the entries in the package, both files and directories, but it does not recurse into subdirectories, e.g.:
for entry in pkg_resources.resource_listdir('my.package', 'subpackage'): print(entry)
This is easily rewritten using the following idiom:
for entry in importlib_resources.files('my.package.subpackage').iterdir(): print(entry.name)
Traversable.iterdir()returns all the entries in the subpackage, i.e. both resources (files) and non-resources (directories).
Traversable.iterdir()returns additional traversable objects, which if directories can also be iterated over (recursively).
pathlib.Pathreturns an iterator, not a concrete sequence.
The order in which the elements are returned is undefined.
You can ask
pkg_resources to tell you whether a particular resource inside
a package is a directory or not:
if pkg_resources.resource_isdir('my.package', 'resource'): print('A directory')
importlib_resources equivalent is straightforward:
if importlib_resources.files('my.package').joinpath('resource').is_dir(): print('A directory')