Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a low-level interface for loaders to implement instead of TraversableResources #183

Merged
merged 16 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
v5.1.0
======

* Added ``simple`` module implementing adapters from
a low-level resource reader interface to a
``TraversableResources`` interface. Closes #90.

v5.0.1
======

Expand Down
116 changes: 116 additions & 0 deletions importlib_resources/simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
Interface adapters for low-level readers.
"""

import abc
import io
import itertools
from typing import BinaryIO, List

from .abc import Traversable, TraversableResources


class SimpleReader(abc.ABC):
"""
The minimum, low-level interface required from a resource
provider.
"""

@abc.abstractproperty
def package(self):
# type: () -> str
"""
The name of the package for which this reader loads resources.
"""

@abc.abstractmethod
def children(self):
# type: () -> List['SimpleReader']
"""
Obtain an iterable of SimpleReader for available
child containers (e.g. directories).
"""

@abc.abstractmethod
def resources(self):
# type: () -> List[str]
"""
Obtain available named resources for this virtual package.
"""

@abc.abstractmethod
def open_binary(self, resource):
# type: (str) -> BinaryIO
"""
Obtain a File-like for a named resource.
"""

@property
def name(self):
return self.package.split('.')[-1]


class ResourceHandle(Traversable):
"""
Handle to a named resource in a ResourceReader.
"""

def __init__(self, parent, name):
# type: (ResourceContainer, str) -> None
self.parent = parent
self.name = name # type: ignore

def is_file(self):
return True

def is_dir(self):
return False

def open(self, mode='r', *args, **kwargs):
stream = self.parent.reader.open_binary(self.name)
if 'b' not in mode:
stream = io.TextIOWrapper(*args, **kwargs)
return stream

def joinpath(self, name):
raise RuntimeError("Cannot traverse into a resource")


class ResourceContainer(Traversable):
"""
Traversable container for a package's resources via its reader.
"""

def __init__(self, reader):
# type: (SimpleReader) -> None
self.reader = reader

def is_dir(self):
return True

def is_file(self):
return False

def iterdir(self):
files = (ResourceHandle(self, name) for name in self.reader.resources)
dirs = map(ResourceContainer, self.reader.children())
return itertools.chain(files, dirs)

def open(self, *args, **kwargs):
raise IsADirectoryError()

def joinpath(self, name):
return next(
traversable for traversable in self.iterdir() if traversable.name == name
)


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):
return ResourceContainer(self)