Robot Framework Integrated Development Environment (RIDE)
populators.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 import os
17 
18 from robotide.lib.robot.errors import DataError
19 from robotide.lib.robot.model import SuiteNamePatterns
20 from robotide.lib.robot.output import LOGGER
21 from robotide.lib.robot.utils import get_error_message, unic
22 
23 from .datarow import DataRow
24 from .tablepopulators import (SettingTablePopulator, VariableTablePopulator,
25  TestTablePopulator, KeywordTablePopulator,
26  NullPopulator)
27 from .htmlreader import HtmlReader
28 from .tsvreader import TsvReader
29 from .robotreader import RobotReader
30 from .restreader import RestReader
31 
32 
33 READERS = {'html': HtmlReader, 'htm': HtmlReader, 'xhtml': HtmlReader,
34  'tsv': TsvReader , 'rst': RestReader, 'rest': RestReader,
35  'txt': RobotReader, 'robot': RobotReader}
36 
37 # Hook for external tools for altering ${CURDIR} processing
38 PROCESS_CURDIR = True
39 
40 
42  pass
43 
44 
46 
49  _populators = {'setting': SettingTablePopulator,
50  'variable': VariableTablePopulator,
51  'test case': TestTablePopulator,
52  'test cases': TestTablePopulator,
53  'task': TestTablePopulator,
54  'tasks': TestTablePopulator,
55  'keyword': KeywordTablePopulator}
56 
57  def __init__(self, datafile, tab_size=2):
58  self._datafile_datafile = datafile
59  self._populator_populator = NullPopulator()
60  self._curdir_curdir = self._get_curdir_get_curdir(datafile.directory)
61  self._tab_size_tab_size = tab_size
62 
63  def _get_curdir(self, path):
64  return path.replace('\\','\\\\') if path else None
65 
66  def add_preamble(self, row):
67  self._datafile_datafile.add_preamble(row)
68 
69  def populate(self, path, resource=False):
70  LOGGER.info("Parsing file '%s'." % path)
71  source = self._open_open(path)
72  try:
73  # print(f"DEBUG: populators populate READER={self._get_reader(path, resource)}")
74  self._get_reader_get_reader(path, resource).read(source, self)
75  except Exception:
76  # print(f"DEBUG: populators populate CALLING DATAERROR")
78  finally:
79  source.close()
80 
81  def _open(self, path):
82  if not os.path.isfile(path):
83  raise DataError("File or directory to execute does not exist.")
84  try:
85  # IronPython handles BOM incorrectly if not using binary mode:
86  # https://ironpython.codeplex.com/workitem/34655
87  return open(path, 'rb')
88  except Exception:
90 
91  def _get_reader(self, path, resource=False):
92  file_format = os.path.splitext(path.lower())[-1][1:]
93  if resource and file_format == 'resource':
94  file_format = 'robot'
95  try:
96  return READERS[file_format](self._tab_size_tab_size)
97  except KeyError:
98  raise DataError("Unsupported file format '%s'." % file_format)
99 
100  def start_table(self, header):
101  self._populator_populator.populate()
102  table = self._datafile_datafile.start_table(DataRow(header).all)
103  self._populator_populator = self._populators_populators[table.type](table) \
104  if table is not None else NullPopulator()
105  return bool(self._populator_populator)
106 
107  def eof(self):
108  self._populator_populator.populate()
109  self._populator_populator = NullPopulator()
110  return bool(self._datafile_datafile)
111 
112  def add(self, row):
113  # print(f"DEBUG: populators enter row={row}")
114  if PROCESS_CURDIR and self._curdir_curdir:
115  row = self._replace_curdirs_in_replace_curdirs_in(row)
116  data = DataRow(row, self._datafile_datafile.source)
117  if data:
118  # print(f"DEBUG: populators add data={data.cells} + {data.comments}")
119  self._populator_populator.add(data)
120 
121  def _replace_curdirs_in(self, row):
122  old, new = '${CURDIR}', self._curdir_curdir
123  return [cell if old not in cell else cell.replace(old, new)
124  for cell in row]
125 
126 
128  ignored_prefixes = ('_', '.')
129  ignored_dirs = ('CVS',)
130 
131  def populate(self, path, datadir, include_suites=None,
132  include_extensions=None, recurse=True, tab_size=2):
133  LOGGER.info("Parsing directory '%s'." % path)
134  include_suites = self._get_include_suites_get_include_suites(path, include_suites)
135  init_file, children = self._get_children_get_children(path, include_extensions,
136  include_suites)
137  if init_file:
138  self._populate_init_file_populate_init_file(datadir, init_file, tab_size)
139  if recurse:
140  self._populate_children_populate_children(datadir, children, include_extensions,
141  include_suites, tab_size)
142 
143  def _populate_init_file(self, datadir, init_file, tab_size):
144  datadir.initfile = init_file
145  try:
146  FromFilePopulator(datadir, tab_size).populate(init_file)
147  except DataError as err:
148  LOGGER.error(err.message)
149 
150  def _populate_children(self, datadir, children, include_extensions,
151  include_suites, tab_size):
152  for child in children:
153  try:
154  datadir.add_child(child, include_suites, include_extensions)
155  except NoTestsFound:
156  LOGGER.info("Data source '%s' has no tests or tasks." % child)
157  except DataError as err:
158  LOGGER.error("Parsing '%s' failed: %s" % (child, err.message))
159 
160  def _get_include_suites(self, path, incl_suites):
161  if not incl_suites:
162  return None
163  if not isinstance(incl_suites, SuiteNamePatterns):
164  incl_suites = SuiteNamePatterns(
165  self._create_included_suites_create_included_suites(incl_suites))
166  # If a directory is included, also all its children should be included.
167  if self._is_in_included_suites_is_in_included_suites(os.path.basename(path), incl_suites):
168  return None
169  return incl_suites
170 
171  def _create_included_suites(self, incl_suites):
172  for suite in incl_suites:
173  yield suite
174  while '.' in suite:
175  suite = suite.split('.', 1)[1]
176  yield suite
177 
178  def _get_children(self, dirpath, incl_extensions, incl_suites):
179  init_file = None
180  children = []
181  for path, is_init_file in self._list_dir_list_dir(dirpath, incl_extensions,
182  incl_suites):
183  if is_init_file:
184  if not init_file:
185  init_file = path
186  else:
187  LOGGER.error("Ignoring second test suite init file '%s'." % path)
188  else:
189  children.append(path)
190  return init_file, children
191 
192  def _list_dir(self, dir_path, incl_extensions, incl_suites):
193  # os.listdir returns Unicode entries when path is Unicode
194  dir_path = unic(dir_path)
195  names = os.listdir(dir_path)
196  for name in sorted(names, key=lambda item: item.lower()):
197  name = unic(name) # needed to handle nfc/nfd normalization on OSX
198  path = os.path.join(dir_path, name)
199  base, ext = os.path.splitext(name)
200  ext = ext[1:].lower()
201  if self._is_init_file_is_init_file(path, base, ext, incl_extensions):
202  yield path, True
203  elif self._is_included_is_included(path, base, ext, incl_extensions, incl_suites):
204  yield path, False
205  else:
206  LOGGER.info("Ignoring file or directory '%s'." % path)
207 
208  def _is_init_file(self, path, base, ext, incl_extensions):
209  return (base.lower() == '__init__' and
210  self._extension_is_accepted_extension_is_accepted(ext, incl_extensions) and
211  os.path.isfile(path))
212 
213  def _extension_is_accepted(self, ext, incl_extensions):
214  if incl_extensions:
215  return ext in incl_extensions
216  return ext in READERS
217 
218  def _is_included(self, path, base, ext, incl_extensions, incl_suites):
219  if base.startswith(self.ignored_prefixesignored_prefixes):
220  return False
221  if os.path.isdir(path):
222  return base not in self.ignored_dirsignored_dirs or ext
223  if not self._extension_is_accepted_extension_is_accepted(ext, incl_extensions):
224  return False
225  return self._is_in_included_suites_is_in_included_suites(base, incl_suites)
226 
227  def _is_in_included_suites(self, name, incl_suites):
228  if not incl_suites:
229  return True
230  return incl_suites.match(self._split_prefix_split_prefix(name))
231 
232  def _split_prefix(self, name):
233  return name.split('__', 1)[-1]
Used when variable does not exist.
Definition: errors.py:67
def _populate_init_file(self, datadir, init_file, tab_size)
Definition: populators.py:143
def _is_included(self, path, base, ext, incl_extensions, incl_suites)
Definition: populators.py:218
def _list_dir(self, dir_path, incl_extensions, incl_suites)
Definition: populators.py:192
def populate(self, path, datadir, include_suites=None, include_extensions=None, recurse=True, tab_size=2)
Definition: populators.py:132
def _populate_children(self, datadir, children, include_extensions, include_suites, tab_size)
Definition: populators.py:151
def _is_init_file(self, path, base, ext, incl_extensions)
Definition: populators.py:208
def _get_children(self, dirpath, incl_extensions, incl_suites)
Definition: populators.py:178
def get_error_message()
Returns error message of the last occurred exception.
Definition: error.py:41