Coverage for gcsfs/_version.py: 41%

358 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2026-04-20 18:41 -0400

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 

7# This file is released into the public domain. 

8# Generated by versioneer-0.29 

9# https://github.com/python-versioneer/python-versioneer 

10 

11"""Git implementation of _version.py.""" 

12 

13import errno 

14import functools 

15import os 

16import re 

17import subprocess 

18import sys 

19from typing import Any, Callable, Dict, List, Optional, Tuple 

20 

21 

22def get_keywords() -> Dict[str, str]: 

23 """Get the keywords needed to look up the version information.""" 

24 # these strings will be replaced by git during git-archive. 

25 # setup.py/versioneer.py will grep for the variable names, so they must 

26 # each be defined on a line of their own. _version.py will just call 

27 # get_keywords(). 

28 git_refnames = "$Format:%d$" 

29 git_full = "$Format:%H$" 

30 git_date = "$Format:%ci$" 

31 keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} 

32 return keywords 

33 

34 

35class VersioneerConfig: 

36 """Container for Versioneer configuration parameters.""" 

37 

38 VCS: str 

39 style: str 

40 tag_prefix: str 

41 parentdir_prefix: str 

42 versionfile_source: str 

43 verbose: bool 

44 

45 

46def get_config() -> VersioneerConfig: 

47 """Create, populate and return the VersioneerConfig() object.""" 

48 # these strings are filled in when 'setup.py versioneer' creates 

49 # _version.py 

50 cfg = VersioneerConfig() 

51 cfg.VCS = "git" 

52 cfg.style = "pep440" 

53 cfg.tag_prefix = "" 

54 cfg.parentdir_prefix = "None" 

55 cfg.versionfile_source = "gcsfs/_version.py" 

56 cfg.verbose = False 

57 return cfg 

58 

59 

60class NotThisMethod(Exception): 

61 """Exception raised if a method is not valid for the current scenario.""" 

62 

63 

64LONG_VERSION_PY: Dict[str, str] = {} 

65HANDLERS: Dict[str, Dict[str, Callable]] = {} 

66 

67 

68def register_vcs_handler(vcs: str, method: str) -> Callable: # decorator 

69 """Create decorator to mark a method as the handler of a VCS.""" 

70 

71 def decorate(f: Callable) -> Callable: 

72 """Store f in HANDLERS[vcs][method].""" 

73 if vcs not in HANDLERS: 

74 HANDLERS[vcs] = {} 

75 HANDLERS[vcs][method] = f 

76 return f 

77 

78 return decorate 

79 

80 

81def run_command( 

82 commands: List[str], 

83 args: List[str], 

84 cwd: Optional[str] = None, 

85 verbose: bool = False, 

86 hide_stderr: bool = False, 

87 env: Optional[Dict[str, str]] = None, 

88) -> Tuple[Optional[str], Optional[int]]: 

89 """Call the given command(s).""" 

90 assert isinstance(commands, list) 

91 process = None 

92 

93 popen_kwargs: Dict[str, Any] = {} 

94 if sys.platform == "win32": 

95 # This hides the console window if pythonw.exe is used 

96 startupinfo = subprocess.STARTUPINFO() 

97 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 

98 popen_kwargs["startupinfo"] = startupinfo 

99 

100 for command in commands: 

101 try: 

102 dispcmd = str([command] + args) 

103 # remember shell=False, so use git.cmd on windows, not just git 

104 process = subprocess.Popen( 

105 [command] + args, 

106 cwd=cwd, 

107 env=env, 

108 stdout=subprocess.PIPE, 

109 stderr=(subprocess.PIPE if hide_stderr else None), 

110 **popen_kwargs, 

111 ) 

112 break 

113 except OSError as e: 

114 if e.errno == errno.ENOENT: 

115 continue 

116 if verbose: 

117 print("unable to run %s" % dispcmd) 

118 print(e) 

119 return None, None 

120 else: 

121 if verbose: 

122 print("unable to find command, tried %s" % (commands,)) 

123 return None, None 

124 stdout = process.communicate()[0].strip().decode() 

125 if process.returncode != 0: 

126 if verbose: 

