_version.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. # This file helps to compute a version number in source trees obtained from
  2. # git-archive tarball (such as those provided by githubs download-from-tag
  3. # feature). Distribution tarballs (built by setup.py sdist) and build
  4. # directories (produced by setup.py build) will contain a much shorter file
  5. # that just contains the computed version number.
  6. # This file is released into the public domain. Generated by
  7. # versioneer-0.21 (https://github.com/python-versioneer/python-versioneer)
  8. """Git implementation of _version.py."""
  9. import errno
  10. import os
  11. import re
  12. import subprocess
  13. import sys
  14. from typing import Callable, Dict
  15. def get_keywords():
  16. """Get the keywords needed to look up the version information."""
  17. # these strings will be replaced by git during git-archive.
  18. # setup.py/versioneer.py will grep for the variable names, so they must
  19. # each be defined on a line of their own. _version.py will just call
  20. # get_keywords().
  21. git_refnames = "$Format:%d$"
  22. git_full = "$Format:%H$"
  23. git_date = "$Format:%ci$"
  24. keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
  25. return keywords
  26. class VersioneerConfig:
  27. """Container for Versioneer configuration parameters."""
  28. def get_config():
  29. """Create, populate and return the VersioneerConfig() object."""
  30. # these strings are filled in when 'setup.py versioneer' creates
  31. # _version.py
  32. cfg = VersioneerConfig()
  33. cfg.VCS = "git"
  34. cfg.style = "pep440"
  35. cfg.tag_prefix = "v"
  36. cfg.parentdir_prefix = "redmail-"
  37. cfg.versionfile_source = "redmail/_version.py"
  38. cfg.verbose = False
  39. return cfg
  40. class NotThisMethod(Exception):
  41. """Exception raised if a method is not valid for the current scenario."""
  42. LONG_VERSION_PY: Dict[str, str] = {}
  43. HANDLERS: Dict[str, Dict[str, Callable]] = {}
  44. def register_vcs_handler(vcs, method): # decorator
  45. """Create decorator to mark a method as the handler of a VCS."""
  46. def decorate(f):
  47. """Store f in HANDLERS[vcs][method]."""
  48. if vcs not in HANDLERS:
  49. HANDLERS[vcs] = {}
  50. HANDLERS[vcs][method] = f
  51. return f
  52. return decorate
  53. def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
  54. env=None):
  55. """Call the given command(s)."""
  56. assert isinstance(commands, list)
  57. process = None
  58. for command in commands:
  59. try:
  60. dispcmd = str([command] + args)
  61. # remember shell=False, so use git.cmd on windows, not just git
  62. process = subprocess.Popen([command] + args, cwd=cwd, env=env,
  63. stdout=subprocess.PIPE,
  64. stderr=(subprocess.PIPE if hide_stderr
  65. else None))
  66. break
  67. except OSError:
  68. e = sys.exc_info()[1]
  69. if e.errno == errno.ENOENT:
  70. continue
  71. if verbose:
  72. print("unable to run %s" % dispcmd)
  73. print(e)
  74. return None, None
  75. else:
  76. if verbose:
  77. print("unable to find command, tried %s" % (commands,))
  78. return None, None
  79. stdout = process.communicate()[0].strip().decode()
  80. if process.returncode != 0:
  81. if verbose:
  82. print("unable to run %s (error)" % dispcmd)
  83. print("stdout was %s" % stdout)
  84. return None, process.returncode
  85. return stdout, process.returncode
  86. def versions_from_parentdir(parentdir_prefix, root, verbose):
  87. """Try to determine the version from the parent directory name.
  88. Source tarballs conventionally unpack into a directory that includes both
  89. the project name and a version string. We will also support searching up
  90. two directory levels for an appropriately named parent directory
  91. """
  92. rootdirs = []
  93. for _ in range(3):
  94. dirname = os.path.basename(root)
  95. if dirname.startswith(parentdir_prefix):
  96. return {"version": dirname[len(parentdir_prefix):],
  97. "full-revisionid": None,
  98. "dirty": False, "error": None, "date": None}
  99. rootdirs.append(root)
  100. root = os.path.dirname(root) # up a level
  101. if verbose:
  102. print("Tried directories %s but none started with prefix %s" %
  103. (str(rootdirs), parentdir_prefix))
  104. raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
  105. @register_vcs_handler("git", "get_keywords")
  106. def git_get_keywords(versionfile_abs):
  107. """Extract version information from the given file."""
  108. # the code embedded in _version.py can just fetch the value of these
  109. # keywords. When used from setup.py, we don't want to import _version.py,
  110. # so we do it with a regexp instead. This function is not used from
  111. # _version.py.
  112. keywords = {}
  113. try:
  114. with open(versionfile_abs, "r") as fobj:
  115. for line in fobj:
  116. if line.strip().startswith("git_refnames ="):
  117. mo = re.search(r'=\s*"(.*)"', line)
  118. if mo:
  119. keywords["refnames"] = mo.group(1)
  120. if line.strip().startswith("git_full ="):
  121. mo = re.search(r'=\s*"(.*)"', line)
  122. if mo:
  123. keywords["full"] = mo.group(1)
  124. if line.strip().startswith("git_date ="):
  125. mo = re.search(r'=\s*"(.*)"', line)
  126. if mo:
  127. keywords["date"] = mo.group(1)
  128. except OSError:
  129. pass
  130. return keywords
  131. @register_vcs_handler("git", "keywords")
  132. def git_versions_from_keywords(keywords, tag_prefix, verbose):
  133. """Get version information from git keywords."""
  134. if "refnames" not in keywords:
  135. raise NotThisMethod("Short version file found")
  136. date = keywords.get("date")
  137. if date is not None:
  138. # Use only the last line. Previous lines may contain GPG signature
  139. # information.
  140. date = date.splitlines()[-1]
  141. # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
  142. # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
  143. # -like" string, which we must then edit to make compliant), because
  144. # it's been around since git-1.5.3, and it's too difficult to
  145. # discover which version we're using, or to work around using an
  146. # older one.
  147. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
  148. refnames = keywords["refnames"].strip()
  149. if refnames.startswith("$Format"):
  150. if verbose:
  151. print("keywords are unexpanded, not using")
  152. raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
  153. refs = {r.strip() for r in refnames.strip("()").split(",")}
  154. # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
  155. # just "foo-1.0". If we see a "tag: " prefix, prefer those.
  156. TAG = "tag: "
  157. tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
  158. if not tags:
  159. # Either we're using git < 1.8.3, or there really are no tags. We use
  160. # a heuristic: assume all version tags have a digit. The old git %d
  161. # expansion behaves like git log --decorate=short and strips out the
  162. # refs/heads/ and refs/tags/ prefixes that would let us distinguish
  163. # between branches and tags. By ignoring refnames without digits, we
  164. # filter out many common branch names like "release" and
  165. # "stabilization", as well as "HEAD" and "master".
  166. tags = {r for r in refs if re.search(r'\d', r)}
  167. if verbose:
  168. print("discarding '%s', no digits" % ",".join(refs - tags))
  169. if verbose:
  170. print("likely tags: %s" % ",".join(sorted(tags)))
  171. for ref in sorted(tags):
  172. # sorting will prefer e.g. "2.0" over "2.0rc1"
  173. if ref.startswith(tag_prefix):
  174. r = ref[len(tag_prefix):]
  175. # Filter out refs that exactly match prefix or that don't start
  176. # with a number once the prefix is stripped (mostly a concern
  177. # when prefix is '')
  178. if not re.match(r'\d', r):
  179. continue
  180. if verbose:
  181. print("picking %s" % r)
  182. return {"version": r,
  183. "full-revisionid": keywords["full"].strip(),
  184. "dirty": False, "error": None,
  185. "date": date}
  186. # no suitable tags, so version is "0+unknown", but full hex is still there
  187. if verbose:
  188. print("no suitable tags, using unknown + full revision id")
  189. return {"version": "0+unknown",
  190. "full-revisionid": keywords["full"].strip(),
  191. "dirty": False, "error": "no suitable tags", "date": None}
  192. @register_vcs_handler("git", "pieces_from_vcs")
  193. def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
  194. """Get version from 'git describe' in the root of the source tree.
  195. This only gets called if the git-archive 'subst' keywords were *not*
  196. expanded, and _version.py hasn't already been rewritten with a short
  197. version string, meaning we're inside a checked out source tree.
  198. """
  199. GITS = ["git"]
  200. TAG_PREFIX_REGEX = "*"
  201. if sys.platform == "win32":
  202. GITS = ["git.cmd", "git.exe"]
  203. TAG_PREFIX_REGEX = r"\*"
  204. TAG_PREFIX_REGEX = r"*" #! NOTE: For some reason \* does not work
  205. _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
  206. hide_stderr=True)
  207. if rc != 0:
  208. if verbose:
  209. print("Directory %s not under git control" % root)
  210. raise NotThisMethod("'git rev-parse --git-dir' returned error")
  211. # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
  212. # if there isn't one, this yields HEX[-dirty] (no NUM)
  213. describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty",
  214. "--always", "--long",
  215. "--match",
  216. "%s%s" % (tag_prefix, TAG_PREFIX_REGEX)],
  217. cwd=root)
  218. # --long was added in git-1.5.5
  219. if describe_out is None:
  220. raise NotThisMethod("'git describe' failed")
  221. describe_out = describe_out.strip()
  222. full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
  223. if full_out is None:
  224. raise NotThisMethod("'git rev-parse' failed")
  225. full_out = full_out.strip()
  226. pieces = {}
  227. pieces["long"] = full_out
  228. pieces["short"] = full_out[:7] # maybe improved later
  229. pieces["error"] = None
  230. branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
  231. cwd=root)
  232. # --abbrev-ref was added in git-1.6.3
  233. if rc != 0 or branch_name is None:
  234. raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
  235. branch_name = branch_name.strip()
  236. if branch_name == "HEAD":
  237. # If we aren't exactly on a branch, pick a branch which represents
  238. # the current commit. If all else fails, we are on a branchless
  239. # commit.
  240. branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
  241. # --contains was added in git-1.5.4
  242. if rc != 0 or branches is None:
  243. raise NotThisMethod("'git branch --contains' returned error")
  244. branches = branches.split("\n")
  245. # Remove the first line if we're running detached
  246. if "(" in branches[0]:
  247. branches.pop(0)
  248. # Strip off the leading "* " from the list of branches.
  249. branches = [branch[2:] for branch in branches]
  250. if "master" in branches:
  251. branch_name = "master"
  252. elif not branches:
  253. branch_name = None
  254. else:
  255. # Pick the first branch that is returned. Good or bad.
  256. branch_name = branches[0]
  257. pieces["branch"] = branch_name
  258. # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
  259. # TAG might have hyphens.
  260. git_describe = describe_out
  261. # look for -dirty suffix
  262. dirty = git_describe.endswith("-dirty")
  263. pieces["dirty"] = dirty
  264. if dirty:
  265. git_describe = git_describe[:git_describe.rindex("-dirty")]
  266. # now we have TAG-NUM-gHEX or HEX
  267. if "-" in git_describe:
  268. # TAG-NUM-gHEX
  269. mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
  270. if not mo:
  271. # unparsable. Maybe git-describe is misbehaving?
  272. pieces["error"] = ("unable to parse git-describe output: '%s'"
  273. % describe_out)
  274. return pieces
  275. # tag
  276. full_tag = mo.group(1)
  277. if not full_tag.startswith(tag_prefix):
  278. if verbose:
  279. fmt = "tag '%s' doesn't start with prefix '%s'"
  280. print(fmt % (full_tag, tag_prefix))
  281. pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
  282. % (full_tag, tag_prefix))
  283. return pieces
  284. pieces["closest-tag"] = full_tag[len(tag_prefix):]
  285. # distance: number of commits since tag
  286. pieces["distance"] = int(mo.group(2))
  287. # commit: short hex revision ID
  288. pieces["short"] = mo.group(3)
  289. else:
  290. # HEX: no tags
  291. pieces["closest-tag"] = None
  292. count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
  293. pieces["distance"] = int(count_out) # total number of commits
  294. # commit date: see ISO-8601 comment in git_versions_from_keywords()
  295. date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
  296. # Use only the last line. Previous lines may contain GPG signature
  297. # information.
  298. date = date.splitlines()[-1]
  299. pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
  300. return pieces
  301. def plus_or_dot(pieces):
  302. """Return a + if we don't already have one, else return a ."""
  303. if "+" in pieces.get("closest-tag", ""):
  304. return "."
  305. return "+"
  306. def render_pep440(pieces):
  307. """Build up version string, with post-release "local version identifier".
  308. Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
  309. get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
  310. Exceptions:
  311. 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
  312. """
  313. if pieces["closest-tag"]:
  314. rendered = pieces["closest-tag"]
  315. if pieces["distance"] or pieces["dirty"]:
  316. rendered += plus_or_dot(pieces)
  317. rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
  318. if pieces["dirty"]:
  319. rendered += ".dirty"
  320. else:
  321. # exception #1
  322. rendered = "0+untagged.%d.g%s" % (pieces["distance"],
  323. pieces["short"])
  324. if pieces["dirty"]:
  325. rendered += ".dirty"
  326. return rendered
  327. def render_pep440_branch(pieces):
  328. """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
  329. The ".dev0" means not master branch. Note that .dev0 sorts backwards
  330. (a feature branch will appear "older" than the master branch).
  331. Exceptions:
  332. 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
  333. """
  334. if pieces["closest-tag"]:
  335. rendered = pieces["closest-tag"]
  336. if pieces["distance"] or pieces["dirty"]:
  337. if pieces["branch"] != "master":
  338. rendered += ".dev0"
  339. rendered += plus_or_dot(pieces)
  340. rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
  341. if pieces["dirty"]:
  342. rendered += ".dirty"
  343. else:
  344. # exception #1
  345. rendered = "0"
  346. if pieces["branch"] != "master":
  347. rendered += ".dev0"
  348. rendered += "+untagged.%d.g%s" % (pieces["distance"],
  349. pieces["short"])
  350. if pieces["dirty"]:
  351. rendered += ".dirty"
  352. return rendered
  353. def pep440_split_post(ver):
  354. """Split pep440 version string at the post-release segment.
  355. Returns the release segments before the post-release and the
  356. post-release version number (or -1 if no post-release segment is present).
  357. """
  358. vc = str.split(ver, ".post")
  359. return vc[0], int(vc[1] or 0) if len(vc) == 2 else None
  360. def render_pep440_pre(pieces):
  361. """TAG[.postN.devDISTANCE] -- No -dirty.
  362. Exceptions:
  363. 1: no tags. 0.post0.devDISTANCE
  364. """
  365. if pieces["closest-tag"]:
  366. if pieces["distance"]:
  367. # update the post release segment
  368. tag_version, post_version = pep440_split_post(pieces["closest-tag"])
  369. rendered = tag_version
  370. if post_version is not None:
  371. rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"])
  372. else:
  373. rendered += ".post0.dev%d" % (pieces["distance"])
  374. else:
  375. # no commits, use the tag as the version
  376. rendered = pieces["closest-tag"]
  377. else:
  378. # exception #1
  379. rendered = "0.post0.dev%d" % pieces["distance"]
  380. return rendered
  381. def render_pep440_post(pieces):
  382. """TAG[.postDISTANCE[.dev0]+gHEX] .
  383. The ".dev0" means dirty. Note that .dev0 sorts backwards
  384. (a dirty tree will appear "older" than the corresponding clean one),
  385. but you shouldn't be releasing software with -dirty anyways.
  386. Exceptions:
  387. 1: no tags. 0.postDISTANCE[.dev0]
  388. """
  389. if pieces["closest-tag"]:
  390. rendered = pieces["closest-tag"]
  391. if pieces["distance"] or pieces["dirty"]:
  392. rendered += ".post%d" % pieces["distance"]
  393. if pieces["dirty"]:
  394. rendered += ".dev0"
  395. rendered += plus_or_dot(pieces)
  396. rendered += "g%s" % pieces["short"]
  397. else:
  398. # exception #1
  399. rendered = "0.post%d" % pieces["distance"]
  400. if pieces["dirty"]:
  401. rendered += ".dev0"
  402. rendered += "+g%s" % pieces["short"]
  403. return rendered
  404. def render_pep440_post_branch(pieces):
  405. """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
  406. The ".dev0" means not master branch.
  407. Exceptions:
  408. 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
  409. """
  410. if pieces["closest-tag"]:
  411. rendered = pieces["closest-tag"]
  412. if pieces["distance"] or pieces["dirty"]:
  413. rendered += ".post%d" % pieces["distance"]
  414. if pieces["branch"] != "master":
  415. rendered += ".dev0"
  416. rendered += plus_or_dot(pieces)
  417. rendered += "g%s" % pieces["short"]
  418. if pieces["dirty"]:
  419. rendered += ".dirty"
  420. else:
  421. # exception #1
  422. rendered = "0.post%d" % pieces["distance"]
  423. if pieces["branch"] != "master":
  424. rendered += ".dev0"
  425. rendered += "+g%s" % pieces["short"]
  426. if pieces["dirty"]:
  427. rendered += ".dirty"
  428. return rendered
  429. def render_pep440_old(pieces):
  430. """TAG[.postDISTANCE[.dev0]] .
  431. The ".dev0" means dirty.
  432. Exceptions:
  433. 1: no tags. 0.postDISTANCE[.dev0]
  434. """
  435. if pieces["closest-tag"]:
  436. rendered = pieces["closest-tag"]
  437. if pieces["distance"] or pieces["dirty"]:
  438. rendered += ".post%d" % pieces["distance"]
  439. if pieces["dirty"]:
  440. rendered += ".dev0"
  441. else:
  442. # exception #1
  443. rendered = "0.post%d" % pieces["distance"]
  444. if pieces["dirty"]:
  445. rendered += ".dev0"
  446. return rendered
  447. def render_git_describe(pieces):
  448. """TAG[-DISTANCE-gHEX][-dirty].
  449. Like 'git describe --tags --dirty --always'.
  450. Exceptions:
  451. 1: no tags. HEX[-dirty] (note: no 'g' prefix)
  452. """
  453. if pieces["closest-tag"]:
  454. rendered = pieces["closest-tag"]
  455. if pieces["distance"]:
  456. rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
  457. else:
  458. # exception #1
  459. rendered = pieces["short"]
  460. if pieces["dirty"]:
  461. rendered += "-dirty"
  462. return rendered
  463. def render_git_describe_long(pieces):
  464. """TAG-DISTANCE-gHEX[-dirty].
  465. Like 'git describe --tags --dirty --always -long'.
  466. The distance/hash is unconditional.
  467. Exceptions:
  468. 1: no tags. HEX[-dirty] (note: no 'g' prefix)
  469. """
  470. if pieces["closest-tag"]:
  471. rendered = pieces["closest-tag"]
  472. rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
  473. else:
  474. # exception #1
  475. rendered = pieces["short"]
  476. if pieces["dirty"]:
  477. rendered += "-dirty"
  478. return rendered
  479. def render(pieces, style):
  480. """Render the given version pieces into the requested style."""
  481. if pieces["error"]:
  482. return {"version": "unknown",
  483. "full-revisionid": pieces.get("long"),
  484. "dirty": None,
  485. "error": pieces["error"],
  486. "date": None}
  487. if not style or style == "default":
  488. style = "pep440" # the default
  489. if style == "pep440":
  490. rendered = render_pep440(pieces)
  491. elif style == "pep440-branch":
  492. rendered = render_pep440_branch(pieces)
  493. elif style == "pep440-pre":
  494. rendered = render_pep440_pre(pieces)
  495. elif style == "pep440-post":
  496. rendered = render_pep440_post(pieces)
  497. elif style == "pep440-post-branch":
  498. rendered = render_pep440_post_branch(pieces)
  499. elif style == "pep440-old":
  500. rendered = render_pep440_old(pieces)
  501. elif style == "git-describe":
  502. rendered = render_git_describe(pieces)
  503. elif style == "git-describe-long":
  504. rendered = render_git_describe_long(pieces)
  505. else:
  506. raise ValueError("unknown style '%s'" % style)
  507. return {"version": rendered, "full-revisionid": pieces["long"],
  508. "dirty": pieces["dirty"], "error": None,
  509. "date": pieces.get("date")}
  510. def get_versions():
  511. """Get version information or return default if unable to do so."""
  512. # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
  513. # __file__, we can work backwards from there to the root. Some
  514. # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
  515. # case we can only use expanded keywords.
  516. cfg = get_config()
  517. verbose = cfg.verbose
  518. try:
  519. return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
  520. verbose)
  521. except NotThisMethod:
  522. pass
  523. try:
  524. root = os.path.realpath(__file__)
  525. # versionfile_source is the relative path from the top of the source
  526. # tree (where the .git directory might live) to this file. Invert
  527. # this to find the root from __file__.
  528. for _ in cfg.versionfile_source.split('/'):
  529. root = os.path.dirname(root)
  530. except NameError:
  531. return {"version": "0+unknown", "full-revisionid": None,
  532. "dirty": None,
  533. "error": "unable to find root of source tree",
  534. "date": None}
  535. try:
  536. pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
  537. return render(pieces, cfg.style)
  538. except NotThisMethod:
  539. pass
  540. try:
  541. if cfg.parentdir_prefix:
  542. return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
  543. except NotThisMethod:
  544. pass
  545. return {"version": "0+unknown", "full-revisionid": None,
  546. "dirty": None,
  547. "error": "unable to compute version", "date": None}