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
|
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import tarfile
from StringIO import StringIO
from file_system import FileNotFoundError
from future import Future
from patcher import Patcher
_CHROMIUM_REPO_BASEURLS = [
'https://src.chromium.org/svn/trunk/src/',
'http://src.chromium.org/svn/trunk/src/',
'svn://svn.chromium.org/chrome/trunk/src',
'https://chromium.googlesource.com/chromium/src.git@master',
'http://git.chromium.org/chromium/src.git@master',
]
class RietveldPatcherError(Exception):
def __init__(self, message):
self.message = message
def _GetAsyncFetchCallback(issue, patchset, files, fetcher):
tarball = fetcher.FetchAsync('tarball/%s/%s' % (issue, patchset))
def resolve():
tarball_result = tarball.Get()
if tarball_result.status_code != 200:
raise RietveldPatcherError(
'Failed to download tarball for issue %s patchset %s. Status: %s' %
(issue, patchset, tarball_result.status_code))
try:
tar = tarfile.open(fileobj=StringIO(tarball_result.content))
except tarfile.TarError as e:
raise RietveldPatcherError(
'Error loading tarball for issue %s patchset %s.' % (issue, patchset))
value = {}
for path in files:
tar_path = 'b/%s' % path
patched_file = None
try:
patched_file = tar.extractfile(tar_path)
data = patched_file.read()
except tarfile.TarError as e:
# Show appropriate error message in the unlikely case that the tarball
# is corrupted.
raise RietveldPatcherError(
'Error extracting tarball for issue %s patchset %s file %s.' %
(issue, patchset, tar_path))
except KeyError as e:
raise FileNotFoundError(
'File %s not found in the tarball for issue %s patchset %s' %
(tar_path, issue, patchset))
finally:
if patched_file:
patched_file.close()
value[path] = data
return value
return resolve
class RietveldPatcher(Patcher):
''' Class to fetch resources from a patchset in Rietveld.
'''
def __init__(self,
issue,
fetcher):
self._issue = issue
self._fetcher = fetcher
self._cache = None
# In RietveldPatcher, the version is the latest patchset number.
def GetVersion(self):
try:
issue_json = json.loads(self._fetcher.Fetch(
'api/%s' % self._issue).content)
except Exception as e:
raise RietveldPatcherError(
'Failed to fetch information for issue %s.' % self._issue)
if issue_json.get('closed'):
raise RietveldPatcherError('Issue %s has been closed.' % self._issue)
patchsets = issue_json.get('patchsets')
if not isinstance(patchsets, list) or len(patchsets) == 0:
raise RietveldPatcherError('Cannot parse issue %s.' % self._issue)
if not issue_json.get('base_url') in _CHROMIUM_REPO_BASEURLS:
raise RietveldPatcherError('Issue %s\'s base url is unknown.' %
self._issue)
return str(patchsets[-1])
def GetPatchedFiles(self, version=None):
if version is None:
patchset = self.GetVersion()
else:
patchset = version
try:
patchset_json = json.loads(self._fetcher.Fetch(
'api/%s/%s' % (self._issue, patchset)).content)
except Exception as e:
raise RietveldPatcherError(
'Failed to fetch details for issue %s patchset %s.' % (self._issue,
patchset))
files = patchset_json.get('files')
if files is None or not isinstance(files, dict):
raise RietveldPatcherError('Failed to parse issue %s patchset %s.' %
(self._issue, patchset))
added = []
deleted = []
modified = []
for f in files:
status = (files[f].get('status') or 'M')
# status can be 'A ' or 'A + '
if 'A' in status:
added.append(f)
elif 'D' in status:
deleted.append(f)
elif 'M' in status:
modified.append(f)
else:
raise RietveldPatcherError('Unknown file status for file %s: "%s."' %
(key, status))
return (added, deleted, modified)
def Apply(self, paths, file_system, version=None):
if version is None:
version = self.GetVersion()
return Future(callback=_GetAsyncFetchCallback(self._issue,
version,
paths,
self._fetcher))
def GetIdentity(self):
return self._issue
|