127 print("unable to run %s (error)" % dispcmd) 

128 print("stdout was %s" % stdout) 

129 return None, process.returncode 

130 return stdout, process.returncode 

131 

132 

133def versions_from_parentdir( 

134 parentdir_prefix: str, 

135 root: str, 

136 verbose: bool, 

137) -> Dict[str, Any]: 

138 """Try to determine the version from the parent directory name. 

139 

140 Source tarballs conventionally unpack into a directory that includes both 

141 the project name and a version string. We will also support searching up 

142 two directory levels for an appropriately named parent directory 

143 """ 

144 rootdirs = [] 

145 

146 for _ in range(3): 

147 dirname = os.path.basename(root) 

148 if dirname.startswith(parentdir_prefix): 

149 return { 

150 "version": dirname[len(parentdir_prefix) :], 

151 "full-revisionid": None, 

152 "dirty": False, 

153 "error": None, 

154 "date": None, 

155 } 

156 rootdirs.append(root) 

157 root = os.path.dirname(root) # up a level 

158 

159 if verbose: 

160 print( 

161 "Tried directories %s but none started with prefix %s" 

162 % (str(rootdirs), parentdir_prefix) 

163 ) 

164 raise NotThisMethod("rootdir doesn't start with parentdir_prefix") 

165 

166 

167@register_vcs_handler("git", "get_keywords") 

168def git_get_keywords(versionfile_abs: str) -> Dict[str, str]: 

169 """Extract version information from the given file.""" 

170 # the code embedded in _version.py can just fetch the value of these 

171 # keywords. When used from setup.py, we don't want to import _version.py, 

172 # so we do it with a regexp instead. This function is not used from 

173 # _version.py. 

174 keywords: Dict[str, str] = {} 

175 try: 

176 with open(versionfile_abs, "r") as fobj: 

177 for line in fobj: 

178 if line.strip().startswith("git_refnames ="): 

179 mo = re.search(r'=\s*"(.*)"', line) 

180 if mo: 

181 keywords["refnames"] = mo.group(1) 

182 if line.strip().startswith("git_full ="): 

183 mo = re.search(r'=\s*"(.*)"', line) 

184 if mo: 

185 keywords["full"] = mo.group(1) 

186 if line.strip().startswith("git_date ="): 

187 mo = re.search(r'=\s*"(.*)"', line) 

188 if mo: 

189 keywords["date"] = mo.group(1) 

190 except OSError: 

191 pass 

192 return keywords 

193 

194 

195@register_vcs_handler("git", "keywords") 

196def git_versions_from_keywords( 

197 keywords: Dict[str, str], 

198 tag_prefix: str, 

199 verbose: bool, 

200) -> Dict[str, Any]: 

201 """Get version information from git keywords.""" 

202 if "refnames" not in keywords: 

203 raise NotThisMethod("Short version file found") 

204 date = keywords.get("date") 

205 if date is not None: 

206 # Use only the last line. Previous lines may contain GPG signature 

207 # information. 

208 date = date.splitlines()[-1] 

209 

210 # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant 

211 # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 

212 # -like" string, which we must then edit to make compliant), because 

213 # it's been around since git-1.5.3, and it's too difficult to 

214 # discover which version we're using, or to work around using an 

215 # older one. 

216 date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 

217 refnames = keywords["refnames"].strip() 

218 if refnames.startswith("$Format"): 

219 if verbose: 

220 print("keywords are unexpanded, not using") 

221 raise NotThisMethod("unexpanded keywords, not a git-archive tarball") 

222 refs = {r.strip() for r in refnames.strip("()").split(",")} 

223 # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of 

224 # just "foo-1.0". If we see a "tag: " prefix, prefer those. 

225 TAG = "tag: " 

226 tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} 

227 if not tags: 

228 # Either we're using git < 1.8.3, or there really are no tags. We use 

229 # a heuristic: assume all version tags have a digit. The old git %d 

230 # expansion behaves like git log --decorate=short and strips out the 

231 # refs/heads/ and refs/tags/ prefixes that would let us distinguish 

232 # between branches and tags. By ignoring refnames without digits, we 

233 # filter out many common branch names like "release" and 

