| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 # coding=utf8 |  | 
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |  | 
| 3 # Use of this source code is governed by a BSD-style license that can be |  | 
| 4 # found in the LICENSE file. |  | 
| 5 """Defines base classes for pending change verification classes.""" |  | 
| 6 |  | 
| 7 import model |  | 
| 8 |  | 
| 9 |  | 
| 10 # Verifier state in priority level. |  | 
| 11 # SUCCEEDED : This verifier is fine to commit this patch. |  | 
| 12 # PROCESSING: No decision was made yet. The verifier runs asynchronously. |  | 
| 13 # FAILED    : Verification failed, this patch must not be committed. |  | 
| 14 # IGNORED   : This patch must be ignored and no comment added to the code |  | 
| 15 #             review. |  | 
| 16 SUCCEEDED, PROCESSING, FAILED, IGNORED = range(4) |  | 
| 17 VALID_STATES = set((SUCCEEDED, PROCESSING, FAILED, IGNORED)) |  | 
| 18 |  | 
| 19 |  | 
| 20 class DiscardPending(Exception): |  | 
| 21   """Exception to be raised when a pending item should be discarded.""" |  | 
| 22 |  | 
| 23   def __init__(self, pending, status): |  | 
| 24     super(DiscardPending, self).__init__(status) |  | 
| 25     self.pending = pending |  | 
| 26     self.status = status |  | 
| 27 |  | 
| 28 |  | 
| 29 class Verified(model.PersistentMixIn): |  | 
| 30   """A set of verifications that are for a specific patch.""" |  | 
| 31   verifications = dict |  | 
| 32 |  | 
| 33   def pending_name(self): |  | 
| 34     raise NotImplementedError() |  | 
| 35 |  | 
| 36   @model.immutable |  | 
| 37   def get_state(self): |  | 
| 38     """Returns the combined state of all the verifiers for this item. |  | 
| 39 |  | 
| 40     Use priority with the states: IGNORED > FAILED > PROCESSING > SUCCEEDED. |  | 
| 41     """ |  | 
| 42     # If there's an error message, it failed. |  | 
| 43     if self.error_message(): |  | 
| 44       return FAILED |  | 
| 45     if not self.verifications: |  | 
| 46       return PROCESSING |  | 
| 47     states = set(v.get_state() for v in self.verifications.itervalues()) |  | 
| 48     assert states.issubset(VALID_STATES) |  | 
| 49     return max(states) |  | 
| 50 |  | 
| 51   @model.immutable |  | 
| 52   def postpone(self): |  | 
| 53     """This item shouldn't be committed right now. |  | 
| 54 |  | 
| 55     Call repeatedly until it returns False. This is a potentially slow call so |  | 
| 56     only call it when get_state() returns SUCEEDED. |  | 
| 57     """ |  | 
| 58     return any(v.postpone() for v in self.verifications.itervalues()) |  | 
| 59 |  | 
| 60   @model.immutable |  | 
| 61   def error_message(self): |  | 
| 62     """Returns all the error messages concatenated if any.""" |  | 
| 63     out = (i.error_message for i in self.verifications.itervalues()) |  | 
| 64     return '\n\n'.join(filter(None, out)) |  | 
| 65 |  | 
| 66   def apply_patch(self, context, prepare): |  | 
| 67     """Applies a patch from the codereview tool to the checkout.""" |  | 
| 68     raise NotImplementedError() |  | 
| 69 |  | 
| 70   @model.immutable |  | 
| 71   def why_not(self): |  | 
| 72     """Returns a string of all the reasons the current patch can't be |  | 
| 73        commited""" |  | 
| 74     why_nots = dict((k, v.why_not()) |  | 
| 75                     for k, v in self.verifications.iteritems()) |  | 
| 76     return '\n'.join('%s: %s' % (k, v) for k, v in why_nots.iteritems() if v) |  | 
| 77 |  | 
| 78 |  | 
| 79 class IVerifierStatus(model.PersistentMixIn): |  | 
| 80   """Interface for objects in Verified.verifications dictionary.""" |  | 
| 81   error_message = (None, unicode) |  | 
| 82 |  | 
| 83   def get_state(self): |  | 
| 84     """See Verified.get_state().""" |  | 
| 85     raise NotImplementedError() |  | 
| 86 |  | 
| 87   @model.immutable |  | 
| 88   def postpone(self):  # pylint: disable=R0201 |  | 
| 89     """See Verified.postpone().""" |  | 
| 90     return False |  | 
| 91 |  | 
| 92   def why_not(self): |  | 
| 93     """Returns a message why the commit cannot be committed yet. |  | 
| 94 |  | 
| 95     E.g. why get_state() == PROCESSING. |  | 
| 96     """ |  | 
| 97     raise NotImplementedError() |  | 
| 98 |  | 
| 99 |  | 
| 100 class SimpleStatus(IVerifierStatus): |  | 
| 101   """Base class to be used for simple true/false and why not verifiers.""" |  | 
| 102   state = int |  | 
| 103 |  | 
| 104   def __init__(self, state=PROCESSING, **kwargs): |  | 
| 105     super(SimpleStatus, self).__init__(state=state, **kwargs) |  | 
| 106 |  | 
| 107   @model.immutable |  | 
| 108   def get_state(self): |  | 
| 109     return self.state |  | 
| 110 |  | 
| 111   @model.immutable |  | 
| 112   def why_not(self): |  | 
| 113     if self.state == PROCESSING: |  | 
| 114       return 'Processing' |  | 
| 115     return |  | 
| 116 |  | 
| 117 |  | 
| 118 class Verifier(object): |  | 
| 119   """This class and its subclasses are *not* serialized.""" |  | 
| 120   name = None |  | 
| 121 |  | 
| 122   def __init__(self): |  | 
| 123     assert self.name is not None |  | 
| 124 |  | 
| 125   def verify(self, pending): |  | 
| 126     """Verifies a pending change. |  | 
| 127 |  | 
| 128     Called with os.getcwd() == checkout.project_path. |  | 
| 129     """ |  | 
| 130     raise NotImplementedError() |  | 
| 131 |  | 
| 132   def update_status(self, queue): |  | 
| 133     """Updates the status of all pending changes, for asynchronous checks. |  | 
| 134 |  | 
| 135     It is not necessarily called from inside checkout.project_path. |  | 
| 136     """ |  | 
| 137     raise NotImplementedError() |  | 
| 138 |  | 
| 139   @model.immutable |  | 
| 140   def loop(self, queue, gen_obj, pending_only): |  | 
| 141     """Loops in a pending queue and returns the verified item corresponding to |  | 
| 142     the Verifier. |  | 
| 143     """ |  | 
| 144     for pending in queue: |  | 
| 145       if self.name not in pending.verifications: |  | 
| 146         pending.verifications[self.name] = gen_obj() |  | 
| 147       if (not pending_only or |  | 
| 148           pending.verifications[self.name].get_state() == PROCESSING): |  | 
| 149         yield pending, pending.verifications[self.name] |  | 
| 150 |  | 
| 151 |  | 
| 152 class VerifierCheckout(Verifier):  # pylint: disable=W0223 |  | 
| 153   """A verifier that needs a rietveld and checkout objects. |  | 
| 154 |  | 
| 155   When verify() is called, it is guaranteed that the patch is applied on the |  | 
| 156   checkout. |  | 
| 157   """ |  | 
| 158   def __init__(self, context_obj): |  | 
| 159     super(VerifierCheckout, self).__init__() |  | 
| 160     self.context = context_obj |  | 
| 161 |  | 
| 162   @model.immutable |  | 
| 163   def send_status(self, pending, data): |  | 
| 164     """Sends an update to the CQ dashboard. |  | 
| 165 |  | 
| 166     self.context.status is usually an instance of AsyncPush so the HTTP POST is |  | 
| 167     done in a background thread. |  | 
| 168     """ |  | 
| 169     self.context.status.send( |  | 
| 170         pending, {'verification': self.name, 'payload': data}) |  | 
| OLD | NEW | 
|---|