Skip to content

Commit

Permalink
Add zip support
Browse files Browse the repository at this point in the history
  • Loading branch information
brettcannon authored Oct 27, 2017
2 parents d99bcd0 + 981b362 commit c26840e
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 165 deletions.
36 changes: 23 additions & 13 deletions importlib_resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ def _get_package(package) -> types.ModuleType:


def _normalize_path(path) -> str:
parent, file_name = os.path.split(path)
str_path = str(path)
parent, file_name = os.path.split(str_path)
if parent:
raise ValueError("{!r} is not only a file name".format(path))
raise ValueError("{!r} must be only a file name".format(path))
else:
return file_name

Expand All @@ -46,18 +47,27 @@ 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)
package_path = os.path.dirname(os.path.abspath(package.__spec__.origin))
# 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)
if not os.path.exists(full_path):
package_name = package.__spec__.name
message = "{!r} does not exist"
raise FileNotFoundError(message.format(full_path))
# 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)
data = loader.get_data(full_path)
return io.BytesIO(data)
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)
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
Binary file added importlib_resources/tests/data/ziptestdata.zip
Binary file not shown.
79 changes: 26 additions & 53 deletions importlib_resources/tests/test_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,47 @@
import unittest

import importlib_resources as resources
from importlib_resources.tests import data


class CommonTests(unittest.TestCase):

def test_package_name(self):
# Passing in the package name should succeed.
with resources.open(data.__name__, 'utf-8.file') as file:
pass # No error.

def test_package_object(self):
# Passing in the package itself should succeed.
with resources.open(data, 'utf-8.file') as file:
pass # No error.

def test_string_path(self):
path = 'utf-8.file'
# Passing in a string for the path should succeed.
with resources.open(data, path) as file:
pass # No error.

@unittest.skipIf(sys.version_info < (3, 6), 'requires os.PathLike support')
def test_pathlib_path(self):
# Passing in a pathlib.PurePath object for the path should succeed.
path = pathlib.PurePath('utf-8.file')
with resources.open(data, path) as file:
pass # No error.

def test_absolute_path(self):
# An absolute path is a ValueError.
path = pathlib.Path(__file__)
full_path = path.parent/'utf-8.file'
with self.assertRaises(ValueError):
with resources.open(data, str(full_path)) as file:
pass
from . import data
from . import util

def test_relative_path(self):
# A reative path is a ValueError.
with self.assertRaises(ValueError):
with resources.open(data, '../data/utf-8.file') as file:
pass

def test_importing_module_as_side_effect(self):
# The anchor package can already be imported.
del sys.modules[data.__name__]
with resources.open(data.__name__, 'utf-8.file') as file:
pass # No Errors.
class CommonTests(util.CommonTests, unittest.TestCase):

def test_non_package(self):
# The anchor package cannot be a module.
with self.assertRaises(TypeError):
with resources.open(__spec__.name, 'utf-8.file') as file:
pass
def execute(self, package, path):
with resources.open(package, path):
pass


class OpenTests(unittest.TestCase):
class OpenTests:

# Subclasses are expected to set the 'data' attribute.

def test_opened_for_reading(self):
# The file-like object is ready for reading.
with resources.open(data, 'utf-8.file') as file:
with resources.open(self.data, 'utf-8.file') as file:
self.assertEqual(b"Hello, UTF-8 world!\n", file.read())

def test_wrap_for_text(self):
# The file-like object can be wrapped for text reading.
with resources.open(data, 'utf-8.file') as file:
with resources.open(self.data, 'utf-8.file') as file:
text_file = io.TextIOWrapper(file, encoding='utf-8')
self.assertEqual('Hello, UTF-8 world!\n', text_file.read())

def test_FileNotFoundError(self):
with self.assertRaises(FileNotFoundError):
with resources.open(self.data, 'does-not-exist') as file:
pass


class OpenDiskTests(OpenTests, unittest.TestCase):

def setUp(self):
self.data = data


class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
pass


if __name__ == '__main__':
unittest.main()
74 changes: 21 additions & 53 deletions importlib_resources/tests/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,38 @@
import unittest

import importlib_resources as resources
from importlib_resources.tests import data
from . import data
from . import util


class CommonTests(unittest.TestCase):
class CommonTests(util.CommonTests, unittest.TestCase):

def test_package_name(self):
# Passing in the package name should succeed.
with resources.path(data.__name__, 'utf-8.file') as path:
pass # No error.
def execute(self, package, path):
with resources.path(package, path):
pass