234 # "stabilization", as well as "HEAD" and "master". 

235 tags = {r for r in refs if re.search(r"\d", r)} 

236 if verbose: 

237 print("discarding '%s', no digits" % ",".join(refs - tags)) 

238 if verbose: 

239 print("likely tags: %s" % ",".join(sorted(tags))) 

240 for ref in sorted(tags): 

241 # sorting will prefer e.g. "2.0" over "2.0rc1" 

242 if ref.startswith(tag_prefix): 

243 r = ref[len(tag_prefix) :] 

244 # Filter out refs that exactly match prefix or that don't start 

245 # with a number once the prefix is stripped (mostly a concern 

246 # when prefix is '') 

247 if not re.match(r"\d", r): 

248 continue 

249 if verbose: 

250 print("picking %s" % r) 

251 return { 

252 "version": r, 

253 "full-revisionid": keywords["full"].strip(), 

254 "dirty": False, 

255 "error": None, 

256 "date": date, 

257 } 

258 # no suitable tags, so version is "0+unknown", but full hex is still there 

259 if verbose: 

260 print("no suitable tags, using unknown + full revision id") 

261 return { 

262 "version": "0+unknown", 

263 "full-revisionid": keywords["full"].strip(), 

264 "dirty": False, 

265 "error": "no suitable tags", 

266 "date": None, 

267 } 

268 

269 

270@register_vcs_handler("git", "pieces_from_vcs") 

271def git_pieces_from_vcs( 

272 tag_prefix: str, root: str, verbose: bool, runner: Callable = run_command 

273) -> Dict[str, Any]: 

274 """Get version from 'git describe' in the root of the source tree. 

275 

276 This only gets called if the git-archive 'subst' keywords were *not* 

277 expanded, and _version.py hasn't already been rewritten with a short 

278 version string, meaning we're inside a checked out source tree. 

279 """ 

280 GITS = ["git"] 

281 if sys.platform == "win32": 

282 GITS = ["git.cmd", "git.exe"] 

283 

284 # GIT_DIR can interfere with correct operation of Versioneer. 

285 # It may be intended to be passed to the Versioneer-versioned project, 

286 # but that should not change where we get our version from. 

287 env = os.environ.copy() 

288 env.pop("GIT_DIR", None) 

289 runner = functools.partial(runner, env=env) 

290 

291 _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=not verbose) 

292 if rc != 0: 

293 if verbose: 

294 print("Directory %s not under git control" % root) 

295 raise NotThisMethod("'git rev-parse --git-dir' returned error") 

296 

297 # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] 

298 # if there isn't one, this yields HEX[-dirty] (no NUM) 

299 describe_out, rc = runner( 

300 GITS, 

301 [ 

302 "describe", 

303 "--tags", 

304 "--dirty", 

305 "--always", 

306 "--long", 

307 "--match", 

308 f"{tag_prefix}[[:digit:]]*", 

309 ], 

310 cwd=root, 

311 ) 

312 # --long was added in git-1.5.5 

313 if describe_out is None: 

314 raise NotThisMethod("'git describe' failed") 

315 describe_out = describe_out.strip() 

316 full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) 

317 if full_out is None: 

318 raise NotThisMethod("'git rev-parse' failed") 

319 full_out = full_out.strip() 

320 

321 pieces: Dict[str, Any] = {} 

322 pieces["long"] = full_out 

323 pieces["short"] = full_out[:7] # maybe improved later 

324 pieces["error"] = None 

325 

326 branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) 

327 # --abbrev-ref was added in git-1.6.3 

328 if rc != 0 or branch_name is None: 

329 raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") 

330 branch_name = branch_name.strip() 

331 

332 if branch_name == "HEAD": 

333 # If we aren't exactly on a branch, pick a branch which represents 

334 # the current commit. If all else fails, we are on a branchless 

335 # commit. 

336 branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) 

337 # --contains was added in git-1.5.4 

338 if rc != 0 or branches is None: 

339 raise NotThisMethod("'git branch --contains' returned error") 

340 branches = branches.split("\n") 

341 

342 # Remove the first line if we're running detached 

343 if "(" in branches[0]: 

