gremlinpython 4.0.0b2__py3-none-any.whl → 4.0.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,16 +19,18 @@
19
19
 
20
20
  import copy
21
21
  import math
22
+ import re
22
23
  import threading
23
24
  import uuid
24
25
  import warnings
25
26
 
26
27
  from aenum import Enum
27
- from gremlin_python.structure.graph import Vertex, Edge, Path, Property
28
+ from gremlin_python.structure.graph import Vertex, Edge, Path, Property, CompositePDT, PrimitivePDT
28
29
 
29
30
  from .. import statics
30
- from ..statics import long, SingleByte, short, bigint, BigDecimal
31
- from datetime import datetime
31
+ from ..statics import long, SingleByte, SingleChar, short, bigint, BigDecimal
32
+ from datetime import datetime, timedelta
33
+ import base64
32
34
 
33
35
 
34
36
  class Traversal(object):
@@ -358,6 +360,8 @@ class P(object):
358
360
  return P("within", args[0])
359
361
  elif len(args) == 1 and type(args[0]) == set:
360
362
  return P("within", list(args[0]))
363
+ elif len(args) == 1 and isinstance(args[0], Traversal):
364
+ return P("within", args[0])
361
365
  else:
362
366
  return P("within", list(args))
363
367
 
@@ -367,6 +371,8 @@ class P(object):
367
371
  return P("without", args[0])
368
372
  elif len(args) == 1 and type(args[0]) == set:
369
373
  return P("without", list(args[0]))
374
+ elif len(args) == 1 and isinstance(args[0], Traversal):
375
+ return P("without", args[0])
370
376
  else:
371
377
  return P("without", list(args))
372
378
 
@@ -813,13 +819,13 @@ class GremlinLang(object):
813
819
  self.gremlin = []
814
820
  self.parameters = {}
815
821
  self.options_strategies = []
816
- self.param_count = AtomicInteger()
822
+ self.pdt_registry = None
817
823
 
818
824
  if gremlin_lang is not None:
819
825
  self.gremlin = list(gremlin_lang.gremlin)
820
826
  self.parameters = dict(gremlin_lang.parameters)
821
827
  self.options_strategies = list(gremlin_lang.options_strategies)
822
- self.param_count = gremlin_lang.param_count
828
+ self.pdt_registry = gremlin_lang.pdt_registry
823
829
 
824
830
  def _add_to_gremlin(self, string_name, *args):
825
831
 
@@ -844,6 +850,9 @@ class GremlinLang(object):
844
850
  if arg is None:
845
851
  return 'null'
846
852
 
853
+ if isinstance(arg, SingleChar):
854
+ return f'{arg!r}c'
855
+
847
856
  if isinstance(arg, str):
848
857
  return f'{arg!r}' # use repr() format for canonical string rep
849
858
  # return f'"{arg}"'
@@ -879,6 +888,20 @@ class GremlinLang(object):
879
888
  if isinstance(arg, uuid.UUID):
880
889
  return f'UUID("{arg}")'
881
890
 
891
+ if isinstance(arg, timedelta):
892
+ is_negative = arg.total_seconds() < 0
893
+ abs_td = -arg if is_negative else arg
894
+ # Use integer components directly to avoid float precision loss
895
+ seconds = abs_td.days * 86400 + abs_td.seconds
896
+ nanos = abs_td.microseconds * 1000
897
+ if is_negative:
898
+ return f'Duration({seconds},{nanos},false)'
899
+ else:
900
+ return f'Duration({seconds},{nanos})'
901
+
902
+ if isinstance(arg, bytes):
903
+ return f'Binary("{base64.b64encode(arg).decode("ascii")}")'
904
+
882
905
  if isinstance(arg, Enum):
883
906
  tmp = str(arg)
884
907
  if tmp.endswith('_'):
@@ -888,6 +911,12 @@ class GremlinLang(object):
888
911
  else:
889
912
  return tmp
890
913
 
914
+ if isinstance(arg, CompositePDT):
915
+ return f'PDT({self._arg_as_string(arg.name)},{self._process_dict(arg.fields)})'
916
+
917
+ if isinstance(arg, PrimitivePDT):
918
+ return f'PDT({self._arg_as_string(arg.name)},{self._arg_as_string(arg.value)})'
919
+
891
920
  if isinstance(arg, Vertex):
