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

Deprecate legacy functions #236

Merged
merged 6 commits into from Oct 18, 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
9 changes: 9 additions & 0 deletions CHANGES.rst
@@ -1,3 +1,12 @@
v5.3.0
======

* #80: Now raise a ``DeprecationWarning`` for all legacy
functions. Instead, users should rely on the ``files()``
API introduced in importlib_resources 1.3. See
`Migrating from Legacy <https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy>`_
for guidance on avoiding the deprecated functions.

v5.2.3
======

Expand Down
17 changes: 17 additions & 0 deletions docs/using.rst
Expand Up @@ -163,6 +163,23 @@ manager.
Both relative and absolute paths work for Python 3.7 and newer.


Migrating from Legacy
=====================

Starting with Python 3.9 and ``importlib_resources`` 1.4, this package
introduced the ``files()`` API, to be preferred over the legacy API,
i.e. the functions ``open_binary``, ``open_text``, ``path``,
``contents``, ``read_text``, ``read_binary``, and ``is_resource``.

To port to the ``files()`` API, refer to the
`_legacy module <https://github.com/python/importlib_resources/blob/66ea2dc7eb12b1be2322b7ad002cefb12d364dff/importlib_resources/_legacy.py>`_
to see simple wrappers that enable drop-in replacement based on the
preferred API, and either copy those or adapt the usage to utilize the
``files`` and
`Traversable <https://github.com/python/importlib_resources/blob/b665a3ea907d93b1b6457217f34e1bfc06f51fe6/importlib_resources/abc.py#L49-L114>`_
interfaces directly.


Extending
=========

Expand Down
24 changes: 24 additions & 0 deletions importlib_resources/_legacy.py
@@ -1,6 +1,8 @@
import functools
import os
import pathlib
import types
import warnings

from typing import Union, Iterable, ContextManager, BinaryIO, TextIO

Expand All @@ -10,16 +12,34 @@
Resource = Union[str, os.PathLike]


