🤝 merge with master branch

This commit is contained in:
chfw 2019-03-16 14:07:36 +00:00
commit 3fc0533512
26 changed files with 558 additions and 220 deletions

View File

@ -5,4 +5,3 @@
{%block compat_block%}
{%endblock%}

View File

@ -1,5 +1,8 @@
{% extends 'tests/requirements.txt.jj2' %}
{%block extras %}
moban
black;python_version>="3.6"
isort;python_version>="3.6"
psutil
pyexcel
pyexcel-xls

View File

@ -1,4 +1,4 @@
{% extends 'tests/test_formatters.py.jj2' %}
{% block test_date_format %}
{% endblock %}
{% endblock %}

View File

@ -1,8 +1,13 @@
requires:
- type: git
url: https://github.com/moremoban/pypi-mobans
submodule: true
- https://github.com/pyexcel/pyexcel-mobans
configuration:
configuration_dir: "commons/config"
configuration_dir: "pyexcel-mobans:config"
template_dir:
- "commons/templates"
- "setupmobans/templates"
- "pyexcel-mobans:templates"
- "pypi-mobans:templates"
- ".moban.d"
configuration: pyexcel-ods.yml
targets:
@ -22,3 +27,6 @@ targets:
- "tests/test_stringio.py": "tests/test_stringio.py.jj2"
- "tests/test_writer.py": "tests/test_writer.py.jj2"
- "tests/base.py": "tests/base.py.jj2"
- output: CHANGELOG.rst
configuration: changelog.yml
template: CHANGELOG.rst.jj2

View File

