]> crepu.dev Git - config.git/blame - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/setuptools/archive_util.py
ActualizaciĆ³n de Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / setuptools / archive_util.py
CommitLineData
53e6db90
DC
1"""Utilities for extracting common archive formats"""
2
3import zipfile
4import tarfile
5import os
6import shutil
7import posixpath
8import contextlib
9from distutils.errors import DistutilsError
10
11from ._path import ensure_directory
12
13__all__ = [
14 "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
15 "UnrecognizedFormat", "extraction_drivers", "unpack_directory",
16]
17
18
19class UnrecognizedFormat(DistutilsError):
20 """Couldn't recognize the archive type"""
21
22
23def default_filter(src, dst):
24 """The default progress/filter callback; returns True for all files"""
25 return dst
26
27
28def unpack_archive(
29 filename, extract_dir, progress_filter=default_filter,
30 drivers=None):
31 """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
32
33 `progress_filter` is a function taking two arguments: a source path
34 internal to the archive ('/'-separated), and a filesystem path where it
35 will be extracted. The callback must return the desired extract path
36 (which may be the same as the one passed in), or else ``None`` to skip
37 that file or directory. The callback can thus be used to report on the
38 progress of the extraction, as well as to filter the items extracted or
39 alter their extraction paths.
40
41 `drivers`, if supplied, must be a non-empty sequence of functions with the
42 same signature as this function (minus the `drivers` argument), that raise
43 ``UnrecognizedFormat`` if they do not support extracting the designated
44 archive type. The `drivers` are tried in sequence until one is found that
45 does not raise an error, or until all are exhausted (in which case
46 ``UnrecognizedFormat`` is raised). If you do not supply a sequence of
47 drivers, the module's ``extraction_drivers`` constant will be used, which
48 means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
49 order.
50 """
51 for driver in drivers or extraction_drivers:
52 try:
53 driver(filename, extract_dir, progress_filter)
54 except UnrecognizedFormat:
55 continue
56 else:
57 return
58 else:
59 raise UnrecognizedFormat(
60 "Not a recognized archive type: %s" % filename
61 )
62
63
64def unpack_directory(filename, extract_dir, progress_filter=default_filter):
65 """"Unpack" a directory, using the same interface as for archives
66
67 Raises ``UnrecognizedFormat`` if `filename` is not a directory
68 """
69 if not os.path.isdir(filename):
70 raise UnrecognizedFormat("%s is not a directory" % filename)
71
72 paths = {
73 filename: ('', extract_dir),
74 }
75 for base, dirs, files in os.walk(filename):
76 src, dst = paths[base]
77 for d in dirs:
78 paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d)
79 for f in files:
80 target = os.path.join(dst, f)
81 target = progress_filter(src + f, target)
82 if not target:
83 # skip non-files
84 continue
85 ensure_directory(target)
86 f = os.path.join(base, f)
87 shutil.copyfile(f, target)
88 shutil.copystat(f, target)
89
90
91def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
92 """Unpack zip `filename` to `extract_dir`
93
94 Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
95 by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation
96 of the `progress_filter` argument.
97 """
98
99 if not zipfile.is_zipfile(filename):
100 raise UnrecognizedFormat("%s is not a zip file" % (filename,))
101
102 with zipfile.ZipFile(filename) as z:
103 _unpack_zipfile_obj(z, extract_dir, progress_filter)
104
105
106def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter):
107 """Internal/private API used by other parts of setuptools.
108 Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile`
109 object instead of a filename.
110 """
111 for info in zipfile_obj.infolist():
112 name = info.filename
113
114 # don't extract absolute paths or ones with .. in them
115 if name.startswith('/') or '..' in name.split('/'):
116 continue
117
118 target = os.path.join(extract_dir, *name.split('/'))
119 target = progress_filter(name, target)
120 if not target:
121 continue
122 if name.endswith('/'):
123 # directory
124 ensure_directory(target)
125 else:
126 # file
127 ensure_directory(target)
128 data = zipfile_obj.read(info.filename)
129 with open(target, 'wb') as f:
130 f.write(data)
131 unix_attributes = info.external_attr >> 16
132 if unix_attributes:
133 os.chmod(target, unix_attributes)
134
135
136def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
137 """Resolve any links and extract link targets as normal files."""
138 while tar_member_obj is not None and (
139 tar_member_obj.islnk() or tar_member_obj.issym()):
140 linkpath = tar_member_obj.linkname
141 if tar_member_obj.issym():
142 base = posixpath.dirname(tar_member_obj.name)
143 linkpath = posixpath.join(base, linkpath)
144 linkpath = posixpath.normpath(linkpath)
145 tar_member_obj = tar_obj._getmember(linkpath)
146
147 is_file_or_dir = (
148 tar_member_obj is not None and
149 (tar_member_obj.isfile() or tar_member_obj.isdir())
150 )
151 if is_file_or_dir:
152 return tar_member_obj
153
154 raise LookupError('Got unknown file type')
155
156
157def _iter_open_tar(tar_obj, extract_dir, progress_filter):
158 """Emit member-destination pairs from a tar archive."""
159 # don't do any chowning!
160 tar_obj.chown = lambda *args: None
161
162 with contextlib.closing(tar_obj):
163 for member in tar_obj:
164 name = member.name
165 # don't extract absolute paths or ones with .. in them
166 if name.startswith('/') or '..' in name.split('/'):
167 continue
168
169 prelim_dst = os.path.join(extract_dir, *name.split('/'))
170
171 try:
172 member = _resolve_tar_file_or_dir(tar_obj, member)
173 except LookupError:
174 continue
175
176 final_dst = progress_filter(name, prelim_dst)
177 if not final_dst:
178 continue
179
180 if final_dst.endswith(os.sep):
181 final_dst = final_dst[:-1]
182
183 yield member, final_dst
184
185
186def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
187 """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
188
189 Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
190 by ``tarfile.open()``). See ``unpack_archive()`` for an explanation
191 of the `progress_filter` argument.
192 """
193 try:
194 tarobj = tarfile.open(filename)
195 except tarfile.TarError as e:
196 raise UnrecognizedFormat(
197 "%s is not a compressed or uncompressed tar file" % (filename,)
198 ) from e
199
200 for member, final_dst in _iter_open_tar(
201 tarobj, extract_dir, progress_filter,
202 ):
203 try:
204 # XXX Ugh
205 tarobj._extract_member(member, final_dst)
206 except tarfile.ExtractError:
207 # chown/chmod/mkfifo/mknode/makedev failed
208 pass
209
210 return True
211
212
213extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile