Skip to content

Commit

Permalink
Merge branch 'issue20' into 'master'
Browse files Browse the repository at this point in the history
Properly prevent namespaces packages from containing resources

Closes python#20

See merge request python-devs/importlib_resources!50
  • Loading branch information
warsaw committed Dec 13, 2017
2 parents b33cb47 + a6ff818 commit b5f1b15
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 7 deletions.
23 changes: 16 additions & 7 deletions importlib_resources/_py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from . import abc as resources_abc
from builtins import open as builtins_open
from contextlib import contextmanager
from contextlib import contextmanager, suppress
from importlib import import_module
from importlib.abc import ResourceLoader
from io import BytesIO, TextIOWrapper
Expand Down Expand Up @@ -78,9 +78,11 @@ def open_binary(package: Package, resource: Resource) -> BinaryIO:
# importlib.machinery loaders are and an AttributeError for
# get_data() will make it clear what is needed from the loader.
loader = cast(ResourceLoader, package.__spec__.loader)
try:
data = loader.get_data(full_path)
except IOError:
data = None
if hasattr(package.__spec__.loader, 'get_data'):
with suppress(IOError):
data = loader.get_data(full_path)
if data is None:
package_name = package.__spec__.name
message = '{!r} resource not found in {!r}'.format(
resource, package_name)
Expand Down Expand Up @@ -112,9 +114,11 @@ def open_text(package: Package,
# importlib.machinery loaders are and an AttributeError for
# get_data() will make it clear what is needed from the loader.
loader = cast(ResourceLoader, package.__spec__.loader)
try:
data = loader.get_data(full_path)
except IOError:
data = None
if hasattr(package.__spec__.loader, 'get_data'):
with suppress(IOError):
data = loader.get_data(full_path)
if data is None:
package_name = package.__spec__.name
message = '{!r} resource not found in {!r}'.format(
resource, package_name)
Expand Down Expand Up @@ -255,6 +259,11 @@ def contents(package: Package) -> Iterator[str]:
if reader is not None:
yield from reader.contents()
return
# Is the package a namespace package? By definition, namespace packages
# cannot have resources.
if (package.__spec__.origin == 'namespace' and
not package.__spec__.has_location):
return []
package_directory = Path(package.__spec__.origin).parent
try:
yield from os.listdir(str(package_directory))
Expand Down
1 change: 1 addition & 0 deletions importlib_resources/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
``open_text()``, ``read_binary()``, and ``read_text()``. Closes #41
* Fix a bug where unrelated resources could be returned from ``contents()``.
Closes #44
* Correctly prevent namespace packages from containing resources. Closes #20


0.1 (2017-12-05)
Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
30 changes: 30 additions & 0 deletions importlib_resources/tests/test_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,35 @@ def test_unrelated_contents(self):
{'__init__.py', 'resource2.txt'})


@unittest.skipIf(sys.version_info < (3,), 'No namespace packages in Python 2')
class NamespaceTest(unittest.TestCase):
def test_namespaces_cant_have_resources(self):
contents = set(resources.contents(
'importlib_resources.tests.data03.namespace'))
self.assertEqual(len(contents), 0)
# Even though there is a file in the namespace directory, it is not
# considered a resource, since namespace packages can't have them.
self.assertFalse(resources.is_resource(
'importlib_resources.tests.data03.namespace',
'resource1.txt'))
# We should get an exception if we try to read it or open it.
self.assertRaises(
FileNotFoundError,
resources.open_text,
'importlib_resources.tests.data03.namespace', 'resource1.txt')
self.assertRaises(
FileNotFoundError,
resources.open_binary,
'importlib_resources.tests.data03.namespace', 'resource1.txt')
self.assertRaises(
FileNotFoundError,
resources.read_text,
'importlib_resources.tests.data03.namespace', 'resource1.txt')
self.assertRaises(
FileNotFoundError,
resources.read_binary,
'importlib_resources.tests.data03.namespace', 'resource1.txt')


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

0 comments on commit b5f1b15

Please sign in to comment.