This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch backport-python-bigdecimal in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit af755ab31c22481166157dfb13844d7c3214b7bb Author: xiazcy <[email protected]> AuthorDate: Wed Oct 29 14:38:17 2025 -0700 Fixed `statics.BigDecimal` in `gremlin-python` to properly calculate `scale` and `unscaled_value`, and added `value` attribute to return a `decimal.Decimal` representation. Updated `GraphSON` in `gremlin-python` to return `statics.BigDecimal` instead of `decimal.Decimal` for consistency with `GraphBinary`. --- CHANGELOG.asciidoc | 2 ++ .../translator/PythonTranslateVisitor.java | 6 +++- .../language/translator/GremlinTranslatorTest.java | 6 ++-- .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 4 +-- gremlin-go/driver/cucumber/gremlin.go | 4 +-- .../gremlin-javascript/test/cucumber/gremlin.js | 4 +-- gremlin-python/build/generate.groovy | 2 +- gremlin-python/docker-compose.yml | 4 +-- .../src/main/python/gremlin_python/statics.py | 40 +++++++++++++++++++++- .../gremlin_python/structure/io/graphsonV2d0.py | 27 ++++----------- .../gremlin_python/structure/io/graphsonV3d0.py | 26 +++----------- .../src/main/python/radish/feature_steps.py | 6 ++-- gremlin-python/src/main/python/radish/gremlin.py | 22 ++++++------ .../python/tests/structure/io/test_graphsonV2d0.py | 34 +++--------------- .../python/tests/structure/io/test_graphsonV3d0.py | 34 +++--------------- .../src/main/python/tests/test_statics.py | 18 ++++++++++ 16 files changed, 112 insertions(+), 127 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index c01b80c945..4d921298b8 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -50,6 +50,8 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>. * Added missing strategies to `strategies.py` in `gremlin-python`. * Fixed fully qualified class names for `TraversalStrategy` names in `gremlin-dotnet`. * Updated `OptionsStrategy` in `gremlin-python` to take options directly as keyword arguments. +* Fixed `statics.BigDecimal` implementation in `gremlin-python` to properly calculate `scale` and `unscaled_value`, and added `value` attribute to return a `decimal.Decimal` representation. +* Updated `GraphSON` in `gremlin-python` to return `statics.BigDecimal` instead of `decimal.Decimal` for consistency with `GraphBinary`. * Added static `instance()` method to `ElementIdStrategy` to an instance with the default configuration. * Updated `ElementIdStrategy.getConfiguration()` to help with serialization. * Added grammar-based `Translator` for all languages including explicit ones for Java and anonymization. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java index 5c7e4e0209..a59f915e20 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java @@ -246,11 +246,15 @@ public class PythonTranslateVisitor extends AbstractTranslateVisitor { final int lastCharIndex = floatLiteral.length() - 1; final char lastChar = floatLiteral.charAt(lastCharIndex); switch (lastChar) { - case 'm': case 'f': case 'd': sb.append(floatLiteral, 0, lastCharIndex); break; + case 'm': + sb.append("bigdecimal("); + sb.append(floatLiteral, 0, lastCharIndex); + sb.append(")"); + break; default: // everything else just goes as specified sb.append(floatLiteral); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java index 1c743e62c6..f1859c7a97 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java @@ -331,7 +331,7 @@ public class GremlinTranslatorTest { "g.with('x', 1.0g)", "g.with(\"x\", new BigDecimal(\"1.0\"))", "g.with_(\"x\", 1.0)", - "g.with_('x', 1.0)"}, + "g.with_('x', bigdecimal(1.0))"}, {"g.with('x', -1.0m)", null, "g.with(string0, bigdecimal0)", @@ -340,7 +340,7 @@ public class GremlinTranslatorTest { "g.with('x', -1.0g)", "g.with(\"x\", new BigDecimal(\"-1.0\"))", "g.with_(\"x\", -1.0)", - "g.with_('x', -1.0)"}, + "g.with_('x', bigdecimal(-1.0))"}, {"g.with('x', -1.0M)", "g.with('x', -1.0m)", "g.with(string0, bigdecimal0)", @@ -349,7 +349,7 @@ public class GremlinTranslatorTest { "g.with('x', -1.0g)", "g.with(\"x\", new BigDecimal(\"-1.0\"))", "g.with_(\"x\", -1.0)", - "g.with_('x', -1.0)"}, + "g.with_('x', bigdecimal(-1.0))"}, {"g.with('x', 1b)", null, "g.with(string0, byte0)", diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index abb46e1776..0cfb61946e 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -879,8 +879,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV().As("first").Repeat(__.AddE("next").To(__.AddV()).InV()).Times(5).AddE("next").To(__.Select<object>("first")), (g,p) =>g.V(), (g,p) =>g.E(), (g,p) =>g.E().HasLabel("next"), (g,p) =>g.V().Limit<object>(1).BothE(), (g,p) =>g.V().Limit<object>(1).InE(), (g,p) =>g.V().Limit<object>(1).OutE()}}, {"g_V_hasXname_markoX_asXaX_outEXcreatedX_asXbX_inV_addEXselectXbX_labelX_toXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("softwar [...] {"g_addEXV_outE_label_groupCount_orderXlocalX_byXvalues_descX_selectXkeysX_unfold_limitX1XX_fromXV_hasXname_vadasXX_toXV_hasXname_lopXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("nam [...] - {"g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripple" [...] - {"g_addEXknowsvarX_fromXaX_toXbX_propertyXweight_0_1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripp [...] + {"g_addEXknowsX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("nam [...] + {"g_addEXknowsvarX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property(" [...] {"g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripple").P [...] {"g_addEXknowsXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27), (g,p) =>g.AddE("knows").Property("weight", null).From(__.V().Has("name", "marko")).To(__.V().Has("name", "vadas")), (g,p) =>g.E().Has("knows", "weight", (object) null)}}, {"g_addEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27), (g,p) =>g.AddE((string) p["xx1"]).Property("weight", null).From(__.V().Has("name", "marko")).To(__.V().Has("name", "vadas")), (g,p) =>g.E().Has("knows", "weight", (object) null)}}, diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go index 5f80cdf7bc..31fbd8a517 100644 --- a/gremlin-go/driver/cucumber/gremlin.go +++ b/gremlin-go/driver/cucumber/gremlin.go @@ -849,8 +849,8 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[ "g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV().As("first").Repeat(gremlingo.T__.AddE("next").To(gremlingo.T__.AddV()).InV()).Times(5).AddE("next").To(gremlingo.T__.Select("first"))}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p ma [...] "g_V_hasXname_markoX_asXaX_outEXcreatedX_asXbX_inV_addEXselectXbX_labelX_toXaX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("softw [...] "g_addEXV_outE_label_groupCount_orderXlocalX_byXvalues_descX_selectXkeysX_unfold_limitX1XX_fromXV_hasXname_vadasXX_toXV_hasXname_lopXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("n [...] - "g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "rippl [...] - "g_addEXknowsvarX_fromXaX_toXbX_propertyXweight_0_1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ri [...] + "g_addEXknowsX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("n [...] + "g_addEXknowsvarX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property [...] "g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).As("marko").AddV("person").Property("name", "vadas").Property("age", 27).As("vadas").AddV("software").Property("name", "lop").Property("lang", "java").As("lop").AddV("person").Property("name", "josh").Property("age", 32).As("josh").AddV("software").Property("name", "ripple") [...] "g_addEXknowsXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddE("knows").Property("weight", nil).From(gremlingo.T__.V().Has("name", "ma [...] "g_addEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddV("person").Property("name", "marko").Property("age", 29).AddV("person").Property("name", "vadas").Property("age", 27)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.AddE(p["xx1"]).Property("weight", nil).From(gremlingo.T__.V().Has("name", [...] diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index 72140c39ef..b63e782dd5 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -880,8 +880,8 @@ const gremlins = { g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX: [function({g}) { return g.addV().as("first").repeat(__.addE("next").to(__.addV()).inV()).times(5).addE("next").to(__.select("first")) }, function({g}) { return g.V() }, function({g}) { return g.E() }, function({g}) { return g.E().hasLabel("next") }, function({g}) { return g.V().limit(1).bothE() }, function({g}) { return g.V().limit(1).inE() }, function({g}) { return g.V().limit(1).outE() }], g_V_hasXname_markoX_asXaX_outEXcreatedX_asXbX_inV_addEXselectXbX_labelX_toXaX: [function({g, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").ad [...] g_addEXV_outE_label_groupCount_orderXlocalX_byXvalues_descX_selectXkeysX_unfold_limitX1XX_fromXV_hasXname_vadasXX_toXV_hasXname_lopXX: [function({g, vid2}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("n [...] - g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X: [function({g, xx1, vid6, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("person").prope [...] - g_addEXknowsvarX_fromXaX_toXbX_propertyXweight_0_1X: [function({g, xx1, vid6, xx2, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("person [...] + g_addEXknowsX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X: [function({g, xx1, vid6, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("pe [...] + g_addEXknowsvarX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X: [function({g, xx1, vid6, xx2, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple"). [...] g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X: [function({g, xx1, vid6, vid1}) { return g.addV("person").property("name", "marko").property("age", 29).as("marko").addV("person").property("name", "vadas").property("age", 27).as("vadas").addV("software").property("name", "lop").property("lang", "java").as("lop").addV("person").property("name", "josh").property("age", 32).as("josh").addV("software").property("name", "ripple").property("lang", "java").as("ripple").addV("person").property [...] g_addEXknowsXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX: [function({g}) { return g.addV("person").property("name", "marko").property("age", 29).addV("person").property("name", "vadas").property("age", 27) }, function({g}) { return g.addE("knows").property("weight", null).from_(__.V().has("name", "marko")).to(__.V().has("name", "vadas")) }, function({g}) { return g.E().has("knows", "weight", null) }], g_addEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX: [function({g, xx1}) { return g.addV("person").property("name", "marko").property("age", 29).addV("person").property("name", "vadas").property("age", 27) }, function({g, xx1}) { return g.addE(xx1).property("weight", null).from_(__.V().has("name", "marko")).to(__.V().has("name", "vadas")) }, function({g, xx1}) { return g.E().has("knows", "weight", null) }], diff --git a/gremlin-python/build/generate.groovy b/gremlin-python/build/generate.groovy index b6a6cf33f2..ae4e6fc425 100644 --- a/gremlin-python/build/generate.groovy +++ b/gremlin-python/build/generate.groovy @@ -57,7 +57,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer -> 'from radish import world\n' + 'import datetime\n' + 'import uuid\n' + - 'from gremlin_python.statics import long, bigint, GremlinType\n' + + 'from gremlin_python.statics import long, bigint, bigdecimal, GremlinType\n' + 'from gremlin_python.process.anonymous_traversal import traversal\n' + 'from gremlin_python.process.strategies import *\n' + 'from gremlin_python.process.traversal import TraversalStrategy\n' + diff --git a/gremlin-python/docker-compose.yml b/gremlin-python/docker-compose.yml index 8b6117a168..9879ae8a0c 100644 --- a/gremlin-python/docker-compose.yml +++ b/gremlin-python/docker-compose.yml @@ -71,8 +71,8 @@ services: && klist && pip install .[test,kerberos] && pytest - && radish -f dots -e -t -b ./radish ./gremlin-test --tags='not DataBigDecimal' --user-data='serializer=application/vnd.gremlin-v3.0+json' - && radish -f dots -e -t -b ./radish ./gremlin-test --tags='not DataBigDecimal' --user-data='serializer=application/vnd.graphbinary-v1.0'; + && radish -f dots -e -t -b ./radish ./gremlin-test --user-data='serializer=application/vnd.gremlin-v3.0+json' + && radish -f dots -e -t -b ./radish ./gremlin-test --user-data='serializer=application/vnd.graphbinary-v1.0'; EXIT_CODE=$$?; chown -R `stat -c "%u:%g" .` .; exit $$EXIT_CODE" depends_on: gremlin-server-test-python: diff --git a/gremlin-python/src/main/python/gremlin_python/statics.py b/gremlin-python/src/main/python/gremlin_python/statics.py index 598b15d0a7..8f791b0845 100644 --- a/gremlin-python/src/main/python/gremlin_python/statics.py +++ b/gremlin-python/src/main/python/gremlin_python/statics.py @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # - +import decimal from types import FunctionType from aenum import Enum @@ -84,10 +84,48 @@ class GremlinType(object): class BigDecimal(object): + """ + Provides a way to represent a BigDecimal for Gremlin. + """ def __init__(self, scale, unscaled_value): self.scale = scale self.unscaled_value = unscaled_value + @property + def value(self): + self._as_decimal = decimal.Decimal(self.unscaled_value) + precision = len(self._as_decimal.as_tuple().digits) + with decimal.localcontext(decimal.Context(prec=precision)): + return self._as_decimal.scaleb(-self.scale) + + def __eq__(self, other): + if not isinstance(other, BigDecimal): + return False + return self.scale == other.scale and self.unscaled_value == other.unscaled_value + + def __hash__(self): + return hash((self.scale, self.unscaled_value)) + + def __repr__(self): + return f"BigDecimal(scale={self.scale}, unscaled_value={self.unscaled_value})" + + def __str__(self): + return str(self.value) + +""" +Create a BigDecimal from a number that can be converted to a Decimal. Note precision may be lost during the conversion. +""" +def bigdecimal(value): + try: + decimal_value = value if isinstance(value, decimal.Decimal) else decimal.Decimal(str(value)) + scale = -decimal_value.as_tuple().exponent + unscaled_value = int("".join(map(str, decimal_value.as_tuple().digits))) + except TypeError: + raise ValueError("BigDecimal does not support NaN, Infinity or -Infinity") + except Exception as err: + raise ValueError(f'Encountered error: {err}. Value must be able to convert to a Decimal.') + return BigDecimal(scale, unscaled_value if decimal_value >= 0 else -unscaled_value) + staticMethods = {} staticEnums = {} 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 7ebffd01b9..f08e8ab72d 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 @@ -30,7 +30,8 @@ from aenum import Enum from isodate import parse_duration, duration_isoformat from gremlin_python import statics -from gremlin_python.statics import FloatType, FunctionType, ShortType, IntType, LongType, TypeType, SingleByte, ByteBufferType, SingleChar +from gremlin_python.statics import FloatType, FunctionType, ShortType, IntType, LongType, TypeType, SingleByte, \ + ByteBufferType, SingleChar, BigDecimal, bigdecimal from gremlin_python.process.traversal import Binding, Bytecode, P, TextP, Traversal, Traverser, TraversalStrategy from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path from gremlin_python.structure.io.util import SymbolUtil @@ -447,34 +448,18 @@ class FloatIO(_NumberIO): class BigDecimalIO(_NumberIO): - python_type = Decimal + python_type = BigDecimal 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") + print(n) + return GraphSONUtil.typed_value(cls.graphson_base_type, str(n.value), "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) + return bigdecimal(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 66a1fb0d7f..99ed150dae 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 @@ -29,7 +29,8 @@ from aenum import Enum from isodate import parse_duration, duration_isoformat from gremlin_python import statics -from gremlin_python.statics import FloatType, FunctionType, ShortType, IntType, LongType, TypeType, DictType, ListType, SetType, SingleByte, ByteBufferType, SingleChar +from gremlin_python.statics import FloatType, FunctionType, ShortType, IntType, LongType, TypeType, DictType, ListType, \ + SetType, SingleByte, ByteBufferType, SingleChar, BigDecimal, bigdecimal from gremlin_python.process.traversal import Binding, Bytecode, Direction, P, TextP, Traversal, Traverser, TraversalStrategy, T from gremlin_python.structure.graph import Edge, Property, Vertex, VertexProperty, Path from gremlin_python.structure.io.util import HashableDict, SymbolUtil @@ -544,34 +545,17 @@ class FloatIO(_NumberIO): class BigDecimalIO(_NumberIO): - python_type = Decimal + python_type = BigDecimal 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") + return GraphSONUtil.typed_value(cls.graphson_base_type, str(n.value), "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) + return bigdecimal(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 26c3aca1d8..c8765d6da5 100644 --- a/gremlin-python/src/main/python/radish/feature_steps.py +++ b/gremlin-python/src/main/python/radish/feature_steps.py @@ -21,7 +21,7 @@ from datetime import datetime import json import re import uuid -from gremlin_python.statics import long +from gremlin_python.statics import long, bigdecimal from gremlin_python.structure.graph import Path, Vertex from gremlin_python.process.anonymous_traversal import traversal from gremlin_python.process.graph_traversal import __ @@ -260,8 +260,10 @@ def _convert(val, ctx): return float("inf") elif isinstance(val, str) and re.match(r"^d\[-Infinity\]$", val): # parse -inf return float("-inf") - elif isinstance(val, str) and re.match(r"^d\[.*\]\.[bsilfdmn]$", val): # parse numeric + 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"^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/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index 1d4dad5b72..e183fd3dc9 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -27,7 +27,7 @@ from radish import world import datetime import uuid -from gremlin_python.statics import long, bigint, GremlinType +from gremlin_python.statics import long, bigint, bigdecimal, GremlinType from gremlin_python.process.anonymous_traversal import traversal from gremlin_python.process.strategies import * from gremlin_python.process.traversal import TraversalStrategy @@ -852,8 +852,8 @@ world.gremlins = { 'g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX': [(lambda g:g.add_v().as_('first').repeat(__.add_e('next').to(__.add_v()).in_v()).times(5).add_e('next').to(__.select('first'))), (lambda g:g.V()), (lambda g:g.E()), (lambda g:g.E().has_label('next')), (lambda g:g.V().limit(1).both_e()), (lambda g:g.V().limit(1).in_e()), (lambda g:g.V().limit(1).out_e())], 'g_V_hasXname_markoX_asXaX_outEXcreatedX_asXbX_inV_addEXselectXbX_labelX_toXaX': [(lambda g, vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('ripple' [...] 'g_addEXV_outE_label_groupCount_orderXlocalX_byXvalues_descX_selectXkeysX_unfold_limitX1XX_fromXV_hasXname_vadasXX_toXV_hasXname_lopXX': [(lambda g, vid2=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property [...] - 'g_addEXknowsX_fromXaX_toXbX_propertyXweight_0_1X': [(lambda g, xx1=None,vid6=None,vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('ripple').add_v('p [...] - 'g_addEXknowsvarX_fromXaX_toXbX_propertyXweight_0_1X': [(lambda g, xx1=None,vid6=None,xx2=None,vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('rippl [...] + 'g_addEXknowsX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X': [(lambda g, xx1=None,vid6=None,vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('rippl [...] + 'g_addEXknowsvarX_fromXVXvid1XX_toXVXvid6XX_propertyXweight_0_1X': [(lambda g, xx1=None,vid6=None,xx2=None,vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java' [...] 'g_VXaX_addEXknowsX_toXbX_propertyXweight_0_1X': [(lambda g, xx1=None,vid6=None,vid1=None:g.add_v('person').property('name', 'marko').property('age', 29).as_('marko').add_v('person').property('name', 'vadas').property('age', 27).as_('vadas').add_v('software').property('name', 'lop').property('lang', 'java').as_('lop').add_v('person').property('name', 'josh').property('age', 32).as_('josh').add_v('software').property('name', 'ripple').property('lang', 'java').as_('ripple').add_v('pers [...] 'g_addEXknowsXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX': [(lambda g:g.add_v('person').property('name', 'marko').property('age', 29).add_v('person').property('name', 'vadas').property('age', 27)), (lambda g:g.add_e('knows').property('weight', None).from_(__.V().has('name', 'marko')).to(__.V().has('name', 'vadas'))), (lambda g:g.E().has('knows', 'weight', None))], 'g_addEXknowsvarXpropertyXweight_nullXfromXV_hasXname_markoXX_toXV_hasXname_vadasXX': [(lambda g, xx1=None:g.add_v('person').property('name', 'marko').property('age', 29).add_v('person').property('name', 'vadas').property('age', 27)), (lambda g, xx1=None:g.add_e(xx1).property('weight', None).from_(__.V().has('name', 'marko')).to(__.V().has('name', 'vadas'))), (lambda g, xx1=None:g.E().has('knows', 'weight', None))], @@ -1827,14 +1827,14 @@ world.gremlins = { 'InjectXInfX_ltXNegInfX': [(lambda g:g.inject(float('inf')).is_(P.lt(float('-inf'))))], 'InjectXNegInfX_ltXInfX': [(lambda g:g.inject(float('-inf')).is_(P.lt(float('inf'))))], 'InjectXNegInfX_gtXInfX': [(lambda g:g.inject(float('-inf')).is_(P.gt(float('inf'))))], - 'Primitives_Number_eqXbyteX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXshortX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXintX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXlongX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXbigintX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXfloatX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXdoubleX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], - 'Primitives_Number_eqXbigdecimalX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, 1, bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXbyteX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXshortX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXintX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXlongX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXbigintX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXfloatX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXdoubleX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], + 'Primitives_Number_eqXbigdecimalX': [(lambda g, xx1=None:g.inject([1, 1, 1, long(1), 1, 1, 1000, bigdecimal(1), bigint(1)]).unfold().where(__.is_(xx1)))], 'g_V_values_order': [(lambda g:g.V().values().order())], 'g_V_properties_order': [(lambda g:g.V().properties().order())], 'g_V_properties_order_id': [(lambda g:g.V().properties().order().id_())], 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 d6ff7cd7b0..5eaad833a0 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,36 +107,15 @@ class TestGraphSONReader: "@type": "gx:BigDecimal", "@value": 31.2 })) - assert isinstance(x, Decimal) - assert Decimal(31.2) == x + assert isinstance(x, BigDecimal) + assert bigdecimal(31.2) == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigDecimal", "@value": 123456789987654321123456789987654321 })) - assert isinstance(x, Decimal) - assert Decimal('123456789987654321123456789987654321') == x - ## - x = self.graphson_reader.read_object(json.dumps({ - "@type": "gx:BigDecimal", - "@value": "NaN" - })) - assert isinstance(x, Decimal) - assert math.isnan(x) - ## - x = self.graphson_reader.read_object(json.dumps({ - "@type": "gx:BigDecimal", - "@value": "Infinity" - })) - assert isinstance(x, Decimal) - assert math.isinf(x) and x > 0 - ## - x = self.graphson_reader.read_object(json.dumps({ - "@type": "gx:BigDecimal", - "@value": "-Infinity" - })) - assert isinstance(x, Decimal) - assert math.isinf(x) and x < 0 + assert isinstance(x, BigDecimal) + assert bigdecimal('123456789987654321123456789987654321') == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigInteger", @@ -392,10 +371,7 @@ class TestGraphSONWriter: assert {"@type": "g:Double", "@value": "NaN"} == json.loads(self.graphson_writer.write_object(float('nan'))) 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(Decimal('123456789987654321123456789987654321'))) - assert {"@type": "gx:BigDecimal", "@value": "NaN"} == json.loads(self.graphson_writer.write_object(Decimal('nan'))) - assert {"@type": "gx:BigDecimal", "@value": "Infinity"} == json.loads(self.graphson_writer.write_object(Decimal('inf'))) - assert {"@type": "gx:BigDecimal", "@value": "-Infinity"} == json.loads(self.graphson_writer.write_object(Decimal('-inf'))) + assert {"@type": "gx:BigDecimal", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(bigdecimal('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 91c1e6d7a7..e882f5ca67 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,36 +152,15 @@ class TestGraphSONReader: "@type": "gx:BigDecimal", "@value": 31.2 })) - assert isinstance(x, Decimal) - assert Decimal(31.2) == x + assert isinstance(x, BigDecimal) + assert bigdecimal(31.2) == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigDecimal", "@value": 123456789987654321123456789987654321 })) - assert isinstance(x, Decimal) - assert Decimal('123456789987654321123456789987654321') == x - ## - x = self.graphson_reader.read_object(json.dumps({ - "@type": "gx:BigDecimal", - "@value": "NaN" - })) - assert isinstance(x, Decimal) - assert math.isnan(x) - ## - x = self.graphson_reader.read_object(json.dumps({ - "@type": "gx:BigDecimal", - "@value": "Infinity" - })) - assert isinstance(x, Decimal) - assert math.isinf(x) and x > 0 - ## - x = self.graphson_reader.read_object(json.dumps({ - "@type": "gx:BigDecimal", - "@value": "-Infinity" - })) - assert isinstance(x, Decimal) - assert math.isinf(x) and x < 0 + assert isinstance(x, BigDecimal) + assert bigdecimal('123456789987654321123456789987654321') == x ## x = self.graphson_reader.read_object(json.dumps({ "@type": "gx:BigInteger", @@ -451,10 +430,7 @@ class TestGraphSONWriter: assert {"@type": "g:Double", "@value": "NaN"} == json.loads(self.graphson_writer.write_object(float('nan'))) 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(Decimal('123456789987654321123456789987654321'))) - assert {"@type": "gx:BigDecimal", "@value": "NaN"} == json.loads(self.graphson_writer.write_object(Decimal('nan'))) - assert {"@type": "gx:BigDecimal", "@value": "Infinity"} == json.loads(self.graphson_writer.write_object(Decimal('inf'))) - assert {"@type": "gx:BigDecimal", "@value": "-Infinity"} == json.loads(self.graphson_writer.write_object(Decimal('-inf'))) + assert {"@type": "gx:BigDecimal", "@value": "123456789987654321123456789987654321"} == json.loads(self.graphson_writer.write_object(bigdecimal('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/test_statics.py b/gremlin-python/src/main/python/tests/test_statics.py index 2c3f864754..9e840b0631 100644 --- a/gremlin-python/src/main/python/tests/test_statics.py +++ b/gremlin-python/src/main/python/tests/test_statics.py @@ -19,6 +19,8 @@ __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)' +from decimal import Decimal + from gremlin_python import statics from gremlin_python.process.traversal import Cardinality from gremlin_python.process.traversal import P @@ -63,3 +65,19 @@ class TestStatics(object): raise Exception("SingleChar should throw a value error if input is not a single character string") except ValueError: pass + + def test_bigdecimal(self): + assert statics.bigdecimal(1.23456).value == statics.BigDecimal(5,123456).value + assert statics.bigdecimal(-1.23456).value == statics.BigDecimal(5,-123456).value + # make sure the precision isn't changed globally + assert Decimal("123456789").scaleb(-5) == Decimal('1234.56789') + try: + statics.bigdecimal('NaN') + raise Exception("to_bigdecimal should throw a value error with NaN, Infinity or -Infinity") + except ValueError: + pass + try: + statics.bigdecimal('abc') + raise Exception("to_bigdecimal should throw a value error if input is not a convertable to Decimal") + except ValueError: + pass \ No newline at end of file