@ -1,14 +1,71 @@
Change log
================================================================================
0.5.4 - 27.11.2018
--------------------------------------------------------------------------------
added
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. `#30 <https://github.com/pyexcel/pyexcel-ods/issues/30>`_, long type will not
be written in ods. please use string type. And if the integer is equal or
greater than 10 to the power of 16, it will not be written either in ods. In
both situation, IntegerPrecisionLossError will be raised.
0.5.3 - unreleased
--------------------------------------------------------------------------------
added
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. `#24 <https://github.com/pyexcel/pyexcel-ods/issues/24>`_, ignore
comments(<office:comment>) in cell
#. `#27 <https://github.com/pyexcel/pyexcel-ods/issues/27>`_, exception raised
when currency type is missing
#. fix odfpy version on 1.3.5.
0.5.2 - 23.10.2017
--------------------------------------------------------------------------------
updated
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. pyexcel `pyexcel#105 <https://github.com/pyexcel/pyexcel/issues/105>`_,
remove gease from setup_requires, introduced by 0.5.1.
#. remove python2.6 test support
0.5.1 - 20.10.2017
--------------------------------------------------------------------------------
added
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. `pyexcel#103 <https://github.com/pyexcel/pyexcel/issues/103>`_, include
LICENSE file in MANIFEST.in, meaning LICENSE file will appear in the released
tar ball.
0.5.0 - 30.08.2017
--------------------------------------------------------------------------------
Updated
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. put dependency on pyexcel-io 0.5.0, which uses cStringIO instead of StringIO.
Hence, there will be performance boost in handling files in memory.
Relocated
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. All ods type conversion code lives in pyexcel_io.service module
0.4.1 - 25.08.2017
--------------------------------------------------------------------------------
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. `#23 <https://github.com/pyexcel/pyexcel-ods/issues/23>`_, handle unseekable
stream given by http response
#. `pyexcel#23 <https://github.com/pyexcel/pyexcel/issues/23>`_, handle
unseekable stream given by http response
#. PR `#22 <https://github.com/pyexcel/pyexcel-ods/pull/22>`_, hanle white
spaces in a cell.
@ -16,39 +73,38 @@ Updated
--------------------------------------------------------------------------------
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. `#14 <https://github.com/pyexcel/pyexcel-xlsx/issues/14>`_, close file
#. `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, close file
handle
#. pyexcel-io plugin interface now updated to use
`lml <https://github.com/chfw/lml>`_.
#. pyexcel-io plugin interface now updated to use `lml
<https://github.com/chfw/lml>`_.
0.3.3 - 07.05.2017
--------------------------------------------------------------------------------
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. issue `#19 <https://github.com/pyexcel/pyexcel-odsr/issues/19>`_, not all texts
in a multi-node cell were extracted.
#. issue `pyexcel#19 <https://github.com/pyexcel/pyexcel/issues/19>`_, not all
texts in a multi-node cell were extracted.
0.3.2 - 13.04.2017
--------------------------------------------------------------------------------
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. issue `#17 <https://github.com/pyexcel/pyexcel-ods/issues/17>`_, empty
new line is ignored
#. issue `#6 <https://github.com/pyexcel/pyexcel-ods/issues/6>`_, PT288H00M00S
is valid duration
#. issue `pyexcel#17 <https://github.com/pyexcel/pyexcel/issues/17>`_, empty new
line is ignored
#. issue `pyexcel#6 <https://github.com/pyexcel/pyexcel/issues/6>`_,
PT288H00M00S is valid duration
0.3.1 - 02.02.2017
--------------------------------------------------------------------------------
Added
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Recognize currency type
@ -56,26 +112,24 @@ Added
--------------------------------------------------------------------------------
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Code refactoring with pyexcel-io v 0.3.0
0.2.2 - 24.10.2016
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. issue `#14 <https://github.com/pyexcel/pyexcel-ods/issues/14>`_, index error
when reading a ods file that has non-uniform columns repeated property.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. issue `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, index
error when reading a ods file that has non-uniform columns repeated property.
0.2.1 - 31.08.2016
--------------------------------------------------------------------------------
Added
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. support pagination. two pairs: start_row, row_limit and start_column,
column_limit help you deal with large files.
@ -85,114 +139,116 @@ Added
--------------------------------------------------------------------------------
Added
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. By default, `float` will be converted to `int` where fits. `auto_detect_int`,
a flag to switch off the autoatic conversion from `float` to `int`.
#. 'library=pyexcel-ods' was added so as to inform pyexcel to use it instead of
other libraries, in the situation where multiple plugins were installed.
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. support the auto-import feature of pyexcel-io 0.2.0
0.1.1 - 30.01.2016
--------------------------------------------------------------------------------
Added
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. 'streaming' is an extra option given to get_data. Only when 'streaming'
is explicitly set to True, the data will be consisted of generators,
hence will break your existing code.
#. 'streaming' is an extra option given to get_data. Only when 'streaming' is
explicitly set to True, the data will be consisted of generators, hence will
break your existing code.
#. uses yield in to_array and returns a generator
#. support multi-line text cell #5
#. feature migration from pyexcel-ods3 pyexcel/pyexcel-ods3#5
Updated
********************************************************************************
#. compatibility with pyexcel-io 0.1.1
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. compatibility with pyexcel-io 0.1.1
0.0.12 - 10.10.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. Bug fix: excessive trailing columns with empty values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Bug fix: excessive trailing columns with empty values
0.0.11 - 26.09.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. Complete fix for libreoffice datetime field
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Complete fix for libreoffice datetime field
0.0.10 - 15.09.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. Bug fix: date field could have datetime from libreoffice
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Bug fix: date field could have datetime from libreoffice
0.0.9 - 21.08.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. Bug fix: utf-8 string throw unicode exceptions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Bug fix: utf-8 string throw unicode exceptions
0.0.8 - 28.06.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. Pin dependency odfpy 0.9.6 to avoid buggy odfpy 1.3.0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Pin dependency odfpy 0.9.6 to avoid buggy odfpy 1.3.0
0.0.7 - 28.05.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. Bug fix: "number-columns-repeated" is now respected
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Bug fix: "number-columns-repeated" is now respected
0.0.6 - 21.05.2015
--------------------------------------------------------------------------------
Updated
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. get_data and save_data are seen across pyexcel-* extensions. remember them
once and use them across all extensions.
0.0.5 - 22.02.2015
--------------------------------------------------------------------------------
Added
********************************************************************************
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. Loads only one sheet from a multiple sheet book
#. Use New BSD License
0.0.4 - 14.12.2014
--------------------------------------------------------------------------------
Updated
********************************************************************************
#. IO interface update as pyexcel-io introduced keywords.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. IO interface update as pyexcel-io introduced keywords.
#. initial release
0.0.3 - 08.12.2014
--------------------------------------------------------------------------------
Updated
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. IO interface update as pyexcel-io introduced keywords.
#. initial release

View File

@ -3,3 +3,7 @@ all: test
test:
bash test.sh
format:
isort -y $(find pyexcel_ods -name "*.py"|xargs echo) $(find tests -name "*.py"|xargs echo)
black -l 79 pyexcel_ods
black -l 79 tests

191
changelog.yml Normal file
View File

@ -0,0 +1,191 @@
name: pyexcel-ods
organisation: pyexcel
releases:
- changes:
- action: added
details:
- '`#32`, fix odfpy pinning'
date: 16.03.2019
version: 0.5.5
- changes:
- action: added
details:
- '`#30`, long type will not be written in ods. please use string type. And if the integer is equal or greater than 10 to the power of 16, it will not be written either in ods. In both situation, IntegerPrecisionLossError will be raised.'
date: 27.11.2018
version: 0.5.4
- changes:
- action: added
details:
- '`#24`, ignore comments(<office:comment>) in cell'
- '`#27`, exception raised when currency type is missing'
- fix odfpy version on 1.3.5.
date: unreleased
version: 0.5.3
- changes:
- action: updated
details:
- pyexcel `pyexcel#105`, remove gease from setup_requires, introduced by 0.5.1.
- remove python2.6 test support
date: 23.10.2017
version: 0.5.2
- changes:
- action: added
details:
- '`pyexcel#103`, include LICENSE file in MANIFEST.in, meaning LICENSE file will
appear in the released tar ball.'
date: 20.10.2017
version: 0.5.1
- changes:
- action: Updated
details:
- put dependency on pyexcel-io 0.5.0, which uses cStringIO instead of StringIO. Hence,
there will be performance boost in handling files in memory.
- action: Relocated
details:
- All ods type conversion code lives in pyexcel_io.service module
date: 30.08.2017
version: 0.5.0
- changes:
- action: Updated
details:
- '`pyexcel#pyexcel-ods#23`, handle unseekable stream given by http response'
- PR `pyexcel#pyexcel-ods#PR#22`, hanle white spaces in a cell.
date: 25.08.2017
version: 0.4.1
- changes:
- action: Updated
details:
- '`pyexcel#pyexcel-xlsx#14`, close file handle'
- pyexcel-io plugin interface now updated to use `lml <https://github.com/chfw/lml>`_.
date: 19.06.2017
version: 0.4.0
- changes:
- action: Updated
details:
- issue `pyexcel#pyexcel-odsr#19`, not all texts in a multi-node cell were extracted.
date: 07.05.2017
version: 0.3.3
- changes:
- action: Updated
details:
- issue `pyexcel#pyexcel-ods#17`, empty new line is ignored
- issue `pyexcel#pyexcel-ods#6`, PT288H00M00S is valid duration
date: 13.04.2017
version: 0.3.2
- changes:
- action: Added
details:
- Recognize currency type
date: 02.02.2017
version: 0.3.1
- changes:
- action: Updated
details:
- Code refactoring with pyexcel-io v 0.3.0
date: 22.12.2016
version: 0.3.0
- changes:
- action: Updated
details:
- issue `pyexcel#pyexcel-ods#14`, index error when reading a ods file that has
non-uniform columns repeated property.
date: 24.10.2016
version: 0.2.2
- changes:
- action: Added
details:
- 'support pagination. two pairs: start_row, row_limit and start_column, column_limit
help you deal with large files.'
- use odfpy 1.3.3 as compulsory package. offically support python 3
date: 31.08.2016
version: 0.2.1
- changes:
- action: Added
details:
- By default, `float` will be converted to `int` where fits. `auto_detect_int`, a
flag to switch off the autoatic conversion from `float` to `int`.
- '''library=pyexcel-ods'' was added so as to inform pyexcel to use it instead
of other libraries, in the situation where multiple plugins were installed.'
- action: Updated
details:
- support the auto-import feature of pyexcel-io 0.2.0
date: 01.06.2016
version: 0.2.0
- changes:
- action: Added
details:
- '''streaming'' is an extra option given to get_data. Only when ''streaming'' is
explicitly set to True, the data will be consisted of generators, hence will
break your existing code.'
- uses yield in to_array and returns a generator
- 'support multi-line text cell #5'
- feature migration from pyexcel-ods3 pyexcel/pyexcel-ods3#5
- action: Updated
details:
- compatibility with pyexcel-io 0.1.1
date: 30.01.2016
version: 0.1.1
- changes:
- action: Updated
details:
- 'Bug fix: excessive trailing columns with empty values'
date: 10.10.2015
version: 0.0.12
- changes:
- action: Updated
details:
- Complete fix for libreoffice datetime field
date: 26.09.2015
version: 0.0.11
- changes:
- action: Updated
details:
- 'Bug fix: date field could have datetime from libreoffice'
date: 15.09.2015
version: 0.0.10
- changes:
- action: Updated
details:
- 'Bug fix: utf-8 string throw unicode exceptions'
date: 21.08.2015
version: 0.0.9
- changes:
- action: Updated
details:
- Pin dependency odfpy 0.9.6 to avoid buggy odfpy 1.3.0
date: 28.06.2015
version: 0.0.8
- changes:
- action: Updated
details:
- 'Bug fix: "number-columns-repeated" is now respected'
date: 28.05.2015
version: 0.0.7
- changes:
- action: Updated
details:
- get_data and save_data are seen across pyexcel-* extensions. remember them once
and use them across all extensions.
date: 21.05.2015
version: 0.0.6
- changes:
- action: Added
details:
- Loads only one sheet from a multiple sheet book
- Use New BSD License
date: 22.02.2015
version: 0.0.5
- changes:
- action: Updated
details:
- IO interface update as pyexcel-io introduced keywords.
- initial release
date: 14.12.2014
version: 0.0.4
- changes:
- action: Updated
details:
- IO interface update as pyexcel-io introduced keywords.
- initial release
date: 08.12.2014
version: 0.0.3

