Skip to content

Commit e618f11

Browse files
committed
pythongh-98098: Move zipfile into a package.
1 parent e63d7da commit e618f11

File tree

5 files changed

+2601
-306
lines changed

5 files changed

+2601
-306
lines changed

Lib/zipfile.py

Lines changed: 0 additions & 306 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,312 +2183,6 @@ def _compile(file, optimize=-1):
21832183
return (fname, archivename)
21842184

21852185

2186-
def _parents(path):
2187-
"""
2188-
Given a path with elements separated by
2189-
posixpath.sep, generate all parents of that path.
2190-
2191-
>>> list(_parents('b/d'))
2192-
['b']
2193-
>>> list(_parents('/b/d/'))
2194-
['/b']
2195-
>>> list(_parents('b/d/f/'))
2196-
['b/d', 'b']
2197-
>>> list(_parents('b'))
2198-
[]
2199-
>>> list(_parents(''))
2200-
[]
2201-
"""
2202-
return itertools.islice(_ancestry(path), 1, None)
2203-
2204-
2205-
def _ancestry(path):
2206-
"""
2207-
Given a path with elements separated by
2208-
posixpath.sep, generate all elements of that path
2209-
2210-
>>> list(_ancestry('b/d'))
2211-
['b/d', 'b']
2212-
>>> list(_ancestry('/b/d/'))
2213-
['/b/d', '/b']
2214-
>>> list(_ancestry('b/d/f/'))
2215-
['b/d/f', 'b/d', 'b']
2216-
>>> list(_ancestry('b'))
2217-
['b']
2218-
>>> list(_ancestry(''))
2219-
[]
2220-
"""
2221-
path = path.rstrip(posixpath.sep)
2222-
while path and path != posixpath.sep:
2223-
yield path
2224-
path, tail = posixpath.split(path)
2225-
2226-
2227-
_dedupe = dict.fromkeys
2228-
"""Deduplicate an iterable in original order"""
2229-
2230-
2231-
def _difference(minuend, subtrahend):
2232-
"""
2233-
Return items in minuend not in subtrahend, retaining order
2234-
with O(1) lookup.
2235-
"""
2236-
return itertools.filterfalse(set(subtrahend).__contains__, minuend)
2237-
2238-
2239-
class CompleteDirs(ZipFile):
2240-
"""
2241-
A ZipFile subclass that ensures that implied directories
2242-
are always included in the namelist.
2243-
"""
2244-
2245-
@staticmethod
2246-
def _implied_dirs(names):
2247-
parents = itertools.chain.from_iterable(map(_parents, names))
2248-
as_dirs = (p + posixpath.sep for p in parents)
2249-
return _dedupe(_difference(as_dirs, names))
2250-
2251-
def namelist(self):
2252-
names = super(CompleteDirs, self).namelist()
2253-
return names + list(self._implied_dirs(names))
2254-
2255-
def _name_set(self):
2256-
return set(self.namelist())
2257-
2258-
def resolve_dir(self, name):
2259-
"""
2260-
If the name represents a directory, return that name
2261-
as a directory (with the trailing slash).
2262-
"""
2263-
names = self._name_set()
2264-
dirname = name + '/'
2265-
dir_match = name not in names and dirname in names
2266-
return dirname if dir_match else name
2267-
2268-
@classmethod
2269-
def make(cls, source):
2270-
"""
2271-
Given a source (filename or zipfile), return an
2272-
appropriate CompleteDirs subclass.
2273-
"""
2274-
if isinstance(source, CompleteDirs):
2275-
return source
2276-
2277-
if not isinstance(source, ZipFile):
2278-
return cls(source)
2279-
2280-
# Only allow for FastLookup when supplied zipfile is read-only
2281-
if 'r' not in source.mode:
2282-
cls = CompleteDirs
2283-
2284-
source.__class__ = cls
2285-
return source
2286-
2287-
2288-
class FastLookup(CompleteDirs):
2289-
"""
2290-
ZipFile subclass to ensure implicit
2291-
dirs exist and are resolved rapidly.
2292-
"""
2293-
2294-
def namelist(self):
2295-
with contextlib.suppress(AttributeError):
2296-
return self.__names
2297-
self.__names = super(FastLookup, self).namelist()
2298-
return self.__names
2299-
2300-
def _name_set(self):
2301-
with contextlib.suppress(AttributeError):
2302-
return self.__lookup
2303-
self.__lookup = super(FastLookup, self)._name_set()
2304-
return self.__lookup
2305-
2306-
2307-
class Path:
2308-
"""
2309-
A pathlib-compatible interface for zip files.
2310-
2311-
Consider a zip file with this structure::
2312-
2313-
.
2314-
├── a.txt
2315-
└── b
2316-
├── c.txt
2317-
└── d
2318-
└── e.txt
2319-
2320-
>>> data = io.BytesIO()
2321-
>>> zf = ZipFile(data, 'w')
2322-
>>> zf.writestr('a.txt', 'content of a')
2323-
>>> zf.writestr('b/c.txt', 'content of c')
2324-
>>> zf.writestr('b/d/e.txt', 'content of e')
2325-
>>> zf.filename = 'mem/abcde.zip'
2326-
2327-
Path accepts the zipfile object itself or a filename
2328-
2329-
>>> root = Path(zf)
2330-
2331-
From there, several path operations are available.
2332-
2333-
Directory iteration (including the zip file itself):
2334-
2335-
>>> a, b = root.iterdir()
2336-
>>> a
2337-
Path('mem/abcde.zip', 'a.txt')
2338-
>>> b
2339-
Path('mem/abcde.zip', 'b/')
2340-
2341-
name property:
2342-
2343-
>>> b.name
2344-
'b'
2345-
2346-
join with divide operator:
2347-
2348-
>>> c = b / 'c.txt'
2349-
>>> c
2350-
Path('mem/abcde.zip', 'b/c.txt')
2351-
>>> c.name
2352-
'c.txt'
2353-
2354-
Read text:
2355-
2356-
>>> c.read_text()
2357-
'content of c'
2358-
2359-
existence:
2360-
2361-
>>> c.exists()
2362-
True
2363-
>>> (b / 'missing.txt').exists()
2364-
False
2365-
2366-
Coercion to string:
2367-
2368-
>>> import os
2369-
>>> str(c).replace(os.sep, posixpath.sep)
2370-
'mem/abcde.zip/b/c.txt'
2371-
2372-
At the root, ``name``, ``filename``, and ``parent``
2373-
resolve to the zipfile. Note these attributes are not
2374-
valid and will raise a ``ValueError`` if the zipfile
2375-
has no filename.
2376-
2377-
>>> root.name
2378-
'abcde.zip'
2379-
>>> str(root.filename).replace(os.sep, posixpath.sep)
2380-
'mem/abcde.zip'
2381-
>>> str(root.parent)
2382-
'mem'
2383-
"""
2384-
2385-
__repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
2386-
2387-
def __init__(self, root, at=""):
2388-
"""
2389-
Construct a Path from a ZipFile or filename.
2390-
2391-
Note: When the source is an existing ZipFile object,
2392-
its type (__class__) will be mutated to a
2393-
specialized type. If the caller wishes to retain the
2394-
original type, the caller should either create a
2395-
separate ZipFile object or pass a filename.
2396-
"""
2397-
self.root = FastLookup.make(root)
2398-
self.at = at
2399-
2400-
def open(self, mode='r', *args, pwd=None, **kwargs):
2401-
"""
2402-
Open this entry as text or binary following the semantics
2403-
of ``pathlib.Path.open()`` by passing arguments through
2404-
to io.TextIOWrapper().
2405-
"""
2406-
if self.is_dir():
2407-
raise IsADirectoryError(self)
2408-
zip_mode = mode[0]
2409-
if not self.exists() and zip_mode == 'r':
2410-
raise FileNotFoundError(self)
2411-
stream = self.root.open(self.at, zip_mode, pwd=pwd)
2412-
if 'b' in mode:
2413-
if args or kwargs:
2414-
raise ValueError("encoding args invalid for binary operation")
2415-
return stream
2416-
else:
2417-
kwargs["encoding"] = io.text_encoding(kwargs.get("encoding"))
2418-
return io.TextIOWrapper(stream, *args, **kwargs)
2419-
2420-
@property
2421-
def name(self):
2422-
return pathlib.Path(self.at).name or self.filename.name
2423-
2424-
@property
2425-
def suffix(self):
2426-
return pathlib.Path(self.at).suffix or self.filename.suffix
2427-
2428-
@property
2429-
def suffixes(self):
2430-
return pathlib.Path(self.at).suffixes or self.filename.suffixes
2431-
2432-
@property
2433-
def stem(self):
2434-
return pathlib.Path(self.at).stem or self.filename.stem
2435-
2436-
@property
2437-
def filename(self):
2438-
return pathlib.Path(self.root.filename).joinpath(self.at)
2439-
2440-
def read_text(self, *args, **kwargs):
2441-
kwargs["encoding"] = io.text_encoding(kwargs.get("encoding"))
2442-
with self.open('r', *args, **kwargs) as strm:
2443-
return strm.read()
2444-
2445-
def read_bytes(self):
2446-
with self.open('rb') as strm:
2447-
return strm.read()
2448-
2449-
def _is_child(self, path):
2450-
return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
2451-
2452-
def _next(self, at):
2453-
return self.__class__(self.root, at)
2454-
2455-
def is_dir(self):
2456-
return not self.at or self.at.endswith("/")
2457-
2458-
def is_file(self):
2459-
return self.exists() and not self.is_dir()
2460-
2461-
def exists(self):
2462-
return self.at in self.root._name_set()
2463-
2464-
def iterdir(self):
2465-
if not self.is_dir():
2466-
raise ValueError("Can't listdir a file")
2467-
subs = map(self._next, self.root.namelist())
2468-
return filter(self._is_child, subs)
2469-
2470-
def __str__(self):
2471-
return posixpath.join(self.root.filename, self.at)
2472-
2473-
def __repr__(self):
2474-
return self.__repr.format(self=self)
2475-
2476-
def joinpath(self, *other):
2477-
next = posixpath.join(self.at, *other)
2478-
return self._next(self.root.resolve_dir(next))
2479-
2480-
__truediv__ = joinpath
2481-
2482-
@property
2483-
def parent(self):
2484-
if not self.at:
2485-
return self.filename.parent
2486-
parent_at = posixpath.dirname(self.at.rstrip('/'))
2487-
if parent_at:
2488-
parent_at += '/'
2489-
return self._next(parent_at)
2490-
2491-
24922186
def main(args=None):
24932187
import argparse
24942188

Lib/zipfile/__init__.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from ._core import (
2+
BadZipFile,
3+
BadZipfile,
4+
error,
5+
ZIP_STORED,
6+
ZIP_DEFLATED,
7+
ZIP_BZIP2,
8+
ZIP_LZMA,
9+
is_zipfile,
10+
ZipInfo,
11+
ZipFile,
12+
PyZipFile,
13+
LargeZipFile,
14+
15+
# used privately for tests
16+
ZIP64_LIMIT,
17+
sizeCentralDir,
18+
sizeEndCentDir,
19+
sizeEndCentDir64,
20+
DEFAULT_VERSION,
21+
_MASK_USE_DATA_DESCRIPTOR,
22+
ZIP_FILECOUNT_LIMIT,
23+
)
24+
from ._path import (
25+
Path,
26+
27+
# used privately for tests
28+
CompleteDirs,
29+
)
30+
# used privately for tests
31+
from .__main__ import main
32+
33+
34+
__all__ = ["BadZipFile", "BadZipfile", "error",
35+
"ZIP_STORED", "ZIP_DEFLATED", "ZIP_BZIP2", "ZIP_LZMA",
36+
"is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile",
37+
"Path"]

0 commit comments

Comments
 (0)