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