Robot Framework Integrated Development Environment (RIDE)
text.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 from itertools import takewhile
17 import inspect
18 import os.path
19 import re
20 
21 from .charwidth import get_char_width
22 from .misc import seq2str2
23 from .robottypes import is_string, is_unicode
24 from .unic import unic
25 
26 
27 MAX_ERROR_LINES = 40
28 
31 _MAX_ASSIGN_LENGTH = 200
32 
35 _MAX_ERROR_LINE_LENGTH = 78
36 
39 _ERROR_CUT_EXPLN = ' [ Message content over the limit has been removed. ]'
40 
43 _TAGS_RE = re.compile(r'\s*tags:(.*)', re.IGNORECASE)
44 
45 
47  if MAX_ERROR_LINES is None:
48  return msg
49  lines = msg.splitlines()
50  lengths = _count_line_lengths(lines)
51  if sum(lengths) <= MAX_ERROR_LINES:
52  return msg
53  start = _prune_excess_lines(lines, lengths)
54  end = _prune_excess_lines(lines, lengths, from_end=True)
55  return '\n'.join(start + [_ERROR_CUT_EXPLN] + end)
56 
57 def _prune_excess_lines(lines, lengths, from_end=False):
58  if from_end:
59  lines.reverse()
60  lengths.reverse()
61  ret = []
62  total = 0
63  limit = MAX_ERROR_LINES // 2
64  for line, length in zip(lines[:limit], lengths[:limit]):
65  if total + length >= limit:
66  ret.append(_cut_long_line(line, total, from_end))
67  break
68  total += length
69  ret.append(line)
70  if from_end:
71  ret.reverse()
72  return ret
73 
74 def _cut_long_line(line, used, from_end):
75  available_lines = MAX_ERROR_LINES // 2 - used
76  available_chars = available_lines * _MAX_ERROR_LINE_LENGTH - 3
77  if len(line) > available_chars:
78  if not from_end:
79  line = line[:available_chars] + '...'
80  else:
81  line = '...' + line[-available_chars:]
82  return line
83 
85  return [ _count_virtual_line_length(line) for line in lines ]
86 
88  if not line:
89  return 1
90  lines, remainder = divmod(len(line), _MAX_ERROR_LINE_LENGTH)
91  return lines if not remainder else lines + 1
92 
93 
94 def format_assign_message(variable, value, cut_long=True):
95  formatter = {'$': unic, '@': seq2str2, '&': _dict_to_str}[variable[0]]
96  value = formatter(value)
97  if cut_long and len(value) > _MAX_ASSIGN_LENGTH:
98  value = value[:_MAX_ASSIGN_LENGTH] + '...'
99  return '%s = %s' % (variable, value)
100 
102  if not d:
103  return '{ }'
104  return '{ %s }' % ' | '.join('%s=%s' % (unic(k), unic(v))
105  for k, v in d.items())
106 
107 
109  return sum(get_char_width(char) for char in text)
110 
111 
112 def pad_console_length(text, width):
113  if width < 5:
114  width = 5
115  diff = get_console_length(text) - width
116  if diff > 0:
117  text = _lose_width(text, diff+3) + '...'
118  return _pad_width(text, width)
119 
120 def _pad_width(text, width):
121  more = width - get_console_length(text)
122  return text + ' ' * more
123 
124 def _lose_width(text, diff):
125  lost = 0
126  while lost < diff:
127  lost += get_console_length(text[-1])
128  text = text[:-1]
129  return text
130 
131 
133  if os.path.exists(name):
134  return os.path.abspath(name), []
136  if index == -1:
137  return name, []
138  args = name[index+1:].split(name[index])
139  name = name[:index]
140  if os.path.exists(name):
141  name = os.path.abspath(name)
142  return name, args
143 
144 
146  colon_index = name.find(':')
147  # Handle absolute Windows paths
148  if colon_index == 1 and name[2:3] in ('/', '\\'):
149  colon_index = name.find(':', colon_index+1)
150  semicolon_index = name.find(';')
151  if colon_index == -1:
152  return semicolon_index
153  if semicolon_index == -1:
154  return colon_index
155  return min(colon_index, semicolon_index)
156 
157 
159  doc = doc.rstrip()
160  tags = []
161  if not doc:
162  return doc, tags
163  lines = doc.splitlines()
164  match = _TAGS_RE.match(lines[-1])
165  if match:
166  doc = '\n'.join(lines[:-1]).rstrip()
167  tags = [tag.strip() for tag in match.group(1).split(',')]
168  return doc, tags
169 
170 
171 def getdoc(item):
172  doc = inspect.getdoc(item) or u''
173  if is_unicode(doc):
174  return doc
175  try:
176  return doc.decode('UTF-8')
177  except UnicodeDecodeError:
178  return unic(doc)
179 
180 
181 def getshortdoc(doc_or_item, linesep='\n'):
182  if not doc_or_item:
183  return u''
184  doc = doc_or_item if is_string(doc_or_item) else getdoc(doc_or_item)
185  lines = takewhile(lambda line: line.strip(), doc.splitlines())
186  return linesep.join(lines)
def get_char_width(char)
A module to handle different character widths on the console.
Definition: charwidth.py:33
def _lose_width(text, diff)
Definition: text.py:124
def _cut_long_line(line, used, from_end)
Definition: text.py:74
def _get_arg_separator_index_from_name_or_path(name)
Definition: text.py:145
def format_assign_message(variable, value, cut_long=True)
Definition: text.py:94
def _prune_excess_lines(lines, lengths, from_end=False)
Definition: text.py:57
def split_args_from_name_or_path(name)
Definition: text.py:132
def pad_console_length(text, width)
Definition: text.py:112
def _count_virtual_line_length(line)
Definition: text.py:87
def _pad_width(text, width)
Definition: text.py:120
def getshortdoc(doc_or_item, linesep='\n')
Definition: text.py:181
def _count_line_lengths(lines)
Definition: text.py:84
def split_tags_from_doc(doc)
Definition: text.py:158
def get_console_length(text)
Definition: text.py:108