| # Copyright 2016 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Functions and classes for formatting buildbot stage annotations.""" |
| |
| import abc |
| import itertools |
| import json |
| |
| |
| class Annotation: |
| """Formatted annotation for buildbot.""" |
| |
| def __init__(self, name, args): |
| """Initialize instance. |
| |
| Args: |
| name: Annotation name. |
| args: A sequence of string arguments. |
| """ |
| self.name = name |
| self.args = args |
| |
| def __str__(self): |
| inner_text = "@".join( |
| _EscapeArgText(text) |
| for text in itertools.chain([self.name], self.args) |
| ) |
| return "@@@%s@@@" % (inner_text,) |
| |
| @property |
| def human_friendly(self): |
| """Human-friendly format.""" |
| if self.args: |
| return "%s: %s" % (self.name, "; ".join(self.args)) |
| else: |
| return self.name |
| |
| |
| class _NamedAnnotation(Annotation, metaclass=abc.ABCMeta): |
| """Abstract subclass for creating named annotations. |
| |
| Concrete subclasses should define the ANNOTATION_NAME class attribute. |
| """ |
| |
| def __init__(self, *args): |
| super().__init__(self.ANNOTATION_NAME, args) |
| |
| # TODO(b/236161656): Fix. |
| # pylint: disable-next=deprecated-decorator |
| @abc.abstractproperty |
| def ANNOTATION_NAME(self): |
| raise NotImplementedError() |
| |
| |
| class StepLink(_NamedAnnotation): |
| """STEP_LINK annotation.""" |
| |
| ANNOTATION_NAME = "STEP_LINK" |
| |
| # Some callers pass in text/url by kwarg. We leave the full signature here |
| # so the API is a bit cleaner/more obvious. |
| # pylint: disable=useless-super-delegation |
| def __init__(self, text, url): |
| super().__init__(text, url) |
| |
| |
| class StepText(_NamedAnnotation): |
| """STEP_TEXT annotation.""" |
| |
| ANNOTATION_NAME = "STEP_TEXT" |
| |
| |
| class StepWarnings(_NamedAnnotation): |
| """STEP_WARNINGS annotation.""" |
| |
| ANNOTATION_NAME = "STEP_WARNINGS" |
| |
| |
| class StepFailure(_NamedAnnotation): |
| """STEP_FAILURE annotation.""" |
| |
| ANNOTATION_NAME = "STEP_FAILURE" |
| |
| |
| class BuildStep(_NamedAnnotation): |
| """BUILD_STEP annotation.""" |
| |
| ANNOTATION_NAME = "BUILD_STEP" |
| |
| |
| class SetBuildProperty(_NamedAnnotation): |
| """SET_BUILD_PROPERTY annotation.""" |
| |
| ANNOTATION_NAME = "SET_BUILD_PROPERTY" |
| |
| def __init__(self, name, value): |
| super().__init__(name, json.dumps(value)) |
| |
| |
| class SetEmailNotifyProperty(_NamedAnnotation): |
| """SET_BUILD_PROPERTY annotation for email_notify.""" |
| |
| ANNOTATION_NAME = "SET_BUILD_PROPERTY" |
| |
| def __init__(self, name, value): |
| super().__init__(name, json.dumps(value)) |
| |
| def __str__(self): |
| inner_text = "@".join( |
| text for text in itertools.chain([self.name], self.args) |
| ) |
| return "@@@%s@@@" % (inner_text) |
| |
| |
| def _EscapeArgText(text): |
| """Escape annotation argument text. |
| |
| Args: |
| text: String to escape. |
| """ |
| return text.replace("@", "-AT-") |