22import errno
33import tempfile
44
5+ from . import trees
56from ._compat import FileNotFoundError
67from contextlib import contextmanager
78from importlib import import_module
89from io import BytesIO , TextIOWrapper , open as io_open
910from pathlib2 import Path
10- from zipfile import ZipFile
1111
1212
1313def _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
196161def 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