TINKERPOP-1857 Improved handling of empty graph in python glv tests Caches were hardcoded to the modern graph. For empty graph this caused hassles because the cache couldn't be initialized until after the graph initializer step was executed. Still a bit messy due to cut/paste issues. Need to clean that up at a later point.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/a1d04032 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/a1d04032 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/a1d04032 Branch: refs/heads/TINKERPOP-1857 Commit: a1d040329e1da734f1af46513e36fd941f3e15b0 Parents: 1425f29 Author: Stephen Mallette <sp...@genoprime.com> Authored: Thu Dec 28 11:18:38 2017 -0500 Committer: Stephen Mallette <sp...@genoprime.com> Committed: Wed Feb 14 15:34:09 2018 -0500 ---------------------------------------------------------------------- .../src/main/jython/radish/feature_steps.py | 78 +++++++++++++++----- .../src/main/jython/radish/terrain.py | 7 +- 2 files changed, 63 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a1d04032/gremlin-python/src/main/jython/radish/feature_steps.py ---------------------------------------------------------------------- diff --git a/gremlin-python/src/main/jython/radish/feature_steps.py b/gremlin-python/src/main/jython/radish/feature_steps.py index 3de641e..e247b25 100644 --- a/gremlin-python/src/main/jython/radish/feature_steps.py +++ b/gremlin-python/src/main/jython/radish/feature_steps.py @@ -36,6 +36,11 @@ regex_or = re.compile(r"([(.,\s])or\(") regex_true = re.compile(r"(true)") regex_false = re.compile(r"(false)") +outV = __.outV +label = __.label +inV = __.inV +project = __.project +tail = __.tail ignores = [ "g.V(v1Id).out().inject(v2).values(\"name\")" # bug in attachment won't connect v2 @@ -44,6 +49,7 @@ ignores = [ @given("the {graph_name:w} graph") def choose_graph(step, graph_name): + step.context.graph_name = graph_name step.context.g = Graph().traversal().withRemote(step.context.remote_conn[graph_name]) @@ -54,7 +60,12 @@ def initialize_graph(step): # just be sure that the traversal returns something to prove that it worked to some degree. probably # is overkill to try to assert the complete success of this init operation. presumably the test # suite would fail elsewhere if this didn't work which would help identify a problem. - assert len(traversal.toList()) > 0 + result = traversal.toList() + assert len(result) > 0 + + # add the first result - if a map - to the bindings. this is useful for cases when parameters for + # the test traversal need to come from the original graph initializer (i.e. a new graph is created + # and you need the id of a vertex from that graph) @given("an unsupported test") @@ -127,37 +138,38 @@ def nothing_happening(step): def _convert(val, ctx): - if isinstance(val, dict): # convert dictionary keys/values + graph_name = ctx.graph_name + if isinstance(val, dict): # convert dictionary keys/values n = {} for key, value in val.items(): n[_convert(key, ctx)] = _convert(value, ctx) return n - elif isinstance(val, unicode): # convert annoying python 2.x unicode nonsense + elif isinstance(val, unicode): # convert annoying python 2.x unicode nonsense return _convert(val.encode('utf-8'), ctx) - elif isinstance(val, str) and re.match("^l\[.*\]$", val): # parse list + elif isinstance(val, str) and re.match("^l\[.*\]$", val): # parse list return list(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) - elif isinstance(val, str) and re.match("^s\[.*\]$", val): # parse set + elif isinstance(val, str) and re.match("^s\[.*\]$", val): # parse set return set(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) elif isinstance(val, str) and re.match("^d\[.*\]\.[ilfdm]$", 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("^v\[.*\]\.id$", val): # parse vertex id - return ctx.lookup_v["modern"][val[2:-4]].id - elif isinstance(val, str) and re.match("^v\[.*\]\.sid$", val): # parse vertex id as string - return ctx.lookup_v["modern"][val[2:-5]].id - elif isinstance(val, str) and re.match("^v\[.*\]$", val): # parse vertex - return ctx.lookup_v["modern"][val[2:-1]] - elif isinstance(val, str) and re.match("^e\[.*\]\.id$", val): # parse edge id - return ctx.lookup_e["modern"][val[2:-4]].id - elif isinstance(val, str) and re.match("^e\[.*\]\.sid$", val): # parse edge id as string - return ctx.lookup_e["modern"][val[2:-5]].id - elif isinstance(val, str) and re.match("^e\[.*\]$", val): # parse edge - return ctx.lookup_e["modern"][val[2:-1]] - elif isinstance(val, str) and re.match("^m\[.*\]$", val): # parse json as a map + elif isinstance(val, str) and re.match("^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("^v\[.*\]\.sid$", val): # parse vertex id as string + return __find_cached_element(ctx, graph_name, val[2:-5], "v").id + elif isinstance(val, str) and re.match("^v\[.*\]$", val): # parse vertex + return __find_cached_element(ctx, graph_name, val[2:-1], "v") + elif isinstance(val, str) and re.match("^e\[.*\]\.id$", val): # parse edge id + return __find_cached_element(ctx, graph_name, val[2:-4], "e").id + elif isinstance(val, str) and re.match("^e\[.*\]\.sid$", val): # parse edge id as string + return __find_cached_element(ctx, graph_name, val[2:-5], "e").id + elif isinstance(val, str) and re.match("^e\[.*\]$", val): # parse edge + return __find_cached_element(ctx, graph_name, val[2:-1], "e") + elif isinstance(val, str) and re.match("^m\[.*\]$", val): # parse json as a map return _convert(json.loads(val[2:-1].replace('\\"', '"')), ctx) - elif isinstance(val, str) and re.match("^p\[.*\]$", val): # parse path + elif isinstance(val, str) and re.match("^p\[.*\]$", val): # parse path path_objects = list(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) return Path([set([])], path_objects) - elif isinstance(val, str) and re.match("^c\[.*\]$", val): # parse lambda/closure + elif isinstance(val, str) and re.match("^c\[.*\]$", val): # parse lambda/closure return lambda: (val[2:-1], "gremlin-groovy") elif isinstance(val, str) and re.match("^t\[.*\]$", val): # parse instance of T enum return T[val[2:-1]] @@ -165,6 +177,15 @@ def _convert(val, ctx): return val +def __find_cached_element(ctx, graph_name, identifier, element_type): + if graph_name == "empty": + cache = __create_lookup_v(ctx.remote_conn["empty"]) if element_type == "v" else __create_lookup_e(ctx.remote_conn["empty"]) + else: + cache = ctx.lookup_v[graph_name] if element_type == "v" else ctx.lookup_e[graph_name] + + return cache[identifier] + + def _convert_results(val): if isinstance(val, Path): # kill out labels as they aren't in the assertion logic @@ -234,3 +255,20 @@ def _make_traversal(g, traversal_string, params): b.update(params) return eval(_translate(traversal_string), b) + + +def __create_lookup_v(remote): + g = Graph().traversal().withRemote(remote) + + # hold a map of name/vertex for use in asserting results + return g.V().group().by('name').by(tail()).next() + + +def __create_lookup_e(remote): + g = Graph().traversal().withRemote(remote) + + # hold a map of the "name"/edge for use in asserting results - "name" in this context is in the form of + # outgoingV-label->incomingV + return g.E().group(). \ + by(lambda: ("it.outVertex().value('name') + '-' + it.label() + '->' + it.inVertex().value('name')", "gremlin-groovy")). \ + by(tail()).next() http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a1d04032/gremlin-python/src/main/jython/radish/terrain.py ---------------------------------------------------------------------- diff --git a/gremlin-python/src/main/jython/radish/terrain.py b/gremlin-python/src/main/jython/radish/terrain.py index 0c749eb..9aed647 100644 --- a/gremlin-python/src/main/jython/radish/terrain.py +++ b/gremlin-python/src/main/jython/radish/terrain.py @@ -17,7 +17,6 @@ specific language governing permissions and limitations under the License. ''' -import re from gremlin_python.structure.graph import Graph from gremlin_python.process.graph_traversal import __ from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection @@ -58,6 +57,10 @@ def prepare_static_traversal_source(features, marker): scenario.context.lookup_v[graph_name] = cache[graph_name]["lookup_v"] scenario.context.lookup_e[graph_name] = cache[graph_name]["lookup_e"] + # setup the "empty" lookups as needed + scenario.context.lookup_v["empty"] = {} + scenario.context.lookup_e["empty"] = {} + @before.each_scenario def prepare_traversal_source(scenario): @@ -97,4 +100,4 @@ def __create_lookup_e(remote): # outgoingV-label->incomingV return g.E().group(). \ by(lambda: ("it.outVertex().value('name') + '-' + it.label() + '->' + it.inVertex().value('name')", "gremlin-groovy")). \ - by(tail()).next() + by(tail()).next() \ No newline at end of file