Skip to content

Commit

Permalink
use files() api in open_* and read_*
Browse files Browse the repository at this point in the history
Signed-off-by: Filipe Laíns <lains@riseup.net>
  • Loading branch information
FFY00 committed May 21, 2021
1 parent 7fbce58 commit 7d12fd6
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 77 deletions.
8 changes: 4 additions & 4 deletions importlib_resources/__init__.py
Expand Up @@ -4,17 +4,17 @@
as_file,
files,
contents,
open_binary,
read_binary,
open_text,
read_text,
)

from importlib_resources._py3 import (
Package,
Resource,
is_resource,
open_binary,
open_text,
path,
read_binary,
read_text,
)
from importlib_resources.abc import ResourceReader

Expand Down
41 changes: 41 additions & 0 deletions importlib_resources/_common.py
Expand Up @@ -5,13 +5,16 @@
import contextlib
import types
import importlib
import io

from typing import Union, Any, Optional, Iterable
from typing.io import BinaryIO, TextIO
from .abc import ResourceReader

from ._compat import wrap_spec

Package = Union[types.ModuleType, str]
Resource = Union[str, os.PathLike]


def files(package):
Expand Down Expand Up @@ -117,6 +120,44 @@ def _(path):
# legacy API


def open_binary(package: Package, resource: Resource) -> BinaryIO:
"""Return a file-like object opened for binary reading of the resource."""
return (files(package) / normalize_path(resource)).open('rb')


def read_binary(package: Package, resource: Resource) -> bytes:
"""Return the binary contents of the resource."""
with open_binary(package, resource) as fp:
return fp.read()


def open_text(
package: Package,
resource: Resource,
encoding: str = 'utf-8',
errors: str = 'strict',
) -> TextIO:
"""Return a file-like object opened for text reading of the resource."""
return io.TextIOWrapper(
open_binary(package, resource), encoding=encoding, errors=errors
)


def read_text(
package: Package,
resource: Resource,
encoding: str = 'utf-8',
errors: str = 'strict',
) -> str:
"""Return the decoded string of the resource.
The decoding-related arguments have the same semantics as those of
bytes.decode().
"""
with open_text(package, resource, encoding, errors) as fp:
return fp.read()


def contents(package: Package) -> Iterable[str]:
"""Return an iterable of entries in `package`.
Expand Down
71 changes: 0 additions & 71 deletions importlib_resources/_py3.py
Expand Up @@ -5,88 +5,17 @@
from contextlib import suppress
from importlib.abc import ResourceLoader
from importlib.machinery import ModuleSpec
from io import BytesIO, TextIOWrapper
from pathlib import Path
from types import ModuleType
from typing import ContextManager, Union
from typing import cast
from typing.io import BinaryIO, TextIO
from collections.abc import Sequence
from functools import singledispatch

Package = Union[str, ModuleType]
Resource = Union[str, os.PathLike]


def open_binary(package: Package, resource: Resource) -> BinaryIO:
"""Return a file-like object opened for binary reading of the resource."""
resource = _common.normalize_path(resource)
package = _common.get_package(package)
reader = _common.get_resource_reader(package)
if reader is not None:
return reader.open_resource(resource)
spec = cast(ModuleSpec, package.__spec__)
# Using pathlib doesn't work well here due to the lack of 'strict'
# argument for pathlib.Path.resolve() prior to Python 3.6.
if spec.submodule_search_locations is not None:
paths = spec.submodule_search_locations
elif spec.origin is not None:
paths = [os.path.dirname(os.path.abspath(spec.origin))]

for package_path in paths:
full_path = os.path.join(package_path, resource)
try:
return open(full_path, mode='rb')
except OSError:
# 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 = cast(ResourceLoader, spec.loader)
data = None
if hasattr(spec.loader, 'get_data'):
with suppress(OSError):
data = loader.get_data(full_path)
if data is not None:
return BytesIO(data)

raise FileNotFoundError(
'{!r} resource not found in {!r}'.format(resource, spec.name)
)


def open_text(
package: Package,
resource: Resource,
encoding: str = 'utf-8',
errors: str = 'strict',
) -> TextIO:
"""Return a file-like object opened for text reading of the resource."""
return TextIOWrapper(
open_binary(package, resource), encoding=encoding, errors=errors
)


def read_binary(package: Package, resource: Resource) -> bytes:
"""Return the binary contents of the resource."""
with open_binary(package, resource) as fp:
return fp.read()


def read_text(
package: Package,
resource: Resource,
encoding: str = 'utf-8',
errors: str = 'strict',
) -> str:
"""Return the decoded string of the resource.
The decoding-related arguments have the same semantics as those of
bytes.decode().
"""
with open_text(package, resource, encoding, errors) as fp:
return fp.read()


def path(
package: Package,
resource: Resource,
Expand Down
9 changes: 7 additions & 2 deletions importlib_resources/tests/util.py
Expand Up @@ -112,16 +112,21 @@ def test_non_package_by_package(self):
module = sys.modules['importlib_resources.tests.util']
self.execute(module, 'utf-8.file')

'''
# FIXME: should this be removed? https://github.com/python/importlib_resources/issues/226
def test_resource_opener(self):
bytes_data = io.BytesIO(b'Hello, world!')
package = create_package(file=bytes_data, path=FileNotFoundError())
package = create_package(
file=bytes_data, path=FileNotFoundError(), contents=('utf-8.file',)
)
self.execute(package, 'utf-8.file')
self.assertEqual(package.__loader__._path, 'utf-8.file')
'''

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

Expand Down

0 comments on commit 7d12fd6

Please sign in to comment.