Skip to content

Commit

Permalink
wip: make files() be enter-able
Browse files Browse the repository at this point in the history
This restores compatibility more or less with the old path() approach
that supports directly running `with x as f: do_stuff(f)`. There is no
more need to use as_file() by hand every time you need to have a file.

Namespace packages are TODO. But currently they are half-broken anyway,
in the sense that namespace packages do not support being inside zip
files unless you use pkg_resources style and importlib_resources does
not support this anyway.

We specifically go to efforts to ensure that the return value of files()
is still usable as-is, representing an isinstance()-compatible
pathlib.Path or zipfile.Path. We do this by replacing it at the last
second with a subclass that adds the context manager magic.
  • Loading branch information
eli-schwartz committed Nov 23, 2023
1 parent f625a8a commit e0e2fad
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 2 deletions.
12 changes: 10 additions & 2 deletions importlib_resources/_common.py
Expand Up @@ -12,7 +12,8 @@
from typing import Union, Optional, cast
from .abc import ResourceReader, Traversable

from ._compat import wrap_spec
from ._compat import ZipPath, wrap_spec
from .readers import EnterablePath, EnterableZip

Package = Union[types.ModuleType, str]
Anchor = Package
Expand Down Expand Up @@ -53,7 +54,14 @@ def files(anchor: Optional[Anchor] = None) -> Traversable:
"""
Get a Traversable resource for an anchor.
"""
return from_package(resolve(anchor))
f = from_package(resolve(anchor))
if isinstance(f, ZipPath):
return EnterableZip(f.root, f.at)
elif isinstance(f, pathlib.Path):
return EnterablePath(f)
else:
# `with f as g:` will fail for MultiplexedPath
return f


def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]:
Expand Down
17 changes: 17 additions & 0 deletions importlib_resources/readers.py
Expand Up @@ -170,3 +170,20 @@ def resource_path(self, resource):

def files(self):
return self.path


class EnterablePath(pathlib.Path):
def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, traceback):
pass

class EnterableZip(ZipPath):
def __enter__(self):
from ._common import as_file
self._as_file = as_file(self)
return self._as_file.__enter__()

def __exit__(self, exc_type, exc_value, traceback):
self._as_file.__exit__(exc_type, exc_value, traceback)

0 comments on commit e0e2fad

Please sign in to comment.