20

I have a python project under SVN, and I'm wanting to display the version number when it is run. Is there any way of doing this (such as automatically running a short script on commit which could update a version file, or querying an SVN repository in Python?)

Smashery
  • 57,848
  • 30
  • 97
  • 128

11 Answers11

29

I'm not sure about the Python specifics, but if put the string $Revision$ into your file somewhere and you have enable-auto-props=true in your SVN config, it'll get rewritten to something like $Revision: 144$. You could then parse this in your script.

There are a number of property keywords you can use in this way.

This won't have any overhead, e.g. querying the SVN repo, because the string is hard-coded into your file on commit or update.

I'm not sure how you'd parse this in Python but in PHP I'd do:

$revString = '$Revision: 144$';
if(preg_match('/: ([0-9]+)\$/', $revString, $matches) {
    echo 'Revision is ' . $matches[1];
}
Ciaran McNulty
  • 18,698
  • 6
  • 32
  • 40
  • This is the right answer! Also, remember to set the property 'keywords=Revision' on each file you want to do this to. – Drew Hall Sep 21 '09 at 11:50
  • Drew - I think if enable-auto-props=true is set, then the default keywords are all set? – Ciaran McNulty Sep 21 '09 at 16:12
  • 3
    +1 That's awesome! In my case, though, is there a way to have SVN edit the revision number if it's a different file that was changed in this revision? For instance, if I have the revision number keyword in version.py, but I don't change version.py in revision nnn, it won't update it, will it? If not, is there a way to use this method in that way? – Smashery Sep 21 '09 at 23:38
  • @Smashery I'm afraid you'd have to write your own commit hook to do that I suspect - you could use the svn:keywords property though by setting your own keyword and letting SVN do the substitution for you. – Ciaran McNulty Sep 22 '09 at 08:22
  • That will only work on the file(s) being committed. We handle it in the build script, easy to do. – orip Sep 23 '09 at 17:44
15

Similar to, but a little more pythonic than the PHP answer; put this in your module's __init__.py:

__version__ = filter(str.isdigit, "$Revision: 13 $")

and make sure you add the Revision property:

svn propset svn:keywords Revision __init__.py
Will
  • 151
  • 1
  • 2
12

Or you can do like this:

import re,subprocess

svn_info = subprocess.check_output("svn info")

print (re.search(ur"Revision:\s\d+", svn_info)).group()

it prints "Revision: 2874" in my project

Or like this:

print (subprocess.check_output("svnversion")).split(":")[0]

it prints "2874" in my project

Edit

For newer python versions (>=3.4) and for explicit file path:

import re,subprocess

file_path = 'c:/foo'

svn_info = subprocess.check_output('svn info ' + file_path)

revision_string = re.search(r"Revision:\s\d+", str(svn_info)).group()
revision = revision_string.split(': ')[1]
print(revision)

Prints for example

'8623'

Stefan
  • 10,010
  • 7
  • 61
  • 117
German Petrov
  • 1,475
  • 1
  • 17
  • 21
  • Not only with working copy. You can retrieve svn remote URL from "svn info" first, like execute "svn info", then take "URL" param value from the output and execute same code, but using "svn info value_of_URL_param" So, updated code is: import re,subprocess svn_info = subprocess.check_output("svn info https://svn.you_remote_repo_url") print (re.search(ur"Revision:\s\d+", svn_info)).group() – German Petrov Aug 29 '14 at 07:36
  • 1
    I get an invalid syntax error on your print statement. print (re.search(ur"Revision:\s\d+", svn_info)).group() – tzg Nov 05 '18 at 15:02
  • a) Use r instead of ur before strings since python 3.4: https://stackoverflow.com/questions/26063899/python-version-3-4-does-not-support-a-ur-prefix b)Wrap binary string in str() – Stefan Jan 04 '22 at 08:33
4

