Robot Framework Integrated Development Environment (RIDE)
highlighting.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 # Windows highlighting code adapted from color_console.py. It is copyright
17 # Andre Burgaud, licensed under the MIT License, and available here:
18 # http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/
19 
20 from contextlib import contextmanager
21 import os
22 import sys
23 try:
24  from ctypes import windll, Structure, c_short, c_ushort, byref
25 except ImportError: # Not on Windows or using Jython
26  windll = None
27 
28 from robotide.lib.robot.errors import DataError
29 from robotide.lib.robot.utils import console_encode, isatty, WINDOWS
30 
31 
33 
34  def __init__(self, stream, colors='AUTO'):
35  self.streamstream = stream
36  self._highlighter_highlighter = self._get_highlighter_get_highlighter(stream, colors)
37 
38  def _get_highlighter(self, stream, colors):
39  options = {'AUTO': Highlighter if isatty(stream) else NoHighlighting,
40  'ON': Highlighter,
41  'OFF': NoHighlighting,
42  'ANSI': AnsiHighlighter}
43  try:
44  highlighter = options[colors.upper()]
45  except KeyError:
46  raise DataError("Invalid console color value '%s'. Available "
47  "'AUTO', 'ON', 'OFF' and 'ANSI'." % colors)
48  return highlighter(stream)
49 
50  def write(self, text, flush=True):
51  self._write_write(console_encode(text, stream=self.streamstream))
52  if flush:
53  self.flushflush()
54 
55  def _write(self, text, retry=5):
56  # Workaround for Windows 10 console bug:
57  # https://github.com/robotframework/robotframework/issues/2709
58  try:
59  self.streamstream.write(text)
60  except IOError as err:
61  if not (WINDOWS and err.errno == 0 and retry > 0):
62  raise
63  self._write_write(text, retry-1)
64 
65  def flush(self):
66  self.streamstream.flush()
67 
68  def highlight(self, text, status=None, flush=True):
69  if self._must_flush_before_and_after_highlighting_must_flush_before_and_after_highlighting():
70  self.flushflush()
71  flush = True
72  with self._highlighting_highlighting(status or text):
73  self.writewrite(text, flush)
74 
76  # Must flush on Windows before and after highlighting to make sure set
77  # console colors only affect the actual highlighted text. Problems
78  # only encountered with Python 3, but better to be safe than sorry.
79  return WINDOWS and not isinstance(self._highlighter_highlighter, NoHighlighting)
80 
81  def error(self, message, level):
82  self.writewrite('[ ', flush=False)
83  self.highlighthighlight(level, flush=False)
84  self.writewrite(' ] %s\n' % message)
85 
86  @contextmanager
87  def _highlighting(self, status):
88  highlighter = self._highlighter_highlighter
89  start = {'PASS': highlighter.green,
90  'FAIL': highlighter.red,
91  'ERROR': highlighter.red,
92  'WARN': highlighter.yellow}[status]
93  start()
94  try:
95  yield
96  finally:
97  highlighter.reset()
98 
99 
100 def Highlighter(stream):
101  if os.sep == '/':
102  return AnsiHighlighter(stream)
103  return DosHighlighter(stream) if windll else NoHighlighting(stream)
104 
105 
107 
110  _ANSI_GREEN = '\033[32m'
111 
114  _ANSI_RED = '\033[31m'
115 
118  _ANSI_YELLOW = '\033[33m'
119 
122  _ANSI_RESET = '\033[0m'
123 
124  def __init__(self, stream):
125  self._stream_stream = stream
126 
127  def green(self):
128  self._set_color_set_color(self._ANSI_GREEN_ANSI_GREEN)
129 
130  def red(self):
131  self._set_color_set_color(self._ANSI_RED_ANSI_RED)
132 
133  def yellow(self):
134  self._set_color_set_color(self._ANSI_YELLOW_ANSI_YELLOW)
135 
136  def reset(self):
137  self._set_color_set_color(self._ANSI_RESET_ANSI_RESET)
138 
139  def _set_color(self, color):
140  self._stream_stream.write(color)
141 
142 
144 
145  def _set_color(self, color):
146  pass
147 
148 
149 class DosHighlighter():
150 
153  _FOREGROUND_GREEN = 0x2
154 
157  _FOREGROUND_RED = 0x4
158 
161  _FOREGROUND_YELLOW = 0x6
162 
165  _FOREGROUND_GREY = 0x7
166 
169  _FOREGROUND_INTENSITY = 0x8
170 
173  _BACKGROUND_MASK = 0xF0
174 
177  _STDOUT_HANDLE = -11
178 
181  _STDERR_HANDLE = -12
182 
183  def __init__(self, stream):
184  self._handle_handle = self._get_std_handle_get_std_handle(stream)
185  self._orig_colors_orig_colors = self._get_colors_get_colors()
186  self._background_background = self._orig_colors_orig_colors & self._BACKGROUND_MASK_BACKGROUND_MASK
187 
188  def green(self):
189  self._set_foreground_colors_set_foreground_colors(self._FOREGROUND_GREEN_FOREGROUND_GREEN)
190 
191  def red(self):
192  self._set_foreground_colors_set_foreground_colors(self._FOREGROUND_RED_FOREGROUND_RED)
193 
194  def yellow(self):
195  self._set_foreground_colors_set_foreground_colors(self._FOREGROUND_YELLOW_FOREGROUND_YELLOW)
196 
197  def reset(self):
198  self._set_colors_set_colors(self._orig_colors_orig_colors)
199 
200  def _get_std_handle(self, stream):
201  handle = self._STDOUT_HANDLE_STDOUT_HANDLE \
202  if stream is sys.__stdout__ else self._STDERR_HANDLE_STDERR_HANDLE
203  return windll.kernel32.GetStdHandle(handle)
204 
205  def _get_colors(self):
207  ok = windll.kernel32.GetConsoleScreenBufferInfo(self._handle_handle, byref(csbi))
208  if not ok: # Call failed, return default console colors (gray on black)
209  return self._FOREGROUND_GREY_FOREGROUND_GREY
210  return csbi.wAttributes
211 
212  def _set_foreground_colors(self, colors):
213  self._set_colors_set_colors(colors | self._FOREGROUND_INTENSITY_FOREGROUND_INTENSITY | self._background_background)
214 
215  def _set_colors(self, colors):
216  windll.kernel32.SetConsoleTextAttribute(self._handle_handle, colors)
217 
218 
219 if windll:
220 
221  class _COORD(Structure):
222 
225  _fields_ = [("X", c_short),
226  ("Y", c_short)]
227 
228  class _SMALL_RECT(Structure):
229 
232  _fields_ = [("Left", c_short),
233  ("Top", c_short),
234  ("Right", c_short),
235  ("Bottom", c_short)]
236 
237  class _CONSOLE_SCREEN_BUFFER_INFO(Structure):
238 
241  _fields_ = [("dwSize", _COORD),
242  ("dwCursorPosition", _COORD),
243  ("wAttributes", c_ushort),
244  ("srWindow", _SMALL_RECT),
245  ("dwMaximumWindowSize", _COORD)]
Used when variable does not exist.
Definition: errors.py:67
def highlight(self, text, status=None, flush=True)
Definition: highlighting.py:68
def write(msg, level='INFO', html=False)
Writes the message to the log file using the given level.
Definition: logger.py:86
def console_encode(string, errors='replace', stream=sys.__stdout__)
Encodes Unicode to bytes in console or system encoding.
Definition: encoding.py:62