The following guide will help you migrate commonpkg_resources APIs toimportlib_resources. Only a small number of the most common APIs aresupported byimportlib_resources, so projects that use other features(e.g. entry points) will have to find other solutions.importlib_resources primarily supports the followingbasic resourceaccess APIs:
pkg_resources.resource_filename()
pkg_resources.resource_stream()
pkg_resources.resource_string()
pkg_resources.resource_listdir()
pkg_resources.resource_isdir()
Note that although the steps below provide a drop-in replacement for theabove methods, for many use-cases, a better approach is to use theTraversable path fromfiles() directly.
resource_filename() is one of the more interesting APIs because itguarantees that the return value names a file on the file system. This meansthat if the resource is in a zip file,pkg_resources will extract thefile and return the name of the temporary file it created. The problem isthatpkg_resources alsoimplicitly cleans up this temporary file,without control over its lifetime by the programmer.
importlib_resources takes a different approach. Its equivalent API is thefiles() function, which returns a Traversable object implementing asubset of thepathlib.Path interface suitable for reading the contents andprovides a wrapper for creating a temporary file on the system in acontext whose lifetime is managed by the user. Note thoughthat if the resource isalready on the file system,importlib_resourcesstill returns a context manager, but nothing needs to get cleaned up.
Here’s an example frompkg_resources:
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'withimportlib_resources.as_file(ref)aspath:# 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 itto stick around for a while? One way of doing this is to use ancontextlib.ExitStack instance and manage the resource explicitly:
fromcontextlibimportExitStackfile_manager=ExitStack()ref=importlib_resources.files('my.package')/'resource.dat'path=file_manager.enter_context(importlib_resources.as_file(ref))
Nowpath will continue to exist until you explicitly callfile_manager.close(). What if you want the file to exist until theprocess exits, or you can’t passfile_manager around in your code? Use anatexit handler:
importatexitfile_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 becleaned up when Python exits.
pkg_resources.resource_stream() returns a readable file-like object openedin binary mode. When you read from the returned file-like object, you getbytes. E.g.:
withpkg_resources.resource_stream('my.package','resource.dat')asfp:my_bytes=fp.read()
The equivalent code inimportlib_resources is pretty straightforward:
ref=importlib_resources.files('my.package').joinpath('resource.dat')withref.open('rb')asfp:my_bytes=fp.read()
In Python 2,pkg_resources.resource_string() returns the contents of aresource as astr. In Python 3, this function is a misnomer; it actuallyreturns the contents of the named resource asbytes. That’s why thefollowing example is often written for clarity as:
frompkg_resourcesimportresource_stringasresource_bytescontents=resource_bytes('my.package','resource.dat')
This can be easily rewritten like so:
ref=importlib_resources.files('my.package').joinpath('resource.dat')contents=ref.read_bytes()
This function lists the entries in the package, both files and directories,but it does not recurse into subdirectories, e.g.:
forentryinpkg_resources.resource_listdir('my.package','subpackage'):print(entry)
This is easily rewritten using the following idiom:
forentryinimportlib_resources.files('my.package.subpackage').iterdir():print(entry.name)
Note:
Traversable.iterdir() returnsall the entries in thesubpackage, i.e. both resources (files) and non-resources (directories).
Traversable.iterdir() returns additional traversable objects, which ifdirectories can also be iterated over (recursively).
Traversable.iterdir(), likepathlib.Path returns an iterator, not aconcrete sequence.
The order in which the elements are returned is undefined.
You can askpkg_resources to tell you whether a particular resource insidea package is a directory or not:
ifpkg_resources.resource_isdir('my.package','resource'):print('A directory')
Theimportlib_resources equivalent is straightforward:
ifimportlib_resources.files('my.package').joinpath('resource').is_dir():print('A directory')