importlib.resources: Read Packaged Files

Published on: August 27, 2026
Reading time: 7 minutes
Detailed view of programming code in a dark theme on a computer screen.

The importlib.resources module provides a modern API for accessing data files distributed inside Python packages. It can read templates, default configuration, public certificates, schemas, text, images, and other assets without assuming that the package lives in an ordinary filesystem directory. The same code can work with normal installations, ZIP files, packaged applications, and compatible loaders.

Building a path from Path(__file__).parent works in many projects but fails when a resource has no permanent physical file. The Traversable abstraction behaves like a resource path, while as_file() temporarily materializes a real path when an external library requires one.

Include resources in the package

Before reading an asset, the build must include it in the installed distribution.

my_package/
    __init__.py
    data/
        defaults.json
        schema.json

Build and inspect the wheel or source distribution. A file present in the repository may still be missing from the published package.

Start with files

The modern API begins with importlib.resources.files().

from importlib.resources import files

root = files("my_package")
resource = root.joinpath("data/defaults.json")

The result is a Traversable, not necessarily a pathlib.Path.

Use a module as the anchor

Modern versions can accept a module or package object as the anchor.

from importlib.resources import files
from . import resources

root = files(resources)

Using an imported object reduces rename errors compared with repeating a string.

Read text

A Traversable resource provides read_text().

text = (
    files("my_package")
    .joinpath("data/defaults.json")
    .read_text(encoding="utf-8")
)

Specify the encoding explicitly. UTF-8 is a common choice for project-owned assets.

Read bytes

Use read_bytes() for images, binary models, and certificates.

data = (
    files("my_package")
    .joinpath("images/logo.png")
    .read_bytes()
)

Apply size limits when resources can come from third-party plugins or distributions.

Open a stream

open() supports incremental access.

resource = files("my_package").joinpath("data/large.csv")
with resource.open("rb") as stream:
    header = stream.read(1024)

This avoids loading the entire asset into memory, although backend behavior can vary.

Traversable is not Path

The interface includes iterdir(), is_file(), is_dir(), joinpath(), open(), read_text(), and read_bytes().

Do not call Path-specific methods such as resolve() unless you first obtain a physical path with as_file().

List package resources

Use iterdir() to enumerate children.

directory = files("my_package").joinpath("templates")
for item in directory.iterdir():
    if item.is_file():
        print(item.name)

Sort by name when deterministic processing matters.

joinpath() works with nested components.

resource = (
    files("my_package")
    .joinpath("templates")
    .joinpath("emails")
    .joinpath("welcome.html")
)

Avoid operating-system-specific separators.

Check the resource type

Use is_file() and is_dir().

if not resource.is_file():
    raise FileNotFoundError("template is missing")

A required missing resource usually indicates a packaging error.

Use as_file for APIs that require Path

Some libraries accept only a filename. as_file() returns a context manager containing a physical path.

from importlib.resources import as_file, files

resource = files("my_package").joinpath("models/model.bin")
with as_file(resource) as path:
    load_model(path)

If the package is inside a ZIP, the resource may be extracted temporarily.

Temporary-path lifetime

The path returned by as_file() is guaranteed only inside the with block.

Do not store it for later use. Complete the path-based operation before leaving the context.

Directories with as_file

Modern versions can materialize Traversable directories in supported cases.

Check the project’s minimum Python version and keep all use within the context.

ZIP imports

A package can be imported directly from a ZIP archive, where resources have no permanent individual paths.

Traversable reads them through the loader, and as_file() materializes them only when necessary.

Executable .pyz applications

Resources can be included in a zipapp if the build copies them into the archive.

See Python zipapp and test the final .pyz.

Resources are not user-data storage

Package resources are assets distributed with code and should normally be treated as read-only.

Mutable configuration, uploads, and databases belong in application data directories, not inside an installed package.

Defaults versus live configuration

A common pattern is reading package defaults and merging external settings.

defaults = json.loads(
    files("my_package")
    .joinpath("data/defaults.json")
    .read_text("utf-8")
)

Do not attempt to write the merged result back to the resource.

Templates

Small templates can be read as text and passed to a rendering engine.

Configure escaping and autoescape in the final context. A packaged template does not make inserted user data safe automatically.

Schemas and migrations

JSON Schema, SQL, and migration files can be package resources.

Version schemas and test that every required file is present in the built wheel.

Certificates

Public certificates and trust bundles can be included, but private keys and secrets should not be distributed in a package.

When a TLS library requires a path, use as_file() while constructing the context.

Large binary assets

Large assets increase wheel size, downloads, installation time, and memory use. Consider a separate distribution or a verified download process.

Do not call read_bytes() on a huge model when a stream or temporary file is more appropriate.

Plugin resources

Each plugin should anchor resource lookup in its own package.

def load_template(plugin_module):
    return files(plugin_module).joinpath("template.html").read_text("utf-8")

Validate plugins before import and limit resource size.

Loader support

