diff --git a/README.rst b/README.rst index b5c50836..765ebb75 100644 --- a/README.rst +++ b/README.rst @@ -19,16 +19,14 @@ pytablewriter Summary ------- -pytablewriter is a python library to write a table in various formats: CSV/HTML/JavaScript/JSON/LTSV/Markdown/MediaWiki/Excel/Pandas/Python/reStructuredText/TSV +pytablewriter is a python library to write a table in various formats: CSV/HTML/JavaScript/JSON/LTSV/Markdown/MediaWiki/Excel/Pandas/Python/reStructuredText/TOML/TSV. Features -------- - Write a table in various formats: - CSV - - Microsoft Excel :superscript:`TM` - - `.xlsx` format - - `.xls` format + - Microsoft Excel :superscript:`TM` (``.xlsx``/``.xls`` file format) - HTML - JSON - `Labeled Tab-separated Values (LTSV) `__ @@ -38,17 +36,15 @@ Features - `Pandas `__ (Definition of a DataFrame variable) - Python code (Definition of a nested list variable) - JavaScript (Definition of a nested list variable) - - reStructuredText - - Grid tables - - Simple tables - - CSV table + - reStructuredText: `Grid Tables `__/`Simple Tables `__/`CSV Table `__ - Tab-separated values (TSV) + - `TOML `__ - Automatic tabular data formatting - Alignment - Padding - Decimal places of numbers - Multibyte character support -- Output table to a stream such as a file or the standard output +- Write table to a stream such as a file/standard-output/string-buffer Examples ======== @@ -307,6 +303,7 @@ Python 2.7+ or 3.3+ - `mbstrdecoder `__ - `pathvalidate `__ - `six `__ +- `toml `__ - `XlsxWriter `__ - `xlwt `__ @@ -328,5 +325,5 @@ Related Project =============== - `pytablereader `__ - - Loaded table data with ``pytablereader`` can write another table format by ``pytablewriter``. + - Tabular data loaded by ``pytablereader`` can be written another tabular data format with ``pytablewriter``. diff --git a/docs/conf.py b/docs/conf.py index 4f7439b4..6537a3d7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,6 +10,7 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../pytablewriter')) +#sys.path.insert(0, os.path.abspath('../pytablewriter/writer')) # -- General configuration ------------------------------------------------ @@ -331,6 +332,7 @@ .. |RstGridTableWriter| replace:: :py:class:`~pytablewriter.RstGridTableWriter` .. |RstSimpleTableWriter| replace:: :py:class:`~pytablewriter.RstSimpleTableWriter` .. |TsvTableWriter| replace:: :py:class:`~pytablewriter.TsvTableWriter` +.. |TomlTableWriter| replace:: :py:class:`~pytablewriter.TomlTableWriter` """ rp_module = u""" @@ -341,22 +343,22 @@ rp_attr = u""" .. |iteration_length| replace:: - :py:attr:`~pytablewriter._table_writer.TableWriter.iteration_length` + :py:attr:`~pytablewriter.writer._table_writer.AbstractTableWriter.iteration_length` .. |stream| replace:: - :py:attr:`~pytablewriter._table_writer.TableWriter.stream` + :py:attr:`~pytablewriter.writer._table_writer.AbstractTableWriter.stream` .. |table_name| replace:: - :py:attr:`~pytablewriter._table_writer.TableWriter.table_name` + :py:attr:`~pytablewriter.writer._table_writer.AbstractTableWriter.table_name` .. |header_list| replace:: - :py:attr:`~pytablewriter._table_writer.TableWriter.header_list` + :py:attr:`~pytablewriter.writer._table_writer.AbstractTableWriter.header_list` .. |value_matrix| replace:: - :py:attr:`~pytablewriter._table_writer.TableWriter.value_matrix` + :py:attr:`~pytablewriter.writer._table_writer.AbstractTableWriter.value_matrix` .. |write_callback| replace:: - :py:attr:`~pytablewriter._table_writer.TableWriter.write_callback` + :py:attr:`~pytablewriter.writer._table_writer.AbstractTableWriter.write_callback` .. |column_delimiter| replace:: :py:attr:`~pytablewriter._text_writer.TextTableWriter.column_delimiter` diff --git a/docs/index.rst b/docs/index.rst index 61dad81e..c3e66638 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,9 @@ Welcome to pytablewriter's documentation! ========================================= +.. image:: https://img.shields.io/github/stars/thombashi/pytablewriter.svg?style=social&label=Star + :target: https://github.com/thombashi/pytablewriter + .. toctree:: :caption: Table of Contents :maxdepth: 3 diff --git a/docs/make_readme.py b/docs/make_readme.py index 53b2cb99..9a7967ba 100644 --- a/docs/make_readme.py +++ b/docs/make_readme.py @@ -72,7 +72,8 @@ def main(): maker.write_chapter("Related Project") maker.write_line_list([ "- `pytablereader `__", - " - Loaded table data with ``pytablereader`` can write another table format by ``pytablewriter``." + " - Tabular data loaded by ``pytablereader`` can be written " + "another tabular data format with ``pytablewriter``.", ]) return 0 diff --git a/docs/pages/examples/index.rst b/docs/pages/examples/index.rst index b964a712..8278d5f2 100644 --- a/docs/pages/examples/index.rst +++ b/docs/pages/examples/index.rst @@ -14,5 +14,6 @@ Examples mediawiki rst/index sourcecode/index + toml multibyte_table datasource/index diff --git a/docs/pages/examples/toml.rst b/docs/pages/examples/toml.rst new file mode 100644 index 00000000..fcf73631 --- /dev/null +++ b/docs/pages/examples/toml.rst @@ -0,0 +1,56 @@ +.. _example-toml-table-writer: + +Write a TOML table +---------------------------- + +|TomlTableWriter| class can write a +`TOML `__ +format table to the |stream| from a matrix of data. + +.. code-block:: python + :caption: Sample code that writes a TOML table + + import pytablewriter + + writer = pytablewriter.TomlTableWriter() + writer.header_list = ["int", "float", "str", "bool", "mix", "time"] + writer.value_matrix = [ + [0, 0.1, "hoge", True, 0, "2017-01-01 03:04:05+0900"], + [2, "-2.23", "foo", False, None, "2017-12-23 45:01:23+0900"], + [3, 0, "bar", "true", "inf", "2017-03-03 33:44:55+0900"], + [-10, -9.9, "", "FALSE", "nan", "2017-01-01 00:00:00+0900"], + ] + + writer.write_table() + + +.. code-block:: none + :caption: Output of TOML + + [[example_table]] + int = 0 + float = 0.1 + mix = 0 + bool = true + str = "hoge" + time = "2017-01-01 03:04:05+0900" + [[example_table]] + int = 2 + float = -2.23 + bool = false + str = "foo" + time = "2017-12-23 12:34:51+0900" + [[example_table]] + int = 3 + float = 0 + mix = Infinity + bool = true + str = "bar" + time = "2017-03-03 22:44:55+0900" + [[example_table]] + int = -10 + float = -9.9 + mix = NaN + bool = false + str = "" + time = "2017-01-01 00:00:00+0900" diff --git a/docs/pages/installation.rst b/docs/pages/installation.rst index 535a74f2..7f2e8c9d 100644 --- a/docs/pages/installation.rst +++ b/docs/pages/installation.rst @@ -16,6 +16,7 @@ Python 2.7+ or 3.3+ - `mbstrdecoder `__ - `pathvalidate `__ - `six `__ +- `toml `__ - `XlsxWriter `__ - `xlwt `__ diff --git a/docs/pages/introduction/feature.txt b/docs/pages/introduction/feature.txt index 53096d2f..4b1fe56f 100644 --- a/docs/pages/introduction/feature.txt +++ b/docs/pages/introduction/feature.txt @@ -3,9 +3,7 @@ Features - Write a table in various formats: - CSV - - Microsoft Excel :superscript:`TM` - - `.xlsx` format - - `.xls` format + - Microsoft Excel :superscript:`TM` (``.xlsx``/``.xls`` file format) - HTML - JSON - `Labeled Tab-separated Values (LTSV) `__ @@ -15,14 +13,12 @@ Features - `Pandas `__ (Definition of a DataFrame variable) - Python code (Definition of a nested list variable) - JavaScript (Definition of a nested list variable) - - reStructuredText - - Grid tables - - Simple tables - - CSV table + - reStructuredText: `Grid Tables `__/`Simple Tables `__/`CSV Table `__ - Tab-separated values (TSV) + - `TOML `__ - Automatic tabular data formatting - Alignment - Padding - Decimal places of numbers - Multibyte character support -- Output table to a stream such as a file or the standard output +- Write table to a stream such as a file/standard-output/string-buffer diff --git a/docs/pages/introduction/summary.txt b/docs/pages/introduction/summary.txt index 92a4137b..3f90ec9f 100644 --- a/docs/pages/introduction/summary.txt +++ b/docs/pages/introduction/summary.txt @@ -1 +1 @@ -pytablewriter is a python library to write a table in various formats: CSV/HTML/JavaScript/JSON/LTSV/Markdown/MediaWiki/Excel/Pandas/Python/reStructuredText/TSV +pytablewriter is a python library to write a table in various formats: CSV/HTML/JavaScript/JSON/LTSV/Markdown/MediaWiki/Excel/Pandas/Python/reStructuredText/TOML/TSV. diff --git a/docs/pages/reference/writer.rst b/docs/pages/reference/writer.rst index ed680af4..d3f01b1f 100644 --- a/docs/pages/reference/writer.rst +++ b/docs/pages/reference/writer.rst @@ -94,3 +94,13 @@ reStructuredText simple table writer class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autoclass:: pytablewriter.RstSimpleTableWriter :inherited-members: + + +TOML table writer class +------------------------------- +.. autoclass:: pytablewriter.TomlTableWriter + :inherited-members: + +Base table writer class +------------------------------- +.. autoclass:: pytablewriter.writer._table_writer.AbstractTableWriter diff --git a/examples/ipynb/pytablewriter_examples.ipynb b/examples/ipynb/pytablewriter_examples.ipynb index 49a37403..8121c69b 100644 --- a/examples/ipynb/pytablewriter_examples.ipynb +++ b/examples/ipynb/pytablewriter_examples.ipynb @@ -68,10 +68,9 @@ } ], "source": [ - "writer = pytablewriter.CsvTableWriter()\n", + "writer = pytablewriter.TsvTableWriter()\n", "writer.header_list = header_list\n", "writer.value_matrix = data\n", - "writer.column_delimiter = \"\\t\"\n", "\n", "writer.write_table()" ] @@ -189,35 +188,35 @@ "text": [ "[\n", "{\n", - " \"bool\": true,\n", - " \"float\": 0.1,\n", - " \"int\": 0,\n", - " \"mix\": 0,\n", - " \"str\": \"hoge\",\n", + " \"bool\": true, \n", + " \"float\": 0.1, \n", + " \"int\": 0, \n", + " \"mix\": 0, \n", + " \"str\": \"hoge\", \n", " \"time\": \"2017-01-01 03:04:05+0900\"\n", "},\n", "{\n", - " \"bool\": false,\n", - " \"float\": -2.23,\n", - " \"int\": 2,\n", - " \"mix\": null,\n", - " \"str\": \"foo\",\n", + " \"bool\": false, \n", + " \"float\": -2.23, \n", + " \"int\": 2, \n", + " \"mix\": null, \n", + " \"str\": \"foo\", \n", " \"time\": \"2017-12-23 12:34:51+0900\"\n", "},\n", "{\n", - " \"bool\": true,\n", - " \"float\": 0,\n", - " \"int\": 3,\n", - " \"mix\": \"Infinity\",\n", - " \"str\": \"bar\",\n", + " \"bool\": true, \n", + " \"float\": 0, \n", + " \"int\": 3, \n", + " \"mix\": \"Infinity\", \n", + " \"str\": \"bar\", \n", " \"time\": \"2017-03-03 22:44:55+0900\"\n", "},\n", "{\n", - " \"bool\": false,\n", - " \"float\": -9.9,\n", - " \"int\": -10,\n", - " \"mix\": \"NaN\",\n", - " \"str\": \"\",\n", + " \"bool\": false, \n", + " \"float\": -9.9, \n", + " \"int\": -10, \n", + " \"mix\": \"NaN\", \n", + " \"str\": \"\", \n", " \"time\": \"2017-01-01 00:00:00+0900\"\n", "}]\n" ] @@ -245,35 +244,35 @@ "text": [ "{ \"example_table\" : [\n", "{\n", - " \"bool\": true,\n", - " \"float\": 0.1,\n", - " \"int\": 0,\n", - " \"mix\": 0,\n", - " \"str\": \"hoge\",\n", + " \"bool\": true, \n", + " \"float\": 0.1, \n", + " \"int\": 0, \n", + " \"mix\": 0, \n", + " \"str\": \"hoge\", \n", " \"time\": \"2017-01-01 03:04:05+0900\"\n", "},\n", "{\n", - " \"bool\": false,\n", - " \"float\": -2.23,\n", - " \"int\": 2,\n", - " \"mix\": null,\n", - " \"str\": \"foo\",\n", + " \"bool\": false, \n", + " \"float\": -2.23, \n", + " \"int\": 2, \n", + " \"mix\": null, \n", + " \"str\": \"foo\", \n", " \"time\": \"2017-12-23 12:34:51+0900\"\n", "},\n", "{\n", - " \"bool\": true,\n", - " \"float\": 0,\n", - " \"int\": 3,\n", - " \"mix\": \"Infinity\",\n", - " \"str\": \"bar\",\n", + " \"bool\": true, \n", + " \"float\": 0, \n", + " \"int\": 3, \n", + " \"mix\": \"Infinity\", \n", + " \"str\": \"bar\", \n", " \"time\": \"2017-03-03 22:44:55+0900\"\n", "},\n", "{\n", - " \"bool\": false,\n", - " \"float\": -9.9,\n", - " \"int\": -10,\n", - " \"mix\": \"NaN\",\n", - " \"str\": \"\",\n", + " \"bool\": false, \n", + " \"float\": -9.9, \n", + " \"int\": -10, \n", + " \"mix\": \"NaN\", \n", + " \"str\": \"\", \n", " \"time\": \"2017-01-01 00:00:00+0900\"\n", "}]}\n" ] @@ -614,25 +613,60 @@ "\n", "writer.write_table()" ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "int:0\tfloat:0.10\tstr:\"hoge\"\tbool:True\tmix:0\ttime:\"2017-01-01 03:04:05+0900\"\n", + "int:2\tfloat:-2.23\tstr:\"foo\"\tbool:False\ttime:\"2017-12-23 12:34:51+0900\"\n", + "int:3\tfloat:0.00\tstr:\"bar\"\tbool:True\tmix:Infinity\ttime:\"2017-03-03 22:44:55+0900\"\n", + "int:-10\tfloat:-9.90\tstr:\"\"\tbool:False\tmix:NaN\ttime:\"2017-01-01 00:00:00+0900\"\n" + ] + } + ], + "source": [ + "writer = pytablewriter.LtsvTableWriter()\n", + "writer.header_list = header_list\n", + "writer.value_matrix = data\n", + "\n", + "writer.write_table()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 2", "language": "python", - "name": "python3" + "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 3 + "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" + "pygments_lexer": "ipython2", + "version": "2.7.12" } }, "nbformat": 4, diff --git a/pytablewriter/__init__.py b/pytablewriter/__init__.py index 3ae1cb2c..dc40f950 100644 --- a/pytablewriter/__init__.py +++ b/pytablewriter/__init__.py @@ -35,6 +35,7 @@ RstSimpleTableWriter ) from .writer._tsv_writer import TsvTableWriter +from .writer._toml_writer import TomlTableWriter from ._factory import TableWriterFactory from ._function import dump_tabledata diff --git a/pytablewriter/_converter.py b/pytablewriter/_converter.py index ce64a34b..1e33f051 100644 --- a/pytablewriter/_converter.py +++ b/pytablewriter/_converter.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import re diff --git a/pytablewriter/_factory.py b/pytablewriter/_factory.py index e425896b..bdcfa23c 100644 --- a/pytablewriter/_factory.py +++ b/pytablewriter/_factory.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import copy import os @@ -13,8 +14,10 @@ from ._error import WriterNotFoundError from .writer._csv_writer import CsvTableWriter -from .writer._excel_writer import ExcelXlsxTableWriter -from .writer._excel_writer import ExcelXlsTableWriter +from .writer._excel_writer import ( + ExcelXlsxTableWriter, + ExcelXlsTableWriter +) from .writer._html_writer import HtmlTableWriter from .writer._javascript_writer import JavaScriptTableWriter from .writer._json_writer import JsonTableWriter @@ -30,6 +33,7 @@ RstSimpleTableWriter ) from .writer._tsv_writer import TsvTableWriter +from .writer._toml_writer import TomlTableWriter class TableWriterFactory(object): @@ -44,6 +48,7 @@ class TableWriterFactory(object): "py": PythonCodeTableWriter, "rst": RstGridTableWriter, "tsv": TsvTableWriter, + "toml": TomlTableWriter, } @classmethod @@ -67,6 +72,7 @@ def create_from_file_extension(cls, file_extension): ``"xls"`` :py:class:`~.ExcelXlsTableWriter` ``"xlsx"`` :py:class:`~.ExcelXlsxTableWriter` ``"tsv"`` :py:class:`~.TsvTableWriter` + ``"toml"`` :py:class:`~.TomlTableWriter` ================== =================================== :param str file_extension: @@ -120,6 +126,7 @@ def create_from_format_name(cls, format_name): ``"rst_simple_table"`` :py:class:`~.RstSimpleTableWriter` ``"rst_csv_table"`` :py:class:`~.RstCsvTableWriter` ``"tsv"`` :py:class:`~.TsvTableWriter` + ``"toml"`` :py:class:`~.TomlTableWriter` ============================== =================================== :param str format_name: Format name string (case insensitive). @@ -167,6 +174,7 @@ def get_format_name_list(cls): javascript js json + ltsv markdown mediawiki null @@ -177,6 +185,8 @@ def get_format_name_list(cls): rst_csv_table rst_grid_table rst_simple_table + toml + tsv """ return sorted(cls.__get_format_name_writer_mapping()) @@ -198,9 +208,12 @@ def get_extension_list(cls): html js json + ltsv md py rst + toml + tsv xls xlsx """ diff --git a/pytablewriter/_function.py b/pytablewriter/_function.py index 6090d8b3..a85c86b2 100644 --- a/pytablewriter/_function.py +++ b/pytablewriter/_function.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals from decimal import Decimal import dataproperty diff --git a/pytablewriter/writer/_csv_writer.py b/pytablewriter/writer/_csv_writer.py index 10461af0..38f738ab 100644 --- a/pytablewriter/writer/_csv_writer.py +++ b/pytablewriter/writer/_csv_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import dataproperty as dp @@ -13,8 +14,8 @@ class CsvTableWriter(TextTableWriter): """ - Concrete class of a table writer for character separated values format. - Default separated character is comma (``","``). + A table writer class for character separated values format. + The default separated character is a comma (``","``). :Examples: @@ -28,8 +29,8 @@ def support_split_write(self): def __init__(self): super(CsvTableWriter, self).__init__() - self.indent_string = u"" - self.column_delimiter = u"," + self.indent_string = "" + self.column_delimiter = "," self.is_padding = False self.is_write_header_separator_row = False diff --git a/pytablewriter/writer/_excel_workbook.py b/pytablewriter/writer/_excel_workbook.py index 57e2a8db..6fe2abeb 100644 --- a/pytablewriter/writer/_excel_workbook.py +++ b/pytablewriter/writer/_excel_workbook.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import abc import dataproperty as dp diff --git a/pytablewriter/writer/_excel_writer.py b/pytablewriter/writer/_excel_writer.py index 3984337f..f34d4755 100644 --- a/pytablewriter/writer/_excel_writer.py +++ b/pytablewriter/writer/_excel_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import abc import dataproperty as dp @@ -18,12 +19,12 @@ ExcelWorkbookXlsx ) from ._interface import TextWriterInterface -from ._table_writer import TableWriter +from ._table_writer import AbstractTableWriter -class ExcelTableWriter(TableWriter, TextWriterInterface): +class ExcelTableWriter(AbstractTableWriter, TextWriterInterface): """ - Abstract class of a table writer for Excel format. + Abstract class of a table writer for Excel file format. """ @property @@ -226,7 +227,7 @@ def _postprocess(self): class ExcelXlsTableWriter(ExcelTableWriter): """ - Concrete class of a table writer for Excel format + A table writer class for Excel file format: ``.xls`` (older or equal to Office 2003). """ @@ -295,7 +296,7 @@ def __get_cell_style(self, col): class ExcelXlsxTableWriter(ExcelTableWriter): """ - Concrete class of a table writer for Excel format + A table writer class for Excel file format: ``.xlsx`` (newer or equal to Office 2007). :Examples: diff --git a/pytablewriter/writer/_html_writer.py b/pytablewriter/writer/_html_writer.py index d8949ee0..da585b92 100644 --- a/pytablewriter/writer/_html_writer.py +++ b/pytablewriter/writer/_html_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import dataproperty as dp import dominate.tags as tags @@ -18,7 +19,7 @@ class HtmlTableWriter(TextTableWriter): """ - Concrete class of a table writer for HTML format. + A table writer class for HTML format. :Examples: @@ -36,8 +37,8 @@ def __init__(self): self.is_quote_header = False self.indent_string = u" " self.is_quote_header = False - self.is_quote_table[dp.Typecode.STRING] = False - self.is_quote_table[dp.Typecode.DATETIME] = False + self.quote_flag_table[dp.Typecode.STRING] = False + self.quote_flag_table[dp.Typecode.DATETIME] = False self._table_tag = None diff --git a/pytablewriter/writer/_javascript_writer.py b/pytablewriter/writer/_javascript_writer.py index 37960e4a..72603e1b 100644 --- a/pytablewriter/writer/_javascript_writer.py +++ b/pytablewriter/writer/_javascript_writer.py @@ -5,12 +5,11 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals -import dataproperty as dp import six from .._converter import lower_bool_converter -from .._error import EmptyTableNameError from .._function import str_datetime_converter from ._text_writer import SourceCodeTableWriter @@ -21,7 +20,7 @@ def js_datetime_converter(value): class JavaScriptTableWriter(SourceCodeTableWriter): """ - Concrete class of a table writer for JavaScript format. + A table writer for class JavaScript format. :Examples: @@ -71,11 +70,11 @@ def write_table(self): self.inc_indent_level() super(JavaScriptTableWriter, self).write_table() self.dec_indent_level() - data_frame_text = self.stream.getvalue().rstrip(u"\n") + data_frame_text = self.stream.getvalue().rstrip("\n") if self.is_write_closing_row: data_frame_line_list = data_frame_text.splitlines() - data_frame_line_list[-2] = data_frame_line_list[-2].rstrip(u",") - data_frame_text = u"\n".join(data_frame_line_list) + data_frame_line_list[-2] = data_frame_line_list[-2].rstrip(",") + data_frame_text = "\n".join(data_frame_line_list) self.stream.close() self.stream = org_stream @@ -84,12 +83,8 @@ def write_table(self): self._write_line(data_frame_text) self.inc_indent_level() - def _verify_table_name(self): - if dp.is_empty_string(self.table_name): - raise EmptyTableNameError() - def _get_opening_row_item_list(self): - return u"var {:s} = [".format(self.variable_name) + return "var {:s} = [".format(self.variable_name) def _get_closing_row_item_list(self): - return u"];" + return "];" diff --git a/pytablewriter/writer/_json_writer.py b/pytablewriter/writer/_json_writer.py index a8636248..ad49943b 100644 --- a/pytablewriter/writer/_json_writer.py +++ b/pytablewriter/writer/_json_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import json import dataproperty as dp @@ -21,7 +22,7 @@ class JsonTableWriter(IndentationTextTableWriter): """ - Concrete class of a table writer for JSON format. + A table writer class for JSON format. :Examples: @@ -37,7 +38,7 @@ def __init__(self): self.is_write_opening_row = True self.is_write_closing_row = True - self.char_right_side_row = u"," + self.char_right_side_row = "," self._prop_extractor.none_value = "null" self._prop_extractor.inf_value = "Infinity" @@ -46,7 +47,7 @@ def __init__(self): def write_null_line(self): self._verify_stream() - self.stream.write(u"\n") + self.stream.write("\n") def write_table(self): """ @@ -76,7 +77,7 @@ def write_table(self): json_text = strip_quote(json_text, "false") json_text_list.append(json_text) - joint_text = self.char_right_side_row + u"\n" + joint_text = self.char_right_side_row + "\n" json_text = joint_text.join(json_text_list) if all([ not self.is_write_closing_row, @@ -117,13 +118,13 @@ def _preprocess_value_matrix(self): def _get_opening_row_item_list(self): if dp.is_not_empty_string(self.table_name): - return u'{{ "{:s}" : ['.format( + return '{{ "{:s}" : ['.format( MultiByteStrDecoder(self.table_name).unicode_str) - return u"[" + return "[" def _get_closing_row_item_list(self): if dp.is_not_empty_string(self.table_name): - return u"]}" + return "]}" - return u"]" + return "]" diff --git a/pytablewriter/writer/_ltsv_writer.py b/pytablewriter/writer/_ltsv_writer.py index 55d622a6..139026d5 100644 --- a/pytablewriter/writer/_ltsv_writer.py +++ b/pytablewriter/writer/_ltsv_writer.py @@ -16,7 +16,7 @@ class LtsvTableWriter(CsvTableWriter): """ - Concrete class of a table writer for + A table writer class for `Labeled Tab-separated Values (LTSV) `__ format. :Examples: @@ -37,6 +37,7 @@ def write_table(self): """ |write_table| with `Labeled Tab-separated Values (LTSV) `__ format. + Invalid characters in labels/data will be removed. :raises pytablewriter.EmptyHeaderError: If the |header_list| is empty. """ diff --git a/pytablewriter/writer/_md_writer.py b/pytablewriter/writer/_md_writer.py index 83fb8e90..b8be3792 100644 --- a/pytablewriter/writer/_md_writer.py +++ b/pytablewriter/writer/_md_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import dataproperty as dp from mbstrdecoder import MultiByteStrDecoder @@ -14,7 +15,7 @@ class MarkdownTableWriter(IndentationTextTableWriter): """ - Concrete class of a table writer for Markdown format. + Atable writer class for Markdown format. :Examples: @@ -28,13 +29,13 @@ def support_split_write(self): def __init__(self): super(MarkdownTableWriter, self).__init__() - self.indent_string = u"" - self.column_delimiter = u"|" - self.char_cross_point = u"|" + self.indent_string = "" + self.column_delimiter = "|" + self.char_cross_point = "|" self.is_write_opening_row = True self.is_quote_header = False - self.is_quote_table[dp.Typecode.STRING] = False - self.is_quote_table[dp.Typecode.DATETIME] = False + self.quote_flag_table[dp.Typecode.STRING] = False + self.quote_flag_table[dp.Typecode.DATETIME] = False self._is_remove_line_break = True @@ -61,7 +62,7 @@ def _get_opening_row_item_list(self): return [] return [ - u"#" * (self._indent_level + 1) + u" " + + "#" * (self._indent_level + 1) + " " + MultiByteStrDecoder(self.table_name).unicode_str ] @@ -71,11 +72,11 @@ def _get_header_row_separator_item_list(self): padding_len = self._get_padding_len(col_prop) if col_prop.align == dp.Align.RIGHT: - separator_item = u"-" * (padding_len - 1) + u":" + separator_item = "-" * (padding_len - 1) + ":" elif col_prop.align == dp.Align.CENTER: - separator_item = u":" + u"-" * (padding_len - 2) + u":" + separator_item = ":" + "-" * (padding_len - 2) + ":" else: - separator_item = u"-" * padding_len + separator_item = "-" * padding_len header_separator_list.append(separator_item) diff --git a/pytablewriter/writer/_mediawiki_writer.py b/pytablewriter/writer/_mediawiki_writer.py index 9eff114e..d748a8d7 100644 --- a/pytablewriter/writer/_mediawiki_writer.py +++ b/pytablewriter/writer/_mediawiki_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import re import dataproperty as dp @@ -16,14 +17,14 @@ class MediaWikiTableWriter(TextTableWriter): """ - Concrete class of a table writer for MediaWiki format. + A table writer class for MediaWiki format. :Examples: :ref:`example-mediawiki-table-writer` """ - __RE_TABLE_SEQUENCE = re.compile(u"^[\s]+[*|#]+") + __RE_TABLE_SEQUENCE = re.compile("^[\s]+[*|#]+") @property def support_split_write(self): @@ -32,7 +33,7 @@ def support_split_write(self): def __init__(self): super(MediaWikiTableWriter, self).__init__() - self.column_delimiter = u"\n" + self.column_delimiter = "\n" self.is_padding = False self.is_write_header_separator_row = True @@ -40,7 +41,7 @@ def __init__(self): self.is_write_opening_row = True self.is_write_closing_row = True self.is_quote_header = False - self.is_quote_table = {} + self.quote_flag_table = {} def _write_header(self): if not self.is_write_header: @@ -48,7 +49,7 @@ def _write_header(self): if dp.is_not_empty_string(self.table_name): self._write_line( - u"|+" + MultiByteStrDecoder(self.table_name).unicode_str) + "|+" + MultiByteStrDecoder(self.table_name).unicode_str) super(MediaWikiTableWriter, self)._write_header() @@ -59,30 +60,30 @@ def _write_value_row(self, value_list, value_prop_list): ]) def _get_opening_row_item_list(self): - return u'{| class="wikitable"' + return '{| class="wikitable"' def _get_header_row_separator_item_list(self): - return [u"|-"] + return ["|-"] def _get_value_row_separator_item_list(self): return self._get_header_row_separator_item_list() def _get_closing_row_item_list(self): - return u"|}" + return "|}" def _get_header_format_string(self, col_prop, value_prop): - return u"! {{:{:s}{:s}}}".format( + return "! {{:{:s}{:s}}}".format( self._get_center_align_formatformat(), str(self._get_padding_len(col_prop, value_prop))) def __modify_table_element(self, value, value_prop): if value_prop.align is dp.Align.LEFT: - forma_stirng = u'| {1:s}' + forma_stirng = '| {1:s}' else: - forma_stirng = u'| style="text-align:{0:s}"| {1:s}' + forma_stirng = '| style="text-align:{0:s}"| {1:s}' if self.__RE_TABLE_SEQUENCE.search(value) is not None: - value = u"\n" + value.lstrip() + value = "\n" + value.lstrip() return forma_stirng.format( value_prop.align.align_string, value) diff --git a/pytablewriter/writer/_pandas_writer.py b/pytablewriter/writer/_pandas_writer.py index c9fe1fcb..af25abfa 100644 --- a/pytablewriter/writer/_pandas_writer.py +++ b/pytablewriter/writer/_pandas_writer.py @@ -20,7 +20,7 @@ class PandasDataFrameWriter(SourceCodeTableWriter): """ - Concrete class of a writer for Pandas DataFrame format. + A writer class for Pandas DataFrame format. :Examples: @@ -42,8 +42,12 @@ def __init__(self): def write_table(self): """ - |write_table| with Pandas DataFrame variable definition format. + |write_table| with Pandas DataFrame format. + The tabular data will be written as ``pandas.DataFrame`` class + variable definition. + :raises pytablewriter.EmptyTableNameError: + If the |table_name| is empty. :raises pytablewriter.EmptyHeaderError: If the |header_list| is empty. .. note:: diff --git a/pytablewriter/writer/_python_code_writer.py b/pytablewriter/writer/_python_code_writer.py index 85a1bf5f..409bfb37 100644 --- a/pytablewriter/writer/_python_code_writer.py +++ b/pytablewriter/writer/_python_code_writer.py @@ -18,7 +18,7 @@ class PythonCodeTableWriter(SourceCodeTableWriter): """ - Concrete class of a table writer for Python code (nested list) format. + A table writer class for Python source code format. :Examples: @@ -38,8 +38,12 @@ def __init__(self): def write_table(self): """ - |write_table| with Python nested list variable definition format. + |write_table| with Python format. + The tabular data will be written as nested list variable definition + for Python format. + :raises pytablewriter.EmptyTableNameError: + If the |table_name| is empty. :raises pytablewriter.EmptyTableDataError: If the |header_list| and the |value_matrix| is empty. diff --git a/pytablewriter/writer/_rst_writer.py b/pytablewriter/writer/_rst_writer.py index d04d23d6..83eb90a3 100644 --- a/pytablewriter/writer/_rst_writer.py +++ b/pytablewriter/writer/_rst_writer.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import dataproperty as dp from mbstrdecoder import MultiByteStrDecoder @@ -20,28 +21,28 @@ class RstTableWriter(IndentationTextTableWriter): def __init__(self): super(RstTableWriter, self).__init__() - self.char_header_row_separator = u"=" - self.char_cross_point = u"+" - self.indent_string = u" " + self.char_header_row_separator = "=" + self.char_cross_point = "+" + self.indent_string = " " self.is_write_header_separator_row = True self.is_write_value_separator_row = True self.is_write_opening_row = True self.is_write_closing_row = True self.is_quote_header = False - self.is_quote_table[dp.Typecode.STRING] = False - self.is_quote_table[dp.Typecode.DATETIME] = False + self.quote_flag_table[dp.Typecode.STRING] = False + self.quote_flag_table[dp.Typecode.DATETIME] = False self._is_remove_line_break = True - self.table_name = u"" + self.table_name = "" def _write_table(self): self._verify_property() if dp.is_empty_string(self.table_name): - self._write_line(u".. table:: ") + self._write_line(".. table:: ") else: - self._write_line(u".. table:: {}".format( + self._write_line(".. table:: {}".format( MultiByteStrDecoder(self.table_name).unicode_str)) self._write_line() @@ -52,7 +53,7 @@ def _write_table(self): class RstCsvTableWriter(RstTableWriter): """ - Concrete class of a table writer for reStructuredText + A table class writer for reStructuredText `CSV table `__ format. @@ -68,14 +69,14 @@ def support_split_write(self): def __init__(self): super(RstCsvTableWriter, self).__init__() - self.column_delimiter = u", " - self.char_cross_point = u"" + self.column_delimiter = ", " + self.char_cross_point = "" self.is_padding = False self.is_write_header_separator_row = False self.is_write_value_separator_row = False self.is_write_closing_row = False - self.is_quote_table[dp.Typecode.STRING] = True - self.is_quote_table[dp.Typecode.DATETIME] = True + self.quote_flag_table[dp.Typecode.STRING] = True + self.quote_flag_table[dp.Typecode.DATETIME] = True def write_table(self): """ @@ -97,7 +98,7 @@ def write_table(self): self.dec_indent_level() def _get_opening_row_item_list(self): - directive = u".. csv-table:: " + directive = ".. csv-table:: " if dp.is_empty_string(self.table_name): return [directive] @@ -106,7 +107,7 @@ def _get_opening_row_item_list(self): def _write_opening_row(self): self.dec_indent_level() - super(RstTableWriter, self)._write_opening_row() + super(RstCsvTableWriter, self)._write_opening_row() self.inc_indent_level() def _write_header(self): @@ -121,7 +122,7 @@ def _write_header(self): ])) ) - self._write_line(u":widths: " + u", ".join([ + self._write_line(":widths: " + ", ".join([ str(col_prop.padding_len) for col_prop in self._column_prop_list ])) @@ -136,8 +137,8 @@ def _get_closing_row_item_list(self): class RstGridTableWriter(RstTableWriter): """ - Concrete class of a table writer for reStructuredText - `grid tables `__ + A table writer class for reStructuredText + `Grid Tables `__ format. :Examples: @@ -152,8 +153,8 @@ def support_split_write(self): def __init__(self): super(RstGridTableWriter, self).__init__() - self.char_left_side_row = u"|" - self.char_right_side_row = u"|" + self.char_left_side_row = "|" + self.char_right_side_row = "|" def write_table(self): """ @@ -169,8 +170,8 @@ def write_table(self): class RstSimpleTableWriter(RstTableWriter): """ - Concrete class of a table writer for reStructuredText - `simple tables `__ + A table writer class for reStructuredText + `Simple Tables `__ format. :Examples: @@ -185,11 +186,11 @@ def support_split_write(self): def __init__(self): super(RstSimpleTableWriter, self).__init__() - self.column_delimiter = u" " - self.char_cross_point = u" " + self.column_delimiter = " " + self.char_cross_point = " " - self.char_opening_row = u"=" - self.char_closing_row = u"=" + self.char_opening_row = "=" + self.char_closing_row = "=" self.is_write_value_separator_row = False diff --git a/pytablewriter/writer/_table_writer.py b/pytablewriter/writer/_table_writer.py index 7e34b0a5..5f73b945 100644 --- a/pytablewriter/writer/_table_writer.py +++ b/pytablewriter/writer/_table_writer.py @@ -5,17 +5,20 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import re import sys import dataproperty as dp from dataproperty import Typecode from mbstrdecoder import MultiByteStrDecoder +import pytablereader as ptr from six.moves import zip from .._error import ( NotSupportedError, EmptyValueError, + EmptyTableNameError, EmptyHeaderError, EmptyTableDataError ) @@ -26,13 +29,16 @@ def default_bool_converter(value): return str(value) -class TableWriter(TableWriterInterface): +class AbstractTableWriter(TableWriterInterface): """ - Abstract class of table writer. + Base abstract class of table writer classes. .. py:attribute:: stream Stream to write tables. + You can use arbitrary stream which supported ``write`` method + such as ``sys.stdout``, file stream, ``StringIO``, etc. + Defaults to ``sys.stdout``. .. py:attribute:: table_name @@ -56,13 +62,28 @@ class TableWriter(TableWriterInterface): .. py:attribute:: is_quote_header - Add double quote to string in the header if the value is |True|. + Add double quote to the headers if the value is |True|. - .. py:attribute:: is_quote_table + .. py:attribute:: quote_flag_table - Add double quote to string in a table elements, - where |Typecode| of table-value is |True| in the mapping table - (dictionary): ``{ Typecode : bool }``. + Add double quote to strings in table elements, + where |Typecode| of table-value is |True| in the ``quote_flag_table`` + mapping table. ``quote_flag_table`` should be a dictionary. + And is ``{ Typecode : bool }``. Defaults to + + .. code-block:: json + :caption: quote_flag_table default value + + { + Typecode.NONE: False, + Typecode.INTEGER: False, + Typecode.FLOAT: False, + Typecode.STRING: True, + Typecode.DATETIME: True, + Typecode.FLOAT: False, + Typecode.NAN: False, + Typecode.BOOL: False, + } .. py:attribute:: iteration_length @@ -100,6 +121,11 @@ def value_matrix(self, value_matrix): self._preprocessed_property = False self._preprocessed_value_matrix = False + @property + def tabledata(self): + return ptr.TableData( + self.table_name, self.header_list, self.value_matrix) + def __init__(self): self.stream = sys.stdout self.table_name = None @@ -109,7 +135,7 @@ def __init__(self): self.is_write_header = True self.is_padding = True self.is_quote_header = True - self.is_quote_table = { + self.quote_flag_table = { Typecode.NONE: False, Typecode.INTEGER: False, Typecode.FLOAT: False, @@ -137,6 +163,7 @@ def __init__(self): self._prop_extractor.datetime_format_str = "%Y-%m-%d %H:%M:%S%z" self._prop_extractor.bool_converter = default_bool_converter + self._is_required_table_name = False self._is_remove_line_break = False self._preprocessed_property = False @@ -179,7 +206,7 @@ def from_tabledata(self, tabledata): self.table_name = tabledata.table_name self.header_list = tabledata.header_list - self.value_matrix = tabledata.record_list + self.value_matrix = tabledata.value_matrix def from_csv(self, csv_source): """ @@ -202,8 +229,6 @@ def from_csv(self, csv_source): :ref:`example-from-csv` """ - import pytablereader as ptr - loader = ptr.CsvTableTextLoader(csv_source) try: for tabledata in loader.load(): @@ -335,13 +360,13 @@ def _get_padding_len(self, column_property, value_prop=None): return 0 def _get_left_align_formatformat(self): - return u"<" + return "<" def _get_right_align_formatformat(self): - return u">" + return ">" def _get_center_align_formatformat(self): - return u"^" + return "^" def _get_row_item(self, col_prop, value_prop): to_string_format_str = self.__get_to_string_format( @@ -351,8 +376,8 @@ def _get_row_item(self, col_prop, value_prop): item = to_string_format_str.format(value_prop.data) else: try: - value = col_prop.type_factory( - value_prop.data, is_strict=False).create_type_converter().convert() + value = col_prop.type_class( + value_prop.data, is_strict=False).convert() except dp.TypeConversionError: value = value_prop.data @@ -365,9 +390,9 @@ def _get_row_item(self, col_prop, value_prop): item = self.__get_align_format(col_prop, value_prop).format(item) if all([ - self.is_quote_table.get(col_prop.typecode, False), + self.quote_flag_table.get(col_prop.typecode, False), any([ - self.is_quote_table.get(value_prop.typecode, False), + self.quote_flag_table.get(value_prop.typecode, False), value_prop.typecode in [Typecode.INTEGER, Typecode.FLOAT], ]) ]): @@ -384,16 +409,16 @@ def __get_to_string_format(self, col_prop, value_prop): ]), value_prop.typecode == Typecode.NONE, ]): - format_str = u"" + format_str = "" else: format_str = col_prop.format_str try: format_str.format(value_prop.data) except ValueError: - format_str = u"" + format_str = "" - return u"{:" + format_str + u"}" + return "{:" + format_str + "}" def __get_align_format(self, col_prop, value_prop): align_func_table = { @@ -405,13 +430,13 @@ def __get_align_format(self, col_prop, value_prop): align = align_func_table[col_prop.align]() - format_list = [u"{:" + align] + format_list = ["{:" + align] col_padding_len = self._get_padding_len(col_prop, value_prop) if col_padding_len > 0: format_list.append(str(col_padding_len)) - format_list.append(u"s}") + format_list.append("s}") - return u"".join(format_list) + return "".join(format_list) def _verify_property(self): self._verify_table_name() @@ -430,7 +455,12 @@ def _verify_property(self): pass def _verify_table_name(self): - pass + if all([ + self._is_required_table_name, + dp.is_empty_string(self.table_name), + ]): + raise EmptyTableNameError( + "table_name must be string, with at least one character or more length.") def _verify_stream(self): if self.stream is None: @@ -465,7 +495,7 @@ def _preprocess_property(self): self._prop_extractor.header_list = self.header_list self._prop_extractor.data_matrix = self.__value_matrix_org - self._column_prop_list = self._prop_extractor.extract_column_property_list() + self._column_prop_list = self._prop_extractor.extract_col_property_list() try: self._value_prop_matrix = self._prop_extractor.extract_data_property_matrix() except TypeError: diff --git a/pytablewriter/writer/_text_writer.py b/pytablewriter/writer/_text_writer.py index 1d11c2db..e7080bf2 100644 --- a/pytablewriter/writer/_text_writer.py +++ b/pytablewriter/writer/_text_writer.py @@ -5,19 +5,20 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import dataproperty as dp from six.moves import zip from .._error import EmptyHeaderError -from ._table_writer import TableWriter +from ._table_writer import AbstractTableWriter from ._interface import ( IndentationInterface, TextWriterInterface ) -class TextTableWriter(TableWriter, TextWriterInterface): +class TextTableWriter(AbstractTableWriter, TextWriterInterface): """ Base class of table writer with text format. @@ -80,15 +81,15 @@ class TextTableWriter(TableWriter, TextWriterInterface): def __init__(self): super(TextTableWriter, self).__init__() - self.column_delimiter = u"|" - self.char_left_side_row = u"" - self.char_right_side_row = u"" - self.char_cross_point = u"" + self.column_delimiter = "|" + self.char_left_side_row = "" + self.char_right_side_row = "" + self.char_cross_point = "" - self.char_opening_row = u"-" - self.char_header_row_separator = u"-" - self.char_value_row_separator = u"-" - self.char_closing_row = u"-" + self.char_opening_row = "-" + self.char_header_row_separator = "-" + self.char_value_row_separator = "-" + self.char_closing_row = "-" def write_null_line(self): """ @@ -158,7 +159,7 @@ def _get_header_item(self, col_prop, value_prop): return item def _get_header_format_string(self, col_prop, value_prop): - return u"{{:{:s}{:s}}}".format( + return "{{:{:s}{:s}}}".format( self._get_center_align_formatformat(), str(self._get_padding_len(col_prop, value_prop))) @@ -167,13 +168,13 @@ def _write_raw_string(self, unicode_text): self.stream.write(unicode_text) - def _write_raw_line(self, unicode_text=u""): - self._write_raw_string(unicode_text + u"\n") + def _write_raw_line(self, unicode_text=""): + self._write_raw_string(unicode_text + "\n") def _write(self, text): self._write_raw_string(text) - def _write_line(self, text=u""): + def _write_line(self, text=""): self._write_raw_line(text) def _write_row(self, value_list): @@ -200,7 +201,7 @@ def _write_header(self): self._get_header_item( col_prop, dp.DataProperty( - header, is_strict_type_mapping=dp.STRICT_TYPE_MAPPING) + header, strict_type_mapping=dp.STRICT_TYPE_MAPPING) ) for col_prop, header in zip(self._column_prop_list, self.header_list) @@ -222,9 +223,9 @@ def __write_separator_row(self, value_list): left_cross_point = self.char_cross_point right_cross_point = self.char_cross_point if dp.is_empty_string(self.char_left_side_row): - left_cross_point = u"" + left_cross_point = "" if dp.is_empty_string(self.char_right_side_row): - right_cross_point = u"" + right_cross_point = "" self._write_line( left_cross_point + @@ -274,7 +275,7 @@ def __init__(self): super(IndentationTextTableWriter, self).__init__() self.set_indent_level(0) - self.indent_string = u"" + self.indent_string = "" def set_indent_level(self, indent_level): """ @@ -305,7 +306,7 @@ def _get_indent_string(self): def _write(self, text): self._write_raw_string(self._get_indent_string() + text) - def _write_line(self, text=u""): + def _write_line(self, text=""): self._write_raw_line(self._get_indent_string() + text) @@ -330,29 +331,30 @@ def variable_name(self): import pathvalidate return pathvalidate.sanitize_python_var_name( - self.table_name, u"_").lower() + self.table_name, "_").lower() def __init__(self): super(SourceCodeTableWriter, self).__init__() - self.indent_string = u" " - self.column_delimiter = u", " - self.char_left_side_row = u"[" - self.char_right_side_row = u"]," - self.char_cross_point = u"" + self.indent_string = " " + self.column_delimiter = ", " + self.char_left_side_row = "[" + self.char_right_side_row = "]," + self.char_cross_point = "" self.is_padding = False self.is_write_header_separator_row = False self.is_write_opening_row = True self.is_write_closing_row = True self.is_datetime_instance_formatting = True - self.is_quote_table[dp.Typecode.DATETIME] = False + self.quote_flag_table[dp.Typecode.DATETIME] = False self._prop_extractor.datetime_format_str = "s" + self._is_required_table_name = True self._is_remove_line_break = True self._prop_extractor.none_value = None - self._prop_extractor.is_strict_type_mapping[ + self._prop_extractor.strict_type_mapping[ dp.Typecode.DATETIME] = False def _get_value_row_separator_item_list(self): diff --git a/pytablewriter/writer/_toml_writer.py b/pytablewriter/writer/_toml_writer.py new file mode 100644 index 00000000..af1ee3bd --- /dev/null +++ b/pytablewriter/writer/_toml_writer.py @@ -0,0 +1,51 @@ +# encoding: utf-8 + +""" +.. codeauthor:: Tsuyoshi Hombashi +""" + +from __future__ import absolute_import +from __future__ import unicode_literals + +import toml + +from ._text_writer import TextTableWriter + + +class TomlTableWriter(TextTableWriter): + """ + A table writer class for + `TOML `__ data format. + + :Examples: + + :ref:`example-toml-table-writer` + """ + + @property + def support_split_write(self): + return True + + def __init__(self): + super(TomlTableWriter, self).__init__() + + self._is_required_table_name = True + + def write_table(self): + """ + |write_table| with + `TOML `__ format. + + :raises pytablewriter.EmptyTableNameError: + If the |header_list| is empty. + :raises pytablewriter.EmptyHeaderError: + If the |header_list| is empty. + """ + + self._verify_property() + + # self.stream.write(pytoml.dumps(self.tabledata.as_dict())) + self.stream.write(toml.dumps(self.tabledata.as_dict())) + + def _verify_header(self): + self._validate_empty_header() diff --git a/pytablewriter/writer/_tsv_writer.py b/pytablewriter/writer/_tsv_writer.py index e724dbfc..1ce75ce8 100644 --- a/pytablewriter/writer/_tsv_writer.py +++ b/pytablewriter/writer/_tsv_writer.py @@ -12,7 +12,7 @@ class TsvTableWriter(CsvTableWriter): """ - Concrete class of a table writer for tab separated values (TSV) format. + A table writer class for tab separated values (TSV) format. :Examples: diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 0c8017d2..b531fad1 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,8 +1,9 @@ -DataProperty>=0.13.6 +DataProperty>=0.14.0 dominate>=2.3.0 -mbstrdecoder>=0.1.1 -pathvalidate>=0.10.0 -pytablereader>=0.6.8 +mbstrdecoder>=0.2.0 +pathvalidate>=0.12.1 +pytablereader>=0.8.2 six +toml XlsxWriter xlwt diff --git a/setup.py b/setup.py index 400c6441..3eef865f 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,10 @@ +# encoding: utf-8 + +""" +.. codeauthor:: Tsuyoshi Hombashi +""" + +from __future__ import unicode_literals import io import os.path import setuptools @@ -26,7 +33,7 @@ setuptools.setup( name="pytablewriter", - version="0.15.0", + version="0.16.0", author="Tsuyoshi Hombashi", author_email="gogogo.vm@gmail.com", url="https://github.com/thombashi/pytablewriter", @@ -37,6 +44,7 @@ keywords=[ "table", "CSV", "Excel", "JavaScript", "JSON", "LTSV", "Markdown", "MediaWiki", "HTML", "pandas", "reStructuredText", "TSV", + "TOML", ], long_description=long_description, packages=setuptools.find_packages(exclude=["test*"]), diff --git a/test/data.py b/test/data.py index f85d684e..f814828d 100644 --- a/test/data.py +++ b/test/data.py @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from __future__ import unicode_literals import collections import datetime import itertools diff --git a/test/test_csv_writer.py b/test/test_csv_writer.py index f74154a0..fe4780bb 100644 --- a/test/test_csv_writer.py +++ b/test/test_csv_writer.py @@ -7,6 +7,7 @@ from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals +from decimal import Decimal import io import collections @@ -112,7 +113,7 @@ class Test_CsvTableWriter_from_csv: """ __CSV_EXPECTED = """"a","b","c","dd","e" -1,1.1,"a","1.0","" +1,1.1,"a","1","" 2,2.2,"","2.2","2.2" 3,3.3,"ccc","","cc cc" """ @@ -127,9 +128,9 @@ def test_normal_from_text(self, capsys): assert writer.table_name == "csv1" assert writer.header_list == ["a", "b", "c", "dd", "e"] assert writer.value_matrix == [ - [1, '1.1', 'a', '1.0', ''], - [2, '2.2', '', '2.2', '2.2'], - [3, '3.3', 'ccc', '', 'cc\ncc'] + [1, Decimal('1.1'), 'a', Decimal('1.0'), ''], + [2, Decimal('2.2'), '', Decimal('2.2'), Decimal('2.2')], + [3, Decimal('3.3'), 'ccc', '', 'cc\ncc'] ] print("[expected]\n{}".format(self.__CSV_EXPECTED)) @@ -151,9 +152,9 @@ def test_normal_from_file(self, capsys, tmpdir): assert writer.table_name == "test_data" assert writer.header_list == ["a", "b", "c", "dd", "e"] assert writer.value_matrix == [ - [1, '1.1', 'a', '1.0', ''], - [2, '2.2', '', '2.2', '2.2'], - [3, '3.3', 'ccc', '', 'cc\ncc'] + [1, Decimal('1.1'), 'a', Decimal('1'), ''], + [2, Decimal('2.2'), '', Decimal('2.2'), Decimal('2.2')], + [3, Decimal('3.3'), 'ccc', '', 'cc\ncc'] ] print("[expected]\n{}".format(self.__CSV_EXPECTED)) diff --git a/test/test_excel_writer.py b/test/test_excel_writer.py index 8d1098ba..4a39fba8 100644 --- a/test/test_excel_writer.py +++ b/test/test_excel_writer.py @@ -6,7 +6,9 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import collections +from decimal import Decimal import itertools import pytablewriter as ptw @@ -22,6 +24,10 @@ value_matrix_iter ) + +inf = Decimal('Infinity') +nan = Decimal('NaN') + Data = collections.namedtuple("Data", "table header value expected") normal_test_data_list = [ @@ -45,8 +51,8 @@ expected=TableData( "tablename", ["a", "b", "c", "dd", "e"], - [ - ]) + [] + ) ), Data( table="", @@ -60,16 +66,16 @@ ], [ [ - 1.0, 1.1, 'aa', 1.0, 1.0, 'True', 'Inf', - 'NaN', 1.0, '2017-01-01T00:00:00', + 1, "1.1", 'aa', 1, 1, 'True', inf, + nan, 1, '2017-01-01T00:00:00', ], [ - 2.0, 2.2, 'bbb', 2.2, 2.2, 'False', 'Inf', 'NaN', - 'Inf', '2017-01-02 03:04:05+09:00', + 2, "2.2", 'bbb', "2.2", "2.2", 'False', inf, nan, + inf, '2017-01-02 03:04:05+09:00', ], [ - 3.0, 3.33, 'cccc', -3.0, 'ccc', 'True', 'Inf', - 'NaN', 'NaN', '2017-01-01T00:00:00', + 3, "3.33", 'cccc', -3, 'ccc', 'True', inf, + nan, nan, '2017-01-01T00:00:00', ], ]) ), diff --git a/test/test_html_writer.py b/test/test_html_writer.py index d8c36008..724a2f9e 100644 --- a/test/test_html_writer.py +++ b/test/test_html_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import pytablewriter import pytest diff --git a/test/test_javascript_writer.py b/test/test_javascript_writer.py index e74d98e1..05f432f0 100644 --- a/test/test_javascript_writer.py +++ b/test/test_javascript_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import collections import itertools diff --git a/test/test_json_writer.py b/test/test_json_writer.py index a16f4ac1..0a8286bb 100644 --- a/test/test_json_writer.py +++ b/test/test_json_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import collections import json diff --git a/test/test_md_writer.py b/test/test_md_writer.py index 2bc5f915..29c01948 100644 --- a/test/test_md_writer.py +++ b/test/test_md_writer.py @@ -8,19 +8,22 @@ from __future__ import print_function from __future__ import unicode_literals import collections +from decimal import Decimal import pytablereader as ptr import pytablewriter as ptw import pytest -from .data import header_list -from .data import value_matrix -from .data import value_matrix_with_none -from .data import mix_header_list -from .data import mix_value_matrix -from .data import float_header_list -from .data import float_value_matrix -from .data import value_matrix_iter +from .data import ( + header_list, + value_matrix, + value_matrix_with_none, + mix_header_list, + mix_value_matrix, + float_header_list, + float_value_matrix, + value_matrix_iter +) Data = collections.namedtuple( @@ -225,9 +228,9 @@ def test_normal(self, capsys): "tmp", ["attr_a", "attr_b", "attr_c"], [ - ["1", "4", "a"], - ["2", "2.1", "bb"], - ["3", "120.9", "ccc"], + ["1", "4", "a"], + ["2", "2.1", "bb"], + ["3", "120.9", "ccc"], ]) writer.from_tabledata(tabledata) @@ -235,9 +238,9 @@ def test_normal(self, capsys): assert writer.table_name == "tmp" assert writer.header_list == ["attr_a", "attr_b", "attr_c"] assert writer.value_matrix == [ - ["1", "4", "a"], - ["2", "2.1", "bb"], - ["3", "120.9", "ccc"], + [1, 4, "a"], + [2, Decimal("2.1"), "bb"], + [3, Decimal("120.9"), "ccc"], ] diff --git a/test/test_mediawiki_writer.py b/test/test_mediawiki_writer.py index b9b7dcef..2fd0582a 100644 --- a/test/test_mediawiki_writer.py +++ b/test/test_mediawiki_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import collections import pytablewriter diff --git a/test/test_null_writer.py b/test/test_null_writer.py index 268547bc..03dd2dd8 100644 --- a/test/test_null_writer.py +++ b/test/test_null_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import pytablewriter diff --git a/test/test_pandas_writer.py b/test/test_pandas_writer.py index ad08f191..5d534497 100644 --- a/test/test_pandas_writer.py +++ b/test/test_pandas_writer.py @@ -6,18 +6,21 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import pytablewriter import pytest -from .data import Data -from .data import null_test_data_list -from .data import header_list -from .data import value_matrix -from .data import value_matrix_with_none -from .data import mix_header_list -from .data import mix_value_matrix -from .data import value_matrix_iter +from .data import ( + Data, + null_test_data_list, + header_list, + value_matrix, + value_matrix_with_none, + mix_header_list, + mix_value_matrix, + value_matrix_iter +) try: import numpy @@ -45,8 +48,7 @@ "dd", "e", ] -""" - ), +"""), Data( table="tablename", indent=0, @@ -54,8 +56,7 @@ value=None, expected="""tablename = pandas.DataFrame([ ]) -""" - ), +"""), Data( table="table with%null-value", indent=0, @@ -74,8 +75,7 @@ "dd", "e", ] -""" - ), +"""), Data( table="tablename", indent=0, @@ -98,8 +98,7 @@ "mix_num", "time", ] -""" - ), +"""), ] exception_test_data_list = [ diff --git a/test/test_python_code_writer.py b/test/test_python_code_writer.py index 05fd6aeb..861d7999 100644 --- a/test/test_python_code_writer.py +++ b/test/test_python_code_writer.py @@ -6,18 +6,21 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals -import pytablewriter +import pytablewriter as ptw import pytest -from .data import Data -from .data import null_test_data_list -from .data import header_list -from .data import value_matrix -from .data import value_matrix_with_none -from .data import mix_header_list -from .data import mix_value_matrix -from .data import value_matrix_iter +from .data import ( + Data, + null_test_data_list, + header_list, + value_matrix, + value_matrix_with_none, + mix_header_list, + mix_value_matrix, + value_matrix_iter +) normal_test_data_list = [ @@ -32,8 +35,7 @@ [2, 2.2, "bb", 2.2, "2.2"], [3, 3.3, "ccc", 3.0, "cccc"], ] -""" - ), +"""), Data( table="TABLENAME", indent=0, @@ -42,20 +44,7 @@ expected="""tablename = [ ["a", "b", "c", "dd", "e"], ] -""" - ), - Data( - table=None, - indent=0, - header=None, - value=value_matrix, - expected="""[ - [1, 123.1, "a", 1.0, "1"], - [2, 2.2, "bb", 2.2, "2.2"], - [3, 3.3, "ccc", 3.0, "cccc"], -] -""" - ), +"""), Data( table="TableName", indent=1, @@ -67,8 +56,7 @@ [2, 2.2, "bb", 2.2, "2.2"], [3, 3.3, "ccc", 3.0, "cccc"], ] -""" - ), +"""), Data( table="TABLE Name", indent=0, @@ -81,8 +69,7 @@ [3, 3.3, "ccc", None, "cccc"], [None, None, None, None, None], ] -""" - ), +"""), Data( table="tablename", indent=0, @@ -94,11 +81,10 @@ [2, 2.20, "bbb", 2.2, "2.2", False, float("inf"), float("nan"), float("inf"), dateutil.parser.parse("2017-01-02T03:04:05+0900")], [3, 3.33, "cccc", -3.0, "ccc", True, float("inf"), float("nan"), float("nan"), dateutil.parser.parse("2017-01-01T00:00:00")], ] -""" - ), +"""), ] -table_writer_class = pytablewriter.PythonCodeTableWriter +table_writer_class = ptw.PythonCodeTableWriter class Test_PythonCodeTableWriter_write_new_line: @@ -158,7 +144,11 @@ def test_normal_not_strict(self, capsys): ["table", "indent", "header", "value", "expected"], [ [data.table, data.indent, data.header, data.value, data.expected] - for data in null_test_data_list + for data in null_test_data_list + [ + Data(table=None, indent=0, header=header_list, + value=value_matrix, + expected=ptw.EmptyTableNameError) + ] ] ) def test_exception(self, capsys, table, indent, header, value, expected): diff --git a/test/test_rst_csv_writer.py b/test/test_rst_csv_writer.py index f365cb3e..eb707c4c 100644 --- a/test/test_rst_csv_writer.py +++ b/test/test_rst_csv_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import pytablewriter import pytest diff --git a/test/test_rst_grid_writer.py b/test/test_rst_grid_writer.py index a5faf74b..8ddc6c84 100644 --- a/test/test_rst_grid_writer.py +++ b/test/test_rst_grid_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import pytablewriter import pytest diff --git a/test/test_rst_simple_writer.py b/test/test_rst_simple_writer.py index ca804b18..57d7f04d 100644 --- a/test/test_rst_simple_writer.py +++ b/test/test_rst_simple_writer.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import pytablewriter import pytest diff --git a/test/test_toml_writer.py b/test/test_toml_writer.py new file mode 100644 index 00000000..c4ff74b4 --- /dev/null +++ b/test/test_toml_writer.py @@ -0,0 +1,194 @@ +# encoding: utf-8 + +""" +.. codeauthor:: Tsuyoshi Hombashi +""" + +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals +import collections +import datetime +from decimal import Decimal +import itertools + +import pytablewriter as ptw +import pytest +import toml + + +from .data import ( + header_list, + value_matrix, + value_matrix_with_none, + mix_header_list, + mix_value_matrix, + value_matrix_iter +) + + +Data = collections.namedtuple("Data", "table_name header value expected") + +normal_test_data_list = [ + Data( + table_name="normal", + header=header_list, + value=value_matrix, + expected="""[[normal]] +a = 1 +c = "a" +b = 123.1 +e = 1 +dd = 1 +[[normal]] +a = 2 +c = "bb" +b = 2.2 +e = 2.2 +dd = 2.2 +[[normal]] +a = 3 +c = "ccc" +b = 3.3 +e = "cccc" +dd = 3 +"""), + Data( + table_name="sparse", + header=header_list, + value=[ + ["1", "", "a", "1", None], + [None, 2.2, None, "2.2", 2.2], + [None, None, None, None, None], + [3, 3.3, "ccc", None, "cccc"], + [None, None, None, None, None], + ], + expected="""[[sparse]] +a = 1 +c = "a" +b = "" +dd = 1 +[[sparse]] +dd = 2.2 +b = 2.2 +e = 2.2 +[[sparse]] +a = 3 +c = "ccc" +b = 3.3 +e = "cccc" +"""), + Data( + table_name="symbols", + header=["a!0", "a#1", "a.2$", "a_%3", "a-&4"], + value=[ + ["a?b", "c d", "e+f", "g=h", "i*j"], + [1, 2.0, 3.3, Decimal("4.4"), ""], + ], + expected="""[[symbols]] +"a-&4" = "i*j" +"a#1" = "c d" +"a_%3" = "g=h" +"a!0" = "a?b" +"a.2$" = "e+f" +[[symbols]] +"a-&4" = "" +"a#1" = 2 +"a_%3" = 4.4 +"a!0" = 1 +"a.2$" = 3.3 +"""), + Data( + table_name="mixtype", + header=["int", "float", "bool", "datetime"], + value=[ + [ + 0, 2.2, True, + datetime.datetime(2017, 1, 2, 3, 4, 5), + ], + [ + -1, Decimal("4.4"), False, + datetime.datetime(2022, 1, 1, 0, 0, 0), + ], + ], + expected="""[[mixtype]] +float = 2.2 +datetime = 2017-01-02T03:04:05Z +int = 0 +bool = true +[[mixtype]] +float = 4.4 +datetime = 2022-01-01T00:00:00Z +int = -1 +bool = false +"""), +] + +exception_test_data_list = [ + Data( + table_name="dummy", + header=header, + value=value, + expected=ptw.EmptyTableDataError + ) + for header, value in itertools.product([None, [], ""], [None, [], ""]) +] + [ + Data( + table_name="empty_header", + header=None, + value=value_matrix, + expected=ptw.EmptyHeaderError + ), + Data( + table_name=None, + header=header_list, + value=value_matrix, + expected=ptw.EmptyTableNameError + ), +] + +table_writer_class = ptw.TomlTableWriter + + +class Test_TomlTableWriter_write_new_line: + + def test_normal(self, capsys): + writer = table_writer_class() + writer.write_null_line() + + out, _err = capsys.readouterr() + assert out == "\n" + + +class Test_TomlTableWriter_write_table: + + @pytest.mark.parametrize(["table_name", "header", "value", "expected"], [ + [data.table_name, data.header, data.value, data.expected] + for data in normal_test_data_list + ]) + def test_normal(self, capsys, table_name, header, value, expected): + writer = table_writer_class() + writer.table_name = table_name + writer.header_list = header + writer.value_matrix = value + writer.write_table() + + out, _err = capsys.readouterr() + + print("[expected]\n{}".format(expected)) + print("[actual]\n{}".format(out)) + + assert toml.loads(out) == toml.loads(expected) + + @pytest.mark.parametrize(["table_name", "header", "value", "expected"], [ + [data.table_name, data.header, data.value, data.expected] + for data in exception_test_data_list + ]) + def test_exception(self, capsys, table_name, header, value, expected): + writer = table_writer_class() + writer.table_name = table_name + writer.header_list = header + writer.value_matrix = value + + with pytest.raises(expected): + writer.write_table() diff --git a/test/test_writer_factory.py b/test/test_writer_factory.py index bdc193c8..248694a7 100644 --- a/test/test_writer_factory.py +++ b/test/test_writer_factory.py @@ -6,6 +6,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals import itertools import pytest @@ -46,6 +47,9 @@ class Test_WriterFactory_create_from_file_extension: ) + list(itertools.product( ["valid_ext.tsv", "valid_ext.TSV", ".tsv", "TSV"], [ptw.TsvTableWriter]) + ) + list(itertools.product( + ["valid_ext.toml", "valid_ext.TOML", ".toml", "TOML"], + [ptw.TomlTableWriter]) ) + list(itertools.product( ["valid_ext.xls", "valid_ext.XLS", ".xls", "XLS"], [ptw.ExcelXlsTableWriter]) @@ -101,6 +105,8 @@ class Test_FileLoaderFactory_create_from_format_name: ["rst_csv_table", ptw.RstCsvTableWriter], ["tsv", ptw.TsvTableWriter], ["TSV", ptw.TsvTableWriter], + ["toml", ptw.TomlTableWriter], + ["TOML", ptw.TomlTableWriter], ]) def test_normal(self, format_name, expected): writer = ptw.TableWriterFactory.create_from_format_name(format_name)