1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
"""Version information."""
import errno
import os
import subprocess
from importlib import import_module
_ver = None
def get_version(project, repo_file, api_version=None):
"""Determine a project's version information.
Standardized version retrieval for git-based projects. In summary, if the
api_version isn't specified it imports __version__ from the main module for
the project. Next it tries to import extended information from a generated
file (for packages using snakeoil's custom sdist phase) and if that fails
assumes it's in a git repo and grabs the git info instead.
:param project: module name
:param repo_file: file belonging to module
:param api_version: version for the project, if not specified __version__
is imported from the main project module
:return: a string describing the project version
"""
global _ver # pylint: disable=global-statement
if _ver is None:
version_info = None
if api_version is None:
try:
api_version = getattr(import_module(project), "__version__")
except ImportError:
raise ValueError(f"no {project} module in the syspath")
try:
version_info = getattr(import_module(f"{project}._verinfo"), "version_info")
except ImportError:
# we're probably in a git repo
path = os.path.dirname(os.path.abspath(repo_file))
version_info = get_git_version(path)
if version_info is None:
s = ""
elif version_info["tag"] == api_version:
s = f" -- released {version_info['date']}"
else:
rev = version_info["rev"][:7]
date = version_info["date"]
commits = version_info.get("commits", None)
commits = f"-{commits}" if commits is not None else ""
s = f"{commits}-g{rev} -- {date}"
_ver = f"{project} {api_version}{s}"
return _ver
def _run_git(path, cmd):
env = dict(os.environ)
for key in env.copy(): # pragma: no cover
if key.startswith("LC_"):
del env[key]
env["LC_CTYPE"] = "C"
env["LC_ALL"] = "C"
r = subprocess.Popen(
["git"] + list(cmd),
stdout=subprocess.PIPE,
env=env,
stderr=subprocess.DEVNULL,
cwd=path,
)
stdout = r.communicate()[0]
return stdout, r.returncode
def get_git_version(path):
"""Return git related revision data."""
path = os.path.abspath(path)
try:
stdout, ret = _run_git(path, ["log", "--format=%H\n%aD", "HEAD^..HEAD"])
if ret != 0:
return None
data = stdout.decode().splitlines()
tag = _get_git_tag(path, data[0])
# get number of commits since most recent tag
stdout, ret = _run_git(path, ["describe", "--tags", "--abbrev=0"])
prev_tag = None
commits = None
if ret == 0:
prev_tag = stdout.decode().strip()
stdout, ret = _run_git(path, ["log", "--oneline", f"{prev_tag}..HEAD"])
if ret == 0:
commits = len(stdout.decode().splitlines())
return {
"rev": data[0],
"date": data[1],
"tag": tag,
"commits": commits,
}
except EnvironmentError as exc:
# ENOENT is thrown when the git binary can't be found.
if exc.errno != errno.ENOENT:
raise
return None
def _get_git_tag(path, rev):
stdout, _ = _run_git(path, ["name-rev", "--tag", rev])
tag = stdout.decode().split()
if len(tag) != 2:
return None
tag = tag[1]
if not tag.startswith("tags/"):
return None
tag = tag[len("tags/") :]
if tag.endswith("^0"):
tag = tag[:-2]
if tag.startswith("v"):
tag = tag[1:]
return tag
|