View File

@ -25,9 +25,9 @@ project = 'pyexcel'
copyright = 'copyright 2015-2019 Onni Software Ltd.'
author = 'Onni Software Ltd.'
# The short X.Y version
version = '0.4.2'
version = '0.5.5'
# The full version, including alpha/beta/rc tags
release = '0.4.1'
release = '0.5.4'
# -- General configuration ---------------------------------------------------

View File

@ -1,11 +1,11 @@
overrides: "pyexcel.yaml"
name: "pyexcel-ods"
nick_name: ods
version: 0.4.2
current_version: 0.4.2
release: 0.4.1
version: 0.5.5
current_version: 0.5.5
release: 0.5.4
file_type: ods
dependencies:
- pyexcel-io>=0.4.0
- odfpy>=1.3.3
- pyexcel-io>=0.5.10
- odfpy==1.3.5
description: A wrapper library to read, manipulate and write data in ods format

View File

@ -9,17 +9,21 @@
# this line has to be place above all else
# because of dynamic import
from pyexcel_io.plugins import IOPluginInfoChain
from pyexcel_io.io import get_data as read_data, isstream, store_data as write_data
from pyexcel_io.io import (
get_data as read_data,
isstream,
store_data as write_data,
)
__FILE_TYPE__ = 'ods'
__FILE_TYPE__ = "ods"
IOPluginInfoChain(__name__).add_a_reader(
relative_plugin_class_path='odsr.ODSBook',
relative_plugin_class_path="odsr.ODSBook",
file_types=[__FILE_TYPE__],
stream_type='binary'
stream_type="binary",
).add_a_writer(
relative_plugin_class_path='odsw.ODSWriter',
relative_plugin_class_path="odsw.ODSWriter",
file_types=[__FILE_TYPE__],
stream_type='binary'
stream_type="binary",
)

