Skip to content

Commit 2cd2748

Browse files
Integrate Python driver examples into automated build process (#3231)
Integrates Python Gremlin examples into CI to ensure they remain functional in future releases. Examples now execute automatically after integration tests and fail the build if broken. Changes Made - Added example execution to gremlin-python-integration-tests container in docker-compose.yml - Made server URLs configurable via environment variables (replaces hardcoded localhost:8182) - Added configurable vertex labels and targeted cleanup using has_label() - Added graph binding detection - uses gmodern in CI, g locally for proper environment handling
1 parent 3f7c775 commit 2cd2748

File tree

7 files changed

+92
-44
lines changed

7 files changed

+92
-44
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
2222
2323
[[release-3-7-6]]
2424
=== TinkerPop 3.7.6 (NOT OFFICIALLY RELEASED YET)
25+
* Integrated Python driver examples into automated build process to ensure examples remain functional.
2526
2627
[[release-3-7-5]]
2728
=== TinkerPop 3.7.5 (Release Date: November 12, 2025)

gremlin-examples/gremlin-python/basic_gremlin.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,29 @@
2323
from gremlin_python.process.strategies import *
2424
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
2525

26+
VERTEX_LABEL = 'person'
2627

2728
def main():
28-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g')
29+
server_url = 'ws://localhost:8182/gremlin'
30+
rc = DriverRemoteConnection(server_url, 'g')
2931
g = traversal().with_remote(rc)
3032

3133
# basic Gremlin: adding and retrieving data
32-
v1 = g.add_v('person').property('name', 'marko').next()
33-
v2 = g.add_v('person').property('name', 'stephen').next()
34-
v3 = g.add_v('person').property('name', 'vadas').next()
34+
v1 = g.add_v(VERTEX_LABEL).property('name', 'marko').next()
35+
v2 = g.add_v(VERTEX_LABEL).property('name', 'stephen').next()
36+
v3 = g.add_v(VERTEX_LABEL).property('name', 'vadas').next()
3537

3638
# be sure to use a terminating step like next() or iterate() so that the traversal "executes"
3739
# iterate() does not return any data and is used to just generate side-effects (i.e. write data to the database)
3840
g.V(v1).add_e('knows').to(v2).property('weight', 0.75).iterate()
3941
g.V(v1).add_e('knows').to(v3).property('weight', 0.75).iterate()
4042

4143
# retrieve the data from the "marko" vertex
42-
marko = g.V().has('person', 'name', 'marko').values('name').next()
44+
marko = g.V().has(VERTEX_LABEL, 'name', 'marko').values('name').next()
4345
print("name: " + marko)
4446

4547
# find the "marko" vertex and then traverse to the people he "knows" and return their data
46-
people_marko_knows = g.V().has('person', 'name', 'marko').out('knows').values('name').to_list()
48+
people_marko_knows = g.V().has(VERTEX_LABEL, 'name', 'marko').out('knows').values('name').to_list()
4749
for person in people_marko_knows:
4850
print("marko knows " + person)
4951

gremlin-examples/gremlin-python/connections.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
2525
from gremlin_python.driver.serializer import GraphBinarySerializersV1
2626

27+
VERTEX_LABEL = 'connection'
2728

2829
def main():
2930
with_remote()
@@ -40,15 +41,13 @@ def with_remote():
4041
#
4142
# which starts it in "console" mode with an empty in-memory TinkerGraph ready to go bound to a
4243
# variable named "g" as referenced in the following line.
43-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g')
44+
server_url = 'ws://localhost:8182/gremlin'
45+
rc = DriverRemoteConnection(server_url, 'g')
4446
g = traversal().with_remote(rc)
4547

46-
# drop existing vertices
47-
g.V().drop().iterate()
48-
4948
# simple query to verify connection
50-
v = g.add_v().iterate()
51-
count = g.V().count().next()
49+
v = g.add_v(VERTEX_LABEL).iterate()
50+
count = g.V().has_label(VERTEX_LABEL).count().next()
5251
print("Vertex count: " + str(count))
5352

5453
# cleanup
@@ -57,41 +56,44 @@ def with_remote():
5756

5857
# connecting with plain text authentication
5958
def with_auth():
60-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', username='stephen', password='password')
59+
server_url = 'ws://localhost:8182/gremlin'
60+
rc = DriverRemoteConnection(server_url, 'g', username='stephen', password='password')
6161
g = traversal().with_remote(rc)
6262

63-
v = g.add_v().iterate()
64-
count = g.V().count().next()
63+
v = g.add_v(VERTEX_LABEL).iterate()
64+
count = g.V().has_label(VERTEX_LABEL).count().next()
6565
print("Vertex count: " + str(count))
6666

6767
rc.close()
6868

6969

7070
# connecting with Kerberos SASL authentication
7171
def with_kerberos():
72-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', kerberized_service='gremlin@hostname.your.org')
72+
server_url = 'ws://localhost:8182/gremlin'
73+
rc = DriverRemoteConnection(server_url, 'g', kerberized_service='gremlin@hostname.your.org')
7374
g = traversal().with_remote(rc)
7475

75-
v = g.add_v().iterate()
76-
count = g.V().count().next()
76+
v = g.add_v(VERTEX_LABEL).iterate()
77+
count = g.V().has_label(VERTEX_LABEL).count().next()
7778
print("Vertex count: " + str(count))
7879

7980
rc.close()
8081

8182

8283
# connecting with customized configurations
8384
def with_configs():
85+
server_url = 'ws://localhost:8182/gremlin'
8486
rc = DriverRemoteConnection(
85-
'ws://localhost:8182/gremlin', 'g',
87+
server_url, 'g',
8688
username="", password="", kerberized_service='',
8789
message_serializer=GraphBinarySerializersV1(), graphson_reader=None,
8890
graphson_writer=None, headers=None, session=None,
8991
enable_user_agent_on_connect=True
9092
)
9193
g = traversal().with_remote(rc)
9294

93-
v = g.add_v().iterate()
94-
count = g.V().count().next()
95+
v = g.add_v(VERTEX_LABEL).iterate()
96+
count = g.V().has_label(VERTEX_LABEL).count().next()
9597
print("Vertex count: " + str(count))
9698

9799
rc.close()

gremlin-python/docker-compose.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ services:
7272
&& pip install .[test,kerberos]
7373
&& pytest
7474
&& radish -f dots -e -t -b ./radish ./gremlin-test --user-data='serializer=application/vnd.gremlin-v3.0+json'
75-
&& radish -f dots -e -t -b ./radish ./gremlin-test --user-data='serializer=application/vnd.graphbinary-v1.0';
75+
&& radish -f dots -e -t -b ./radish ./gremlin-test --user-data='serializer=application/vnd.graphbinary-v1.0'
76+
&& pip install .
77+
&& echo 'Running examples...'
78+
&& python3 examples/basic_gremlin.py
79+
&& python3 examples/connections.py
80+
&& python3 examples/modern_traversals.py
81+
&& echo 'All examples completed successfully';
7682
EXIT_CODE=$$?; chown -R `stat -c "%u:%g" .` .; exit $$EXIT_CODE"
7783
depends_on:
7884
gremlin-server-test-python:

gremlin-python/src/main/python/examples/basic_gremlin.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,38 @@
1616
# under the License.
1717

1818
import sys
19+
import os
1920

2021
sys.path.append("..")
2122

2223
from gremlin_python.process.anonymous_traversal import traversal
2324
from gremlin_python.process.strategies import *
2425
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
2526

27+
VERTEX_LABEL = os.getenv('VERTEX_LABEL', 'person')
2628

2729
def main():
28-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g')
30+
# if there is a port placeholder in the env var then we are running with docker so set appropriate port
31+
server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940)
32+
rc = DriverRemoteConnection(server_url, 'g')
2933
g = traversal().with_remote(rc)
3034

