]>
crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/zipp/__init__.py
9 from .py310compat
import text_encoding
10 from .glob
import translate
18 Given a path with elements separated by
19 posixpath.sep, generate all parents of that path.
21 >>> list(_parents('b/d'))
23 >>> list(_parents('/b/d/'))
25 >>> list(_parents('b/d/f/'))
27 >>> list(_parents('b'))
29 >>> list(_parents(''))
32 return itertools
.islice(_ancestry(path
), 1, None)
37 Given a path with elements separated by
38 posixpath.sep, generate all elements of that path
40 >>> list(_ancestry('b/d'))
42 >>> list(_ancestry('/b/d/'))
44 >>> list(_ancestry('b/d/f/'))
46 >>> list(_ancestry('b'))
48 >>> list(_ancestry(''))
51 path
= path
.rstrip(posixpath
.sep
)
52 while path
and path
!= posixpath
.sep
:
54 path
, tail
= posixpath
.split(path
)
57 _dedupe
= dict.fromkeys
58 """Deduplicate an iterable in original order"""
61 def _difference(minuend
, subtrahend
):
63 Return items in minuend not in subtrahend, retaining order
66 return itertools
.filterfalse(set(subtrahend
).__contains
__, minuend
)
69 class InitializedState
:
71 Mix-in to save the initialization state for pickling.
74 def __init__(self
, *args
, **kwargs
):
76 self
.__kwargs
= kwargs
77 super().__init
__(*args
, **kwargs
)
79 def __getstate__(self
):
80 return self
.__args
, self
.__kwargs
82 def __setstate__(self
, state
):
84 super().__init
__(*args
, **kwargs
)
87 class CompleteDirs(InitializedState
, zipfile
.ZipFile
):
89 A ZipFile subclass that ensures that implied directories
90 are always included in the namelist.
92 >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt']))
94 >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt', 'foo/bar/']))
99 def _implied_dirs(names
):
100 parents
= itertools
.chain
.from_iterable(map(_parents
, names
))
101 as_dirs
= (p
+ posixpath
.sep
for p
in parents
)
102 return _dedupe(_difference(as_dirs
, names
))
105 names
= super().namelist()
106 return names
+ list(self
._implied
_dirs
(names
))
109 return set(self
.namelist())
111 def resolve_dir(self
, name
):
113 If the name represents a directory, return that name
114 as a directory (with the trailing slash).
116 names
= self
._name
_set
()
118 dir_match
= name
not in names
and dirname
in names
119 return dirname
if dir_match
else name
121 def getinfo(self
, name
):
123 Supplement getinfo for implied dirs.
126 return super().getinfo(name
)
128 if not name
.endswith('/') or name
not in self
._name
_set
():
130 return zipfile
.ZipInfo(filename
=name
)
133 def make(cls
, source
):
135 Given a source (filename or zipfile), return an
136 appropriate CompleteDirs subclass.
138 if isinstance(source
, CompleteDirs
):
141 if not isinstance(source
, zipfile
.ZipFile
):
144 # Only allow for FastLookup when supplied zipfile is read-only
145 if 'r' not in source
.mode
:
148 source
.__class
__ = cls
152 def inject(cls
, zf
: zipfile
.ZipFile
) -> zipfile
.ZipFile
:
154 Given a writable zip file zf, inject directory entries for
155 any directories implied by the presence of children.
157 for name
in cls
._implied
_dirs
(zf
.namelist()):
158 zf
.writestr(name
, b
"")
162 class FastLookup(CompleteDirs
):
164 ZipFile subclass to ensure implicit
165 dirs exist and are resolved rapidly.
169 with contextlib
.suppress(AttributeError):
171 self
.__names
= super().namelist()
175 with contextlib
.suppress(AttributeError):
177 self
.__lookup
= super()._name
_set
()
181 def _extract_text_encoding(encoding
=None, *args
, **kwargs
):
182 # stacklevel=3 so that the caller of the caller see any warning.
183 return text_encoding(encoding
, 3), args
, kwargs
188 A pathlib-compatible interface for zip files.
190 Consider a zip file with this structure::
199 >>> data = io.BytesIO()
200 >>> zf = zipfile.ZipFile(data, 'w')
201 >>> zf.writestr('a.txt', 'content of a')
202 >>> zf.writestr('b/c.txt', 'content of c')
203 >>> zf.writestr('b/d/e.txt', 'content of e')
204 >>> zf.filename = 'mem/abcde.zip'
206 Path accepts the zipfile object itself or a filename
210 From there, several path operations are available.
212 Directory iteration (including the zip file itself):
214 >>> a, b = path.iterdir()
216 Path('mem/abcde.zip', 'a.txt')
218 Path('mem/abcde.zip', 'b/')
225 join with divide operator:
229 Path('mem/abcde.zip', 'b/c.txt')
235 >>> c.read_text(encoding='utf-8')
242 >>> (b / 'missing.txt').exists()
248 >>> str(c).replace(os.sep, posixpath.sep)
249 'mem/abcde.zip/b/c.txt'
251 At the root, ``name``, ``filename``, and ``parent``
252 resolve to the zipfile.
258 >>> path.filename == pathlib.Path('mem/abcde.zip')
263 If the zipfile has no filename, such attribtues are not
264 valid and accessing them will raise an Exception.
266 >>> zf.filename = None
268 Traceback (most recent call last):
273 Traceback (most recent call last):
278 Traceback (most recent call last):
282 # workaround python/cpython#106763
286 __repr
= "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
288 def __init__(self
, root
, at
=""):
290 Construct a Path from a ZipFile or filename.
292 Note: When the source is an existing ZipFile object,
293 its type (__class__) will be mutated to a
294 specialized type. If the caller wishes to retain the
295 original type, the caller should either create a
296 separate ZipFile object or pass a filename.
298 self
.root
= FastLookup
.make(root
)
301 def __eq__(self
, other
):
303 >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo'
306 if self
.__class
__ is not other
.__class
__:
307 return NotImplemented
308 return (self
.root
, self
.at
) == (other
.root
, other
.at
)
311 return hash((self
.root
, self
.at
))
313 def open(self
, mode
='r', *args
, pwd
=None, **kwargs
):
315 Open this entry as text or binary following the semantics
316 of ``pathlib.Path.open()`` by passing arguments through
317 to io.TextIOWrapper().
320 raise IsADirectoryError(self
)
322 if not self
.exists() and zip_mode
== 'r':
323 raise FileNotFoundError(self
)
324 stream
= self
.root
.open(self
.at
, zip_mode
, pwd
=pwd
)
327 raise ValueError("encoding args invalid for binary operation")
330 encoding
, args
, kwargs
= _extract_text_encoding(*args
, **kwargs
)
331 return io
.TextIOWrapper(stream
, encoding
, *args
, **kwargs
)
334 return pathlib
.PurePosixPath(self
.at
or self
.root
.filename
)
338 return self
._base
().name
342 return self
._base
().suffix
346 return self
._base
().suffixes
350 return self
._base
().stem
354 return pathlib
.Path(self
.root
.filename
).joinpath(self
.at
)
356 def read_text(self
, *args
, **kwargs
):
357 encoding
, args
, kwargs
= _extract_text_encoding(*args
, **kwargs
)
358 with self
.open('r', encoding
, *args
, **kwargs
) as strm
:
361 def read_bytes(self
):
362 with self
.open('rb') as strm
:
365 def _is_child(self
, path
):
366 return posixpath
.dirname(path
.at
.rstrip("/")) == self
.at
.rstrip("/")
369 return self
.__class
__(self
.root
, at
)
372 return not self
.at
or self
.at
.endswith("/")
375 return self
.exists() and not self
.is_dir()
378 return self
.at
in self
.root
._name
_set
()
381 if not self
.is_dir():
382 raise ValueError("Can't listdir a file")
383 subs
= map(self
._next
, self
.root
.namelist())
384 return filter(self
._is
_child
, subs
)
386 def match(self
, path_pattern
):
387 return pathlib
.PurePosixPath(self
.at
).match(path_pattern
)
389 def is_symlink(self
):
391 Return whether this path is a symlink. Always false (python/cpython#82102).
395 def glob(self
, pattern
):
397 raise ValueError(f
"Unacceptable pattern: {pattern!r}")
399 prefix
= re
.escape(self
.at
)
400 matches
= re
.compile(prefix
+ translate(pattern
)).fullmatch
401 return map(self
._next
, filter(matches
, self
.root
.namelist()))
403 def rglob(self
, pattern
):
404 return self
.glob(f
'**/{pattern}')
406 def relative_to(self
, other
, *extra
):
407 return posixpath
.relpath(str(self
), str(other
.joinpath(*extra
)))
410 return posixpath
.join(self
.root
.filename
, self
.at
)
413 return self
.__repr
.format(self
=self
)
415 def joinpath(self
, *other
):
416 next
= posixpath
.join(self
.at
, *other
)
417 return self
._next
(self
.root
.resolve_dir(next
))
419 __truediv__
= joinpath
424 return self
.filename
.parent
425 parent_at
= posixpath
.dirname(self
.at
.rstrip('/'))
428 return self
._next
(parent_at
)