Index: third_party/recipe_engine/third_party/annotator.py |
diff --git a/third_party/recipe_engine/third_party/annotator.py b/third_party/recipe_engine/third_party/annotator.py |
deleted file mode 100644 |
index 8fcb5ebc02ba7e2aa3554c2c54cafb7ae9155f85..0000000000000000000000000000000000000000 |
--- a/third_party/recipe_engine/third_party/annotator.py |
+++ /dev/null |
@@ -1,348 +0,0 @@ |
-# Copyright (c) 2015 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. |
- |
-"""Contains the parsing system of the Chromium Buildbot Annotator.""" |
- |
-import os |
-import sys |
-import traceback |
- |
-# These are maps of annotation key -> number of expected arguments. |
-STEP_ANNOTATIONS = { |
- 'SET_BUILD_PROPERTY': 2, |
- 'STEP_CLEAR': 0, |
- 'STEP_EXCEPTION': 0, |
- 'STEP_FAILURE': 0, |
- 'STEP_LINK': 2, |
- 'STEP_LOG_END': 1, |
- 'STEP_LOG_END_PERF': 2, |
- 'STEP_LOG_LINE': 2, |
- 'STEP_SUMMARY_CLEAR': 0, |
- 'STEP_SUMMARY_TEXT': 1, |
- 'STEP_TEXT': 1, |
- 'STEP_TRIGGER': 1, |
- 'STEP_WARNINGS': 0, |
- 'STEP_NEST_LEVEL': 1, |
-} |
- |
-CONTROL_ANNOTATIONS = { |
- 'STEP_CLOSED': 0, |
- 'STEP_STARTED': 0, |
-} |
- |
-STREAM_ANNOTATIONS = { |
- 'HALT_ON_FAILURE': 0, |
- 'HONOR_ZERO_RETURN_CODE': 0, |
- 'SEED_STEP': 1, |
- 'SEED_STEP_TEXT': 2, |
- 'STEP_CURSOR': 1, |
-} |
- |
-DEPRECATED_ANNOTATIONS = { |
- 'BUILD_STEP': 1, |
-} |
- |
-ALL_ANNOTATIONS = {} |
-ALL_ANNOTATIONS.update(STEP_ANNOTATIONS) |
-ALL_ANNOTATIONS.update(CONTROL_ANNOTATIONS) |
-ALL_ANNOTATIONS.update(STREAM_ANNOTATIONS) |
-ALL_ANNOTATIONS.update(DEPRECATED_ANNOTATIONS) |
- |
-# This is a mapping of old_annotation_name -> new_annotation_name. |
-# Theoretically all annotator scripts should use the new names, but it's hard |
-# to tell due to the decentralized nature of the annotator. |
-DEPRECATED_ALIASES = { |
- 'BUILD_FAILED': 'STEP_FAILURE', |
- 'BUILD_WARNINGS': 'STEP_WARNINGS', |
- 'BUILD_EXCEPTION': 'STEP_EXCEPTION', |
- 'link': 'STEP_LINK', |
-} |
- |
-# A couple of the annotations have the format: |
-# @@@THING arg@@@ |
-# for reasons no one knows. We only need this case until all masters have been |
-# restarted to pick up the new master-side parsing code. |
-OLD_STYLE_ANNOTATIONS = set(( |
- 'SEED_STEP', |
- 'STEP_CURSOR', |
-)) |
- |
- |
-def emit(line, stream, flush_before=None): |
- if flush_before: |
- flush_before.flush() |
- print >> stream |
- # WinDOS can only handle 64kb of output to the console at a time, per process. |
- if sys.platform.startswith('win'): |
- lim = 2**15 |
- while line: |
- to_print, line = line[:lim], line[lim:] |
- stream.write(to_print) |
- stream.write('\n') |
- else: |
- print >> stream, line |
- stream.flush() |
- |
- |
-class MetaAnnotationPrinter(type): |
- def __new__(mcs, name, bases, dct): |
- annotation_map = dct.get('ANNOTATIONS') |
- if annotation_map: |
- for key, v in annotation_map.iteritems(): |
- key = key.lower() |
- dct[key] = mcs.make_printer_fn(key, v) |
- return type.__new__(mcs, name, bases, dct) |
- |
- @staticmethod |
- def make_printer_fn(name, n_args): |
- """Generates a method which emits an annotation to the log stream.""" |
- upname = name.upper() |
- if upname in OLD_STYLE_ANNOTATIONS: |
- assert n_args >= 1 |
- fmt = '@@@%s %%s%s@@@' % (upname, '@%s' * (n_args - 1)) |
- else: |
- fmt = '@@@%s%s@@@' % (upname, '@%s' * n_args) |
- |
- inner_args = n_args + 1 # self counts |
- infix = '1 argument' if inner_args == 1 else ('%d arguments' % inner_args) |
- err = '%s() takes %s (%%d given)' % (name, infix) |
- |
- def printer(self, *args): |
- if len(args) != n_args: |
- raise TypeError(err % (len(args) + 1)) |
- self.emit(fmt % args) |
- printer.__name__ = name |
- printer.__doc__ = """Emits an annotation for %s.""" % name.upper() |
- |
- return printer |
- |
- |
-class AnnotationPrinter(object): |
- """A derivable class which will inject annotation-printing methods into the |
- subclass. |
- |
- A subclass should define a class variable ANNOTATIONS equal to a |
- dictionary of the form { '<ANNOTATION_NAME>': <# args> }. This class will |
- then inject methods whose names are the undercased version of your |
- annotation names, and which take the number of arguments specified in the |
- dictionary. |
- |
- Example: |
- >>> my_annotations = { 'STEP_LOG_LINE': 2 } |
- >>> class MyObj(AnnotationPrinter): |
- ... ANNOTATIONS = my_annotations |
- ... |
- >>> o = MyObj() |
- >>> o.step_log_line('logname', 'here is a line to put in the log') |
- @@@STEP_LOG_LINE@logname@here is a line to put in the log@@@ |
- >>> o.step_log_line() |
- Traceback (most recent call last): |
- File "<stdin>", line 1, in <module> |
- TypeError: step_log_line() takes exactly 3 arguments (1 given) |
- >>> o.setp_log_line.__doc__ |
- "Emits an annotation for STEP_LOG_LINE." |
- >>> |
- """ |
- __metaclass__ = MetaAnnotationPrinter |
- |
- def __init__(self, stream, flush_before): |
- self.stream = stream |
- self.flush_before = flush_before |
- |
- def emit(self, line): |
- emit(line, self.stream, self.flush_before) |
- |
- |
-class StepCommands(AnnotationPrinter): |
- """Class holding step commands. Intended to be subclassed.""" |
- ANNOTATIONS = STEP_ANNOTATIONS |
- |
- def __init__(self, stream, flush_before): |
- super(StepCommands, self).__init__(stream, flush_before) |
- self.emitted_logs = set() |
- |
- def write_log_lines(self, logname, lines, perf=None): |
- if logname in self.emitted_logs: |
- raise ValueError('Log %s has been emitted multiple times.' % logname) |
- self.emitted_logs.add(logname) |
- |
- logname = logname.replace('/', '/') |
- |
- for line in lines: |
- for actual_line in line.split('\n'): |
- self.step_log_line(logname, actual_line) |
- |
- if perf: |
- self.step_log_end_perf(logname, perf) |
- else: |
- self.step_log_end(logname) |
- |
- |
-class StepControlCommands(AnnotationPrinter): |
- """Subclass holding step control commands. Intended to be subclassed. |
- |
- This is subclassed out so callers in StructuredAnnotationStep can't call |
- step_started() or step_closed(). |
- """ |
- ANNOTATIONS = CONTROL_ANNOTATIONS |
- |
- |
-class StructuredAnnotationStep(StepCommands, StepControlCommands): |
- """Helper class to provide context for a step.""" |
- |
- def __init__(self, annotation_stream, *args, **kwargs): |
- self.annotation_stream = annotation_stream |
- super(StructuredAnnotationStep, self).__init__(*args, **kwargs) |
- self.control = StepControlCommands(self.stream, self.flush_before) |
- self.emitted_logs = set() |
- |
- |
- def __enter__(self): |
- return self.step_started() |
- |
- def step_started(self): |
- self.control.step_started() |
- return self |
- |
- def __exit__(self, exc_type, exc_value, tb): |
- self.annotation_stream.step_cursor(self.annotation_stream.current_step) |
- #TODO(martinis) combine this and step_ended |
- if exc_type: |
- self.step_exception_occured(exc_type, exc_value, tb) |
- |
- self.control.step_closed() |
- self.annotation_stream.current_step = '' |
- return not exc_type |
- |
- def step_exception_occured(self, exc_type, exc_value, tb): |
- trace = traceback.format_exception(exc_type, exc_value, tb) |
- trace_lines = ''.join(trace).split('\n') |
- self.write_log_lines('exception', filter(None, trace_lines)) |
- self.step_exception() |
- |
- def step_ended(self): |
- self.annotation_stream.step_cursor(self.annotation_stream.current_step) |
- self.control.step_closed() |
- self.annotation_stream.current_step = '' |
- |
- return True |
- |
- |
-class StructuredAnnotationStream(AnnotationPrinter): |
- """Provides an interface to handle an annotated build. |
- |
- StructuredAnnotationStream handles most of the step setup and closure calls |
- for you. All you have to do is execute your code within the steps and set any |
- failures or warnings that come up. You may optionally provide a list of steps |
- to seed before execution. |
- |
- Usage: |
- |
- stream = StructuredAnnotationStream() |
- with stream.step('compile') as s: |
- # do something |
- if error: |
- s.step_failure() |
- with stream.step('test') as s: |
- # do something |
- if warnings: |
- s.step_warnings() |
- """ |
- ANNOTATIONS = STREAM_ANNOTATIONS |
- |
- def __init__(self, stream=sys.stdout, |
- flush_before=sys.stderr, |
- seed_steps=None): # pylint: disable=W0613 |
- super(StructuredAnnotationStream, self).__init__(stream=stream, |
- flush_before=flush_before) |
- self.current_step = '' |
- |
- def step(self, name): |
- """Provide a context with which to execute a step.""" |
- if self.current_step: |
- raise Exception('Can\'t start step %s while in step %s.' % ( |
- name, self.current_step)) |
- |
- self.seed_step(name) |
- self.step_cursor(name) |
- self.current_step = name |
- return StructuredAnnotationStep(self, stream=self.stream, |
- flush_before=self.flush_before) |
- |
- |
-def MatchAnnotation(line, callback_implementor): |
- """Call back into |callback_implementor| if line contains an annotation. |
- |
- Args: |
- line (str) - The line to analyze |
- callback_implementor (object) - An object which contains methods |
- corresponding to all of the annotations in the |ALL_ANNOTATIONS| |
- dictionary. For example, it should contain a method STEP_SUMMARY_TEXT |
- taking a single argument. |
- |
- Parsing method: |
- * if line doesn't match /^@@@.*@@@$/, return without calling back |
- * Look for the first '@' or ' ' |
- """ |
- if not (line.startswith('@@@') and line.endswith('@@@') and len(line) > 6): |
- return |
- line = line[3:-3] |
- |
- # look until the first @ or ' ' |
- idx = min((x for x in (line.find('@'), line.find(' '), len(line)) if x > 0)) |
- cmd_text = line[:idx] |
- cmd = DEPRECATED_ALIASES.get(cmd_text, cmd_text) |
- |
- field_count = ALL_ANNOTATIONS.get(cmd) |
- if field_count is None: |
- raise Exception('Unrecognized annotator command "%s"' % cmd_text) |
- |
- if field_count: |
- if idx == len(line): |
- raise Exception('Annotator command "%s" expects %d args, got 0.' |
- % (cmd_text, field_count)) |
- |
- line = line[idx+1:] |
- |
- args = line.split('@', field_count-1) |
- if len(args) != field_count: |
- raise Exception('Annotator command "%s" expects %d args, got %d.' |
- % (cmd_text, field_count, len(args))) |
- else: |
- line = line[len(cmd_text):] |
- if line: |
- raise Exception('Annotator command "%s" expects no args, got cruft "%s".' |
- % (cmd_text, line)) |
- args = [] |
- |
- fn = getattr(callback_implementor, cmd, None) |
- if fn is None: |
- raise Exception('"%s" does not implement "%s"' |
- % (callback_implementor, cmd)) |
- |
- fn(*args) |
- |
- |
-def print_step(step, env, stream): |
- """Prints the step command and relevant metadata. |
- |
- Intended to be similar to the information that Buildbot prints at the |
- beginning of each non-annotator step. |
- """ |
- step_info_lines = [] |
- step_info_lines.append(' '.join(step['cmd'])) |
- step_info_lines.append('in dir %s:' % (step['cwd'] or os.getcwd())) |
- for key, value in sorted(step.items()): |
- if value is not None: |
- if callable(value): |
- # This prevents functions from showing up as: |
- # '<function foo at 0x7f523ec7a410>' |
- # which is tricky to test. |
- value = value.__name__+'(...)' |
- step_info_lines.append(' %s: %s' % (key, value)) |
- step_info_lines.append('full environment:') |
- for key, value in sorted(env.items()): |
- step_info_lines.append(' %s: %s' % (key, value)) |
- step_info_lines.append('') |
- stream.emit('\n'.join(step_info_lines)) |