Skip to content

Commit da271a0

Browse files
committed
switch to Versioneer for version-string management
1 parent 36e9cfa commit da271a0

File tree

6 files changed

+968
-71
lines changed

6 files changed

+968
-71
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ecdsa/_version.py export-subst

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
*.py[cod]
2-
ecdsa/_version.py
32
MANIFEST
43

54
# C extensions

ecdsa/__init__.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99

1010
# This code comes from http://github.com/warner/python-ecdsa
1111

12-
try:
13-
from _version import __version__ as v
14-
__version__ = v
15-
del v
16-
except ImportError:
17-
__version__ = "UNKNOWN"
12+
from ._version import get_versions
13+
__version__ = get_versions()['version']
14+
del get_versions

ecdsa/_version.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
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 (build 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.
7+
8+
# This file is released into the public domain. Generated by
9+
# versioneer-0.8+ (https://github.com/warner/python-versioneer)
10+
11+
# these strings will be replaced by git during git-archive
12+
git_refnames = "$Format:%d$"
13+
git_full = "$Format:%H$"
14+
15+
16+
import subprocess
17+
import sys
18+
import errno
19+
20+
21+
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
22+
assert isinstance(commands, list)
23+
p = None
24+
for c in commands:
25+
try:
26+
# remember shell=False, so use git.cmd on windows, not just git
27+
p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
28+
stderr=(subprocess.PIPE if hide_stderr
29+
else None))
30+
break
31+
except EnvironmentError:
32+
e = sys.exc_info()[1]
33+
if e.errno == errno.ENOENT:
34+
continue
35+
if verbose:
36+
print("unable to run %s" % args[0])
37+
print(e)
38+
return None
39+
else:
40+
if verbose:
41+
print("unable to find command, tried %s" % (commands,))
42+
return None
43+
stdout = p.communicate()[0].strip()
44+
if sys.version >= '3':
45+
stdout = stdout.decode()
46+
if p.returncode != 0:
47+
if verbose:
48+
print("unable to run %s (error)" % args[0])
49+
return None
50+
return stdout
51+
52+
53+
import sys
54+
import re
55+
import os.path
56+
57+
def get_expanded_variables(versionfile_abs):
58+
# the code embedded in _version.py can just fetch the value of these
59+
# variables. When used from setup.py, we don't want to import
60+
# _version.py, so we do it with a regexp instead. This function is not
61+
# used from _version.py.
62+
variables = {}
63+
try:
64+
f = open(versionfile_abs,"r")
65+
for line in f.readlines():
66+
if line.strip().startswith("git_refnames ="):
67+
mo = re.search(r'=\s*"(.*)"', line)
68+
if mo:
69+
variables["refnames"] = mo.group(1)
70+
if line.strip().startswith("git_full ="):
71+
mo = re.search(r'=\s*"(.*)"', line)
72+
if mo:
73+
variables["full"] = mo.group(1)
74+
f.close()
75+
except EnvironmentError:
76+
pass
77+
return variables
78+
79+
def versions_from_expanded_variables(variables, tag_prefix, verbose=False):
80+
refnames = variables["refnames"].strip()
81+
if refnames.startswith("$Format"):
82+
if verbose:
83+
print("variables are unexpanded, not using")
84+
return {} # unexpanded, so not in an unpacked git-archive tarball
85+
refs = set([r.strip() for r in refnames.strip("()").split(",")])
86+
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
87+
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
88+
TAG = "tag: "
89+
tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
90+
if not tags:
91+
# Either we're using git < 1.8.3, or there really are no tags. We use
92+
# a heuristic: assume all version tags have a digit. The old git %d
93+
# expansion behaves like git log --decorate=short and strips out the
94+
# refs/heads/ and refs/tags/ prefixes that would let us distinguish
95+
# between branches and tags. By ignoring refnames without digits, we
96+
# filter out many common branch names like "release" and
97+
# "stabilization", as well as "HEAD" and "master".
98+
tags = set([r for r in refs if re.search(r'\d', r)])
99+
if verbose:
100+
print("discarding '%s', no digits" % ",".join(refs-tags))
101+
if verbose:
102+
print("likely tags: %s" % ",".join(sorted(tags)))
103+
for ref in sorted(tags):
104+
# sorting will prefer e.g. "2.0" over "2.0rc1"
105+
if ref.startswith(tag_prefix):
106+
r = ref[len(tag_prefix):]
107+
if verbose:
108+
print("picking %s" % r)
109+
return { "version": r,
110+
"full": variables["full"].strip() }
111+
# no suitable tags, so we use the full revision id
112+
if verbose:
113+
print("no suitable tags, using full revision id")
114+
return { "version": variables["full"].strip(),
115+
"full": variables["full"].strip() }
116+
117+
def versions_from_vcs(tag_prefix, root, verbose=False):
118+
# this runs 'git' from the root of the source tree. This only gets called
119+
# if the git-archive 'subst' variables were *not* expanded, and
120+
# _version.py hasn't already been rewritten with a short version string,
121+
# meaning we're inside a checked out source tree.
122+
123+
if not os.path.exists(os.path.join(root, ".git")):
124+
if verbose:
125+
print("no .git in %s" % root)
126+
return {}
127+
128+
GITS = ["git"]
129+
if sys.platform == "win32":
130+
GITS = ["git.cmd", "git.exe"]
131+
stdout = run_command(GITS, ["describe", "--tags", "--dirty", "--always"],
132+
cwd=root)
133+
if stdout is None:
134+
return {}
135+
if not stdout.startswith(tag_prefix):
136+
if verbose:
137+
print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix))
138+
return {}
139+
tag = stdout[len(tag_prefix):]
140+
stdout = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
141+
if stdout is None:
142+
return {}
143+
full = stdout.strip()
144+
if tag.endswith("-dirty"):
145+
full += "-dirty"
146+
return {"version": tag, "full": full}
147+
148+
149+
def versions_from_parentdir(parentdir_prefix, root, verbose=False):
150+
# Source tarballs conventionally unpack into a directory that includes
151+
# both the project name and a version string.
152+
dirname = os.path.basename(root)
153+
if not dirname.startswith(parentdir_prefix):
154+
if verbose:
155+
print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" %
156+
(root, dirname, parentdir_prefix))
157+
return None
158+
return {"version": dirname[len(parentdir_prefix):], "full": ""}
159+
160+
tag_prefix = "python-ecdsa-"
161+
parentdir_prefix = "ecdsa-"
162+
versionfile_source = "ecdsa/_version.py"
163+
164+
def get_versions(default={"version": "unknown", "full": ""}, verbose=False):
165+
# I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
166+
# __file__, we can work backwards from there to the root. Some
167+
# py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
168+
# case we can only use expanded variables.
169+
170+
variables = { "refnames": git_refnames, "full": git_full }
171+
ver = versions_from_expanded_variables(variables, tag_prefix, verbose)
172+
if ver:
173+
return ver
174+
175+
try:
176+
root = os.path.abspath(__file__)
177+
# versionfile_source is the relative path from the top of the source
178+
# tree (where the .git directory might live) to this file. Invert
179+
# this to find the root from __file__.
180+
for i in range(len(versionfile_source.split("/"))):
181+
root = os.path.dirname(root)
182+
except NameError:
183+
return default
184+
185+
return (versions_from_vcs(tag_prefix, root, verbose)
186+
or versions_from_parentdir(parentdir_prefix, root, verbose)
187+
or default)
188+

