diff options
author | Barry Warsaw <barry@python.org> | 2015-06-09 15:28:55 -0400 |
---|---|---|
committer | Barry Warsaw <barry@python.org> | 2015-06-09 15:28:55 -0400 |
commit | f7e751759340246ef00f6fd31d6991c8150cc511 (patch) | |
tree | 72591f1843fc49d04c9f93c8d601336aa8a9ad4a | |
parent | 9ae55eea1a70044e482190a46dae3aff568ad9d1 (diff) | |
download | wheel-f7e751759340246ef00f6fd31d6991c8150cc511.tar.gz |
Apply the Debian patch for reproducible wheel files, but instead of hardcoding
the timestamp, allow the environment variable WHEEL_FORCE_TIMESTAMP to be used
to force the TarInfo timestamps.
-rw-r--r-- | wheel/archive.py | 9 | ||||
-rw-r--r-- | wheel/bdist_wheel.py | 2 | ||||
-rw-r--r-- | wheel/metadata.py | 9 | ||||
-rw-r--r-- | wheel/test/test_wheelfile.py | 56 |
4 files changed, 73 insertions, 3 deletions
diff --git a/wheel/archive.py b/wheel/archive.py index 225d295..71dcbd4 100644 --- a/wheel/archive.py +++ b/wheel/archive.py @@ -2,6 +2,7 @@ Archive tools for wheel. """ +import time import logging import os.path import zipfile @@ -56,6 +57,14 @@ def make_wheelfile_inner(base_name, base_dir='.'): for score, path in deferred: writefile(path) + # Before we close the zip file, see if the caller is forcing the timestamp + # of the individual TarInfo objects. See issue #143. + timestamp = os.environ.get('WHEEL_FORCE_TIMESTAMP') + if timestamp is not None: + date_time = time.localtime(int(timestamp))[0:6] + for info in zip.infolist(): + info.date_time = date_time + zip.close() return zip_filename diff --git a/wheel/bdist_wheel.py b/wheel/bdist_wheel.py index ac770e9..bcf63fb 100644 --- a/wheel/bdist_wheel.py +++ b/wheel/bdist_wheel.py @@ -410,7 +410,7 @@ class bdist_wheel(Command): pymeta['extensions']['python.details']['document_names']['license'] = license_filename with open(metadata_json_path, "w") as metadata_json: - json.dump(pymeta, metadata_json) + json.dump(pymeta, metadata_json, sort_keys=True) adios(egginfo_path) diff --git a/wheel/metadata.py b/wheel/metadata.py index 02c0663..d6a7627 100644 --- a/wheel/metadata.py +++ b/wheel/metadata.py @@ -74,7 +74,14 @@ def handle_requires(metadata, pkg_info, key): if may_requires: metadata['run_requires'] = [] - for key, value in may_requires.items(): + def sort_key(item): + # Both condition and extra could be None, which can't be compared + # against strings in Python 3. + key, value = item + if key.condition is None: + return '' + return key.condition + for key, value in sorted(may_requires.items(), key=sort_key): may_requirement = OrderedDict((('requires', value),)) if key.extra: may_requirement['extra'] = key.extra diff --git a/wheel/test/test_wheelfile.py b/wheel/test/test_wheelfile.py index e362ceb..561f578 100644 --- a/wheel/test/test_wheelfile.py +++ b/wheel/test/test_wheelfile.py @@ -1,11 +1,48 @@ +import os import wheel.install +import wheel.archive import hashlib try: from StringIO import StringIO except ImportError: from io import BytesIO as StringIO +import codecs import zipfile import pytest +import shutil +import tempfile +from contextlib import contextmanager + +@contextmanager +def environ(key, value): + old_value = os.environ.get(key) + try: + os.environ[key] = value + yield + finally: + if old_value is None: + del os.environ[key] + else: + os.environ[key] = old_value + +@contextmanager +def temporary_directory(): + # tempfile.TemporaryDirectory doesn't exist in Python 2. + tempdir = tempfile.mkdtemp() + try: + yield tempdir + finally: + shutil.rmtree(tempdir) + +@contextmanager +def readable_zipfile(path): + # zipfile.ZipFile() isn't a context manager under Python 2. + zf = zipfile.ZipFile(path, 'r') + try: + yield zf + finally: + zf.close() + def test_verifying_zipfile(): if not hasattr(zipfile.ZipExtFile, '_update_crc'): @@ -66,4 +103,21 @@ def test_pop_zipfile(): zf = wheel.install.VerifyingZipFile(sio, 'r') assert len(zf.infolist()) == 1 -
\ No newline at end of file + +def test_zipfile_timestamp(): + # An environment variable can be used to influence the timestamp on + # TarInfo objects inside the zip. See issue #143. TemporaryDirectory is + # not a context manager under Python 3. + with temporary_directory() as tempdir: + for filename in ('one', 'two', 'three'): + path = os.path.join(tempdir, filename) + with codecs.open(path, 'w', encoding='utf-8') as fp: + fp.write(filename + '\n') + zip_base_name = os.path.join(tempdir, 'dummy') + # The earliest date representable in TarInfos, 1980-01-01 + with environ('WHEEL_FORCE_TIMESTAMP', '315576060'): + zip_filename = wheel.archive.make_wheelfile_inner( + zip_base_name, tempdir) + with readable_zipfile(zip_filename) as zf: + for info in zf.infolist(): + assert info.date_time[:3] == (1980, 1, 1) |