def deprecated(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
warnings.warn(
f"{func.__name__} is deprecated. Use files() instead. "
"Refer to https://importlib-resources.readthedocs.io"
"/en/latest/using.html#migrating-from-legacy for migration advice.",
DeprecationWarning,
stacklevel=2,
)
return func(*args, **kwargs)

return wrapper


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


@deprecated
def read_binary(package: Package, resource: Resource) -> bytes:
"""Return the binary contents of the resource."""
return (_common.files(package) / _common.normalize_path(resource)).read_bytes()


@deprecated
def open_text(
package: Package,
resource: Resource,
Expand All @@ -32,6 +52,7 @@ def open_text(
)


@deprecated
def read_text(
package: Package,
resource: Resource,
Expand All @@ -47,6 +68,7 @@ def read_text(
return fp.read()


@deprecated
def contents(package: Package) -> Iterable[str]:
"""Return an iterable of entries in `package`.

Expand All @@ -57,6 +79,7 @@ def contents(package: Package) -> Iterable[str]:
return [path.name for path in _common.files(package).iterdir()]


@deprecated
def is_resource(package: Package, name: str) -> bool:
"""True if `name` is a resource inside `package`.

Expand All @@ -69,6 +92,7 @@ def is_resource(package: Package, name: str) -> bool:
)


@deprecated
def path(
package: Package,
resource: Resource,
Expand Down
3 changes: 2 additions & 1 deletion importlib_resources/tests/test_contents.py
Expand Up @@ -15,7 +15,8 @@ class ContentsTests:
}

def test_contents(self):
assert self.expected <= set(resources.contents(self.data))
with util.suppress_known_deprecation():
assert self.expected <= set(resources.contents(self.data))


class ContentsDiskTests(ContentsTests, unittest.TestCase):
Expand Down
51 changes: 31 additions & 20 deletions importlib_resources/tests/test_open.py
Expand Up @@ -7,38 +7,47 @@

class CommonBinaryTests(util.CommonTests, unittest.TestCase):
def execute(self, package, path):
with resources.open_binary(package, path):
pass
with util.suppress_known_deprecation():
with resources.open_binary(package, path):
pass


class CommonTextTests(util.CommonTests, unittest.TestCase):
def execute(self, package, path):
with resources.open_text(package, path):
pass
with util.suppress_known_deprecation():
with resources.open_text(package, path):
pass


class OpenTests:
def test_open_binary(self):
with resources.open_binary(self.data, 'utf-8.file') as fp:
result = fp.read()
with util.suppress_known_deprecation():
with resources.open_binary(self.data, 'utf-8.file') as fp:
result = fp.read()
self.assertEqual(result, b'Hello, UTF-8 world!\n')

def test_open_text_default_encoding(self):
with resources.open_text(self.data, 'utf-8.file') as fp:
result = fp.read()
with util.suppress_known_deprecation():
with resources.open_text(self.data, 'utf-8.file') as fp:
result = fp.read()
self.assertEqual(result, 'Hello, UTF-8 world!\n')

def test_open_text_given_encoding(self):
with resources.open_text(self.data, 'utf-16.file', 'utf-16', 'strict') as fp:
result = fp.read()
with util.suppress_known_deprecation():
with resources.open_text(
self.data, 'utf-16.file', 'utf-16', 'strict'
) as fp:
result = fp.read()
self.assertEqual(result, 'Hello, UTF-16 world!\n')

def test_open_text_with_errors(self):
# Raises UnicodeError without the 'errors' argument.
with resources.open_text(self.data, 'utf-16.file', 'utf-8', 'strict') as fp:
self.assertRaises(UnicodeError, fp.read)
with resources.open_text(self.data, 'utf-16.file', 'utf-8', 'ignore') as fp:
result = fp.read()
with util.suppress_known_deprecation():
with resources.open_text(self.data, 'utf-16.file', 'utf-8', 'strict') as fp:
self.assertRaises(UnicodeError, fp.read)
with util.suppress_known_deprecation():
with resources.open_text(self.data, 'utf-16.file', 'utf-8', 'ignore') as fp:
result = fp.read()
self.assertEqual(
result,
'H\x00e\x00l\x00l\x00o\x00,\x00 '
Expand All @@ -47,14 +56,16 @@ def test_open_text_with_errors(self):
)

def test_open_binary_FileNotFoundError(self):
self.assertRaises(
FileNotFoundError, resources.open_binary, self.data, 'does-not-exist'
)
with util.suppress_known_deprecation():
self.assertRaises(
FileNotFoundError, resources.open_binary, self.data, 'does-not-exist'
)

def test_open_text_FileNotFoundError(self):
self.assertRaises(
FileNotFoundError, resources.open_text, self.data, 'does-not-exist'
)
with util.suppress_known_deprecation():
self.assertRaises(
FileNotFoundError, resources.open_text, self.data, 'does-not-exist'
)


class OpenDiskTests(OpenTests, unittest.TestCase):
Expand Down
28 changes: 16 additions & 12 deletions importlib_resources/tests/test_path.py
Expand Up @@ -8,21 +8,23 @@

class CommonTests(util.CommonTests, unittest.TestCase):
def execute(self, package, path):
with resources.path(package, path):
pass
with util.suppress_known_deprecation():
with resources.path(package, path):
pass


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(self.data, 'utf-8.file') as path:
self.assertTrue(path.name.endswith("utf-8.file"), repr(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)
with util.suppress_known_deprecation():
with resources.path(self.data, 'utf-8.file') as path:
self.assertTrue(path.name.endswith("utf-8.file"), repr(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):
Expand All @@ -34,8 +36,9 @@ def test_natural_path(self):
file-system-backed resources do not get the tempdir
treatment.
"""
with resources.path(self.data, 'utf-8.file') as path:
assert 'data' in str(path)
with util.suppress_known_deprecation():
with resources.path(self.data, 'utf-8.file') as path:
assert 'data' in str(path)


class PathMemoryTests(PathTests, unittest.TestCase):
Expand All @@ -53,8 +56,9 @@ class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase):
def test_remove_in_context_manager(self):
# It is not an error if the file that was temporarily stashed on the
# file system is removed inside the `with` stanza.
with resources.path(self.data, 'utf-8.file') as path:
path.unlink()
with util.suppress_known_deprecation():
with resources.path(self.data, 'utf-8.file') as path:
path.unlink()


if __name__ == '__main__':
Expand Down
29 changes: 20 additions & 9 deletions importlib_resources/tests/test_read.py
Expand Up @@ -8,31 +8,40 @@

class CommonBinaryTests(util.CommonTests, unittest.TestCase):
def execute(self, package, path):
resources.read_binary(package, path)
with util.suppress_known_deprecation():
resources.read_binary(package, path)


class CommonTextTests(util.CommonTests, unittest.TestCase):
def execute(self, package, path):
resources.read_text(package, path)
with util.suppress_known_deprecation():
resources.read_text(package, path)


class ReadTests:
def test_read_binary(self):
result = resources.read_binary(self.data, 'binary.file')
with util.suppress_known_deprecation():
result = resources.read_binary(self.data, 'binary.file')
self.assertEqual(result, b'\0\1\2\3')

def test_read_text_default_encoding(self):
result = resources.read_text(self.data, 'utf-8.file')
with util.suppress_known_deprecation():
result = resources.read_text(self.data, 'utf-8.file')
self.assertEqual(result, 'Hello, UTF-8 world!\n')

def test_read_text_given_encoding(self):
result = resources.read_text(self.data, 'utf-16.file', encoding='utf-16')
with util.suppress_known_deprecation():
result = resources.read_text(self.data, 'utf-16.file', encoding='utf-16')
self.assertEqual(result, 'Hello, UTF-16 world!\n')

def test_read_text_with_errors(self):
# Raises UnicodeError without the 'errors' argument.
self.assertRaises(UnicodeError, resources.read_text, self.data, 'utf-16.file')
result = resources.read_text(self.data, 'utf-16.file', errors='ignore')
with util.suppress_known_deprecation():
self.assertRaises(
UnicodeError, resources.read_text, self.data, 'utf-16.file'
)
with util.suppress_known_deprecation():
result = resources.read_text(self.data, 'utf-16.file', errors='ignore')
self.assertEqual(
result,
'H\x00e\x00l\x00l\x00o\x00,\x00 '
Expand All @@ -48,11 +57,13 @@ class ReadDiskTests(ReadTests, unittest.TestCase):
class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
def test_read_submodule_resource(self):
submodule = import_module('ziptestdata.subdirectory')
result = resources.read_binary(submodule, 'binary.file')
with util.suppress_known_deprecation():
result = resources.read_binary(submodule, 'binary.file')
self.assertEqual(result, b'\0\1\2\3')

def test_read_submodule_resource_by_name(self):
result = resources.read_binary('ziptestdata.subdirectory', 'binary.file')
with util.suppress_known_deprecation():
result = resources.read_binary('ziptestdata.subdirectory', 'binary.file')
self.assertEqual(result, b'\0\1\2\3')


Expand Down