1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
import os
import re
import sys
import codecs
from distutils import log
from xml.sax.saxutils import unescape
import xml.dom.pulldom
#requires python >= 2.4
from subprocess import Popen as _Popen, PIPE as _PIPE
#NOTE: Use of the command line options
# require SVN 1.3 or newer (December 2005)
# and SVN 1.3 hsan't been supported by the
# developers since mid 2008.
#subprocess is called several times with shell=(sys.platform=='win32')
#see the follow for more information:
# http://bugs.python.org/issue8557
# http://stackoverflow.com/questions/5658622/
# python-subprocess-popen-environment-path
def _run_command(args, stdout=_PIPE, stderr=_PIPE):
#regarding the shell argument, see: http://bugs.python.org/issue8557
proc = _Popen(args, stdout=stdout, stderr=stderr,
shell=(sys.platform=='win32'))
data = proc.communicate()[0]
#TODO: this is probably NOT always utf-8
try:
data = unicode(data, encoding='utf-8')
except NameError:
data = str(data, encoding='utf-8')
return proc.returncode, data
#svnversion return values (previous implementations return max revision)
# 4123:4168 mixed revision working copy
# 4168M modified working copy
# 4123S switched working copy
# 4123:4168MS mixed revision, modified, switched working copy
_SVN_VER_RE = re.compile(r'(?:(\d+):)?(\d+)([a-z]*)\s*$', re.I)
def parse_revision(path):
code, data = _run_command(['svnversion', path])
if code:
log.warn("svnversion failed")
return []
parsed = _SVN_VER_RE.match(data)
if parsed:
try:
#No max needed this command summarizes working copy since 1.0
return int(parsed.group(2))
except ValueError:
#This should only happen if the revision is WAY too big.
pass
return 0
def parse_dir_entries(path):
code, data = _run_command(['svn', 'info',
'--depth', 'immediates', '--xml', path])
if code:
log.warn("svn info failed")
return []
doc = xml.dom.pulldom.parseString(data)
entries = list()
for event, node in doc:
if event=='START_ELEMENT' and node.nodeName=='entry':
doc.expandNode(node)
entries.append(node)
if entries:
return [
_get_entry_name(element)
for element in entries[1:]
if _get_entry_schedule(element).lower() != 'deleted'
]
else:
return []
def _get_entry_name(entry):
return entry.getAttribute('path')
def _get_entry_schedule(entry):
schedule = entry.getElementsByTagName('schedule')[0]
return "".join([t.nodeValue for t in schedule.childNodes
if t.nodeType == t.TEXT_NODE])
#--xml wasn't supported until 1.5.x
#-R without --xml parses a bit funny
def parse_externals(path):
try:
code, lines = _run_command(['svn',
'propget', 'svn:externals', path])
if code:
log.warn("svn propget failed")
return []
lines = [line for line in lines.splitlines() if line]
except ValueError:
lines = []
externals = []
for line in lines:
line = line.split()
if not line:
continue
#TODO: urlparse?
if "://" in line[-1] or ":\\\\" in line[-1]:
externals.append(line[0])
else:
externals.append(line[-1])
return externals
def get_svn_tool_version():
_, data = _run_command(['svn', '--version', '--quiet'])
if data:
return data.strip()
else:
return ''
|