344 branches.pop(0) 

345 

346 # Strip off the leading "* " from the list of branches. 

347 branches = [branch[2:] for branch in branches] 

348 if "master" in branches: 

349 branch_name = "master" 

350 elif not branches: 

351 branch_name = None 

352 else: 

353 # Pick the first branch that is returned. Good or bad. 

354 branch_name = branches[0] 

355 

356 pieces["branch"] = branch_name 

357 

358 # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] 

359 # TAG might have hyphens. 

360 git_describe = describe_out 

361 

362 # look for -dirty suffix 

363 dirty = git_describe.endswith("-dirty") 

364 pieces["dirty"] = dirty 

365 if dirty: 

366 git_describe = git_describe[: git_describe.rindex("-dirty")] 

367 

368 # now we have TAG-NUM-gHEX or HEX 

369 

370 if "-" in git_describe: 

371 # TAG-NUM-gHEX 

372 mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) 

373 if not mo: 

374 # unparsable. Maybe git-describe is misbehaving? 

375 pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out 

376 return pieces 

377 

378 # tag 

379 full_tag = mo.group(1) 

380 if not full_tag.startswith(tag_prefix): 

381 if verbose: 

382 fmt = "tag '%s' doesn't start with prefix '%s'" 

383 print(fmt % (full_tag, tag_prefix)) 

384 pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( 

385 full_tag, 

386 tag_prefix, 

387 ) 

388 return pieces 

389 pieces["closest-tag"] = full_tag[len(tag_prefix) :] 

390 

391 # distance: number of commits since tag 

392 pieces["distance"] = int(mo.group(2)) 

393 

394 # commit: short hex revision ID 

395 pieces["short"] = mo.group(3) 

396 

397 else: 

398 # HEX: no tags 

399 pieces["closest-tag"] = None 

400 out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) 

401 pieces["distance"] = len(out.split()) # total number of commits 

402 

403 # commit date: see ISO-8601 comment in git_versions_from_keywords() 

404 date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() 

405 # Use only the last line. Previous lines may contain GPG signature 

406 # information. 

407 date = date.splitlines()[-1] 

408 pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 

409 

410 return pieces 

411 

412 

413def plus_or_dot(pieces: Dict[str, Any]) -> str: 

414 """Return a + if we don't already have one, else return a .""" 

415 if "+" in pieces.get("closest-tag", ""): 

416 return "." 

417 return "+" 

418 

419 

420def render_pep440(pieces: Dict[str, Any]) -> str: 

421 """Build up version string, with post-release "local version identifier". 

422 

423 Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you 

424 get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty 

425 

426 Exceptions: 

427 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] 

428 """ 

429 if pieces["closest-tag"]: 

430 rendered = pieces["closest-tag"] 

431 if pieces["distance"] or pieces["dirty"]: 

432 rendered += plus_or_dot(pieces) 

433 rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 

434 if pieces["dirty"]: 

435 rendered += ".dirty" 

436 else: 

437 # exception #1 

438 rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) 

439 if pieces["dirty"]: 

440 rendered += ".dirty" 

441 return rendered 

442 

443 

444def render_pep440_branch(pieces: Dict[str, Any]) -> str: 

445 """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . 

446 

447 The ".dev0" means not master branch. Note that .dev0 sorts backwards 

448 (a feature branch will appear "older" than the master branch). 

449 

450 Exceptions: 

451 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] 

452 """ 

453 if pieces["closest-tag"]: 

454 rendered = pieces["closest-tag"] 

455 if pieces["distance"] or pieces["dirty"]: 

456 if pieces["branch"] != "master": 

457 rendered += ".dev0" 

458 rendered += plus_or_dot(pieces) 

459 rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 

460 if pieces["dirty"]: 

461 rendered += ".dirty" 

462 else: 

463 # exception #1 

464 rendered = "0" 

465 if pieces["branch"] != "master": 

466 rendered += ".dev0" 

467 rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) 

468 if pieces["dirty"]: 

469 rendered += ".dirty" 

470 return rendered 

471 

472 

473def pep440_split_post(ver: str) -> Tuple[str, Optional[int]]: 

