view _postprocess-sdist.py @ 377:6b327893a9c3

treesum: Handle comments in .treesum files without accounting for CRCs
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 12 May 2025 09:40:33 +0200
parents c19a21180a8f
children
line wrap: on
line source

# -*- coding: utf-8 -*-
# :-
# SPDX-FileCopyrightText: © 2025 Franz Glasner
# SPDX-License-Identifier: BSD-3-Clause
# :-
"""Postprocress a .tar-sdist to include tests/data with symlinks as symlinks.

Produce an sdist with all the data in :file:`tests/data/`::

  rm -rf dist py_cutils.egg-info
  python setup.py sdist
  python _postprocess-sdist.py
  gzip dist/*.tar

"""

from __future__ import print_function, absolute_import

try:
    from configparser import ConfigParser
except ImportError:
    from ConfigParser import SafeConfigParser as ConfigParser
import importlib
import io
import os
import tarfile


def main():
    with io.open("setup.cfg", "rt", encoding="utf-8") as cfgfile:
        cp = ConfigParser()
        if hasattr(cp, "read_file"):
            cp.read_file(cfgfile, "setup.cfg")
        else:
            cp.readfp(cfgfile, "setup.cfg")
    project_name = cp.get("metadata", "name")
    project_version = cp.get("metadata", "version")
    if project_version.startswith("attr:"):
        vermodname, dummy, vermodattr = (project_version[5:]
                                         .strip()
                                         .rpartition('.'))
        assert dummy is not None and vermodattr is not None
        vermod = importlib.import_module(vermodname)
        project_version = getattr(vermod, vermodattr)
    elif project_version.startswith("file:"):
        assert False
    #
    # Compressed tar files cannot be modified by Python: make sure the
    # originally generated archive is uncompressed.
    #
    assert cp.get("sdist", "formats") == "tar"

    archive_name = "{}-{}.tar".format(project_name, project_version)
    archive_path = "dist/" + archive_name
    assert os.path.isfile(archive_path)

    # the directory within the archive
    archive_path_prefix = "{}-{}".format(project_name, project_version)

    egg_directory = "{}.egg-info".format(project_name.replace("-", "_"))
    assert os.path.isdir(egg_directory)
    sources_txt_path = "{}/SOURCES.txt".format(egg_directory)
    sources_txt_arcname = "{}/{}/SOURCES.txt".format(
        archive_path_prefix,
        egg_directory)

    with tarfile.TarFile(archive_path, "r") as tf:
        sf = tf.extractfile(sources_txt_arcname)
        try:
            sources_txt = sf.read()
        finally:
            sf.close()

    with tarfile.TarFile(archive_path, "a") as tf:
        arcname = "{}/tests/data".format(archive_path_prefix)
        try:
            info = tf.getmember(arcname)
        except KeyError:
            pass
        else:
            raise RuntimeError("already postprocessed")
        pre_names = set(tf.getnames())
        tf.add("tests/data", arcname=arcname, recursive=True)

        #
        # Determine the new files and symlinks that are to be added
        # to SOURCES.txt. Skip directories.
        #
        post_names = set(tf.getnames())
        new_names = list(post_names - pre_names)
        new_names.sort()
        new_sources = []

        for np in new_names:
            nn = np[len(archive_path_prefix)+1:]
            info = tf.getmember(np)
            if not info.isdir():
                new_sources.append(nn)

        # Augment SOURCES.txt and add it to the archive
        sources_info = tf.gettarinfo(
            sources_txt_path, arcname=sources_txt_arcname)
        sf = io.BytesIO()
        sf.write(sources_txt)
        if not sources_txt.endswith(b'\n'):
            sf.write(b'\n')
        sf.write(b('\n'.join(new_sources)))
        sources_info.size = len(sf.getvalue())
        sf.seek(0)
        #
        # This adds SOURCES.txt a 2nd time: this effectively overwrites
        # the "earlier" one.
        #
        tf.addfile(sources_info, sf)


def b(buf, encoding="ascii"):
    if isinstance(buf, bytes):
        return buf
    else:
        return buf.encode(encoding)


if __name__ == "__main__":
    main()