2 # This file helps to compute a version number in source trees obtained from
3 # git-archive tarball (such as those provided by githubs download-from-tag
4 # feature). Distribution tarballs (built by setup.py sdist) and build
5 # directories (produced by setup.py build) will contain a much shorter file
6 # that just contains the computed version number.
8 # This file is released into the public domain. Generated by
9 # versioneer-0.18 (https://github.com/warner/python-versioneer)
11 """Git implementation of _version.py."""
21 """Get the keywords needed to look up the version information."""
22 # these strings will be replaced by git during git-archive.
23 # setup.py/versioneer.py will grep for the variable names, so they must
24 # each be defined on a line of their own. _version.py will just call
26 git_refnames
= "$Format:%d$"
27 git_full
= "$Format:%H$"
28 git_date
= "$Format:%ci$"
29 keywords
= {"refnames": git_refnames
, "full": git_full
, "date": git_date
}
33 class VersioneerConfig
:
34 """Container for Versioneer configuration parameters."""
38 """Create, populate and return the VersioneerConfig() object."""
39 # these strings are filled in when 'setup.py versioneer' creates
41 cfg
= VersioneerConfig()
45 cfg
.parentdir_prefix
= "salmon-"
46 cfg
.versionfile_source
= "salmon/_version.py"
51 class NotThisMethod(Exception):
52 """Exception raised if a method is not valid for the current scenario."""
59 def register_vcs_handler(vcs
, method
): # decorator
60 """Decorator to mark a method as the handler for a particular VCS."""
62 """Store f in HANDLERS[vcs][method]."""
63 if vcs
not in HANDLERS
:
65 HANDLERS
[vcs
][method
] = f
70 def run_command(commands
, args
, cwd
=None, verbose
=False, hide_stderr
=False,
72 """Call the given command(s)."""
73 assert isinstance(commands
, list)
77 dispcmd
= str([c
] + args
)
78 # remember shell=False, so use git.cmd on windows, not just git
79 p
= subprocess
.Popen([c
] + args
, cwd
=cwd
, env
=env
,
80 stdout
=subprocess
.PIPE
,
81 stderr
=(subprocess
.PIPE
if hide_stderr
84 except EnvironmentError:
86 if e
.errno
== errno
.ENOENT
:
89 print("unable to run %s" % dispcmd
)
94 print("unable to find command, tried %s" % (commands
,))
96 stdout
= p
.communicate()[0].strip()
97 if sys
.version_info
[0] >= 3:
98 stdout
= stdout
.decode()
101 print("unable to run %s (error)" % dispcmd
)
102 print("stdout was %s" % stdout
)
103 return None, p
.returncode
104 return stdout
, p
.returncode
107 def versions_from_parentdir(parentdir_prefix
, root
, verbose
):
108 """Try to determine the version from the parent directory name.
110 Source tarballs conventionally unpack into a directory that includes both
111 the project name and a version string. We will also support searching up
112 two directory levels for an appropriately named parent directory
117 dirname
= os
.path
.basename(root
)
118 if dirname
.startswith(parentdir_prefix
):
119 return {"version": dirname
[len(parentdir_prefix
):],
120 "full-revisionid": None,
121 "dirty": False, "error": None, "date": None}
123 rootdirs
.append(root
)
124 root
= os
.path
.dirname(root
) # up a level
127 print("Tried directories %s but none started with prefix %s" %
128 (str(rootdirs
), parentdir_prefix
))
129 raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
132 @register_vcs_handler("git", "get_keywords")
133 def git_get_keywords(versionfile_abs
):
134 """Extract version information from the given file."""
135 # the code embedded in _version.py can just fetch the value of these
136 # keywords. When used from setup.py, we don't want to import _version.py,
137 # so we do it with a regexp instead. This function is not used from
141 f
= open(versionfile_abs
, "r")
142 for line
in f
.readlines():
143 if line
.strip().startswith("git_refnames ="):
144 mo
= re
.search(r
'=\s*"(.*)"', line
)
146 keywords
["refnames"] = mo
.group(1)
147 if line
.strip().startswith("git_full ="):
148 mo
= re
.search(r
'=\s*"(.*)"', line
)
150 keywords
["full"] = mo
.group(1)
151 if line
.strip().startswith("git_date ="):
152 mo
= re
.search(r
'=\s*"(.*)"', line
)
154 keywords
["date"] = mo
.group(1)
156 except EnvironmentError:
161 @register_vcs_handler("git", "keywords") # noqa: C901
162 def git_versions_from_keywords(keywords
, tag_prefix
, verbose
): # noqa: C901
163 """Get version information from git keywords."""
165 raise NotThisMethod("no keywords at all, weird")
166 date
= keywords
.get("date")
168 # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
169 # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
170 # -like" string, which we must then edit to make compliant), because
171 # it's been around since git-1.5.3, and it's too difficult to
172 # discover which version we're using, or to work around using an
174 date
= date
.strip().replace(" ", "T", 1).replace(" ", "", 1)
175 refnames
= keywords
["refnames"].strip()
176 if refnames
.startswith("$Format"):
178 print("keywords are unexpanded, not using")
179 raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
180 refs
= set([r
.strip() for r
in refnames
.strip("()").split(",")])
181 # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
182 # just "foo-1.0". If we see a "tag: " prefix, prefer those.
184 tags
= set([r
[len(TAG
):] for r
in refs
if r
.startswith(TAG
)])
186 # Either we're using git < 1.8.3, or there really are no tags. We use
187 # a heuristic: assume all version tags have a digit. The old git %d
188 # expansion behaves like git log --decorate=short and strips out the
189 # refs/heads/ and refs/tags/ prefixes that would let us distinguish
190 # between branches and tags. By ignoring refnames without digits, we
191 # filter out many common branch names like "release" and
192 # "stabilization", as well as "HEAD" and "master".
193 tags
= set([r
for r
in refs
if re
.search(r
'\d', r
)])
195 print("discarding '%s', no digits" % ",".join(refs
- tags
))
197 print("likely tags: %s" % ",".join(sorted(tags
)))
198 for ref
in sorted(tags
):
199 # sorting will prefer e.g. "2.0" over "2.0rc1"
200 if ref
.startswith(tag_prefix
):
201 r
= ref
[len(tag_prefix
):]
203 print("picking %s" % r
)
204 return {"version": r
,
205 "full-revisionid": keywords
["full"].strip(),
206 "dirty": False, "error": None,
208 # no suitable tags, so version is "0+unknown", but full hex is still there
210 print("no suitable tags, using unknown + full revision id")
211 return {"version": "0+unknown",
212 "full-revisionid": keywords
["full"].strip(),
213 "dirty": False, "error": "no suitable tags", "date": None}
216 @register_vcs_handler("git", "pieces_from_vcs") # noqa: C901
217 def git_pieces_from_vcs(tag_prefix
, root
, verbose
, run_command
=run_command
): # noqa: C901
218 """Get version from 'git describe' in the root of the source tree.
220 This only gets called if the git-archive 'subst' keywords were *not*
221 expanded, and _version.py hasn't already been rewritten with a short
222 version string, meaning we're inside a checked out source tree.
225 if sys
.platform
== "win32":
226 GITS
= ["git.cmd", "git.exe"]
228 out
, rc
= run_command(GITS
, ["rev-parse", "--git-dir"], cwd
=root
,
232 print("Directory %s not under git control" % root
)
233 raise NotThisMethod("'git rev-parse --git-dir' returned error")
235 # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
236 # if there isn't one, this yields HEX[-dirty] (no NUM)
237 describe_out
, rc
= run_command(GITS
, ["describe", "--tags", "--dirty",
238 "--always", "--long",
239 "--match", "%s*" % tag_prefix
],
241 # --long was added in git-1.5.5
242 if describe_out
is None:
243 raise NotThisMethod("'git describe' failed")
244 describe_out
= describe_out
.strip()
245 full_out
, rc
= run_command(GITS
, ["rev-parse", "HEAD"], cwd
=root
)
247 raise NotThisMethod("'git rev-parse' failed")
248 full_out
= full_out
.strip()
251 pieces
["long"] = full_out
252 pieces
["short"] = full_out
[:7] # maybe improved later
253 pieces
["error"] = None
255 # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
256 # TAG might have hyphens.
257 git_describe
= describe_out
259 # look for -dirty suffix
260 dirty
= git_describe
.endswith("-dirty")
261 pieces
["dirty"] = dirty
263 git_describe
= git_describe
[:git_describe
.rindex("-dirty")]
265 # now we have TAG-NUM-gHEX or HEX
267 if "-" in git_describe
:
269 mo
= re
.search(r
'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe
)
271 # unparseable. Maybe git-describe is misbehaving?
272 pieces
["error"] = ("unable to parse git-describe output: '%s'"
277 full_tag
= mo
.group(1)
278 if not full_tag
.startswith(tag_prefix
):
280 fmt
= "tag '%s' doesn't start with prefix '%s'"
281 print(fmt
% (full_tag
, tag_prefix
))
282 pieces
["error"] = ("tag '%s' doesn't start with prefix '%s'"
283 % (full_tag
, tag_prefix
))
285 pieces
["closest-tag"] = full_tag
[len(tag_prefix
):]
287 # distance: number of commits since tag
288 pieces
["distance"] = int(mo
.group(2))
290 # commit: short hex revision ID
291 pieces
["short"] = mo
.group(3)
295 pieces
["closest-tag"] = None
296 count_out
, rc
= run_command(GITS
, ["rev-list", "HEAD", "--count"],
298 pieces
["distance"] = int(count_out
) # total number of commits
300 # commit date: see ISO-8601 comment in git_versions_from_keywords()
301 date
= run_command(GITS
, ["show", "-s", "--format=%ci", "HEAD"],
303 pieces
["date"] = date
.strip().replace(" ", "T", 1).replace(" ", "", 1)
308 def plus_or_dot(pieces
):
309 """Return a + if we don't already have one, else return a ."""
310 if "+" in pieces
.get("closest-tag", ""):
315 def render_pep440(pieces
):
316 """Build up version string, with post-release "local version identifier".
318 Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
319 get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
322 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
324 if pieces
["closest-tag"]:
325 rendered
= pieces
["closest-tag"]
326 if pieces
["distance"] or pieces
["dirty"]:
327 rendered
+= plus_or_dot(pieces
)
328 rendered
+= "%d.g%s" % (pieces
["distance"], pieces
["short"])
333 rendered
= "0+untagged.%d.g%s" % (pieces
["distance"],
340 def render_pep440_pre(pieces
):
341 """TAG[.post.devDISTANCE] -- No -dirty.
344 1: no tags. 0.post.devDISTANCE
346 if pieces
["closest-tag"]:
347 rendered
= pieces
["closest-tag"]
348 if pieces
["distance"]:
349 rendered
+= ".post.dev%d" % pieces
["distance"]
352 rendered
= "0.post.dev%d" % pieces
["distance"]
356 def render_pep440_post(pieces
):
357 """TAG[.postDISTANCE[.dev0]+gHEX] .
359 The ".dev0" means dirty. Note that .dev0 sorts backwards
360 (a dirty tree will appear "older" than the corresponding clean one),
361 but you shouldn't be releasing software with -dirty anyways.
364 1: no tags. 0.postDISTANCE[.dev0]
366 if pieces
["closest-tag"]:
367 rendered
= pieces
["closest-tag"]
368 if pieces
["distance"] or pieces
["dirty"]:
369 rendered
+= ".post%d" % pieces
["distance"]
372 rendered
+= plus_or_dot(pieces
)
373 rendered
+= "g%s" % pieces
["short"]
376 rendered
= "0.post%d" % pieces
["distance"]
379 rendered
+= "+g%s" % pieces
["short"]
383 def render_pep440_old(pieces
):
384 """TAG[.postDISTANCE[.dev0]] .
386 The ".dev0" means dirty.
389 1: no tags. 0.postDISTANCE[.dev0]
391 if pieces
["closest-tag"]:
392 rendered
= pieces
["closest-tag"]
393 if pieces
["distance"] or pieces
["dirty"]:
394 rendered
+= ".post%d" % pieces
["distance"]
399 rendered
= "0.post%d" % pieces
["distance"]
405 def render_git_describe(pieces
):
406 """TAG[-DISTANCE-gHEX][-dirty].
408 Like 'git describe --tags --dirty --always'.
411 1: no tags. HEX[-dirty] (note: no 'g' prefix)
413 if pieces
["closest-tag"]:
414 rendered
= pieces
["closest-tag"]
415 if pieces
["distance"]:
416 rendered
+= "-%d-g%s" % (pieces
["distance"], pieces
["short"])
419 rendered
= pieces
["short"]
425 def render_git_describe_long(pieces
):
426 """TAG-DISTANCE-gHEX[-dirty].
428 Like 'git describe --tags --dirty --always -long'.
429 The distance/hash is unconditional.
432 1: no tags. HEX[-dirty] (note: no 'g' prefix)
434 if pieces
["closest-tag"]:
435 rendered
= pieces
["closest-tag"]
436 rendered
+= "-%d-g%s" % (pieces
["distance"], pieces
["short"])
439 rendered
= pieces
["short"]
445 def render(pieces
, style
):
446 """Render the given version pieces into the requested style."""
448 return {"version": "unknown",
449 "full-revisionid": pieces
.get("long"),
451 "error": pieces
["error"],
454 if not style
or style
== "default":
455 style
= "pep440" # the default
457 if style
== "pep440":
458 rendered
= render_pep440(pieces
)
459 elif style
== "pep440-pre":
460 rendered
= render_pep440_pre(pieces
)
461 elif style
== "pep440-post":
462 rendered
= render_pep440_post(pieces
)
463 elif style
== "pep440-old":
464 rendered
= render_pep440_old(pieces
)
465 elif style
== "git-describe":
466 rendered
= render_git_describe(pieces
)
467 elif style
== "git-describe-long":
468 rendered
= render_git_describe_long(pieces
)
470 raise ValueError("unknown style '%s'" % style
)
472 return {"version": rendered
, "full-revisionid": pieces
["long"],
473 "dirty": pieces
["dirty"], "error": None,
474 "date": pieces
.get("date")}
477 def get_versions(): # noqa: C901
478 """Get version information or return default if unable to do so."""
479 # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
480 # __file__, we can work backwards from there to the root. Some
481 # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
482 # case we can only use expanded keywords.
485 verbose
= cfg
.verbose
488 return git_versions_from_keywords(get_keywords(), cfg
.tag_prefix
,
490 except NotThisMethod
:
494 root
= os
.path
.realpath(__file__
)
495 # versionfile_source is the relative path from the top of the source
496 # tree (where the .git directory might live) to this file. Invert
497 # this to find the root from __file__.
498 for i
in cfg
.versionfile_source
.split('/'):
499 root
= os
.path
.dirname(root
)
501 return {"version": "0+unknown", "full-revisionid": None,
503 "error": "unable to find root of source tree",
507 pieces
= git_pieces_from_vcs(cfg
.tag_prefix
, root
, verbose
)
508 return render(pieces
, cfg
.style
)
509 except NotThisMethod
:
513 if cfg
.parentdir_prefix
:
514 return versions_from_parentdir(cfg
.parentdir_prefix
, root
, verbose
)
515 except NotThisMethod
:
518 return {"version": "0+unknown", "full-revisionid": None,
520 "error": "unable to compute version", "date": None}