892
921
  return f'{self._arg_as_string(arg.id)}'
893
922
 
@@ -901,9 +930,8 @@ class GremlinLang(object):
901
930
 
902
931
  if isinstance(arg, GValue):
903
932
  key = arg.get_name()
904
-
905
- if not key.isidentifier():
906
- raise Exception(f'invalid parameter name {key}.')
933
+ if not re.fullmatch(r'(?:[^\W\d]|\$)(?:\w|\$)*', key):
934
+ raise Exception(f'Invalid parameter name [{key}].')
907
935
 
908
936
  if key in self.parameters:
909
937
  if self.parameters[key] != arg.value:
@@ -921,18 +949,42 @@ class GremlinLang(object):
921
949
  if isinstance(arg, list):
922
950
  return self._process_list(arg)
923
951
 
924
- if hasattr(arg, '__class__'):
925
- try:
926
- return arg.__name__
927
- except AttributeError:
928
- pass
929
-
930
- return self._as_parameter(arg)
931
-
932
- def _as_parameter(self, arg):
933
- param_name = f'_{self.param_count.get_and_increment()}'
934
- self.parameters[param_name] = arg
935
- return param_name
952
+ # Strategy instances render as their name (e.g. "ReadOnlyStrategy") for withoutStrategies.
953
+ # Class objects render as their __name__ (e.g. strategy classes passed directly).
954
+ # These replace the old hasattr(arg, '__class__') check which was too broad since every
955
+ # Python object has __class__, making it a silent escape hatch for anything with __name__.
956
+ if isinstance(arg, TraversalStrategy):
957
+ return arg.strategy_name
958
+
959
+ if isinstance(arg, type):
960
+ return arg.__name__
961
+
962
+ # Registry-based dehydration — a registered adapter intentionally takes
963
+ # precedence over the @provider_defined decorator fallback below, allowing
964
+ # explicit adapters to override decorator-derived behavior.
965
+ if self.pdt_registry is not None:
966
+ primitive_adapter = self.pdt_registry.get_primitive_adapter_by_class(type(arg))
967
+ if primitive_adapter is not None and primitive_adapter['to_value'] is not None:
968
+ value = primitive_adapter['to_value'](arg)
969
+ return self._arg_as_string(PrimitivePDT(primitive_adapter['type_name'], value))
970
+ adapter = self.pdt_registry.get_composite_adapter_by_class(type(arg))
971
+ if adapter is not None and adapter['serialize'] is not None:
972
+ fields = adapter['serialize'](arg)
973
+ return self._arg_as_string(CompositePDT(adapter['type_name'], fields))
974
+
975
+ # Auto-dehydrate @provider_defined decorated objects
976
+ if hasattr(arg, '_pdt_name'):
977
+ included = getattr(arg, '_pdt_included_fields', None)
978
+ excluded = getattr(arg, '_pdt_excluded_fields', None)
979
+ fields = [f for f in vars(arg) if not f.startswith('_')]
980
+ if included:
981
+ fields = [f for f in fields if f in included]
982
+ elif excluded:
983
+ fields = [f for f in fields if f not in excluded]
984
+ pdt = CompositePDT(arg._pdt_name, {f: getattr(arg, f) for f in fields})
985
+ return self._arg_as_string(pdt)
986
+
987
+ raise TypeError(f'GremlinLang contains at least one type [{type(arg).__name__}] that cannot be represented as text.')
936
988
 
937
989
  # Do special processing needed to format predicates that come in
938
990
  # such as "gt(a)" correctly.
@@ -949,13 +1001,22 @@ class GremlinLang(object):
949
1001
  c = 0
950
1002
  res = [str(p).split('(')[0] + '(']
951
1003
  if isinstance(p.value, list):
