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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
import os
import re
import sys
import codecs
from distutils import log
import xml.dom.pulldom
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
#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.
#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'(?:([\-0-9]+):)?(\d+)([a-z]*)\s*$', re.I)
#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
try:
proc = _Popen(args, stdout=stdout, stderr=stderr,
shell=(sys.platform == 'win32'))
data = proc.communicate()[0]
except OSError:
return 1, ''
#TODO: this is probably NOT always utf-8
try:
data = unicode(data, encoding='utf-8')
except NameError:
data = str(data, encoding='utf-8')
#communciate calls wait()
return proc.returncode, data
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])
def parse_revision(path):
code, data = _run_command(['svnversion', '-c', path])
if code:
log.warn("svnversion failed")
return 0
else:
log.warn('Version: %s' % data.strip())
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
#TODO: Need to do this with the -R because only root has .svn in 1.7.x
def parse_dir_entries(path):
code, data = _run_command(['svn', 'info',
'--depth', 'immediates', '--xml', path])
if code:
log.warn("svn info failed")
return []
data = codecs.encode(data, 'UTF-8')
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 []
#--xml wasn't supported until 1.5.x need to do -R
#TODO: -R looks like directories are seperated by blank lines
# with dir - prepened to first directory
# what about directories with spaces?
# put quotes around them
# what about the URL's?
# same
# convert to UTF-8 and use csv
# delimiter = space
#
#-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
if urlparse.urlsplit(line[-1])[0]:
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 ''
if __name__ == '__main__':
def entries_externals_finder(dirname):
for record in parse_dir_entries(dirname):
yield os.path.join(dirname, record)
for name in parse_externals(dirname):
yield os.path.join(dirname, name)
for name in entries_externals_finder(sys.argv[1]):
print(name)
|