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 16import re 17 18from robot.output import LOGGER 19from robot.utils import JYTHON, Utf8Reader, prepr 20 21 22NBSP = u'\xa0' 23 24 25class RobotReader(object): 26 _space_splitter = re.compile(u'[ \t\xa0]{2,}|\t+') 27 _pipe_splitter = re.compile(u'[ \t\xa0]+\|(?=[ \t\xa0]+)') 28 _pipe_starts = ('|', '| ', '|\t', u'|\xa0') 29 _pipe_ends = (' |', '\t|', u'\xa0|') 30 31 def read(self, file, populator, path=None): 32 path = path or getattr(file, 'name', '<file-like object>') 33 process = False 34 for lineno, line in enumerate(Utf8Reader(file).readlines(), start=1): 35 cells = self.split_row(line.rstrip()) 36 cells = list(self._check_deprecations(cells, path, lineno)) 37 if cells and cells[0].strip().startswith('*') and \ 38 populator.start_table([c.replace('*', '').strip() 39 for c in cells]): 40 process = True 41 elif process: 42 populator.add(cells) 43 return populator.eof() 44 45 @classmethod 46 def split_row(cls, row): 47 if row[:2] in cls._pipe_starts: 48 row = row[1:-1] if row[-2:] in cls._pipe_ends else row[1:] 49 return [cls._strip_whitespace(cell) 50 for cell in cls._pipe_splitter.split(row)] 51 return cls._space_splitter.split(row) 52 53 def _check_deprecations(self, cells, path, line_number): 54 for original in cells: 55 normalized = self._normalize_whitespace(original) 56 if normalized != original: 57 if len(normalized) != len(original): 58 msg = 'Collapsing consecutive whitespace' 59 else: 60 msg = 'Converting whitespace characters to ASCII spaces' 61 LOGGER.warn("%s during parsing is deprecated. Fix %s in file " 62 "'%s' on line %d." 63 % (msg, prepr(original), path, line_number)) 64 yield normalized 65 66 # Jython has issues with non-ASCII spaces https://bugs.jython.org/issue2772 67 if JYTHON: 68 69 _whitespace = re.compile(u'\\s+', re.UNICODE) 70 _trailing_whitespace = re.compile(u'\\s+$', re.UNICODE) 71 72 @classmethod 73 def _strip_whitespace(cls, string): 74 match = cls._whitespace.match(string) 75 if match: 76 string = string[match.end():] 77 match = cls._trailing_whitespace.search(string) 78 if match: 79 string = string[:match.start()] 80 return string 81 82 def _normalize_whitespace(self, string): 83 return ' '.join(self._whitespace.split(string)) 84 85 else: 86 87 @classmethod 88 def _strip_whitespace(cls, string): 89 return string.strip() 90 91 def _normalize_whitespace(self, string): 92 return ' '.join(string.split()) 93