Skip to content

Commit 295c699

Browse files
committed
Rely on trees for detecting resources on Python 2.
1 parent 1150cd6 commit 295c699

File tree

1 file changed

+3
-82
lines changed

1 file changed

+3
-82
lines changed

importlib_resources/_py2.py

+3-82
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import errno
33
import tempfile
44

5+
from . import trees
56
from ._compat import FileNotFoundError
67
from contextlib import contextmanager
78
from importlib import import_module
89
from io import BytesIO, TextIOWrapper, open as io_open
910
from pathlib2 import Path
10-
from zipfile import ZipFile
1111

1212

1313
def _resolve(name):
@@ -155,42 +155,7 @@ def is_resource(package, name):
155155
return False
156156
if name not in package_contents:
157157
return False
158-
# Just because the given file_name lives as an entry in the package's
159-
# contents doesn't necessarily mean it's a resource. Directories are not
160-
# resources, so let's try to find out if it's a directory or not.
161-
path = Path(package.__file__).parent / name
162-
if path.is_file():
163-
return True
164-
if path.is_dir():
165-
return False
166-
# If it's not a file and it's not a directory, what is it? Well, this
167-
# means the file doesn't exist on the file system, so it probably lives
168-
# inside a zip file. We have to crack open the zip, look at its table of
169-
# contents, and make sure that this entry doesn't have sub-entries.
170-
archive_path = package.__loader__.archive # type: ignore
171-
package_directory = Path(package.__file__).parent
172-
with ZipFile(archive_path) as zf:
173-
toc = zf.namelist()
174-
relpath = package_directory.relative_to(archive_path)
175-
candidate_path = relpath / name
176-
for entry in toc: # pragma: nobranch
177-
try:
178-
relative_to_candidate = Path(entry).relative_to(candidate_path)
179-
except ValueError:
180-
# The two paths aren't relative to each other so we can ignore it.
181-
continue
182-
# Since directories aren't explicitly listed in the zip file, we must
183-
# infer their 'directory-ness' by looking at the number of path
184-
# components in the path relative to the package resource we're
185-
# looking up. If there are zero additional parts, it's a file, i.e. a
186-
# resource. If there are more than zero it's a directory, i.e. not a
187-
# resource. It has to be one of these two cases.
188-
return len(relative_to_candidate.parts) == 0
189-
# I think it's impossible to get here. It would mean that we are looking
190-
# for a resource in a zip file, there's an entry matching it in the return
191-
# value of contents(), but we never actually found it in the zip's table of
192-
# contents.
193-
raise AssertionError('Impossible situation')
158+
return (trees.from_package(package) / name).is_file()
194159

195160

196161
def contents(package):
@@ -201,48 +166,4 @@ def contents(package):
201166
to check if it is a resource or not.
202167
"""
203168
package = _get_package(package)
204-
package_directory = Path(package.__file__).parent
205-
try:
206-
return os.listdir(str(package_directory))
207-
except OSError as error:
208-
if error.errno not in (errno.ENOENT, errno.ENOTDIR):
209-
# We won't hit this in the Python 2 tests, so it'll appear
210-
# uncovered. We could mock os.listdir() to return a non-ENOENT or
211-
# ENOTDIR, but then we'd have to depend on another external
212-
# library since Python 2 doesn't have unittest.mock. It's not
213-
# worth it.
214-
raise # pragma: nocover
215-
# The package is probably in a zip file.
216-
archive_path = getattr(package.__loader__, 'archive', None)
217-
if archive_path is None:
218-
raise
219-
relpath = package_directory.relative_to(archive_path)
220-
with ZipFile(archive_path) as zf:
221-
toc = zf.namelist()
222-
subdirs_seen = set()
223-
subdirs_returned = []
224-
for filename in toc:
225-
path = Path(filename)
226-
# Strip off any path component parts that are in common with the
227-
# package directory, relative to the zip archive's file system
228-
# path. This gives us all the parts that live under the named
229-
# package inside the zip file. If the length of these subparts is
230-
# exactly 1, then it is situated inside the package. The resulting
231-
# length will be 0 if it's above the package, and it will be
232-
# greater than 1 if it lives in a subdirectory of the package
233-
# directory.
234-
#
235-
# However, since directories themselves don't appear in the zip
236-
# archive as a separate entry, we need to return the first path
237-
# component for any case that has > 1 subparts -- but only once!
238-
if path.parts[:len(relpath.parts)] != relpath.parts:
239-
continue
240-
subparts = path.parts[len(relpath.parts):]
241-
if len(subparts) == 1:
242-
subdirs_returned.append(subparts[0])
243-
elif len(subparts) > 1: # pragma: nobranch
244-
subdir = subparts[0]
245-
if subdir not in subdirs_seen:
246-
subdirs_seen.add(subdir)
247-
subdirs_returned.append(subdir)
248-
return subdirs_returned
169+
return list(item.name for item in trees.from_package(package).iterdir())

0 commit comments

Comments
 (0)