952
- res.append('[')
953
- for v in p.value:
954
- if c > 0:
955
- res.append(',')
956
- res.append(self._arg_as_string(v))
957
- c += 1
958
- res.append(']')
1004
+ # If all elements are Traversals, serialize as comma-separated args (no brackets)
1005
+ # This matches the server grammar: within(trav1, trav2) via genericArgumentVarargs
1006
+ if len(p.value) > 0 and all(isinstance(v, Traversal) for v in p.value):
1007
+ for v in p.value:
1008
+ if c > 0:
1009
+ res.append(',')
1010
+ res.append(self._arg_as_string(v))
1011
+ c += 1
1012
+ else:
1013
+ res.append('[')
1014
+ for v in p.value:
1015
+ if c > 0:
1016
+ res.append(',')
1017
+ res.append(self._arg_as_string(v))
1018
+ c += 1
1019
+ res.append(']')
959
1020
  else:
960
1021
  res.append(self._arg_as_string(p.value))
961
1022
  if p.other is not None:
@@ -1011,11 +1072,23 @@ class GremlinLang(object):
1011
1072
  def get_parameters(self):
1012
1073
  return self.parameters
1013
1074
 
1014
- def add_g(self, g):
1015
- self.parameters['g'] = g
1075
+ def get_parameters_as_string(self):
1076
+ return GremlinLang.convert_parameters_to_string(self.parameters)
1016
1077
 
1017
- def reset(self):
1018
- self.param_count.set(0)
1078
+ @staticmethod
1079
+ def convert_parameters_to_string(params):
1080
+ """Converts a parameter map to a gremlin-lang map literal string.
1081
+
1082
+ Raises TypeError if any value is an unsupported type.
1083
+ """
1084
+ if params is None or len(params) == 0:
1085
+ return '[:]'
1086
+
1087
+ helper = GremlinLang()
1088
+ parts = []
1089
+ for k, v in params.items():
1090
+ parts.append(f'{helper._arg_as_string(k)}:{helper._arg_as_string(v)}')
1091
+ return '[' + ','.join(parts) + ']'
1019
1092
 
1020
1093
  def add_source(self, source_name, *args):
1021
1094
 
@@ -1062,7 +1135,7 @@ class GremlinLang(object):
1062
1135
 
1063
1136
  def __eq__(self, other):
1064
1137
  if isinstance(other, self.__class__):
1065
- return ''.join(self.gremlin) == ''.join(self.gremlin) and self.parameters == other.parameters
1138
+ return ''.join(self.gremlin) == ''.join(other.gremlin) and self.parameters == other.parameters
1066
1139
  else:
1067
1140
  return False
1068
1141
 
@@ -1111,30 +1184,14 @@ class GremlinLang(object):
1111
1184
  return (''.join(self.gremlin) if len(self.gremlin) > 0 else "") + \
1112
1185
  (str(self.parameters) if len(self.parameters) > 0 else "")
1113
1186
 
1114
- # TODO to be removed or updated once HTTP transaction is implemented
1115
- # @staticmethod
1116
- # def _create_graph_op(name, *values):
1117
- # bc = Bytecode()
1118
- # bc.add_source(name, *values)
1119
- # return bc
1120
- #
1121
- # @staticmethod
1122
- # class GraphOp:
1123
- # @staticmethod
1124
- # def commit():
1125
- # return Bytecode._create_graph_op("tx", "commit")
1126
- #
1127
- # @staticmethod
1128
- # def rollback():
1129
- # return Bytecode._create_graph_op("tx", "rollback")
1130
1187
 
1131
1188
 
1132
1189
  class GValue:
1133
1190
  def __init__(self, name, value):
1191
+ if isinstance(value, GValue):
1192
+ raise Exception('GValues cannot be nested')
1134
1193
  if name is None:
1135
- raise Exception("The parameter name cannot be None.")
1136
- if name.startswith('_'):
1137
- raise Exception(f'invalid GValue name {name}. Should not start with _.')
1194
+ raise Exception('GValue name cannot be null.')
1138
1195
  self.name = name
1139
1196
  self.value = value
1140
1197
 
@@ -1147,6 +1204,12 @@ class GValue:
1147
1204
  def get(self):
1148
1205
  return self.value
1149
1206
 
1207
+ def __repr__(self):
1208
+ return f'{self.name}={self.value}'
1209
+
1210
+ def __str__(self):
1211
+ return f'{self.name}={self.value}'
1212
+
1150
1213
 
1151
1214
  class CardinalityValue(GremlinLang):
1152
1215
  def __init__(self, cardinality, val):