[Python-modules-commits] r32960 - in packages/wheel/trunk/debian (3 files)

barry at users.alioth.debian.org barry at users.alioth.debian.org
Fri Jun 12 15:06:56 UTC 2015


    Date: Friday, June 12, 2015 @ 15:06:55
  Author: barry
Revision: 32960

d/patch/reproducible-whls.diff: Added based on initial contribution
from Reiner Herrmann, with further refinements by Barry Warsaw based
on upstream pull request review.  (Closes: #776026)

Added:
  packages/wheel/trunk/debian/patches/reproducible-whls.diff
  packages/wheel/trunk/debian/patches/series
Modified:
  packages/wheel/trunk/debian/changelog

Modified: packages/wheel/trunk/debian/changelog
===================================================================
--- packages/wheel/trunk/debian/changelog	2015-06-12 14:54:48 UTC (rev 32959)
+++ packages/wheel/trunk/debian/changelog	2015-06-12 15:06:55 UTC (rev 32960)
@@ -4,6 +4,9 @@
   * d/rules:
     - Add commented out DH_VERBOSE setting.
     - Override the manpage date for reproducible builds. (Closes: #782405)
+  * d/patch/reproducible-whls.diff: Added based on initial contribution
+    from Reiner Herrmann, with further refinements by Barry Warsaw based
+    on upstream pull request review.  (Closes: #776026)
   * d/watch: Use the pypi.debian.net redirector.
 
  -- Barry Warsaw <barry at debian.org>  Wed, 04 Feb 2015 16:44:43 -0500

Added: packages/wheel/trunk/debian/patches/reproducible-whls.diff
===================================================================
--- packages/wheel/trunk/debian/patches/reproducible-whls.diff	                        (rev 0)
+++ packages/wheel/trunk/debian/patches/reproducible-whls.diff	2015-06-12 15:06:55 UTC (rev 32960)
@@ -0,0 +1,172 @@
+Description: .whl files are zips, and as such they contain timestamps.  As
+ part of https://wiki.debian.org/ReproducibleBuilds it was requested to be
+ able to build whl files reproducibly (i.e. with a predictable, stable
+ timestamp between builds).  While this version of the patch has not yet been
+ merged into upstream, it has been reviewed favorably.  The patch can be
+ updated after upstream merge if needed.
+Origin: https://bitbucket.org/pypa/wheel/pull-request/52/apply-the-debian-patch-for-reproducible/diff
+Bug: https://bitbucket.org/pypa/wheel/issue/143/reproducible-whl-files
+Author: Barry Warsaw <barry at debian.org>
+
+--- a/wheel/archive.py
++++ b/wheel/archive.py
+@@ -2,6 +2,8 @@
+ Archive tools for wheel.
+ """
+ 
++import os
++import time
+ import logging
+ import os.path
+ import zipfile
+@@ -31,6 +33,15 @@
+ 
+     log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
+ 
++    # 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 = os.environ.get('WHEEL_FORCE_TIMESTAMP')
++    if timestamp is None:
++        date_time = None
++    else:
++        date_time = time.localtime(int(timestamp))[0:6]
++
+     # XXX support bz2, xz when available
+     zip = zipfile.ZipFile(open(zip_filename, "wb+"), "w",
+                           compression=zipfile.ZIP_DEFLATED)
+@@ -38,8 +49,14 @@
+     score = {'WHEEL': 1, 'METADATA': 2, 'RECORD': 3}
+     deferred = []
+ 
+-    def writefile(path):
+-        zip.write(path, path)
++    def writefile(path, date_time):
++        if date_time is None:
++            st = os.stat(path)
++            mtime = time.localtime(st.st_mtime)
++            date_time = mtime[0:6]
++        zinfo = zipfile.ZipInfo(path, date_time)
++        with open(path, 'rb') as fp:
++            zip.writestr(zinfo, fp.read())
+         log.info("adding '%s'" % path)
+ 
+     for dirpath, dirnames, filenames in os.walk(base_dir):
+@@ -50,11 +67,11 @@
+                 if dirpath.endswith('.dist-info'):
+                     deferred.append((score.get(name, 0), path))
+                 else:
+-                    writefile(path)
++                    writefile(path, date_time)
+ 
+     deferred.sort()
+     for score, path in deferred:
+-        writefile(path)
++        writefile(path, date_time)
+ 
+     zip.close()
+ 
+--- a/wheel/bdist_wheel.py
++++ b/wheel/bdist_wheel.py
+@@ -409,7 +409,7 @@
+             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)
+ 
+--- a/wheel/metadata.py
++++ b/wheel/metadata.py
+@@ -69,7 +69,14 @@
+ 
+     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 = {'requires':value}
+             if key.extra:
+                 may_requirement['extra'] = key.extra
+--- 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
++
++ at 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
++
++ at contextmanager
++def temporary_directory():
++    # tempfile.TemporaryDirectory doesn't exist in Python 2.
++    tempdir = tempfile.mkdtemp()
++    try:
++        yield tempdir
++    finally:
++        shutil.rmtree(tempdir)
++
++ at 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 @@
+     
+     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)

Added: packages/wheel/trunk/debian/patches/series
===================================================================
--- packages/wheel/trunk/debian/patches/series	                        (rev 0)
+++ packages/wheel/trunk/debian/patches/series	2015-06-12 15:06:55 UTC (rev 32960)
@@ -0,0 +1,2 @@
+reproducible-whls.patch
+reproducible-whls.diff




More information about the Python-modules-commits mailing list