1# This file is part of Buildbot.  Buildbot is free software: you can
2# redistribute it and/or modify it under the terms of the GNU General Public
3# License as published by the Free Software Foundation, version 2.
4#
5# This program is distributed in the hope that it will be useful, but WITHOUT
6# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8# details.
9#
10# You should have received a copy of the GNU General Public License along with
11# this program; if not, write to the Free Software Foundation, Inc., 51
12# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13#
14# Portions Copyright Buildbot Team Members
15
16
17from twisted.internet import protocol
18
19
20class LineBuffer:
21    def __init__(self):
22        self._buffer = b''
23
24    def add_data(self, data):
25        # returns lines that have been processed, if any
26        lines = (self._buffer + data).split(b'\n')
27        self._buffer = lines.pop(-1)
28        for l in lines:
29            yield l.rstrip(b'\r')
30
31    def get_trailing_line(self):
32        if self._buffer:
33            ret = [self._buffer]
34            self._buffer = b''
35            return ret
36        return []
37
38
39class LineProcessProtocol(protocol.ProcessProtocol):
40
41    def __init__(self):
42        self._out_buffer = LineBuffer()
43        self._err_buffer = LineBuffer()
44
45    def outReceived(self, data):
46        """
47        Translates bytes into lines, and calls outLineReceived.
48        """
49        for line in self._out_buffer.add_data(data):
50            self.outLineReceived(line)
51
52    def errReceived(self, data):
53        """
54        Translates bytes into lines, and calls errLineReceived.
55        """
56        for line in self._err_buffer.add_data(data):
57            self.errLineReceived(line)
58
59    def processEnded(self, status):
60        for line in self._out_buffer.get_trailing_line():
61            self.outLineReceived(line)
62        for line in self._err_buffer.get_trailing_line():
63            self.errLineReceived(line)
64
65    def outLineReceived(self, line):
66        """
67        Callback to which stdout lines will be sent.
68        Any line that is not terminated by a newline will be processed once the next line comes,
69        or when processEnded is called.
70        """
71        raise NotImplementedError
72
73    def errLineReceived(self, line):
74        """
75        Callback to which stdout lines will be sent.
76        Any line that is not terminated by a newline will be processed once the next line comes,
77        or when processEnded is called.
78        """
79        raise NotImplementedError
80