guard against integer precision loss. https://github.com/pyexcel/pyexcel-ods/issues/30

This commit is contained in:
chfw 2018-11-25 13:40:10 +00:00
parent 92f694ba64
commit a62527e7de
4 changed files with 55 additions and 2 deletions

View File

@ -59,3 +59,5 @@ SEPARATOR_FORMATTER = "---%s---" % DEFAULT_NAME + "%s"
SEPARATOR_MATCHER = "---%s:(.*)---" % DEFAULT_NAME
DEFAULT_CSV_STREAM_FILE_FORMATTER = "---%s:" % DEFAULT_NAME + "%s---%s"
DEFAULT_CSV_NEWLINE = "\r\n"
MAX_INTEGER = 999999999999999

View File

@ -25,3 +25,32 @@ class UpgradePlugin(Exception):
"""raised when a known plugin is not compatible"""
pass
class IntegerAccuracyLossError(Exception):
"""
When an interger is greater than 999999999999999, ods loses its accuracy.
from pyexcel import Sheet, get_sheet
s = Sheet()
s[0,0] = 999999999999999 # 15 '9's
print(s)
s.save_as('abc.ods')
b=get_sheet(file_name='abc.ods')
b[0,0] == s[0,0]
s = Sheet()
s[0,0] = 9999999999999999 # 16 '9's
print(s)
s.save_as('abc.ods')
b=get_sheet(file_name='abc.ods')
b[0,0] != s[0,0]
"""
def __init__(self, message):
custom_message = (
message + '\n' +
"In order to keep its accuracy, please save as string. Then " +
"convert to int, long or float after the value will be read back"
)
super(IntegerAccuracyLossError, self).__init__(custom_message)

View File

@ -12,6 +12,8 @@ import math
import datetime
from pyexcel_io._compact import PY2
from pyexcel_io import constants
from pyexcel_io import exceptions
def has_no_digits_in_float(value):
@ -97,10 +99,16 @@ def detect_int_value(cell_text, pep_0515_off=True):
def float_value(value):
"""convert a value to float"""
if value > constants.MAX_INTEGER:
raise exceptions.IntegerAccuracyLossError("%s is too big" % value)
ret = float(value)
return ret
def throw_exception(value):
raise exceptions.IntegerAccuracyLossError("%s is too big" % value)
def date_value(value):
"""convert to data value accroding ods specification"""
ret = "invalid"
@ -168,7 +176,6 @@ ODS_FORMAT_CONVERSION = {
ODS_WRITE_FORMAT_COVERSION = {
float: "float",
int: "float",
str: "string",
datetime.date: "date",
datetime.time: "time",
@ -178,7 +185,8 @@ ODS_WRITE_FORMAT_COVERSION = {
if PY2:
ODS_WRITE_FORMAT_COVERSION[unicode] = "string"
ODS_WRITE_FORMAT_COVERSION[long] = "float"
ODS_WRITE_FORMAT_COVERSION[long] = "throw_exception"
VALUE_CONVERTERS = {
"float": float_value,
@ -220,6 +228,7 @@ ODS_VALUE_CONVERTERS = {
"time": ods_time_value,
"boolean": ods_bool_value,
"timedelta": ods_timedelta_value,
"throw_exception": throw_exception
}

View File

@ -3,7 +3,10 @@ from pyexcel_io.service import date_value, time_value
from pyexcel_io.service import detect_int_value
from pyexcel_io.service import detect_float_value
from pyexcel_io.service import ODS_WRITE_FORMAT_COVERSION
from pyexcel_io.service import float_value
from pyexcel_io.service import throw_exception
from pyexcel_io._compact import PY2
from pyexcel_io.exceptions import IntegerAccuracyLossError
from nose import SkipTest
@ -100,3 +103,13 @@ def test_ods_write_format_conversion():
eq_('float', expected)
else:
raise SkipTest
@raises(IntegerAccuracyLossError)
def test_big_int_value():
float_value(1000000000000000)
@raises(IntegerAccuracyLossError)
def test_throw_exception():
throw_exception(1000000000000000)