def test_package_object(self):
# Passing in the package itself should succeed.
with resources.path(data, 'utf-8.file') as path:
pass # No error.

def test_string_path(self):
path = 'utf-8.file'
# Passing in a string for the path should succeed.
with resources.path(data, path) as path:
pass # No error.

@unittest.skipIf(sys.version_info < (3, 6), 'requires os.PathLike support')
def test_pathlib_path(self):
# Passing in a pathlib.PurePath object for the path should succeed.
path = pathlib.PurePath('utf-8.file')
with resources.path(data, path) as path:
pass # No error.

# Don't fail if run under e.g. pytest.
def test_absolute_path(self):
# An absolute path is a ValueError.
path = pathlib.Path(__file__)
full_path = path.parent/'utf-8.file'
with self.assertRaises(ValueError):
with resources.path(data, str(full_path)) as path:
pass

def test_relative_path(self):
# A reative path is a ValueError.
with self.assertRaises(ValueError):
with resources.path(data, '../data/utf-8.file') as path:
pass

def test_importing_module_as_side_effect(self):
# The anchor package can already be imported.
del sys.modules[data.__name__]
with resources.path(data.__name__, 'utf-8.file') as path:
pass # No Errors.

def test_non_package(self):
# The anchor package cannot be a module.
with self.assertRaises(TypeError):
with resources.path(__spec__.name, 'utf-8.file') as path:
pass


class PathTests(unittest.TestCase):
class PathTests:

def test_reading(self):
# Path should be readable.
# Test also implicitly verifies the returned object is a pathlib.Path
# instance.
with resources.path(data, 'utf-8.file') as path:
with resources.path(self.data, 'utf-8.file') as path:
# pathlib.Path.read_text() was introduced in Python 3.5.
with path.open('r', encoding='utf-8') as file:
text = file.read()
self.assertEqual('Hello, UTF-8 world!\n', text)


class PathDiskTests(PathTests, unittest.TestCase):

data = data


class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase):
pass


if __name__ == '__main__':
unittest.main()
68 changes: 22 additions & 46 deletions importlib_resources/tests/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,40 @@
import unittest

import importlib_resources as resources
from importlib_resources.tests import data
from . import data
from . import util


class CommonTests(unittest.TestCase):
class CommonTests(util.CommonTests, unittest.TestCase):

def test_package_name(self):
# Passing in the package name should succeed.
resources.read(data.__name__, 'utf-8.file')
def execute(self, package, path):
resources.read(package, path)

def test_package_object(self):
# Passing in the package itself should succeed.
resources.read(data, 'utf-8.file')

def test_string_path(self):
path = 'utf-8.file'
# Passing in a string for the path should succeed.
resources.read(data, path)

@unittest.skipIf(sys.version_info < (3, 6), 'requires os.PathLike support')
def test_pathlib_path(self):
# Passing in a pathlib.PurePath object for the path should succeed.
path = pathlib.PurePath('utf-8.file')
resources.read(data, path)

def test_absolute_path(self):
# An absolute path is a ValueError.
path = pathlib.Path(__file__)
full_path = path.parent/'utf-8.file'
with self.assertRaises(ValueError):
resources.read(data, str(full_path))

def test_relative_path(self):
# A reative path is a ValueError.
with self.assertRaises(ValueError):
resources.read(data, '../data/utf-8.file')

def test_importing_module_as_side_effect(self):
# The anchor package can already be imported.
del sys.modules[data.__name__]
resources.read(data.__name__, 'utf-8.file')

def test_non_package(self):
# The anchor package cannot be a module.
with self.assertRaises(TypeError):
resources.read(__spec__.name, 'utf-8.file')


class ReadTests(unittest.TestCase):
class ReadTests:

def test_default_encoding(self):
result = resources.read(data, 'utf-8.file')
result = resources.read(self.data, 'utf-8.file')
self.assertEqual("Hello, UTF-8 world!\n", result)

def test_encoding(self):
result = resources.read(data, 'utf-16.file', encoding='utf-16')
result = resources.read(self.data, 'utf-16.file', encoding='utf-16')
self.assertEqual("Hello, UTF-16 world!\n", result)

def test_errors(self):
# Raises UnicodeError without the 'errors' argument.
result = resources.read(data, 'utf-16.file', encoding='utf-8',
result = resources.read(self.data, 'utf-16.file', encoding='utf-8',
errors='ignore')


class ReadDiskTests(ReadTests, unittest.TestCase):

data = data


class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
pass


if __name__ == '__main__':
unittest.main()
Loading

0 comments on commit c26840e

Please sign in to comment.