diff options
author | Ny Antra Ranaivoarison <nyantra.ranaivoarison@smile.fr> | 2023-12-21 10:41:00 +0100 |
---|---|---|
committer | Khem Raj <raj.khem@gmail.com> | 2023-12-21 08:20:34 -0800 |
commit | a50c63e2b7d78687aae48f25eec88442c4ecddfa (patch) | |
tree | 3d3ca5e95bb661f0069e9a7faf8f1a924a6d8c4c /meta-python/recipes-devtools/python/python3-click-spinner/0001-Update-Versioneer-to-0.22.patch | |
parent | fc035a8c4dc84e7414a5a06a0949c514fdc69531 (diff) | |
download | meta-openembedded-a50c63e2b7d78687aae48f25eec88442c4ecddfa.tar.gz |
python3-click-spinner: backport patch that fixes deprecated methods
This allows build with python 3.12.
Signed-off-by: Ny Antra Ranaivoarison <nyantra.ranaivoarison@smile.fr>
Signed-off-by: Yoann Congal <yoann.congal@smile.fr>
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Diffstat (limited to 'meta-python/recipes-devtools/python/python3-click-spinner/0001-Update-Versioneer-to-0.22.patch')
-rw-r--r-- | meta-python/recipes-devtools/python/python3-click-spinner/0001-Update-Versioneer-to-0.22.patch | 2489 |
1 files changed, 2489 insertions, 0 deletions
diff --git a/meta-python/recipes-devtools/python/python3-click-spinner/0001-Update-Versioneer-to-0.22.patch b/meta-python/recipes-devtools/python/python3-click-spinner/0001-Update-Versioneer-to-0.22.patch new file mode 100644 index 0000000000..4edb5da9ef --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-click-spinner/0001-Update-Versioneer-to-0.22.patch | |||
@@ -0,0 +1,2489 @@ | |||
1 | From 739f9da6bf0d2d9f0de624aee2ec71c65f62c275 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hugo van Kemenade <hugovk@users.noreply.github.com> | ||
3 | Date: Tue, 10 May 2022 18:17:50 +0300 | ||
4 | Subject: [PATCH] Update Versioneer to 0.22 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/click-contrib/click-spinner/commit/5622ab0a0b4296dc8f10863f268ed98dccf4b642] | ||
7 | |||
8 | Signed-off-by: Ny Antra Ranaivoarison <nyantra.ranaivoarison@smile.fr> | ||
9 | --- | ||
10 | click_spinner/__init__.py | 5 +- | ||
11 | click_spinner/_version.py | 665 +++++++++++++++++++++- | ||
12 | versioneer.py | 1128 ++++++++++++++++++++++++------------- | ||
13 | 3 files changed, 1400 insertions(+), 398 deletions(-) | ||
14 | |||
15 | diff --git a/click_spinner/__init__.py b/click_spinner/__init__.py | ||
16 | index aeec089..8e9f4f9 100644 | ||
17 | --- a/click_spinner/__init__.py | ||
18 | +++ b/click_spinner/__init__.py | ||
19 | @@ -77,6 +77,5 @@ def spinner(beep=False, disable=False, force=False, stream=sys.stdout): | ||
20 | return Spinner(beep, disable, force, stream) | ||
21 | |||
22 | |||
23 | -from ._version import get_versions | ||
24 | -__version__ = get_versions()['version'] | ||
25 | -del get_versions | ||
26 | +from . import _version | ||
27 | +__version__ = _version.get_versions()['version'] | ||
28 | diff --git a/click_spinner/_version.py b/click_spinner/_version.py | ||
29 | index 5ae340e..d44565d 100644 | ||
30 | --- a/click_spinner/_version.py | ||
31 | +++ b/click_spinner/_version.py | ||
32 | @@ -1,21 +1,658 @@ | ||
33 | |||
34 | -# This file was generated by 'versioneer.py' (0.16) from | ||
35 | -# revision-control system data, or from the parent directory name of an | ||
36 | -# unpacked source archive. Distribution tarballs contain a pre-generated copy | ||
37 | -# of this file. | ||
38 | +# This file helps to compute a version number in source trees obtained from | ||
39 | +# git-archive tarball (such as those provided by githubs download-from-tag | ||
40 | +# feature). Distribution tarballs (built by setup.py sdist) and build | ||
41 | +# directories (produced by setup.py build) will contain a much shorter file | ||
42 | +# that just contains the computed version number. | ||
43 | |||
44 | -import json | ||
45 | +# This file is released into the public domain. Generated by | ||
46 | +# versioneer-0.22 (https://github.com/python-versioneer/python-versioneer) | ||
47 | + | ||
48 | +"""Git implementation of _version.py.""" | ||
49 | + | ||
50 | +import errno | ||
51 | +import os | ||
52 | +import re | ||
53 | +import subprocess | ||
54 | import sys | ||
55 | +from typing import Callable, Dict | ||
56 | +import functools | ||
57 | + | ||
58 | + | ||
59 | +def get_keywords(): | ||
60 | + """Get the keywords needed to look up the version information.""" | ||
61 | + # these strings will be replaced by git during git-archive. | ||
62 | + # setup.py/versioneer.py will grep for the variable names, so they must | ||
63 | + # each be defined on a line of their own. _version.py will just call | ||
64 | + # get_keywords(). | ||
65 | + git_refnames = "$Format:%d$" | ||
66 | + git_full = "$Format:%H$" | ||
67 | + git_date = "$Format:%ci$" | ||
68 | + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} | ||
69 | + return keywords | ||
70 | + | ||
71 | + | ||
72 | +class VersioneerConfig: | ||
73 | + """Container for Versioneer configuration parameters.""" | ||
74 | + | ||
75 | + | ||
76 | +def get_config(): | ||
77 | + """Create, populate and return the VersioneerConfig() object.""" | ||
78 | + # these strings are filled in when 'setup.py versioneer' creates | ||
79 | + # _version.py | ||
80 | + cfg = VersioneerConfig() | ||
81 | + cfg.VCS = "git" | ||
82 | + cfg.style = "pep440" | ||
83 | + cfg.tag_prefix = "v" | ||
84 | + cfg.parentdir_prefix = "click-spinner-" | ||
85 | + cfg.versionfile_source = "click_spinner/_version.py" | ||
86 | + cfg.verbose = False | ||
87 | + return cfg | ||
88 | + | ||
89 | + | ||
90 | +class NotThisMethod(Exception): | ||
91 | + """Exception raised if a method is not valid for the current scenario.""" | ||
92 | + | ||
93 | + | ||
94 | +LONG_VERSION_PY: Dict[str, str] = {} | ||
95 | +HANDLERS: Dict[str, Dict[str, Callable]] = {} | ||
96 | + | ||
97 | + | ||
98 | +def register_vcs_handler(vcs, method): # decorator | ||
99 | + """Create decorator to mark a method as the handler of a VCS.""" | ||
100 | + def decorate(f): | ||
101 | + """Store f in HANDLERS[vcs][method].""" | ||
102 | + if vcs not in HANDLERS: | ||
103 | + HANDLERS[vcs] = {} | ||
104 | + HANDLERS[vcs][method] = f | ||
105 | + return f | ||
106 | + return decorate | ||
107 | + | ||
108 | + | ||
109 | +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, | ||
110 | + env=None): | ||
111 | + """Call the given command(s).""" | ||
112 | + assert isinstance(commands, list) | ||
113 | + process = None | ||
114 | + | ||
115 | + popen_kwargs = {} | ||
116 | + if sys.platform == "win32": | ||
117 | + # This hides the console window if pythonw.exe is used | ||
118 | + startupinfo = subprocess.STARTUPINFO() | ||
119 | + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW | ||
120 | + popen_kwargs["startupinfo"] = startupinfo | ||
121 | + | ||
122 | + for command in commands: | ||
123 | + try: | ||
124 | + dispcmd = str([command] + args) | ||
125 | + # remember shell=False, so use git.cmd on windows, not just git | ||
126 | + process = subprocess.Popen([command] + args, cwd=cwd, env=env, | ||
127 | + stdout=subprocess.PIPE, | ||
128 | + stderr=(subprocess.PIPE if hide_stderr | ||
129 | + else None), **popen_kwargs) | ||
130 | + break | ||
131 | + except OSError: | ||
132 | + e = sys.exc_info()[1] | ||
133 | + if e.errno == errno.ENOENT: | ||
134 | + continue | ||
135 | + if verbose: | ||
136 | + print("unable to run %s" % dispcmd) | ||
137 | + print(e) | ||
138 | + return None, None | ||
139 | + else: | ||
140 | + if verbose: | ||
141 | + print("unable to find command, tried %s" % (commands,)) | ||
142 | + return None, None | ||
143 | + stdout = process.communicate()[0].strip().decode() | ||
144 | + if process.returncode != 0: | ||
145 | + if verbose: | ||
146 | + print("unable to run %s (error)" % dispcmd) | ||
147 | + print("stdout was %s" % stdout) | ||
148 | + return None, process.returncode | ||
149 | + return stdout, process.returncode | ||
150 | + | ||
151 | + | ||
152 | +def versions_from_parentdir(parentdir_prefix, root, verbose): | ||
153 | + """Try to determine the version from the parent directory name. | ||
154 | + | ||
155 | + Source tarballs conventionally unpack into a directory that includes both | ||
156 | + the project name and a version string. We will also support searching up | ||
157 | + two directory levels for an appropriately named parent directory | ||
158 | + """ | ||
159 | + rootdirs = [] | ||
160 | + | ||
161 | + for _ in range(3): | ||
162 | + dirname = os.path.basename(root) | ||
163 | + if dirname.startswith(parentdir_prefix): | ||
164 | + return {"version": dirname[len(parentdir_prefix):], | ||
165 | + "full-revisionid": None, | ||
166 | + "dirty": False, "error": None, "date": None} | ||
167 | + rootdirs.append(root) | ||
168 | + root = os.path.dirname(root) # up a level | ||
169 | + | ||
170 | + if verbose: | ||
171 | + print("Tried directories %s but none started with prefix %s" % | ||
172 | + (str(rootdirs), parentdir_prefix)) | ||
173 | + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") | ||
174 | + | ||
175 | + | ||
176 | +@register_vcs_handler("git", "get_keywords") | ||
177 | +def git_get_keywords(versionfile_abs): | ||
178 | + """Extract version information from the given file.""" | ||
179 | + # the code embedded in _version.py can just fetch the value of these | ||
180 | + # keywords. When used from setup.py, we don't want to import _version.py, | ||
181 | + # so we do it with a regexp instead. This function is not used from | ||
182 | + # _version.py. | ||
183 | + keywords = {} | ||
184 | + try: | ||
185 | + with open(versionfile_abs, "r") as fobj: | ||
186 | + for line in fobj: | ||
187 | + if line.strip().startswith("git_refnames ="): | ||
188 | + mo = re.search(r'=\s*"(.*)"', line) | ||
189 | + if mo: | ||
190 | + keywords["refnames"] = mo.group(1) | ||
191 | + if line.strip().startswith("git_full ="): | ||
192 | + mo = re.search(r'=\s*"(.*)"', line) | ||
193 | + if mo: | ||
194 | + keywords["full"] = mo.group(1) | ||
195 | + if line.strip().startswith("git_date ="): | ||
196 | + mo = re.search(r'=\s*"(.*)"', line) | ||
197 | + if mo: | ||
198 | + keywords["date"] = mo.group(1) | ||
199 | + except OSError: | ||
200 | + pass | ||
201 | + return keywords | ||
202 | + | ||
203 | + | ||
204 | +@register_vcs_handler("git", "keywords") | ||
205 | +def git_versions_from_keywords(keywords, tag_prefix, verbose): | ||
206 | + """Get version information from git keywords.""" | ||
207 | + if "refnames" not in keywords: | ||
208 | + raise NotThisMethod("Short version file found") | ||
209 | + date = keywords.get("date") | ||
210 | + if date is not None: | ||
211 | + # Use only the last line. Previous lines may contain GPG signature | ||
212 | + # information. | ||
213 | + date = date.splitlines()[-1] | ||
214 | + | ||
215 | + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant | ||
216 | + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 | ||
217 | + # -like" string, which we must then edit to make compliant), because | ||
218 | + # it's been around since git-1.5.3, and it's too difficult to | ||
219 | + # discover which version we're using, or to work around using an | ||
220 | + # older one. | ||
221 | + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) | ||
222 | + refnames = keywords["refnames"].strip() | ||
223 | + if refnames.startswith("$Format"): | ||
224 | + if verbose: | ||
225 | + print("keywords are unexpanded, not using") | ||
226 | + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") | ||
227 | + refs = {r.strip() for r in refnames.strip("()").split(",")} | ||
228 | + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of | ||
229 | + # just "foo-1.0". If we see a "tag: " prefix, prefer those. | ||
230 | + TAG = "tag: " | ||
231 | + tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} | ||
232 | + if not tags: | ||
233 | + # Either we're using git < 1.8.3, or there really are no tags. We use | ||
234 | + # a heuristic: assume all version tags have a digit. The old git %d | ||
235 | + # expansion behaves like git log --decorate=short and strips out the | ||
236 | + # refs/heads/ and refs/tags/ prefixes that would let us distinguish | ||
237 | + # between branches and tags. By ignoring refnames without digits, we | ||
238 | + # filter out many common branch names like "release" and | ||
239 | + # "stabilization", as well as "HEAD" and "master". | ||
240 | + tags = {r for r in refs if re.search(r'\d', r)} | ||
241 | + if verbose: | ||
242 | + print("discarding '%s', no digits" % ",".join(refs - tags)) | ||
243 | + if verbose: | ||
244 | + print("likely tags: %s" % ",".join(sorted(tags))) | ||
245 | + for ref in sorted(tags): | ||
246 | + # sorting will prefer e.g. "2.0" over "2.0rc1" | ||
247 | + if ref.startswith(tag_prefix): | ||
248 | + r = ref[len(tag_prefix):] | ||
249 | + # Filter out refs that exactly match prefix or that don't start | ||
250 | + # with a number once the prefix is stripped (mostly a concern | ||
251 | + # when prefix is '') | ||
252 | + if not re.match(r'\d', r): | ||
253 | + continue | ||
254 | + if verbose: | ||
255 | + print("picking %s" % r) | ||
256 | + return {"version": r, | ||
257 | + "full-revisionid": keywords["full"].strip(), | ||
258 | + "dirty": False, "error": None, | ||
259 | + "date": date} | ||
260 | + # no suitable tags, so version is "0+unknown", but full hex is still there | ||
261 | + if verbose: | ||
262 | + print("no suitable tags, using unknown + full revision id") | ||
263 | + return {"version": "0+unknown", | ||
264 | + "full-revisionid": keywords["full"].strip(), | ||
265 | + "dirty": False, "error": "no suitable tags", "date": None} | ||
266 | + | ||
267 | + | ||
268 | +@register_vcs_handler("git", "pieces_from_vcs") | ||
269 | +def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): | ||
270 | + """Get version from 'git describe' in the root of the source tree. | ||
271 | + | ||
272 | + This only gets called if the git-archive 'subst' keywords were *not* | ||
273 | + expanded, and _version.py hasn't already been rewritten with a short | ||
274 | + version string, meaning we're inside a checked out source tree. | ||
275 | + """ | ||
276 | + GITS = ["git"] | ||
277 | + if sys.platform == "win32": | ||
278 | + GITS = ["git.cmd", "git.exe"] | ||
279 | + | ||
280 | + # GIT_DIR can interfere with correct operation of Versioneer. | ||
281 | + # It may be intended to be passed to the Versioneer-versioned project, | ||
282 | + # but that should not change where we get our version from. | ||
283 | + env = os.environ.copy() | ||
284 | + env.pop("GIT_DIR", None) | ||
285 | + runner = functools.partial(runner, env=env) | ||
286 | + | ||
287 | + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, | ||
288 | + hide_stderr=True) | ||
289 | + if rc != 0: | ||
290 | + if verbose: | ||
291 | + print("Directory %s not under git control" % root) | ||
292 | + raise NotThisMethod("'git rev-parse --git-dir' returned error") | ||
293 | + | ||
294 | + MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else [] | ||
295 | + | ||
296 | + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] | ||
297 | + # if there isn't one, this yields HEX[-dirty] (no NUM) | ||
298 | + describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", | ||
299 | + "--always", "--long", *MATCH_ARGS], | ||
300 | + cwd=root) | ||
301 | + # --long was added in git-1.5.5 | ||
302 | + if describe_out is None: | ||
303 | + raise NotThisMethod("'git describe' failed") | ||
304 | + describe_out = describe_out.strip() | ||
305 | + full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) | ||
306 | + if full_out is None: | ||
307 | + raise NotThisMethod("'git rev-parse' failed") | ||
308 | + full_out = full_out.strip() | ||
309 | + | ||
310 | + pieces = {} | ||
311 | + pieces["long"] = full_out | ||
312 | + pieces["short"] = full_out[:7] # maybe improved later | ||
313 | + pieces["error"] = None | ||
314 | + | ||
315 | + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], | ||
316 | + cwd=root) | ||
317 | + # --abbrev-ref was added in git-1.6.3 | ||
318 | + if rc != 0 or branch_name is None: | ||
319 | + raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") | ||
320 | + branch_name = branch_name.strip() | ||
321 | + | ||
322 | + if branch_name == "HEAD": | ||
323 | + # If we aren't exactly on a branch, pick a branch which represents | ||
324 | + # the current commit. If all else fails, we are on a branchless | ||
325 | + # commit. | ||
326 | + branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) | ||
327 | + # --contains was added in git-1.5.4 | ||
328 | + if rc != 0 or branches is None: | ||
329 | + raise NotThisMethod("'git branch --contains' returned error") | ||
330 | + branches = branches.split("\n") | ||
331 | + | ||
332 | + # Remove the first line if we're running detached | ||
333 | + if "(" in branches[0]: | ||
334 | + branches.pop(0) | ||
335 | + | ||
336 | + # Strip off the leading "* " from the list of branches. | ||
337 | + branches = [branch[2:] for branch in branches] | ||
338 | + if "master" in branches: | ||
339 | + branch_name = "master" | ||
340 | + elif not branches: | ||
341 | + branch_name = None | ||
342 | + else: | ||
343 | + # Pick the first branch that is returned. Good or bad. | ||
344 | + branch_name = branches[0] | ||
345 | + | ||
346 | + pieces["branch"] = branch_name | ||
347 | + | ||
348 | + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] | ||
349 | + # TAG might have hyphens. | ||
350 | + git_describe = describe_out | ||
351 | + | ||
352 | + # look for -dirty suffix | ||
353 | + dirty = git_describe.endswith("-dirty") | ||
354 | + pieces["dirty"] = dirty | ||
355 | + if dirty: | ||
356 | + git_describe = git_describe[:git_describe.rindex("-dirty")] | ||
357 | + | ||
358 | + # now we have TAG-NUM-gHEX or HEX | ||
359 | + | ||
360 | + if "-" in git_describe: | ||
361 | + # TAG-NUM-gHEX | ||
362 | + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) | ||
363 | + if not mo: | ||
364 | + # unparsable. Maybe git-describe is misbehaving? | ||
365 | + pieces["error"] = ("unable to parse git-describe output: '%s'" | ||
366 | + % describe_out) | ||
367 | + return pieces | ||
368 | + | ||
369 | + # tag | ||
370 | + full_tag = mo.group(1) | ||
371 | + if not full_tag.startswith(tag_prefix): | ||
372 | + if verbose: | ||
373 | + fmt = "tag '%s' doesn't start with prefix '%s'" | ||
374 | + print(fmt % (full_tag, tag_prefix)) | ||
375 | + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" | ||
376 | + % (full_tag, tag_prefix)) | ||
377 | + return pieces | ||
378 | + pieces["closest-tag"] = full_tag[len(tag_prefix):] | ||
379 | + | ||
380 | + # distance: number of commits since tag | ||
381 | + pieces["distance"] = int(mo.group(2)) | ||
382 | + | ||
383 | + # commit: short hex revision ID | ||
384 | + pieces["short"] = mo.group(3) | ||
385 | + | ||
386 | + else: | ||
387 | + # HEX: no tags | ||
388 | + pieces["closest-tag"] = None | ||
389 | + count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) | ||
390 | + pieces["distance"] = int(count_out) # total number of commits | ||
391 | |||
392 | -version_json = ''' | ||
393 | -{ | ||
394 | - "dirty": false, | ||
395 | - "error": null, | ||
396 | - "full-revisionid": "7cadb31e3e257c64a47a67255547f0a746e1a465", | ||
397 | - "version": "0.1.10" | ||
398 | -} | ||
399 | -''' # END VERSION_JSON | ||
400 | + # commit date: see ISO-8601 comment in git_versions_from_keywords() | ||
401 | + date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() | ||
402 | + # Use only the last line. Previous lines may contain GPG signature | ||
403 | + # information. | ||
404 | + date = date.splitlines()[-1] | ||
405 | + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) | ||
406 | + | ||
407 | + return pieces | ||
408 | + | ||
409 | + | ||
410 | +def plus_or_dot(pieces): | ||
411 | + """Return a + if we don't already have one, else return a .""" | ||
412 | + if "+" in pieces.get("closest-tag", ""): | ||
413 | + return "." | ||
414 | + return "+" | ||
415 | + | ||
416 | + | ||
417 | +def render_pep440(pieces): | ||
418 | + """Build up version string, with post-release "local version identifier". | ||
419 | + | ||
420 | + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you | ||
421 | + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty | ||
422 | + | ||
423 | + Exceptions: | ||
424 | + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] | ||
425 | + """ | ||
426 | + if pieces["closest-tag"]: | ||
427 | + rendered = pieces["closest-tag"] | ||
428 | + if pieces["distance"] or pieces["dirty"]: | ||
429 | + rendered += plus_or_dot(pieces) | ||
430 | + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) | ||
431 | + if pieces["dirty"]: | ||
432 | + rendered += ".dirty" | ||
433 | + else: | ||
434 | + # exception #1 | ||
435 | + rendered = "0+untagged.%d.g%s" % (pieces["distance"], | ||
436 | + pieces["short"]) | ||
437 | + if pieces["dirty"]: | ||
438 | + rendered += ".dirty" | ||
439 | + return rendered | ||
440 | + | ||
441 | + | ||
442 | +def render_pep440_branch(pieces): | ||
443 | + """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . | ||
444 | + | ||
445 | + The ".dev0" means not master branch. Note that .dev0 sorts backwards | ||
446 | + (a feature branch will appear "older" than the master branch). | ||
447 | + | ||
448 | + Exceptions: | ||
449 | + 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] | ||
450 | + """ | ||
451 | + if pieces["closest-tag"]: | ||
452 | + rendered = pieces["closest-tag"] | ||
453 | + if pieces["distance"] or pieces["dirty"]: | ||
454 | + if pieces["branch"] != "master": | ||
455 | + rendered += ".dev0" | ||
456 | + rendered += plus_or_dot(pieces) | ||
457 | + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) | ||
458 | + if pieces["dirty"]: | ||
459 | + rendered += ".dirty" | ||
460 | + else: | ||
461 | + # exception #1 | ||
462 | + rendered = "0" | ||
463 | + if pieces["branch"] != "master": | ||
464 | + rendered += ".dev0" | ||
465 | + rendered += "+untagged.%d.g%s" % (pieces["distance"], | ||
466 | + pieces["short"]) | ||
467 | + if pieces["dirty"]: | ||
468 | + rendered += ".dirty" | ||
469 | + return rendered | ||
470 | + | ||
471 | + | ||
472 | +def pep440_split_post(ver): | ||
473 | + """Split pep440 version string at the post-release segment. | ||
474 | + | ||
475 | + Returns the release segments before the post-release and the | ||
476 | + post-release version number (or -1 if no post-release segment is present). | ||
477 | + """ | ||
478 | + vc = str.split(ver, ".post") | ||
479 | + return vc[0], int(vc[1] or 0) if len(vc) == 2 else None | ||
480 | + | ||
481 | + | ||
482 | +def render_pep440_pre(pieces): | ||
483 | + """TAG[.postN.devDISTANCE] -- No -dirty. | ||
484 | + | ||
485 | + Exceptions: | ||
486 | + 1: no tags. 0.post0.devDISTANCE | ||
487 | + """ | ||
488 | + if pieces["closest-tag"]: | ||
489 | + if pieces["distance"]: | ||
490 | + # update the post release segment | ||
491 | + tag_version, post_version = pep440_split_post(pieces["closest-tag"]) | ||
492 | + rendered = tag_version | ||
493 | + if post_version is not None: | ||
494 | + rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"]) | ||
495 | + else: | ||
496 | + rendered += ".post0.dev%d" % (pieces["distance"]) | ||
497 | + else: | ||
498 | + # no commits, use the tag as the version | ||
499 | + rendered = pieces["closest-tag"] | ||
500 | + else: | ||
501 | + # exception #1 | ||
502 | + rendered = "0.post0.dev%d" % pieces["distance"] | ||
503 | + return rendered | ||
504 | + | ||
505 | + | ||
506 | +def render_pep440_post(pieces): | ||
507 | + """TAG[.postDISTANCE[.dev0]+gHEX] . | ||
508 | + | ||
509 | + The ".dev0" means dirty. Note that .dev0 sorts backwards | ||
510 | + (a dirty tree will appear "older" than the corresponding clean one), | ||
511 | + but you shouldn't be releasing software with -dirty anyways. | ||
512 | + | ||
513 | + Exceptions: | ||
514 | + 1: no tags. 0.postDISTANCE[.dev0] | ||
515 | + """ | ||
516 | + if pieces["closest-tag"]: | ||
517 | + rendered = pieces["closest-tag"] | ||
518 | + if pieces["distance"] or pieces["dirty"]: | ||
519 | + rendered += ".post%d" % pieces["distance"] | ||
520 | + if pieces["dirty"]: | ||
521 | + rendered += ".dev0" | ||
522 | + rendered += plus_or_dot(pieces) | ||
523 | + rendered += "g%s" % pieces["short"] | ||
524 | + else: | ||
525 | + # exception #1 | ||
526 | + rendered = "0.post%d" % pieces["distance"] | ||
527 | + if pieces["dirty"]: | ||
528 | + rendered += ".dev0" | ||
529 | + rendered += "+g%s" % pieces["short"] | ||
530 | + return rendered | ||
531 | + | ||
532 | + | ||
533 | +def render_pep440_post_branch(pieces): | ||
534 | + """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . | ||
535 | + | ||
536 | + The ".dev0" means not master branch. | ||
537 | + | ||
538 | + Exceptions: | ||
539 | + 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] | ||
540 | + """ | ||
541 | + if pieces["closest-tag"]: | ||
542 | + rendered = pieces["closest-tag"] | ||
543 | + if pieces["distance"] or pieces["dirty"]: | ||
544 | + rendered += ".post%d" % pieces["distance"] | ||
545 | + if pieces["branch"] != "master": | ||
546 | + rendered += ".dev0" | ||
547 | + rendered += plus_or_dot(pieces) | ||
548 | + rendered += "g%s" % pieces["short"] | ||
549 | + if pieces["dirty"]: | ||
550 | + rendered += ".dirty" | ||
551 | + else: | ||
552 | + # exception #1 | ||
553 | + rendered = "0.post%d" % pieces["distance"] | ||
554 | + if pieces["branch"] != "master": | ||
555 | + rendered += ".dev0" | ||
556 | + rendered += "+g%s" % pieces["short"] | ||
557 | + if pieces["dirty"]: | ||
558 | + rendered += ".dirty" | ||
559 | + return rendered | ||
560 | + | ||
561 | + | ||
562 | +def render_pep440_old(pieces): | ||
563 | + """TAG[.postDISTANCE[.dev0]] . | ||
564 | + | ||
565 | + The ".dev0" means dirty. | ||
566 | + | ||
567 | + Exceptions: | ||
568 | + 1: no tags. 0.postDISTANCE[.dev0] | ||
569 | + """ | ||
570 | + if pieces["closest-tag"]: | ||
571 | + rendered = pieces["closest-tag"] | ||
572 | + if pieces["distance"] or pieces["dirty"]: | ||
573 | + rendered += ".post%d" % pieces["distance"] | ||
574 | + if pieces["dirty"]: | ||
575 | + rendered += ".dev0" | ||
576 | + else: | ||
577 | + # exception #1 | ||
578 | + rendered = "0.post%d" % pieces["distance"] | ||
579 | + if pieces["dirty"]: | ||
580 | + rendered += ".dev0" | ||
581 | + return rendered | ||
582 | + | ||
583 | + | ||
584 | +def render_git_describe(pieces): | ||
585 | + """TAG[-DISTANCE-gHEX][-dirty]. | ||
586 | + | ||
587 | + Like 'git describe --tags --dirty --always'. | ||
588 | + | ||
589 | + Exceptions: | ||
590 | + 1: no tags. HEX[-dirty] (note: no 'g' prefix) | ||
591 | + """ | ||
592 | + if pieces["closest-tag"]: | ||
593 | + rendered = pieces["closest-tag"] | ||
594 | + if pieces["distance"]: | ||
595 | + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) | ||
596 | + else: | ||
597 | + # exception #1 | ||
598 | + rendered = pieces["short"] | ||
599 | + if pieces["dirty"]: | ||
600 | + rendered += "-dirty" | ||
601 | + return rendered | ||
602 | + | ||
603 | + | ||
604 | +def render_git_describe_long(pieces): | ||
605 | + """TAG-DISTANCE-gHEX[-dirty]. | ||
606 | + | ||
607 | + Like 'git describe --tags --dirty --always -long'. | ||
608 | + The distance/hash is unconditional. | ||
609 | + | ||
610 | + Exceptions: | ||
611 | + 1: no tags. HEX[-dirty] (note: no 'g' prefix) | ||
612 | + """ | ||
613 | + if pieces["closest-tag"]: | ||
614 | + rendered = pieces["closest-tag"] | ||
615 | + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) | ||
616 | + else: | ||
617 | + # exception #1 | ||
618 | + rendered = pieces["short"] | ||
619 | + if pieces["dirty"]: | ||
620 | + rendered += "-dirty" | ||
621 | + return rendered | ||
622 | + | ||
623 | + | ||
624 | +def render(pieces, style): | ||
625 | + """Render the given version pieces into the requested style.""" | ||
626 | + if pieces["error"]: | ||
627 | + return {"version": "unknown", | ||
628 | + "full-revisionid": pieces.get("long"), | ||
629 | + "dirty": None, | ||
630 | + "error": pieces["error"], | ||
631 | + "date": None} | ||
632 | + | ||
633 | + if not style or style == "default": | ||
634 | + style = "pep440" # the default | ||
635 | + | ||
636 | + if style == "pep440": | ||
637 | + rendered = render_pep440(pieces) | ||
638 | + elif style == "pep440-branch": | ||
639 | + rendered = render_pep440_branch(pieces) | ||
640 | + elif style == "pep440-pre": | ||
641 | + rendered = render_pep440_pre(pieces) | ||
642 | + elif style == "pep440-post": | ||
643 | + rendered = render_pep440_post(pieces) | ||
644 | + elif style == "pep440-post-branch": | ||
645 | + rendered = render_pep440_post_branch(pieces) | ||
646 | + elif style == "pep440-old": | ||
647 | + rendered = render_pep440_old(pieces) | ||
648 | + elif style == "git-describe": | ||
649 | + rendered = render_git_describe(pieces) | ||
650 | + elif style == "git-describe-long": | ||
651 | + rendered = render_git_describe_long(pieces) | ||
652 | + else: | ||
653 | + raise ValueError("unknown style '%s'" % style) | ||
654 | + | ||
655 | + return {"version": rendered, "full-revisionid": pieces["long"], | ||
656 | + "dirty": pieces["dirty"], "error": None, | ||
657 | + "date": pieces.get("date")} | ||
658 | |||
659 | |||
660 | def get_versions(): | ||
661 | - return json.loads(version_json) | ||
662 | + """Get version information or return default if unable to do so.""" | ||
663 | + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have | ||
664 | + # __file__, we can work backwards from there to the root. Some | ||
665 | + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which | ||
666 | + # case we can only use expanded keywords. | ||
667 | + | ||
668 | + cfg = get_config() | ||
669 | + verbose = cfg.verbose | ||
670 | + | ||
671 | + try: | ||
672 | + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, | ||
673 | + verbose) | ||
674 | + except NotThisMethod: | ||
675 | + pass | ||
676 | + | ||
677 | + try: | ||
678 | + root = os.path.realpath(__file__) | ||
679 | + # versionfile_source is the relative path from the top of the source | ||
680 | + # tree (where the .git directory might live) to this file. Invert | ||
681 | + # this to find the root from __file__. | ||
682 | + for _ in cfg.versionfile_source.split('/'): | ||
683 | + root = os.path.dirname(root) | ||
684 | + except NameError: | ||
685 | + return {"version": "0+unknown", "full-revisionid": None, | ||
686 | + "dirty": None, | ||
687 | + "error": "unable to find root of source tree", | ||
688 | + "date": None} | ||
689 | + | ||
690 | + try: | ||
691 | + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) | ||
692 | + return render(pieces, cfg.style) | ||
693 | + except NotThisMethod: | ||
694 | + pass | ||
695 | + | ||
696 | + try: | ||
697 | + if cfg.parentdir_prefix: | ||
698 | + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) | ||
699 | + except NotThisMethod: | ||
700 | + pass | ||
701 | + | ||
702 | + return {"version": "0+unknown", "full-revisionid": None, | ||
703 | + "dirty": None, | ||
704 | + "error": "unable to compute version", "date": None} | ||
705 | diff --git a/versioneer.py b/versioneer.py | ||
706 | index 7ed2a21..a142bf5 100644 | ||
707 | --- a/versioneer.py | ||
708 | +++ b/versioneer.py | ||
709 | @@ -1,5 +1,5 @@ | ||
710 | |||
711 | -# Version: 0.16 | ||
712 | +# Version: 0.22 | ||
713 | |||
714 | """The Versioneer - like a rocketeer, but for versions. | ||
715 | |||
716 | @@ -7,18 +7,14 @@ The Versioneer | ||
717 | ============== | ||
718 | |||
719 | * like a rocketeer, but for versions! | ||
720 | -* https://github.com/warner/python-versioneer | ||
721 | +* https://github.com/python-versioneer/python-versioneer | ||
722 | * Brian Warner | ||
723 | * License: Public Domain | ||
724 | -* Compatible With: python2.6, 2.7, 3.3, 3.4, 3.5, and pypy | ||
725 | -* [![Latest Version] | ||
726 | -(https://pypip.in/version/versioneer/badge.svg?style=flat) | ||
727 | -](https://pypi.python.org/pypi/versioneer/) | ||
728 | -* [![Build Status] | ||
729 | -(https://travis-ci.org/warner/python-versioneer.png?branch=master) | ||
730 | -](https://travis-ci.org/warner/python-versioneer) | ||
731 | - | ||
732 | -This is a tool for managing a recorded version number in distutils-based | ||
733 | +* Compatible with: Python 3.6, 3.7, 3.8, 3.9, 3.10 and pypy3 | ||
734 | +* [![Latest Version][pypi-image]][pypi-url] | ||
735 | +* [![Build Status][travis-image]][travis-url] | ||
736 | + | ||
737 | +This is a tool for managing a recorded version number in distutils/setuptools-based | ||
738 | python projects. The goal is to remove the tedious and error-prone "update | ||
739 | the embedded version string" step from your release process. Making a new | ||
740 | release should be as easy as recording a new tag in your version-control | ||
741 | @@ -27,9 +23,10 @@ system, and maybe making new tarballs. | ||
742 | |||
743 | ## Quick Install | ||
744 | |||
745 | -* `pip install versioneer` to somewhere to your $PATH | ||
746 | -* add a `[versioneer]` section to your setup.cfg (see below) | ||
747 | +* `pip install versioneer` to somewhere in your $PATH | ||
748 | +* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md)) | ||
749 | * run `versioneer install` in your source tree, commit the results | ||
750 | +* Verify version information with `python setup.py version` | ||
751 | |||
752 | ## Version Identifiers | ||
753 | |||
754 | @@ -61,7 +58,7 @@ version 1.3). Many VCS systems can report a description that captures this, | ||
755 | for example `git describe --tags --dirty --always` reports things like | ||
756 | "0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the | ||
757 | 0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has | ||
758 | -uncommitted changes. | ||
759 | +uncommitted changes). | ||
760 | |||
761 | The version identifier is used for multiple purposes: | ||
762 | |||
763 | @@ -88,127 +85,7 @@ the generated version data. | ||
764 | |||
765 | ## Installation | ||
766 | |||
767 | -First, decide on values for the following configuration variables: | ||
768 | - | ||
769 | -* `VCS`: the version control system you use. Currently accepts "git". | ||
770 | - | ||
771 | -* `style`: the style of version string to be produced. See "Styles" below for | ||
772 | - details. Defaults to "pep440", which looks like | ||
773 | - `TAG[+DISTANCE.gSHORTHASH[.dirty]]`. | ||
774 | - | ||
775 | -* `versionfile_source`: | ||
776 | - | ||
777 | - A project-relative pathname into which the generated version strings should | ||
778 | - be written. This is usually a `_version.py` next to your project's main | ||
779 | - `__init__.py` file, so it can be imported at runtime. If your project uses | ||
780 | - `src/myproject/__init__.py`, this should be `src/myproject/_version.py`. | ||
781 | - This file should be checked in to your VCS as usual: the copy created below | ||
782 | - by `setup.py setup_versioneer` will include code that parses expanded VCS | ||
783 | - keywords in generated tarballs. The 'build' and 'sdist' commands will | ||
784 | - replace it with a copy that has just the calculated version string. | ||
785 | - | ||
786 | - This must be set even if your project does not have any modules (and will | ||
787 | - therefore never import `_version.py`), since "setup.py sdist" -based trees | ||
788 | - still need somewhere to record the pre-calculated version strings. Anywhere | ||
789 | - in the source tree should do. If there is a `__init__.py` next to your | ||
790 | - `_version.py`, the `setup.py setup_versioneer` command (described below) | ||
791 | - will append some `__version__`-setting assignments, if they aren't already | ||
792 | - present. | ||
793 | - | ||
794 | -* `versionfile_build`: | ||
795 | - | ||
796 | - Like `versionfile_source`, but relative to the build directory instead of | ||
797 | - the source directory. These will differ when your setup.py uses | ||
798 | - 'package_dir='. If you have `package_dir={'myproject': 'src/myproject'}`, | ||
799 | - then you will probably have `versionfile_build='myproject/_version.py'` and | ||
800 | - `versionfile_source='src/myproject/_version.py'`. | ||
801 | - | ||
802 | - If this is set to None, then `setup.py build` will not attempt to rewrite | ||
803 | - any `_version.py` in the built tree. If your project does not have any | ||
804 | - libraries (e.g. if it only builds a script), then you should use | ||
805 | - `versionfile_build = None`. To actually use the computed version string, | ||
806 | - your `setup.py` will need to override `distutils.command.build_scripts` | ||
807 | - with a subclass that explicitly inserts a copy of | ||
808 | - `versioneer.get_version()` into your script file. See | ||
809 | - `test/demoapp-script-only/setup.py` for an example. | ||
810 | - | ||
811 | -* `tag_prefix`: | ||
812 | - | ||
813 | - a string, like 'PROJECTNAME-', which appears at the start of all VCS tags. | ||
814 | - If your tags look like 'myproject-1.2.0', then you should use | ||
815 | - tag_prefix='myproject-'. If you use unprefixed tags like '1.2.0', this | ||
816 | - should be an empty string, using either `tag_prefix=` or `tag_prefix=''`. | ||
817 | - | ||
818 | -* `parentdir_prefix`: | ||
819 | - | ||
820 | - a optional string, frequently the same as tag_prefix, which appears at the | ||
821 | - start of all unpacked tarball filenames. If your tarball unpacks into | ||
822 | - 'myproject-1.2.0', this should be 'myproject-'. To disable this feature, | ||
823 | - just omit the field from your `setup.cfg`. | ||
824 | - | ||
825 | -This tool provides one script, named `versioneer`. That script has one mode, | ||
826 | -"install", which writes a copy of `versioneer.py` into the current directory | ||
827 | -and runs `versioneer.py setup` to finish the installation. | ||
828 | - | ||
829 | -To versioneer-enable your project: | ||
830 | - | ||
831 | -* 1: Modify your `setup.cfg`, adding a section named `[versioneer]` and | ||
832 | - populating it with the configuration values you decided earlier (note that | ||
833 | - the option names are not case-sensitive): | ||
834 | - | ||
835 | - ```` | ||
836 | - [versioneer] | ||
837 | - VCS = git | ||
838 | - style = pep440 | ||
839 | - versionfile_source = src/myproject/_version.py | ||
840 | - versionfile_build = myproject/_version.py | ||
841 | - tag_prefix = | ||
842 | - parentdir_prefix = myproject- | ||
843 | - ```` | ||
844 | - | ||
845 | -* 2: Run `versioneer install`. This will do the following: | ||
846 | - | ||
847 | - * copy `versioneer.py` into the top of your source tree | ||
848 | - * create `_version.py` in the right place (`versionfile_source`) | ||
849 | - * modify your `__init__.py` (if one exists next to `_version.py`) to define | ||
850 | - `__version__` (by calling a function from `_version.py`) | ||
851 | - * modify your `MANIFEST.in` to include both `versioneer.py` and the | ||
852 | - generated `_version.py` in sdist tarballs | ||
853 | - | ||
854 | - `versioneer install` will complain about any problems it finds with your | ||
855 | - `setup.py` or `setup.cfg`. Run it multiple times until you have fixed all | ||
856 | - the problems. | ||
857 | - | ||
858 | -* 3: add a `import versioneer` to your setup.py, and add the following | ||
859 | - arguments to the setup() call: | ||
860 | - | ||
861 | - version=versioneer.get_version(), | ||
862 | - cmdclass=versioneer.get_cmdclass(), | ||
863 | - | ||
864 | -* 4: commit these changes to your VCS. To make sure you won't forget, | ||
865 | - `versioneer install` will mark everything it touched for addition using | ||
866 | - `git add`. Don't forget to add `setup.py` and `setup.cfg` too. | ||
867 | - | ||
868 | -## Post-Installation Usage | ||
869 | - | ||
870 | -Once established, all uses of your tree from a VCS checkout should get the | ||
871 | -current version string. All generated tarballs should include an embedded | ||
872 | -version string (so users who unpack them will not need a VCS tool installed). | ||
873 | - | ||
874 | -If you distribute your project through PyPI, then the release process should | ||
875 | -boil down to two steps: | ||
876 | - | ||
877 | -* 1: git tag 1.0 | ||
878 | -* 2: python setup.py register sdist upload | ||
879 | - | ||
880 | -If you distribute it through github (i.e. users use github to generate | ||
881 | -tarballs with `git archive`), the process is: | ||
882 | - | ||
883 | -* 1: git tag 1.0 | ||
884 | -* 2: git push; git push --tags | ||
885 | - | ||
886 | -Versioneer will report "0+untagged.NUMCOMMITS.gHASH" until your tree has at | ||
887 | -least one tag in its history. | ||
888 | +See [INSTALL.md](./INSTALL.md) for detailed installation instructions. | ||
889 | |||
890 | ## Version-String Flavors | ||
891 | |||
892 | @@ -229,6 +106,10 @@ information: | ||
893 | * `['full-revisionid']`: detailed revision identifier. For Git, this is the | ||
894 | full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". | ||
895 | |||
896 | +* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the | ||
897 | + commit date in ISO 8601 format. This will be None if the date is not | ||
898 | + available. | ||
899 | + | ||
900 | * `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that | ||
901 | this is only accurate if run in a VCS checkout, otherwise it is likely to | ||
902 | be False or None | ||
903 | @@ -267,8 +148,8 @@ that this commit is two revisions ("+2") beyond the "0.11" tag. For released | ||
904 | software (exactly equal to a known tag), the identifier will only contain the | ||
905 | stripped tag, e.g. "0.11". | ||
906 | |||
907 | -Other styles are available. See details.md in the Versioneer source tree for | ||
908 | -descriptions. | ||
909 | +Other styles are available. See [details.md](details.md) in the Versioneer | ||
910 | +source tree for descriptions. | ||
911 | |||
912 | ## Debugging | ||
913 | |||
914 | @@ -278,51 +159,83 @@ version`, which will run the version-lookup code in a verbose mode, and will | ||
915 | display the full contents of `get_versions()` (including the `error` string, | ||
916 | which may help identify what went wrong). | ||
917 | |||
918 | -## Updating Versioneer | ||
919 | +## Known Limitations | ||
920 | |||
921 | -To upgrade your project to a new release of Versioneer, do the following: | ||
922 | +Some situations are known to cause problems for Versioneer. This details the | ||
923 | +most significant ones. More can be found on Github | ||
924 | +[issues page](https://github.com/python-versioneer/python-versioneer/issues). | ||
925 | |||
926 | -* install the new Versioneer (`pip install -U versioneer` or equivalent) | ||
927 | -* edit `setup.cfg`, if necessary, to include any new configuration settings | ||
928 | - indicated by the release notes | ||
929 | -* re-run `versioneer install` in your source tree, to replace | ||
930 | - `SRC/_version.py` | ||
931 | -* commit any changed files | ||
932 | +### Subprojects | ||
933 | + | ||
934 | +Versioneer has limited support for source trees in which `setup.py` is not in | ||
935 | +the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are | ||
936 | +two common reasons why `setup.py` might not be in the root: | ||
937 | + | ||
938 | +* Source trees which contain multiple subprojects, such as | ||
939 | + [Buildbot](https://github.com/buildbot/buildbot), which contains both | ||
940 | + "master" and "slave" subprojects, each with their own `setup.py`, | ||
941 | + `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI | ||
942 | + distributions (and upload multiple independently-installable tarballs). | ||
943 | +* Source trees whose main purpose is to contain a C library, but which also | ||
944 | + provide bindings to Python (and perhaps other languages) in subdirectories. | ||
945 | + | ||
946 | +Versioneer will look for `.git` in parent directories, and most operations | ||
947 | +should get the right version string. However `pip` and `setuptools` have bugs | ||
948 | +and implementation details which frequently cause `pip install .` from a | ||
949 | +subproject directory to fail to find a correct version string (so it usually | ||
950 | +defaults to `0+unknown`). | ||
951 | |||
952 | -### Upgrading to 0.16 | ||
953 | +`pip install --editable .` should work correctly. `setup.py install` might | ||
954 | +work too. | ||
955 | |||
956 | -Nothing special. | ||
957 | +Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in | ||
958 | +some later version. | ||
959 | |||
960 | -### Upgrading to 0.15 | ||
961 | +[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking | ||
962 | +this issue. The discussion in | ||
963 | +[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the | ||
964 | +issue from the Versioneer side in more detail. | ||
965 | +[pip PR#3176](https://github.com/pypa/pip/pull/3176) and | ||
966 | +[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve | ||
967 | +pip to let Versioneer work correctly. | ||
968 | |||
969 | -Starting with this version, Versioneer is configured with a `[versioneer]` | ||
970 | -section in your `setup.cfg` file. Earlier versions required the `setup.py` to | ||
971 | -set attributes on the `versioneer` module immediately after import. The new | ||
972 | -version will refuse to run (raising an exception during import) until you | ||
973 | -have provided the necessary `setup.cfg` section. | ||
974 | +Versioneer-0.16 and earlier only looked for a `.git` directory next to the | ||
975 | +`setup.cfg`, so subprojects were completely unsupported with those releases. | ||
976 | |||
977 | -In addition, the Versioneer package provides an executable named | ||
978 | -`versioneer`, and the installation process is driven by running `versioneer | ||
979 | -install`. In 0.14 and earlier, the executable was named | ||
980 | -`versioneer-installer` and was run without an argument. | ||
981 | +### Editable installs with setuptools <= 18.5 | ||
982 | |||
983 | -### Upgrading to 0.14 | ||
984 | +`setup.py develop` and `pip install --editable .` allow you to install a | ||
985 | +project into a virtualenv once, then continue editing the source code (and | ||
986 | +test) without re-installing after every change. | ||
987 | |||
988 | -0.14 changes the format of the version string. 0.13 and earlier used | ||
989 | -hyphen-separated strings like "0.11-2-g1076c97-dirty". 0.14 and beyond use a | ||
990 | -plus-separated "local version" section strings, with dot-separated | ||
991 | -components, like "0.11+2.g1076c97". PEP440-strict tools did not like the old | ||
992 | -format, but should be ok with the new one. | ||
993 | +"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a | ||
994 | +convenient way to specify executable scripts that should be installed along | ||
995 | +with the python package. | ||
996 | |||
997 | -### Upgrading from 0.11 to 0.12 | ||
998 | +These both work as expected when using modern setuptools. When using | ||
999 | +setuptools-18.5 or earlier, however, certain operations will cause | ||
1000 | +`pkg_resources.DistributionNotFound` errors when running the entrypoint | ||
1001 | +script, which must be resolved by re-installing the package. This happens | ||
1002 | +when the install happens with one version, then the egg_info data is | ||
1003 | +regenerated while a different version is checked out. Many setup.py commands | ||
1004 | +cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into | ||
1005 | +a different virtualenv), so this can be surprising. | ||
1006 | |||
1007 | -Nothing special. | ||
1008 | +[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes | ||
1009 | +this one, but upgrading to a newer version of setuptools should probably | ||
1010 | +resolve it. | ||
1011 | |||
1012 | -### Upgrading from 0.10 to 0.11 | ||
1013 | |||
1014 | -You must add a `versioneer.VCS = "git"` to your `setup.py` before re-running | ||
1015 | -`setup.py setup_versioneer`. This will enable the use of additional | ||
1016 | -version-control systems (SVN, etc) in the future. | ||
1017 | +## Updating Versioneer | ||
1018 | + | ||
1019 | +To upgrade your project to a new release of Versioneer, do the following: | ||
1020 | + | ||
1021 | +* install the new Versioneer (`pip install -U versioneer` or equivalent) | ||
1022 | +* edit `setup.cfg`, if necessary, to include any new configuration settings | ||
1023 | + indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. | ||
1024 | +* re-run `versioneer install` in your source tree, to replace | ||
1025 | + `SRC/_version.py` | ||
1026 | +* commit any changed files | ||
1027 | |||
1028 | ## Future Directions | ||
1029 | |||
1030 | @@ -337,6 +250,14 @@ installation by editing setup.py . Alternatively, it might go the other | ||
1031 | direction and include code from all supported VCS systems, reducing the | ||
1032 | number of intermediate scripts. | ||
1033 | |||
1034 | +## Similar projects | ||
1035 | + | ||
1036 | +* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time | ||
1037 | + dependency | ||
1038 | +* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of | ||
1039 | + versioneer | ||
1040 | +* [versioningit](https://github.com/jwodder/versioningit) - a PEP 518-based setuptools | ||
1041 | + plugin | ||
1042 | |||
1043 | ## License | ||
1044 | |||
1045 | @@ -346,19 +267,28 @@ Specifically, both are released under the Creative Commons "Public Domain | ||
1046 | Dedication" license (CC0-1.0), as described in | ||
1047 | https://creativecommons.org/publicdomain/zero/1.0/ . | ||
1048 | |||
1049 | +[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg | ||
1050 | +[pypi-url]: https://pypi.python.org/pypi/versioneer/ | ||
1051 | +[travis-image]: | ||
1052 | +https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg | ||
1053 | +[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer | ||
1054 | + | ||
1055 | """ | ||
1056 | +# pylint:disable=invalid-name,import-outside-toplevel,missing-function-docstring | ||
1057 | +# pylint:disable=missing-class-docstring,too-many-branches,too-many-statements | ||
1058 | +# pylint:disable=raise-missing-from,too-many-lines,too-many-locals,import-error | ||
1059 | +# pylint:disable=too-few-public-methods,redefined-outer-name,consider-using-with | ||
1060 | +# pylint:disable=attribute-defined-outside-init,too-many-arguments | ||
1061 | |||
1062 | -from __future__ import print_function | ||
1063 | -try: | ||
1064 | - import configparser | ||
1065 | -except ImportError: | ||
1066 | - import ConfigParser as configparser | ||
1067 | +import configparser | ||
1068 | import errno | ||
1069 | import json | ||
1070 | import os | ||
1071 | import re | ||
1072 | import subprocess | ||
1073 | import sys | ||
1074 | +from typing import Callable, Dict | ||
1075 | +import functools | ||
1076 | |||
1077 | |||
1078 | class VersioneerConfig: | ||
1079 | @@ -393,10 +323,12 @@ def get_root(): | ||
1080 | # module-import table will cache the first one. So we can't use | ||
1081 | # os.path.dirname(__file__), as that will find whichever | ||
1082 | # versioneer.py was first imported, even in later projects. | ||
1083 | - me = os.path.realpath(os.path.abspath(__file__)) | ||
1084 | - if os.path.splitext(me)[0] != os.path.splitext(versioneer_py)[0]: | ||
1085 | + my_path = os.path.realpath(os.path.abspath(__file__)) | ||
1086 | + me_dir = os.path.normcase(os.path.splitext(my_path)[0]) | ||
1087 | + vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) | ||
1088 | + if me_dir != vsr_dir: | ||
1089 | print("Warning: build in %s is using versioneer.py from %s" | ||
1090 | - % (os.path.dirname(me), versioneer_py)) | ||
1091 | + % (os.path.dirname(my_path), versioneer_py)) | ||
1092 | except NameError: | ||
1093 | pass | ||
1094 | return root | ||
1095 | @@ -404,85 +336,94 @@ def get_root(): | ||
1096 | |||
1097 | def get_config_from_root(root): | ||
1098 | """Read the project setup.cfg file to determine Versioneer config.""" | ||
1099 | - # This might raise EnvironmentError (if setup.cfg is missing), or | ||
1100 | + # This might raise OSError (if setup.cfg is missing), or | ||
1101 | # configparser.NoSectionError (if it lacks a [versioneer] section), or | ||
1102 | # configparser.NoOptionError (if it lacks "VCS="). See the docstring at | ||
1103 | # the top of versioneer.py for instructions on writing your setup.cfg . | ||
1104 | setup_cfg = os.path.join(root, "setup.cfg") | ||
1105 | - parser = configparser.SafeConfigParser() | ||
1106 | - with open(setup_cfg, "r") as f: | ||
1107 | - parser.readfp(f) | ||
1108 | + parser = configparser.ConfigParser() | ||
1109 | + with open(setup_cfg, "r") as cfg_file: | ||
1110 | + parser.read_file(cfg_file) | ||
1111 | VCS = parser.get("versioneer", "VCS") # mandatory | ||
1112 | |||
1113 | - def get(parser, name): | ||
1114 | - if parser.has_option("versioneer", name): | ||
1115 | - return parser.get("versioneer", name) | ||
1116 | - return None | ||
1117 | + # Dict-like interface for non-mandatory entries | ||
1118 | + section = parser["versioneer"] | ||
1119 | + | ||
1120 | cfg = VersioneerConfig() | ||
1121 | cfg.VCS = VCS | ||
1122 | - cfg.style = get(parser, "style") or "" | ||
1123 | - cfg.versionfile_source = get(parser, "versionfile_source") | ||
1124 | - cfg.versionfile_build = get(parser, "versionfile_build") | ||
1125 | - cfg.tag_prefix = get(parser, "tag_prefix") | ||
1126 | + cfg.style = section.get("style", "") | ||
1127 | + cfg.versionfile_source = section.get("versionfile_source") | ||
1128 | + cfg.versionfile_build = section.get("versionfile_build") | ||
1129 | + cfg.tag_prefix = section.get("tag_prefix") | ||
1130 | if cfg.tag_prefix in ("''", '""'): | ||
1131 | cfg.tag_prefix = "" | ||
1132 | - cfg.parentdir_prefix = get(parser, "parentdir_prefix") | ||
1133 | - cfg.verbose = get(parser, "verbose") | ||
1134 | + cfg.parentdir_prefix = section.get("parentdir_prefix") | ||
1135 | + cfg.verbose = section.get("verbose") | ||
1136 | return cfg | ||
1137 | |||
1138 | |||
1139 | class NotThisMethod(Exception): | ||
1140 | """Exception raised if a method is not valid for the current scenario.""" | ||
1141 | |||
1142 | + | ||
1143 | # these dictionaries contain VCS-specific tools | ||
1144 | -LONG_VERSION_PY = {} | ||
1145 | -HANDLERS = {} | ||
1146 | +LONG_VERSION_PY: Dict[str, str] = {} | ||
1147 | +HANDLERS: Dict[str, Dict[str, Callable]] = {} | ||
1148 | |||
1149 | |||
1150 | def register_vcs_handler(vcs, method): # decorator | ||
1151 | - """Decorator to mark a method as the handler for a particular VCS.""" | ||
1152 | + """Create decorator to mark a method as the handler of a VCS.""" | ||
1153 | def decorate(f): | ||
1154 | """Store f in HANDLERS[vcs][method].""" | ||
1155 | - if vcs not in HANDLERS: | ||
1156 | - HANDLERS[vcs] = {} | ||
1157 | - HANDLERS[vcs][method] = f | ||
1158 | + HANDLERS.setdefault(vcs, {})[method] = f | ||
1159 | return f | ||
1160 | return decorate | ||
1161 | |||
1162 | |||
1163 | -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): | ||
1164 | +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, | ||
1165 | + env=None): | ||
1166 | """Call the given command(s).""" | ||
1167 | assert isinstance(commands, list) | ||
1168 | - p = None | ||
1169 | - for c in commands: | ||
1170 | + process = None | ||
1171 | + | ||
1172 | + popen_kwargs = {} | ||
1173 | + if sys.platform == "win32": | ||
1174 | + # This hides the console window if pythonw.exe is used | ||
1175 | + startupinfo = subprocess.STARTUPINFO() | ||
1176 | + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW | ||
1177 | + popen_kwargs["startupinfo"] = startupinfo | ||
1178 | + | ||
1179 | + for command in commands: | ||
1180 | try: | ||
1181 | - dispcmd = str([c] + args) | ||
1182 | + dispcmd = str([command] + args) | ||
1183 | # remember shell=False, so use git.cmd on windows, not just git | ||
1184 | - p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, | ||
1185 | - stderr=(subprocess.PIPE if hide_stderr | ||
1186 | - else None)) | ||
1187 | + process = subprocess.Popen([command] + args, cwd=cwd, env=env, | ||
1188 | + stdout=subprocess.PIPE, | ||
1189 | + stderr=(subprocess.PIPE if hide_stderr | ||
1190 | + else None), **popen_kwargs) | ||
1191 | break | ||
1192 | - except EnvironmentError: | ||
1193 | + except OSError: | ||
1194 | e = sys.exc_info()[1] | ||
1195 | if e.errno == errno.ENOENT: | ||
1196 | continue | ||
1197 | if verbose: | ||
1198 | print("unable to run %s" % dispcmd) | ||
1199 | print(e) | ||
1200 | - return None | ||
1201 | + return None, None | ||
1202 | else: | ||
1203 | if verbose: | ||
1204 | print("unable to find command, tried %s" % (commands,)) | ||
1205 | - return None | ||
1206 | - stdout = p.communicate()[0].strip() | ||
1207 | - if sys.version_info[0] >= 3: | ||
1208 | - stdout = stdout.decode() | ||
1209 | - if p.returncode != 0: | ||
1210 | + return None, None | ||
1211 | + stdout = process.communicate()[0].strip().decode() | ||
1212 | + if process.returncode != 0: | ||
1213 | if verbose: | ||
1214 | print("unable to run %s (error)" % dispcmd) | ||
1215 | - return None | ||
1216 | - return stdout | ||
1217 | -LONG_VERSION_PY['git'] = ''' | ||
1218 | + print("stdout was %s" % stdout) | ||
1219 | + return None, process.returncode | ||
1220 | + return stdout, process.returncode | ||
1221 | + | ||
1222 | + | ||
1223 | +LONG_VERSION_PY['git'] = r''' | ||
1224 | # This file helps to compute a version number in source trees obtained from | ||
1225 | # git-archive tarball (such as those provided by githubs download-from-tag | ||
1226 | # feature). Distribution tarballs (built by setup.py sdist) and build | ||
1227 | @@ -490,7 +431,7 @@ LONG_VERSION_PY['git'] = ''' | ||
1228 | # that just contains the computed version number. | ||
1229 | |||
1230 | # This file is released into the public domain. Generated by | ||
1231 | -# versioneer-0.16 (https://github.com/warner/python-versioneer) | ||
1232 | +# versioneer-0.22 (https://github.com/python-versioneer/python-versioneer) | ||
1233 | |||
1234 | """Git implementation of _version.py.""" | ||
1235 | |||
1236 | @@ -499,6 +440,8 @@ import os | ||
1237 | import re | ||
1238 | import subprocess | ||
1239 | import sys | ||
1240 | +from typing import Callable, Dict | ||
1241 | +import functools | ||
1242 | |||
1243 | |||
1244 | def get_keywords(): | ||
1245 | @@ -509,7 +452,8 @@ def get_keywords(): | ||
1246 | # get_keywords(). | ||
1247 | git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" | ||
1248 | git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" | ||
1249 | - keywords = {"refnames": git_refnames, "full": git_full} | ||
1250 | + git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" | ||
1251 | + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} | ||
1252 | return keywords | ||
1253 | |||
1254 | |||
1255 | @@ -535,12 +479,12 @@ class NotThisMethod(Exception): | ||
1256 | """Exception raised if a method is not valid for the current scenario.""" | ||
1257 | |||
1258 | |||
1259 | -LONG_VERSION_PY = {} | ||
1260 | -HANDLERS = {} | ||
1261 | +LONG_VERSION_PY: Dict[str, str] = {} | ||
1262 | +HANDLERS: Dict[str, Dict[str, Callable]] = {} | ||
1263 | |||
1264 | |||
1265 | def register_vcs_handler(vcs, method): # decorator | ||
1266 | - """Decorator to mark a method as the handler for a particular VCS.""" | ||
1267 | + """Create decorator to mark a method as the handler of a VCS.""" | ||
1268 | def decorate(f): | ||
1269 | """Store f in HANDLERS[vcs][method].""" | ||
1270 | if vcs not in HANDLERS: | ||
1271 | @@ -550,55 +494,71 @@ def register_vcs_handler(vcs, method): # decorator | ||
1272 | return decorate | ||
1273 | |||
1274 | |||
1275 | -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): | ||
1276 | +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, | ||
1277 | + env=None): | ||
1278 | """Call the given command(s).""" | ||
1279 | assert isinstance(commands, list) | ||
1280 | - p = None | ||
1281 | - for c in commands: | ||
1282 | + process = None | ||
1283 | + | ||
1284 | + popen_kwargs = {} | ||
1285 | + if sys.platform == "win32": | ||
1286 | + # This hides the console window if pythonw.exe is used | ||
1287 | + startupinfo = subprocess.STARTUPINFO() | ||
1288 | + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW | ||
1289 | + popen_kwargs["startupinfo"] = startupinfo | ||
1290 | + | ||
1291 | + for command in commands: | ||
1292 | try: | ||
1293 | - dispcmd = str([c] + args) | ||
1294 | + dispcmd = str([command] + args) | ||
1295 | # remember shell=False, so use git.cmd on windows, not just git | ||
1296 | - p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, | ||
1297 | - stderr=(subprocess.PIPE if hide_stderr | ||
1298 | - else None)) | ||
1299 | + process = subprocess.Popen([command] + args, cwd=cwd, env=env, | ||
1300 | + stdout=subprocess.PIPE, | ||
1301 | + stderr=(subprocess.PIPE if hide_stderr | ||
1302 | + else None), **popen_kwargs) | ||
1303 | break | ||
1304 | - except EnvironmentError: | ||
1305 | + except OSError: | ||
1306 | e = sys.exc_info()[1] | ||
1307 | if e.errno == errno.ENOENT: | ||
1308 | continue | ||
1309 | if verbose: | ||
1310 | print("unable to run %%s" %% dispcmd) | ||
1311 | print(e) | ||
1312 | - return None | ||
1313 | + return None, None | ||
1314 | else: | ||
1315 | if verbose: | ||
1316 | print("unable to find command, tried %%s" %% (commands,)) | ||
1317 | - return None | ||
1318 | - stdout = p.communicate()[0].strip() | ||
1319 | - if sys.version_info[0] >= 3: | ||
1320 | - stdout = stdout.decode() | ||
1321 | - if p.returncode != 0: | ||
1322 | + return None, None | ||
1323 | + stdout = process.communicate()[0].strip().decode() | ||
1324 | + if process.returncode != 0: | ||
1325 | if verbose: | ||
1326 | print("unable to run %%s (error)" %% dispcmd) | ||
1327 | - return None | ||
1328 | - return stdout | ||
1329 | + print("stdout was %%s" %% stdout) | ||
1330 | + return None, process.returncode | ||
1331 | + return stdout, process.returncode | ||
1332 | |||
1333 | |||
1334 | def versions_from_parentdir(parentdir_prefix, root, verbose): | ||
1335 | """Try to determine the version from the parent directory name. | ||
1336 | |||
1337 | - Source tarballs conventionally unpack into a directory that includes | ||
1338 | - both the project name and a version string. | ||
1339 | + Source tarballs conventionally unpack into a directory that includes both | ||
1340 | + the project name and a version string. We will also support searching up | ||
1341 | + two directory levels for an appropriately named parent directory | ||
1342 | """ | ||
1343 | - dirname = os.path.basename(root) | ||
1344 | - if not dirname.startswith(parentdir_prefix): | ||
1345 | - if verbose: | ||
1346 | - print("guessing rootdir is '%%s', but '%%s' doesn't start with " | ||
1347 | - "prefix '%%s'" %% (root, dirname, parentdir_prefix)) | ||
1348 | - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") | ||
1349 | - return {"version": dirname[len(parentdir_prefix):], | ||
1350 | - "full-revisionid": None, | ||
1351 | - "dirty": False, "error": None} | ||
1352 | + rootdirs = [] | ||
1353 | + | ||
1354 | + for _ in range(3): | ||
1355 | + dirname = os.path.basename(root) | ||
1356 | + if dirname.startswith(parentdir_prefix): | ||
1357 | + return {"version": dirname[len(parentdir_prefix):], | ||
1358 | + "full-revisionid": None, | ||
1359 | + "dirty": False, "error": None, "date": None} | ||
1360 | + rootdirs.append(root) | ||
1361 | + root = os.path.dirname(root) # up a level | ||
1362 | + | ||
1363 | + if verbose: | ||
1364 | + print("Tried directories %%s but none started with prefix %%s" %% | ||
1365 | + (str(rootdirs), parentdir_prefix)) | ||
1366 | + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") | ||
1367 | |||
1368 | |||
1369 | @register_vcs_handler("git", "get_keywords") | ||
1370 | @@ -610,18 +570,21 @@ def git_get_keywords(versionfile_abs): | ||
1371 | # _version.py. | ||
1372 | keywords = {} | ||
1373 | try: | ||
1374 | - f = open(versionfile_abs, "r") | ||
1375 | - for line in f.readlines(): | ||
1376 | - if line.strip().startswith("git_refnames ="): | ||
1377 | - mo = re.search(r'=\s*"(.*)"', line) | ||
1378 | - if mo: | ||
1379 | - keywords["refnames"] = mo.group(1) | ||
1380 | - if line.strip().startswith("git_full ="): | ||
1381 | - mo = re.search(r'=\s*"(.*)"', line) | ||
1382 | - if mo: | ||
1383 | - keywords["full"] = mo.group(1) | ||
1384 | - f.close() | ||
1385 | - except EnvironmentError: | ||
1386 | + with open(versionfile_abs, "r") as fobj: | ||
1387 | + for line in fobj: | ||
1388 | + if line.strip().startswith("git_refnames ="): | ||
1389 | + mo = re.search(r'=\s*"(.*)"', line) | ||
1390 | + if mo: | ||
1391 | + keywords["refnames"] = mo.group(1) | ||
1392 | + if line.strip().startswith("git_full ="): | ||
1393 | + mo = re.search(r'=\s*"(.*)"', line) | ||
1394 | + if mo: | ||
1395 | + keywords["full"] = mo.group(1) | ||
1396 | + if line.strip().startswith("git_date ="): | ||
1397 | + mo = re.search(r'=\s*"(.*)"', line) | ||
1398 | + if mo: | ||
1399 | + keywords["date"] = mo.group(1) | ||
1400 | + except OSError: | ||
1401 | pass | ||
1402 | return keywords | ||
1403 | |||
1404 | @@ -629,18 +592,31 @@ def git_get_keywords(versionfile_abs): | ||
1405 | @register_vcs_handler("git", "keywords") | ||
1406 | def git_versions_from_keywords(keywords, tag_prefix, verbose): | ||
1407 | """Get version information from git keywords.""" | ||
1408 | - if not keywords: | ||
1409 | - raise NotThisMethod("no keywords at all, weird") | ||
1410 | + if "refnames" not in keywords: | ||
1411 | + raise NotThisMethod("Short version file found") | ||
1412 | + date = keywords.get("date") | ||
1413 | + if date is not None: | ||
1414 | + # Use only the last line. Previous lines may contain GPG signature | ||
1415 | + # information. | ||
1416 | + date = date.splitlines()[-1] | ||
1417 | + | ||
1418 | + # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant | ||
1419 | + # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 | ||
1420 | + # -like" string, which we must then edit to make compliant), because | ||
1421 | + # it's been around since git-1.5.3, and it's too difficult to | ||
1422 | + # discover which version we're using, or to work around using an | ||
1423 | + # older one. | ||
1424 | + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) | ||
1425 | refnames = keywords["refnames"].strip() | ||
1426 | if refnames.startswith("$Format"): | ||
1427 | if verbose: | ||
1428 | print("keywords are unexpanded, not using") | ||
1429 | raise NotThisMethod("unexpanded keywords, not a git-archive tarball") | ||
1430 | - refs = set([r.strip() for r in refnames.strip("()").split(",")]) | ||
1431 | + refs = {r.strip() for r in refnames.strip("()").split(",")} | ||
1432 | # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of | ||
1433 | # just "foo-1.0". If we see a "tag: " prefix, prefer those. | ||
1434 | TAG = "tag: " | ||
1435 | - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) | ||
1436 | + tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} | ||
1437 | if not tags: | ||
1438 | # Either we're using git < 1.8.3, or there really are no tags. We use | ||
1439 | # a heuristic: assume all version tags have a digit. The old git %%d | ||
1440 | @@ -649,56 +625,72 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): | ||
1441 | # between branches and tags. By ignoring refnames without digits, we | ||
1442 | # filter out many common branch names like "release" and | ||
1443 | # "stabilization", as well as "HEAD" and "master". | ||
1444 | - tags = set([r for r in refs if re.search(r'\d', r)]) | ||
1445 | + tags = {r for r in refs if re.search(r'\d', r)} | ||
1446 | if verbose: | ||
1447 | - print("discarding '%%s', no digits" %% ",".join(refs-tags)) | ||
1448 | + print("discarding '%%s', no digits" %% ",".join(refs - tags)) | ||
1449 | if verbose: | ||
1450 | print("likely tags: %%s" %% ",".join(sorted(tags))) | ||
1451 | for ref in sorted(tags): | ||
1452 | # sorting will prefer e.g. "2.0" over "2.0rc1" | ||
1453 | if ref.startswith(tag_prefix): | ||
1454 | r = ref[len(tag_prefix):] | ||
1455 | + # Filter out refs that exactly match prefix or that don't start | ||
1456 | + # with a number once the prefix is stripped (mostly a concern | ||
1457 | + # when prefix is '') | ||
1458 | + if not re.match(r'\d', r): | ||
1459 | + continue | ||
1460 | if verbose: | ||
1461 | print("picking %%s" %% r) | ||
1462 | return {"version": r, | ||
1463 | "full-revisionid": keywords["full"].strip(), | ||
1464 | - "dirty": False, "error": None | ||
1465 | - } | ||
1466 | + "dirty": False, "error": None, | ||
1467 | + "date": date} | ||
1468 | # no suitable tags, so version is "0+unknown", but full hex is still there | ||
1469 | if verbose: | ||
1470 | print("no suitable tags, using unknown + full revision id") | ||
1471 | return {"version": "0+unknown", | ||
1472 | "full-revisionid": keywords["full"].strip(), | ||
1473 | - "dirty": False, "error": "no suitable tags"} | ||
1474 | + "dirty": False, "error": "no suitable tags", "date": None} | ||
1475 | |||
1476 | |||
1477 | @register_vcs_handler("git", "pieces_from_vcs") | ||
1478 | -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1479 | +def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): | ||
1480 | """Get version from 'git describe' in the root of the source tree. | ||
1481 | |||
1482 | This only gets called if the git-archive 'subst' keywords were *not* | ||
1483 | expanded, and _version.py hasn't already been rewritten with a short | ||
1484 | version string, meaning we're inside a checked out source tree. | ||
1485 | """ | ||
1486 | - if not os.path.exists(os.path.join(root, ".git")): | ||
1487 | - if verbose: | ||
1488 | - print("no .git in %%s" %% root) | ||
1489 | - raise NotThisMethod("no .git directory") | ||
1490 | - | ||
1491 | GITS = ["git"] | ||
1492 | if sys.platform == "win32": | ||
1493 | GITS = ["git.cmd", "git.exe"] | ||
1494 | + | ||
1495 | + # GIT_DIR can interfere with correct operation of Versioneer. | ||
1496 | + # It may be intended to be passed to the Versioneer-versioned project, | ||
1497 | + # but that should not change where we get our version from. | ||
1498 | + env = os.environ.copy() | ||
1499 | + env.pop("GIT_DIR", None) | ||
1500 | + runner = functools.partial(runner, env=env) | ||
1501 | + | ||
1502 | + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, | ||
1503 | + hide_stderr=True) | ||
1504 | + if rc != 0: | ||
1505 | + if verbose: | ||
1506 | + print("Directory %%s not under git control" %% root) | ||
1507 | + raise NotThisMethod("'git rev-parse --git-dir' returned error") | ||
1508 | + | ||
1509 | + MATCH_ARGS = ["--match", "%%s*" %% tag_prefix] if tag_prefix else [] | ||
1510 | + | ||
1511 | # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] | ||
1512 | # if there isn't one, this yields HEX[-dirty] (no NUM) | ||
1513 | - describe_out = run_command(GITS, ["describe", "--tags", "--dirty", | ||
1514 | - "--always", "--long", | ||
1515 | - "--match", "%%s*" %% tag_prefix], | ||
1516 | - cwd=root) | ||
1517 | + describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", | ||
1518 | + "--always", "--long", *MATCH_ARGS], | ||
1519 | + cwd=root) | ||
1520 | # --long was added in git-1.5.5 | ||
1521 | if describe_out is None: | ||
1522 | raise NotThisMethod("'git describe' failed") | ||
1523 | describe_out = describe_out.strip() | ||
1524 | - full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) | ||
1525 | + full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) | ||
1526 | if full_out is None: | ||
1527 | raise NotThisMethod("'git rev-parse' failed") | ||
1528 | full_out = full_out.strip() | ||
1529 | @@ -708,6 +700,39 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1530 | pieces["short"] = full_out[:7] # maybe improved later | ||
1531 | pieces["error"] = None | ||
1532 | |||
1533 | + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], | ||
1534 | + cwd=root) | ||
1535 | + # --abbrev-ref was added in git-1.6.3 | ||
1536 | + if rc != 0 or branch_name is None: | ||
1537 | + raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") | ||
1538 | + branch_name = branch_name.strip() | ||
1539 | + | ||
1540 | + if branch_name == "HEAD": | ||
1541 | + # If we aren't exactly on a branch, pick a branch which represents | ||
1542 | + # the current commit. If all else fails, we are on a branchless | ||
1543 | + # commit. | ||
1544 | + branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) | ||
1545 | + # --contains was added in git-1.5.4 | ||
1546 | + if rc != 0 or branches is None: | ||
1547 | + raise NotThisMethod("'git branch --contains' returned error") | ||
1548 | + branches = branches.split("\n") | ||
1549 | + | ||
1550 | + # Remove the first line if we're running detached | ||
1551 | + if "(" in branches[0]: | ||
1552 | + branches.pop(0) | ||
1553 | + | ||
1554 | + # Strip off the leading "* " from the list of branches. | ||
1555 | + branches = [branch[2:] for branch in branches] | ||
1556 | + if "master" in branches: | ||
1557 | + branch_name = "master" | ||
1558 | + elif not branches: | ||
1559 | + branch_name = None | ||
1560 | + else: | ||
1561 | + # Pick the first branch that is returned. Good or bad. | ||
1562 | + branch_name = branches[0] | ||
1563 | + | ||
1564 | + pieces["branch"] = branch_name | ||
1565 | + | ||
1566 | # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] | ||
1567 | # TAG might have hyphens. | ||
1568 | git_describe = describe_out | ||
1569 | @@ -724,7 +749,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1570 | # TAG-NUM-gHEX | ||
1571 | mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) | ||
1572 | if not mo: | ||
1573 | - # unparseable. Maybe git-describe is misbehaving? | ||
1574 | + # unparsable. Maybe git-describe is misbehaving? | ||
1575 | pieces["error"] = ("unable to parse git-describe output: '%%s'" | ||
1576 | %% describe_out) | ||
1577 | return pieces | ||
1578 | @@ -749,10 +774,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1579 | else: | ||
1580 | # HEX: no tags | ||
1581 | pieces["closest-tag"] = None | ||
1582 | - count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], | ||
1583 | - cwd=root) | ||
1584 | + count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) | ||
1585 | pieces["distance"] = int(count_out) # total number of commits | ||
1586 | |||
1587 | + # commit date: see ISO-8601 comment in git_versions_from_keywords() | ||
1588 | + date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip() | ||
1589 | + # Use only the last line. Previous lines may contain GPG signature | ||
1590 | + # information. | ||
1591 | + date = date.splitlines()[-1] | ||
1592 | + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) | ||
1593 | + | ||
1594 | return pieces | ||
1595 | |||
1596 | |||
1597 | @@ -788,19 +819,67 @@ def render_pep440(pieces): | ||
1598 | return rendered | ||
1599 | |||
1600 | |||
1601 | -def render_pep440_pre(pieces): | ||
1602 | - """TAG[.post.devDISTANCE] -- No -dirty. | ||
1603 | +def render_pep440_branch(pieces): | ||
1604 | + """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . | ||
1605 | + | ||
1606 | + The ".dev0" means not master branch. Note that .dev0 sorts backwards | ||
1607 | + (a feature branch will appear "older" than the master branch). | ||
1608 | |||
1609 | Exceptions: | ||
1610 | - 1: no tags. 0.post.devDISTANCE | ||
1611 | + 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] | ||
1612 | """ | ||
1613 | if pieces["closest-tag"]: | ||
1614 | rendered = pieces["closest-tag"] | ||
1615 | + if pieces["distance"] or pieces["dirty"]: | ||
1616 | + if pieces["branch"] != "master": | ||
1617 | + rendered += ".dev0" | ||
1618 | + rendered += plus_or_dot(pieces) | ||
1619 | + rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) | ||
1620 | + if pieces["dirty"]: | ||
1621 | + rendered += ".dirty" | ||
1622 | + else: | ||
1623 | + # exception #1 | ||
1624 | + rendered = "0" | ||
1625 | + if pieces["branch"] != "master": | ||
1626 | + rendered += ".dev0" | ||
1627 | + rendered += "+untagged.%%d.g%%s" %% (pieces["distance"], | ||
1628 | + pieces["short"]) | ||
1629 | + if pieces["dirty"]: | ||
1630 | + rendered += ".dirty" | ||
1631 | + return rendered | ||
1632 | + | ||
1633 | + | ||
1634 | +def pep440_split_post(ver): | ||
1635 | + """Split pep440 version string at the post-release segment. | ||
1636 | + | ||
1637 | + Returns the release segments before the post-release and the | ||
1638 | + post-release version number (or -1 if no post-release segment is present). | ||
1639 | + """ | ||
1640 | + vc = str.split(ver, ".post") | ||
1641 | + return vc[0], int(vc[1] or 0) if len(vc) == 2 else None | ||
1642 | + | ||
1643 | + | ||
1644 | +def render_pep440_pre(pieces): | ||
1645 | + """TAG[.postN.devDISTANCE] -- No -dirty. | ||
1646 | + | ||
1647 | + Exceptions: | ||
1648 | + 1: no tags. 0.post0.devDISTANCE | ||
1649 | + """ | ||
1650 | + if pieces["closest-tag"]: | ||
1651 | if pieces["distance"]: | ||
1652 | - rendered += ".post.dev%%d" %% pieces["distance"] | ||
1653 | + # update the post release segment | ||
1654 | + tag_version, post_version = pep440_split_post(pieces["closest-tag"]) | ||
1655 | + rendered = tag_version | ||
1656 | + if post_version is not None: | ||
1657 | + rendered += ".post%%d.dev%%d" %% (post_version+1, pieces["distance"]) | ||
1658 | + else: | ||
1659 | + rendered += ".post0.dev%%d" %% (pieces["distance"]) | ||
1660 | + else: | ||
1661 | + # no commits, use the tag as the version | ||
1662 | + rendered = pieces["closest-tag"] | ||
1663 | else: | ||
1664 | # exception #1 | ||
1665 | - rendered = "0.post.dev%%d" %% pieces["distance"] | ||
1666 | + rendered = "0.post0.dev%%d" %% pieces["distance"] | ||
1667 | return rendered | ||
1668 | |||
1669 | |||
1670 | @@ -831,12 +910,41 @@ def render_pep440_post(pieces): | ||
1671 | return rendered | ||
1672 | |||
1673 | |||
1674 | +def render_pep440_post_branch(pieces): | ||
1675 | + """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . | ||
1676 | + | ||
1677 | + The ".dev0" means not master branch. | ||
1678 | + | ||
1679 | + Exceptions: | ||
1680 | + 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] | ||
1681 | + """ | ||
1682 | + if pieces["closest-tag"]: | ||
1683 | + rendered = pieces["closest-tag"] | ||
1684 | + if pieces["distance"] or pieces["dirty"]: | ||
1685 | + rendered += ".post%%d" %% pieces["distance"] | ||
1686 | + if pieces["branch"] != "master": | ||
1687 | + rendered += ".dev0" | ||
1688 | + rendered += plus_or_dot(pieces) | ||
1689 | + rendered += "g%%s" %% pieces["short"] | ||
1690 | + if pieces["dirty"]: | ||
1691 | + rendered += ".dirty" | ||
1692 | + else: | ||
1693 | + # exception #1 | ||
1694 | + rendered = "0.post%%d" %% pieces["distance"] | ||
1695 | + if pieces["branch"] != "master": | ||
1696 | + rendered += ".dev0" | ||
1697 | + rendered += "+g%%s" %% pieces["short"] | ||
1698 | + if pieces["dirty"]: | ||
1699 | + rendered += ".dirty" | ||
1700 | + return rendered | ||
1701 | + | ||
1702 | + | ||
1703 | def render_pep440_old(pieces): | ||
1704 | """TAG[.postDISTANCE[.dev0]] . | ||
1705 | |||
1706 | The ".dev0" means dirty. | ||
1707 | |||
1708 | - Eexceptions: | ||
1709 | + Exceptions: | ||
1710 | 1: no tags. 0.postDISTANCE[.dev0] | ||
1711 | """ | ||
1712 | if pieces["closest-tag"]: | ||
1713 | @@ -899,17 +1007,22 @@ def render(pieces, style): | ||
1714 | return {"version": "unknown", | ||
1715 | "full-revisionid": pieces.get("long"), | ||
1716 | "dirty": None, | ||
1717 | - "error": pieces["error"]} | ||
1718 | + "error": pieces["error"], | ||
1719 | + "date": None} | ||
1720 | |||
1721 | if not style or style == "default": | ||
1722 | style = "pep440" # the default | ||
1723 | |||
1724 | if style == "pep440": | ||
1725 | rendered = render_pep440(pieces) | ||
1726 | + elif style == "pep440-branch": | ||
1727 | + rendered = render_pep440_branch(pieces) | ||
1728 | elif style == "pep440-pre": | ||
1729 | rendered = render_pep440_pre(pieces) | ||
1730 | elif style == "pep440-post": | ||
1731 | rendered = render_pep440_post(pieces) | ||
1732 | + elif style == "pep440-post-branch": | ||
1733 | + rendered = render_pep440_post_branch(pieces) | ||
1734 | elif style == "pep440-old": | ||
1735 | rendered = render_pep440_old(pieces) | ||
1736 | elif style == "git-describe": | ||
1737 | @@ -920,7 +1033,8 @@ def render(pieces, style): | ||
1738 | raise ValueError("unknown style '%%s'" %% style) | ||
1739 | |||
1740 | return {"version": rendered, "full-revisionid": pieces["long"], | ||
1741 | - "dirty": pieces["dirty"], "error": None} | ||
1742 | + "dirty": pieces["dirty"], "error": None, | ||
1743 | + "date": pieces.get("date")} | ||
1744 | |||
1745 | |||
1746 | def get_versions(): | ||
1747 | @@ -944,12 +1058,13 @@ def get_versions(): | ||
1748 | # versionfile_source is the relative path from the top of the source | ||
1749 | # tree (where the .git directory might live) to this file. Invert | ||
1750 | # this to find the root from __file__. | ||
1751 | - for i in cfg.versionfile_source.split('/'): | ||
1752 | + for _ in cfg.versionfile_source.split('/'): | ||
1753 | root = os.path.dirname(root) | ||
1754 | except NameError: | ||
1755 | return {"version": "0+unknown", "full-revisionid": None, | ||
1756 | "dirty": None, | ||
1757 | - "error": "unable to find root of source tree"} | ||
1758 | + "error": "unable to find root of source tree", | ||
1759 | + "date": None} | ||
1760 | |||
1761 | try: | ||
1762 | pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) | ||
1763 | @@ -965,7 +1080,7 @@ def get_versions(): | ||
1764 | |||
1765 | return {"version": "0+unknown", "full-revisionid": None, | ||
1766 | "dirty": None, | ||
1767 | - "error": "unable to compute version"} | ||
1768 | + "error": "unable to compute version", "date": None} | ||
1769 | ''' | ||
1770 | |||
1771 | |||
1772 | @@ -978,18 +1093,21 @@ def git_get_keywords(versionfile_abs): | ||
1773 | # _version.py. | ||
1774 | keywords = {} | ||
1775 | try: | ||
1776 | - f = open(versionfile_abs, "r") | ||
1777 | - for line in f.readlines(): | ||
1778 | - if line.strip().startswith("git_refnames ="): | ||
1779 | - mo = re.search(r'=\s*"(.*)"', line) | ||
1780 | - if mo: | ||
1781 | - keywords["refnames"] = mo.group(1) | ||
1782 | - if line.strip().startswith("git_full ="): | ||
1783 | - mo = re.search(r'=\s*"(.*)"', line) | ||
1784 | - if mo: | ||
1785 | - keywords["full"] = mo.group(1) | ||
1786 | - f.close() | ||
1787 | - except EnvironmentError: | ||
1788 | + with open(versionfile_abs, "r") as fobj: | ||
1789 | + for line in fobj: | ||
1790 | + if line.strip().startswith("git_refnames ="): | ||
1791 | + mo = re.search(r'=\s*"(.*)"', line) | ||
1792 | + if mo: | ||
1793 | + keywords["refnames"] = mo.group(1) | ||
1794 | + if line.strip().startswith("git_full ="): | ||
1795 | + mo = re.search(r'=\s*"(.*)"', line) | ||
1796 | + if mo: | ||
1797 | + keywords["full"] = mo.group(1) | ||
1798 | + if line.strip().startswith("git_date ="): | ||
1799 | + mo = re.search(r'=\s*"(.*)"', line) | ||
1800 | + if mo: | ||
1801 | + keywords["date"] = mo.group(1) | ||
1802 | + except OSError: | ||
1803 | pass | ||
1804 | return keywords | ||
1805 | |||
1806 | @@ -997,18 +1115,31 @@ def git_get_keywords(versionfile_abs): | ||
1807 | @register_vcs_handler("git", "keywords") | ||
1808 | def git_versions_from_keywords(keywords, tag_prefix, verbose): | ||
1809 | """Get version information from git keywords.""" | ||
1810 | - if not keywords: | ||
1811 | - raise NotThisMethod("no keywords at all, weird") | ||
1812 | + if "refnames" not in keywords: | ||
1813 | + raise NotThisMethod("Short version file found") | ||
1814 | + date = keywords.get("date") | ||
1815 | + if date is not None: | ||
1816 | + # Use only the last line. Previous lines may contain GPG signature | ||
1817 | + # information. | ||
1818 | + date = date.splitlines()[-1] | ||
1819 | + | ||
1820 | + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant | ||
1821 | + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 | ||
1822 | + # -like" string, which we must then edit to make compliant), because | ||
1823 | + # it's been around since git-1.5.3, and it's too difficult to | ||
1824 | + # discover which version we're using, or to work around using an | ||
1825 | + # older one. | ||
1826 | + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) | ||
1827 | refnames = keywords["refnames"].strip() | ||
1828 | if refnames.startswith("$Format"): | ||
1829 | if verbose: | ||
1830 | print("keywords are unexpanded, not using") | ||
1831 | raise NotThisMethod("unexpanded keywords, not a git-archive tarball") | ||
1832 | - refs = set([r.strip() for r in refnames.strip("()").split(",")]) | ||
1833 | + refs = {r.strip() for r in refnames.strip("()").split(",")} | ||
1834 | # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of | ||
1835 | # just "foo-1.0". If we see a "tag: " prefix, prefer those. | ||
1836 | TAG = "tag: " | ||
1837 | - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) | ||
1838 | + tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} | ||
1839 | if not tags: | ||
1840 | # Either we're using git < 1.8.3, or there really are no tags. We use | ||
1841 | # a heuristic: assume all version tags have a digit. The old git %d | ||
1842 | @@ -1017,56 +1148,72 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): | ||
1843 | # between branches and tags. By ignoring refnames without digits, we | ||
1844 | # filter out many common branch names like "release" and | ||
1845 | # "stabilization", as well as "HEAD" and "master". | ||
1846 | - tags = set([r for r in refs if re.search(r'\d', r)]) | ||
1847 | + tags = {r for r in refs if re.search(r'\d', r)} | ||
1848 | if verbose: | ||
1849 | - print("discarding '%s', no digits" % ",".join(refs-tags)) | ||
1850 | + print("discarding '%s', no digits" % ",".join(refs - tags)) | ||
1851 | if verbose: | ||
1852 | print("likely tags: %s" % ",".join(sorted(tags))) | ||
1853 | for ref in sorted(tags): | ||
1854 | # sorting will prefer e.g. "2.0" over "2.0rc1" | ||
1855 | if ref.startswith(tag_prefix): | ||
1856 | r = ref[len(tag_prefix):] | ||
1857 | + # Filter out refs that exactly match prefix or that don't start | ||
1858 | + # with a number once the prefix is stripped (mostly a concern | ||
1859 | + # when prefix is '') | ||
1860 | + if not re.match(r'\d', r): | ||
1861 | + continue | ||
1862 | if verbose: | ||
1863 | print("picking %s" % r) | ||
1864 | return {"version": r, | ||
1865 | "full-revisionid": keywords["full"].strip(), | ||
1866 | - "dirty": False, "error": None | ||
1867 | - } | ||
1868 | + "dirty": False, "error": None, | ||
1869 | + "date": date} | ||
1870 | # no suitable tags, so version is "0+unknown", but full hex is still there | ||
1871 | if verbose: | ||
1872 | print("no suitable tags, using unknown + full revision id") | ||
1873 | return {"version": "0+unknown", | ||
1874 | "full-revisionid": keywords["full"].strip(), | ||
1875 | - "dirty": False, "error": "no suitable tags"} | ||
1876 | + "dirty": False, "error": "no suitable tags", "date": None} | ||
1877 | |||
1878 | |||
1879 | @register_vcs_handler("git", "pieces_from_vcs") | ||
1880 | -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1881 | +def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): | ||
1882 | """Get version from 'git describe' in the root of the source tree. | ||
1883 | |||
1884 | This only gets called if the git-archive 'subst' keywords were *not* | ||
1885 | expanded, and _version.py hasn't already been rewritten with a short | ||
1886 | version string, meaning we're inside a checked out source tree. | ||
1887 | """ | ||
1888 | - if not os.path.exists(os.path.join(root, ".git")): | ||
1889 | - if verbose: | ||
1890 | - print("no .git in %s" % root) | ||
1891 | - raise NotThisMethod("no .git directory") | ||
1892 | - | ||
1893 | GITS = ["git"] | ||
1894 | if sys.platform == "win32": | ||
1895 | GITS = ["git.cmd", "git.exe"] | ||
1896 | + | ||
1897 | + # GIT_DIR can interfere with correct operation of Versioneer. | ||
1898 | + # It may be intended to be passed to the Versioneer-versioned project, | ||
1899 | + # but that should not change where we get our version from. | ||
1900 | + env = os.environ.copy() | ||
1901 | + env.pop("GIT_DIR", None) | ||
1902 | + runner = functools.partial(runner, env=env) | ||
1903 | + | ||
1904 | + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, | ||
1905 | + hide_stderr=True) | ||
1906 | + if rc != 0: | ||
1907 | + if verbose: | ||
1908 | + print("Directory %s not under git control" % root) | ||
1909 | + raise NotThisMethod("'git rev-parse --git-dir' returned error") | ||
1910 | + | ||
1911 | + MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else [] | ||
1912 | + | ||
1913 | # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] | ||
1914 | # if there isn't one, this yields HEX[-dirty] (no NUM) | ||
1915 | - describe_out = run_command(GITS, ["describe", "--tags", "--dirty", | ||
1916 | - "--always", "--long", | ||
1917 | - "--match", "%s*" % tag_prefix], | ||
1918 | - cwd=root) | ||
1919 | + describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", | ||
1920 | + "--always", "--long", *MATCH_ARGS], | ||
1921 | + cwd=root) | ||
1922 | # --long was added in git-1.5.5 | ||
1923 | if describe_out is None: | ||
1924 | raise NotThisMethod("'git describe' failed") | ||
1925 | describe_out = describe_out.strip() | ||
1926 | - full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) | ||
1927 | + full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) | ||
1928 | if full_out is None: | ||
1929 | raise NotThisMethod("'git rev-parse' failed") | ||
1930 | full_out = full_out.strip() | ||
1931 | @@ -1076,6 +1223,39 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1932 | pieces["short"] = full_out[:7] # maybe improved later | ||
1933 | pieces["error"] = None | ||
1934 | |||
1935 | + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], | ||
1936 | + cwd=root) | ||
1937 | + # --abbrev-ref was added in git-1.6.3 | ||
1938 | + if rc != 0 or branch_name is None: | ||
1939 | + raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") | ||
1940 | + branch_name = branch_name.strip() | ||
1941 | + | ||
1942 | + if branch_name == "HEAD": | ||
1943 | + # If we aren't exactly on a branch, pick a branch which represents | ||
1944 | + # the current commit. If all else fails, we are on a branchless | ||
1945 | + # commit. | ||
1946 | + branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) | ||
1947 | + # --contains was added in git-1.5.4 | ||
1948 | + if rc != 0 or branches is None: | ||
1949 | + raise NotThisMethod("'git branch --contains' returned error") | ||
1950 | + branches = branches.split("\n") | ||
1951 | + | ||
1952 | + # Remove the first line if we're running detached | ||
1953 | + if "(" in branches[0]: | ||
1954 | + branches.pop(0) | ||
1955 | + | ||
1956 | + # Strip off the leading "* " from the list of branches. | ||
1957 | + branches = [branch[2:] for branch in branches] | ||
1958 | + if "master" in branches: | ||
1959 | + branch_name = "master" | ||
1960 | + elif not branches: | ||
1961 | + branch_name = None | ||
1962 | + else: | ||
1963 | + # Pick the first branch that is returned. Good or bad. | ||
1964 | + branch_name = branches[0] | ||
1965 | + | ||
1966 | + pieces["branch"] = branch_name | ||
1967 | + | ||
1968 | # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] | ||
1969 | # TAG might have hyphens. | ||
1970 | git_describe = describe_out | ||
1971 | @@ -1092,7 +1272,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1972 | # TAG-NUM-gHEX | ||
1973 | mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) | ||
1974 | if not mo: | ||
1975 | - # unparseable. Maybe git-describe is misbehaving? | ||
1976 | + # unparsable. Maybe git-describe is misbehaving? | ||
1977 | pieces["error"] = ("unable to parse git-describe output: '%s'" | ||
1978 | % describe_out) | ||
1979 | return pieces | ||
1980 | @@ -1117,10 +1297,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): | ||
1981 | else: | ||
1982 | # HEX: no tags | ||
1983 | pieces["closest-tag"] = None | ||
1984 | - count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], | ||
1985 | - cwd=root) | ||
1986 | + count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) | ||
1987 | pieces["distance"] = int(count_out) # total number of commits | ||
1988 | |||
1989 | + # commit date: see ISO-8601 comment in git_versions_from_keywords() | ||
1990 | + date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() | ||
1991 | + # Use only the last line. Previous lines may contain GPG signature | ||
1992 | + # information. | ||
1993 | + date = date.splitlines()[-1] | ||
1994 | + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) | ||
1995 | + | ||
1996 | return pieces | ||
1997 | |||
1998 | |||
1999 | @@ -1128,7 +1314,7 @@ def do_vcs_install(manifest_in, versionfile_source, ipy): | ||
2000 | """Git-specific installation logic for Versioneer. | ||
2001 | |||
2002 | For Git, this means creating/changing .gitattributes to mark _version.py | ||
2003 | - for export-time keyword substitution. | ||
2004 | + for export-subst keyword substitution. | ||
2005 | """ | ||
2006 | GITS = ["git"] | ||
2007 | if sys.platform == "win32": | ||
2008 | @@ -1137,27 +1323,26 @@ def do_vcs_install(manifest_in, versionfile_source, ipy): | ||
2009 | if ipy: | ||
2010 | files.append(ipy) | ||
2011 | try: | ||
2012 | - me = __file__ | ||
2013 | - if me.endswith(".pyc") or me.endswith(".pyo"): | ||
2014 | - me = os.path.splitext(me)[0] + ".py" | ||
2015 | - versioneer_file = os.path.relpath(me) | ||
2016 | + my_path = __file__ | ||
2017 | + if my_path.endswith(".pyc") or my_path.endswith(".pyo"): | ||
2018 | + my_path = os.path.splitext(my_path)[0] + ".py" | ||
2019 | + versioneer_file = os.path.relpath(my_path) | ||
2020 | except NameError: | ||
2021 | versioneer_file = "versioneer.py" | ||
2022 | files.append(versioneer_file) | ||
2023 | present = False | ||
2024 | try: | ||
2025 | - f = open(".gitattributes", "r") | ||
2026 | - for line in f.readlines(): | ||
2027 | - if line.strip().startswith(versionfile_source): | ||
2028 | - if "export-subst" in line.strip().split()[1:]: | ||
2029 | - present = True | ||
2030 | - f.close() | ||
2031 | - except EnvironmentError: | ||
2032 | + with open(".gitattributes", "r") as fobj: | ||
2033 | + for line in fobj: | ||
2034 | + if line.strip().startswith(versionfile_source): | ||
2035 | + if "export-subst" in line.strip().split()[1:]: | ||
2036 | + present = True | ||
2037 | + break | ||
2038 | + except OSError: | ||
2039 | pass | ||
2040 | if not present: | ||
2041 | - f = open(".gitattributes", "a+") | ||
2042 | - f.write("%s export-subst\n" % versionfile_source) | ||
2043 | - f.close() | ||
2044 | + with open(".gitattributes", "a+") as fobj: | ||
2045 | + fobj.write(f"{versionfile_source} export-subst\n") | ||
2046 | files.append(".gitattributes") | ||
2047 | run_command(GITS, ["add", "--"] + files) | ||
2048 | |||
2049 | @@ -1165,27 +1350,34 @@ def do_vcs_install(manifest_in, versionfile_source, ipy): | ||
2050 | def versions_from_parentdir(parentdir_prefix, root, verbose): | ||
2051 | """Try to determine the version from the parent directory name. | ||
2052 | |||
2053 | - Source tarballs conventionally unpack into a directory that includes | ||
2054 | - both the project name and a version string. | ||
2055 | + Source tarballs conventionally unpack into a directory that includes both | ||
2056 | + the project name and a version string. We will also support searching up | ||
2057 | + two directory levels for an appropriately named parent directory | ||
2058 | """ | ||
2059 | - dirname = os.path.basename(root) | ||
2060 | - if not dirname.startswith(parentdir_prefix): | ||
2061 | - if verbose: | ||
2062 | - print("guessing rootdir is '%s', but '%s' doesn't start with " | ||
2063 | - "prefix '%s'" % (root, dirname, parentdir_prefix)) | ||
2064 | - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") | ||
2065 | - return {"version": dirname[len(parentdir_prefix):], | ||
2066 | - "full-revisionid": None, | ||
2067 | - "dirty": False, "error": None} | ||
2068 | + rootdirs = [] | ||
2069 | + | ||
2070 | + for _ in range(3): | ||
2071 | + dirname = os.path.basename(root) | ||
2072 | + if dirname.startswith(parentdir_prefix): | ||
2073 | + return {"version": dirname[len(parentdir_prefix):], | ||
2074 | + "full-revisionid": None, | ||
2075 | + "dirty": False, "error": None, "date": None} | ||
2076 | + rootdirs.append(root) | ||
2077 | + root = os.path.dirname(root) # up a level | ||
2078 | + | ||
2079 | + if verbose: | ||
2080 | + print("Tried directories %s but none started with prefix %s" % | ||
2081 | + (str(rootdirs), parentdir_prefix)) | ||
2082 | + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") | ||
2083 | + | ||
2084 | |||
2085 | SHORT_VERSION_PY = """ | ||
2086 | -# This file was generated by 'versioneer.py' (0.16) from | ||
2087 | +# This file was generated by 'versioneer.py' (0.22) from | ||
2088 | # revision-control system data, or from the parent directory name of an | ||
2089 | # unpacked source archive. Distribution tarballs contain a pre-generated copy | ||
2090 | # of this file. | ||
2091 | |||
2092 | import json | ||
2093 | -import sys | ||
2094 | |||
2095 | version_json = ''' | ||
2096 | %s | ||
2097 | @@ -1202,10 +1394,13 @@ def versions_from_file(filename): | ||
2098 | try: | ||
2099 | with open(filename) as f: | ||
2100 | contents = f.read() | ||
2101 | - except EnvironmentError: | ||
2102 | + except OSError: | ||
2103 | raise NotThisMethod("unable to read _version.py") | ||
2104 | mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", | ||
2105 | contents, re.M | re.S) | ||
2106 | + if not mo: | ||
2107 | + mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", | ||
2108 | + contents, re.M | re.S) | ||
2109 | if not mo: | ||
2110 | raise NotThisMethod("no version_json in _version.py") | ||
2111 | return json.loads(mo.group(1)) | ||
2112 | @@ -1254,19 +1449,67 @@ def render_pep440(pieces): | ||
2113 | return rendered | ||
2114 | |||
2115 | |||
2116 | -def render_pep440_pre(pieces): | ||
2117 | - """TAG[.post.devDISTANCE] -- No -dirty. | ||
2118 | +def render_pep440_branch(pieces): | ||
2119 | + """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . | ||
2120 | + | ||
2121 | + The ".dev0" means not master branch. Note that .dev0 sorts backwards | ||
2122 | + (a feature branch will appear "older" than the master branch). | ||
2123 | |||
2124 | Exceptions: | ||
2125 | - 1: no tags. 0.post.devDISTANCE | ||
2126 | + 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] | ||
2127 | """ | ||
2128 | if pieces["closest-tag"]: | ||
2129 | rendered = pieces["closest-tag"] | ||
2130 | + if pieces["distance"] or pieces["dirty"]: | ||
2131 | + if pieces["branch"] != "master": | ||
2132 | + rendered += ".dev0" | ||
2133 | + rendered += plus_or_dot(pieces) | ||
2134 | + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) | ||
2135 | + if pieces["dirty"]: | ||
2136 | + rendered += ".dirty" | ||
2137 | + else: | ||
2138 | + # exception #1 | ||
2139 | + rendered = "0" | ||
2140 | + if pieces["branch"] != "master": | ||
2141 | + rendered += ".dev0" | ||
2142 | + rendered += "+untagged.%d.g%s" % (pieces["distance"], | ||
2143 | + pieces["short"]) | ||
2144 | + if pieces["dirty"]: | ||
2145 | + rendered += ".dirty" | ||
2146 | + return rendered | ||
2147 | + | ||
2148 | + | ||
2149 | +def pep440_split_post(ver): | ||
2150 | + """Split pep440 version string at the post-release segment. | ||
2151 | + | ||
2152 | + Returns the release segments before the post-release and the | ||
2153 | + post-release version number (or -1 if no post-release segment is present). | ||
2154 | + """ | ||
2155 | + vc = str.split(ver, ".post") | ||
2156 | + return vc[0], int(vc[1] or 0) if len(vc) == 2 else None | ||
2157 | + | ||
2158 | + | ||
2159 | +def render_pep440_pre(pieces): | ||
2160 | + """TAG[.postN.devDISTANCE] -- No -dirty. | ||
2161 | + | ||
2162 | + Exceptions: | ||
2163 | + 1: no tags. 0.post0.devDISTANCE | ||
2164 | + """ | ||
2165 | + if pieces["closest-tag"]: | ||
2166 | if pieces["distance"]: | ||
2167 | - rendered += ".post.dev%d" % pieces["distance"] | ||
2168 | + # update the post release segment | ||
2169 | + tag_version, post_version = pep440_split_post(pieces["closest-tag"]) | ||
2170 | + rendered = tag_version | ||
2171 | + if post_version is not None: | ||
2172 | + rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"]) | ||
2173 | + else: | ||
2174 | + rendered += ".post0.dev%d" % (pieces["distance"]) | ||
2175 | + else: | ||
2176 | + # no commits, use the tag as the version | ||
2177 | + rendered = pieces["closest-tag"] | ||
2178 | else: | ||
2179 | # exception #1 | ||
2180 | - rendered = "0.post.dev%d" % pieces["distance"] | ||
2181 | + rendered = "0.post0.dev%d" % pieces["distance"] | ||
2182 | return rendered | ||
2183 | |||
2184 | |||
2185 | @@ -1297,12 +1540,41 @@ def render_pep440_post(pieces): | ||
2186 | return rendered | ||
2187 | |||
2188 | |||
2189 | +def render_pep440_post_branch(pieces): | ||
2190 | + """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . | ||
2191 | + | ||
2192 | + The ".dev0" means not master branch. | ||
2193 | + | ||
2194 | + Exceptions: | ||
2195 | + 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] | ||
2196 | + """ | ||
2197 | + if pieces["closest-tag"]: | ||
2198 | + rendered = pieces["closest-tag"] | ||
2199 | + if pieces["distance"] or pieces["dirty"]: | ||
2200 | + rendered += ".post%d" % pieces["distance"] | ||
2201 | + if pieces["branch"] != "master": | ||
2202 | + rendered += ".dev0" | ||
2203 | + rendered += plus_or_dot(pieces) | ||
2204 | + rendered += "g%s" % pieces["short"] | ||
2205 | + if pieces["dirty"]: | ||
2206 | + rendered += ".dirty" | ||
2207 | + else: | ||
2208 | + # exception #1 | ||
2209 | + rendered = "0.post%d" % pieces["distance"] | ||
2210 | + if pieces["branch"] != "master": | ||
2211 | + rendered += ".dev0" | ||
2212 | + rendered += "+g%s" % pieces["short"] | ||
2213 | + if pieces["dirty"]: | ||
2214 | + rendered += ".dirty" | ||
2215 | + return rendered | ||
2216 | + | ||
2217 | + | ||
2218 | def render_pep440_old(pieces): | ||
2219 | """TAG[.postDISTANCE[.dev0]] . | ||
2220 | |||
2221 | The ".dev0" means dirty. | ||
2222 | |||
2223 | - Eexceptions: | ||
2224 | + Exceptions: | ||
2225 | 1: no tags. 0.postDISTANCE[.dev0] | ||
2226 | """ | ||
2227 | if pieces["closest-tag"]: | ||
2228 | @@ -1365,17 +1637,22 @@ def render(pieces, style): | ||
2229 | return {"version": "unknown", | ||
2230 | "full-revisionid": pieces.get("long"), | ||
2231 | "dirty": None, | ||
2232 | - "error": pieces["error"]} | ||
2233 | + "error": pieces["error"], | ||
2234 | + "date": None} | ||
2235 | |||
2236 | if not style or style == "default": | ||
2237 | style = "pep440" # the default | ||
2238 | |||
2239 | if style == "pep440": | ||
2240 | rendered = render_pep440(pieces) | ||
2241 | + elif style == "pep440-branch": | ||
2242 | + rendered = render_pep440_branch(pieces) | ||
2243 | elif style == "pep440-pre": | ||
2244 | rendered = render_pep440_pre(pieces) | ||
2245 | elif style == "pep440-post": | ||
2246 | rendered = render_pep440_post(pieces) | ||
2247 | + elif style == "pep440-post-branch": | ||
2248 | + rendered = render_pep440_post_branch(pieces) | ||
2249 | elif style == "pep440-old": | ||
2250 | rendered = render_pep440_old(pieces) | ||
2251 | elif style == "git-describe": | ||
2252 | @@ -1386,7 +1663,8 @@ def render(pieces, style): | ||
2253 | raise ValueError("unknown style '%s'" % style) | ||
2254 | |||
2255 | return {"version": rendered, "full-revisionid": pieces["long"], | ||
2256 | - "dirty": pieces["dirty"], "error": None} | ||
2257 | + "dirty": pieces["dirty"], "error": None, | ||
2258 | + "date": pieces.get("date")} | ||
2259 | |||
2260 | |||
2261 | class VersioneerBadRootError(Exception): | ||
2262 | @@ -1465,7 +1743,8 @@ def get_versions(verbose=False): | ||
2263 | print("unable to compute version") | ||
2264 | |||
2265 | return {"version": "0+unknown", "full-revisionid": None, | ||
2266 | - "dirty": None, "error": "unable to compute version"} | ||
2267 | + "dirty": None, "error": "unable to compute version", | ||
2268 | + "date": None} | ||
2269 | |||
2270 | |||
2271 | def get_version(): | ||
2272 | @@ -1473,8 +1752,12 @@ def get_version(): | ||
2273 | return get_versions()["version"] | ||
2274 | |||
2275 | |||
2276 | -def get_cmdclass(): | ||
2277 | - """Get the custom setuptools/distutils subclasses used by Versioneer.""" | ||
2278 | +def get_cmdclass(cmdclass=None): | ||
2279 | + """Get the custom setuptools/distutils subclasses used by Versioneer. | ||
2280 | + | ||
2281 | + If the package uses a different cmdclass (e.g. one from numpy), it | ||
2282 | + should be provide as an argument. | ||
2283 | + """ | ||
2284 | if "versioneer" in sys.modules: | ||
2285 | del sys.modules["versioneer"] | ||
2286 | # this fixes the "python setup.py develop" case (also 'install' and | ||
2287 | @@ -1488,12 +1771,15 @@ def get_cmdclass(): | ||
2288 | # parent is protected against the child's "import versioneer". By | ||
2289 | # removing ourselves from sys.modules here, before the child build | ||
2290 | # happens, we protect the child from the parent's versioneer too. | ||
2291 | - # Also see https://github.com/warner/python-versioneer/issues/52 | ||
2292 | + # Also see https://github.com/python-versioneer/python-versioneer/issues/52 | ||
2293 | |||
2294 | - cmds = {} | ||
2295 | + cmds = {} if cmdclass is None else cmdclass.copy() | ||
2296 | |||
2297 | # we add "version" to both distutils and setuptools | ||
2298 | - from distutils.core import Command | ||
2299 | + try: | ||
2300 | + from setuptools import Command | ||
2301 | + except ImportError: | ||
2302 | + from distutils.core import Command | ||
2303 | |||
2304 | class cmd_version(Command): | ||
2305 | description = "report generated version string" | ||
2306 | @@ -1511,6 +1797,7 @@ def get_cmdclass(): | ||
2307 | print("Version: %s" % vers["version"]) | ||
2308 | print(" full-revisionid: %s" % vers.get("full-revisionid")) | ||
2309 | print(" dirty: %s" % vers.get("dirty")) | ||
2310 | + print(" date: %s" % vers.get("date")) | ||
2311 | if vers["error"]: | ||
2312 | print(" error: %s" % vers["error"]) | ||
2313 | cmds["version"] = cmd_version | ||
2314 | @@ -1524,9 +1811,16 @@ def get_cmdclass(): | ||
2315 | # setuptools/bdist_egg -> distutils/install_lib -> build_py | ||
2316 | # setuptools/install -> bdist_egg ->.. | ||
2317 | # setuptools/develop -> ? | ||
2318 | + # pip install: | ||
2319 | + # copies source tree to a tempdir before running egg_info/etc | ||
2320 | + # if .git isn't copied too, 'git describe' will fail | ||
2321 | + # then does setup.py bdist_wheel, or sometimes setup.py install | ||
2322 | + # setup.py egg_info -> ? | ||
2323 | |||
2324 | # we override different "build_py" commands for both environments | ||
2325 | - if "setuptools" in sys.modules: | ||
2326 | + if 'build_py' in cmds: | ||
2327 | + _build_py = cmds['build_py'] | ||
2328 | + elif "setuptools" in sys.modules: | ||
2329 | from setuptools.command.build_py import build_py as _build_py | ||
2330 | else: | ||
2331 | from distutils.command.build_py import build_py as _build_py | ||
2332 | @@ -1546,8 +1840,41 @@ def get_cmdclass(): | ||
2333 | write_to_version_file(target_versionfile, versions) | ||
2334 | cmds["build_py"] = cmd_build_py | ||
2335 | |||
2336 | + if 'build_ext' in cmds: | ||
2337 | + _build_ext = cmds['build_ext'] | ||
2338 | + elif "setuptools" in sys.modules: | ||
2339 | + from setuptools.command.build_ext import build_ext as _build_ext | ||
2340 | + else: | ||
2341 | + from distutils.command.build_ext import build_ext as _build_ext | ||
2342 | + | ||
2343 | + class cmd_build_ext(_build_ext): | ||
2344 | + def run(self): | ||
2345 | + root = get_root() | ||
2346 | + cfg = get_config_from_root(root) | ||
2347 | + versions = get_versions() | ||
2348 | + _build_ext.run(self) | ||
2349 | + if self.inplace: | ||
2350 | + # build_ext --inplace will only build extensions in | ||
2351 | + # build/lib<..> dir with no _version.py to write to. | ||
2352 | + # As in place builds will already have a _version.py | ||
2353 | + # in the module dir, we do not need to write one. | ||
2354 | + return | ||
2355 | + # now locate _version.py in the new build/ directory and replace | ||
2356 | + # it with an updated value | ||
2357 | + target_versionfile = os.path.join(self.build_lib, | ||
2358 | + cfg.versionfile_build) | ||
2359 | + print("UPDATING %s" % target_versionfile) | ||
2360 | + write_to_version_file(target_versionfile, versions) | ||
2361 | + cmds["build_ext"] = cmd_build_ext | ||
2362 | + | ||
2363 | if "cx_Freeze" in sys.modules: # cx_freeze enabled? | ||
2364 | from cx_Freeze.dist import build_exe as _build_exe | ||
2365 | + # nczeczulin reports that py2exe won't like the pep440-style string | ||
2366 | + # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. | ||
2367 | + # setup(console=[{ | ||
2368 | + # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION | ||
2369 | + # "product_version": versioneer.get_version(), | ||
2370 | + # ... | ||
2371 | |||
2372 | class cmd_build_exe(_build_exe): | ||
2373 | def run(self): | ||
2374 | @@ -1572,8 +1899,35 @@ def get_cmdclass(): | ||
2375 | cmds["build_exe"] = cmd_build_exe | ||
2376 | del cmds["build_py"] | ||
2377 | |||
2378 | + if 'py2exe' in sys.modules: # py2exe enabled? | ||
2379 | + from py2exe.distutils_buildexe import py2exe as _py2exe | ||
2380 | + | ||
2381 | + class cmd_py2exe(_py2exe): | ||
2382 | + def run(self): | ||
2383 | + root = get_root() | ||
2384 | + cfg = get_config_from_root(root) | ||
2385 | + versions = get_versions() | ||
2386 | + target_versionfile = cfg.versionfile_source | ||
2387 | + print("UPDATING %s" % target_versionfile) | ||
2388 | + write_to_version_file(target_versionfile, versions) | ||
2389 | + | ||
2390 | + _py2exe.run(self) | ||
2391 | + os.unlink(target_versionfile) | ||
2392 | + with open(cfg.versionfile_source, "w") as f: | ||
2393 | + LONG = LONG_VERSION_PY[cfg.VCS] | ||
2394 | + f.write(LONG % | ||
2395 | + {"DOLLAR": "$", | ||
2396 | + "STYLE": cfg.style, | ||
2397 | + "TAG_PREFIX": cfg.tag_prefix, | ||
2398 | + "PARENTDIR_PREFIX": cfg.parentdir_prefix, | ||
2399 | + "VERSIONFILE_SOURCE": cfg.versionfile_source, | ||
2400 | + }) | ||
2401 | + cmds["py2exe"] = cmd_py2exe | ||
2402 | + | ||
2403 | # we override different "sdist" commands for both environments | ||
2404 | - if "setuptools" in sys.modules: | ||
2405 | + if 'sdist' in cmds: | ||
2406 | + _sdist = cmds['sdist'] | ||
2407 | + elif "setuptools" in sys.modules: | ||
2408 | from setuptools.command.sdist import sdist as _sdist | ||
2409 | else: | ||
2410 | from distutils.command.sdist import sdist as _sdist | ||
2411 | @@ -1640,21 +1994,26 @@ SAMPLE_CONFIG = """ | ||
2412 | |||
2413 | """ | ||
2414 | |||
2415 | -INIT_PY_SNIPPET = """ | ||
2416 | +OLD_SNIPPET = """ | ||
2417 | from ._version import get_versions | ||
2418 | __version__ = get_versions()['version'] | ||
2419 | del get_versions | ||
2420 | """ | ||
2421 | |||
2422 | +INIT_PY_SNIPPET = """ | ||
2423 | +from . import {0} | ||
2424 | +__version__ = {0}.get_versions()['version'] | ||
2425 | +""" | ||
2426 | + | ||
2427 | |||
2428 | def do_setup(): | ||
2429 | - """Main VCS-independent setup function for installing Versioneer.""" | ||
2430 | + """Do main VCS-independent setup function for installing Versioneer.""" | ||
2431 | root = get_root() | ||
2432 | try: | ||
2433 | cfg = get_config_from_root(root) | ||
2434 | - except (EnvironmentError, configparser.NoSectionError, | ||
2435 | + except (OSError, configparser.NoSectionError, | ||
2436 | configparser.NoOptionError) as e: | ||
2437 | - if isinstance(e, (EnvironmentError, configparser.NoSectionError)): | ||
2438 | + if isinstance(e, (OSError, configparser.NoSectionError)): | ||
2439 | print("Adding sample versioneer config to setup.cfg", | ||
2440 | file=sys.stderr) | ||
2441 | with open(os.path.join(root, "setup.cfg"), "a") as f: | ||
2442 | @@ -1678,12 +2037,18 @@ def do_setup(): | ||
2443 | try: | ||
2444 | with open(ipy, "r") as f: | ||
2445 | old = f.read() | ||
2446 | - except EnvironmentError: | ||
2447 | + except OSError: | ||
2448 | old = "" | ||
2449 | - if INIT_PY_SNIPPET not in old: | ||
2450 | + module = os.path.splitext(os.path.basename(cfg.versionfile_source))[0] | ||
2451 | + snippet = INIT_PY_SNIPPET.format(module) | ||
2452 | + if OLD_SNIPPET in old: | ||
2453 | + print(" replacing boilerplate in %s" % ipy) | ||
2454 | + with open(ipy, "w") as f: | ||
2455 | + f.write(old.replace(OLD_SNIPPET, snippet)) | ||
2456 | + elif snippet not in old: | ||
2457 | print(" appending to %s" % ipy) | ||
2458 | with open(ipy, "a") as f: | ||
2459 | - f.write(INIT_PY_SNIPPET) | ||
2460 | + f.write(snippet) | ||
2461 | else: | ||
2462 | print(" %s unmodified" % ipy) | ||
2463 | else: | ||
2464 | @@ -1702,7 +2067,7 @@ def do_setup(): | ||
2465 | if line.startswith("include "): | ||
2466 | for include in line.split()[1:]: | ||
2467 | simple_includes.add(include) | ||
2468 | - except EnvironmentError: | ||
2469 | + except OSError: | ||
2470 | pass | ||
2471 | # That doesn't cover everything MANIFEST.in can do | ||
2472 | # (http://docs.python.org/2/distutils/sourcedist.html#commands), so | ||
2473 | @@ -1723,7 +2088,7 @@ def do_setup(): | ||
2474 | print(" versionfile_source already in MANIFEST.in") | ||
2475 | |||
2476 | # Make VCS-specific changes. For git, this means creating/changing | ||
2477 | - # .gitattributes to mark _version.py for export-time keyword | ||
2478 | + # .gitattributes to mark _version.py for export-subst keyword | ||
2479 | # substitution. | ||
2480 | do_vcs_install(manifest_in, cfg.versionfile_source, ipy) | ||
2481 | return 0 | ||
2482 | @@ -1765,6 +2130,7 @@ def scan_setup_py(): | ||
2483 | errors += 1 | ||
2484 | return errors | ||
2485 | |||
2486 | + | ||
2487 | if __name__ == "__main__": | ||
2488 | cmd = sys.argv[1] | ||
2489 | if cmd == "setup": | ||