| OLD | NEW |
| 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 """Defines class Rietveld to easily access a rietveld instance. | 4 """Defines class Rietveld to easily access a rietveld instance. |
| 5 | 5 |
| 6 Security implications: | 6 Security implications: |
| 7 | 7 |
| 8 The following hypothesis are made: | 8 The following hypothesis are made: |
| 9 - Rietveld enforces: | 9 - Rietveld enforces: |
| 10 - Nobody else than issue owner can upload a patch set | 10 - Nobody else than issue owner can upload a patch set |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 url = '/download/issue%s_%s_%s.diff' % (issue, patchset, item) | 114 url = '/download/issue%s_%s_%s.diff' % (issue, patchset, item) |
| 115 return self.get(url) | 115 return self.get(url) |
| 116 | 116 |
| 117 def get_patch(self, issue, patchset): | 117 def get_patch(self, issue, patchset): |
| 118 """Returns a PatchSet object containing the details to apply this patch.""" | 118 """Returns a PatchSet object containing the details to apply this patch.""" |
| 119 props = self.get_patchset_properties(issue, patchset) or {} | 119 props = self.get_patchset_properties(issue, patchset) or {} |
| 120 out = [] | 120 out = [] |
| 121 for filename, state in props.get('files', {}).iteritems(): | 121 for filename, state in props.get('files', {}).iteritems(): |
| 122 logging.debug('%s' % filename) | 122 logging.debug('%s' % filename) |
| 123 status = state.get('status') | 123 status = state.get('status') |
| 124 if status is None: | 124 if not status: |
| 125 raise patch.UnsupportedPatchFormat( | 125 raise patch.UnsupportedPatchFormat( |
| 126 filename, 'File\'s status is None, patchset upload is incomplete.') | 126 filename, 'File\'s status is None, patchset upload is incomplete.') |
| 127 if status[0] not in ('A', 'D', 'M'): |
| 128 raise patch.UnsupportedPatchFormat( |
| 129 filename, 'Change with status \'%s\' is not supported.' % status) |
| 127 | 130 |
| 128 if status[0] == 'D': | 131 svn_props = self.parse_svn_properties( |
| 129 if status[0] != status.strip(): | 132 state.get('property_changes', ''), filename) |
| 130 raise patch.UnsupportedPatchFormat( | 133 |
| 131 filename, 'Deleted file shouldn\'t have property change.') | 134 if state.get('is_binary'): |
| 132 # Ignore the diff. | 135 if status[0] == 'D': |
| 133 out.append(patch.FilePatchDelete(filename, state['is_binary'])) | 136 if status[0] != status.strip(): |
| 134 elif status[0] in ('A', 'M'): | 137 raise patch.UnsupportedPatchFormat( |
| 135 svn_props = self.parse_svn_properties( | 138 filename, 'Deleted file shouldn\'t have property change.') |
| 136 state.get('property_changes', ''), filename) | 139 out.append(patch.FilePatchDelete(filename, state['is_binary'])) |
| 137 if state['is_binary']: | 140 else: |
| 138 out.append(patch.FilePatchBinary( | 141 out.append(patch.FilePatchBinary( |
| 139 filename, | 142 filename, |
| 140 self.get_file_content(issue, patchset, state['id']), | 143 self.get_file_content(issue, patchset, state['id']), |
| 141 svn_props, | 144 svn_props, |
| 142 is_new=(status[0] == 'A'))) | 145 is_new=(status[0] == 'A'))) |
| 143 else: | 146 continue |
| 144 # Ignores num_chunks since it may only contain an header. | 147 |
| 145 diff = None | 148 try: |
| 146 try: | 149 diff = self.get_file_diff(issue, patchset, state['id']) |
| 147 diff = self.get_file_diff(issue, patchset, state['id']) | 150 except urllib2.HTTPError, e: |
| 148 except urllib2.HTTPError, e: | 151 if e.code == 404: |
| 149 if e.code == 404: | 152 raise patch.UnsupportedPatchFormat( |
| 150 raise patch.UnsupportedPatchFormat( | 153 filename, 'File doesn\'t have a diff.') |
| 151 filename, 'File doesn\'t have a diff.') | 154 raise |
| 152 raise | 155 |
| 153 # It may happen on property-only change or svn copy without diff. | 156 # FilePatchDiff() will detect file deletion automatically. |
| 154 if not diff: | 157 p = patch.FilePatchDiff(filename, diff, svn_props) |
| 155 # Support this use case if it ever happen. | 158 out.append(p) |
| 156 raise patch.UnsupportedPatchFormat( | 159 if status[0] == 'A': |
| 157 filename, 'Empty diff is not supported yet.\n') | 160 # It won't be set for empty file. |
| 158 p = patch.FilePatchDiff(filename, diff, svn_props) | 161 p.is_new = True |
| 159 out.append(p) | 162 if (len(status) > 1 and |
| 160 if status[0] == 'A': | 163 status[1] == '+' and |
| 161 # It won't be set for empty file. | 164 not (p.source_filename or p.svn_properties)): |
| 162 p.is_new = True | |
| 163 if (len(status) > 1 and | |
| 164 status[1] == '+' and | |
| 165 not (p.source_filename or p.svn_properties)): | |
| 166 raise patch.UnsupportedPatchFormat( | |
| 167 filename, 'Failed to process the svn properties') | |
| 168 else: | |
| 169 raise patch.UnsupportedPatchFormat( | 165 raise patch.UnsupportedPatchFormat( |
| 170 filename, 'Change with status \'%s\' is not supported.' % status) | 166 filename, 'Failed to process the svn properties') |
| 171 | 167 |
| 172 return patch.PatchSet(out) | 168 return patch.PatchSet(out) |
| 173 | 169 |
| 174 @staticmethod | 170 @staticmethod |
| 175 def parse_svn_properties(rietveld_svn_props, filename): | 171 def parse_svn_properties(rietveld_svn_props, filename): |
| 176 """Returns a list of tuple [('property', 'newvalue')]. | 172 """Returns a list of tuple [('property', 'newvalue')]. |
| 177 | 173 |
| 178 rietveld_svn_props is the exact format from 'svn diff'. | 174 rietveld_svn_props is the exact format from 'svn diff'. |
| 179 """ | 175 """ |
| 180 rietveld_svn_props = rietveld_svn_props.splitlines() | 176 rietveld_svn_props = rietveld_svn_props.splitlines() |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 if retry >= (maxtries - 1): | 269 if retry >= (maxtries - 1): |
| 274 raise | 270 raise |
| 275 if not 'Name or service not known' in e.reason: | 271 if not 'Name or service not known' in e.reason: |
| 276 # Usually internal GAE flakiness. | 272 # Usually internal GAE flakiness. |
| 277 raise | 273 raise |
| 278 # If reaching this line, loop again. Uses a small backoff. | 274 # If reaching this line, loop again. Uses a small backoff. |
| 279 time.sleep(1+maxtries*2) | 275 time.sleep(1+maxtries*2) |
| 280 | 276 |
| 281 # DEPRECATED. | 277 # DEPRECATED. |
| 282 Send = get | 278 Send = get |
| OLD | NEW |