474 """Split pep440 version string at the post-release segment. 

475 

476 Returns the release segments before the post-release and the 

477 post-release version number (or -1 if no post-release segment is present). 

478 """ 

479 vc = str.split(ver, ".post") 

480 return vc[0], int(vc[1] or 0) if len(vc) == 2 else None 

481 

482 

483def render_pep440_pre(pieces: Dict[str, Any]) -> str: 

484 """TAG[.postN.devDISTANCE] -- No -dirty. 

485 

486 Exceptions: 

487 1: no tags. 0.post0.devDISTANCE 

488 """ 

489 if pieces["closest-tag"]: 

490 if pieces["distance"]: 

491 # update the post release segment 

492 tag_version, post_version = pep440_split_post(pieces["closest-tag"]) 

493 rendered = tag_version 

494 if post_version is not None: 

495 rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) 

496 else: 

497 rendered += ".post0.dev%d" % (pieces["distance"]) 

498 else: 

499 # no commits, use the tag as the version 

500 rendered = pieces["closest-tag"] 

501 else: 

502 # exception #1 

503 rendered = "0.post0.dev%d" % pieces["distance"] 

504 return rendered 

505 

506 

507def render_pep440_post(pieces: Dict[str, Any]) -> str: 

508 """TAG[.postDISTANCE[.dev0]+gHEX] . 

509 

510 The ".dev0" means dirty. Note that .dev0 sorts backwards 

511 (a dirty tree will appear "older" than the corresponding clean one), 

512 but you shouldn't be releasing software with -dirty anyways. 

513 

514 Exceptions: 

515 1: no tags. 0.postDISTANCE[.dev0] 

516 """ 

517 if pieces["closest-tag"]: 

518 rendered = pieces["closest-tag"] 

519 if pieces["distance"] or pieces["dirty"]: 

520 rendered += ".post%d" % pieces["distance"] 

521 if pieces["dirty"]: 

522 rendered += ".dev0" 

523 rendered += plus_or_dot(pieces) 

524 rendered += "g%s" % pieces["short"] 

525 else: 

526 # exception #1 

527 rendered = "0.post%d" % pieces["distance"] 

528 if pieces["dirty"]: 

529 rendered += ".dev0" 

530 rendered += "+g%s" % pieces["short"] 

531 return rendered 

532 

533 

534def render_pep440_post_branch(pieces: Dict[str, Any]) -> str: 

535 """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . 

536 

537 The ".dev0" means not master branch. 

538 

539 Exceptions: 

540 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] 

541 """ 

542 if pieces["closest-tag"]: 

543 rendered = pieces["closest-tag"] 

544 if pieces["distance"] or pieces["dirty"]: 

545 rendered += ".post%d" % pieces["distance"] 

546 if pieces["branch"] != "master": 

547 rendered += ".dev0" 

548 rendered += plus_or_dot(pieces) 

549 rendered += "g%s" % pieces["short"] 

550 if pieces["dirty"]: 

551 rendered += ".dirty" 

552 else: 

553 # exception #1 

554 rendered = "0.post%d" % pieces["distance"] 

555 if pieces["branch"] != "master": 

556 rendered += ".dev0" 

557 rendered += "+g%s" % pieces["short"] 

558 if pieces["dirty"]: 

559 rendered += ".dirty" 

560 return rendered 

561 

562 

563def render_pep440_old(pieces: Dict[str, Any]) -> str: 

564 """TAG[.postDISTANCE[.dev0]] . 

565 

566 The ".dev0" means dirty. 

567 

568 Exceptions: 

569 1: no tags. 0.postDISTANCE[.dev0] 

570 """ 

571 if pieces["closest-tag"]: 

572 rendered = pieces["closest-tag"] 

573 if pieces["distance"] or pieces["dirty"]: 

574 rendered += ".post%d" % pieces["distance"] 

575 if pieces["dirty"]: 

576 rendered += ".dev0" 

577 else: 

578 # exception #1 

579 rendered = "0.post%d" % pieces["distance"] 

580 if pieces["dirty"]: 

581 rendered += ".dev0" 

582 return rendered 

583 

584 

585def render_git_describe(pieces: Dict[str, Any]) -> str: 

