Read resources from Python packages with enhanced capabilities backported from standard library
npx @tessl/cli install tessl/pypi-importlib-resources@6.5.0A backport of the standard library's importlib.resources module providing enhanced resource access capabilities for Python packages. This library enables reliable access to package resources such as data files, templates, configuration files, and other assets bundled with Python packages in a cross-platform, version-agnostic manner.
pip install importlib_resourcesimport importlib_resourcesMost common imports for resource access:
from importlib_resources import files, as_file, read_text, read_binaryFor functional API:
from importlib_resources import (
open_text, open_binary, read_text, read_binary,
path, contents, is_resource
)For abstract base classes and protocols:
from importlib_resources.abc import ResourceReader, Traversable, TraversableResources, TraversalErrorFor reader implementations:
from importlib_resources.readers import FileReader, ZipReader, NamespaceReader, MultiplexedPathFor simple reader interface:
from importlib_resources.simple import SimpleReader, ResourceContainer, ResourceHandle, TraversableReaderimport importlib_resources
from importlib_resources import files, as_file, read_text
# Access package resources using files() - recommended approach
resource_dir = files('mypackage.data')
config_file = resource_dir / 'config.json'
# Read resource content directly
content = config_file.read_text()
# Get actual file system path using context manager
with as_file(config_file) as path:
# Use path as regular file system path
with open(path) as f:
data = f.read()
# Functional API - alternative approach
text_content = read_text('mypackage.data', 'readme.txt')
binary_content = read_binary('mypackage.data', 'image.png')
# Check if resource exists
from importlib_resources import is_resource
if is_resource('mypackage.data', 'config.json'):
config = read_text('mypackage.data', 'config.json')importlib_resources provides a layered architecture for accessing package resources:
files(), as_file()) providing filesystem-like navigationFileReader, ZipReader, NamespaceReader) that handle different package storage formatsread_text, open_binary, etc.) for backward compatibilitySimpleReader) for implementing custom resource providersThis design enables transparent access to resources whether packages are installed as directories, zip files, or namespace packages, while providing both modern and legacy APIs for different use cases.
Core functions for discovering and navigating package resources using the modern Traversable interface.
def files(anchor: Optional[Anchor] = None) -> Traversable:
"""
Get a Traversable resource for an anchor.
Parameters:
- anchor: Union[types.ModuleType, str, None] - Package as module object, string name, or None to infer from caller
Returns:
Traversable: Object for navigating package resources
"""def as_file(path) -> ContextManager:
"""
Given a Traversable object, return that object as a path on the local file system in a context manager.
Parameters:
- path: Traversable object representing a resource
Returns:
ContextManager yielding pathlib.Path: Temporary path to resource on file system
"""Functional API for directly reading resource content with simplified parameter handling.
def read_text(anchor, *path_names, encoding=_MISSING, errors='strict') -> str:
"""
Read and return contents of resource within package as str.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to resource file
- encoding: str - Text encoding (default: 'utf-8')
- errors: str - Error handling mode (default: 'strict')
Returns:
str: Resource content as text
"""def read_binary(anchor, *path_names) -> bytes:
"""
Read and return contents of resource within package as bytes.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to resource file
Returns:
bytes: Resource content as binary data
"""Functions for opening resources with file-like objects for streaming access.
def open_text(anchor, *path_names, encoding=_MISSING, errors='strict') -> TextIO:
"""
Open for text reading the resource within package.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to resource file
- encoding: str - Text encoding (default: 'utf-8', required when using multiple path_names)
- errors: str - Error handling mode (default: 'strict')
Returns:
TextIO: Text file-like object for reading
"""def open_binary(anchor, *path_names) -> BinaryIO:
"""
Open for binary reading the resource within package.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to resource file
Returns:
BinaryIO: Binary file-like object for reading
"""Functions for accessing resources as file system paths.
def path(anchor, *path_names) -> ContextManager:
"""
Return the path to the resource as an actual file system path.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to resource file
Returns:
ContextManager yielding pathlib.Path: Temporary path to resource
"""Functions for checking resource existence and listing package contents.
def is_resource(anchor, *path_names) -> bool:
"""
Return True if there is a resource at the specified path within the package.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to resource
Returns:
bool: True if resource exists and is a file
"""def contents(anchor, *path_names) -> Iterator[str]:
"""
Return an iterable over the named resources within the package.
DEPRECATED: Use files(anchor).iterdir() instead.
Parameters:
- anchor: Union[types.ModuleType, str] - Package as module object or string name
- path_names: Variable path components to directory
Returns:
Iterator[str]: Names of resources in the directory
"""Abstract base class for implementing custom resource readers.
class ResourceReader(metaclass=abc.ABCMeta):
"""Abstract base class for loaders to provide resource reading support."""
@abc.abstractmethod
def open_resource(self, resource: Text) -> BinaryIO:
"""
Return an opened, file-like object for binary reading.
The 'resource' argument is expected to represent only a file name.
If the resource cannot be found, FileNotFoundError is raised.
Parameters:
- resource: Text - Resource file name
Returns:
BinaryIO: Binary file-like object
Raises:
FileNotFoundError: If resource cannot be found
"""
@abc.abstractmethod
def resource_path(self, resource: Text) -> Text:
"""
Return the file system path to the specified resource.
The 'resource' argument is expected to represent only a file name.
If the resource does not exist on the file system, raise FileNotFoundError.
Parameters:
- resource: Text - Resource file name
Returns:
Text: File system path to resource
Raises:
FileNotFoundError: If resource does not exist on file system
"""
@abc.abstractmethod
def is_resource(self, path: Text) -> bool:
"""
Return True if the named 'path' is a resource.
Files are resources, directories are not.
Parameters:
- path: Text - Resource path
Returns:
bool: True if path is a resource (file), False for directories
"""
@abc.abstractmethod
def contents(self) -> Iterable[str]:
"""
Return an iterable of entries in `package`.
Returns:
Iterable[str]: Resource names
"""Protocol defining path-like interface for resource navigation.
class Traversable(Protocol):
"""
An object with a subset of pathlib.Path methods suitable for traversing directories and opening files.
"""
@abc.abstractmethod
def iterdir(self) -> Iterator["Traversable"]:
"""
Yield Traversable objects in self.
Returns:
Iterator[Traversable]: Child resources
"""
def read_bytes(self) -> bytes:
"""
Read contents of self as bytes.
Returns:
bytes: Resource content as binary data
"""
def read_text(self, encoding: Optional[str] = None, errors: Optional[str] = None) -> str:
"""
Read contents of self as text.
Parameters:
- encoding: Optional[str] - Text encoding
- errors: Optional[str] - Error handling mode
Returns:
str: Resource content as text
"""
@abc.abstractmethod
def is_dir(self) -> bool:
"""
Return True if self is a directory.
Returns:
bool: True if directory
"""
@abc.abstractmethod
def is_file(self) -> bool:
"""
Return True if self is a file.
Returns:
bool: True if file
"""
def joinpath(self, *descendants: Union[str, os.PathLike[str]]) -> "Traversable":
"""
Return Traversable resolved with any descendants applied.
Parameters:
- descendants: Variable path segments relative to self
Returns:
Traversable: Resource at joined path
Raises:
TraversalError: If target not found during traversal
"""
def __truediv__(self, child: Union[str, os.PathLike[str]]) -> "Traversable":
"""
Return Traversable child in self.
Parameters:
- child: Path segment for child resource
Returns:
Traversable: Child resource
"""
@overload
def open(self, mode: Literal['r'] = 'r', *args: Any, **kwargs: Any) -> TextIO: ...
@overload
def open(self, mode: Literal['rb'], *args: Any, **kwargs: Any) -> BinaryIO: ...
@abc.abstractmethod
def open(self, mode: str = 'r', *args: Any, **kwargs: Any) -> Union[TextIO, BinaryIO]:
"""
Open resource for reading.
Parameters:
- mode: str - Open mode ('r' for text, 'rb' for binary)
- args, kwargs: Additional arguments for opening
Returns:
Union[TextIO, BinaryIO]: File-like object
"""
@property
@abc.abstractmethod
def name(self) -> str:
"""
The base name of this object without any parent references.
Returns:
str: Resource name
"""Extended ResourceReader that provides traversable access to resources.
class TraversableResources(ResourceReader):
"""
The required interface for providing traversable resources.
"""
@abc.abstractmethod
def files(self) -> "Traversable":
"""
Return a Traversable object for the loaded package.
Returns:
Traversable: Root traversable for package resources
"""Reader for package resources stored as regular files on the filesystem.
class FileReader(TraversableResources):
"""
Reader for package resources stored as regular files on the filesystem.
"""
def __init__(self, loader):
"""
Initialize with a loader that has a path attribute.
Parameters:
- loader: Loader object with path attribute pointing to package directory
"""
def resource_path(self, resource: str) -> str:
"""
Return the file system path to prevent resources.path() from creating a temporary copy.
Parameters:
- resource: str - Resource file name
Returns:
str: Absolute path to resource on filesystem
"""
def files(self) -> Traversable:
"""
Return a Traversable object for the package directory.
Returns:
Traversable: Path object for the package directory
"""Reader for package resources stored within zip files.
class ZipReader(TraversableResources):
"""
Reader for package resources stored within zip files.
"""
def __init__(self, loader, module: str):
"""
Initialize with a zip loader and module name.
Parameters:
- loader: Zip loader with archive and prefix attributes
- module: str - Module name for the package
"""
def open_resource(self, resource: str) -> BinaryIO:
"""
Return an opened, file-like object for binary reading.
Parameters:
- resource: str - Resource file name
Returns:
BinaryIO: Binary file-like object
Raises:
FileNotFoundError: If resource cannot be found
"""
def is_resource(self, path: str) -> bool:
"""
Return True if the named path is a resource (workaround for zipfile issues).
Parameters:
- path: str - Resource path
Returns:
bool: True if path is a resource and exists
"""
def files(self) -> Traversable:
"""
Return a Traversable object for the zip package.
Returns:
Traversable: ZipPath object for navigating zip contents
"""Reader for namespace packages that may span multiple directories.
class NamespaceReader(TraversableResources):
"""
Reader for namespace packages that may span multiple directories.
"""
def __init__(self, namespace_path):
"""
Initialize with a namespace path.
Parameters:
- namespace_path: NamespacePath object with multiple locations
Raises:
ValueError: If path is not a valid namespace path
"""
def resource_path(self, resource: str) -> str:
"""
Return the file system path to prevent resources.path() from creating a temporary copy.
Parameters:
- resource: str - Resource file name
Returns:
str: Path to resource
"""
def files(self) -> Traversable:
"""
Return a MultiplexedPath for the namespace package.
Returns:
Traversable: MultiplexedPath spanning all namespace locations
"""Traversable implementation that merges multiple paths for namespace packages.
class MultiplexedPath(Traversable):
"""
Given a series of Traversable objects, implement a merged version of the interface across all objects.
Useful for namespace packages which may be multihomed at a single name.
"""
def __init__(self, *paths: Traversable):
"""
Initialize with multiple Traversable paths.
Parameters:
- paths: Variable number of Traversable objects to merge
Raises:
FileNotFoundError: If no paths provided
NotADirectoryError: If any path is not a directory
"""
def iterdir(self) -> Iterator[Traversable]:
"""
Yield Traversable objects in self, merging children from all paths.
Returns:
Iterator[Traversable]: Merged children from all paths
"""
def is_dir(self) -> bool:
"""
Return True (MultiplexedPath represents directories only).
Returns:
bool: Always True
"""
def is_file(self) -> bool:
"""
Return False (MultiplexedPath represents directories only).
Returns:
bool: Always False
"""
def joinpath(self, *descendants: StrPath) -> Traversable:
"""
Return Traversable resolved with any descendants applied.
Parameters:
- descendants: Variable path segments relative to self
Returns:
Traversable: Resource at joined path
"""
def open(self, *args, **kwargs):
"""
Raise FileNotFoundError (directories cannot be opened).
Raises:
FileNotFoundError: Always raised
"""
@property
def name(self) -> str:
"""
The base name of this object.
Returns:
str: Name from first path
"""The minimum, low-level interface required from a resource provider.
class SimpleReader(metaclass=abc.ABCMeta):
"""
The minimum, low-level interface required from a resource provider.
"""
@property
@abc.abstractmethod
def package(self) -> str:
"""
The name of the package for which this reader loads resources.
Returns:
str: Package name
"""
@abc.abstractmethod
def children(self) -> List[SimpleReader]:
"""
Obtain an iterable of SimpleReader for available child containers (e.g. directories).
Returns:
List[SimpleReader]: Child readers for subdirectories
"""
@abc.abstractmethod
def resources(self) -> List[str]:
"""
Obtain available named resources for this virtual package.
Returns:
List[str]: Resource file names
"""
@abc.abstractmethod
def open_binary(self, resource: str) -> BinaryIO:
"""
Obtain a File-like for a named resource.
Parameters:
- resource: str - Resource file name
Returns:
BinaryIO: Binary file-like object for the resource
"""
@property
def name(self) -> str:
"""
The base name of the package.
Returns:
str: Last component of package name
"""Traversable container for a package's resources via its reader.
class ResourceContainer(Traversable):
"""
Traversable container for a package's resources via its reader.
"""
def __init__(self, reader: SimpleReader):
"""
Initialize with a SimpleReader.
Parameters:
- reader: SimpleReader instance
"""
def is_dir(self) -> bool:
"""
Return True (containers are directories).
Returns:
bool: Always True
"""
def is_file(self) -> bool:
"""
Return False (containers are directories).
Returns:
bool: Always False
"""
def iterdir(self) -> Iterator[Traversable]:
"""
Yield Traversable objects in self.
Returns:
Iterator[Traversable]: Child resources and containers
"""
def open(self, *args, **kwargs):
"""
Raise IsADirectoryError (directories cannot be opened).
Raises:
IsADirectoryError: Always raised
"""Handle to a named resource in a ResourceReader.
class ResourceHandle(Traversable):
"""
Handle to a named resource in a ResourceReader.
"""
def __init__(self, parent: ResourceContainer, name: str):
"""
Initialize with parent container and resource name.
Parameters:
- parent: ResourceContainer that contains this resource
- name: str - Resource file name
"""
def is_file(self) -> bool:
"""
Return True (handles represent files).
Returns:
bool: Always True
"""
def is_dir(self) -> bool:
"""
Return False (handles represent files).
Returns:
bool: Always False
"""
def open(self, mode: str = 'r', *args, **kwargs) -> Union[TextIO, BinaryIO]:
"""
Open the resource for reading.
Parameters:
- mode: str - Open mode ('r' for text, 'rb' for binary)
- args, kwargs: Additional arguments for text mode
Returns:
Union[TextIO, BinaryIO]: File-like object
"""
def joinpath(self, name: str):
"""
Raise RuntimeError (cannot traverse into a resource).
Raises:
RuntimeError: Always raised
"""A TraversableResources based on SimpleReader.
class TraversableReader(TraversableResources, SimpleReader):
"""
A TraversableResources based on SimpleReader. Resource providers may derive from this class
to provide the TraversableResources interface by supplying the SimpleReader interface.
"""
def files(self) -> Traversable:
"""
Return a ResourceContainer for this reader.
Returns:
Traversable: ResourceContainer wrapping this reader
"""from typing import (
Any, BinaryIO, Iterable, Iterator, List, Literal, NoReturn,
Optional, Protocol, Text, TextIO, Union, overload, runtime_checkable
)
import types
import os
import abc
Package = Union[types.ModuleType, str]
Anchor = Package
StrPath = Union[str, os.PathLike[str]]
Text = str # For compatibility with typing in source code
_MISSING = object() # Sentinel for default encoding parameterclass TraversalError(Exception):
"""Exception raised during traversal operations when target cannot be found."""
passfrom importlib_resources import files, as_file
import json
# Access data files in your package
data_dir = files('mypackage.data')
config_file = data_dir / 'config.json'
# Read JSON configuration
config_data = json.loads(config_file.read_text())
# Work with binary data
image_file = data_dir / 'logo.png'
with as_file(image_file) as temp_path:
# Use with libraries that need file paths
from PIL import Image
img = Image.open(temp_path)from importlib_resources import files
import jinja2
# Access template files
templates_dir = files('myapp.templates')
template_file = templates_dir / 'email.html'
# Use with template engines
template_content = template_file.read_text()
template = jinja2.Template(template_content)from importlib_resources import files
# List all resources in a package directory
resource_dir = files('mypackage.resources')
for resource in resource_dir.iterdir():
if resource.is_file():
print(f"Found resource: {resource.name}")
# Process each resource
content = resource.read_text()from importlib_resources import read_text, is_resource, path
# Check if resource exists before accessing
if is_resource('mypackage.data', 'settings.ini'):
settings = read_text('mypackage.data', 'settings.ini')
# Access nested resources
config = read_text('mypackage.config', 'production', 'database.yaml')
# Get temporary file path for external tools
with path('mypackage.data', 'input.csv') as csv_path:
# Use with pandas or other libraries requiring file paths
import pandas as pd
df = pd.read_csv(csv_path)from importlib_resources.readers import FileReader, ZipReader, NamespaceReader
# For packages installed as directories
file_reader = FileReader(some_loader)
files = file_reader.files()
# For packages in zip files
zip_reader = ZipReader(zip_loader, 'mypackage')
zip_files = zip_reader.files()
# For namespace packages
namespace_reader = NamespaceReader(namespace_path)
namespace_files = namespace_reader.files()from importlib_resources.simple import SimpleReader, TraversableReader
from typing import List, BinaryIO
class CustomReader(TraversableReader):
"""Custom resource reader implementation."""
@property
def package(self) -> str:
return "mypackage"
def children(self) -> List[SimpleReader]:
return [] # Return child readers for subdirectories
def resources(self) -> List[str]:
return ["config.json", "data.txt"] # Return available resources
def open_binary(self, resource: str) -> BinaryIO:
# Return binary file-like object for the resource
return open(f"/path/to/{resource}", "rb")
# Use the custom reader
reader = CustomReader()
files = reader.files()