3135
# basic Gremlin: adding and retrieving data
32-
v1 = g.add_v('person').property('name', 'marko').next()
33-
v2 = g.add_v('person').property('name', 'stephen').next()
34-
v3 = g.add_v('person').property('name', 'vadas').next()
36+
v1 = g.add_v(VERTEX_LABEL).property('name', 'marko').next()
37+
v2 = g.add_v(VERTEX_LABEL).property('name', 'stephen').next()
38+
v3 = g.add_v(VERTEX_LABEL).property('name', 'vadas').next()
3539

3640
# be sure to use a terminating step like next() or iterate() so that the traversal "executes"
3741
# iterate() does not return any data and is used to just generate side-effects (i.e. write data to the database)
3842
g.V(v1).add_e('knows').to(v2).property('weight', 0.75).iterate()
3943
g.V(v1).add_e('knows').to(v3).property('weight', 0.75).iterate()
4044

4145
# retrieve the data from the "marko" vertex
42-
marko = g.V().has('person', 'name', 'marko').values('name').next()
46+
marko = g.V().has(VERTEX_LABEL, 'name', 'marko').values('name').next()
4347
print("name: " + marko)
4448

4549
# find the "marko" vertex and then traverse to the people he "knows" and return their data
46-
people_marko_knows = g.V().has('person', 'name', 'marko').out('knows').values('name').to_list()
50+
people_marko_knows = g.V().has(VERTEX_LABEL, 'name', 'marko').out('knows').values('name').to_list()
4751
for person in people_marko_knows:
4852
print("marko knows " + person)
4953

gremlin-python/src/main/python/examples/connections.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@
1616
# under the License.
1717

1818
import sys
19+
import os
20+
import ssl
21+
import socket
1922

2023
sys.path.append("..")
2124

2225
from gremlin_python.process.anonymous_traversal import traversal
2326
from gremlin_python.process.strategies import *
2427
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
2528
from gremlin_python.driver.serializer import GraphBinarySerializersV1
29+
from gremlin_python.driver.aiohttp.transport import AiohttpTransport
2630

31+
VERTEX_LABEL = os.getenv('VERTEX_LABEL', 'connection')
2732

