Revision: 2eac96b5fe0d
Author: Robot Framework Developers <[email protected]>
Date: Fri Jan 20 03:06:10 2012
Log: writer: grand refactoring
http://code.google.com/p/robotframework/source/detail?r=2eac96b5fe0d
Added:
/src/robot/writer/aligners.py
/src/robot/writer/dataextractor.py
/src/robot/writer/htmlformatter.py
/src/robot/writer/rowsplitter.py
/utest/writer/test_extractor.py
Deleted:
/src/robot/writer/tableformatters.py
Modified:
/src/robot/writer/datafilewriter.py
/src/robot/writer/filewriters.py
/src/robot/writer/formatters.py
/utest/writer/test_formatters.py
=======================================
--- /dev/null
+++ /src/robot/writer/aligners.py Fri Jan 20 03:06:10 2012
@@ -0,0 +1,58 @@
+# Copyright 2008-2011 Nokia Siemens Networks Oyj
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class _Aligner(object):
+
+ def __init__(self, widths, align_last_column=False):
+ self._widths = widths
+ self._align_last_column = align_last_column
+
+ def align_rows(self, rows):
+ return [self.align_row(r) for r in rows]
+
+ def align_row(self, row):
+ for index, col in enumerate(row[:self._last_aligned_column(row)]):
+ if len(self._widths) <= index:
+ continue
+ row[index] = row[index].ljust(self._widths[index])
+ return row
+
+ def _last_aligned_column(self, row):
+ return len(row) if self._align_last_column else -1
+
+
+class FirstColumnAligner(_Aligner):
+
+ def __init__(self, cols, first_column_width):
+ _Aligner.__init__(self, [first_column_width])
+
+
+class ColumnAligner(_Aligner):
+
+ def __init__(self, first_column_width, table, align_last_column):
+ self._first_column_width = first_column_width
+ _Aligner.__init__(self, self._count_justifications(table),
+ align_last_column)
+
+ def _count_justifications(self, table):
+ result = [self._first_column_width] + [len(h) for h in
table.header[1:]]
+ for element in [list(kw) for kw in list(table)]:
+ for step in element:
+ for index, col in enumerate(step.as_list()):
+ index += 1
+ if len(result) <= index:
+ result.append(0)
+ result[index] = max(len(col), result[index])
+ return result
=======================================
--- /dev/null
+++ /src/robot/writer/dataextractor.py Fri Jan 20 03:06:10 2012
@@ -0,0 +1,56 @@
+# Copyright 2008-2011 Nokia Siemens Networks Oyj
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class DataExtractor(object):
+ """The DataExtractor object. Transforms table of a parsed Robot
Framework
+ test data file into list of rows."""
+
+ def __init__(self, want_name_on_first_content_row=False):
+ self._want_names_on_first_content_row =
want_name_on_first_content_row
+
+ def rows_from_simple_table(self, table):
+ """Return list of rows from a setting or variable table"""
+ return self._rows_from_item(table)
+
+ def rows_from_indented_table(self, table):
+ """Return list of rows from a test case or user keyword table"""
+ items = list(table)
+ for index, item in enumerate(items):
+ for row in self._rows_from_test_or_keyword(item):
+ yield row
+ if not self._last(items, index):
+ yield []
+
+ def _rows_from_test_or_keyword(self, test_or_keyword):
+ rows = list(self._rows_from_item(test_or_keyword, 1)) or ['']
+ first_row, rest = self._first_row(test_or_keyword.name, rows)
+ yield first_row
+ for r in rest:
+ yield r
+
+ def _first_row(self, name, rows):
+ if self._want_names_on_first_content_row:
+ return [name] + rows[0][1:], rows[1:]
+ return [name], rows
+
+ def _rows_from_item(self, item, indent=0):
+ for child in (c for c in item if c.is_set()):
+ yield [''] * indent + child.as_list()
+ if child.is_for_loop():
+ for row in self._rows_from_item(child, indent+1):
+ yield row
+
+ def _last(self, items, index):
+ return index >= len(items) -1
=======================================
--- /dev/null
+++ /src/robot/writer/htmlformatter.py Fri Jan 20 03:06:10 2012
@@ -0,0 +1,134 @@
+# Copyright 2008-2011 Nokia Siemens Networks Oyj
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+
+from robot import utils
+
+from .formatters import _DataFileFormatter
+
+
+class HtmlFormatter(_DataFileFormatter):
+ _want_names_on_first_content_row = True
+
+ def __init__(self):
+ _DataFileFormatter.__init__(self, 5)
+
+ def empty_row(self):
+ return [NameCell('')] + [HtmlCell('') for _ in range(self._cols-1)]
+
+ def _format_row(self, row):
+ row = self._pad(row)
+ if self._is_documentation_row(row):
+ return self._create_documentation_row(row)
+ first_cell = self._create_first_cell(row[0])
+ if self._is_indented_documentation_row(row[1:]):
+ return self._create_indented_documentation_row(first_cell,
row[1:])
+ return [first_cell] + [HtmlCell(c) for c in row[1:]]
+
+ def _is_documentation_row(self, row):
+ return row[0] == 'Documentation'
+
+ def _create_documentation_row(self, row):
+ return [NameCell(row[0]), DocumentationCell(row[1],
span=self._cols-1)]
+
+ def _is_indented_documentation_row(self, cells):
+ return self._is_indented_table(self._current_table) and cells and \
+ cells[0] == '[Documentation]'
+
+ def _create_indented_documentation_row(self, first_cell, cells):
+ start = [first_cell, HtmlCell(cells[0])]
+ if any(c.startswith('#') for c in cells):
+ return start + [HtmlCell(c) for c in cells[1:]]
+ return start + [DocumentationCell(cells[1], self._cols-2)]
+
+ def _create_first_cell(self, cell):
+ if self._is_indented_table(self._current_table) and cell:
+ return AnchorNameCell(cell, 'keyword' if
self._current_table.type == 'keyword' else 'test')
+ return NameCell(cell)
+
+ def header_row(self, table):
+ if not self._should_align_columns(table) or len(table.header) == 1:
+ return [HeaderCell(table.header[0], self._cols)]
+ headers = self._pad_header(table)
+ return [HeaderCell(hdr) for hdr in headers]
+
+ def _pad_header(self, table):
+ return table.header + [''] * (self._max_column_count(table) -
len(table.header))
+
+ def _max_column_count(self, table):
+ count = 0
+ for item in table:
+ for child in item:
+ count = max(count, len(child.as_list()) + 1)
+ return count
+
+ def _pad(self, row):
+ if len(self._current_table.header) == 1 or not
self._is_indented_table(self._current_table):
+ cols = self._cols
+ else:
+ cols = max(self._max_column_count(self._current_table),
+ len(self._current_table.header))
+ return row + [''] * (cols - len(row))
+class HtmlCell(object):
+ _backslash_matcher = re.compile(r'(\\+)n ')
+
+ def __init__(self, content='', attributes=None, tag='td', escape=True):
+ if escape:
+ content = utils.html_escape(content)
+ self.content = self._replace_newlines(content)
+ self.attributes = attributes or {}
+ self.tag = tag
+
+ def _replace_newlines(self, content):
+ def replacer(match):
+ backslash_count = len(match.group(1))
+ if backslash_count % 2 == 1:
+ return '%sn<br>\n' % match.group(1)
+ return match.group()
+ return self._backslash_matcher.sub(replacer, content)
+
+
+class NameCell(HtmlCell):
+
+ def __init__(self, name='', attributes=None):
+ HtmlCell.__init__(self, name, attributes)
+ self.attributes.update({'class': 'name'})
+
+
+class AnchorNameCell(HtmlCell):
+
+ def __init__(self, name, type_):
+ HtmlCell.__init__(self, self._link_from_name(name, type_),
+ {'class': 'name'}, escape=False)
+
+ def _link_from_name(self, name, type_):
+ return '<a name="%s_%s">%s</a>' % (type_,
utils.html_attr_escape(name),
+ utils.html_escape(name))
+
+
+class DocumentationCell(HtmlCell):
+
+ def __init__(self, content, span):
+ HtmlCell.__init__(self, content)
+ self.attributes = {'class': 'colspan%d' % span, 'colspan': '%d' %
span}
+
+
+class HeaderCell(HtmlCell):
+
+ def __init__(self, name, span=1):
+ HtmlCell.__init__(self, name, {'class': 'name', 'colspan': '%d' %
span},
+ tag='th')
+
+
=======================================
--- /dev/null
+++ /src/robot/writer/rowsplitter.py Fri Jan 20 03:06:10 2012
@@ -0,0 +1,62 @@
+# Copyright 2008-2011 Nokia Siemens Networks Oyj
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import itertools
+
+class RowSplitter(object):
+ _comment_mark = '#'
+ _empty_cell_escape = '${EMPTY}'
+ _line_continuation = '...'
+
+ def __init__(self, cols=8):
+ self._cols = cols
+
+ def split(self, row, indented_table=False):
+ if not row:
+ return [[]]
+ return self._split_to_rows(row, indented_table)
+
+ def _split_to_rows(self, data, indented_table):
+ indent = len(list(itertools.takewhile(lambda x: x == '', data)))
+ if indented_table:
+ indent = max(indent, 1)
+ rows = []
+ while data:
+ current, data = self._split(data)
+ rows.append(self._escape_last_empty_cell(current))
+ if data:
+ data = self._indent(data, indent)
+ return rows
+
+ def _split(self, data):
+ row, rest = data[:self._cols], data[self._cols:]
+ self._in_comment = any(c for c in row if c.startswith(
self._comment_mark))
+ rest = self._add_line_continuation(rest)
+ return row, rest
+
+ def _escape_last_empty_cell(self, row):
+ if not row[-1].strip():
+ row[-1] = self._empty_cell_escape
+ return row
+
+ def _add_line_continuation(self, data):
+ if data:
+ if self._in_comment:
+ data[0] = self._comment_mark + data[0]
+ data = [self._line_continuation] + data
+ return data
+
+ def _indent(self, row, indent):
+ return [''] * indent + row
=======================================
--- /dev/null
+++ /utest/writer/test_extractor.py Fri Jan 20 03:06:10 2012
@@ -0,0 +1,49 @@
+import unittest
+
+from robot.parsing.model import VariableTable, TestCaseTable
+from robot.utils.asserts import assert_equals
+from robot.writer.dataextractor import DataExtractor
+
+var_table = VariableTable(None)
+var_table.add('${A scalar}', 'value', 'var comment')
+var_table.add('@{A list}', ['v', 'a', 'lue'])
+
+var_table_rows = [['${A scalar}', 'value', '# var comment'],
+ ['@{A list}', 'v', 'a', 'lue']]
+
+test_table = TestCaseTable(None)
+test = test_table.add('A test case')
+test.add_step(['No Operation'])
+test.add_step(['Log Many', 'bar', 'quux', '#comment'])
+loop = test.add_for_loop(['${i}', 'IN RANGE', '10'])
+loop.add_step(['Log', '${i}'])
+test2 = test_table.add('Second test')
+test2.add_step(['FAIL'])
+
+test_table_rows = [['A test case'],
+ ['', 'No Operation'],
+ ['', 'Log Many', 'bar', 'quux', '#comment'],
+ ['',': FOR', '${i}', 'IN RANGE', '10'],
+ ['', '', 'Log', '${i}'],
+ [],
+ ['Second test'],
+ ['', 'FAIL'],
+ []]
+
+class DataExtractorTest(unittest.TestCase):
+
+ def test_extracting_from_simple_table(self):
+
assert_equals(list(DataExtractor().rows_from_simple_table(var_table)),
+ var_table_rows)
+
+ def test_extracting_from_indented_table(self):
+ for idx, row in
enumerate(DataExtractor().rows_from_indented_table(test_table)):
+ assert_equals(row, test_table_rows[idx])
+
+ def test_names_on_first_content_row(self):
+ table = TestCaseTable(None)
+ t = table.add('Test')
+ t.add_step(['No op'])
+ extractor = DataExtractor(want_name_on_first_content_row=True)
+ assert_equals(list(extractor.rows_from_indented_table(table)),
+ [['Test', 'No op'], []])
=======================================
--- /src/robot/writer/tableformatters.py Thu Jan 5 07:23:09 2012
+++ /dev/null
@@ -1,325 +0,0 @@
-# Copyright 2008-2011 Nokia Siemens Networks Oyj
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import re
-
-from robot.parsing.settings import Documentation
-from robot import utils
-
-
-class _Formatter(object):
-
- def _rows_from_simple_table(self, item, indent=0):
- return self._rows_from_item(item, indent)
-
- def _rows_from_indented_table(self, table):
- items = list(table)
- for index, item in enumerate(items):
- yield self._format_name(item)
- for row in self._rows_from_item(item, 1):
- yield row
- if index < len(items) -1:
- yield []
-
- def _rows_from_item(self, item, indent=0):
- for child in (c for c in item if c.is_set()):
- for row in self._format_model_item(child, indent):
- yield row
- if child.is_for_loop():
- for row in self._rows_from_simple_table(child, indent+1):
- yield row
-
- def _format_name(self, item):
- return [item.name]
-
- def _format_model_item(self, item, indent):
- return item.as_list()
-
-
-class RowSplittingFormatter(_Formatter):
-
- def __init__(self, cols):
- self._cols = cols
- self._row_splitter = RowSplitter(cols)
-
- def format_simple_table(self, table):
- for row in self._rows_from_simple_table(table):
- yield row
-
- def format_indented_table(self, table):
- for row in self._rows_from_indented_table(table):
- yield row
-
- def _format_model_item(self, item, indent):
- return self._row_splitter.split(item.as_list(), indent)
-
-
-class _Aligner(_Formatter):
-
- def __init__(self, widths, align_last_column=False):
- self._widths = widths
- self._align_last_column = align_last_column
-
- def align_rows(self, rows):
- return [self.align_row(r) for r in rows]
-
- def align_row(self, row):
- for index, col in enumerate(row[:self._last_aligned_column(row)]):
- if len(self._widths) <= index:
- continue
- row[index] = row[index].ljust(self._widths[index])
- return row
-
-
- def _last_aligned_column(self, row):
- return len(row) if self._align_last_column else -1
-
-
-class SettingTableAligner(_Aligner):
-
- def __init__(self, cols, first_column_width):
- self._row_splitter = RowSplitter(cols)
- _Aligner.__init__(self, [first_column_width])
-
- def format_simple_table(self, table):
- return self.align_rows(self._rows_from_simple_table(table))
-
- def _format_model_item(self, item, indent):
- return self._row_splitter.split(item.as_list(), indent)
-
-
-class ColumnAligner(_Aligner):
-
- def __init__(self, first_column_width, table, align_last_column):
- self._first_column_width = first_column_width
- _Aligner.__init__(self, self._count_justifications(table),
- align_last_column)
-
- def _count_justifications(self, table):
- result = [self._first_column_width] + [len(h) for h in
table.header[1:]]
- for element in [list(kw) for kw in list(table)]:
- for step in element:
- for index, col in enumerate(step.as_list()):
- index += 1
- if len(result) <= index:
- result.append(0)
- result[index] = max(len(col), result[index])
- return result
-
- def format_indented_table(self, table):
- items = list(table)
- for i, item in enumerate(items):
- rows = list(self._rows_from_item(item, 1))
- if len(item.name) > self._first_column_width:
- yield [item.name]
- else:
- first_row = [item.name] + rows[0][1:]
- yield self.align_row(first_row)
- rows = rows[1:]
- for r in rows:
- yield self.align_row(r)
- if i < len(items) - 1:
- yield []
-
- def _format_model_item(self, item, indent):
- return [self._escape(['']*indent + item.as_list())]
-
- def _escape(self, row):
- if len(row) >= 2 and row[0] == '' and row[1] == '':
- row[1] = '\\'
- return [re.sub('\s\s+(?=[^\s])', lambda
match: '\\'.join(match.group(0)), item) for item in row]
-
-
-class SplittingHtmlFormatter(RowSplittingFormatter):
-
- def format_indented_table(self, table):
- items = list(table)
- for i, item in enumerate(items):
- rows = list(self._rows_from_item(item, 1))
- yield self._first_row(item, rows[0])
- for row in rows[1:]:
- yield row
- if i < len(items) - 1:
- yield self._pad([NameCell()])
-
- def _first_row(self, item, row):
- return [self._format_name(item)] + row[1:]
-
- def _format_name(self, item):
- from robot.parsing.model import UserKeyword
- type_ = 'keyword' if isinstance(item, UserKeyword) else 'test'
- return AnchorNameCell(item.name, type_)
-
- def _format_model_item(self, item, indent):
- if isinstance(item, Documentation):
- return self._format_documentation(item, indent)
- rows = self._row_splitter.split(item.as_list(), indent)
- return [self._pad([NameCell(row[0])] + [HtmlCell(c) for c in
row[1:]]) for row in rows]
-
- def _format_documentation(self, doc, indent):
- if indent:
- start = [NameCell(), HtmlCell(doc.setting_name)]
- value = doc.as_list()[1:]
- if len(value) == 1:
- return [start + [DocumentationCell(doc.value,
self._cols-1-indent)]]
- return [self._pad(start + [HtmlCell(v) for v in value])]
- return [[NameCell(doc.setting_name),
- DocumentationCell(doc.value, self._cols-1)]]
-
- def _format_row(self, row):
- if row and not isinstance(row[0], basestring):
- return row
- return self._pad([NameCell(row[0])] + [HtmlCell(c) for c in
row[1:]])
-
- def _pad(self, row, colspan=False, indent=0):
- if colspan:
- return row
- return row + [HtmlCell()] * (self._cols - len(row) - indent)
-
-
-class SingleLineHtmlFormatter(_Formatter):
-
- def __init__(self, cols):
- self._cols = cols
-
- def format_indented_table(self, table):
- items = list(table)
- for i, item in enumerate(items):
- rows = list(self._rows_from_item(item, 1))
- yield self._pad(self._first_row(item, rows[0]))
- for row in rows[1:]:
- yield self._pad(row)
- if i < len(items) - 1:
- yield self._pad([NameCell()])
-
- def _pad(self, row):
- return row + [HtmlCell()] * (self._cols - len(row))
-
- def _first_row(self, item, row):
- return [self._format_name(item)] + row[1:]
-
- def _format_name(self, item):
- from robot.parsing.model import UserKeyword
- type_ = 'keyword' if isinstance(item, UserKeyword) else 'test'
- return AnchorNameCell(item.name, type_)
-
- def _format_model_item(self, item, indent):
- if isinstance(item, Documentation):
- return self._format_documentation(item, indent)
- data = [''] * indent + item.as_list()
- return [[NameCell(data[0])] + [HtmlCell(c) for c in data[1:]]]
-
- def _format_documentation(self, doc, indent):
- if indent:
- start = [NameCell(), HtmlCell(doc.setting_name)]
- value = doc.as_list()[1:]
- if len(value) == 1:
- return [start + [DocumentationCell(doc.value, 1)]]
- return [start + [HtmlCell(v) for v in value]]
- return [[NameCell(doc.setting_name), DocumentationCell(doc.value,
1)]]
-
-
-class HtmlCell(object):
- _backslash_matcher = re.compile(r'(\\+)n ')
-
- def __init__(self, content='', attributes=None, tag='td', escape=True):
- if escape:
- content = utils.html_escape(content)
- self.content = self._replace_newlines(content)
- self.attributes = attributes or {}
- self.tag = tag
-
- def _replace_newlines(self, content):
- def replacer(match):
- backslash_count = len(match.group(1))
- if backslash_count % 2 == 1:
- return '%sn<br>\n' % match.group(1)
- return match.group()
- return self._backslash_matcher.sub(replacer, content)
-
-
-class NameCell(HtmlCell):
-
- def __init__(self, name='', attributes=None):
- HtmlCell.__init__(self, name, attributes)
- self.attributes.update({'class': 'name'})
-
-
-class AnchorNameCell(HtmlCell):
-
- def __init__(self, name, type_):
- HtmlCell.__init__(self, self._link_from_name(name, type_),
- {'class': 'name'}, escape=False)
-
- def _link_from_name(self, name, type_):
- return '<a name="%s_%s">%s</a>' % (type_,
utils.html_attr_escape(name),
- utils.html_escape(name))
-
-
-class DocumentationCell(HtmlCell):
-
- def __init__(self, content, span):
- HtmlCell.__init__(self, content)
- self.attributes = {'class': 'colspan%d' % span, 'colspan': '%d' %
span}
-
-
-class HeaderCell(HtmlCell):
-
- def __init__(self, name, span=1):
- HtmlCell.__init__(self, name, {'class': 'name', 'colspan': '%d' %
span},
- tag='th')
-
-
-class RowSplitter(object):
- _comment_mark = '#'
- _empty_cell_escape = '${EMPTY}'
- _line_continuation = '...'
-
- def __init__(self, cols=8):
- self._cols = cols
-
- def split(self, row, indent):
- self._in_comment = False
- return self._split_to_rows(row, indent)
-
- def _split_to_rows(self, data, indent=0):
- if not data:
- return [[]]
- rows = []
- while data:
- current, data = self._split(self._indent(data, indent))
- rows.append(self._escape_last_empty_cell(current))
- return rows
-
- def _split(self, data):
- row, rest = data[:self._cols], data[self._cols:]
- self._in_comment = any(c for c in row if
- c.startswith(self._comment_mark))
- rest = self._add_line_continuation(rest)
- return row, rest
-
- def _escape_last_empty_cell(self, row):
- if not row[-1].strip():
- row[-1] = self._empty_cell_escape
- return row
-
- def _add_line_continuation(self, data):
- if data:
- if self._in_comment:
- data[0] = self._comment_mark + data[0]
- data = [self._line_continuation] + data
- return data
-
- def _indent(self, row, indent):
- return [''] * indent + row
=======================================
--- /src/robot/writer/datafilewriter.py Wed Jan 18 22:55:12 2012
+++ /src/robot/writer/datafilewriter.py Fri Jan 20 03:06:10 2012
@@ -28,9 +28,9 @@
:param datafile: A robot.parsing.model.DataFile object to be
written
:param options: A :py:class:`.WriteConfiguration` is created with
these
"""
- context = WriteConfiguration(datafile, **options)
- FileWriter(context).write(datafile)
- context.finish()
+ configuration = WriteConfiguration(datafile, **options)
+ FileWriter(configuration).write(datafile)
+ configuration.finish()
class WriteConfiguration(object):
=======================================
--- /src/robot/writer/filewriters.py Wed Jan 18 22:55:12 2012
+++ /src/robot/writer/filewriters.py Fri Jan 20 03:06:10 2012
@@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+from robot.writer.htmlformatter import HtmlFormatter
try:
import csv
@@ -20,7 +21,7 @@
from robot import utils
-from .formatters import TsvFormatter, TxtFormatter, PipeFormatter,
HtmlFormatter
+from .formatters import TsvFormatter, TxtFormatter, PipeFormatter
from .htmltemplate import TEMPLATE_START, TEMPLATE_END
@@ -65,10 +66,10 @@
self._write_row(self._formatter.header_row(table))
def _formatted_table(self, table):
- formatter = {'setting': self._formatter.setting_rows,
- 'variable': self._formatter.variable_rows,
- 'test case': self._formatter.test_rows,
- 'keyword': self._formatter.keyword_rows}[table.type]
+ formatter = {'setting': self._formatter.setting_table,
+ 'variable': self._formatter.variable_table,
+ 'test case': self._formatter.test_table,
+ 'keyword': self._formatter.keyword_table}[table.type]
return formatter(table)
def _write_empty_row(self):
=======================================
--- /src/robot/writer/formatters.py Tue Jan 10 05:00:23 2012
+++ /src/robot/writer/formatters.py Fri Jan 20 03:06:10 2012
@@ -13,57 +13,74 @@
# limitations under the License.
import re
-from robot.writer.tableformatters import SingleLineHtmlFormatter
-
-from .tableformatters import (RowSplittingFormatter,
SplittingHtmlFormatter,
- ColumnAligner, SettingTableAligner, NameCell, HeaderCell, HtmlCell)
+
+from .aligners import FirstColumnAligner, ColumnAligner
+from .dataextractor import DataExtractor
+from .rowsplitter import RowSplitter
-class _TestDataFileFormatter(object):
-
- def variable_rows(self, variables):
- for row in
self._variable_table_formatter().format_simple_table(variables):
- yield self._format_row(row)
-
- def setting_rows(self, settings):
- for row in
self._setting_table_formatter().format_simple_table(settings):
- yield self._format_row(row)
-
- def test_rows(self, tests):
- for row in
self._test_table_formatter(tests).format_indented_table(tests):
- yield self._format_row(row)
-
- def keyword_rows(self, keywords):
- for row in
self._keyword_table_formatter(keywords).format_indented_table(keywords):
- yield self._format_row(row)
-
- def empty_row(self):
- return self._format_row([])
-
- def _format_row(self, row):
- return row
-
- def _should_align_columns(self, table):
- return table.type in ['test case', 'keyword'] and
bool(table.header[1:])
-
-
-class TsvFormatter(_TestDataFileFormatter):
-
- def __init__(self, cols=8):
- self._cols = cols
- self._formatter = RowSplittingFormatter(self._cols)
-
- def _variable_table_formatter(self):
- return self._formatter
-
- def _setting_table_formatter(self):
- return self._formatter
-
- def _test_table_formatter(self, tests):
- return self._formatter
-
- def _keyword_table_formatter(self, keywords):
- return self._formatter
+class _DataFileFormatter(object):
+ _want_names_on_first_content_row = False
+
+ def __init__(self, cols):
+ self._splitter = RowSplitter(cols)
+ self._cols = cols
+ self._current_table = None
+ self._extractor =
DataExtractor(self._want_names_on_first_content_row)
+
+ def empty_row(self):
+ return self._format_row([])
+
+ def setting_table(self, settings):
+ self._current_table = settings
+ return self._simple_table()
+
+ def variable_table(self, variables):
+ self._current_table = variables
+ return self._simple_table()
+
+ def _simple_table(self):
+ return
self._format_rows(self._extractor.rows_from_simple_table(self._current_table))
+
+ def test_table(self, tests):
+ self._current_table = tests
+ return self._indented_table()
+
+ def keyword_table(self, keywords):
+ self._current_table = keywords
+ return self._indented_table()
+
+ def _indented_table(self):
+ return
self._format_rows(self._extractor.rows_from_indented_table(self._current_table))
+
+ def _format_rows(self, rows):
+ if self._should_split_rows():
+ return self._split_rows(rows)
+ return [self._format_row(r) for r in rows]
+
+ def _should_split_rows(self):
+ return True
+
+ def _split_rows(self, rows):
+ for row in rows:
+ for r in self._splitter.split(row,
self._is_indented_table(self._current_table)):
+ yield self._format_row(r)
+
+ def _should_align_columns(self, table):
+ return self._is_indented_table(table) and bool(table.header[1:])
+
+ def _is_indented_table(self, table):
+ return table.type in ['test case', 'keyword']
+
+ def _format_row(self, row):
+ return row
+
+
+class TsvFormatter(_DataFileFormatter):
+
+ def __init__(self, cols=8):
+ _DataFileFormatter.__init__(self, cols)
+ self._cols = cols
def header_row(self, table):
return self._format_row(['*%s*' % cell for cell in table.header])
@@ -76,41 +93,41 @@
return row + [''] * (self._cols - len(row))
-class TxtFormatter(_TestDataFileFormatter):
- _FIRST_COL_WIDTH = 18
- _SETTING_NAME_WIDTH = 14
+class TxtFormatter(_DataFileFormatter):
+ _test_or_keyword_name_width = 18
+ _setting_and_variable_name_width = 14
_align_last_column = False
def __init__(self, cols=8):
- self._cols = cols
-
- def _variable_table_formatter(self):
- return SettingTableAligner(self._cols, self._SETTING_NAME_WIDTH)
-
- def _setting_table_formatter(self):
- return SettingTableAligner(self._cols, self._SETTING_NAME_WIDTH)
-
- def _test_table_formatter(self, tests):
- return self._indented_table_formatter(tests)
-
- def _keyword_table_formatter(self, keywords):
- return self._indented_table_formatter(keywords)
+ _DataFileFormatter.__init__(self, cols)
+ self._simple_aligner = FirstColumnAligner(cols,
+ self._setting_and_variable_name_width)
+ self._aligner = None
+
+ def _format_row(self, row):
+ row = self._escape(row)
+ if self._aligner:
+ return self._aligner.align_row(row)
+ return row
def header_row(self, table):
header = ['*** %s ***' % table.header[0]] + table.header[1:]
if self._should_align_columns(table):
- return ColumnAligner(self._FIRST_COL_WIDTH, table,
- self._align_last_column).align_row(header)
+ aligner = ColumnAligner(self._test_or_keyword_name_width,
table,
+ self._align_last_column)
+ return aligner.align_row(header)
return header
- def _indented_table_formatter(self, table):
- if self._should_align_columns(table):
- return ColumnAligner(self._FIRST_COL_WIDTH, table,
- self._align_last_column)
- return RowSplittingFormatter(self._cols)
-
- def _format_row(self, row):
- return self._escape(row)
+ def _should_split_rows(self):
+ if self._should_align_columns(self._current_table):
+ self._aligner = ColumnAligner(self._test_or_keyword_name_width,
+ self._current_table,
self._align_last_column)
+ return False
+ elif self._is_indented_table(self._current_table):
+ self._aligner = None
+ return True
+ self._aligner = self._simple_aligner
+ return True
def _escape(self, row):
return self._escape_consecutive_whitespace(
@@ -147,50 +164,3 @@
cell = cell[:-1] + '\\|'
return cell
-
-class HtmlFormatter(_TestDataFileFormatter):
-
- def __init__(self):
- self._default_cols = 5
- self._cols = self._default_cols
- self._formatter = SplittingHtmlFormatter(self._default_cols)
-
- def empty_row(self):
- return [NameCell('')] + [HtmlCell('') for _ in range(self._cols-1)]
-
- def _setting_table_formatter(self):
- self._cols = self._default_cols
- return self._formatter
-
- def _variable_table_formatter(self):
- self._cols = self._default_cols
- return self._formatter
-
- def _test_table_formatter(self, tests):
- return self._dynamic_width_formatter(tests)
-
- def _keyword_table_formatter(self, keywords):
- return self._dynamic_width_formatter(keywords)
-
- def _dynamic_width_formatter(self, table):
- if len(table.header) == 1:
- self._cols = self._default_cols
- return SplittingHtmlFormatter(self._cols)
- self._cols = max(self._max_column_count(table), len(table.header))
- return SingleLineHtmlFormatter(self._cols)
-
- def header_row(self, table):
- if not self._should_align_columns(table) or len(table.header) == 1:
- return [HeaderCell(table.header[0], self._default_cols)]
- headers = self._pad_header(table)
- return [HeaderCell(hdr) for hdr in headers]
-
- def _pad_header(self, table):
- return table.header + [''] * (self._max_column_count(table) -
len(table.header))
-
- def _max_column_count(self, table):
- count = 0
- for item in table:
- for child in item:
- count = max(count, len(child.as_list()) + 1)
- return count
=======================================
--- /utest/writer/test_formatters.py Tue Jan 3 04:46:29 2012
+++ /utest/writer/test_formatters.py Fri Jan 20 03:06:10 2012
@@ -1,8 +1,8 @@
import unittest
from robot.parsing.model import TestCaseTable, TestCaseFileSettingTable
-from robot.writer.formatters import TxtFormatter, HtmlFormatter,
TsvFormatter, PipeFormatter
-from robot.writer.tableformatters import RowSplitter, HtmlCell
+from robot.writer.formatters import TxtFormatter, TsvFormatter,
PipeFormatter, RowSplitter
+from robot.writer.htmlformatter import HtmlFormatter, HtmlCell
from robot.utils.asserts import assert_equals, assert_true
@@ -40,7 +40,7 @@
def test_empty_cell(self):
settings = TestCaseFileSettingTable(None)
settings.force_tags.value = ['f1', '', 'f3']
- assert_equals(list(PipeFormatter().setting_rows(settings))[0],
+ assert_equals(list(PipeFormatter().setting_table(settings))[0],
['Force Tags ', 'f1', ' ', 'f3'])
@@ -59,7 +59,7 @@
table = TestCaseFileSettingTable(None)
table.set_header('Settings')
table.doc.value = 'Some documentation'
- formatted = list(self._formatter.setting_rows(table))
+ formatted = list(self._formatter.setting_table(table))
assert_equals(self._rows_to_text(formatted),
[['Documentation', 'Some documentation']])
assert_equals(formatted[0][1].attributes,
@@ -70,7 +70,7 @@
test = table.add('A Test')
test.tags.value = ['t1', 't2', 't3', 't4']
formatted = self._rows(table)
- assert_equals(len(formatted), 2)
+ assert_equals(len(formatted), 2, formatted)
assert_equals(formatted[0], ['<a name="test_A Test">A
Test</a>', '[Tags]', 't1', 't2', 't3'])
assert_equals(formatted[1], ['', '...', 't4', '', ''])
@@ -80,7 +80,7 @@
test.doc.value = 'Some doc'
assert_equals(self._rows(table)[0],
['<a name="test_Test">Test</a>', '[Documentation]', 'Some
doc'])
-
assert_equals(list(self._formatter.test_rows(table))[0][2].attributes,
+
assert_equals(list(self._formatter.test_table(table))[0][2].attributes,
{'colspan': '3', 'class': 'colspan3'})
def test_test_documentation_with_comment(self):
@@ -90,7 +90,7 @@
test.doc._set_comment('a comment')
assert_equals(self._rows(table)[0],
['<a name="test_Test">Test</a>', '[Documentation]', 'Some
doc', '# a comment', ''])
-
assert_equals(list(self._formatter.test_rows(table))[0][2].attributes, {})
+
assert_equals(list(self._formatter.test_table(table))[0][2].attributes, {})
def test_testcase_table_custom_headers(self):
self._check_header_length([], 1)
@@ -128,13 +128,13 @@
self._check_row_lengths(table, 4)
def _check_row_lengths(self, table, expected_length):
- rows = list(self._formatter.test_rows(table))
+ rows = list(self._formatter.test_table(table))
assert_true(len(rows) > 0)
for row in rows:
assert_equals(len(row), expected_length)
def _rows(self, table):
- return self._rows_to_text(self._formatter.test_rows(table))
+ return self._rows_to_text(self._formatter.test_table(table))
def _rows_to_text(self, rows):
return [[cell.content for cell in row] for row in rows]