setup.py

Lines changed: 11 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
from distutils.command.sdist import sdist as _sdist
55
from ecdsa.six import print_
66

7+
import versioneer
8+
versioneer.versionfile_source = "ecdsa/_version.py"
9+
versioneer.versionfile_build = versioneer.versionfile_source
10+
versioneer.tag_prefix = "python-ecdsa-"
11+
versioneer.parentdir_prefix = "ecdsa-"
12+
13+
commands = versioneer.get_cmdclass().copy()
14+
715
class Test(Command):
816
description = "run unit tests"
917
user_options = []
@@ -22,78 +30,17 @@ def run(self):
2230
from ecdsa import test_pyecdsa
2331
test_pyecdsa.unittest.main(module=test_pyecdsa, argv=["dummy"])
2432
# all tests os.exit(1) upon failure
25-
26-
VERSION_PY = """
27-
# This file is originally generated from Git information by running 'setup.py
28-
# version'. Distribution tarballs contain a pre-generated copy of this file.
29-
30-
__version__ = '%s'
31-
"""
32-
33-
def update_version_py():
34-
if not os.path.isdir(".git"):
35-
print_("This does not appear to be a Git repository.")
36-
return
37-
try:
38-
p = subprocess.Popen(["git", "describe",
39-
"--tags", "--dirty", "--always"],
40-
stdout=subprocess.PIPE)
41-
except EnvironmentError:
42-
print_("unable to run git, leaving ecdsa/_version.py alone")
43-
return
44-
stdout = p.communicate()[0]
45-
if p.returncode != 0:
46-
print_("unable to run git, leaving ecdsa/_version.py alone")
47-
return
48-
# we use tags like "python-ecdsa-0.5", so strip the prefix
49-
assert stdout.startswith("python-ecdsa-")
50-
ver = stdout[len("python-ecdsa-"):].strip()
51-
f = open("ecdsa/_version.py", "w")
52-
f.write(VERSION_PY % ver)
53-
f.close()
54-
print_("set ecdsa/_version.py to '%s'" % ver)
55-
56-
def get_version():
57-
try:
58-
f = open("ecdsa/_version.py")
59-
except EnvironmentError:
60-
return None
61-
for line in f.readlines():
62-
mo = re.match("__version__ = '([^']+)'", line)
63-
if mo:
64-
ver = mo.group(1)
65-
return ver
66-
return None
67-
68-
class Version(Command):
69-
description = "update _version.py from Git repo"
70-
user_options = []
71-
boolean_options = []
72-
def initialize_options(self):
73-
pass
74-
def finalize_options(self):
75-
pass
76-
def run(self):
77-
update_version_py()
78-
print_("Version is now", get_version())
79-
80-
class sdist(_sdist):
81-
def run(self):
82-
update_version_py()
83-
# unless we update this, the sdist command will keep using the old
84-
# version
85-
self.distribution.metadata.version = get_version()
86-
return _sdist.run(self)
33+
commands["test"] = Test
8734

8835
setup(name="ecdsa",
89-
version=get_version(),
36+
version=versioneer.get_version(),
9037
description="ECDSA cryptographic signature library (pure python)",
9138
author="Brian Warner",
9239
author_email="[email protected]",
9340
url="http://github.com/warner/python-ecdsa",
9441
packages=["ecdsa"],
9542
license="MIT",
96-
cmdclass={ "test": Test, "version": Version, "sdist": sdist },
43+
cmdclass=commands,
9744
classifiers=[
9845
"Programming Language :: Python",
9946
"Programming Language :: Python :: 2",

0 commit comments

Comments
 (0)