The file hooks/pre-commit in your repository is a script or program which will be executed before a successful commit. Simply make this a script which updates your file with the proper version number as it is being committed (the revision number is passed to your script as it's run). Here's a tutorial with an example of writing a pre-commit hook: http://wordaligned.org/articles/a-subversion-pre-commit-hook

Eli Courtwright
  • 186,300
  • 67
  • 213
  • 256
  • 2
    Ouch. Letting the pre-commit hook modify files is generally a bad idea. The version in the repo will be different from the version in the working copy, and this may lead to some confusion. The committer has to revert to an earlier version of the file, and then get the new one, probably resulting in some kind of conflict, as svn has stored the original version in the text-base of the working copy. – sunny256 Sep 20 '09 at 00:47
  • I agree that this is dangerous and not usually a desired behavior, but if you limit yourself appropriately, then I don't see the problem. For example, if you have a "svn_version.txt" file that contains nothing but the current revision number, then it would be perfectly safe; your local copy would always have the version which you've checked out and the repository would always have the latest (often the same as your working copy). – Eli Courtwright Sep 20 '09 at 00:51
4

There's a snippet of code in Django that allows you to do this, I'd recommend looking at it. http://code.djangoproject.com/browser/django/trunk/django/utils/version.py

David
  • 1,669
  • 1
  • 12
  • 14
  • This function only works when you're running from within a subversion checkout. If this is what the OP wants, then problem solved. However, I suspect he may want the script to have contained within itself or a config file or something its subversion revision, even if the script is no longer under subversion, in which case that link won't be useful. – Eli Courtwright Sep 20 '09 at 00:41
  • Yeah, this is great fallback option, but having the version number also under source control would be better. Thanks! +1 – Smashery Sep 20 '09 at 00:52
3

Check out pysvn. it exposes extensions in Python for Subversion runtime functionality.

Rich Seller
  • 83,208
  • 23
  • 172
  • 177
Kostas Konstantinidis
  • 13,347
  • 10
  • 48
  • 61
3

I do this by simply running a small script when building my project. The script just calls svn info in combination with sed to get out the bare revision data and injects that number into the revision.txt file.

The Hudson build server makes it even easier as it sets the SVN revision number as an environment variable right before invoking your Build scripts.

zedoo
  • 10,562
  • 12
  • 44
  • 55
1

I find the when it is run section slightly ambiguous - when it is run from where ? From an SVN checkout or from released code ? All the solutions work on injecting the SVN version somehow into the Python source or somewhere in the repository in a separate version file.

The version file is changed on fresh checkouts, so clean checkouts need to be done before distribution. So my question of where the program is being run remains.

whatnick
  • 5,400
  • 3
  • 19
  • 35
1

Re-answering because I had trouble finding all the information for python 3. In python 3 you can use the following:

import re

revstring = '$Revision: 123 $'
revnumber = re.sub(r'\D', '', revstring)

For SVN to replace the number, the file-keyword "Revision" has to be set. you achieve this by typing the following command into a terminal:

svn propset svn:keywords Revision YOURFILE.py

Moreover, in the subversion configuration file the property enable-auto-props has to be set to yes. This should already be the case. In Tortoise SVN this config file can be accessed by clicking the button "edit" next to "subversion configuration file" in the general settings tab.

Richard
  • 709
  • 1
  • 6
  • 15
0

This worked for me in Python:

Get all the svn info:

svn_info = subprocess.check_output('svn info').decode("utf-8") 
print(svn_info)

Do your search and split off the number:

revisionNum = re.search("Revision:\s\d+", svn_info)
print(revisionNum.group().split(":")[1])
tzg
  • 616
  • 1
  • 8
  • 17
0

You could do this programmatically by parsing the output from 'svn info --xml' to an xml Element:

def get_svn_rev(dir: Union[str, Path]) -> str:
    """Get the svn revision of the given directory"""
    dir = str(dir)
    svn_info_cmd = ['svn', 'info', '--xml', dir]
    print(' '.join(svn_info_cmd))
    svn_info_process = subprocess.run(svn_info_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if svn_info_process.returncode:
        eprint(svn_info_process.stderr.decode().strip())
        raise subprocess.CalledProcessError(returncode=svn_info_process.returncode, cmd=svn_info_cmd,
                                            output=svn_info_process.stdout,
                                            stderr=svn_info_process.stderr)
    else:
        info = ElementTree.fromstring(svn_info_process.stdout.decode().strip())
        entry = info.find('entry')
        revision = entry.get('revision')

    return revision
Craig
  • 2,286
  • 3
  • 24
  • 37