source: rtems-tools/rtemstoolkit/log.py @ 7e5cdea

5
Last change on this file since 7e5cdea was 7e5cdea, checked in by Chris Johns <chrisj@…>, on 11/23/18 at 04:02:52

rtemstoolkit: Add unit testing for the python modules

  • Add support to run the unit tests for the rtemstoolkit python modules from waf. Enter './waf test' for the tests to be run on python2 and python3.
  • Update the importing of rtemstoolkit modules to the standard method which works on python2 and python3.
  • Update the README.
  • Property mode set to 100755
File size: 7.1 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2016 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-testing'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# Log output to stdout and/or a file.
33#
34
35from __future__ import print_function
36
37import os
38import sys
39import threading
40
41from rtemstoolkit import error
42
43#
44# A global log.
45#
46default = None
47
48#
49# A global capture handler.
50#
51capture = None
52
53#
54# Global parameters.
55#
56tracing = False
57quiet = False
58
59#
60# Global output lock to keep output together when working with threads
61#
62lock = threading.Lock()
63
64def set_default_once(log):
65    if default is None:
66        default = log
67
68def _output(text = os.linesep, log = None):
69    """Output the text to a log if provided else send it to stdout."""
70    if text is None:
71        text = os.linesep
72    if type(text) is list:
73        text = os.linesep.join(text) + os.linesep
74    if isinstance(text, bytes):
75        text = text.decode('utf-8', 'ignore')
76    if log:
77        log.output(text)
78    elif default is not None:
79        default.output(text)
80    else:
81        lock.acquire()
82        for l in text.replace(chr(13), '').splitlines():
83            print(l)
84        lock.release()
85    if capture is not None:
86        lock.acquire()
87        capture(text)
88        lock.release()
89
90def stderr(text = os.linesep, log = None):
91    lock.acquire()
92    for l in text.replace(chr(13), '').splitlines():
93        print(l, file = sys.stderr)
94    lock.release()
95
96def output(text = os.linesep, log = None):
97    if not quiet:
98        _output(text, log)
99
100def notice(text = os.linesep, log = None, stdout_only = False):
101    if not quiet and \
102            (default is not None and not default.has_stdout() or stdout_only):
103        lock.acquire()
104        for l in text.replace(chr(13), '').splitlines():
105            print(l)
106        lock.release()
107    if not stdout_only:
108        _output(text, log)
109
110def trace(text = os.linesep, log = None):
111    if tracing:
112        _output(text, log)
113
114def warning(text = os.linesep, log = None):
115    for l in text.replace(chr(13), '').splitlines():
116        _output('warning: %s' % (l), log)
117
118def flush(log = None):
119    if log:
120        log.flush()
121    elif default is not None:
122        default.flush()
123
124class log:
125    """Log output to stdout or a file."""
126    def __init__(self, streams = None, tail_size = 100):
127        self.lock = threading.Lock()
128        self.tail = []
129        self.tail_size = tail_size
130        self.fhs = [None, None]
131        if streams:
132            for s in streams:
133                if s == 'stdout':
134                    self.fhs[0] = sys.stdout
135                elif s == 'stderr':
136                    self.fhs[1] = sys.stderr
137                else:
138                    try:
139                        self.fhs.append(open(s, 'w'))
140                    except IOError as ioe:
141                         raise error.general("creating log file '" + s + \
142                                             "': " + str(ioe))
143
144    def __del__(self):
145        for f in range(2, len(self.fhs)):
146            self.fhs[f].close()
147
148    def __str__(self):
149        t = ''
150        for tl in self.tail:
151            t += tl + os.linesep
152        return t[:-len(os.linesep)]
153
154    def _tail(self, text):
155        if type(text) is not list:
156            text = text.splitlines()
157        self.tail += text
158        if len(self.tail) > self.tail_size:
159            self.tail = self.tail[-self.tail_size:]
160
161    def has_stdout(self):
162        return self.fhs[0] is not None
163
164    def has_stderr(self):
165        return self.fhs[1] is not None
166
167    def output(self, text):
168        """Output the text message to all the logs."""
169        # Reformat the text to have local line types.
170        text = text.replace(chr(13), '').splitlines()
171        self._tail(text)
172        out = os.linesep.join(text) + os.linesep
173        if isinstance(out, bytes):
174            out = out.decode('utf-8', 'ignore')
175        self.lock.acquire()
176        try:
177            for f in range(0, len(self.fhs)):
178                if self.fhs[f] is not None:
179                    self.fhs[f].write(out)
180            self.flush()
181        except:
182            raise
183        finally:
184            self.lock.release()
185
186    def flush(self):
187        """Flush the output."""
188        for f in range(0, len(self.fhs)):
189            if self.fhs[f] is not None:
190                self.fhs[f].flush()
191
192if __name__ == "__main__":
193    l = log(['stdout', 'log.txt'], tail_size = 20)
194    for i in range(0, 10):
195        l.output('log: hello world: %d\n' % (i))
196    l.output('log: hello world CRLF\r\n')
197    l.output('log: hello world NONE')
198    l.flush()
199    print('=-' * 40)
200    print('tail: %d' % (len(l.tail)))
201    print(l)
202    print('=-' * 40)
203    for i in range(0, 10):
204        l.output('log: hello world 2: %d\n' % (i))
205    l.flush()
206    print('=-' * 40)
207    print('tail: %d' % (len(l.tail)))
208    print(l)
209    print('=-' * 40)
210    for i in [0, 1]:
211        quiet = False
212        tracing = False
213        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
214        trace('trace with quiet and trace off')
215        notice('notice with quiet and trace off')
216        quiet = True
217        tracing = False
218        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
219        trace('trace with quiet on and trace off')
220        notice('notice with quiet on and trace off')
221        quiet = False
222        tracing = True
223        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
224        trace('trace with quiet off and trace on')
225        notice('notice with quiet off and trace on')
226        quiet = True
227        tracing = True
228        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
229        trace('trace with quiet on and trace on')
230        notice('notice with quiet on and trace on')
231        default = l
232    print('=-' * 40)
233    print('tail: %d' % (len(l.tail)))
234    print(l)
235    print('=-' * 40)
236    del l
Note: See TracBrowser for help on using the repository browser.