Skip to content

Commit

Permalink
Merge pull request #448 from takluyver/zip-date-min-1980
Browse files Browse the repository at this point in the history
Push wheel timestamps to 1980 if SOURCE_DATE_EPOCH before that
  • Loading branch information
takluyver committed Oct 6, 2021
2 parents 7c7f3f9 + 47c1b48 commit 039efba
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 13 deletions.
19 changes: 19 additions & 0 deletions flit_core/flit_core/tests/test_wheel.py
@@ -1,4 +1,5 @@
from pathlib import Path
from zipfile import ZipFile

from testpath import assert_isfile

Expand All @@ -10,3 +11,21 @@ def test_licenses_dir(tmp_path):
# Smoketest for https://github.com/takluyver/flit/issues/399
info = make_wheel_in(samples_dir / 'inclusion' / 'pyproject.toml', tmp_path)
assert_isfile(info.file)


def test_source_date_epoch(tmp_path, monkeypatch):
monkeypatch.setenv('SOURCE_DATE_EPOCH', '1633007882')
info = make_wheel_in(samples_dir / 'pep621' / 'pyproject.toml', tmp_path)
assert_isfile(info.file)
# Minimum value for zip timestamps is 1980-1-1
with ZipFile(info.file, 'r') as zf:
assert zf.getinfo('module1a.py').date_time[:3] == (2021, 9, 30)


def test_zero_timestamp(tmp_path, monkeypatch):
monkeypatch.setenv('SOURCE_DATE_EPOCH', '0')
info = make_wheel_in(samples_dir / 'pep621' / 'pyproject.toml', tmp_path)
assert_isfile(info.file)
# Minimum value for zip timestamps is 1980-1-1
with ZipFile(info.file, 'r') as zf:
assert zf.getinfo('module1a.py').date_time == (1980, 1, 1, 0, 0, 0)
36 changes: 23 additions & 13 deletions flit_core/flit_core/wheel.py
Expand Up @@ -2,15 +2,14 @@
import contextlib
from datetime import datetime
import hashlib
from glob import glob
import io
import logging
import os
import os.path as osp
import stat
import sys
import tempfile
from types import SimpleNamespace
from typing import Optional
import zipfile

from flit_core import __version__
Expand All @@ -36,6 +35,27 @@ def _set_zinfo_mode(zinfo, mode):
zinfo.external_attr = mode << 16


def zip_timestamp_from_env() -> Optional[tuple]:
"""Prepare a timestamp from $SOURCE_DATE_EPOCH, if set"""
try:
# If SOURCE_DATE_EPOCH is set (e.g. by Debian), it's used for
# timestamps inside the zip file.
d = datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']))
except (KeyError, ValueError):
# Otherwise, we'll use the mtime of files, and generated files will
# default to 2016-1-1 00:00:00
return None

if d.year >= 1980:
log.info("Zip timestamps will be from SOURCE_DATE_EPOCH: %s", d)
# zipfile expects a 6-tuple, not a datetime object
return d.year, d.month, d.day, d.hour, d.minute, d.second
else:
log.info("SOURCE_DATE_EPOCH is below the minimum for zip file timestamps")
log.info("Zip timestamps will be 1980-01-01 00:00:00")
return 1980, 1, 1, 0, 0, 0


class WheelBuilder:
def __init__(self, directory, module, metadata, entrypoints, target_fp):
"""Build a wheel from a module/package
Expand All @@ -46,17 +66,7 @@ def __init__(self, directory, module, metadata, entrypoints, target_fp):
self.entrypoints = entrypoints

self.records = []
try:
# If SOURCE_DATE_EPOCH is set (e.g. by Debian), it's used for
# timestamps inside the zip file.
d = datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']))
log.info("Zip timestamps will be from SOURCE_DATE_EPOCH: %s", d)
# zipfile expects a 6-tuple, not a datetime object
self.source_time_stamp = (d.year, d.month, d.day, d.hour, d.minute, d.second)
except (KeyError, ValueError):
# Otherwise, we'll use the mtime of files, and generated files will
# default to 2016-1-1 00:00:00
self.source_time_stamp = None
self.source_time_stamp = zip_timestamp_from_env()

# Open the zip file ready to write
self.wheel_zip = zipfile.ZipFile(target_fp, 'w',
Expand Down

0 comments on commit 039efba

Please sign in to comment.