_version.py 23 KB

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