This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch py-bigdecimal-to-decimal in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit be11ef7baa8e77fee6aae49cc37e6c8a176d2c3b Author: xiazcy <[email protected]> AuthorDate: Thu Oct 30 15:43:14 2025 -0700 Experiment with everything deserialized to decimal.Decimal, but keep statics.BigDecimal as utility class, with serializers for both Decimal and BigDecimal in gremlin-python --- .../src/main/python/gremlin_python/statics.py | 3 +- .../gremlin_python/structure/io/graphbinaryV1.py | 27 ++++++++++++++++- .../gremlin_python/structure/io/graphsonV2d0.py | 35 ++++++++++++++++++++-- .../gremlin_python/structure/io/graphsonV3d0.py | 34 ++++++++++++++++++++- .../src/main/python/radish/feature_steps.py | 5 ++-- .../tests/structure/io/test_functionalityio.py | 6 ++-- .../tests/structure/io/test_graphbinaryV1.py | 13 ++++++-- .../python/tests/structure/io/test_graphsonV2d0.py | 9 +++--- .../python/tests/structure/io/test_graphsonV3d0.py | 9 +++--- 9 files changed, 120 insertions(+), 21 deletions(-) diff --git a/gremlin-python/src/main/python/gremlin_python/statics.py b/gremlin-python/src/main/python/gremlin_python/statics.py index 8f791b0845..ed7e929bef 100644 --- a/gremlin-python/src/main/python/gremlin_python/statics.py +++ b/gremlin-python/src/main/python/gremlin_python/statics.py @@ -85,7 +85,8 @@ class GremlinType(object): class BigDecimal(object): """ - Provides a way to represent a BigDecimal for Gremlin. + Provides a way to represent a BigDecimal in Gremlin for serialization of decimal.Decimal only. Not round-trippable, + use decimal.Decimal for proper representation and for serialization round-tripping. """ def __init__(self, scale, unscaled_value): self.scale = scale diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py index 4bfa71e43a..f8b50aef86 100644 --- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py +++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py @@ -25,6 +25,7 @@ import io import struct from collections import OrderedDict import logging +from decimal import Decimal from struct import pack, unpack from aenum import Enum @@ -32,7 +33,7 @@ from datetime import timedelta from gremlin_python import statics from gremlin_python.statics import FloatType, BigDecimal, FunctionType, ShortType, IntType, LongType, BigIntType, \ TypeType, DictType, ListType, SetType, SingleByte, ByteBufferType, GremlinType, \ - SingleChar + SingleChar, bigdecimal from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Cardinality, Column, Direction, DT, GType, \ Merge,Operator, Order, Pick, Pop, P, Scope, TextP, Traversal, Traverser, \ TraversalStrategy, T @@ -479,6 +480,30 @@ class BigDecimalIO(_GraphBinaryTypeIO): return cls.is_null(buff, reader, lambda b, r: cls._read(b), nullable) +class DecimalIO(_GraphBinaryTypeIO): + + python_type = Decimal + graphbinary_type = DataType.bigdecimal + + @classmethod + def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True): + cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend) + bd_obj = bigdecimal(obj) + to_extend.extend(int32_pack(bd_obj.scale)) + return BigIntIO.write_bigint(bd_obj.unscaled_value, to_extend) + + @classmethod + def _read(cls, buff): + scale = int32_unpack(buff.read(4)) + unscaled_value = BigIntIO.read_bigint(buff) + bg_obj = BigDecimal(scale, unscaled_value) + return bg_obj.value + + @classmethod + def objectify(cls, buff, reader, nullable=False): + return cls.is_null(buff, reader, lambda b, r: cls._read(b), nullable) + + class CharIO(_GraphBinaryTypeIO): python_type = SingleChar graphbinary_type = DataType.char diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py index f08e8ab72d..57cd3e5e5f 100644 --- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py +++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py @@ -454,12 +454,43 @@ class BigDecimalIO(_NumberIO): @classmethod def dictify(cls, n, writer): - print(n) return GraphSONUtil.typed_value(cls.graphson_base_type, str(n.value), "gx") @classmethod def objectify(cls, v, _): - return bigdecimal(v) + bd_v = bigdecimal(v) + return bd_v.value + + +class DecimalIO(_NumberIO): + python_type = Decimal + graphson_type = "gx:BigDecimal" + graphson_base_type = "BigDecimal" + + @classmethod + def dictify(cls, n, writer): + if isinstance(n, bool): # because isinstance(False, int) and isinstance(True, int) + return n + elif math.isnan(n): + return GraphSONUtil.typed_value(cls.graphson_base_type, "NaN", "gx") + elif math.isinf(n) and n > 0: + return GraphSONUtil.typed_value(cls.graphson_base_type, "Infinity", "gx") + elif math.isinf(n) and n < 0: + return GraphSONUtil.typed_value(cls.graphson_base_type, "-Infinity", "gx") + else: + return GraphSONUtil.typed_value(cls.graphson_base_type, str(n), "gx") + + @classmethod + def objectify(cls, v, _): + if isinstance(v, str): + if v == 'NaN': + return Decimal('nan') + elif v == "Infinity": + return Decimal('inf') + elif v == "-Infinity": + return Decimal('-inf') + + return Decimal(v) class DoubleIO(FloatIO): diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py index 99ed150dae..7fc74d78ef 100644 --- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py +++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py @@ -555,7 +555,39 @@ class BigDecimalIO(_NumberIO): @classmethod def objectify(cls, v, _): - return bigdecimal(v) + bd_v = bigdecimal(v) + return bd_v.value + + +class DecimalIO(_NumberIO): + python_type = Decimal + graphson_type = "gx:BigDecimal" + graphson_base_type = "BigDecimal" + + @classmethod + def dictify(cls, n, writer): + if isinstance(n, bool): # because isinstance(False, int) and isinstance(True, int) + return n + elif math.isnan(n): + return GraphSONUtil.typed_value(cls.graphson_base_type, "NaN", "gx") + elif math.isinf(n) and n > 0: + return GraphSONUtil.typed_value(cls.graphson_base_type, "Infinity", "gx") + elif math.isinf(n) and n < 0: + return GraphSONUtil.typed_value(cls.graphson_base_type, "-Infinity", "gx") + else: + return GraphSONUtil.typed_value(cls.graphson_base_type, str(n), "gx") + + @classmethod + def objectify(cls, v, _): + if isinstance(v, str): + if v == 'NaN': + return Decimal('nan') + elif v == "Infinity": + return Decimal('inf') + elif v == "-Infinity": + return Decimal('-inf') + + return Decimal(v) class DoubleIO(FloatIO): diff --git a/gremlin-python/src/main/python/radish/feature_steps.py b/gremlin-python/src/main/python/radish/feature_steps.py index c8765d6da5..a68f773515 100644 --- a/gremlin-python/src/main/python/radish/feature_steps.py +++ b/gremlin-python/src/main/python/radish/feature_steps.py @@ -262,8 +262,9 @@ def _convert(val, ctx): return float("-inf") elif isinstance(val, str) and re.match(r"^d\[.*\]\.[bsilfdn]$", val): # parse numeric return float(val[2:-3]) if val[2:-3].__contains__(".") else long(val[2:-3]) - elif isinstance(val, str) and re.match(r"^d\[.*\]\.m$", val): # parse bigDecimal - return bigdecimal(val[2:-3]) + elif isinstance(val, str) and re.match(r"^d\[.*\]\.m$", val): # parse BigDecimal (into decimal.Decimal) + bd_val = bigdecimal(val[2:-3]) + return bd_val.value elif isinstance(val, str) and re.match(r"^v\[.*\]\.id$", val): # parse vertex id return __find_cached_element(ctx, graph_name, val[2:-4], "v").id elif isinstance(val, str) and re.match(r"^v\[.*\]\.sid$", val): # parse vertex id as string diff --git a/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py b/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py index d6effb4813..2ecee51b8b 100644 --- a/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py +++ b/gremlin-python/src/main/python/tests/structure/io/test_functionalityio.py @@ -19,6 +19,7 @@ under the License. import datetime import uuid +from decimal import Decimal from gremlin_python.driver.serializer import GraphSONSerializersV2d0, GraphBinarySerializersV1 from gremlin_python.process.anonymous_traversal import traversal @@ -192,9 +193,8 @@ def test_bigdecimal(remote_connection): vid = resp[0].id try: bigdecimal_prop = g.V(vid).properties('bigdecimal').toList()[0] - assert isinstance(bigdecimal_prop.value, BigDecimal) - assert bigdecimal_prop.value.scale == bigdecimal.scale - assert bigdecimal_prop.value.unscaled_value == bigdecimal.unscaled_value + assert isinstance(bigdecimal_prop.value, Decimal) + assert bigdecimal_prop.value == bigdecimal.value finally: g.V(vid).drop().iterate() diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py b/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py index 102be4997a..cb4de18ad9 100644 --- a/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py +++ b/gremlin-python/src/main/python/tests/structure/io/test_graphbinaryV1.py @@ -21,7 +21,9 @@ import uuid import math from datetime import datetime, timedelta, timezone -from gremlin_python.statics import long, bigint, BigDecimal, SingleByte, SingleChar, ByteBufferType, timestamp +from decimal import Decimal + +from gremlin_python.statics import long, bigint, BigDecimal, SingleByte, SingleChar, ByteBufferType, timestamp, bigdecimal from gremlin_python.structure.graph import Vertex, Edge, Property, VertexProperty, Path from gremlin_python.structure.io.graphbinaryV1 import GraphBinaryWriter, GraphBinaryReader from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Merge, Direction @@ -79,8 +81,13 @@ class TestGraphBinaryWriter(object): def test_bigdecimal(self): x = BigDecimal(100, 234) output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) - assert x.scale == output.scale - assert x.unscaled_value == output.unscaled_value + assert x.scale == bigdecimal(output).scale + assert x.unscaled_value == bigdecimal(output).unscaled_value + + def test_decimal(self): + x = Decimal(100) + output = self.graphbinary_reader.read_object(self.graphbinary_writer.write_object(x)) + assert x == output def test_date(self): x = datetime(2016, 12, 14, 16, 14, 36, 295000) diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py index 5eaad833a0..81932d58b8 100644 --- a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py +++ b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py @@ -107,15 +107,15 @@ class TestGraphSONReader: "@type": "gx:BigDecimal", "@value": 31.2 })) - assert isinstance(x, BigDecimal) - assert bigdecimal(31.2) == x + assert isinstance(x, Decimal) + assert Decimal(31.2) == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigDecimal", "@value": 123456789987654321123456789987654321 })) - assert isinstance(x, BigDecimal) - assert bigdecimal('123456789987654321123456789987654321') == x + assert isinstance(x, Decimal) + assert Decimal('123456789987654321123456789987654321') == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigInteger", @@ -372,6 +372,7 @@ class TestGraphSONWriter: assert {"@type": "g:Double", "@value": "Infinity"} == json.loads(self.graphson_writer.write_object(float('inf'))) assert {"@type": "g:Double", "@value": "-Infinity"} == json.loads(self.graphson_writer.write_object(float('-inf'))) assert {"@type": "gx:BigDecimal", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(bigdecimal('123456789987654321123456789987654321'))) + assert {"@type": "gx:BigDecimal", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(Decimal('123456789987654321123456789987654321'))) assert {"@type": "gx:BigInteger", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(long(123456789987654321123456789987654321))) assert {"@type": "gx:BigInteger", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(123456789987654321123456789987654321)) assert """true""" == self.graphson_writer.write_object(True) diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py index e882f5ca67..6792678b31 100644 --- a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py +++ b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py @@ -152,15 +152,15 @@ class TestGraphSONReader: "@type": "gx:BigDecimal", "@value": 31.2 })) - assert isinstance(x, BigDecimal) - assert bigdecimal(31.2) == x + assert isinstance(x, Decimal) + assert Decimal(31.2) == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigDecimal", "@value": 123456789987654321123456789987654321 })) - assert isinstance(x, BigDecimal) - assert bigdecimal('123456789987654321123456789987654321') == x + assert isinstance(x, Decimal) + assert Decimal('123456789987654321123456789987654321') == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigInteger", @@ -431,6 +431,7 @@ class TestGraphSONWriter: assert {"@type": "g:Double", "@value": "Infinity"} == json.loads(self.graphson_writer.write_object(float('inf'))) assert {"@type": "g:Double", "@value": "-Infinity"} == json.loads(self.graphson_writer.write_object(float('-inf'))) assert {"@type": "gx:BigDecimal", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(bigdecimal('123456789987654321123456789987654321'))) + assert {"@type": "gx:BigDecimal", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(Decimal('123456789987654321123456789987654321'))) assert {"@type": "gx:BigInteger", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(long(123456789987654321123456789987654321))) assert {"@type": "gx:BigInteger", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(123456789987654321123456789987654321)) assert """true""" == self.graphson_writer.write_object(True)
