import csv import hashlib import os.path import re import stat import time from io import StringIO, TextIOWrapper from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
from wheel.cli import WheelError from wheel.util import log, urlsafe_b64decode, urlsafe_b64encode
# Non-greedy matching of an optional build number may be too clever (more # invalid wheel filenames will match). Separate regex for .dist-info?
WHEEL_INFO_RE = re.compile(
r"""^(?P(?P[^\s-]+?)-(?P[^\s-]+?))(-(?P\d[^\s-]*))?
-(?P<pyver>[^\s-]+?)-(?P<abi>[^\s-]+?)-(?P<plat>\S+)\.whl$""",
re.VERBOSE,
)
MINIMUM_TIMESTAMP = 315532800 # 1980-01-01 00:00:00 UTC
def get_zipinfo_datetime(timestamp=None): # Some applications need reproducible .whl files, but they can't do this without # forcing the timestamp of the individual ZipInfo objects. See issue #143.
timestamp = int(os.environ.get("SOURCE_DATE_EPOCH", timestamp or time.time()))
timestamp = max(timestamp, MINIMUM_TIMESTAMP) return time.gmtime(timestamp)[0:6]
class WheelFile(ZipFile): """A ZipFile derivative class that also reads SHA-256 hashes from
.dist-info/RECORD and checks any read files against those. """
# Fill in the expected hashes by reading them from RECORD try:
record = self.open(self.record_path) except KeyError: raise WheelError(f"Missing {self.record_path} file") fromNone
with record: for line in csv.reader(
TextIOWrapper(record, newline="", encoding="utf-8")
):
path, hash_sum, size = line ifnot hash_sum: continue
def open(self, name_or_info, mode="r", pwd=None): def _update_crc(newdata):
eof = ef._eof
update_crc_orig(newdata)
running_hash.update(newdata) if eof and running_hash.digest() != expected_hash: raise WheelError(f"Hash mismatch for file '{ef_name}'")
ef_name = (
name_or_info.filename if isinstance(name_or_info, ZipInfo) else name_or_info
) if (
mode == "r" andnot ef_name.endswith("/") and ef_name notin self._file_hashes
): raise WheelError(f"No hash found for file '{ef_name}'")
ef = ZipFile.open(self, name_or_info, mode, pwd) if mode == "r"andnot ef_name.endswith("/"):
algorithm, expected_hash = self._file_hashes[ef_name] if expected_hash isnotNone: # Monkey patch the _update_crc method to also check for the hash from # RECORD
running_hash = hashlib.new(algorithm)
update_crc_orig, ef._update_crc = ef._update_crc, _update_crc
return ef
def write_files(self, base_dir):
log.info(f"creating '{self.filename}' and adding '{base_dir}' to it")
deferred = [] for root, dirnames, filenames in os.walk(base_dir): # Sort the directory names so that `os.walk` will walk them in a # defined order on the next iteration.
dirnames.sort() for name in sorted(filenames):
path = os.path.normpath(os.path.join(root, name)) if os.path.isfile(path):
arcname = os.path.relpath(path, base_dir).replace(os.path.sep, "/") if arcname == self.record_path: pass elif root.endswith(".dist-info"):
deferred.append((path, arcname)) else:
self.write(path, arcname)
deferred.sort() for path, arcname in deferred:
self.write(path, arcname)
def write(self, filename, arcname=None, compress_type=None): with open(filename, "rb") as f:
st = os.fstat(f.fileno())
data = f.read()
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.