LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
svn.py
Go to the documentation of this file.
1 #
2 # A simple python interface to svn, using os.popen
3 #
4 # If ever we want to do anything clever, we should use one of
5 # the supported svn/python packages
6 #
7 from __future__ import absolute_import, division, print_function
8 import os
9 import re
10 import sys
11 
12 def isSvnFile(file):
13  """Is file under svn control?"""
14 
15  return re.search(r"is not a working copy",
16  "".join(os.popen("svn info %s 2>&1" % file).readlines())) == None
17 
18 def getInfo(file="."):
19  """Return a dictionary of all the information returned by "svn info" for the specified file"""
20 
21  if not isSvnFile(file):
22  raise RuntimeError("%s is not under svn control" % file)
23 
24  infoList = os.popen("svn info %s" % file).readlines()
25 
26  info = {}
27  for line in infoList:
28  mat = re.search(r"^([^:]+)\s*:\s*(.*)", line)
29  if mat:
30  info[mat.group(1)] = mat.group(2)
31 
32  return info
33 
34 def isTrunk(file="."):
35  """Is file on the trunk?"""
36 
37  info = getInfo(file)
38 
39  return re.search(r"/trunk($|/)", info["URL"]) != None
40 
41 def revision(file=None, lastChanged=False):
42  """Return file's Revision as a string; if file is None return
43  a tuple (oldestRevision, youngestRevision, flags) as reported
44  by svnversion; e.g. (4123, 4168, ("M", "S")) (oldestRevision
45  and youngestRevision may be equal)
46  """
47 
48  if file:
49  info = getInfo(file)
50 
51  if lastChanged:
52  return info["Last Changed Rev"]
53  else:
54  return info["Revision"]
55 
56  if lastChanged:
57  raise RuntimeError("lastChanged makes no sense if file is None")
58 
59  res = os.popen("svnversion . 2>&1").readline()
60 
61  if res == "exported\n":
62  raise RuntimeError("No svn revision information is available")
63 
64  versionRe = r"^(?P<oldest>\d+)(:(?P<youngest>\d+))?(?P<flags>[MS]*)"
65  mat = re.search(versionRe, res)
66  if mat:
67  matches = mat.groupdict()
68  if not matches["youngest"]:
69  matches["youngest"] = matches["oldest"]
70  # OK, we have only one revision present. Find the newest revision
71  # that actually changed anything in this product and ignore "oldest" (#522)
72  res = os.popen("svnversion --committed . 2>&1").readline()
73  mat = re.search(versionRe, res)
74  if mat:
75  matches = mat.groupdict()
76  return matches["youngest"], matches["youngest"], tuple(matches["flags"])
77 
78  return matches["oldest"], matches["youngest"], tuple(matches["flags"])
79 
80  raise RuntimeError("svnversion returned unexpected result \"%s\"" % res[:-1])
81 
82 #
83 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
84 #
85 def guessVersionName(HeadURL):
86  """Guess a version name given a HeadURL"""
87 
88  if re.search(r"/trunk$", HeadURL):
89  versionName = ""
90  elif re.search(r"/branches/(.+)$", HeadURL):
91  versionName = "branch_%s+" % re.search(r"/branches/(.+)$", HeadURL).group(1)
92  elif re.search(r"/tags/(.+)$", HeadURL):
93  versionName = "%s" % re.search(r"/tags/(.*)$", HeadURL).group(1)
94 
95  return versionName # no need for a "+svnXXXX"
96  elif re.search(r"/tickets/(\d+)$", HeadURL):
97  versionName = "ticket_%s+" % re.search(r"/tickets/(\d+)$", HeadURL).group(1)
98  else:
99  print("Unable to guess versionName name from %s" % HeadURL, file=sys.stderr)
100  versionName = "unknown+"
101 
102  try: # Try to lookup the svn versionName
103  (oldest, youngest, flags) = revision()
104 
105  okVersion = True
106  if "M" in flags:
107  msg = "You are installing, but have unchecked in files"
108  okVersion = False
109  if "S" in flags:
110  msg = "You are installing, but have switched SVN URLs"
111  okVersion = False
112  if oldest != youngest:
113  msg = "You have a range of revisions in your tree (%s:%s); adopting %s" % \
114  (oldest, youngest, youngest)
115  okVersion = False
116 
117  if not okVersion:
118  raise RuntimeError("Problem with determining svn revision: %s" % msg)
119 
120  versionName += "svn" + youngest
121  except IOError:
122  return "unknown"
123 
124  return versionName
125 
126 def parseVersionName(versionName):
127  """A callback that knows about the LSST convention that a tagname such as
128  ticket_374
129  means the top of ticket 374, and
130  ticket_374+svn6021
131  means revision 6021 on ticket 374. You may replace "ticket" with "branch" if you wish
132 
133  The "versionName" may actually be the directory part of a URL, and ".../(branches|tags|tickets)/tagname"
134  is also supported
135  """
136 
137  mat = re.search(r"/(branche|tag|ticket)s/(\d+(?:\.\d+)*)(?:([-+])((svn)?(\d+)))?$", versionName)
138  if not mat:
139  mat = re.search(r"/(branch|ticket)_(\d+)(?:([-+])svn(\d+))?$", versionName)
140  if mat:
141  type = mat.group(1)
142  if type == "branches":
143  type = "branch"
144  ticket = mat.group(2)
145  pm = mat.group(3) # + or -
146  revision = mat.group(4)
147  if revision:
148  revision = re.sub("^svn", "", revision)
149 
150  return (type, ticket, revision, pm)
151 
152  return (None, None, None, None)