View File

@ -21,21 +21,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# Thanks to grt for the fixes
from odf.teletype import extractText
from odf.table import TableRow, TableCell, Table
from odf.text import P
import pyexcel_io.service as service
from odf.namespaces import OFFICENS
from odf.opendocument import load
from odf.table import Table, TableCell, TableRow
# Thanks to grt for the fixes
from odf.teletype import extractText
from odf.text import P
from pyexcel_io._compact import OrderedDict
from pyexcel_io.book import BookReader
from pyexcel_io.sheet import SheetReader
from pyexcel_io._compact import OrderedDict
import pyexcel_io.service as service
class ODSSheet(SheetReader):
"""native ods sheet"""
def __init__(self, sheet, auto_detect_int=True, **keywords):
SheetReader.__init__(self, sheet, **keywords)
self.__auto_detect_int = auto_detect_int
@ -69,12 +70,15 @@ class ODSSheet(SheetReader):
elif cell_type == "currency":
value = cell.getAttrNS(OFFICENS, value_token)
currency = cell.getAttrNS(OFFICENS, cell_type)
ret = value + ' ' + currency
if currency:
ret = value + " " + currency
else:
ret = value
else:
if cell_type in service.VALUE_CONVERTERS:
value = cell.getAttrNS(OFFICENS, value_token)
n_value = service.VALUE_CONVERTERS[cell_type](value)
if cell_type == 'float' and self.__auto_detect_int:
if cell_type == "float" and self.__auto_detect_int:
if service.has_no_digits_in_float(n_value):
n_value = int(n_value)
ret = n_value
@ -88,13 +92,16 @@ class ODSSheet(SheetReader):
paragraphs = cell.getElementsByType(P)
# for each text node
for paragraph in paragraphs:
data = extractText(paragraph)
text_content.append(data)
return '\n'.join(text_content)
name_space, tag = paragraph.parentNode.qname
if tag != str("annotation"):
data = extractText(paragraph)
text_content.append(data)
return "\n".join(text_content)
class ODSBook(BookReader):
"""read ods book"""
def open(self, file_name, **keywords):
"""open ods file"""
BookReader.open(self, file_name, **keywords)
@ -108,8 +115,11 @@ class ODSBook(BookReader):
def read_sheet_by_name(self, sheet_name):
"""read a named sheet"""
tables = self._native_book.spreadsheet.getElementsByType(Table)
rets = [table for table in tables
if table.getAttribute('name') == sheet_name]
rets = [
table
for table in tables
if table.getAttribute("name") == sheet_name
]
if len(rets) == 0:
raise ValueError("%s cannot be found" % sheet_name)
else:
@ -122,8 +132,9 @@ class ODSBook(BookReader):
if sheet_index < length:
return self.read_sheet(tables[sheet_index])
else:
raise IndexError("Index %d of out bound %d" % (
sheet_index, length))
raise IndexError(
"Index %d of out bound %d" % (sheet_index, length)
)
def read_all(self):
"""read all sheets"""

View File

@ -9,16 +9,14 @@
"""
import sys
from odf.table import TableRow, TableCell, Table
from odf.text import P
import pyexcel_io.service as converter
from odf.namespaces import OFFICENS
from odf.opendocument import OpenDocumentSpreadsheet
from odf.table import Table, TableCell, TableRow
from odf.text import P
from pyexcel_io.book import BookWriter
from pyexcel_io.sheet import SheetWriter
import pyexcel_io.service as converter
PY2 = sys.version_info[0] == 2
PY27_BELOW = PY2 and sys.version_info[1] < 7
@ -28,6 +26,7 @@ class ODSSheetWriter(SheetWriter):
"""
ODS sheet writer
"""
def set_sheet_name(self, name):
"""initialize the native table"""
self._native_sheet = Table(name=name)
@ -41,19 +40,22 @@ class ODSSheetWriter(SheetWriter):
cell_to_be_written = TableCell()
cell_type = type(cell)
cell_odf_type = converter.ODS_WRITE_FORMAT_COVERSION.get(
cell_type, "string")
cell_type, "string"
)
cell_to_be_written.setAttrNS(OFFICENS, "value-type", cell_odf_type)
cell_odf_value_token = converter.VALUE_TOKEN.get(
cell_odf_type, "value")
cell_odf_type, "value"
)
converter_func = converter.ODS_VALUE_CONVERTERS.get(
cell_odf_type, None)
cell_odf_type, None
)
if converter_func:
cell = converter_func(cell)
if cell_odf_type != 'string':
if cell_odf_type != "string":
cell_to_be_written.setAttrNS(OFFICENS, cell_odf_value_token, cell)
cell_to_be_written.addElement(P(text=cell))
else:
lines = cell.split('\n')
lines = cell.split("\n")
for line in lines:
cell_to_be_written.addElement(P(text=line))
row.addElement(cell_to_be_written)
@ -80,6 +82,7 @@ class ODSWriter(BookWriter):
open document spreadsheet writer
"""
def __init__(self):
BookWriter.__init__(self)
self._native_book = OpenDocumentSpreadsheet()

View File

@ -1,2 +1,2 @@
pyexcel-io>=0.4.0
odfpy>=1.3.3
pyexcel-io>=0.5.10
odfpy==1.3.5

View File

