From 27c148ac73ed12503c8df520460353aeb18a9327 Mon Sep 17 00:00:00 2001 From: pelson Date: Thu, 25 Jun 2015 13:10:34 +0100 Subject: [PATCH 01/18] pyflakes the codebase. --- src/from_file.py | 1 - src/get_versions.py | 1 + src/git/from_keywords.py | 7 +- src/git/from_vcs.py | 7 ++ src/render.py | 52 +++++++++- test/git/test_git.py | 217 +++++++++++++++++++++++++++++++++++---- test/test_render.py | 46 ++++++++- 7 files changed, 304 insertions(+), 27 deletions(-) diff --git a/src/from_file.py b/src/from_file.py index 7f4eae05..fcbd78d7 100644 --- a/src/from_file.py +++ b/src/from_file.py @@ -47,4 +47,3 @@ def write_to_version_file(filename, versions): f.write(SHORT_VERSION_PY % contents) print("set %s to '%s'" % (filename, versions["version"])) - diff --git a/src/get_versions.py b/src/get_versions.py index 01553710..31cdb05f 100644 --- a/src/get_versions.py +++ b/src/get_versions.py @@ -7,6 +7,7 @@ def render(): pass # --STRIP DURING BUILD HANDLERS = {} # --STRIP DURING BUILD class NotThisMethod(Exception): pass # --STRIP DURING BUILD + class VersioneerBadRootError(Exception): """The project root directory is unknown or missing key files.""" diff --git a/src/git/from_keywords.py b/src/git/from_keywords.py index ea7c040f..385c931f 100644 --- a/src/git/from_keywords.py +++ b/src/git/from_keywords.py @@ -79,12 +79,11 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, - "date": date} + "date": date, "branch": None} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} - - + "dirty": False, "error": "no suitable tags", "date": None, + "branch": None} diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index c7db10d1..883c7401 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -52,6 +52,13 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None + # abbrev-ref available with git >= 1.7 + branch_name = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root).strip() + if branch_name == 'HEAD': + branch_name = None + pieces['branch'] = branch_name + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out diff --git a/src/render.py b/src/render.py index 9770d0ea..831f4dae 100644 --- a/src/render.py +++ b/src/render.py @@ -1,3 +1,5 @@ +import re # --STRIP DURING BUILD + def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" @@ -136,6 +138,55 @@ def render_git_describe_long(pieces): return rendered +def add_one_to_version(version_string, number_index_to_increment=-1): + """ + Add one to a version string at the given numeric indices. + + >>> add_one_to_version('v1.2.3') + 'v1.2.4' + + """ + # Break up the tag by number groups (preserving multi-digit + # numbers as multidigit) + parts = re.split("([0-9]+)", version_string) + + digit_parts = [(i, part) for i, part in enumerate(parts) + if part.isdigit()] + + # Deal with negative indexing. + increment_at_index = ((number_index_to_increment + len(digit_parts)) + % len(digit_parts)) + for n_seen, (i, part) in enumerate(digit_parts): + if n_seen == increment_at_index: + parts[i] = str(int(part) + 1) + elif n_seen > increment_at_index: + parts[i] = '0' + return ''.join(parts) + + +def render_pep440_plus_one_dev(pieces): + # [TAG+1 of minor number][.devDISTANCE][+gHEX]. The git short is + # included for dirty. + + # exceptions: + # 1: no tags. 0.0.0.devDISTANCE[+gHEX] + + if pieces["closest-tag"]: + if pieces["distance"] or pieces["dirty"]: + rendered = add_one_to_version(pieces["closest-tag"]) + rendered += ".dev%d" % pieces["distance"] + if pieces["dirty"]: + rendered += "+g%s" % pieces["short"] + else: + rendered = pieces["closest-tag"] + else: + # exception #1 + rendered = "0.0.0.dev%d" % pieces["distance"] + if pieces["dirty"]: + rendered += "+g%s" % pieces["short"] + return rendered + + def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: @@ -166,4 +217,3 @@ def render(pieces, style): return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} - diff --git a/test/git/test_git.py b/test/git/test_git.py index 41f2dfd3..44adeac0 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -1,11 +1,12 @@ #! /usr/bin/python from __future__ import print_function -import os, sys +import os import shutil import tarfile import unittest import tempfile +import sys from pkg_resources import parse_version, SetuptoolsLegacyVersion @@ -16,14 +17,15 @@ from git import from_vcs, from_keywords from subprocess_helper import run_command -class ParseGitDescribe(unittest.TestCase): + +class Test_ParseGitDescribe(unittest.TestCase): def setUp(self): self.fakeroot = tempfile.mkdtemp() self.fakegit = os.path.join(self.fakeroot, ".git") os.mkdir(self.fakegit) def test_pieces(self): - def pv(git_describe, do_error=False, expect_pieces=False): + def pv(git_describe, do_error=False, expect_pieces=False, branch_name="HEAD"): def fake_run_command(exes, args, cwd=None, hide_stderr=None): if args[0] == "describe": if do_error == "describe": @@ -32,7 +34,10 @@ def fake_run_command(exes, args, cwd=None, hide_stderr=None): if args[0] == "rev-parse": if do_error == "rev-parse": return None, 0 - return "longlong\n", 0 + if args[1] == '--abbrev-ref': + return '%s\n' % branch_name, 0 + else: + return "longlong\n", 0 if args[0] == "rev-list": return "42\n", 0 if args[0] == "show": @@ -46,37 +51,49 @@ def fake_run_command(exes, args, cwd=None, hide_stderr=None): self.assertRaises(from_vcs.NotThisMethod, pv, "ignored", do_error="rev-parse") self.assertEqual(pv("1f"), - {"closest-tag": None, "dirty": False, "error": None, + {"branch": None, "closest-tag": None, + "dirty": False, "error": None, "distance": 42, "long": "longlong", "short": "longlon", "date": "12345"}) self.assertEqual(pv("1f-dirty"), - {"closest-tag": None, "dirty": True, "error": None, + {"branch": None, "closest-tag": None, + "dirty": True, "error": None, "distance": 42, "long": "longlong", "short": "longlon", "date": "12345"}) self.assertEqual(pv("v1.0-0-g1f"), - {"closest-tag": "1.0", "dirty": False, "error": None, + {"branch": None, "closest-tag": "1.0", + "dirty": False, "error": None, "distance": 0, "long": "longlong", "short": "1f", "date": "12345"}) self.assertEqual(pv("v1.0-0-g1f-dirty"), - {"closest-tag": "1.0", "dirty": True, "error": None, + {"branch": None, "closest-tag": "1.0", + "dirty": True, "error": None, "distance": 0, "long": "longlong", "short": "1f", "date": "12345"}) self.assertEqual(pv("v1.0-1-g1f"), - {"closest-tag": "1.0", "dirty": False, "error": None, + {"branch": None, "closest-tag": "1.0", + "dirty": False, "error": None, "distance": 1, "long": "longlong", "short": "1f", "date": "12345"}) self.assertEqual(pv("v1.0-1-g1f-dirty"), - {"closest-tag": "1.0", "dirty": True, "error": None, + {"branch": None, "closest-tag": "1.0", + "dirty": True, "error": None, + "distance": 1, + "long": "longlong", + "short": "1f"}) + self.assertEqual(pv("v1.0-1-g1f-dirty", branch_name="v1.0.x"), + {"branch": 'v1.0.x', "closest-tag": "1.0", + "dirty": True, "error": None, "distance": 1, "long": "longlong", "short": "1f", @@ -87,13 +104,14 @@ def tearDown(self): os.rmdir(self.fakeroot) -class Keywords(unittest.TestCase): +class Test_Keywords(unittest.TestCase): def parse(self, refnames, full, prefix=""): return from_keywords.git_versions_from_keywords( {"refnames": refnames, "full": full}, prefix, False) def test_parse(self): v = self.parse(" (HEAD, 2.0,master , otherbranch ) ", " full ") + self.assertEqual(v["branch"], None) self.assertEqual(v["version"], "2.0") self.assertEqual(v["full-revisionid"], "full") self.assertEqual(v["dirty"], False) @@ -101,6 +119,7 @@ def test_parse(self): def test_prefer_short(self): v = self.parse(" (HEAD, 2.0rc1, 2.0, 2.0rc2) ", " full ") + self.assertEqual(v["branch"], None) self.assertEqual(v["version"], "2.0") self.assertEqual(v["full-revisionid"], "full") self.assertEqual(v["dirty"], False) @@ -108,6 +127,7 @@ def test_prefer_short(self): def test_prefix(self): v = self.parse(" (HEAD, projectname-2.0) ", " full ", "projectname-") + self.assertEqual(v["branch"], None) self.assertEqual(v["version"], "2.0") self.assertEqual(v["full-revisionid"], "full") self.assertEqual(v["dirty"], False) @@ -119,6 +139,7 @@ def test_unexpanded(self): def test_no_tags(self): v = self.parse("(HEAD, master)", "full") + self.assertEqual(v["branch"], None) self.assertEqual(v["version"], "0+unknown") self.assertEqual(v["full-revisionid"], "full") self.assertEqual(v["dirty"], False) @@ -126,11 +147,28 @@ def test_no_tags(self): def test_no_prefix(self): v = self.parse("(HEAD, master, 1.23)", "full", "missingprefix-") + self.assertEqual(v["branch"], None) self.assertEqual(v["version"], "0+unknown") - self.assertEqual(v["full-revisionid"], "full") self.assertEqual(v["dirty"], False) self.assertEqual(v["error"], "no suitable tags") + def test_branch_heuristics(self): + v = self.parse("(v0.12.x)", "full", "v") + self.assertEqual(v["branch"], None) + # Questionable whether this is desirable. + self.assertEqual(v["version"], "0.12.x") + self.assertEqual(v["dirty"], False) + self.assertEqual(v["error"], None) + + def test_new_tag_style(self): + v = self.parse("(tag: v0.12.0)", "full", "v") + self.assertEqual(v["branch"], None) + self.assertEqual(v["version"], "0.12.0") + self.assertEqual(v["full-revisionid"], "full") + self.assertEqual(v["dirty"], False) + self.assertEqual(v["error"], None) + + expected_renders = """ closest-tag: 1.0 distance: 0 @@ -216,7 +254,8 @@ def test_no_prefix(self): """ -class RenderPieces(unittest.TestCase): + +class Test_RenderPieces(unittest.TestCase): def do_render(self, pieces): out = {} for style in ["pep440", "pep440-pre", "pep440-post", "pep440-old", @@ -271,7 +310,8 @@ def test_render(self): VERBOSE = False -class Repo(common.Common, unittest.TestCase): + +class Test_RepoIntegration(common.Common, unittest.TestCase): # There are three tree states we're interested in: # S1: sitting on the initial commit, no tags @@ -317,12 +357,7 @@ def test_project_in_subdir(self): # i.e. setup.py -- is not located in the root directory self.run_test("test/demoapp", False, "project") - def run_test(self, demoapp_dir, script_only, project_sub_dir): - # The test dir should live under /tmp/ or /var/ or somewhere that - # isn't the child of the versioneer repo's .git directory, since that - # will confuse the tests that check what happens when there is no - # .git parent. So if you change this to use a fixed directory (say, - # when debugging problems), use /tmp/_test rather than ./_test . + def run_case(self, demoapp_dir, script_only, project_sub_dir): self.testdir = tempfile.mkdtemp() if VERBOSE: print("testdir: %s" % (self.testdir,)) if os.path.exists(self.testdir): @@ -597,6 +632,148 @@ def assertPEP440(self, got, state, tree, runtime): "%s: '%s' pep440-normalized to '%s'" % (where, got, str(pv))) + +class Test_GitRepo(common.Common, unittest.TestCase): + # We care about the following git scenarios: + # S1 : master, 0 commits, 0 tags, v0 + # S2 : master, 1 commits, 0 tags, v0.dev1 + # S3 : master, 1 commits, 1 tags, v1 + # S4 : master, 2 commits, 1 tags, v1.dev1 + # S5 : v1.x, 2 commits, 2 tags, v1.1 + # S6 : master, 3 commits, 1 tags, v2.pre2 + # (merge of v1.x back to master) + # + # For both clean and dirty situations (uncommitted code changes) + # We should also check export state. + + expecteds = {'S1': None, + 'S2': {'branch': 'master', + 'closest-tag': None, + 'dirty': False, + 'distance': 1, + 'error': None}, + 'S3': {'branch': 'master', + 'closest-tag': '1.0', + 'dirty': False, + 'distance': 0, + 'error': None}, + 'S4': {'branch': 'master', + 'closest-tag': '1.0', + 'dirty': False, + 'distance': 1, + 'error': None}, + 'S5': {'branch': 'master', + 'closest-tag': '2.0', + 'dirty': False, + 'distance': 0, + 'error': None}, + 'S6': {'branch': 'v1.x', + 'closest-tag': '1.0', + 'dirty': False, + 'distance': 1, + 'error': None}, + 'S7': {'branch': 'v1.x', + 'closest-tag': '1.1', + 'dirty': False, + 'distance': 0, + 'error': None}, + 'S8': {'branch': 'master', + 'closest-tag': '2.0', + 'dirty': False, + 'distance': 2, + 'error': None}, + 'S9': {'branch': 'master', + 'closest-tag': '2.1', + 'dirty': False, + 'distance': 0, + 'error': None}, + } + + def assert_case(self, case_name, dirty=False): + tag_prefix = 'v' + pieces = from_vcs.git_pieces_from_vcs(tag_prefix, self.repo_root, + run_command=run_command) + pieces.pop('short') + pieces.pop('long') + expected = self.expecteds.get(case_name, {}) + + if dirty: + expected['dirty'] = True + if expected != pieces: + print('Case: %s' % case_name) + + self.assertEqual(expected, pieces) + + def write_file(self, fname, content): + with open(os.path.join(self.repo_root, fname), 'w') as fh: + fh.write(content) + + def test(self): + tag_prefix = 'v' + + self.testdir = os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'git_test_repo') + self.repo_root = os.path.join(self.testdir, 'demoapp') + + # Cleanup + if os.path.exists(self.repo_root): + import shutil + shutil.rmtree(self.repo_root) + os.makedirs(self.repo_root) + + # S1 + self.git("init") + self.assertRaises(from_vcs.NotThisMethod, + from_vcs.git_pieces_from_vcs, + tag_prefix, self.repo_root, run_command=run_command) + + # S2 + self.write_file('a.txt', 'abc') + self.git("add", "a.txt") + self.git("commit", "-m", "First commmit.") + self.assert_case('S2') + + # S3 + self.git("tag", "-a", "v1.0", "-m", "First tag.") + self.git("branch", "v1.x") + self.assert_case('S3') + + # S4 + self.write_file('b.txt', 'abc') + self.git("add", "b.txt") + self.assert_case('S3', dirty=True) + self.git("commit", "-m", "Start of 2.0.") + self.assert_case('S4') + + # S5 + self.git("tag", "-a", "v2.0", "-m", "Second tag.") + self.assert_case('S5') + + # S6 + self.git("checkout", "v1.x") + self.write_file('a.txt', 'abcdef') + self.git("commit", "-am", "Modify 1.x") + self.assert_case('S6') + + # S7 + self.git("tag", "-a", "v1.1", "-m", "Tag v1.1.") + self.assert_case('S7') + + # S8 + self.git("checkout", "master") + # Just check that S5 hasn't been distrupted - we've just added a tag. + self.assert_case('S5') + self.git("merge", "v1.x") + self.assert_case('S8') + + # S9 + self.git("tag", "-a", "v2.1", "-m", "Tag v2.1.") + self.assert_case('S9') + self.git("checkout", "v1.x") + # Again, just check that we haven't disrupted the v1.x branch. + self.assert_case('S7') + + if __name__ == '__main__': ver, rc = run_command(common.GITS, ["--version"], ".", True) print("git --version: %s" % ver.strip()) diff --git a/test/test_render.py b/test/test_render.py index ab01a936..16979b9b 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -1,7 +1,8 @@ import unittest -from versioneer import render +from versioneer import render, add_one_to_version +from versioneer_src.render import add_one_to_version class Testing_renderer_case_mixin(object): """ @@ -137,5 +138,48 @@ class Test_git_describe(unittest.TestCase, Testing_renderer_case_mixin): } +class Test_add_one_to_version(unittest.TestCase): + def test_index_0(self): + result = add_one_to_version('v1.2.3', 0) + self.assertEqual(result, 'v2.0.0') + + def test_index_1(self): + result = add_one_to_version('v1.2.3', 1) + self.assertEqual(result, 'v1.3.0') + + def test_index_2(self): + result = add_one_to_version('v1.2.3', 2) + self.assertEqual(result, 'v1.2.4') + + def test_negative_indexing(self): + result = add_one_to_version('v1.2.3', -2) + self.assertEqual(result, 'v1.3.0') + + def test_year_version(self): + result = add_one_to_version('1066.8', 1) + self.assertEqual(result, '1066.9') + + def test_index_with_rc(self): + # Note this is not the result you would want from a style, + # but it is the expected behaviour of this function. + result = add_one_to_version('v1.2.3rc4', 2) + self.assertEqual(result, 'v1.2.4rc0') + + + +#class Test_git_describe(unittest.TestCase, Testing_renderer_case_mixin): +# style = 'pep440-branch-based' +# expected = {'tagged_0_commits_clean': 'v1.2.3', +# 'tagged_0_commits_dirty': 'v1.2.3-dirty', +# 'tagged_1_commits_clean': 'v1.2.3-1-gabc', +# 'tagged_1_commits_dirty': 'v1.2.3-1-gabc-dirty', +# 'untagged_0_commits_clean': '', +# 'untagged_0_commits_dirty': '-dirty', +# 'untagged_1_commits_clean': 'abc', +# 'untagged_1_commits_dirty': 'abc-dirty', +# 'error_getting_parts': 'unknown' +# } + + if __name__ == '__main__': unittest.main() From 41acc15486ad49b35275ea63717bcdd9302db573 Mon Sep 17 00:00:00 2001 From: pelson Date: Thu, 25 Jun 2015 16:21:14 +0100 Subject: [PATCH 02/18] Branch information from git. --- src/render.py | 2 + test/git/test_git.py | 1 + test/test_render.py | 139 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/src/render.py b/src/render.py index 831f4dae..f6a49b3e 100644 --- a/src/render.py +++ b/src/render.py @@ -1,6 +1,7 @@ import re # --STRIP DURING BUILD + def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): @@ -138,6 +139,7 @@ def render_git_describe_long(pieces): return rendered +<<<<<<< HEAD def add_one_to_version(version_string, number_index_to_increment=-1): """ Add one to a version string at the given numeric indices. diff --git a/test/git/test_git.py b/test/git/test_git.py index 44adeac0..4087b6d5 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -169,6 +169,7 @@ def test_new_tag_style(self): self.assertEqual(v["error"], None) +<<<<<<< HEAD expected_renders = """ closest-tag: 1.0 distance: 0 diff --git a/test/test_render.py b/test/test_render.py index 16979b9b..0e51eea7 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -4,6 +4,145 @@ from versioneer_src.render import add_one_to_version +expected_renders = """ +closest-tag: 1.0 +distance: 0 +dirty: False +pep440: 1.0 +pep440-pre: 1.0 +pep440-post: 1.0 +pep440-old: 1.0 +git-describe: 1.0 +git-describe-long: 1.0-0-g250b7ca + +closest-tag: 1.0 +distance: 0 +dirty: True +pep440: 1.0+0.g250b7ca.dirty +pep440-pre: 1.0 +pep440-post: 1.0.post0.dev0+g250b7ca +pep440-old: 1.0.post0.dev0 +git-describe: 1.0-dirty +git-describe-long: 1.0-0-g250b7ca-dirty + +closest-tag: 1.0 +distance: 1 +dirty: False +pep440: 1.0+1.g250b7ca +pep440-pre: 1.0.post.dev1 +pep440-post: 1.0.post1+g250b7ca +pep440-old: 1.0.post1 +git-describe: 1.0-1-g250b7ca +git-describe-long: 1.0-1-g250b7ca + +closest-tag: 1.0 +distance: 1 +dirty: True +pep440: 1.0+1.g250b7ca.dirty +pep440-pre: 1.0.post.dev1 +pep440-post: 1.0.post1.dev0+g250b7ca +pep440-old: 1.0.post1.dev0 +git-describe: 1.0-1-g250b7ca-dirty +git-describe-long: 1.0-1-g250b7ca-dirty + + +closest-tag: 1.0+plus +distance: 1 +dirty: False +pep440: 1.0+plus.1.g250b7ca +pep440-pre: 1.0+plus.post.dev1 +pep440-post: 1.0+plus.post1.g250b7ca +pep440-old: 1.0+plus.post1 +git-describe: 1.0+plus-1-g250b7ca +git-describe-long: 1.0+plus-1-g250b7ca + +closest-tag: 1.0+plus +distance: 1 +dirty: True +pep440: 1.0+plus.1.g250b7ca.dirty +pep440-pre: 1.0+plus.post.dev1 +pep440-post: 1.0+plus.post1.dev0.g250b7ca +pep440-old: 1.0+plus.post1.dev0 +git-describe: 1.0+plus-1-g250b7ca-dirty +git-describe-long: 1.0+plus-1-g250b7ca-dirty + + +closest-tag: None +distance: 1 +dirty: False +pep440: 0+untagged.1.g250b7ca +pep440-pre: 0.post.dev1 +pep440-post: 0.post1+g250b7ca +pep440-old: 0.post1 +git-describe: 250b7ca +git-describe-long: 250b7ca + +closest-tag: None +distance: 1 +dirty: True +pep440: 0+untagged.1.g250b7ca.dirty +pep440-pre: 0.post.dev1 +pep440-post: 0.post1.dev0+g250b7ca +pep440-old: 0.post1.dev0 +git-describe: 250b7ca-dirty +git-describe-long: 250b7ca-dirty + +""" + + +class Test_RenderPieces(unittest.TestCase): + def do_render(self, pieces): + out = {} + for style in ["pep440", "pep440-pre", "pep440-post", "pep440-old", + "git-describe", "git-describe-long"]: + out[style] = render(pieces, style)["version"] + DEFAULT = "pep440" + self.assertEqual(render(pieces, ""), render(pieces, DEFAULT)) + self.assertEqual(render(pieces, "default"), render(pieces, DEFAULT)) + return out + + def parse_expected(self): + base_pieces = {"long": "250b7ca731388d8f016db2e06ab1d6289486424b", + "short": "250b7ca", + "error": None} + more_pieces = {} + expected = {} + for line in expected_renders.splitlines(): + line = line.strip() + if not line: + if more_pieces and expected: + pieces = base_pieces.copy() + pieces.update(more_pieces) + yield (pieces, expected) + more_pieces = {} + expected = {} + continue + name, value = line.split(":") + name = name.strip() + value = value.strip() + if name == "distance": + more_pieces["distance"] = int(value) + elif name == "dirty": + more_pieces["dirty"] = bool(value.lower() == "true") + elif name == "closest-tag": + more_pieces["closest-tag"] = value + if value == "None": + more_pieces["closest-tag"] = None + else: + expected[name] = value + if more_pieces and expected: + pieces = base_pieces.copy() + pieces.update(more_pieces) + yield (pieces, expected) + + def test_render(self): + for (pieces, expected) in self.parse_expected(): + got = self.do_render(pieces) + for key in expected: + self.assertEqual(got[key], expected[key], + (pieces, key, got[key], expected[key])) + + class Testing_renderer_case_mixin(object): """ This is a mixin object which can be combined with a unittest.TestCase From 53a94a9758cf48e42446f63b2cecf6f36a6c93e0 Mon Sep 17 00:00:00 2001 From: pelson Date: Fri, 26 Jun 2015 14:39:58 +0100 Subject: [PATCH 03/18] Fully implemented versioning based on branch. --- src/header.py | 5 +++ src/render.py | 44 +++++++++++++++++++------ test/git/test_git.py | 17 ++-------- test/test_render.py | 76 ++++++++++++++++++++++++++++++++++++-------- 4 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/header.py b/src/header.py index a3018ed1..90c91569 100644 --- a/src/header.py +++ b/src/header.py @@ -74,6 +74,9 @@ def get_config_from_root(root): parser.readfp(f) VCS = parser.get("versioneer", "VCS") # mandatory + # Default matches v1.2.x, maint/1.2.x, 1.2.x, 1.x etc. + default_maint_branch_regexp = ".*([0-9]+\.)+x$" + def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) @@ -88,6 +91,8 @@ def get(parser, name): cfg.tag_prefix = "" cfg.parentdir_prefix = get(parser, "parentdir_prefix") cfg.verbose = get(parser, "verbose") + cfg.maint_branch_regexp = (get(parser, "maint_branch_regexp") or + default_maint_branch_regexp) return cfg diff --git a/src/render.py b/src/render.py index f6a49b3e..9be9998c 100644 --- a/src/render.py +++ b/src/render.py @@ -1,5 +1,6 @@ import re # --STRIP DURING BUILD +from header import get_root, get_config_from_root # --STRIP DURING BUILD def plus_or_dot(pieces): @@ -139,7 +140,6 @@ def render_git_describe_long(pieces): return rendered -<<<<<<< HEAD def add_one_to_version(version_string, number_index_to_increment=-1): """ Add one to a version string at the given numeric indices. @@ -166,26 +166,48 @@ def add_one_to_version(version_string, number_index_to_increment=-1): return ''.join(parts) -def render_pep440_plus_one_dev(pieces): +def render_pep440_branch_based(pieces): # [TAG+1 of minor number][.devDISTANCE][+gHEX]. The git short is # included for dirty. # exceptions: # 1: no tags. 0.0.0.devDISTANCE[+gHEX] - if pieces["closest-tag"]: + cfg = get_config_from_root(get_root()) + + master = pieces.get('branch') == 'master' + maint = re.match(cfg.maint_branch_regexp, + pieces.get('branch') or '') + + # If we are on a tag, just pep440-pre it. + if pieces["closest-tag"] and not (pieces["distance"] or + pieces["dirty"]): + rendered = pieces["closest-tag"] + else: + # Put a default closest-tag in. + if not pieces["closest-tag"]: + pieces["closest-tag"] = '0.0.0' + if pieces["distance"] or pieces["dirty"]: - rendered = add_one_to_version(pieces["closest-tag"]) - rendered += ".dev%d" % pieces["distance"] + if maint: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post%d" % pieces["distance"] + else: + rendered = add_one_to_version(pieces["closest-tag"]) + if pieces["distance"]: + rendered += ".dev%d" % pieces["distance"] + # Put the branch name in if it isn't master nor a + # maintenance branch. + + if not (master or maint): + rendered += "+%s" % (pieces.get('branch') or + 'unknown_branch') + if pieces["dirty"]: rendered += "+g%s" % pieces["short"] else: rendered = pieces["closest-tag"] - else: - # exception #1 - rendered = "0.0.0.dev%d" % pieces["distance"] - if pieces["dirty"]: - rendered += "+g%s" % pieces["short"] return rendered @@ -209,6 +231,8 @@ def render(pieces, style): rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) + elif style == "pep440-branch-based": + rendered = render_pep440_branch_based(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": diff --git a/test/git/test_git.py b/test/git/test_git.py index 4087b6d5..4d80ef12 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -169,7 +169,6 @@ def test_new_tag_style(self): self.assertEqual(v["error"], None) -<<<<<<< HEAD expected_renders = """ closest-tag: 1.0 distance: 0 @@ -635,18 +634,6 @@ def assertPEP440(self, got, state, tree, runtime): class Test_GitRepo(common.Common, unittest.TestCase): - # We care about the following git scenarios: - # S1 : master, 0 commits, 0 tags, v0 - # S2 : master, 1 commits, 0 tags, v0.dev1 - # S3 : master, 1 commits, 1 tags, v1 - # S4 : master, 2 commits, 1 tags, v1.dev1 - # S5 : v1.x, 2 commits, 2 tags, v1.1 - # S6 : master, 3 commits, 1 tags, v2.pre2 - # (merge of v1.x back to master) - # - # For both clean and dirty situations (uncommitted code changes) - # We should also check export state. - expecteds = {'S1': None, 'S2': {'branch': 'master', 'closest-tag': None, @@ -693,6 +680,7 @@ class Test_GitRepo(common.Common, unittest.TestCase): def assert_case(self, case_name, dirty=False): tag_prefix = 'v' pieces = from_vcs.git_pieces_from_vcs(tag_prefix, self.repo_root, + verbose=False, run_command=run_command) pieces.pop('short') pieces.pop('long') @@ -726,7 +714,8 @@ def test(self): self.git("init") self.assertRaises(from_vcs.NotThisMethod, from_vcs.git_pieces_from_vcs, - tag_prefix, self.repo_root, run_command=run_command) + tag_prefix, self.repo_root, run_command=run_command, + verbose=False) # S2 self.write_file('a.txt', 'abc') diff --git a/test/test_render.py b/test/test_render.py index 0e51eea7..2c3151fe 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -157,7 +157,8 @@ def define_pieces(self, closest_tag, distance=0, dirty=False): "dirty": dirty, "short": "abc" if distance else '', "long": "abcdefg" if distance else '', - "date": "2016-05-31T13:02:11+0200"} + "date": "2016-05-31T13:02:11+0200", + "branch": getattr(self, 'branch', 'master')} def assert_rendered(self, pieces, test_case_name): version = render(pieces, self.style)['version'] @@ -305,19 +306,68 @@ def test_index_with_rc(self): self.assertEqual(result, 'v1.2.4rc0') +class Test_pep440_branch_based__master(unittest.TestCase, + Testing_renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'master' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.4+g', + 'tagged_1_commits_clean': 'v1.2.4.dev1', + 'tagged_1_commits_dirty': 'v1.2.4.dev1+gabc', + 'untagged_0_commits_clean': '0.0.0', + 'untagged_0_commits_dirty': '0.0.1+g', + 'untagged_1_commits_clean': '0.0.1.dev1', + 'untagged_1_commits_dirty': '0.0.1.dev1+gabc', + 'error_getting_parts': 'unknown' + } + + +class Test_pep440_branch_based__maint(unittest.TestCase, + Testing_renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'v1.2.x' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.3+g', + 'tagged_1_commits_clean': 'v1.2.3.post1', + 'tagged_1_commits_dirty': 'v1.2.3.post1+gabc', + 'untagged_0_commits_clean': '0.0.0', + 'untagged_0_commits_dirty': '0.0.0+g', + 'untagged_1_commits_clean': '0.0.0.post1', + 'untagged_1_commits_dirty': '0.0.0.post1+gabc', + 'error_getting_parts': 'unknown' + } -#class Test_git_describe(unittest.TestCase, Testing_renderer_case_mixin): -# style = 'pep440-branch-based' -# expected = {'tagged_0_commits_clean': 'v1.2.3', -# 'tagged_0_commits_dirty': 'v1.2.3-dirty', -# 'tagged_1_commits_clean': 'v1.2.3-1-gabc', -# 'tagged_1_commits_dirty': 'v1.2.3-1-gabc-dirty', -# 'untagged_0_commits_clean': '', -# 'untagged_0_commits_dirty': '-dirty', -# 'untagged_1_commits_clean': 'abc', -# 'untagged_1_commits_dirty': 'abc-dirty', -# 'error_getting_parts': 'unknown' -# } + +class Test_pep440_branch_based__feature_branch(unittest.TestCase, + Testing_renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'feature_branch' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.4+feature_branch+g', + 'tagged_1_commits_clean': 'v1.2.4.dev1+feature_branch', + 'tagged_1_commits_dirty': 'v1.2.4.dev1+feature_branch+gabc', + 'untagged_0_commits_clean': '0.0.0', + 'untagged_0_commits_dirty': '0.0.1+feature_branch+g', + 'untagged_1_commits_clean': '0.0.1.dev1+feature_branch', + 'untagged_1_commits_dirty': '0.0.1.dev1+feature_branch+gabc', + 'error_getting_parts': 'unknown' + } + + +class Test_pep440_branch_based__no_branch_info(unittest.TestCase, + Testing_renderer_case_mixin): + style = 'pep440-branch-based' + branch = None + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.4+unknown_branch+g', + 'tagged_1_commits_clean': 'v1.2.4.dev1+unknown_branch', + 'tagged_1_commits_dirty': 'v1.2.4.dev1+unknown_branch+gabc', + 'untagged_0_commits_clean': '0.0.0', + 'untagged_0_commits_dirty': '0.0.1+unknown_branch+g', + 'untagged_1_commits_clean': '0.0.1.dev1+unknown_branch', + 'untagged_1_commits_dirty': '0.0.1.dev1+unknown_branch+gabc', + 'error_getting_parts': 'unknown' + } if __name__ == '__main__': From d72e30f0087d66c171b63fbc2ea26bf8b8e3a9f1 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Mon, 6 Jul 2015 22:22:14 -0500 Subject: [PATCH 04/18] Handle checked out SHA which lives on a branch. --- src/git/from_vcs.py | 12 +++++++++++- test/git/test_git.py | 5 +++++ test/test_render.py | 1 - 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index 883c7401..f17b93ab 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -56,7 +56,17 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): branch_name = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root).strip() if branch_name == 'HEAD': - branch_name = None + branches = run_command(GITS, ["branch", "--list", "--contains"], + cwd=root).split('\n') + branches = [branch[2:] for branch in branches if branch[4:5] != '('] + if 'master' in branches: + branch_name = 'master' + elif not branches: + branch_name = None + else: + # Pick the first branch that is returned. Good or bad. + branch_name = branches[0] + pieces['branch'] = branch_name # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] diff --git a/test/git/test_git.py b/test/git/test_git.py index 4d80ef12..e40def45 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -738,6 +738,11 @@ def test(self): # S5 self.git("tag", "-a", "v2.0", "-m", "Second tag.") self.assert_case('S5') + # Even checking out the SHA, without a branch, should figure out the + # appropriate branch name. + sha = self.git("rev-parse", "HEAD") + self.git("checkout", sha) + self.assert_case('S5') # S6 self.git("checkout", "v1.x") diff --git a/test/test_render.py b/test/test_render.py index 2c3151fe..d176c7e6 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -2,7 +2,6 @@ from versioneer import render, add_one_to_version -from versioneer_src.render import add_one_to_version expected_renders = """ closest-tag: 1.0 From 74285eafe097b70e1c2bbf437660d5df02f14c33 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Tue, 7 Jul 2015 15:46:41 -0500 Subject: [PATCH 05/18] Flake8, remove need for config. --- src/git/long_get_versions.py | 1 + src/header.py | 5 ----- src/render.py | 7 +++---- test/test_render.py | 20 ++++++++++---------- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/git/long_get_versions.py b/src/git/long_get_versions.py index af081a5a..d5497236 100644 --- a/src/git/long_get_versions.py +++ b/src/git/long_get_versions.py @@ -7,6 +7,7 @@ def versions_from_parentdir(): pass # --STRIP DURING BUILD class NotThisMethod(Exception): pass # --STRIP DURING BUILD def render(): pass # --STRIP DURING BUILD + def get_versions(): """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have diff --git a/src/header.py b/src/header.py index 90c91569..a3018ed1 100644 --- a/src/header.py +++ b/src/header.py @@ -74,9 +74,6 @@ def get_config_from_root(root): parser.readfp(f) VCS = parser.get("versioneer", "VCS") # mandatory - # Default matches v1.2.x, maint/1.2.x, 1.2.x, 1.x etc. - default_maint_branch_regexp = ".*([0-9]+\.)+x$" - def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) @@ -91,8 +88,6 @@ def get(parser, name): cfg.tag_prefix = "" cfg.parentdir_prefix = get(parser, "parentdir_prefix") cfg.verbose = get(parser, "verbose") - cfg.maint_branch_regexp = (get(parser, "maint_branch_regexp") or - default_maint_branch_regexp) return cfg diff --git a/src/render.py b/src/render.py index 9be9998c..924deec3 100644 --- a/src/render.py +++ b/src/render.py @@ -1,6 +1,7 @@ import re # --STRIP DURING BUILD -from header import get_root, get_config_from_root # --STRIP DURING BUILD +# Default matches v1.2.x, maint/1.2.x, 1.2.x, 1.x etc. +default_maint_branch_regexp = ".*([0-9]+\.)+x$" def plus_or_dot(pieces): @@ -173,10 +174,8 @@ def render_pep440_branch_based(pieces): # exceptions: # 1: no tags. 0.0.0.devDISTANCE[+gHEX] - cfg = get_config_from_root(get_root()) - master = pieces.get('branch') == 'master' - maint = re.match(cfg.maint_branch_regexp, + maint = re.match(default_maint_branch_regexp, pieces.get('branch') or '') # If we are on a tag, just pep440-pre it. diff --git a/test/test_render.py b/test/test_render.py index d176c7e6..bf3e4cd5 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -142,7 +142,7 @@ def test_render(self): (pieces, key, got[key], expected[key])) -class Testing_renderer_case_mixin(object): +class renderer_case_mixin(object): """ This is a mixin object which can be combined with a unittest.TestCase which defines a style and an expected dictionary. See Test_pep440 for @@ -207,7 +207,7 @@ def test_error_getting_parts(self): 'error_getting_parts') -class Test_pep440(unittest.TestCase, Testing_renderer_case_mixin): +class Test_pep440(unittest.TestCase, renderer_case_mixin): style = 'pep440' expected = {'tagged_0_commits_clean': 'v1.2.3', 'tagged_0_commits_dirty': 'v1.2.3+0.g.dirty', @@ -221,7 +221,7 @@ class Test_pep440(unittest.TestCase, Testing_renderer_case_mixin): } -class Test_pep440_old(unittest.TestCase, Testing_renderer_case_mixin): +class Test_pep440_old(unittest.TestCase, renderer_case_mixin): style = 'pep440-old' expected = {'tagged_0_commits_clean': 'v1.2.3', 'tagged_0_commits_dirty': 'v1.2.3.post0.dev0', @@ -235,7 +235,7 @@ class Test_pep440_old(unittest.TestCase, Testing_renderer_case_mixin): } -class Test_pep440_post(unittest.TestCase, Testing_renderer_case_mixin): +class Test_pep440_post(unittest.TestCase, renderer_case_mixin): style = 'pep440-post' expected = {'tagged_0_commits_clean': 'v1.2.3', 'tagged_0_commits_dirty': 'v1.2.3.post0.dev0+g', @@ -249,7 +249,7 @@ class Test_pep440_post(unittest.TestCase, Testing_renderer_case_mixin): } -class Test_pep440_pre(unittest.TestCase, Testing_renderer_case_mixin): +class Test_pep440_pre(unittest.TestCase, renderer_case_mixin): style = 'pep440-pre' expected = {'tagged_0_commits_clean': 'v1.2.3', 'tagged_0_commits_dirty': 'v1.2.3', @@ -263,7 +263,7 @@ class Test_pep440_pre(unittest.TestCase, Testing_renderer_case_mixin): } -class Test_git_describe(unittest.TestCase, Testing_renderer_case_mixin): +class Test_git_describe(unittest.TestCase, renderer_case_mixin): style = 'git-describe' expected = {'tagged_0_commits_clean': 'v1.2.3', 'tagged_0_commits_dirty': 'v1.2.3-dirty', @@ -306,7 +306,7 @@ def test_index_with_rc(self): class Test_pep440_branch_based__master(unittest.TestCase, - Testing_renderer_case_mixin): + renderer_case_mixin): style = 'pep440-branch-based' branch = 'master' expected = {'tagged_0_commits_clean': 'v1.2.3', @@ -322,7 +322,7 @@ class Test_pep440_branch_based__master(unittest.TestCase, class Test_pep440_branch_based__maint(unittest.TestCase, - Testing_renderer_case_mixin): + renderer_case_mixin): style = 'pep440-branch-based' branch = 'v1.2.x' expected = {'tagged_0_commits_clean': 'v1.2.3', @@ -338,7 +338,7 @@ class Test_pep440_branch_based__maint(unittest.TestCase, class Test_pep440_branch_based__feature_branch(unittest.TestCase, - Testing_renderer_case_mixin): + renderer_case_mixin): style = 'pep440-branch-based' branch = 'feature_branch' expected = {'tagged_0_commits_clean': 'v1.2.3', @@ -354,7 +354,7 @@ class Test_pep440_branch_based__feature_branch(unittest.TestCase, class Test_pep440_branch_based__no_branch_info(unittest.TestCase, - Testing_renderer_case_mixin): + renderer_case_mixin): style = 'pep440-branch-based' branch = None expected = {'tagged_0_commits_clean': 'v1.2.3', From cb3829a24ec934d5b3ba98507dd5b72d844c183c Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Sat, 10 Oct 2015 10:29:13 +0100 Subject: [PATCH 06/18] Fixed branch determination for git 1.7. --- src/git/from_vcs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index f17b93ab..692d9e09 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -56,7 +56,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): branch_name = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root).strip() if branch_name == 'HEAD': - branches = run_command(GITS, ["branch", "--list", "--contains"], + branches = run_command(GITS, ["branch", "--contains"], cwd=root).split('\n') branches = [branch[2:] for branch in branches if branch[4:5] != '('] if 'master' in branches: From 0e69983268d9e5a36bf20a98b514d9d6030f9d50 Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Fri, 13 Nov 2015 11:57:35 +0000 Subject: [PATCH 07/18] Updated git branch to work in more situations. --- setup.py | 2 +- src/git/from_vcs.py | 7 ++++++- src/render.py | 21 +++++++++++++-------- test/git/test_git.py | 2 ++ test/test_render.py | 16 ++++++++-------- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/setup.py b/setup.py index 2323750a..b5e23eaa 100755 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ def generate_versioneer_py(): s.write(get("src/subprocess_helper.py", do_strip=True)) for VCS in get_vcs_list(): - s.write(u("LONG_VERSION_PY['%s'] = '''\n" % VCS)) + s.write(u("LONG_VERSION_PY['%s'] = r'''\n" % VCS)) s.write(generate_long_version_py(VCS)) s.write(u("'''\n")) s.write(u("\n\n")) diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index 692d9e09..4209d585 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -56,9 +56,14 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): branch_name = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root).strip() if branch_name == 'HEAD': + # If we aren't exactly on a branch, pick a branch which represents + # the current commit. If all else fails, we are on a branchless + # commit. branches = run_command(GITS, ["branch", "--contains"], cwd=root).split('\n') - branches = [branch[2:] for branch in branches if branch[4:5] != '('] + # Strip off the leading "* " from the list of branches. + branches = [branch[2:] for branch in branches + if branch and branch[4:5] != '('] if 'master' in branches: branch_name = 'master' elif not branches: diff --git a/src/render.py b/src/render.py index 924deec3..793109ba 100644 --- a/src/render.py +++ b/src/render.py @@ -174,9 +174,12 @@ def render_pep440_branch_based(pieces): # exceptions: # 1: no tags. 0.0.0.devDISTANCE[+gHEX] - master = pieces.get('branch') == 'master' - maint = re.match(default_maint_branch_regexp, - pieces.get('branch') or '') + replacements = ([' ', '.'], ['(', ''], [')', '']) + branch_name = pieces.get('branch') or '' + for old, new in replacements: + branch_name = branch_name.replace(old, new) + master = branch_name == 'master' + maint = re.match(default_maint_branch_regexp, branch_name) # If we are on a tag, just pep440-pre it. if pieces["closest-tag"] and not (pieces["distance"] or @@ -196,15 +199,17 @@ def render_pep440_branch_based(pieces): rendered = add_one_to_version(pieces["closest-tag"]) if pieces["distance"]: rendered += ".dev%d" % pieces["distance"] - # Put the branch name in if it isn't master nor a - # maintenance branch. + suffix = [] + # Put the branch name in if it isn't master nor a + # maintenance branch. if not (master or maint): - rendered += "+%s" % (pieces.get('branch') or - 'unknown_branch') + suffix.append('%s' % (branch_name or 'unknown_branch')) if pieces["dirty"]: - rendered += "+g%s" % pieces["short"] + suffix.append('g%s' % pieces["short"]) + if suffix: + rendered += '+%s' % '_'.join(suffix) else: rendered = pieces["closest-tag"] return rendered diff --git a/test/git/test_git.py b/test/git/test_git.py index e40def45..3db0a616 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -42,6 +42,8 @@ def fake_run_command(exes, args, cwd=None, hide_stderr=None): return "42\n", 0 if args[0] == "show": return "12345\n", 0 + elif args[0:2] == ["branch", "--contains"]: + return '' self.fail("git called in weird way: %s" % (args,)) return from_vcs.git_pieces_from_vcs( "v", self.fakeroot, verbose=False, diff --git a/test/test_render.py b/test/test_render.py index bf3e4cd5..138fcb71 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -342,13 +342,13 @@ class Test_pep440_branch_based__feature_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'feature_branch' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.4+feature_branch+g', + 'tagged_0_commits_dirty': 'v1.2.4+feature_branch_g', 'tagged_1_commits_clean': 'v1.2.4.dev1+feature_branch', - 'tagged_1_commits_dirty': 'v1.2.4.dev1+feature_branch+gabc', + 'tagged_1_commits_dirty': 'v1.2.4.dev1+feature_branch_gabc', 'untagged_0_commits_clean': '0.0.0', - 'untagged_0_commits_dirty': '0.0.1+feature_branch+g', + 'untagged_0_commits_dirty': '0.0.1+feature_branch_g', 'untagged_1_commits_clean': '0.0.1.dev1+feature_branch', - 'untagged_1_commits_dirty': '0.0.1.dev1+feature_branch+gabc', + 'untagged_1_commits_dirty': '0.0.1.dev1+feature_branch_gabc', 'error_getting_parts': 'unknown' } @@ -358,13 +358,13 @@ class Test_pep440_branch_based__no_branch_info(unittest.TestCase, style = 'pep440-branch-based' branch = None expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.4+unknown_branch+g', + 'tagged_0_commits_dirty': 'v1.2.4+unknown_branch_g', 'tagged_1_commits_clean': 'v1.2.4.dev1+unknown_branch', - 'tagged_1_commits_dirty': 'v1.2.4.dev1+unknown_branch+gabc', + 'tagged_1_commits_dirty': 'v1.2.4.dev1+unknown_branch_gabc', 'untagged_0_commits_clean': '0.0.0', - 'untagged_0_commits_dirty': '0.0.1+unknown_branch+g', + 'untagged_0_commits_dirty': '0.0.1+unknown_branch_g', 'untagged_1_commits_clean': '0.0.1.dev1+unknown_branch', - 'untagged_1_commits_dirty': '0.0.1.dev1+unknown_branch+gabc', + 'untagged_1_commits_dirty': '0.0.1.dev1+unknown_branch_gabc', 'error_getting_parts': 'unknown' } From b687778eb5a3f77f4bda607b6172e2c137324cde Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 12:15:52 +0000 Subject: [PATCH 08/18] Ignore `__pycache__` directories --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b5e23eaa..834a2d19 100755 --- a/setup.py +++ b/setup.py @@ -51,7 +51,8 @@ def get_vcs_list(): return [filename for filename in os.listdir(project_path) - if path.isdir(path.join(project_path, filename))] + if path.isdir(path.join(project_path, filename)) + and filename != '__pycache__'] def generate_long_version_py(VCS): s = io.StringIO() From 6671e261e7895999455cae1a4c7f408a834034ab Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 12:16:25 +0000 Subject: [PATCH 09/18] Fix run_command calls --- src/git/from_vcs.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index 4209d585..a7547493 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -53,14 +53,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): pieces["error"] = None # abbrev-ref available with git >= 1.7 - branch_name = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root).strip() + branch_name_out, rc = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + branch_name = branch_name_out.strip() if branch_name == 'HEAD': # If we aren't exactly on a branch, pick a branch which represents # the current commit. If all else fails, we are on a branchless # commit. - branches = run_command(GITS, ["branch", "--contains"], - cwd=root).split('\n') + branches_out, rc = run_command(GITS, ["branch", "--contains"], + cwd=root) + branches = branches_out.split('\n') # Strip off the leading "* " from the list of branches. branches = [branch[2:] for branch in branches if branch and branch[4:5] != '('] From 020627628b476f1e638d01d779a731468b1cfc80 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 12:16:51 +0000 Subject: [PATCH 10/18] Adapt to newer test procedures --- test/git/test_git.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/git/test_git.py b/test/git/test_git.py index 3db0a616..ddddcb32 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -43,7 +43,7 @@ def fake_run_command(exes, args, cwd=None, hide_stderr=None): if args[0] == "show": return "12345\n", 0 elif args[0:2] == ["branch", "--contains"]: - return '' + return '', 0 self.fail("git called in weird way: %s" % (args,)) return from_vcs.git_pieces_from_vcs( "v", self.fakeroot, verbose=False, @@ -92,7 +92,8 @@ def fake_run_command(exes, args, cwd=None, hide_stderr=None): "dirty": True, "error": None, "distance": 1, "long": "longlong", - "short": "1f"}) + "short": "1f", + "date": "12345"}) self.assertEqual(pv("v1.0-1-g1f-dirty", branch_name="v1.0.x"), {"branch": 'v1.0.x', "closest-tag": "1.0", "dirty": True, "error": None, @@ -343,7 +344,7 @@ class Test_RepoIntegration(common.Common, unittest.TestCase): # or test/demoapp-script-only/) def test_full(self): - self.run_test("test/demoapp", False, ".") + self.run_case("test/demoapp", False, ".") def test_script_only(self): # This test looks at an application that consists entirely of a @@ -352,12 +353,12 @@ def test_script_only(self): # anything executable. So of the 3 runtime situations examined by # Repo.test_full above, we only care about RB. (RA1 is valid too, but # covered by Repo). - self.run_test("test/demoapp-script-only", True, ".") + self.run_case("test/demoapp-script-only", True, ".") def test_project_in_subdir(self): # This test sets of the git repository so that the python project -- # i.e. setup.py -- is not located in the root directory - self.run_test("test/demoapp", False, "project") + self.run_case("test/demoapp", False, "project") def run_case(self, demoapp_dir, script_only, project_sub_dir): self.testdir = tempfile.mkdtemp() From e2cbb92b1ef2f3a0c9b25afa88f18bc46a020c7a Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 12:33:54 +0000 Subject: [PATCH 11/18] Evaluate branch after describe --- src/git/from_vcs.py | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index a7547493..07410b9a 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -52,30 +52,6 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None - # abbrev-ref available with git >= 1.7 - branch_name_out, rc = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], - cwd=root) - branch_name = branch_name_out.strip() - if branch_name == 'HEAD': - # If we aren't exactly on a branch, pick a branch which represents - # the current commit. If all else fails, we are on a branchless - # commit. - branches_out, rc = run_command(GITS, ["branch", "--contains"], - cwd=root) - branches = branches_out.split('\n') - # Strip off the leading "* " from the list of branches. - branches = [branch[2:] for branch in branches - if branch and branch[4:5] != '('] - if 'master' in branches: - branch_name = 'master' - elif not branches: - branch_name = None - else: - # Pick the first branch that is returned. Good or bad. - branch_name = branches[0] - - pieces['branch'] = branch_name - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out @@ -121,6 +97,30 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): cwd=root) pieces["distance"] = int(count_out) # total number of commits + # abbrev-ref available with git >= 1.7 + branch_name_out, rc = run_command(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + branch_name = branch_name_out.strip() + if branch_name == 'HEAD': + # If we aren't exactly on a branch, pick a branch which represents + # the current commit. If all else fails, we are on a branchless + # commit. + branches_out, rc = run_command(GITS, ["branch", "--contains"], + cwd=root) + branches = branches_out.split('\n') + # Strip off the leading "* " from the list of branches. + branches = [branch[2:] for branch in branches + if branch and branch[4:5] != '('] + if 'master' in branches: + branch_name = 'master' + elif not branches: + branch_name = None + else: + # Pick the first branch that is returned. Good or bad. + branch_name = branches[0] + + pieces['branch'] = branch_name + # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() From 15e173e85f03ff45a876ed9868f1fc28865fe823 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 12:35:25 +0000 Subject: [PATCH 12/18] Adapt to expected gitdir attribute. Remove date from pieces. --- test/git/test_git.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/test/git/test_git.py b/test/git/test_git.py index ddddcb32..a4e1d7fd 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -681,12 +681,12 @@ class Test_GitRepo(common.Common, unittest.TestCase): } def assert_case(self, case_name, dirty=False): - tag_prefix = 'v' - pieces = from_vcs.git_pieces_from_vcs(tag_prefix, self.repo_root, + pieces = from_vcs.git_pieces_from_vcs(self.tag_prefix, self.gitdir, verbose=False, run_command=run_command) pieces.pop('short') pieces.pop('long') + pieces.pop('date') expected = self.expecteds.get(case_name, {}) if dirty: @@ -697,27 +697,31 @@ def assert_case(self, case_name, dirty=False): self.assertEqual(expected, pieces) def write_file(self, fname, content): - with open(os.path.join(self.repo_root, fname), 'w') as fh: + with open(os.path.join(self.gitdir, fname), 'w') as fh: fh.write(content) + + def tearDown(self): + if os.path.exists(self.gitdir): + import shutil + shutil.rmtree(self.gitdir) + def test(self): - tag_prefix = 'v' + self.tag_prefix = 'v' self.testdir = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'git_test_repo') - self.repo_root = os.path.join(self.testdir, 'demoapp') + 'git_test_repo') + self.gitdir = os.path.join(self.testdir, 'demoapp') # Cleanup - if os.path.exists(self.repo_root): - import shutil - shutil.rmtree(self.repo_root) - os.makedirs(self.repo_root) + self.tearDown() + os.makedirs(self.gitdir) # S1 self.git("init") self.assertRaises(from_vcs.NotThisMethod, from_vcs.git_pieces_from_vcs, - tag_prefix, self.repo_root, run_command=run_command, + self.tag_prefix, self.gitdir, run_command=run_command, verbose=False) # S2 From b36dc7180a44fe6b0c0b7822846fb4a9389a0483 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 14:45:54 +0000 Subject: [PATCH 13/18] Identical to pep440 with the obvious exception of branch info --- src/render.py | 93 ++++++++++++++------------------------------ test/test_render.py | 94 ++++++++++++++++++--------------------------- 2 files changed, 65 insertions(+), 122 deletions(-) diff --git a/src/render.py b/src/render.py index 793109ba..207fadb8 100644 --- a/src/render.py +++ b/src/render.py @@ -1,8 +1,5 @@ import re # --STRIP DURING BUILD -# Default matches v1.2.x, maint/1.2.x, 1.2.x, 1.x etc. -default_maint_branch_regexp = ".*([0-9]+\.)+x$" - def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" @@ -141,77 +138,43 @@ def render_git_describe_long(pieces): return rendered -def add_one_to_version(version_string, number_index_to_increment=-1): - """ - Add one to a version string at the given numeric indices. - - >>> add_one_to_version('v1.2.3') - 'v1.2.4' - - """ - # Break up the tag by number groups (preserving multi-digit - # numbers as multidigit) - parts = re.split("([0-9]+)", version_string) - - digit_parts = [(i, part) for i, part in enumerate(parts) - if part.isdigit()] - - # Deal with negative indexing. - increment_at_index = ((number_index_to_increment + len(digit_parts)) - % len(digit_parts)) - for n_seen, (i, part) in enumerate(digit_parts): - if n_seen == increment_at_index: - parts[i] = str(int(part) + 1) - elif n_seen > increment_at_index: - parts[i] = '0' - return ''.join(parts) - - def render_pep440_branch_based(pieces): - # [TAG+1 of minor number][.devDISTANCE][+gHEX]. The git short is - # included for dirty. + """Build up version string, with post-release "local version identifier". - # exceptions: - # 1: no tags. 0.0.0.devDISTANCE[+gHEX] + Our goal: TAG[+DISTANCE.BRANCH_gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.BRANCH_gHEX.dirty + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.BRANCH_gHEX[.dirty] + """ replacements = ([' ', '.'], ['(', ''], [')', '']) branch_name = pieces.get('branch') or '' - for old, new in replacements: - branch_name = branch_name.replace(old, new) - master = branch_name == 'master' - maint = re.match(default_maint_branch_regexp, branch_name) - - # If we are on a tag, just pep440-pre it. - if pieces["closest-tag"] and not (pieces["distance"] or - pieces["dirty"]): - rendered = pieces["closest-tag"] + if branch_name: + for old, new in replacements: + branch_name = branch_name.replace(old, new) else: - # Put a default closest-tag in. - if not pieces["closest-tag"]: - pieces["closest-tag"] = '0.0.0' + branch_name = 'unknown_branch' + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: - if maint: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post%d" % pieces["distance"] - else: - rendered = add_one_to_version(pieces["closest-tag"]) - if pieces["distance"]: - rendered += ".dev%d" % pieces["distance"] - - suffix = [] - # Put the branch name in if it isn't master nor a - # maintenance branch. - if not (master or maint): - suffix.append('%s' % (branch_name or 'unknown_branch')) - + rendered += plus_or_dot(pieces) + rendered += "%d.%s.g%s" % ( + pieces["distance"], + branch_name, + pieces['short'] + ) if pieces["dirty"]: - suffix.append('g%s' % pieces["short"]) - if suffix: - rendered += '+%s' % '_'.join(suffix) - else: - rendered = pieces["closest-tag"] + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.%s.g%s" % ( + pieces["distance"], + branch_name, + pieces['short'] + ) + if pieces["dirty"]: + rendered += ".dirty" return rendered diff --git a/test/test_render.py b/test/test_render.py index 138fcb71..e5899a62 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -1,6 +1,14 @@ +import os +import sys import unittest -from versioneer import render, add_one_to_version +SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +if not os.path.exists(os.path.join(SOURCE_ROOT, 'versioneer.py')): + print('Please run "python setup.py make_versioneer"') + sys.exit(1) + +sys.path.insert(0, SOURCE_ROOT) +from versioneer import render expected_renders = """ @@ -277,46 +285,18 @@ class Test_git_describe(unittest.TestCase, renderer_case_mixin): } -class Test_add_one_to_version(unittest.TestCase): - def test_index_0(self): - result = add_one_to_version('v1.2.3', 0) - self.assertEqual(result, 'v2.0.0') - - def test_index_1(self): - result = add_one_to_version('v1.2.3', 1) - self.assertEqual(result, 'v1.3.0') - - def test_index_2(self): - result = add_one_to_version('v1.2.3', 2) - self.assertEqual(result, 'v1.2.4') - - def test_negative_indexing(self): - result = add_one_to_version('v1.2.3', -2) - self.assertEqual(result, 'v1.3.0') - - def test_year_version(self): - result = add_one_to_version('1066.8', 1) - self.assertEqual(result, '1066.9') - - def test_index_with_rc(self): - # Note this is not the result you would want from a style, - # but it is the expected behaviour of this function. - result = add_one_to_version('v1.2.3rc4', 2) - self.assertEqual(result, 'v1.2.4rc0') - - class Test_pep440_branch_based__master(unittest.TestCase, renderer_case_mixin): style = 'pep440-branch-based' branch = 'master' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.4+g', - 'tagged_1_commits_clean': 'v1.2.4.dev1', - 'tagged_1_commits_dirty': 'v1.2.4.dev1+gabc', - 'untagged_0_commits_clean': '0.0.0', - 'untagged_0_commits_dirty': '0.0.1+g', - 'untagged_1_commits_clean': '0.0.1.dev1', - 'untagged_1_commits_dirty': '0.0.1.dev1+gabc', + 'tagged_0_commits_dirty': 'v1.2.3+0.master.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.master.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.master.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.master.g', + 'untagged_0_commits_dirty': '0+untagged.0.master.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.master.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.master.gabc.dirty', 'error_getting_parts': 'unknown' } @@ -326,13 +306,13 @@ class Test_pep440_branch_based__maint(unittest.TestCase, style = 'pep440-branch-based' branch = 'v1.2.x' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+g', - 'tagged_1_commits_clean': 'v1.2.3.post1', - 'tagged_1_commits_dirty': 'v1.2.3.post1+gabc', - 'untagged_0_commits_clean': '0.0.0', - 'untagged_0_commits_dirty': '0.0.0+g', - 'untagged_1_commits_clean': '0.0.0.post1', - 'untagged_1_commits_dirty': '0.0.0.post1+gabc', + 'tagged_0_commits_dirty': 'v1.2.3+0.v1.2.x.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.v1.2.x.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.v1.2.x.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.v1.2.x.g', + 'untagged_0_commits_dirty': '0+untagged.0.v1.2.x.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.v1.2.x.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.v1.2.x.gabc.dirty', 'error_getting_parts': 'unknown' } @@ -342,13 +322,13 @@ class Test_pep440_branch_based__feature_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'feature_branch' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.4+feature_branch_g', - 'tagged_1_commits_clean': 'v1.2.4.dev1+feature_branch', - 'tagged_1_commits_dirty': 'v1.2.4.dev1+feature_branch_gabc', - 'untagged_0_commits_clean': '0.0.0', - 'untagged_0_commits_dirty': '0.0.1+feature_branch_g', - 'untagged_1_commits_clean': '0.0.1.dev1+feature_branch', - 'untagged_1_commits_dirty': '0.0.1.dev1+feature_branch_gabc', + 'tagged_0_commits_dirty': 'v1.2.3+0.feature_branch.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.feature_branch.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.feature_branch.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.feature_branch.g', + 'untagged_0_commits_dirty': '0+untagged.0.feature_branch.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.feature_branch.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.feature_branch.gabc.dirty', 'error_getting_parts': 'unknown' } @@ -358,13 +338,13 @@ class Test_pep440_branch_based__no_branch_info(unittest.TestCase, style = 'pep440-branch-based' branch = None expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.4+unknown_branch_g', - 'tagged_1_commits_clean': 'v1.2.4.dev1+unknown_branch', - 'tagged_1_commits_dirty': 'v1.2.4.dev1+unknown_branch_gabc', - 'untagged_0_commits_clean': '0.0.0', - 'untagged_0_commits_dirty': '0.0.1+unknown_branch_g', - 'untagged_1_commits_clean': '0.0.1.dev1+unknown_branch', - 'untagged_1_commits_dirty': '0.0.1.dev1+unknown_branch_gabc', + 'tagged_0_commits_dirty': 'v1.2.3+0.unknown_branch.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.unknown_branch.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.unknown_branch.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.unknown_branch.g', + 'untagged_0_commits_dirty': '0+untagged.0.unknown_branch.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.unknown_branch.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.unknown_branch.gabc.dirty', 'error_getting_parts': 'unknown' } From 6ecca8a5d5e03afe1d3a466c25894b67cc41efee Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 15:06:17 +0000 Subject: [PATCH 14/18] Don't force the use of relative imports --- src/setupfunc.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/setupfunc.py b/src/setupfunc.py index 69805c28..51b9d86e 100644 --- a/src/setupfunc.py +++ b/src/setupfunc.py @@ -1,6 +1,6 @@ from __future__ import print_function # --STRIP DURING BUILD -import os, sys # --STRIP DURING BUILD +import os, re, sys # --STRIP DURING BUILD def get_root(): pass # --STRIP DURING BUILD def get_config_from_root(): pass # --STRIP DURING BUILD LONG_VERSION_PY = {} # --STRIP DURING BUILD @@ -50,6 +50,13 @@ def do_vcs_install(): pass # --STRIP DURING BUILD del get_versions """ +INIT_PY_SNIPPET_RE = re.compile(INIT_PY_SNIPPET.replace( + '(', r'\(').replace( + ')', r'\)').replace( + '[', r'\[').replace( + ']', r'\]').replace( + ' ._version', r' ([\w\._]+)?._version'), re.MULTILINE) + def do_setup(): """Do main VCS-independent setup function for installing Versioneer.""" @@ -84,7 +91,7 @@ def do_setup(): old = f.read() except EnvironmentError: old = "" - if INIT_PY_SNIPPET not in old: + if INIT_PY_SNIPPET_RE.search(old) is None: print(" appending to %s" % ipy) with open(ipy, "a") as f: f.write(INIT_PY_SNIPPET) From f86b92b8577aebc76c4e978c4c07e2a3db5993e7 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Wed, 14 Feb 2018 15:12:41 +0000 Subject: [PATCH 15/18] `versioneer.get_cmdclass` now accepts an argument. Improve matching. --- src/setupfunc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setupfunc.py b/src/setupfunc.py index 51b9d86e..7d973d67 100644 --- a/src/setupfunc.py +++ b/src/setupfunc.py @@ -149,7 +149,7 @@ def scan_setup_py(): for line in f.readlines(): if "import versioneer" in line: found.add("import") - if "versioneer.get_cmdclass()" in line: + if "versioneer.get_cmdclass(" in line: found.add("cmdclass") if "versioneer.get_version()" in line: found.add("get_version") From 0ad11413464f0c2eccfc5b921233dd5dbbec2d22 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Thu, 15 Feb 2018 12:36:39 +0000 Subject: [PATCH 16/18] Add test coverage for branch names which bad characters --- src/render.py | 2 +- test/test_render.py | 72 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/render.py b/src/render.py index 207fadb8..027704e9 100644 --- a/src/render.py +++ b/src/render.py @@ -147,7 +147,7 @@ def render_pep440_branch_based(pieces): Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.BRANCH_gHEX[.dirty] """ - replacements = ([' ', '.'], ['(', ''], [')', '']) + replacements = ([' ', '.'], ['(', ''], [')', ''], ['\\', '-'], ['/', '-']) branch_name = pieces.get('branch') or '' if branch_name: for old, new in replacements: diff --git a/test/test_render.py b/test/test_render.py index e5899a62..e34f5d6b 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -158,6 +158,12 @@ class renderer_case_mixin(object): """ def define_pieces(self, closest_tag, distance=0, dirty=False): + branch = getattr(self, 'branch', 'master') + if branch: + replacements = ([' ', '.'], ['(', ''], [')', ''], ['\\', '-'], ['/', '-']) + for old, new in replacements: + branch = branch.replace(old, new) + return {"error": '', "closest-tag": closest_tag, "distance": distance, @@ -165,7 +171,7 @@ def define_pieces(self, closest_tag, distance=0, dirty=False): "short": "abc" if distance else '', "long": "abcdefg" if distance else '', "date": "2016-05-31T13:02:11+0200", - "branch": getattr(self, 'branch', 'master')} + "branch": branch} def assert_rendered(self, pieces, test_case_name): version = render(pieces, self.style)['version'] @@ -349,5 +355,69 @@ class Test_pep440_branch_based__no_branch_info(unittest.TestCase, } +class Test_pep440_branch_based__space_in_branch(unittest.TestCase, + renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'foo bar' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.3+0.foo.bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.foo.bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.foo.bar.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.foo.bar.g', + 'untagged_0_commits_dirty': '0+untagged.0.foo.bar.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.foo.bar.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.foo.bar.gabc.dirty', + 'error_getting_parts': 'unknown' + } + + +class Test_pep440_branch_based__forward_slash_in_branch(unittest.TestCase, + renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'foo/bar' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.3+0.foo-bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.foo-bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.foo-bar.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.foo-bar.g', + 'untagged_0_commits_dirty': '0+untagged.0.foo-bar.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.foo-bar.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.foo-bar.gabc.dirty', + 'error_getting_parts': 'unknown' + } + + +class Test_pep440_branch_based__back_slash_in_branch(unittest.TestCase, + renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'foo\\bar' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.3+0.foo-bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.foo-bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.foo-bar.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.foo-bar.g', + 'untagged_0_commits_dirty': '0+untagged.0.foo-bar.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.foo-bar.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.foo-bar.gabc.dirty', + 'error_getting_parts': 'unknown' + } + + +class Test_pep440_branch_based__parenthesis_in_branch(unittest.TestCase, + renderer_case_mixin): + style = 'pep440-branch-based' + branch = 'foo(bar)' + expected = {'tagged_0_commits_clean': 'v1.2.3', + 'tagged_0_commits_dirty': 'v1.2.3+0.foobar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.foobar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.foobar.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.foobar.g', + 'untagged_0_commits_dirty': '0+untagged.0.foobar.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.foobar.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.foobar.gabc.dirty', + 'error_getting_parts': 'unknown' + } + + if __name__ == '__main__': unittest.main() From 71771eca3452533b094aa7aaef2fc405cce1ace2 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Sat, 17 Feb 2018 16:22:48 +0000 Subject: [PATCH 17/18] Dashes are not allowed and are normalized to dots --- src/render.py | 2 +- test/test_render.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/render.py b/src/render.py index 027704e9..d1850fce 100644 --- a/src/render.py +++ b/src/render.py @@ -147,7 +147,7 @@ def render_pep440_branch_based(pieces): Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.BRANCH_gHEX[.dirty] """ - replacements = ([' ', '.'], ['(', ''], [')', ''], ['\\', '-'], ['/', '-']) + replacements = ([' ', '.'], ['(', ''], [')', ''], ['\\', '.'], ['/', '.']) branch_name = pieces.get('branch') or '' if branch_name: for old, new in replacements: diff --git a/test/test_render.py b/test/test_render.py index e34f5d6b..1631a158 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -376,13 +376,13 @@ class Test_pep440_branch_based__forward_slash_in_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'foo/bar' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.foo-bar.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.foo-bar.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.foo-bar.gabc.dirty', - 'untagged_0_commits_clean': '0+untagged.0.foo-bar.g', - 'untagged_0_commits_dirty': '0+untagged.0.foo-bar.g.dirty', - 'untagged_1_commits_clean': '0+untagged.1.foo-bar.gabc', - 'untagged_1_commits_dirty': '0+untagged.1.foo-bar.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3+0.foo.bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.foo.bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.foo.bar.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.foo.bar.g', + 'untagged_0_commits_dirty': '0+untagged.0.foo.bar.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.foo.bar.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.foo.bar.gabc.dirty', 'error_getting_parts': 'unknown' } @@ -392,13 +392,13 @@ class Test_pep440_branch_based__back_slash_in_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'foo\\bar' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.foo-bar.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.foo-bar.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.foo-bar.gabc.dirty', - 'untagged_0_commits_clean': '0+untagged.0.foo-bar.g', - 'untagged_0_commits_dirty': '0+untagged.0.foo-bar.g.dirty', - 'untagged_1_commits_clean': '0+untagged.1.foo-bar.gabc', - 'untagged_1_commits_dirty': '0+untagged.1.foo-bar.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3+0.foo.bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3+1.foo.bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3+1.foo.bar.gabc.dirty', + 'untagged_0_commits_clean': '0+untagged.0.foo.bar.g', + 'untagged_0_commits_dirty': '0+untagged.0.foo.bar.g.dirty', + 'untagged_1_commits_clean': '0+untagged.1.foo.bar.gabc', + 'untagged_1_commits_dirty': '0+untagged.1.foo.bar.gabc.dirty', 'error_getting_parts': 'unknown' } From e80ab79a91fe788134a995019fe6ffa325c2ad28 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Fri, 26 Oct 2018 15:44:49 +0100 Subject: [PATCH 18/18] Add `.dev0` so pip knows what to do --- src/render.py | 2 +- test/test_render.py | 48 ++++++++++++++++++++++----------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/render.py b/src/render.py index d1850fce..e8db4437 100644 --- a/src/render.py +++ b/src/render.py @@ -158,7 +158,7 @@ def render_pep440_branch_based(pieces): if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) + rendered += '.dev0' + plus_or_dot(pieces) rendered += "%d.%s.g%s" % ( pieces["distance"], branch_name, diff --git a/test/test_render.py b/test/test_render.py index 1631a158..23161b7b 100644 --- a/test/test_render.py +++ b/test/test_render.py @@ -296,9 +296,9 @@ class Test_pep440_branch_based__master(unittest.TestCase, style = 'pep440-branch-based' branch = 'master' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.master.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.master.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.master.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.master.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.master.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.master.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.master.g', 'untagged_0_commits_dirty': '0+untagged.0.master.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.master.gabc', @@ -312,9 +312,9 @@ class Test_pep440_branch_based__maint(unittest.TestCase, style = 'pep440-branch-based' branch = 'v1.2.x' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.v1.2.x.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.v1.2.x.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.v1.2.x.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.v1.2.x.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.v1.2.x.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.v1.2.x.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.v1.2.x.g', 'untagged_0_commits_dirty': '0+untagged.0.v1.2.x.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.v1.2.x.gabc', @@ -328,9 +328,9 @@ class Test_pep440_branch_based__feature_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'feature_branch' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.feature_branch.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.feature_branch.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.feature_branch.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.feature_branch.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.feature_branch.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.feature_branch.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.feature_branch.g', 'untagged_0_commits_dirty': '0+untagged.0.feature_branch.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.feature_branch.gabc', @@ -344,9 +344,9 @@ class Test_pep440_branch_based__no_branch_info(unittest.TestCase, style = 'pep440-branch-based' branch = None expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.unknown_branch.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.unknown_branch.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.unknown_branch.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.unknown_branch.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.unknown_branch.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.unknown_branch.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.unknown_branch.g', 'untagged_0_commits_dirty': '0+untagged.0.unknown_branch.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.unknown_branch.gabc', @@ -360,9 +360,9 @@ class Test_pep440_branch_based__space_in_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'foo bar' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.foo.bar.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.foo.bar.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.foo.bar.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.foo.bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.foo.bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.foo.bar.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.foo.bar.g', 'untagged_0_commits_dirty': '0+untagged.0.foo.bar.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.foo.bar.gabc', @@ -376,9 +376,9 @@ class Test_pep440_branch_based__forward_slash_in_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'foo/bar' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.foo.bar.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.foo.bar.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.foo.bar.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.foo.bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.foo.bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.foo.bar.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.foo.bar.g', 'untagged_0_commits_dirty': '0+untagged.0.foo.bar.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.foo.bar.gabc', @@ -392,9 +392,9 @@ class Test_pep440_branch_based__back_slash_in_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'foo\\bar' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.foo.bar.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.foo.bar.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.foo.bar.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.foo.bar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.foo.bar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.foo.bar.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.foo.bar.g', 'untagged_0_commits_dirty': '0+untagged.0.foo.bar.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.foo.bar.gabc', @@ -408,9 +408,9 @@ class Test_pep440_branch_based__parenthesis_in_branch(unittest.TestCase, style = 'pep440-branch-based' branch = 'foo(bar)' expected = {'tagged_0_commits_clean': 'v1.2.3', - 'tagged_0_commits_dirty': 'v1.2.3+0.foobar.g.dirty', - 'tagged_1_commits_clean': 'v1.2.3+1.foobar.gabc', - 'tagged_1_commits_dirty': 'v1.2.3+1.foobar.gabc.dirty', + 'tagged_0_commits_dirty': 'v1.2.3.dev0+0.foobar.g.dirty', + 'tagged_1_commits_clean': 'v1.2.3.dev0+1.foobar.gabc', + 'tagged_1_commits_dirty': 'v1.2.3.dev0+1.foobar.gabc.dirty', 'untagged_0_commits_clean': '0+untagged.0.foobar.g', 'untagged_0_commits_dirty': '0+untagged.0.foobar.g.dirty', 'untagged_1_commits_clean': '0+untagged.1.foobar.gabc',