]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | """ |
2 | Create a dist_info directory | |
3 | As defined in the wheel specification | |
4 | """ | |
5 | ||
6 | import os | |
7 | import re | |
8 | import shutil | |
9 | import sys | |
10 | import warnings | |
11 | from contextlib import contextmanager | |
12 | from inspect import cleandoc | |
13 | from pathlib import Path | |
14 | ||
15 | from distutils.core import Command | |
16 | from distutils import log | |
17 | from setuptools.extern import packaging | |
18 | from setuptools._deprecation_warning import SetuptoolsDeprecationWarning | |
19 | ||
20 | ||
21 | class dist_info(Command): | |
22 | ||
23 | description = 'create a .dist-info directory' | |
24 | ||
25 | user_options = [ | |
26 | ('egg-base=', 'e', "directory containing .egg-info directories" | |
27 | " (default: top of the source tree)" | |
28 | " DEPRECATED: use --output-dir."), | |
29 | ('output-dir=', 'o', "directory inside of which the .dist-info will be" | |
30 | "created (default: top of the source tree)"), | |
31 | ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), | |
32 | ('tag-build=', 'b', "Specify explicit tag to add to version number"), | |
33 | ('no-date', 'D', "Don't include date stamp [default]"), | |
34 | ('keep-egg-info', None, "*TRANSITIONAL* will be removed in the future"), | |
35 | ] | |
36 | ||
37 | boolean_options = ['tag-date', 'keep-egg-info'] | |
38 | negative_opt = {'no-date': 'tag-date'} | |
39 | ||
40 | def initialize_options(self): | |
41 | self.egg_base = None | |
42 | self.output_dir = None | |
43 | self.name = None | |
44 | self.dist_info_dir = None | |
45 | self.tag_date = None | |
46 | self.tag_build = None | |
47 | self.keep_egg_info = False | |
48 | ||
49 | def finalize_options(self): | |
50 | if self.egg_base: | |
51 | msg = "--egg-base is deprecated for dist_info command. Use --output-dir." | |
52 | warnings.warn(msg, SetuptoolsDeprecationWarning) | |
53 | self.output_dir = self.egg_base or self.output_dir | |
54 | ||
55 | dist = self.distribution | |
56 | project_dir = dist.src_root or os.curdir | |
57 | self.output_dir = Path(self.output_dir or project_dir) | |
58 | ||
59 | egg_info = self.reinitialize_command("egg_info") | |
60 | egg_info.egg_base = str(self.output_dir) | |
61 | ||
62 | if self.tag_date: | |
63 | egg_info.tag_date = self.tag_date | |
64 | else: | |
65 | self.tag_date = egg_info.tag_date | |
66 | ||
67 | if self.tag_build: | |
68 | egg_info.tag_build = self.tag_build | |
69 | else: | |
70 | self.tag_build = egg_info.tag_build | |
71 | ||
72 | egg_info.finalize_options() | |
73 | self.egg_info = egg_info | |
74 | ||
75 | name = _safe(dist.get_name()) | |
76 | version = _version(dist.get_version()) | |
77 | self.name = f"{name}-{version}" | |
78 | self.dist_info_dir = os.path.join(self.output_dir, f"{self.name}.dist-info") | |
79 | ||
80 | @contextmanager | |
81 | def _maybe_bkp_dir(self, dir_path: str, requires_bkp: bool): | |
82 | if requires_bkp: | |
83 | bkp_name = f"{dir_path}.__bkp__" | |
84 | _rm(bkp_name, ignore_errors=True) | |
85 | _copy(dir_path, bkp_name, dirs_exist_ok=True, symlinks=True) | |
86 | try: | |
87 | yield | |
88 | finally: | |
89 | _rm(dir_path, ignore_errors=True) | |
90 | shutil.move(bkp_name, dir_path) | |
91 | else: | |
92 | yield | |
93 | ||
94 | def run(self): | |
95 | self.output_dir.mkdir(parents=True, exist_ok=True) | |
96 | self.egg_info.run() | |
97 | egg_info_dir = self.egg_info.egg_info | |
98 | assert os.path.isdir(egg_info_dir), ".egg-info dir should have been created" | |
99 | ||
100 | log.info("creating '{}'".format(os.path.abspath(self.dist_info_dir))) | |
101 | bdist_wheel = self.get_finalized_command('bdist_wheel') | |
102 | ||
103 | # TODO: if bdist_wheel if merged into setuptools, just add "keep_egg_info" there | |
104 | with self._maybe_bkp_dir(egg_info_dir, self.keep_egg_info): | |
105 | bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir) | |
106 | ||
107 | ||
108 | def _safe(component: str) -> str: | |
109 | """Escape a component used to form a wheel name according to PEP 491""" | |
110 | return re.sub(r"[^\w\d.]+", "_", component) | |
111 | ||
112 | ||
113 | def _version(version: str) -> str: | |
114 | """Convert an arbitrary string to a version string.""" | |
115 | v = version.replace(' ', '.') | |
116 | try: | |
117 | return str(packaging.version.Version(v)).replace("-", "_") | |
118 | except packaging.version.InvalidVersion: | |
119 | msg = f"""Invalid version: {version!r}. | |
120 | !!\n\n | |
121 | ################### | |
122 | # Invalid version # | |
123 | ################### | |
124 | {version!r} is not valid according to PEP 440.\n | |
125 | Please make sure specify a valid version for your package. | |
126 | Also note that future releases of setuptools may halt the build process | |
127 | if an invalid version is given. | |
128 | \n\n!! | |
129 | """ | |
130 | warnings.warn(cleandoc(msg)) | |
131 | return _safe(v).strip("_") | |
132 | ||
133 | ||
134 | def _rm(dir_name, **opts): | |
135 | if os.path.isdir(dir_name): | |
136 | shutil.rmtree(dir_name, **opts) | |
137 | ||
138 | ||
139 | def _copy(src, dst, **opts): | |
140 | if sys.version_info < (3, 8): | |
141 | opts.pop("dirs_exist_ok", None) | |
142 | shutil.copytree(src, dst, **opts) |