Pycon 2018 Cleveland, Ohio
May 2018
Barry Warsaw
Python Foundation @ LinkedIn
My code needs some static files. How hard can it be to read them at run time?
thepkg/
__init__.py
a.py
b.py
data/
sample.dat
import thepkg
from pathlib import Path
pkg = Path(thepkg.__file__).parent
path = pkg / 'data' / 'sample.dat'
with open(path, 'rb') as fp:
contents = fp.read()
What's the problem?
thepkg/
__init__.py
a.py
b.py
data/
sample.dat
pkg = Path(thepkg.__file__).parent
path = pkg / 'data' / 'sample.dat'
with open(path, 'rb') as fp:
contents = fp.read()
Traceback (most recent call last):
File "run.py", line 7, in <module>
with open(path, 'rb') as fp:
NotADirectoryError: [Errno 20] Not a directory: '.../thepkg.zip/thepkg/data/sample.dat'
Basic Resource Access
from pkg_resources import \ resource_string as resource_bytes contents = resource_bytes( 'thepkg', 'data/sample.dat')
What's the problem?
Because we have Python's import machinery to help us
from importlib.resources import read_binary
contents = read_binary(
'thepkg.data', 'sample.dat')
import thepkg.data
contents = read_binary(
thepkg.data, 'sample.dat')
thepkg/
__init__.py
a.py
b.py
data/
sample.dat
thepkg/
__init__.py
a.py
b.py
data/
__init__.py
sample.dat
Access a "resource" in a "package"
Q: What's a "package"?
Q: What's a "resource"?
E.g. a directory containing an __init__.py
A: Any importable module with a __path__ attribute
A: Any readable object contained in a package
E.g. a file inside a package
thepkg/
__init__.py
a.py
b.py
data/
__init__.py
sample.dat
Package: thepkg
thepkg/
__init__.py
a.py
b.py
data/
__init__.py
sample.dat
Package: thepkg.data
Types
Package = Union[str, ModuleType] Resource = Union[str, os.PathLike]
Get the contents of a resource
read_binary( package: Package, resource: Resource) -> bytes
read_text( package: Package, resource: Resource, encoding: str = 'utf-8', errors: str = 'strict') -> str
Get a file-like object open for reading
open_text( package: Package, resource: Resource, encoding: str = 'utf-8', errors: str = 'strict') -> TextIO
open_binary( package: Package, resource: Resource) -> BinaryIO
Get a concrete file system path
with path( thepkg, 'foo.cpython-37m-darwin.so' ) as lib: import_shared_library(lib)
path( package: Package, resource: Resource) -> Iterator[Path]
List what's in a package *
contents( package: Package) -> Iterable[str]
* Items are not guaranteed to be resources!
>>> print(sorted(contents( 'thepkg.data'))) ['__init__.py', '__pycache__', 'sample.dat']
Is a thing a resource?
is_resource( package: Package name: str) -> bool
* Use this with contents() to iterate over resources in a package
Low level API for custom loaders
Built-in support for file system and zips
loader.get_resource_reader( str: package_name ) -> importlib.abc.ResourceReader
open_resource(str: resource) -> BytesIO
resource_path(str: resource) -> str
is_resource(str: name) -> bool
contents() -> Iterable[str]
Backport of resource reading for Python 2.7, 3.4-3.6 (works as a shim for 3.7)
importlib-resources.rtfd.org
First of hopefully many great collaborations between the LinkedIn and Microsoft Python teams
barry@python.org
bwarsaw@linkedin.com
@pumpichank
github.com/warsaw
gitlab.com/warsaw
importlib-resources.rtfd.org