diff --git a/sandbox/grist/objtypes.py b/sandbox/grist/objtypes.py index 2a18dd2f..5a97598e 100644 --- a/sandbox/grist/objtypes.py +++ b/sandbox/grist/objtypes.py @@ -11,7 +11,6 @@ If an object cannot be encoded or decoded, an "UnmarshallableValue" is returned of the form ['U', repr(obj)]. """ # pylint: disable=too-many-return-statements -import exceptions import traceback from datetime import date, datetime from math import isnan @@ -131,11 +130,19 @@ def strict_equal(a, b): return False def equal_encoding(a, b): - # pylint: disable=unidiomatic-typecheck - if isinstance(a, (str, unicode, bool, long, int)) or a is None: + # Compare NaNs as equal. + if isinstance(a, float) and isinstance(b, float): + return a == b or (isnan(a) and isnan(b)) + + # Compare bools as equal only to bools (these are distinguishable from numbers in JSON, and we + # take care to distinguish them in DB too). + if isinstance(a, bool) or isinstance(b, bool): + # pylint: disable=unidiomatic-typecheck return type(a) == type(b) and a == b - if isinstance(a, float): - return type(a) == type(b) and (a == b or (isnan(a) and isnan(b))) + + # Note for simple types, encode_object is trivial, and will result in a non-type-specific + # comparison (e.g. 1 and 1.0 will compare equal, as would "a" and u"a"). This is to capture + # equivalence of values in their JSON representations. return encode_object(a) == encode_object(b) def encode_object(value): @@ -230,11 +237,15 @@ class RaisedException(object): widely-used wrapper. To encode_args, it simply returns the entire encoded stored error, e.g. RaisedException(ValueError("foo")) is encoded as ["E", "ValueError", "foo"]. """ - def __init__(self, error, include_details=False): + def __init__(self, error, include_details=False, encoded_error=None): self.error = error self.details = traceback.format_exc() if include_details else None + self._encoded_error = encoded_error or self._encode_error() def encode_args(self): + return self._encoded_error + + def _encode_error(self): # TODO: We should probably return all args, to communicate the error details to the browser # and to DB (for when we store formula results). There are two concerns: one is that it's # potentially quite verbose; the other is that it's makes the tests more annoying (again b/c @@ -247,17 +258,8 @@ class RaisedException(object): @classmethod def decode_args(cls, *args): - # Decoding of a RaisedException is currently only used in tests. - name = args[0] - exc_type = getattr(exceptions, name) - assert isinstance(exc_type, type) and issubclass(exc_type, BaseException) - return cls(exc_type(*args[1:])) - - def __eq__(self, other): - return isinstance(other, type(self)) and self.encode_args() == other.encode_args() - - def __ne__(self, other): - return not self.__eq__(other) + # Decoding of a RaisedException is only enough to re-encode it. + return cls(None, encoded_error=list(args)) class RecordList(list): diff --git a/sandbox/grist/test_types.py b/sandbox/grist/test_types.py index 650a52a7..2199f123 100644 --- a/sandbox/grist/test_types.py +++ b/sandbox/grist/test_types.py @@ -209,13 +209,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "text", {"type": "Numeric"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"text": [0.0, 1.0, 1509556595.0, 0.0, 1.0, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"text": [0.0, 1.0, None]}], ["UpdateRecord", "_grist_Tables_column", 21, {"type": "Numeric"}], ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"text": [False, True, 1509556595, 0, 1, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"text": [False, True, ""]}], ["ModifyColumn", "Types", "text", {"type": "Text"}], ["UpdateRecord", "_grist_Tables_column", 21, {"type": "Text"}], ] @@ -233,13 +233,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "int", {"type": "Numeric"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"int": [0.0, 1.0, 1509556595.0, 0.0, 1.0, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"int": [0.0, 1.0, None]}], ["UpdateRecord", "_grist_Tables_column", 23, {"type": "Numeric"}], ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"int": [False, True, 1509556595, 0, 1, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"int": [False, True, ""]}], ["ModifyColumn", "Types", "int", {"type": "Int"}], ["UpdateRecord", "_grist_Tables_column", 23, {"type": "Int"}], ] @@ -250,13 +250,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "bool", {"type": "Numeric"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"bool": [0.0, 1.0, 1509556595.0, 0.0, 1.0, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 17, 18, 19], + {"bool": [0.0, 1.0, 0.0, 1.0, None]}], ["UpdateRecord", "_grist_Tables_column", 24, {"type": "Numeric"}], ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"bool": [False, True, 1509556595, False, True, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 17, 18, 19], + {"bool": [False, True, False, True, ""]}], ["ModifyColumn", "Types", "bool", {"type": "Bool"}], ["UpdateRecord", "_grist_Tables_column", 24, {"type": "Bool"}], ] @@ -324,14 +324,14 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "numeric", {"type": "Int"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 16, 17, 18, 19], - {"numeric": [0, 1, 1509556595, 8, 0, 1, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 16, 19], + {"numeric": [0, 1, 8, None]}], ["UpdateRecord", "_grist_Tables_column", 22, {"type": "Int"}], ["UpdateRecord", "Formulas", 1, {"division": 0}], ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 16, 17, 18, 19], - {"numeric": [False, True, 1509556595.0, 8.153, 0.0, 1.0, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 16, 19], + {"numeric": [False, True, 8.153, ""]}], ["ModifyColumn", "Types", "numeric", {"type": "Numeric"}], ["UpdateRecord", "_grist_Tables_column", 22, {"type": "Numeric"}], ["UpdateRecord", "Formulas", 1, {"division": 0.5}], @@ -367,13 +367,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "date", {"type": "Int"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 16, 17, 18, 19], - {"date": [0, 1, 1509556595, 8, 0, 1, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 16, 19], + {"date": [0, 1, 8, None]}], ["UpdateRecord", "_grist_Tables_column", 25, {"type": "Int"}] ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 16, 17, 18, 19], - {"date": [False, True, 1509556595.0, 8.153, 0.0, 1.0, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 16, 19], + {"date": [False, True, 8.153, ""]}], ["ModifyColumn", "Types", "date", {"type": "Date"}], ["UpdateRecord", "_grist_Tables_column", 25, {"type": "Date"}] ] @@ -509,13 +509,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "text", {"type": "Date"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"text": [0.0, 1.0, 1509556595.0, 0.0, 1.0, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"text": [0.0, 1.0, None]}], ["UpdateRecord", "_grist_Tables_column", 21, {"type": "Date"}], ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"text": [False, True, 1509556595, 0, 1, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"text": [False, True, ""]}], ["ModifyColumn", "Types", "text", {"type": "Text"}], ["UpdateRecord", "_grist_Tables_column", 21, {"type": "Text"}], ] @@ -545,13 +545,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "int", {"type": "Date"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"int": [0.0, 1.0, 1509556595.0, 0.0, 1.0, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"int": [0.0, 1.0, None]}], ["UpdateRecord", "_grist_Tables_column", 23, {"type": "Date"}], ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"int": [False, True, 1509556595, 0, 1, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 19], + {"int": [False, True, ""]}], ["ModifyColumn", "Types", "int", {"type": "Int"}], ["UpdateRecord", "_grist_Tables_column", 23, {"type": "Int"}], ] @@ -562,13 +562,13 @@ class TestTypes(test_engine.EngineTestCase): self.assertPartialOutActions(out_actions, { "stored": [ ["ModifyColumn", "Types", "bool", {"type": "Date"}], - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"bool": [0.0, 1.0, 1509556595.0, 0.0, 1.0, None]}], + ["BulkUpdateRecord", "Types", [13, 14, 17, 18, 19], + {"bool": [0.0, 1.0, 0.0, 1.0, None]}], ["UpdateRecord", "_grist_Tables_column", 24, {"type": "Date"}] ], "undo": [ - ["BulkUpdateRecord", "Types", [13, 14, 15, 17, 18, 19], - {"bool": [False, True, 1509556595, False, True, ""]}], + ["BulkUpdateRecord", "Types", [13, 14, 17, 18, 19], + {"bool": [False, True, False, True, ""]}], ["ModifyColumn", "Types", "bool", {"type": "Bool"}], ["UpdateRecord", "_grist_Tables_column", 24, {"type": "Bool"}] ] diff --git a/sandbox/grist/testscript.json b/sandbox/grist/testscript.json index 1d31bd4a..47ce3c00 100644 --- a/sandbox/grist/testscript.json +++ b/sandbox/grist/testscript.json @@ -262,8 +262,8 @@ ["ModifyColumn", "Schools", "numStudents", {"type": "Numeric"}], // Record 13 is not updated since it can't be properly converted. - ["BulkUpdateRecord", "Schools", [1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15], - {"numStudents": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e+27, 1000000.0]}], + // Other records aren't updated because the converted value is equivalent for JSON and DB. + ["UpdateRecord", "Schools", 14, {"numStudents": 1e+27}], ["UpdateRecord", "_grist_Tables_column", 30, {"type": "Numeric"}], ["AddRecord", "Schools", 16, {"numStudents": "@+Infinity"}], ["BulkUpdateRecord", "Schools", [14, 15, 16], {"name": ["14$\\$$'\\'", "15$\\$$'\\'", "16$\\$$'\\'"]}] @@ -1445,7 +1445,6 @@ "addr = $university.address\naddr.stateName if addr.country == 'US' else addr.region"] }], ["ModifyColumn", "Address", "stateName", {"type": "Numeric"}], - ["UpdateRecord", "Address", 2, {"stateName": 73.0}], ["UpdateRecord", "_grist_Tables_column", 27, {"type": "Numeric"}] ], "undo": [ @@ -1458,7 +1457,6 @@ "formula": ["", "addr = $university.address\naddr.state if addr.country == 'US' else addr.region"] }], - ["UpdateRecord", "Address", 2, {"stateName": 73}], ["ModifyColumn", "Address", "stateName", {"type": "Int"}], ["UpdateRecord", "_grist_Tables_column", 27, {"type": "Int"}] ]