Skip to content

Commit

Permalink
Use the ResourceReader API
Browse files Browse the repository at this point in the history
  • Loading branch information
brettcannon committed Oct 27, 2017
1 parent f77ef54 commit e3d9572
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 38 deletions.
83 changes: 45 additions & 38 deletions importlib_resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,31 @@ def open(package: Package, file_name: FileName) -> BinaryIO:
"""Return a file-like object opened for binary-reading of the resource."""
file_name = _normalize_path(file_name)
package = _get_package(package)
# Using pathlib doesn't work well here due to the lack of 'strict' argument
# for pathlib.Path.resolve() prior to Python 3.6.
absolute_package_path = os.path.abspath(package.__spec__.origin)
package_path = os.path.dirname(absolute_package_path)
full_path = os.path.join(package_path, file_name)
try:
return builtins.open(full_path, 'rb')
except IOError:
# Just assume the loader is a resource loader; all the relevant
# importlib.machinery loaders are and an AttributeError for get_data()
# will make it clear what is needed from the loader.
loader = typing.cast(importlib.abc.ResourceLoader,
package.__spec__.loader)
return package.__spec__.loader.open_resource(file_name)
except AttributeError:
# Using pathlib doesn't work well here due to the lack of 'strict'
# argument for pathlib.Path.resolve() prior to Python 3.6.
absolute_package_path = os.path.abspath(package.__spec__.origin)
package_path = os.path.dirname(absolute_package_path)
full_path = os.path.join(package_path, file_name)
try:
data = loader.get_data(full_path)
return builtins.open(full_path, 'rb')
except IOError:
package_name = package.__spec__.name
message = '{!r} resource not found in {!r}'.format(file_name, package_name)
raise FileNotFoundError(message)
else:
return io.BytesIO(data)
# Just assume the loader is a resource loader; all the relevant
# importlib.machinery loaders are and an AttributeError for
# get_data() will make it clear what is needed from the loader.
loader = typing.cast(importlib.abc.ResourceLoader,
package.__spec__.loader)
try:
data = loader.get_data(full_path)
except IOError:
package_name = package.__spec__.name
message = '{!r} resource not found in {!r}'.format(file_name,
package_name)
raise FileNotFoundError(message)
else:
return io.BytesIO(data)


def read(package: Package, file_name: FileName, encoding: str = 'utf-8',
Expand Down Expand Up @@ -98,25 +102,28 @@ def path(package: Package, file_name: FileName) -> Iterator[pathlib.Path]:
raised if the file was deleted prior to the context manager
exiting).
"""
normalized_path = _normalize_path(file_name)
file_name = _normalize_path(file_name)
package = _get_package(package)
package_directory = pathlib.Path(package.__spec__.origin).parent
file_path = package_directory / normalized_path
if file_path.exists():
yield file_path
else:
with open(package, normalized_path) as file:
data = file.read()
# Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
# blocks due to the need to close the temporary file to work on
# Windows properly.
fd, raw_path = tempfile.mkstemp()
try:
os.write(fd, data)
os.close(fd)
yield pathlib.Path(raw_path)
finally:
try:
yield pathlib.Path(package.__spec__.loader.resource_path(file_name))
except (AttributeError, FileNotFoundError):
package_directory = pathlib.Path(package.__spec__.origin).parent
file_path = package_directory / file_name
if file_path.exists():
yield file_path
else:
with open(package, file_name) as file:
data = file.read()
# Not using tempfile.NamedTemporaryFile as it leads to deeper 'try'
# blocks due to the need to close the temporary file to work on
# Windows properly.
fd, raw_path = tempfile.mkstemp()
try:
os.remove(raw_path)
except FileNotFoundError:
pass
os.write(fd, data)
os.close(fd)
yield pathlib.Path(raw_path)
finally:
try:
os.remove(raw_path)
except FileNotFoundError:
pass
45 changes: 45 additions & 0 deletions importlib_resources/tests/util.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import abc
import importlib
import importlib.machinery
import importlib.util
import io
import pathlib
import sys
import unittest

from .. import abc as resources_abc
from . import data


def create_package(*, file, path):
class Reader(resources_abc.ResourceReader):
def open_resource(self, path):
self._path = path
if isinstance(file, Exception):
raise file
else:
return file

def resource_path(self, path_):
self._path = path_
if isinstance(path, Exception):
raise path
else:
return path

spec = importlib.machinery.ModuleSpec('testingpackage', Reader(),
origin='does-not-exist',
is_package=True)
return importlib.util.module_from_spec(spec)


class CommonTests(abc.ABC):

@abc.abstractmethod
Expand Down Expand Up @@ -54,6 +80,25 @@ def test_non_package(self):
with self.assertRaises(TypeError):
self.execute(__spec__.name, 'utf-8.file')

def test_resource_opener(self):
data = io.BytesIO(b'Hello, world!')
package = create_package(file=data, path=FileNotFoundError())
self.execute(package, 'utf-8.file')
self.assertEqual(package.__spec__.loader._path, 'utf-8.file')

def test_resource_path(self):
data = io.BytesIO(b'Hello, world!')
path = __file__
package = create_package(file=data, path=path)
self.execute(package, 'utf-8.file')
self.assertEqual(package.__spec__.loader._path, 'utf-8.file')

def test_useless_loader(self):
package = create_package(file=FileNotFoundError(),
path=FileNotFoundError())
with self.assertRaises(FileNotFoundError):
self.execute(package, 'utf-8.file')


class ZipSetup:

Expand Down

0 comments on commit e3d9572

Please sign in to comment.