Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1065)

Unified Diff: presubmit_support.py

Issue 6694009: refactor presubmit parsing code from git-cl into presubmit (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: backport git-cl fixes from gcl_owners patch Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « presubmit_canned_checks.py ('k') | tests/presubmit_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: presubmit_support.py
diff --git a/presubmit_support.py b/presubmit_support.py
index fc1c7f23b0c09d666c88b2e03cb19eb50b828ba4..7ee2b0d1326856a9e8343c7ba1545a3916a31a99 100755
--- a/presubmit_support.py
+++ b/presubmit_support.py
@@ -78,12 +78,6 @@ def normpath(path):
return os.path.normpath(path)
-def PromptYesNo(input_stream, output_stream, prompt):
- output_stream.write(prompt)
- response = input_stream.readline().strip().lower()
- return response == 'y' or response == 'yes'
-
-
def _RightHandSideLinesImpl(affected_files):
"""Implements RightHandSideLines for InputApi and GclChange."""
for af in affected_files:
@@ -92,14 +86,46 @@ def _RightHandSideLinesImpl(affected_files):
yield (af, line[0], line[1])
+class PresubmitOutput(object):
+ def __init__(self, input_stream=None, output_stream=None):
+ self.input_stream = input_stream
+ self.output_stream = output_stream
+ self.reviewers = []
+ self.written_output = []
+ self.error_count = 0
+
+ def prompt_yes_no(self, prompt_string):
+ self.write(prompt_string)
+ if self.input_stream:
+ response = self.input_stream.readline().strip().lower()
+ if response not in ('y', 'yes'):
+ self.fail()
+ else:
+ self.fail()
+
+ def fail(self):
+ self.error_count += 1
+
+ def should_continue(self):
+ return not self.error_count
+
+ def write(self, s):
+ self.written_output.append(s)
+ if self.output_stream:
+ self.output_stream.write(s)
+
+ def getvalue(self):
+ return ''.join(self.written_output)
+
+
class OutputApi(object):
"""This class (more like a module) gets passed to presubmit scripts so that
they can specify various types of results.
"""
- # Method could be a function
- # pylint: disable=R0201
class PresubmitResult(object):
"""Base class for result objects."""
+ fatal = False
+ should_prompt = False
def __init__(self, message, items=None, long_text=''):
"""
@@ -113,19 +139,11 @@ class OutputApi(object):
self._items = items
self._long_text = long_text.rstrip()
- def _Handle(self, output_stream, input_stream, may_prompt=True):
- """Writes this result to the output stream.
-
- Args:
- output_stream: Where to write
-
- Returns:
- True if execution may continue, False otherwise.
- """
- output_stream.write(self._message)
- output_stream.write('\n')
+ def handle(self, output):
+ output.write(self._message)
+ output.write('\n')
if len(self._items) > 0:
- output_stream.write(' ' + ' \\\n '.join(map(str, self._items)) + '\n')
+ output.write(' ' + ' \\\n '.join(map(str, self._items)) + '\n')
if self._long_text:
# Sometimes self._long_text is a ascii string, a codepage string
# (on windows), or a unicode object.
@@ -134,41 +152,27 @@ class OutputApi(object):
except UnicodeDecodeError:
long_text = self._long_text.decode('ascii', 'replace')
- output_stream.write('\n***************\n%s\n***************\n' %
+ output.write('\n***************\n%s\n***************\n' %
long_text)
+ if self.fatal:
+ output.fail()
- if self.ShouldPrompt() and may_prompt:
- if not PromptYesNo(input_stream, output_stream,
- 'Are you sure you want to continue? (y/N): '):
- return False
+ class PresubmitAddReviewers(PresubmitResult):
+ """Add some suggested reviewers to the change."""
+ def __init__(self, reviewers):
+ super(OutputApi.PresubmitAddReviewers, self).__init__('')
+ self.reviewers = reviewers
- return not self.IsFatal()
-
- def IsFatal(self):
- """An error that is fatal stops g4 mail/submit immediately, i.e. before
- other presubmit scripts are run.
- """
- return False
-
- def ShouldPrompt(self):
- """Whether this presubmit result should result in a prompt warning."""
- return False
-
- class PresubmitAddText(PresubmitResult):
- """Propagates a line of text back to the caller."""
- def __init__(self, message, items=None, long_text=''):
- super(OutputApi.PresubmitAddText, self).__init__("ADD: " + message,
- items, long_text)
+ def handle(self, output):
+ output.reviewers.extend(self.reviewers)
class PresubmitError(PresubmitResult):
"""A hard presubmit error."""
- def IsFatal(self):
- return True
+ fatal = True
class PresubmitPromptWarning(PresubmitResult):
"""An warning that prompts the user if they want to continue."""
- def ShouldPrompt(self):
- return True
+ should_prompt = True
class PresubmitNotifyResult(PresubmitResult):
"""Just print something to the screen -- but it's not even a warning."""
@@ -993,6 +997,7 @@ class PresubmitExecuter(object):
os.chdir(main_path)
return result
+
# TODO(dpranke): make all callers pass in tbr, host_url?
def DoPresubmitChecks(change,
committing,
@@ -1029,25 +1034,27 @@ def DoPresubmitChecks(change,
SHOULD be sys.stdin.
Return:
- True if execution can continue, False if not.
+ A PresubmitOutput object. Use output.should_continue() to figure out
+ if there were errors or warnings and the caller should abort.
"""
- print "Running presubmit hooks..."
+ output = PresubmitOutput(input_stream, output_stream)
+ output.write("Running presubmit hooks...\n")
start_time = time.time()
presubmit_files = ListRelevantPresubmitFiles(change.AbsoluteLocalPaths(True),
change.RepositoryRoot())
if not presubmit_files and verbose:
- output_stream.write("Warning, no presubmit.py found.\n")
+ output.write("Warning, no presubmit.py found.\n")
results = []
executer = PresubmitExecuter(change, committing, tbr, host_url)
if default_presubmit:
if verbose:
- output_stream.write("Running default presubmit script.\n")
+ output.write("Running default presubmit script.\n")
fake_path = os.path.join(change.RepositoryRoot(), 'PRESUBMIT.py')
results += executer.ExecPresubmitScript(default_presubmit, fake_path)
for filename in presubmit_files:
filename = os.path.abspath(filename)
if verbose:
- output_stream.write("Running %s\n" % filename)
+ output.write("Running %s\n" % filename)
# Accept CRLF presubmit script.
presubmit_script = gclient_utils.FileRead(filename, 'rU')
results += executer.ExecPresubmitScript(presubmit_script, filename)
@@ -1056,44 +1063,37 @@ def DoPresubmitChecks(change,
notifications = []
warnings = []
for result in results:
- if not result.IsFatal() and not result.ShouldPrompt():
- notifications.append(result)
- elif result.ShouldPrompt():
+ if result.fatal:
+ errors.append(result)
+ elif result.should_prompt:
warnings.append(result)
else:
- errors.append(result)
+ notifications.append(result)
- error_count = 0
for name, items in (('Messages', notifications),
('Warnings', warnings),
('ERRORS', errors)):
if items:
- output_stream.write('** Presubmit %s **\n' % name)
+ output.write('** Presubmit %s **\n' % name)
for item in items:
- # Access to a protected member XXX of a client class
- # pylint: disable=W0212
- if not item._Handle(output_stream, input_stream,
- may_prompt=False):
- error_count += 1
- output_stream.write('\n')
+ item.handle(output)
+ output.write('\n')
total_time = time.time() - start_time
if total_time > 1.0:
- print "Presubmit checks took %.1fs to calculate." % total_time
+ output.write("Presubmit checks took %.1fs to calculate.\n" % total_time)
if not errors and warnings and may_prompt:
- if not PromptYesNo(input_stream, output_stream,
- 'There were presubmit warnings. '
- 'Are you sure you wish to continue? (y/N): '):
- error_count += 1
+ output.prompt_yes_no('There were presubmit warnings. '
+ 'Are you sure you wish to continue? (y/N): ')
global _ASKED_FOR_FEEDBACK
# Ask for feedback one time out of 5.
if (len(results) and random.randint(0, 4) == 0 and not _ASKED_FOR_FEEDBACK):
- output_stream.write("Was the presubmit check useful? Please send feedback "
- "& hate mail to maruel@chromium.org!\n")
+ output.write("Was the presubmit check useful? Please send feedback "
+ "& hate mail to maruel@chromium.org!\n")
_ASKED_FOR_FEEDBACK = True
- return (error_count == 0)
+ return output
def ScanSubDirs(mask, recursive):
@@ -1179,18 +1179,19 @@ def Main(argv):
print "Found %d files." % len(options.files)
else:
print "Found 1 file."
- return not DoPresubmitChecks(change_class(options.name,
- options.description,
- options.root,
- options.files,
- options.issue,
- options.patchset),
- options.commit,
- options.verbose,
- sys.stdout,
- sys.stdin,
- options.default_presubmit,
- options.may_prompt)
+ results = DoPresubmitChecks(change_class(options.name,
+ options.description,
+ options.root,
+ options.files,
+ options.issue,
+ options.patchset),
+ options.commit,
+ options.verbose,
+ sys.stdout,
+ sys.stdin,
+ options.default_presubmit,
+ options.may_prompt)
+ return not results.should_continue()
if __name__ == '__main__':
« no previous file with comments | « presubmit_canned_checks.py ('k') | tests/presubmit_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698