✨ guard against integer precision loss. https://github.com/pyexcel/pyexcel-ods/issues/30
This commit is contained in:
parent
92f694ba64
commit
a62527e7de
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue