teamcity/output.py (38 lines of code) (raw):
# coding=utf-8
import errno
import sys
from functools import wraps
import time
# Capture some time functions to allow monkeypatching them in tests
_time = time.time
_stderr = sys.stderr
class TeamCityMessagesPrinter(object):
def __init__(self, output=None, context_manager=None):
if output is None:
output = sys.stdout
if sys.version_info < (3,) or not hasattr(output, 'buffer'):
self.output = output
else:
self.output = output.buffer
# context manager can be set for testing frameworks such as pytest, which may redirect
# normal output, to temporarily disable redirection for Teamcity messages
self.context_manager = context_manager
def send_message(self, message):
if self.context_manager:
with self.context_manager():
self._output(message)
else:
self._output(message)
def _output(self, message):
# Python may buffer it for a long time, flushing helps to see real-time result
retry_on_EAGAIN(self.output.write)(message)
retry_on_EAGAIN(self.output.flush)()
def retry_on_EAGAIN(callable):
# output seems to be non-blocking when running under teamcity.
@wraps(callable)
def wrapped(*args, **kwargs):
start_time = _time()
while True:
try:
return callable(*args, **kwargs)
except IOError as e:
if e.errno != errno.EAGAIN:
raise
# Give up after a minute.
if _time() - start_time > 60:
raise
time.sleep(.1)
return wrapped