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
168
169
170
171
|
import cStringIO
import getpass
import errno
import os
import shutil
import sys
import tempfile
import urlparse
import urllib
import collections
import fnmatch
import ConfigParser
import sys
class SubversionRepoCanNotReplay(Exception):
"""Exception raised when the svn server is too old to have replay.
"""
class SubversionRepoCanNotDiff(Exception):
"""Exception raised when the svn API diff3() command cannot be used
"""
class SubversionConnectionException(Exception):
"""Exception raised when a generic error occurs when connecting to a
repository.
"""
# Default chunk size used in fetch_history_at_paths() and revisions().
chunk_size = 1000
def parse_url(url, user=None, passwd=None):
"""Parse a URL and return a tuple (username, password, url)
"""
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
if '@' in netloc:
userpass, netloc = netloc.split('@')
if not user and not passwd:
if ':' in userpass:
user, passwd = userpass.split(':')
else:
user, passwd = userpass, ''
user, passwd = urllib.unquote(user), urllib.unquote(passwd)
if user and scheme == 'svn+ssh':
netloc = '@'.join((user, netloc,))
url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
return (user or None, passwd or None, url)
class Revision(tuple):
"""Wrapper for a Subversion revision.
Derives from tuple in an attempt to minimise the memory footprint.
"""
def __new__(self, revnum, author, message, date, paths=None, strip_path=''):
_paths = {}
if paths:
for p in paths:
_paths[p[len(strip_path):]] = paths[p]
return tuple.__new__(self,
(revnum, author, message, date, _paths))
@property
def revnum(self):
return self[0]
@property
def author(self):
return self[1]
@property
def message(self):
return self[2]
@property
def date(self):
return self[3]
@property
def paths(self):
return self[4]
def __str__(self):
return 'r%d by %s' % (self.revnum, self.author)
_svn_config_dir = None
class AutoPropsConfig(object):
"""Provides the subversion auto-props functionality
when pushing new files.
"""
def __init__(self, config_dir=None):
config_file = config_file_path(config_dir)
self.config = ConfigParser.RawConfigParser()
self.config.read([config_file])
def properties(self, file):
"""Returns a dictionary of the auto-props applicable for file.
Takes enable-auto-props into account.
"""
properties = {}
if self.autoprops_enabled():
for pattern,prop_list in self.config.items('auto-props'):
if fnmatch.fnmatchcase(os.path.basename(file), pattern):
properties.update(parse_autoprops(prop_list))
return properties
def autoprops_enabled(self):
return (self.config.has_option('miscellany', 'enable-auto-props')
and self.config.getboolean( 'miscellany', 'enable-auto-props')
and self.config.has_section('auto-props'))
def config_file_path(config_dir):
if config_dir == None:
global _svn_config_dir
config_dir = _svn_config_dir
if config_dir == None:
if sys.platform == 'win32':
config_dir = os.path.join(os.environ['APPDATA'], 'Subversion')
else:
config_dir = os.path.join(os.environ['HOME'], '.subversion')
return os.path.join(config_dir, 'config')
def parse_autoprops(prop_list):
"""Parses a string of autoprops and returns a dictionary of
the results.
Emulates the parsing of core.auto_props_enumerator.
"""
def unquote(s):
if len(s)>1 and s[0] in ['"', "'"] and s[0]==s[-1]:
return s[1:-1]
return s
properties = {}
for prop in prop_list.split(';'):
if '=' in prop:
prop, value = prop.split('=',1)
value = unquote(value.strip())
else:
value = ''
properties[prop.strip()] = value
return properties
class SimpleStringIO(object):
"""SimpleStringIO can replace a StringIO in write mode.
cStringIO reallocates and doubles the size of its internal buffer
when it needs to append new data which requires two large blocks for
large inputs. SimpleStringIO stores each individual blocks and joins
them once done. This might cause more memory fragmentation but
requires only one large block. In practice, ra.get_file() seems to
write in 16kB blocks (svn 1.7.5) which should be friendly to memory
allocators.
"""
def __init__(self, closing=True):
self._blocks = []
self._closing = closing
def write(self, s):
self._blocks.append(s)
def getvalue(self):
return ''.join(self._blocks)
def close(self):
if self._closing:
del self._blocks
|