Coverage for Applications / PyCharm.app / Contents / plugins / python-ce / helpers / pycharm / teamcity / messages.py: 59%
137 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-01 16:37 -0600
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-01 16:37 -0600
1# coding=utf-8
2import sys
3import time
5from teamcity.output import TeamCityMessagesPrinter
7if sys.version_info < (3, ):
8 # Python 2
9 # flake8: noqa
10 text_type = unicode
11else:
12 # Python 3
13 text_type = str
15# Capture some time functions to allow monkeypatching them in tests
16_time = time.time
17_localtime = time.localtime
18_strftime = time.strftime
20_quote = {"'": "|'", "|": "||", "\n": "|n", "\r": "|r", '[': '|[', ']': '|]'}
23def escape_value(value):
24 return "".join(_quote.get(x, x) for x in value)
27class TeamcityServiceMessages(object):
28 def __init__(self, output=None, now=_time, encoding='auto', output_handler=None):
29 if output_handler is None:
30 output_handler = TeamCityMessagesPrinter(output)
31 self.output_handler = output_handler
32 self.now = now
34 if encoding and encoding != 'auto':
35 self.encoding = encoding
36 elif getattr(output, 'encoding', None) or encoding == 'auto':
37 # Default encoding to 'utf-8' because it sucks if we fail with a
38 # `UnicodeEncodeError` simply because LANG didn't get propagated to
39 # a subprocess or something and sys.stdout.encoding is None
40 self.encoding = getattr(output, 'encoding', None) or 'utf-8'
41 else:
42 self.encoding = None
44 def encode(self, value):
45 if self.encoding and isinstance(value, text_type):
46 value = value.encode(self.encoding, errors='backslashreplace')
47 return value
49 def decode(self, value):
50 if self.encoding and not isinstance(value, text_type):
51 value = value.decode(self.encoding)
52 return value
54 if sys.version_info < (3, ):
55 def escapeValue(self, value):
56 return escape_value(self.encode(value))
57 else:
58 def escapeValue(self, value):
59 return escape_value(self.decode(value))
61 def message(self, messageName, **properties):
62 current_time = self.now()
63 (current_time_int, current_time_fraction) = divmod(current_time, 1)
64 current_time_struct = _localtime(current_time_int)
66 timestamp = _strftime("%Y-%m-%dT%H:%M:%S.", current_time_struct) + "%03d" % (int(current_time_fraction * 1000))
67 message = ("##teamcity[%s timestamp='%s'" % (messageName, timestamp))
69 for k in sorted(properties.keys()):
70 value = properties[k]
71 if value is None:
72 continue
74 message += (" %s='%s'" % (k, self.escapeValue(value)))
76 message += ("]\n")
78 self.output_handler.send_message(self.encode(message))
80 def _single_value_message(self, messageName, value):
81 message = ("##teamcity[%s '%s']\n" % (messageName, self.escapeValue(value)))
83 self.output_handler.send_message(self.encode(message))
85 def blockOpened(self, name, flowId=None):
86 self.message('blockOpened', name=name, flowId=flowId)
88 def blockClosed(self, name, flowId=None):
89 self.message('blockClosed', name=name, flowId=flowId)
91 # Special PyCharm-specific extension to track subtests, additional property is ignored by TeamCity
92 def subTestBlockOpened(self, name, subTestResult, flowId=None):
93 self.message('blockOpened', name=name, subTestResult=subTestResult, flowId=flowId)
95 def block(self, name, flowId=None):
96 import teamcity.context_managers as cm
97 return cm.block(self, name=name, flowId=flowId)
99 def compilationStarted(self, compiler):
100 self.message('compilationStarted', compiler=compiler)
102 def compilationFinished(self, compiler):
103 self.message('compilationFinished', compiler=compiler)
105 def compilation(self, compiler):
106 import teamcity.context_managers as cm
107 return cm.compilation(self, compiler=compiler)
109 def testSuiteStarted(self, suiteName, flowId=None):
110 self.message('testSuiteStarted', name=suiteName, flowId=flowId)
112 def testSuiteFinished(self, suiteName, flowId=None):
113 self.message('testSuiteFinished', name=suiteName, flowId=flowId)
115 def testSuite(self, name):
116 import teamcity.context_managers as cm
117 return cm.testSuite(self, name=name)
119 def testStarted(self, testName, captureStandardOutput=None, flowId=None, metainfo=None, **properties):
120 """
122 :param metainfo: Used to pass any payload from test runner to Intellij. See IDEA-176950
123 """
124 self.message('testStarted', name=testName, captureStandardOutput=captureStandardOutput, flowId=flowId, metainfo=metainfo, **properties)
126 def testFinished(self, testName, testDuration=None, flowId=None):
127 if testDuration is not None:
128 duration_ms = testDuration.days * 86400000 + \
129 testDuration.seconds * 1000 + \
130 int(testDuration.microseconds / 1000)
131 self.message('testFinished', name=testName, duration=str(duration_ms), flowId=flowId)
132 else:
133 self.message('testFinished', name=testName, flowId=flowId)
135 def test(self, testName, captureStandardOutput=None, testDuration=None, flowId=None):
136 import teamcity.context_managers as cm
137 return cm.test(self, testName=testName, captureStandardOutput=captureStandardOutput, testDuration=testDuration, flowId=flowId)
139 # Unsupported in TeamCity, used in IntellIJ-based IDEs to predict number of tests to be run in the test session
140 def testCount(self, count, flowId=None):
141 self.message('testCount', count=str(count), flowId=flowId)
143 def testIgnored(self, testName, message='', flowId=None):
144 self.message('testIgnored', name=testName, message=message, flowId=flowId)
146 def testStopped(self, testName, message='', flowId=None):
147 self.message('testIgnored', name=testName, message=message, flowId=flowId, stopped="true")
149 def testFailed(self, testName, message='', details='', flowId=None, comparison_failure=None):
150 if not comparison_failure:
151 self.message('testFailed', name=testName, message=message, details=details, flowId=flowId)
152 else:
153 diff_message = u"\n{0} != {1}\n".format(comparison_failure.actual, comparison_failure.expected)
154 self.message('testFailed',
155 name=testName,
156 message=text_type(message) + text_type(diff_message),
157 details=details,
158 flowId=flowId,
159 type="comparisonFailure",
160 actual=comparison_failure.actual,
161 expected=comparison_failure.expected)
163 def testStdOut(self, testName, out, flowId=None):
164 self.message('testStdOut', name=testName, out=out, flowId=flowId)
166 def testStdErr(self, testName, out, flowId=None):
167 self.message('testStdErr', name=testName, out=out, flowId=flowId)
169 def publishArtifacts(self, path, flowId=None):
170 self._single_value_message('publishArtifacts', path)
172 def progressMessage(self, message):
173 self._single_value_message('progressMessage', message)
175 def progressStart(self, message):
176 self._single_value_message('progressStart', message)
178 def progressFinish(self, message):
179 self._single_value_message('progressFinish', message)
181 def progress(self, message):
182 import teamcity.context_managers as cm
183 return cm.progress(self, message=message)
185 def buildProblem(self, description, identity):
186 self.message('buildProblem', description=description, identity=identity)
188 def buildStatus(self, status, text):
189 self.message('buildStatus', status=status, text=text)
191 def setParameter(self, name, value):
192 self.message('setParameter', name=name, value=value)
194 def buildStatisticLinesCovered(self, linesCovered):
195 self.message('buildStatisticValue', key='CodeCoverageAbsLCovered', value=str(linesCovered))
197 def buildStatisticTotalLines(self, totalLines):
198 self.message('buildStatisticValue', key='CodeCoverageAbsLTotal', value=str(totalLines))
200 def buildStatisticLinesUncovered(self, linesUncovered):
201 self.message('buildStatisticValue', key='CodeCoverageAbsLUncovered', value=str(linesUncovered))
203 def enableServiceMessages(self, flowId=None):
204 self.message('enableServiceMessages', flowId=flowId)
206 def disableServiceMessages(self, flowId=None):
207 self.message('disableServiceMessages', flowId=flowId)
209 def serviceMessagesDisabled(self, flowId=None):
210 import teamcity.context_managers as cm
211 return cm.serviceMessagesDisabled(self, flowId=flowId)
213 def serviceMessagesEnabled(self, flowId=None):
214 import teamcity.context_managers as cm
215 return cm.serviceMessagesEnabled(self, flowId=flowId)
217 def importData(self, typeID, pathToXMLFile):
218 self.message('importData', type=typeID, path=pathToXMLFile)
220 def customMessage(self, text, status, errorDetails='', flowId=None):
221 self.message('message', text=text, status=status, errorDetails=errorDetails, flowId=flowId)