The API depends on resource support from the package loader. Custom loaders need to implement the appropriate protocols.

Test custom importers and frozen executables using the final artifact.

Namespace packages

Resources in namespace packages need care because the namespace can span multiple locations.

Avoid conflicting resource names across distributions and test the installed composition.

Resource names

Use known relative names and components. Do not pass arbitrary user input directly to joinpath().

Applications should maintain an allowlist of assets.

Logical path traversal

An endpoint receiving template=... should map public identifiers to internal names.

TEMPLATES = {
    "welcome": "templates/welcome.html",
    "receipt": "templates/receipt.html",
}

This avoids exposing the resource tree directly.

Validate content

Installed resources can come from a damaged or compromised dependency. Validate JSON, schemas, signatures, or hashes when integrity matters.

Do not execute a text resource as code merely because it came from a package.

Importing the anchor

The anchor must be resolved by the import system. Importing a package can execute its __init__.py.

Keep initializers lightweight and free of unexpected side effects. Isolate third-party plugin loading where appropriate.

Performance

Applications may cache small immutable resources that are read repeatedly.

Use a bounded cache with a clear memory policy and do not assume the loader keeps streams open.

Caches in tests

Application-level caching can return stale data when tests replace loaders or package fixtures.

Provide a cache-clear function or inject the resource provider.

Older functional APIs

Older convenience functions exist in previous versions, but modern code should generally start with files().

Avoid building new systems on APIs marked legacy or deprecated.

Version compatibility

Signatures, anchor naming, and directory support in as_file() have evolved.

Declare a minimum Python version, use feature detection when necessary, or use the importlib_resources backport for older interpreters.

The backport package

The external importlib_resources package brings modern APIs to earlier Python versions.

Centralize compatibility imports so the rest of the application uses one interface.

Inspect the wheel

Build and inspect the distribution.

python -m build
unzip -l dist/*.whl

Confirm that templates, schemas, and data appear in the expected package paths.

sdist versus wheel

A resource can be included in one distribution format and missing from another depending on configuration.

Test installations from both formats when both are published.

Editable installations

An editable install may read resources directly from the checkout and hide packaging mistakes.

CI should install the built wheel in a clean environment.

Frozen applications

Packagers such as PyInstaller may need explicit data-file configuration and custom resource support.

Test files() and as_file() in the final executable.

Concurrency

Reading immutable Traversables is straightforward, but as_file() can create context-specific temporary files.

Do not share a temporary path beyond its guaranteed lifetime across threads or processes.

Cleanup

The as_file() context manager handles temporary materialization cleanup.

Do not move or manually remove the path it provides.

Observability

Record logical resource name, package, distribution version, size, and error without logging sensitive content.

A missing required resource should be reported as a build or installation defect.

Integration with pkgutil

pkgutil.get_data() is an older byte-oriented resource API. importlib.resources adds modern navigation and path contexts.

See Python pkgutil.

Testing

Test directory packages, installed wheels, ZIP files, missing resources, Unicode text, binary data, subdirectories, as_file(), namespace packages, and frozen executables.

Do not rely only on tests run from the repository checkout.

Common mistakes

Common failures include using __file__, assuming Traversable is Path, keeping an as_file() path after the block, omitting assets from the wheel, writing to package resources, accepting arbitrary names, packaging secrets, and testing only editable installs.

Conclusion

importlib.resources accesses package assets without requiring a normal filesystem layout. Start with files(), navigate with Traversable, read text or bytes, and use as_file() only for APIs that require a real path.

Include resources in the build, treat them as read-only, and test wheels, ZIP files, and final executables. Consult the official importlib.resources documentation and Python pkgutil.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Close-up of a USB pen drive being inserted into a laptop USB port on a white surface.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python importlib.metadata: Versions and Plugins

    Learn Python importlib.metadata to query versions, requirements, files, distributions, entry points, and plugins without importing packages.

    Ler mais

    Tempo de leitura: 9 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python runpy: Execute Modules and Scripts

    Learn Python runpy to execute modules and scripts, control __main__, run_path, alter_sys, namespaces, testing, and process isolation.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pkgutil: Discover Packages

    Learn Python pkgutil to list modules, walk packages, discover plugins, inspect importers, and read package resources safely.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Colorful stacked shipping containers at Hamburg port, showcasing global trade and logistics.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python modulefinder: Discover Imports

    Learn Python modulefinder to discover imports, transitive dependencies, missing modules, paths, plugins, and limitations of static analysis.

    Ler mais

    Tempo de leitura: 8 minutos
    27/08/2026
    Detailed view of code and file structure in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python py_compile: Compile One File

    Learn Python py_compile to compile one file, control .pyc output, logical filenames, optimization, hash invalidation, and build errors.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python compileall: Generate .pyc Bytecode

    Learn Python compileall to generate .pyc files, validate syntax, compile in parallel, control optimization, paths, and reproducible builds.

    Ler mais

    Tempo de leitura: 7 minutos
    27/08/2026