comparison _postprocess-sdist.py @ 284:b65d25882e44

REFACTOR: sdist generation: postprocess an sdist to include symbolic links as symbolic links. A standard sdist contains no symlinks but dereferences symlinks encountered in the source tree.
author Franz Glasner <fzglas.hg@dom66.de>
date Sun, 23 Feb 2025 21:27:48 +0100
parents
children 39a19c008708
comparison
equal deleted inserted replaced
283:99b78fa04bc1 284:b65d25882e44
1 # -*- coding: utf-8 -*-
2 """Postprogress a .tar-sdist to include tests/data with symlinks as symlinks
3
4 """
5
6 from __future__ import print_function, absolute_import
7
8 import ast
9 try:
10 from configparser import ConfigParser
11 except ImportError:
12 from ConfigParser import SafeConfigParser as ConfigParser
13 import io
14 import os
15 import tarfile
16
17 import cutils
18 import cutils.util.walk
19
20
21 def main():
22 with io.open("setup.cfg", "rt", encoding="utf-8") as cfgfile:
23 cp = ConfigParser()
24 if hasattr(cp, "read_file"):
25 cp.read_file(cfgfile, "setup.cfg")
26 else:
27 cp.readfp(cfgfile, "setup.cfg")
28 project_name = cp.get("metadata", "name")
29 project_version = cp.get("metadata", "version")
30 if project_version.startswith("attr:"):
31 assert project_version == "attr: cutils.__version__"
32 project_version = ast.literal_eval(repr(cutils.__version__))
33 #
34 # Compressed tar files cannot be modified by Python: make sure the
35 # originally generated archive is uncompressed.
36 #
37 assert cp.get("sdist", "formats") == "tar"
38
39 archive_name = "{}-{}.tar".format(project_name, project_version)
40 archive_path = "dist/" + archive_name
41 assert os.path.isfile(archive_path)
42
43 # the directory within the archive
44 archive_path_prefix = "{}-{}".format(project_name, project_version)
45
46 egg_directory = "{}.egg-info".format(project_name.replace("-", "_"))
47 assert os.path.isdir(egg_directory)
48 sources_txt_path = "{}/SOURCES.txt".format(egg_directory)
49 sources_txt_arcname = "{}/{}/SOURCES.txt".format(
50 archive_path_prefix,
51 egg_directory)
52
53 with tarfile.TarFile(archive_path, "r") as tf:
54 sf = tf.extractfile(sources_txt_arcname)
55 try:
56 sources_txt = sf.read()
57 finally:
58 sf.close()
59
60 with tarfile.TarFile(archive_path, "a") as tf:
61 arcname = "{}/tests/data".format(archive_path_prefix)
62 try:
63 info = tf.getmember(arcname)
64 except KeyError:
65 pass
66 else:
67 raise RuntimeError("already postprocessed")
68 pre_names = set(tf.getnames())
69 tf.add("tests/data", arcname=arcname, recursive=True)
70
71 #
72 # Determine the new files and symlinks that are to be added
73 # to SOURCES.txt. Skip directories.
74 #
75 post_names = set(tf.getnames())
76 new_names = list(post_names - pre_names)
77 new_names.sort()
78 new_sources = []
79
80 for np in new_names:
81 nn = np[len(archive_path_prefix)+1:]
82 info = tf.getmember(np)
83 if not info.isdir():
84 new_sources.append(nn)
85
86 # Augment SOURCES.txt and add it to the archive
87 sources_info = tf.gettarinfo(
88 sources_txt_path, arcname=sources_txt_arcname)
89 sf = io.BytesIO()
90 sf.write(sources_txt)
91 if not sources_txt.endswith(b'\n'):
92 sf.write(b'\n')
93 sf.write(b('\n'.join(new_sources)))
94 sources_info.size = len(sf.getvalue())
95 sf.seek(0)
96 tf.addfile(sources_info, sf)
97
98
99 def b(buf, encoding="ascii"):
100 if isinstance(buf, bytes):
101 return buf
102 else:
103 return buf.encode(encoding)
104
105
106 if __name__ == "__main__":
107 main()