1# -*- coding: utf-8 -*- 2# Copyright 2016 The Chromium OS 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 6"""Functions and classes for formatting buildbot stage annotations.""" 7 8from __future__ import print_function 9 10import abc 11import itertools 12import json 13 14import six 15 16 17class Annotation(object): 18 """Formatted annotation for buildbot.""" 19 20 def __init__(self, name, args): 21 """Initialize instance. 22 23 Args: 24 name: Annotation name. 25 args: A sequence of string arguments. 26 """ 27 self.name = name 28 self.args = args 29 30 def __str__(self): 31 inner_text = '@'.join( 32 _EscapeArgText(text) 33 for text in itertools.chain([self.name], self.args) 34 ) 35 return '@@@%s@@@' % (inner_text,) 36 37 @property 38 def human_friendly(self): 39 """Human-friendly format.""" 40 if self.args: 41 return '%s: %s' % (self.name, '; '.join(self.args)) 42 else: 43 return self.name 44 45 46@six.add_metaclass(abc.ABCMeta) 47class _NamedAnnotation(Annotation): 48 """Abstract subclass for creating named annotations. 49 50 Concrete subclasses should define the ANNOTATION_NAME class attribute. 51 """ 52 53 def __init__(self, *args): 54 super(_NamedAnnotation, self).__init__(self.ANNOTATION_NAME, args) 55 56 @abc.abstractproperty 57 def ANNOTATION_NAME(self): 58 raise NotImplementedError() 59 60 61class StepLink(_NamedAnnotation): 62 """STEP_LINK annotation.""" 63 ANNOTATION_NAME = 'STEP_LINK' 64 65 # Some callers pass in text/url by kwarg. We leave the full signature here 66 # so the API is a bit cleaner/more obvious. 67 # pylint: disable=useless-super-delegation 68 def __init__(self, text, url): 69 super(StepLink, self).__init__(text, url) 70 71 72class StepText(_NamedAnnotation): 73 """STEP_TEXT annotation.""" 74 ANNOTATION_NAME = 'STEP_TEXT' 75 76 77class StepWarnings(_NamedAnnotation): 78 """STEP_WARNINGS annotation.""" 79 ANNOTATION_NAME = 'STEP_WARNINGS' 80 81 82class StepFailure(_NamedAnnotation): 83 """STEP_FAILURE annotation.""" 84 ANNOTATION_NAME = 'STEP_FAILURE' 85 86 87class BuildStep(_NamedAnnotation): 88 """BUILD_STEP annotation.""" 89 ANNOTATION_NAME = 'BUILD_STEP' 90 91 92class SetBuildProperty(_NamedAnnotation): 93 """SET_BUILD_PROPERTY annotation.""" 94 ANNOTATION_NAME = 'SET_BUILD_PROPERTY' 95 96 def __init__(self, name, value): 97 super(SetBuildProperty, self).__init__(name, json.dumps(value)) 98 99 100class SetEmailNotifyProperty(_NamedAnnotation): 101 """SET_BUILD_PROPERTY annotation for email_notify.""" 102 ANNOTATION_NAME = 'SET_BUILD_PROPERTY' 103 104 def __init__(self, name, value): 105 super(SetEmailNotifyProperty, self).__init__(name, json.dumps(value)) 106 107 def __str__(self): 108 inner_text = '@'.join( 109 text for text in itertools.chain([self.name], self.args)) 110 return '@@@%s@@@' % (inner_text) 111 112 113def _EscapeArgText(text): 114 """Escape annotation argument text. 115 116 Args: 117 text: String to escape. 118 """ 119 return text.replace('@', '-AT-') 120