Skip to content

Commit

Permalink
Merge pull request #236 from python/feature/deprecate-legacy
Browse files Browse the repository at this point in the history
Deprecate legacy functions
  • Loading branch information
jaraco authored Oct 18, 2021
2 parents b665a3e + 239ae6d commit 8ac7ab5
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 80 deletions.
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 8ac7ab5

Please sign in to comment.