@ -1,15 +1,14 @@
#!/usr/bin/env python3
# Template by pypi-mobans
import os
import sys
import codecs
import locale
# Template by pypi-mobans
import os
import platform
import sys
from shutil import rmtree
from setuptools import Command, setup, find_packages
from setuptools import Command, find_packages, setup
# Work around mbcs bug in distutils.
# http://bugs.python.org/issue10945
@ -26,14 +25,14 @@ except (ValueError, UnicodeError, locale.Error):
NAME = 'pyexcel-ods'
AUTHOR = 'C.W.'
VERSION = '0.4.2'
VERSION = '0.5.5'
EMAIL = 'wangc_2011@hotmail.com'
LICENSE = 'New BSD'
DESCRIPTION = (
'A wrapper library to read, manipulate and write data in ods format'
)
URL = 'https://github.com/pyexcel/pyexcel-ods'
DOWNLOAD_URL = '%s/archive/0.4.1.tar.gz' % URL
DOWNLOAD_URL = '%s/archive/0.5.4.tar.gz' % URL
FILES = ['README.rst', 'CHANGELOG.rst']
KEYWORDS = [
'python',
@ -52,8 +51,8 @@ CLASSIFIERS = [
]
INSTALL_REQUIRES = [
'pyexcel-io>=0.4.0',
'odfpy>=1.3.3',
'pyexcel-io>=0.5.10',
'odfpy==1.3.5',
]
SETUP_COMMANDS = {}
@ -64,8 +63,8 @@ EXTRAS_REQUIRE = {
# You do not need to read beyond this line
PUBLISH_COMMAND = '{0} setup.py sdist bdist_wheel upload -r pypi'.format(
sys.executable)
GS_COMMAND = ('gs pyexcel-ods v0.4.1 ' +
"Find 0.4.1 in changelog for more details")
GS_COMMAND = ('gs pyexcel-ods v0.5.4 ' +
"Find 0.5.4 in changelog for more details")
NO_GS_MESSAGE = ('Automatic github release is disabled. ' +
'Please install gease to enable it.')
UPLOAD_FAILED_MSG = (

View File

@ -1,11 +1,13 @@
import os # noqa
import pyexcel
import datetime # noqa
from nose.tools import raises, eq_ # noqa
import os # noqa
from nose.tools import eq_, raises # noqa
import pyexcel
def create_sample_file1(file):
data = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 1.1, 1]
data = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", 1.1, 1]
table = []
table.append(data[:4])
table.append(data[4:8])
@ -17,10 +19,11 @@ class PyexcelHatWriterBase:
"""
Abstract functional test for hat writers
"""
content = {
"X": [1, 2, 3, 4, 5],
"Y": [6, 7, 8, 9, 10],
"Z": [11, 12, 13, 14, 15]
"Z": [11, 12, 13, 14, 15],
}
def test_series_table(self):
@ -36,11 +39,12 @@ class PyexcelWriterBase:
testfile and testfile2 have to be initialized before
it is used for testing
"""
content = [
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5],
]
def _create_a_file(self, file):
@ -54,7 +58,6 @@ class PyexcelWriterBase:
class PyexcelMultipleSheetBase:
def _write_test_file(self, filename):
pyexcel.save_book_as(bookdict=self.content, dest_file_name=filename)
@ -80,7 +83,7 @@ class PyexcelMultipleSheetBase:
expected = [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]
assert data == expected
data = list(b["Sheet3"].rows())
expected = [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
expected = [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
assert data == expected
sheet3 = b["Sheet3"]
sheet3.name_columns_by_row(0)
@ -90,7 +93,6 @@ class PyexcelMultipleSheetBase:
class ODSCellTypes:
def test_formats(self):
# date formats
date_format = "%d/%m/%Y"
@ -104,9 +106,10 @@ class ODSCellTypes:
eq_(self.data["Sheet1"][1][1].strftime(time_format), "12:12:11")
eq_(self.data["Sheet1"][2][1].strftime(time_format), "12:00:00")
eq_(self.data["Sheet1"][3][1], 0)
eq_(self.data["Sheet1"][4][1], datetime.timedelta(hours=27,
minutes=17,
seconds=54))
eq_(
self.data["Sheet1"][4][1],
datetime.timedelta(hours=27, minutes=17, seconds=54),
)
eq_(self.data["Sheet1"][5][1], "Other")
# boolean
eq_(self.data["Sheet1"][0][2], "Boolean")
@ -117,8 +120,8 @@ class ODSCellTypes:
eq_(self.data["Sheet1"][1][3], 11.11)
# Currency
eq_(self.data["Sheet1"][0][4], "Currency")
eq_(self.data["Sheet1"][1][4], '1 GBP')
eq_(self.data["Sheet1"][2][4], '-10000 GBP')
eq_(self.data["Sheet1"][1][4], "1 GBP")
eq_(self.data["Sheet1"][2][4], "-10000 GBP")
# Percentage
eq_(self.data["Sheet1"][0][5], "Percentage")
eq_(self.data["Sheet1"][1][5], 2)

BIN
tests/fixtures/comment-in-cell.ods vendored Normal file

Binary file not shown.

BIN
tests/fixtures/issue_27.ods vendored Normal file

Binary file not shown.

View File

@ -3,6 +3,9 @@ mock;python_version<"3"
codecov
coverage
flake8
moban
black;python_version>="3.6"
isort;python_version>="3.6"
psutil
pyexcel
pyexcel-xls

View File

@ -1,18 +1,21 @@
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import os
from nose import SkipTest
from nose.tools import eq_, raises
import psutil
import pyexcel as pe
from pyexcel_io.exceptions import IntegerAccuracyLossError
from pyexcel_ods import get_data, save_data
from nose.tools import raises, eq_
from nose import SkipTest
IN_TRAVIS = 'TRAVIS' in os.environ
IN_TRAVIS = "TRAVIS" in os.environ
def test_bug_fix_for_issue_1():
data = get_data(get_fixtures("repeated.ods"))
eq_(data["Sheet1"], [['repeated', 'repeated', 'repeated', 'repeated']])
eq_(data["Sheet1"], [["repeated", "repeated", "repeated", "repeated"]])
def test_bug_fix_for_issue_2():
@ -21,12 +24,13 @@ def test_bug_fix_for_issue_2():
data.update({"Sheet 2": [[u"row 1", u"Héllô!", u"HolÁ!"]]})
save_data("your_file.ods", data)
new_data = get_data("your_file.ods")
assert new_data["Sheet 2"] == [[u'row 1', u'H\xe9ll\xf4!', u'Hol\xc1!']]
assert new_data["Sheet 2"] == [[u"row 1", u"H\xe9ll\xf4!", u"Hol\xc1!"]]
@raises(Exception)
def test_invalid_date():
from pyexcel_ods.ods import date_value
value = "2015-08-"
date_value(value)
@ -34,30 +38,27 @@ def test_invalid_date():
@raises(Exception)
def test_fake_date_time_10():
from pyexcel_ods.ods import date_value
date_value("1234567890")
@raises(Exception)
def test_fake_date_time_19():
from pyexcel_ods.ods import date_value
date_value("1234567890123456789")
@raises(Exception)
def test_fake_date_time_20():
from pyexcel_ods.ods import date_value
date_value("12345678901234567890")
def test_issue_13():
test_file = "test_issue_13.ods"
data = [
[1, 2],
[],
[],
[],
[3, 4]
]
data = [[1, 2], [], [], [], [3, 4]]
save_data(test_file, {test_file: data})
written_data = get_data(test_file, skip_empty_rows=False)
eq_(data, written_data[test_file])
@ -67,23 +68,20 @@ def test_issue_13():
def test_issue_14():
# pyexcel issue 61
test_file = "issue_61.ods"
data = get_data(get_fixtures(test_file),
skip_empty_rows=True)
eq_(data['S-LMC'], [[u'aaa'], [0]])
data = get_data(get_fixtures(test_file), skip_empty_rows=True)
eq_(data["S-LMC"], [[u"aaa"], [0]])
def test_issue_6():
test_file = "12_day_as_time.ods"
data = get_data(get_fixtures(test_file),
skip_empty_rows=True)
eq_(data['Sheet1'][0][0].days, 12)
data = get_data(get_fixtures(test_file), skip_empty_rows=True)
eq_(data["Sheet1"][0][0].days, 12)
def test_issue_19():
test_file = "pyexcel_81_ods_19.ods"
data = get_data(get_fixtures(test_file),
skip_empty_rows=True)
eq_(data['product.template'][1][1], 'PRODUCT NAME PMP')
data = get_data(get_fixtures(test_file), skip_empty_rows=True)
eq_(data["product.template"][1][1], "PRODUCT NAME PMP")
def test_issue_83_ods_file_handle():
@ -94,7 +92,7 @@ def test_issue_83_ods_file_handle():
open_files_l1 = proc.open_files()
# start with a csv file
data = pe.iget_array(file_name=test_file, library='pyexcel-ods')
data = pe.iget_array(file_name=test_file, library="pyexcel-ods")
open_files_l2 = proc.open_files()
delta = len(open_files_l2) - len(open_files_l1)
# cannot catch open file handle
@ -118,14 +116,48 @@ def test_issue_83_ods_file_handle():
def test_pr_22():
test_file = get_fixtures("white_space.ods")
data = get_data(test_file)
# OrderedDict([(u'Sheet1', [[u'paragraph with tab, space, new line']])])
eq_(data['Sheet1'][0][0], 'paragraph with tab(\t), space, \nnew line')
eq_(data["Sheet1"][0][0], "paragraph with tab(\t), space, \nnew line")
def test_issue_23():
if not IN_TRAVIS:
raise SkipTest()
pe.get_book(url="https://github.com/pyexcel/pyexcel-ods/raw/master/tests/fixtures/white_space.ods"); # flake8: noqa
pe.get_book(
url=(
"https://github.com/pyexcel/pyexcel-ods/"
+ "raw/master/tests/fixtures/white_space.ods"
)
)
def test_issue_24():
test_file = get_fixtures("comment-in-cell.ods")
data = get_data(test_file)
eq_(data["Sheet1"], [["test"]])
def test_issue_27():
test_file = get_fixtures("issue_27.ods")
data = get_data(test_file, skip_empty_rows=True)
eq_(data["VGPMX"], [["", "Cost Basis", "0"]])
def test_issue_30():
test_file = "issue_30.ods"
sheet = pe.Sheet()
sheet[0, 0] = 999999999999999
sheet.save_as(test_file)
sheet2 = pe.get_sheet(file_name=test_file)
eq_(sheet[0, 0], sheet2[0, 0])
os.unlink(test_file)
@raises(IntegerAccuracyLossError)
def test_issue_30_precision_loss():
test_file = "issue_30_2.ods"
sheet = pe.Sheet()
sheet[0, 0] = 9999999999999999
sheet.save_as(test_file)
def get_fixtures(filename):

View File

@ -1,8 +1,9 @@
import os
from pyexcel_io import get_data, save_data
from nose.tools import eq_
from pyexcel_io import get_data, save_data
class TestFilter:
def setUp(self):
@ -13,49 +14,58 @@ class TestFilter:
[3, 23, 33],
[4, 24, 34],
[5, 25, 35],
[6, 26, 36]
[6, 26, 36],
]
save_data(self.test_file, sample)
self.sheet_name = "pyexcel_sheet1"
def test_filter_row(self):
filtered_data = get_data(self.test_file, start_row=3,
library="pyexcel-ods")
filtered_data = get_data(
self.test_file, start_row=3, library="pyexcel-ods"
)
expected = [[4, 24, 34], [5, 25, 35], [6, 26, 36]]
eq_(filtered_data[self.sheet_name], expected)
def test_filter_row_2(self):
filtered_data = get_data(self.test_file, start_row=3, row_limit=1,
library="pyexcel-ods")
filtered_data = get_data(
self.test_file, start_row=3, row_limit=1, library="pyexcel-ods"
)
expected = [[4, 24, 34]]
eq_(filtered_data[self.sheet_name], expected)
def test_filter_column(self):
filtered_data = get_data(self.test_file, start_column=1,
library="pyexcel-ods")
expected = [[21, 31], [22, 32], [23, 33],
[24, 34], [25, 35], [26, 36]]
filtered_data = get_data(
self.test_file, start_column=1, library="pyexcel-ods"
)
expected = [[21, 31], [22, 32], [23, 33], [24, 34], [25, 35], [26, 36]]
eq_(filtered_data[self.sheet_name], expected)
def test_filter_column_2(self):
filtered_data = get_data(self.test_file,
start_column=1, column_limit=1,
library="pyexcel-ods")
filtered_data = get_data(
self.test_file,
start_column=1,
column_limit=1,
library="pyexcel-ods",
)
expected = [[21], [22], [23], [24], [25], [26]]
eq_(filtered_data[self.sheet_name], expected)
def test_filter_both_ways(self):
filtered_data = get_data(self.test_file,
start_column=1, start_row=3,
library="pyexcel-ods")
filtered_data = get_data(
self.test_file, start_column=1, start_row=3, library="pyexcel-ods"
)
expected = [[24, 34], [25, 35], [26, 36]]
eq_(filtered_data[self.sheet_name], expected)
def test_filter_both_ways_2(self):
filtered_data = get_data(self.test_file,
start_column=1, column_limit=1,
start_row=3, row_limit=1,
library="pyexcel-ods")
filtered_data = get_data(
self.test_file,
start_column=1,
column_limit=1,
start_row=3,
row_limit=1,
library="pyexcel-ods",
)
expected = [[24]]
eq_(filtered_data[self.sheet_name], expected)

View File

@ -1,5 +1,6 @@
import os
from textwrap import dedent
from nose.tools import eq_
import pyexcel as pe
@ -9,46 +10,58 @@ class TestAutoDetectInt:
def setUp(self):
self.content = [[1, 2, 3.1]]
self.test_file = "test_auto_detect_init.ods"
pe.save_as(
array=self.content, dest_file_name=self.test_file
)
pe.save_as(array=self.content, dest_file_name=self.test_file)
def test_auto_detect_int(self):
sheet = pe.get_sheet(file_name=self.test_file, library="pyexcel-ods")
expected = dedent("""
expected = dedent(
"""
pyexcel_sheet1:
+---+---+-----+
| 1 | 2 | 3.1 |
+---+---+-----+""").strip()
+---+---+-----+"""
).strip()
eq_(str(sheet), expected)
def test_get_book_auto_detect_int(self):
book = pe.get_book(file_name=self.test_file, library="pyexcel-ods")
expected = dedent("""
expected = dedent(
"""
pyexcel_sheet1:
+---+---+-----+
| 1 | 2 | 3.1 |
+---+---+-----+""").strip()
+---+---+-----+"""
).strip()
eq_(str(book), expected)
def test_auto_detect_int_false(self):
sheet = pe.get_sheet(file_name=self.test_file, auto_detect_int=False,
library="pyexcel-ods")
expected = dedent("""
sheet = pe.get_sheet(
file_name=self.test_file,
auto_detect_int=False,
library="pyexcel-ods",
)
expected = dedent(
"""
pyexcel_sheet1:
+-----+-----+-----+
| 1.0 | 2.0 | 3.1 |
+-----+-----+-----+""").strip()
+-----+-----+-----+"""
).strip()
eq_(str(sheet), expected)
def test_get_book_auto_detect_int_false(self):
book = pe.get_book(file_name=self.test_file, auto_detect_int=False,
library="pyexcel-ods")
expected = dedent("""
book = pe.get_book(
file_name=self.test_file,
auto_detect_int=False,
library="pyexcel-ods",
)
expected = dedent(
"""
pyexcel_sheet1:
+-----+-----+-----+
| 1.0 | 2.0 | 3.1 |
+-----+-----+-----+""").strip()
+-----+-----+-----+"""
).strip()
eq_(str(book), expected)
def tearDown(self):

View File

@ -1,12 +1,13 @@
import os
import pyexcel
def test_reading_multiline_ods():
testfile = os.path.join("tests", "fixtures", "multilineods.ods")
sheet = pyexcel.get_sheet(file_name=testfile)
assert sheet[0, 0] == '1\n2\n3\n4'
assert sheet[1, 0] == 'Line 1\n\nLine 2'
assert sheet[0, 0] == "1\n2\n3\n4"
assert sheet[1, 0] == "Line 1\n\nLine 2"
def test_writing_multiline_ods():

View File

@ -1,7 +1,9 @@
import os
import sys
import pyexcel
from nose.tools import raises
import pyexcel
from base import PyexcelMultipleSheetBase
if sys.version_info[0] == 2 and sys.version_info[1] < 7:
@ -42,8 +44,7 @@ class TestAddBooks:
3,3,3,3
"""
self.rows = 3
pyexcel.save_book_as(bookdict=self.content,
dest_file_name=file)
pyexcel.save_book_as(bookdict=self.content, dest_file_name=file)
def setUp(self):
self.testfile = "multiple1.ods"
@ -55,12 +56,12 @@ class TestAddBooks:
def test_load_a_single_sheet(self):
b1 = pyexcel.get_book(file_name=self.testfile, sheet_name="Sheet1")
assert len(b1.sheet_names()) == 1
assert b1['Sheet1'].to_array() == self.content['Sheet1']
assert b1["Sheet1"].to_array() == self.content["Sheet1"]
def test_load_a_single_sheet2(self):
b1 = pyexcel.load_book(self.testfile, sheet_index=0)
assert len(b1.sheet_names()) == 1
assert b1['Sheet1'].to_array() == self.content['Sheet1']
assert b1["Sheet1"].to_array() == self.content["Sheet1"]
@raises(IndexError)
def test_load_a_single_sheet3(self):
@ -229,17 +230,17 @@ class TestMultiSheetReader:
self.testfile = "file_with_an_empty_sheet.ods"
def test_reader_with_correct_sheets(self):
r = pyexcel.BookReader(os.path.join("tests", "fixtures",
self.testfile))
r = pyexcel.BookReader(
os.path.join("tests", "fixtures", self.testfile)
)
assert r.number_of_sheets() == 3
def _produce_ordered_dict():
data_dict = OrderedDict()
data_dict.update({
"Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]})
data_dict.update({
"Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]})
data_dict.update({
"Sheet3": [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]})
data_dict.update({"Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]})
data_dict.update({"Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]})
data_dict.update(
{"Sheet3": [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]]}
)
return data_dict

View File

@ -1,15 +1,14 @@
import os
from base import ODSCellTypes
from pyexcel_ods.odsr import ODSBook
from pyexcel_ods.odsw import ODSWriter
from base import ODSCellTypes
class TestODSReader(ODSCellTypes):
def setUp(self):
r = ODSBook()
r.open(os.path.join("tests",
"fixtures",
"ods_formats.ods"))
r.open(os.path.join("tests", "fixtures", "ods_formats.ods"))
self.data = r.read_all()
for key in self.data.keys():
self.data[key] = list(self.data[key])
@ -19,9 +18,7 @@ class TestODSReader(ODSCellTypes):
class TestODSWriter(ODSCellTypes):
def setUp(self):
r = ODSBook()
r.open(os.path.join("tests",
"fixtures",
"ods_formats.ods"))
r.open(os.path.join("tests", "fixtures", "ods_formats.ods"))
self.data1 = r.read_all()
self.testfile = "odswriter.ods"
w = ODSWriter()

View File

@ -1,33 +1,32 @@
import os
import pyexcel
from nose.tools import eq_
import pyexcel
from base import create_sample_file1
class TestStringIO:
def test_ods_stringio(self):
testfile = "cute.ods"
create_sample_file1(testfile)
with open(testfile, "rb") as f:
content = f.read()
r = pyexcel.get_sheet(file_type="ods", file_content=content,
library="pyexcel-ods")
result = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 1.1, 1]
r = pyexcel.get_sheet(
file_type="ods", file_content=content, library="pyexcel-ods"
)
result = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", 1.1, 1]
actual = list(r.enumerate())
eq_(result, actual)
if os.path.exists(testfile):
os.unlink(testfile)
def test_ods_output_stringio(self):
data = [
[1, 2, 3],
[4, 5, 6]
]
io = pyexcel.save_as(dest_file_type="ods",
array=data)
r = pyexcel.get_sheet(file_type="ods", file_content=io.getvalue(),
library="pyexcel-ods")
data = [[1, 2, 3], [4, 5, 6]]
io = pyexcel.save_as(dest_file_type="ods", array=data)
r = pyexcel.get_sheet(
file_type="ods", file_content=io.getvalue(), library="pyexcel-ods"
)
result = [1, 2, 3, 4, 5, 6]
actual = list(r.enumerate())
eq_(result, actual)

View File

@ -1,7 +1,8 @@
import os
from pyexcel_ods.odsw import ODSWriter as Writer
from base import PyexcelHatWriterBase, PyexcelWriterBase
from pyexcel_ods.odsr import ODSBook as Reader
from base import PyexcelWriterBase, PyexcelHatWriterBase
from pyexcel_ods.odsw import ODSWriter as Writer
class TestNativeODSWriter:
@ -9,7 +10,7 @@ class TestNativeODSWriter:
self.content = {
"Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]],
"Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]],
"Sheet3": [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
"Sheet3": [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]],
}
self.testfile = "writer.ods"
writer = Writer()