diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 72b44557410..b292f8939aa 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -22,6 +22,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima [[release-3-7-6]] === TinkerPop 3.7.6 (NOT OFFICIALLY RELEASED YET) +* Integrated Python driver examples into automated build process to ensure examples remain functional. [[release-3-7-5]] === TinkerPop 3.7.5 (Release Date: November 12, 2025) diff --git a/gremlin-examples/gremlin-python/basic_gremlin.py b/gremlin-examples/gremlin-python/basic_gremlin.py index 256ed984416..c351270de85 100644 --- a/gremlin-examples/gremlin-python/basic_gremlin.py +++ b/gremlin-examples/gremlin-python/basic_gremlin.py @@ -23,15 +23,17 @@ from gremlin_python.process.strategies import * from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection +VERTEX_LABEL = 'person' def main(): - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g') + server_url = 'ws://localhost:8182/gremlin' + rc = DriverRemoteConnection(server_url, 'g') g = traversal().with_remote(rc) # basic Gremlin: adding and retrieving data - v1 = g.add_v('person').property('name', 'marko').next() - v2 = g.add_v('person').property('name', 'stephen').next() - v3 = g.add_v('person').property('name', 'vadas').next() + v1 = g.add_v(VERTEX_LABEL).property('name', 'marko').next() + v2 = g.add_v(VERTEX_LABEL).property('name', 'stephen').next() + v3 = g.add_v(VERTEX_LABEL).property('name', 'vadas').next() # be sure to use a terminating step like next() or iterate() so that the traversal "executes" # iterate() does not return any data and is used to just generate side-effects (i.e. write data to the database) @@ -39,11 +41,11 @@ def main(): g.V(v1).add_e('knows').to(v3).property('weight', 0.75).iterate() # retrieve the data from the "marko" vertex - marko = g.V().has('person', 'name', 'marko').values('name').next() + marko = g.V().has(VERTEX_LABEL, 'name', 'marko').values('name').next() print("name: " + marko) # find the "marko" vertex and then traverse to the people he "knows" and return their data - people_marko_knows = g.V().has('person', 'name', 'marko').out('knows').values('name').to_list() + people_marko_knows = g.V().has(VERTEX_LABEL, 'name', 'marko').out('knows').values('name').to_list() for person in people_marko_knows: print("marko knows " + person) diff --git a/gremlin-examples/gremlin-python/connections.py b/gremlin-examples/gremlin-python/connections.py index f268e6c27d5..9997a181ebf 100644 --- a/gremlin-examples/gremlin-python/connections.py +++ b/gremlin-examples/gremlin-python/connections.py @@ -24,6 +24,7 @@ from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection from gremlin_python.driver.serializer import GraphBinarySerializersV1 +VERTEX_LABEL = 'connection' def main(): with_remote() @@ -40,15 +41,13 @@ def with_remote(): # # which starts it in "console" mode with an empty in-memory TinkerGraph ready to go bound to a # variable named "g" as referenced in the following line. - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g') + server_url = 'ws://localhost:8182/gremlin' + rc = DriverRemoteConnection(server_url, 'g') g = traversal().with_remote(rc) - # drop existing vertices - g.V().drop().iterate() - # simple query to verify connection - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) # cleanup @@ -57,11 +56,12 @@ def with_remote(): # connecting with plain text authentication def with_auth(): - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', username='stephen', password='password') + server_url = 'ws://localhost:8182/gremlin' + rc = DriverRemoteConnection(server_url, 'g', username='stephen', password='password') g = traversal().with_remote(rc) - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) rc.close() @@ -69,11 +69,12 @@ def with_auth(): # connecting with Kerberos SASL authentication def with_kerberos(): - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', kerberized_service='gremlin@hostname.your.org') + server_url = 'ws://localhost:8182/gremlin' + rc = DriverRemoteConnection(server_url, 'g', kerberized_service='gremlin@hostname.your.org') g = traversal().with_remote(rc) - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) rc.close() @@ -81,8 +82,9 @@ def with_kerberos(): # connecting with customized configurations def with_configs(): + server_url = 'ws://localhost:8182/gremlin' rc = DriverRemoteConnection( - 'ws://localhost:8182/gremlin', 'g', + server_url, 'g', username="", password="", kerberized_service='', message_serializer=GraphBinarySerializersV1(), graphson_reader=None, graphson_writer=None, headers=None, session=None, @@ -90,8 +92,8 @@ def with_configs(): ) g = traversal().with_remote(rc) - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) rc.close() diff --git a/gremlin-python/docker-compose.yml b/gremlin-python/docker-compose.yml index 9879ae8a0c5..8b9ae9e2ac7 100644 --- a/gremlin-python/docker-compose.yml +++ b/gremlin-python/docker-compose.yml @@ -72,7 +72,13 @@ services: && pip install .[test,kerberos] && pytest && 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'; + && radish -f dots -e -t -b ./radish ./gremlin-test --user-data='serializer=application/vnd.graphbinary-v1.0' + && pip install . + && echo 'Running examples...' + && python3 examples/basic_gremlin.py + && python3 examples/connections.py + && python3 examples/modern_traversals.py + && echo 'All examples completed successfully'; 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/examples/basic_gremlin.py b/gremlin-python/src/main/python/examples/basic_gremlin.py index 256ed984416..2c086218967 100644 --- a/gremlin-python/src/main/python/examples/basic_gremlin.py +++ b/gremlin-python/src/main/python/examples/basic_gremlin.py @@ -16,6 +16,7 @@ # under the License. import sys +import os sys.path.append("..") @@ -23,15 +24,18 @@ from gremlin_python.process.strategies import * from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection +VERTEX_LABEL = os.getenv('VERTEX_LABEL', 'person') def main(): - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g') + # if there is a port placeholder in the env var then we are running with docker so set appropriate port + server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940) + rc = DriverRemoteConnection(server_url, 'g') g = traversal().with_remote(rc) # basic Gremlin: adding and retrieving data - v1 = g.add_v('person').property('name', 'marko').next() - v2 = g.add_v('person').property('name', 'stephen').next() - v3 = g.add_v('person').property('name', 'vadas').next() + v1 = g.add_v(VERTEX_LABEL).property('name', 'marko').next() + v2 = g.add_v(VERTEX_LABEL).property('name', 'stephen').next() + v3 = g.add_v(VERTEX_LABEL).property('name', 'vadas').next() # be sure to use a terminating step like next() or iterate() so that the traversal "executes" # iterate() does not return any data and is used to just generate side-effects (i.e. write data to the database) @@ -39,11 +43,11 @@ def main(): g.V(v1).add_e('knows').to(v3).property('weight', 0.75).iterate() # retrieve the data from the "marko" vertex - marko = g.V().has('person', 'name', 'marko').values('name').next() + marko = g.V().has(VERTEX_LABEL, 'name', 'marko').values('name').next() print("name: " + marko) # find the "marko" vertex and then traverse to the people he "knows" and return their data - people_marko_knows = g.V().has('person', 'name', 'marko').out('knows').values('name').to_list() + people_marko_knows = g.V().has(VERTEX_LABEL, 'name', 'marko').out('knows').values('name').to_list() for person in people_marko_knows: print("marko knows " + person) diff --git a/gremlin-python/src/main/python/examples/connections.py b/gremlin-python/src/main/python/examples/connections.py index f268e6c27d5..d4e36d49438 100644 --- a/gremlin-python/src/main/python/examples/connections.py +++ b/gremlin-python/src/main/python/examples/connections.py @@ -16,6 +16,9 @@ # under the License. import sys +import os +import ssl +import socket sys.path.append("..") @@ -23,7 +26,9 @@ from gremlin_python.process.strategies import * from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection from gremlin_python.driver.serializer import GraphBinarySerializersV1 +from gremlin_python.driver.aiohttp.transport import AiohttpTransport +VERTEX_LABEL = os.getenv('VERTEX_LABEL', 'connection') def main(): with_remote() @@ -40,15 +45,14 @@ def with_remote(): # # which starts it in "console" mode with an empty in-memory TinkerGraph ready to go bound to a # variable named "g" as referenced in the following line. - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g') + # if there is a port placeholder in the env var then we are running with docker so set appropriate port + server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940) + rc = DriverRemoteConnection(server_url, 'g') g = traversal().with_remote(rc) - # drop existing vertices - g.V().drop().iterate() - # simple query to verify connection - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) # cleanup @@ -57,11 +61,23 @@ def with_remote(): # connecting with plain text authentication def with_auth(): - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', username='stephen', password='password') + # if there is a port placeholder in the env var then we are running with docker so set appropriate port + server_url = os.getenv('GREMLIN_SERVER_BASIC_AUTH_URL', 'ws://localhost:8182/gremlin').format(45941) + + # disable SSL certificate verification for CI environments + if ':45941' in server_url: + ssl_opts = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ssl_opts.check_hostname = False + ssl_opts.verify_mode = ssl.CERT_NONE + rc = DriverRemoteConnection(server_url, 'g', username='stephen', password='password', + transport_factory=lambda: AiohttpTransport(ssl_options=ssl_opts)) + else: + rc = DriverRemoteConnection(server_url, 'g', username='stephen', password='password') + g = traversal().with_remote(rc) - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) rc.close() @@ -69,11 +85,16 @@ def with_auth(): # connecting with Kerberos SASL authentication def with_kerberos(): - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', kerberized_service='gremlin@hostname.your.org') + # if there is a port placeholder in the env var then we are running with docker so set appropriate port + server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45942) + kerberos_hostname = os.getenv('KRB_HOSTNAME', socket.gethostname()) + kerberized_service = f'test-service@{kerberos_hostname}' + + rc = DriverRemoteConnection(server_url, 'g', kerberized_service=kerberized_service) g = traversal().with_remote(rc) - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) rc.close() @@ -81,8 +102,10 @@ def with_kerberos(): # connecting with customized configurations def with_configs(): + # if there is a port placeholder in the env var then we are running with docker so set appropriate port + server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940) rc = DriverRemoteConnection( - 'ws://localhost:8182/gremlin', 'g', + server_url, 'g', username="", password="", kerberized_service='', message_serializer=GraphBinarySerializersV1(), graphson_reader=None, graphson_writer=None, headers=None, session=None, @@ -90,8 +113,8 @@ def with_configs(): ) g = traversal().with_remote(rc) - v = g.add_v().iterate() - count = g.V().count().next() + v = g.add_v(VERTEX_LABEL).iterate() + count = g.V().has_label(VERTEX_LABEL).count().next() print("Vertex count: " + str(count)) rc.close() diff --git a/gremlin-python/src/main/python/examples/modern_traversals.py b/gremlin-python/src/main/python/examples/modern_traversals.py index ae757b10b4f..bbab62d110f 100644 --- a/gremlin-python/src/main/python/examples/modern_traversals.py +++ b/gremlin-python/src/main/python/examples/modern_traversals.py @@ -16,6 +16,7 @@ # under the License. import sys +import os sys.path.append("..") @@ -31,7 +32,16 @@ def main(): # This example requires the Modern toy graph to be preloaded upon launching the Gremlin server. # For details, see https://tinkerpop.apache.org/docs/current/reference/#gremlin-server-docker-image and use # conf/gremlin-server-modern.yaml. - rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g') + # if there is a port placeholder in the env var then we are running with docker so set appropriate port + server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940) + + # CI uses port 45940 with gmodern binding, local uses 8182 with g binding + if ':45940' in server_url: + graph_binding = 'gmodern' # CI environment + else: + graph_binding = 'g' # Local environment + + rc = DriverRemoteConnection(server_url, graph_binding) g = traversal().with_remote(rc) e1 = g.V(1).both_e().to_list() # (1)