aboutsummaryrefslogtreecommitdiffstats
path: root/setuptools/svn_utils.py
blob: 10c5861dee51775c2be1865da5e66446308506da (plain)
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
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'(?:(\d+):)?(\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
        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')

        #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', 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 []


#--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

        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 ''