586 """TAG[-DISTANCE-gHEX][-dirty]. 

587 

588 Like 'git describe --tags --dirty --always'. 

589 

590 Exceptions: 

591 1: no tags. HEX[-dirty] (note: no 'g' prefix) 

592 """ 

593 if pieces["closest-tag"]: 

594 rendered = pieces["closest-tag"] 

595 if pieces["distance"]: 

596 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 

597 else: 

598 # exception #1 

599 rendered = pieces["short"] 

600 if pieces["dirty"]: 

601 rendered += "-dirty" 

602 return rendered 

603 

604 

605def render_git_describe_long(pieces: Dict[str, Any]) -> str: 

606 """TAG-DISTANCE-gHEX[-dirty]. 

607 

608 Like 'git describe --tags --dirty --always -long'. 

609 The distance/hash is unconditional. 

610 

611 Exceptions: 

612 1: no tags. HEX[-dirty] (note: no 'g' prefix) 

613 """ 

614 if pieces["closest-tag"]: 

615 rendered = pieces["closest-tag"] 

616 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 

617 else: 

618 # exception #1 

619 rendered = pieces["short"] 

620 if pieces["dirty"]: 

621 rendered += "-dirty" 

622 return rendered 

623 

624 

625def render(pieces: Dict[str, Any], style: str) -> Dict[str, Any]: 

626 """Render the given version pieces into the requested style.""" 

627 if pieces["error"]: 

628 return { 

629 "version": "unknown", 

630 "full-revisionid": pieces.get("long"), 

631 "dirty": None, 

632 "error": pieces["error"], 

633 "date": None, 

634 } 

635 

636 if not style or style == "default": 

637 style = "pep440" # the default 

638 

639 if style == "pep440": 

640 rendered = render_pep440(pieces) 

641 elif style == "pep440-branch": 

642 rendered = render_pep440_branch(pieces) 

643 elif style == "pep440-pre": 

644 rendered = render_pep440_pre(pieces) 

645 elif style == "pep440-post": 

646 rendered = render_pep440_post(pieces) 

647 elif style == "pep440-post-branch": 

648 rendered = render_pep440_post_branch(pieces) 

649 elif style == "pep440-old": 

650 rendered = render_pep440_old(pieces) 

651 elif style == "git-describe": 

652 rendered = render_git_describe(pieces) 

653 elif style == "git-describe-long": 

654 rendered = render_git_describe_long(pieces) 

655 else: 

656 raise ValueError("unknown style '%s'" % style) 

657 

658 return { 

659 "version": rendered, 

660 "full-revisionid": pieces["long"], 

661 "dirty": pieces["dirty"], 

662 "error": None, 

663 "date": pieces.get("date"), 

664 } 

665 

666 

667def get_versions() -> Dict[str, Any]: 

668 """Get version information or return default if unable to do so.""" 

669 # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have 

670 # __file__, we can work backwards from there to the root. Some 

671 # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which 

672 # case we can only use expanded keywords. 

673 

674 cfg = get_config() 

675 verbose = cfg.verbose 

676 

677 try: 

678 return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) 

679 except NotThisMethod: 

680 pass 

681 

682 try: 

683 root = os.path.realpath(__file__) 

684 # versionfile_source is the relative path from the top of the source 

685 # tree (where the .git directory might live) to this file. Invert 

686 # this to find the root from __file__. 

687 for _ in cfg.versionfile_source.split("/"): 

688 root = os.path.dirname(root) 

689 except NameError: 

690 return { 

691 "version": "0+unknown", 

692 "full-revisionid": None, 

693 "dirty": None, 

694 "error": "unable to find root of source tree", 

695 "date": None, 

696 } 

697 

698 try: 

699 pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) 

700 return render(pieces, cfg.style) 

701 except NotThisMethod: 

702 pass 

703 

704 try: 

705 if cfg.parentdir_prefix: 

706 return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) 

707 except NotThisMethod: 

708 pass 

709 

710 return { 

711 "version": "0+unknown", 

712 "full-revisionid": None, 

713 "dirty": None, 

714 "error": "unable to compute version", 

715 "date": None, 

716 }