2833
def main():
2934
with_remote()
@@ -40,15 +45,14 @@ def with_remote():
4045
#
4146
# which starts it in "console" mode with an empty in-memory TinkerGraph ready to go bound to a
4247
# variable named "g" as referenced in the following line.
43-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g')
48+
# if there is a port placeholder in the env var then we are running with docker so set appropriate port
49+
server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940)
50+
rc = DriverRemoteConnection(server_url, 'g')
4451
g = traversal().with_remote(rc)
4552

46-
# drop existing vertices
47-
g.V().drop().iterate()
48-
4953
# simple query to verify connection
50-
v = g.add_v().iterate()
51-
count = g.V().count().next()
54+
v = g.add_v(VERTEX_LABEL).iterate()
55+
count = g.V().has_label(VERTEX_LABEL).count().next()
5256
print("Vertex count: " + str(count))
5357

5458
# cleanup
@@ -57,41 +61,60 @@ def with_remote():
5761

5862
# connecting with plain text authentication
5963
def with_auth():
60-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', username='stephen', password='password')
64+
# if there is a port placeholder in the env var then we are running with docker so set appropriate port
65+
server_url = os.getenv('GREMLIN_SERVER_BASIC_AUTH_URL', 'ws://localhost:8182/gremlin').format(45941)
66+
67+
# disable SSL certificate verification for CI environments
68+
if ':45941' in server_url:
69+
ssl_opts = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
70+
ssl_opts.check_hostname = False
71+
ssl_opts.verify_mode = ssl.CERT_NONE
72+
rc = DriverRemoteConnection(server_url, 'g', username='stephen', password='password',
73+
transport_factory=lambda: AiohttpTransport(ssl_options=ssl_opts))
74+
else:
75+
rc = DriverRemoteConnection(server_url, 'g', username='stephen', password='password')
76+
6177
g = traversal().with_remote(rc)
6278

63-
v = g.add_v().iterate()
64-
count = g.V().count().next()
79+
v = g.add_v(VERTEX_LABEL).iterate()
80+
count = g.V().has_label(VERTEX_LABEL).count().next()
6581
print("Vertex count: " + str(count))
6682

6783
rc.close()
6884

6985

7086
# connecting with Kerberos SASL authentication
7187
def with_kerberos():
72-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g', kerberized_service='gremlin@hostname.your.org')
88+
# if there is a port placeholder in the env var then we are running with docker so set appropriate port
89+
server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45942)
90+
kerberos_hostname = os.getenv('KRB_HOSTNAME', socket.gethostname())
91+
kerberized_service = f'test-service@{kerberos_hostname}'
92+
93+
rc = DriverRemoteConnection(server_url, 'g', kerberized_service=kerberized_service)
7394
g = traversal().with_remote(rc)
7495

75-
v = g.add_v().iterate()
76-
count = g.V().count().next()
96+
v = g.add_v(VERTEX_LABEL).iterate()
97+
count = g.V().has_label(VERTEX_LABEL).count().next()
7798
print("Vertex count: " + str(count))
7899

79100
rc.close()
80101

81102

82103
# connecting with customized configurations
83104
def with_configs():
105+
# if there is a port placeholder in the env var then we are running with docker so set appropriate port
106+
server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940)
84107
rc = DriverRemoteConnection(
85-
'ws://localhost:8182/gremlin', 'g',
108+
server_url, 'g',
86109
username="", password="", kerberized_service='',
87110
message_serializer=GraphBinarySerializersV1(), graphson_reader=None,
88111
graphson_writer=None, headers=None, session=None,
89112
enable_user_agent_on_connect=True
90113
)
91114
g = traversal().with_remote(rc)
92115

93-
v = g.add_v().iterate()
94-
count = g.V().count().next()
116+
v = g.add_v(VERTEX_LABEL).iterate()
117+
count = g.V().has_label(VERTEX_LABEL).count().next()
95118
print("Vertex count: " + str(count))
96119

97120
rc.close()

gremlin-python/src/main/python/examples/modern_traversals.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717

1818
import sys
19+
import os
1920

2021
sys.path.append("..")
2122

@@ -31,7 +32,16 @@ def main():
3132
# This example requires the Modern toy graph to be preloaded upon launching the Gremlin server.
3233
# For details, see https://tinkerpop.apache.org/docs/current/reference/#gremlin-server-docker-image and use
3334
# conf/gremlin-server-modern.yaml.
34-
rc = DriverRemoteConnection('ws://localhost:8182/gremlin', 'g')
35+
# if there is a port placeholder in the env var then we are running with docker so set appropriate port
36+
server_url = os.getenv('GREMLIN_SERVER_URL', 'ws://localhost:8182/gremlin').format(45940)
37+
38+
# CI uses port 45940 with gmodern binding, local uses 8182 with g binding
39+
if ':45940' in server_url:
40+
graph_binding = 'gmodern' # CI environment
41+
else:
42+
graph_binding = 'g' # Local environment
43+
44+
rc = DriverRemoteConnection(server_url, graph_binding)
3545
g = traversal().with_remote(rc)
3646

3747
e1 = g.V(1).both_e().to_list() # (1)

0 commit comments

Comments
 (0)