Python’s importlib.resources module provides a standard API for reading data files bundled with Python packages — works whether the package is installed from a wheel, loaded from a zip archive, or running from source. import importlib.resources as ilr. Modern API (Python 3.9+): ref = ilr.files("mypackage") → Traversable; (ref / "data.json").read_text() or (ref / "data.json").read_bytes(). Open file: with ilr.as_file(ref / "model.pkl") as path: load(path) — materializes to disk if needed. Legacy API (3.7+): ilr.read_text("mypackage", "config.toml"), ilr.read_binary("mypackage", "icon.png"), ilr.open_text("mypackage", "template.html", encoding="utf-8"), ilr.contents("mypackage") → Iterator[str], ilr.is_resource("mypackage", "data.json") → bool. Subdirectories: ilr.files("mypackage.data") — use dotted package path for sub-packages. Backport: importlib_resources (PyPI) for Python < 3.9. Claude Code generates installed package file loaders, config readers, embedded asset accessors, and data bundle managers.
CLAUDE.md for importlib.resources
## importlib.resources Stack
- Stdlib: import importlib.resources as ilr
- Read text: (ilr.files("mypkg") / "data.json").read_text(encoding="utf-8")
- Read bytes: (ilr.files("mypkg") / "icon.png").read_bytes()
- As path: with ilr.as_file(ilr.files("mypkg") / "model.pkl") as p: ...
- List: [r.name for r in ilr.files("mypkg").iterdir()]
- Legacy: ilr.read_text("mypkg", "cfg.toml") # 3.7+
- Note: use ilr.files() (3.9+) for subdirectory traversal
importlib.resources Package Data Pipeline
# app/resourceutil.py — loader, reader, asset manager, config loader, bundler
from __future__ import annotations
import importlib.resources as ilr
import json
import os
import shutil
from contextlib import contextmanager
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Iterator
# ─────────────────────────────────────────────────────────────────────────────
# 1. Basic resource access helpers
# ─────────────────────────────────────────────────────────────────────────────
def resource_text(
package: str,
resource: str,
encoding: str = "utf-8",
) -> str:
"""
Read a text resource from a package and return its content as str.
Example:
text = resource_text("myapp", "templates/hello.html")
text = resource_text("myapp.data", "defaults.toml")
"""
return (ilr.files(package) / resource).read_text(encoding=encoding)
def resource_bytes(package: str, resource: str) -> bytes:
"""
Read a binary resource from a package and return its content as bytes.
Example:
data = resource_bytes("myapp", "assets/logo.png")
model = resource_bytes("myapp.models", "tfidf.pkl")
"""
return (ilr.files(package) / resource).read_bytes()
def resource_exists(package: str, resource: str) -> bool:
"""
Return True if the named resource exists in the package.
Example:
resource_exists("myapp", "config.json") # True or False
"""
ref = ilr.files(package) / resource
try:
return ref.is_file()
except (FileNotFoundError, TypeError):
return False
def list_resources(package: str) -> list[str]:
"""
Return a sorted list of resource names in the top level of a package.
Example:
names = list_resources("myapp.data")
print(names)
"""
return sorted(r.name for r in ilr.files(package).iterdir())
@contextmanager
def resource_path(package: str, resource: str) -> Iterator[Path]:
"""
Context manager that yields a real filesystem Path for the resource.
Materializes zip-embedded resources to a temp file if needed.
Example:
with resource_path("myapp", "lib/native.so") as p:
ctypes.cdll.LoadLibrary(str(p))
"""
ref = ilr.files(package) / resource
with ilr.as_file(ref) as path:
yield Path(path)
# ─────────────────────────────────────────────────────────────────────────────
# 2. Structured config / data loaders
# ─────────────────────────────────────────────────────────────────────────────
def load_json_resource(package: str, resource: str) -> Any:
"""
Load a JSON resource from a package and return the parsed object.
Example:
config = load_json_resource("myapp", "config/defaults.json")
schema = load_json_resource("myapp.schemas", "user.json")
"""
text = resource_text(package, resource)
return json.loads(text)
def load_lines_resource(
package: str,
resource: str,
strip_comments: bool = True,
encoding: str = "utf-8",
) -> list[str]:
"""
Load a text resource as a list of non-empty lines.
Strips lines starting with '#' when strip_comments=True.
Example:
words = load_lines_resource("myapp", "data/stopwords.txt")
patterns = load_lines_resource("myapp", "data/rules.txt", strip_comments=True)
"""
lines = resource_text(package, resource, encoding=encoding).splitlines()
result = []
for line in lines:
line = line.strip()
if not line:
continue
if strip_comments and line.startswith("#"):
continue
result.append(line)
return result
# ─────────────────────────────────────────────────────────────────────────────
# 3. Asset manager
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class PackageAssets:
"""
Typed accessor for a package's bundled assets directory.
Provides read, exists, list, and extract operations.
Example:
assets = PackageAssets("myapp", subdir="assets")
logo = assets.read_bytes("logo.png")
css = assets.read_text("style.css")
assets.extract_all("/tmp/static/")
"""
package: str
subdir: str = ""
encoding: str = "utf-8"
def _ref(self, name: str = "") -> Any:
base = ilr.files(self.package)
if self.subdir:
base = base / self.subdir
return base / name if name else base
def read_text(self, name: str) -> str:
return self._ref(name).read_text(encoding=self.encoding)
def read_bytes(self, name: str) -> bytes:
return self._ref(name).read_bytes()
def exists(self, name: str) -> bool:
ref = self._ref(name)
try:
return ref.is_file()
except (FileNotFoundError, TypeError):
return False
def list(self) -> list[str]:
return sorted(r.name for r in self._ref().iterdir())
@contextmanager
def as_path(self, name: str) -> Iterator[Path]:
with ilr.as_file(self._ref(name)) as path:
yield Path(path)
def extract_all(self, dest_dir: "str | Path") -> list[Path]:
"""
Copy all assets from this subdir to dest_dir on disk.
Returns list of written paths.
Example:
written = assets.extract_all("/var/www/static")
"""
dest = Path(dest_dir)
dest.mkdir(parents=True, exist_ok=True)
written: list[Path] = []
for name in self.list():
out = dest / name
out.write_bytes(self.read_bytes(name))
written.append(out)
return written
# ─────────────────────────────────────────────────────────────────────────────
# 4. Resource discovery and reporting
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class ResourceInfo:
"""Describes one resource in a package."""
package: str
name: str
is_file: bool
size: "int | None" # None for directories or zip-embedded resources
def discover_resources(
package: str,
recursive: bool = True,
prefix: str = "",
) -> list[ResourceInfo]:
"""
Walk a package's resource tree and return ResourceInfo for each entry.
Example:
for ri in discover_resources("myapp"):
print(ri.package, ri.name, ri.size)
"""
results: list[ResourceInfo] = []
base = ilr.files(package)
def _walk(traversable: Any, current_prefix: str) -> None:
try:
children = list(traversable.iterdir())
except (NotADirectoryError, TypeError):
return
for child in children:
name = current_prefix + child.name
is_file = child.is_file()
size: "int | None" = None
if is_file:
try:
with ilr.as_file(child) as p:
size = Path(p).stat().st_size
except Exception:
pass
results.append(ResourceInfo(package=package, name=name, is_file=is_file, size=size))
if recursive and not is_file:
_walk(child, name + "/")
_walk(base, prefix)
return sorted(results, key=lambda r: r.name)
# ─────────────────────────────────────────────────────────────────────────────
# Demo (uses __main__ as the "package" via __spec__)
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
import importlib.util
import sys
import tempfile
print("=== importlib.resources demo ===")
# Build a temporary package with bundled data files
with tempfile.TemporaryDirectory() as tmpdir:
pkg_dir = Path(tmpdir) / "demopkg"
pkg_dir.mkdir()
data_dir = pkg_dir / "data"
data_dir.mkdir()
# Write package files
(pkg_dir / "__init__.py").write_text("")
(pkg_dir / "config.json").write_text('{"debug": false, "version": "1.0"}')
(pkg_dir / "data" / "__init__.py").write_text("")
(pkg_dir / "data" / "words.txt").write_text("hello\n# comment\nworld\nfoo")
(pkg_dir / "data" / "icon.png").write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 20)
# Add tmpdir to sys.path so we can import demopkg
sys.path.insert(0, tmpdir)
# ── resource_text / resource_bytes ─────────────────────────────────────
print("\n--- resource_text ---")
text = resource_text("demopkg", "config.json")
print(f" config.json: {text[:60]!r}")
print("\n--- load_json_resource ---")
cfg = load_json_resource("demopkg", "config.json")
print(f" parsed: {cfg}")
print("\n--- load_lines_resource ---")
words = load_lines_resource("demopkg.data", "words.txt", strip_comments=True)
print(f" words: {words}")
# ── list_resources ────────────────────────────────────────────────────
print("\n--- list_resources ---")
print(f" demopkg: {list_resources('demopkg')}")
print(f" demopkg.data: {list_resources('demopkg.data')}")
# ── resource_exists ───────────────────────────────────────────────────
print("\n--- resource_exists ---")
print(f" config.json exists: {resource_exists('demopkg', 'config.json')}")
print(f" missing.txt exists: {resource_exists('demopkg', 'missing.txt')}")
# ── PackageAssets ─────────────────────────────────────────────────────
print("\n--- PackageAssets ---")
assets = PackageAssets("demopkg", subdir="data")
print(f" list: {assets.list()}")
print(f" icon.png size: {len(assets.read_bytes('icon.png'))} bytes")
# ── discover_resources ─────────────────────────────────────────────────
print("\n--- discover_resources ---")
for ri in discover_resources("demopkg", recursive=True):
size_str = f"{ri.size}B" if ri.size is not None else "dir"
print(f" {'FILE' if ri.is_file else 'DIR ':4s} {ri.name:30s} {size_str}")
# ── extract_all ────────────────────────────────────────────────────────
print("\n--- PackageAssets.extract_all ---")
out_dir = Path(tmpdir) / "extracted"
written = assets.extract_all(out_dir)
print(f" extracted {len(written)} files to {out_dir.name}/")
sys.path.pop(0)
print("\n=== done ===")
For the pkg_resources (setuptools) alternative — pkg_resources.resource_string("mypackage", "data.json") and pkg_resources.resource_filename() were the standard approach before Python 3.7 and are still found in many projects — always prefer importlib.resources for new code; pkg_resources imports the entire setuptools machinery (slow), while importlib.resources is a lightweight stdlib module. For the __file__ / Path(__file__).parent / "data" direct path alternative — constructing paths relative to __file__ works when running from source on a filesystem, but fails when the package is loaded from a zip or wheel that hasn’t been extracted — use importlib.resources.files() instead; it handles all installation modes correctly including zip imports and namespace packages. The Claude Skills 360 bundle includes importlib.resources skill sets covering resource_text()/resource_bytes()/resource_exists()/list_resources()/resource_path() access helpers, load_json_resource()/load_lines_resource() structured loaders, PackageAssets with read_text()/read_bytes()/exists()/list()/as_path()/extract_all(), and ResourceInfo/discover_resources() tree walker. Start with the free tier to try package resource patterns and importlib.resources pipeline code generation.