diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2020-10-25 18:21:46 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-25 18:21:46 (GMT) |
commit | df8d4c83a6e1727e766191896aeabde886979587 (patch) | |
tree | a286f28927d1cd736787880784d2c1af908b45b8 /Lib/importlib/resources.py | |
parent | c32f2976b8f4034724c3270397aa16f38daf470f (diff) | |
download | cpython-df8d4c83a6e1727e766191896aeabde886979587.zip cpython-df8d4c83a6e1727e766191896aeabde886979587.tar.gz cpython-df8d4c83a6e1727e766191896aeabde886979587.tar.bz2 |
bpo-41490: ``path`` and ``contents`` to aggressively close handles (#22915)
* bpo-41490: ``path`` method to aggressively close handles
* Add blurb
* In ZipReader.contents, eagerly evaluate the contents to release references to the zipfile.
* Instead use _ensure_sequence to ensure any iterable from a reader is eagerly converted to a list if it's not already a sequence.
Diffstat (limited to 'Lib/importlib/resources.py')
-rw-r--r-- | Lib/importlib/resources.py | 37 |
1 files changed, 27 insertions, 10 deletions
diff --git a/Lib/importlib/resources.py b/Lib/importlib/resources.py index 4535619..4169171 100644 --- a/Lib/importlib/resources.py +++ b/Lib/importlib/resources.py @@ -1,8 +1,9 @@ import os +import io from . import _common from ._common import as_file, files -from contextlib import contextmanager, suppress +from contextlib import suppress from importlib.abc import ResourceLoader from io import BytesIO, TextIOWrapper from pathlib import Path @@ -10,6 +11,8 @@ from types import ModuleType from typing import ContextManager, Iterable, Union from typing import cast from typing.io import BinaryIO, TextIO +from collections.abc import Sequence +from functools import singledispatch __all__ = [ @@ -102,22 +105,26 @@ def path( """ reader = _common.get_resource_reader(_common.get_package(package)) return ( - _path_from_reader(reader, resource) + _path_from_reader(reader, _common.normalize_path(resource)) if reader else _common.as_file( _common.files(package).joinpath(_common.normalize_path(resource))) ) -@contextmanager def _path_from_reader(reader, resource): - norm_resource = _common.normalize_path(resource) + return _path_from_resource_path(reader, resource) or \ + _path_from_open_resource(reader, resource) + + +def _path_from_resource_path(reader, resource): with suppress(FileNotFoundError): - yield Path(reader.resource_path(norm_resource)) - return - opener_reader = reader.open_resource(norm_resource) - with _common._tempfile(opener_reader.read, suffix=norm_resource) as res: - yield res + return Path(reader.resource_path(resource)) + + +def _path_from_open_resource(reader, resource): + saved = io.BytesIO(reader.open_resource(resource).read()) + return _common._tempfile(saved.read, suffix=resource) def is_resource(package: Package, name: str) -> bool: @@ -146,7 +153,7 @@ def contents(package: Package) -> Iterable[str]: package = _common.get_package(package) reader = _common.get_resource_reader(package) if reader is not None: - return reader.contents() + return _ensure_sequence(reader.contents()) # Is the package a namespace package? By definition, namespace packages # cannot have resources. namespace = ( @@ -156,3 +163,13 @@ def contents(package: Package) -> Iterable[str]: if namespace or not package.__spec__.has_location: return () return list(item.name for item in _common.from_package(package).iterdir()) + + +@singledispatch +def _ensure_sequence(iterable): + return list(iterable) + + +@_ensure_sequence.register(Sequence) +def _(iterable): + return iterable |