This is an automated email from the ASF dual-hosted git repository.

Cole-Greer pushed a commit to branch simplePDT
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit ef194e358f17ab2b49bf327995176b31b38e1659
Author: Cole Greer <[email protected]>
AuthorDate: Wed Jun 24 17:05:14 2026 -0700

    PDT review fixes: adapter precedence, Python gremlin-lang primitive 
support, registry naming, and integration coverage
    
    Outcome of the post-implementation review pass over the PrimitivePDT work.
    Touches several already-merged beads; details and departures below.
    
    Production fixes:
    - GraphBinaryWriter.dehydrateToPdt now prefers a registered adapter over the
      @ProviderDefined annotation, matching the documented precedence in
      GremlinLang.argAsString ("a registered adapter takes priority"). 
Previously
      the binary write path preferred the annotation, diverging from the request
      path. (refs tinkerpop-lka, tinkerpop-2gy.3)
    - gremlin-python: GremlinLang._arg_as_string did not handle
      PrimitiveProviderDefinedType, raising TypeError when a primitive PDT was 
used
      as a traversal argument. Added the PDT("name","value") text emission and
      primitive-adapter auto-dehydration (primitive checked before composite),
      mirroring the Java side. This gap shipped in the Python GLV bead and was
      caught by the new integration tests. (refs tinkerpop-2gy.8)
    - Renamed the composite adapter accessors/maps to be explicit about 
"composite"
      in both Java (getCompositeAdapterByName/ByClass, compositeAdaptersBy*) and
      Python (get_composite_adapter_by_class, _composite_adapters_by_*), now 
that a
      parallel primitive set exists. (refs tinkerpop-2gy.1, tinkerpop-2gy.3)
    
    Test coverage added (these gaps allowed the above bugs to ship):
    - GraphBinaryWriterPdtTest: precedence regression test asserting a 
registered
      adapter wins over @ProviderDefined on the binary write path.
    - GremlinDriverIntegrateTest: PrimitivePDT traversal-API integration tests
      covering the unregistered base case, registered auto de/hydration, and the
      registered nested (composite-containing-primitive) case. Reuses the
      server-side Uint32/Uint32Adapter/Measurement fixtures rather than
      duplicating them. (refs tinkerpop-2gy.6, tinkerpop-2gy.7)
    - gremlin-python: traversal-API integration tests (raw/unregistered, 
registered
      hydration, in-collection, nested-in-composite) mirroring the composite 
suite.
      (refs tinkerpop-2gy.8)
    
    Departure note: the gremlin-python GLV (tinkerpop-2gy.8) does not add a 
GraphSON
    g:PrimitivePdt read path because the Python driver is GraphBinary-only for 
V4;
    an orphaned GraphSON reader added during implementation was removed.
    
    Assisted-by: Kiro:claude-opus-4.8
---
 .../gremlin/process/traversal/GremlinLang.java     |  5 +-
 .../structure/io/binary/GraphBinaryWriter.java     | 37 +++++-----
 .../io/graphson/GraphSONTypeIdResolver.java        |  2 +-
 .../graphson/PdtGraphSONSerializerProviderV4.java  |  2 +-
 .../io/graphson/PdtGraphSONSerializersV4.java      |  5 +-
 .../io/pdt/ProviderDefinedTypeAdapter.java         |  2 +-
 .../io/pdt/ProviderDefinedTypeRegistry.java        | 25 +++----
 .../io/pdt/ProviderDefinedTypeRegistryTest.java    |  6 +-
 .../python/gremlin_python/driver/serializer.py     |  4 +-
 .../python/gremlin_python/process/traversal.py     | 11 ++-
 .../main/python/gremlin_python/structure/graph.py  | 14 ++--
 .../src/main/python/tests/integration/conftest.py  | 28 ++++++++
 .../python/tests/integration/driver/test_client.py | 53 ++++++++++++++-
 .../driver/test_driver_remote_connection.py        | 19 +++++-
 .../structure/io/test_provider_defined_type.py     | 10 +--
 .../gremlin/server/GremlinDriverIntegrateTest.java | 79 ++++++++++++++++++++++
 .../GremlinServerPrimitivePdtIntegrateTest.java    |  2 -
 .../util/ser/binary/GraphBinaryWriterPdtTest.java  | 49 ++++++++++++--
 18 files changed, 278 insertions(+), 75 deletions(-)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
index 3470de1981..5d041614e8 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
@@ -35,7 +35,6 @@ import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDTAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDTAdapter;
-import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry;
 import org.apache.tinkerpop.gremlin.util.NumberHelper;
 
@@ -280,13 +279,11 @@ public class GremlinLang implements Cloneable, 
Serializable {
         if (pdtRegistry != null) {
             final Optional<PrimitivePDTAdapter<?>> primitiveAdapter = 
pdtRegistry.getPrimitiveAdapterByClass(arg.getClass());
             if (primitiveAdapter.isPresent()) {
-                @SuppressWarnings("unchecked")
                 final String value = ((PrimitivePDTAdapter) 
primitiveAdapter.get()).toValue(arg);
                 return argAsString(new 
PrimitiveProviderDefinedType(primitiveAdapter.get().typeName(), value));
             }
-            final Optional<ProviderDefinedTypeAdapter<?>> adapter = 
pdtRegistry.getAdapterByClass(arg.getClass());
+            final Optional<CompositePDTAdapter<?>> adapter = 
pdtRegistry.getCompositeAdapterByClass(arg.getClass());
             if (adapter.isPresent()) {
-                @SuppressWarnings("unchecked")
                 final Map<String, Object> fields = ((CompositePDTAdapter) 
adapter.get()).toFields(arg);
                 return argAsString(new 
ProviderDefinedType(adapter.get().typeName(), fields));
             }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
index 7bbdb6d0f6..e7b47590a5 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java
@@ -25,7 +25,6 @@ import 
org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDTAdapter;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDTAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType;
-import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.Buffer;
 
@@ -89,7 +88,7 @@ public class GraphBinaryWriter {
 
         final Class<?> objectClass = value.getClass();
 
-        final TypeSerializer<T> serializer = (TypeSerializer<T>) 
getSerializerOrAdapterFallback(objectClass);
+        final TypeSerializer<T> serializer = 
getSerializerOrAdapterFallback(objectClass);
         if (serializer instanceof ProviderDefinedTypeSerializer && !(value 
instanceof ProviderDefinedType)) {
             serializer.writeValue((T) dehydrateToPdt(value, objectClass), 
buffer, this, nullable);
             return;
@@ -112,9 +111,12 @@ public class GraphBinaryWriter {
         }
 
         final Class<?> objectClass = value.getClass();
-        final TypeSerializer<T> serializer = (TypeSerializer<T>) 
getSerializerOrAdapterFallback(objectClass);
+        final TypeSerializer<T> serializer = 
getSerializerOrAdapterFallback(objectClass);
 
         if (serializer instanceof ProviderDefinedTypeSerializer && !(value 
instanceof ProviderDefinedType)) {
+            // Convert @ProviderDefined-annotated object to 
ProviderDefinedType, then re-enter write().
+            // On re-entry, ProviderDefinedType.class is directly registered 
in the registry,
+            // and the instanceof guard prevents double-wrapping.
             write((T) dehydrateToPdt(value, objectClass), buffer);
             return;
         }
@@ -125,6 +127,8 @@ public class GraphBinaryWriter {
         }
 
         if (serializer instanceof TransformSerializer) {
+            // For historical reasons, there are types that need to be 
transformed into another type
+            // before serialization, e.g., Map.Entry
             final TransformSerializer<T> transformSerializer = 
(TransformSerializer<T>) serializer;
             write(transformSerializer.transform(value), buffer);
             return;
@@ -177,17 +181,16 @@ public class GraphBinaryWriter {
      * Attempts to get a serializer for the given class. If no serializer is 
found and the pdtRegistry
      * has an adapter for the class (composite or primitive), returns the 
appropriate PDT serializer.
      */
-    @SuppressWarnings("unchecked")
     private <DT> TypeSerializer<DT> getSerializerOrAdapterFallback(final 
Class<?> type) throws IOException {
         try {
             return (TypeSerializer<DT>) registry.getSerializer(type);
         } catch (final IOException e) {
             if (pdtRegistry != null) {
-                if (pdtRegistry.getAdapterByClass(type).isPresent()) {
-                    return (TypeSerializer<DT>) 
registry.getSerializer(DataType.COMPOSITE_PDT);
+                if (pdtRegistry.getCompositeAdapterByClass(type).isPresent()) {
+                    return registry.getSerializer(DataType.COMPOSITE_PDT);
                 }
                 if (pdtRegistry.getPrimitiveAdapterByClass(type).isPresent()) {
-                    return (TypeSerializer<DT>) 
registry.getSerializer(DataType.PRIMITIVE_PDT);
+                    return registry.getSerializer(DataType.PRIMITIVE_PDT);
                 }
             }
             throw e;
@@ -198,32 +201,26 @@ public class GraphBinaryWriter {
      * Dehydrates a value to a {@link ProviderDefinedType} using annotation 
reflection or an adapter from the
      * pdtRegistry.
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
     private ProviderDefinedType dehydrateToPdt(final Object value, final 
Class<?> objectClass) {
-        // Prefer annotation-based conversion
-        if 
(objectClass.isAnnotationPresent(org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefined.class))
 {
-            return ProviderDefinedType.from(value);
-        }
-        // Fall back to adapter-based conversion
+        // A registered adapter takes priority
         if (pdtRegistry != null) {
-            final Optional<ProviderDefinedTypeAdapter<?>> opt = 
pdtRegistry.getAdapterByClass(objectClass);
+            final Optional<CompositePDTAdapter<?>> opt = 
pdtRegistry.getCompositeAdapterByClass(objectClass);
             if (opt.isPresent()) {
-                final CompositePDTAdapter adapter = (CompositePDTAdapter) 
opt.get();
-                final Map<String, Object> fields = adapter.toFields(value);
-                return new ProviderDefinedType(adapter.typeName(), fields);
+                final CompositePDTAdapter adapter = opt.get();
+                return new ProviderDefinedType(adapter.typeName(), 
adapter.toFields(value));
             }
         }
-        // Should not reach here since getSerializerOrAdapterFallback already 
validated
+        // @ProviderDefined annotation base case
         return ProviderDefinedType.from(value);
     }
 
+
     /**
      * Dehydrates a value to a {@link PrimitiveProviderDefinedType} using a 
{@link PrimitivePDTAdapter} from the
      * pdtRegistry.
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
     private PrimitiveProviderDefinedType dehydrateToPrimitivePdt(final Object 
value, final Class<?> objectClass) {
-        final PrimitivePDTAdapter adapter = (PrimitivePDTAdapter) 
pdtRegistry.getPrimitiveAdapterByClass(objectClass).get();
+        final PrimitivePDTAdapter adapter = 
pdtRegistry.getPrimitiveAdapterByClass(objectClass).get();
         return new PrimitiveProviderDefinedType(adapter.typeName(), 
adapter.toValue(value));
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java
index cec70fb058..de4314a7ef 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java
@@ -85,7 +85,7 @@ public class GraphSONTypeIdResolver implements TypeIdResolver 
{
     public String idFromValueAndType(final Object o, final Class<?> aClass) {
         if (!typeToId.containsKey(aClass)) {
             // Check if pdtRegistry has an adapter for this class
-            if (pdtRegistry != null && 
pdtRegistry.getAdapterByClass(aClass).isPresent()) {
+            if (pdtRegistry != null && 
pdtRegistry.getCompositeAdapterByClass(aClass).isPresent()) {
                 return 
typeToId.get(org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType.class);
             }
             if (pdtRegistry != null && 
pdtRegistry.getPrimitiveAdapterByClass(aClass).isPresent()) {
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java
index 008be0ec3b..45a349d103 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java
@@ -55,7 +55,7 @@ final class PdtGraphSONSerializerProviderV4 extends 
DefaultSerializerProvider {
 
     @Override
     public JsonSerializer<Object> getUnknownTypeSerializer(final Class<?> 
aClass) {
-        if (pdtRegistry != null && 
pdtRegistry.getAdapterByClass(aClass).isPresent()) {
+        if (pdtRegistry != null && 
pdtRegistry.getCompositeAdapterByClass(aClass).isPresent()) {
             return pdtAdapterSerializer;
         }
         if (pdtRegistry != null && 
pdtRegistry.getPrimitiveAdapterByClass(aClass).isPresent()) {
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
index 193392f8b1..91dd87b0dd 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
@@ -22,7 +22,6 @@ import 
org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDTAdapter;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDTAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType;
-import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry;
 import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
 import org.apache.tinkerpop.shaded.jackson.core.JsonParser;
@@ -221,11 +220,11 @@ final class PdtGraphSONSerializersV4 {
         }
 
         private ProviderDefinedType toPdt(final Object value) throws 
IOException {
-            final Optional<ProviderDefinedTypeAdapter<?>> opt = 
registry.getAdapterByClass(value.getClass());
+            final Optional<CompositePDTAdapter<?>> opt = 
registry.getCompositeAdapterByClass(value.getClass());
             if (!opt.isPresent()) {
                 throw new IOException("No adapter found for " + 
value.getClass().getName());
             }
-            final CompositePDTAdapter adapter = (CompositePDTAdapter) 
opt.get();
+            final CompositePDTAdapter adapter = opt.get();
             final Map<String, Object> fields = adapter.toFields(value);
             return new ProviderDefinedType(adapter.typeName(), fields);
         }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeAdapter.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeAdapter.java
index 3612cda193..bcd3fcfebf 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeAdapter.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeAdapter.java
@@ -20,7 +20,7 @@ package org.apache.tinkerpop.gremlin.structure.io.pdt;
 
 /**
  * Common supertype for all PDT adapters. Exposes the type name and target 
class;
- * serialization-specific methods live in subtypes ({@link 
CompositePDTAdapter}).
+ * serialization-specific methods live in subtypes ({@link 
CompositePDTAdapter} and {@link PrimitivePDTAdapter}).
  */
 public interface ProviderDefinedTypeAdapter<T> {
     String typeName();
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistry.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistry.java
index bb3b7eb3f4..fc33b92415 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistry.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistry.java
@@ -40,8 +40,8 @@ public final class ProviderDefinedTypeRegistry {
 
     private static final Logger logger = 
LoggerFactory.getLogger(ProviderDefinedTypeRegistry.class);
 
-    private final Map<String, CompositePDTAdapter<?>> adaptersByName = new 
ConcurrentHashMap<>();
-    private final Map<Class<?>, CompositePDTAdapter<?>> adaptersByClass = new 
ConcurrentHashMap<>();
+    private final Map<String, CompositePDTAdapter<?>> compositeAdaptersByName 
= new ConcurrentHashMap<>();
+    private final Map<Class<?>, CompositePDTAdapter<?>> 
compositeAdaptersByClass = new ConcurrentHashMap<>();
     private final Map<String, PrimitivePDTAdapter<?>> primitiveAdaptersByName 
= new ConcurrentHashMap<>();
     private final Map<Class<?>, PrimitivePDTAdapter<?>> 
primitiveAdaptersByClass = new ConcurrentHashMap<>();
 
@@ -50,7 +50,6 @@ public final class ProviderDefinedTypeRegistry {
     /**
      * Creates a registry populated via {@link ServiceLoader} discovery.
      */
-    @SuppressWarnings("rawtypes")
     public static ProviderDefinedTypeRegistry create() {
         final ProviderDefinedTypeRegistry registry = new 
ProviderDefinedTypeRegistry();
         for (final ProviderDefinedTypeAdapter adapter : 
ServiceLoader.load(ProviderDefinedTypeAdapter.class)) {
@@ -75,7 +74,7 @@ public final class ProviderDefinedTypeRegistry {
     public void register(final ProviderDefinedTypeAdapter<?> adapter) {
         if (adapter instanceof PrimitivePDTAdapter) {
             final PrimitivePDTAdapter<?> primitive = (PrimitivePDTAdapter<?>) 
adapter;
-            if (adaptersByClass.containsKey(primitive.targetClass()))
+            if (compositeAdaptersByClass.containsKey(primitive.targetClass()))
                 throw new IllegalArgumentException("Class " + 
primitive.targetClass().getName() +
                         " is already registered as a composite PDT adapter");
             primitiveAdaptersByName.put(primitive.typeName(), primitive);
@@ -85,8 +84,8 @@ public final class ProviderDefinedTypeRegistry {
             if (primitiveAdaptersByClass.containsKey(composite.targetClass()))
                 throw new IllegalArgumentException("Class " + 
composite.targetClass().getName() +
                         " is already registered as a primitive PDT adapter");
-            adaptersByName.put(composite.typeName(), composite);
-            adaptersByClass.put(composite.targetClass(), composite);
+            compositeAdaptersByName.put(composite.typeName(), composite);
+            compositeAdaptersByClass.put(composite.targetClass(), composite);
         }
     }
 
@@ -102,12 +101,12 @@ public final class ProviderDefinedTypeRegistry {
         }
     }
 
-    public Optional<ProviderDefinedTypeAdapter<?>> getAdapterByName(final 
String name) {
-        return Optional.ofNullable(adaptersByName.get(name));
+    public Optional<CompositePDTAdapter<?>> getCompositeAdapterByName(final 
String name) {
+        return Optional.ofNullable(compositeAdaptersByName.get(name));
     }
 
-    public Optional<ProviderDefinedTypeAdapter<?>> getAdapterByClass(final 
Class<?> clazz) {
-        return Optional.ofNullable(adaptersByClass.get(clazz));
+    public Optional<CompositePDTAdapter<?>> getCompositeAdapterByClass(final 
Class<?> clazz) {
+        return Optional.ofNullable(compositeAdaptersByClass.get(clazz));
     }
 
     public Optional<PrimitivePDTAdapter<?>> getPrimitiveAdapterByName(final 
String name) {
@@ -126,7 +125,6 @@ public final class ProviderDefinedTypeRegistry {
      * Returns the original PDT (with nested values hydrated) if no adapter is 
found for the outer type,
      * or if the adapter throws an exception.
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
     public Object hydrate(final ProviderDefinedType pdt) {
         // recursively hydrate nested PDTs in the fields map, whether or not 
the outer has an adapter
         boolean nestedChanged = false;
@@ -138,7 +136,7 @@ public final class ProviderDefinedTypeRegistry {
             hydrated.put(entry.getKey(), value);
         }
 
-        final CompositePDTAdapter adapter = adaptersByName.get(pdt.getName());
+        final CompositePDTAdapter adapter = 
compositeAdaptersByName.get(pdt.getName());
         if (adapter == null) {
             // No adapter for the outer type: return it raw, but with any 
registered nested types hydrated.
             // Preserve identity when nothing nested was hydrated.
@@ -154,7 +152,6 @@ public final class ProviderDefinedTypeRegistry {
         }
     }
 
-    @SuppressWarnings({"unchecked", "rawtypes"})
     private Object hydrateValue(final Object value) {
         if (value instanceof ProviderDefinedType)
             return hydrate((ProviderDefinedType) value);
@@ -186,7 +183,6 @@ public final class ProviderDefinedTypeRegistry {
      * {@link PrimitivePDTAdapter}. Returns the original primitive PDT if no 
adapter is found or if the
      * adapter throws an exception.
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
     public Object hydratePrimitive(final PrimitiveProviderDefinedType pdt) {
         final PrimitivePDTAdapter adapter = 
primitiveAdaptersByName.get(pdt.getName());
         if (adapter == null) {
@@ -206,7 +202,6 @@ public final class ProviderDefinedTypeRegistry {
     /**
      * A reflective adapter synthesized from a {@link 
ProviderDefined}-annotated class.
      */
-    @SuppressWarnings({"unchecked", "rawtypes"})
     private static final class AnnotatedTypeAdapter<T> implements 
CompositePDTAdapter<T> {
         private final String typeName;
         private final Class<T> targetClass;
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistryTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistryTest.java
index 3442038337..d82174776c 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistryTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/ProviderDefinedTypeRegistryTest.java
@@ -188,7 +188,7 @@ public class ProviderDefinedTypeRegistryTest {
         final PointAdapter adapter = new PointAdapter();
         registry.register(adapter);
 
-        final Optional<ProviderDefinedTypeAdapter<?>> found = 
registry.getAdapterByClass(Point.class);
+        final Optional<CompositePDTAdapter<?>> found = 
registry.getCompositeAdapterByClass(Point.class);
         assertTrue(found.isPresent());
         assertEquals("Point", found.get().typeName());
     }
@@ -338,7 +338,7 @@ public class ProviderDefinedTypeRegistryTest {
         final ProviderDefinedTypeRegistry registry = 
ProviderDefinedTypeRegistry.empty();
         registry.register(AnnotatedPoint.class);
 
-        final Optional<ProviderDefinedTypeAdapter<?>> adapter = 
registry.getAdapterByClass(AnnotatedPoint.class);
+        final Optional<CompositePDTAdapter<?>> adapter = 
registry.getCompositeAdapterByClass(AnnotatedPoint.class);
         assertTrue(adapter.isPresent());
         assertEquals("AnnotatedPoint", adapter.get().typeName());
     }
@@ -537,7 +537,6 @@ public class ProviderDefinedTypeRegistryTest {
             @Override public String typeName() { return "Container"; }
             @Override public Class<Map> targetClass() { return Map.class; }
             @Override public Map<String, Object> toFields(Map obj) { return 
new HashMap<>(); }
-            @SuppressWarnings("unchecked")
             @Override public Map fromFields(Map<String, Object> fields) { 
return fields; }
         });
 
@@ -548,7 +547,6 @@ public class ProviderDefinedTypeRegistryTest {
 
         final Object result = registry.hydrate(containerPdt);
         assertTrue(result instanceof Map);
-        @SuppressWarnings("unchecked")
         final Map<String, Object> resultMap = (Map<String, Object>) result;
         assertTrue(resultMap.get("id") instanceof Uint32);
         assertEquals(99L, ((Uint32) resultMap.get("id")).value);
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/serializer.py 
b/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
index 47182b04da..ff02f4dd68 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
@@ -49,8 +49,8 @@ class GraphBinarySerializersV4(object):
         if self._graphbinary_reader.pdt_registry is None:
             self._graphbinary_reader.pdt_registry = pdt_registry
         else:
-            
self._graphbinary_reader.pdt_registry._adapters_by_name.update(pdt_registry._adapters_by_name)
-            
self._graphbinary_reader.pdt_registry._adapters_by_class.update(pdt_registry._adapters_by_class)
+            
self._graphbinary_reader.pdt_registry._composite_adapters_by_name.update(pdt_registry._composite_adapters_by_name)
+            
self._graphbinary_reader.pdt_registry._composite_adapters_by_class.update(pdt_registry._composite_adapters_by_class)
             
self._graphbinary_reader.pdt_registry._primitive_adapters_by_name.update(pdt_registry._primitive_adapters_by_name)
             
self._graphbinary_reader.pdt_registry._primitive_adapters_by_class.update(pdt_registry._primitive_adapters_by_class)
 
diff --git a/gremlin-python/src/main/python/gremlin_python/process/traversal.py 
b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
index cbc035ee2c..1ab8679a7d 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
@@ -24,7 +24,7 @@ import uuid
 import warnings
 
 from aenum import Enum
-from gremlin_python.structure.graph import Vertex, Edge, Path, Property, 
ProviderDefinedType
+from gremlin_python.structure.graph import Vertex, Edge, Path, Property, 
ProviderDefinedType, PrimitiveProviderDefinedType
 
 from .. import statics
 from ..statics import long, SingleByte, SingleChar, short, bigint, BigDecimal
@@ -909,6 +909,9 @@ class GremlinLang(object):
         if isinstance(arg, ProviderDefinedType):
             return 
f'PDT({self._arg_as_string(arg.name)},{self._process_dict(arg.fields)})'
 
+        if isinstance(arg, PrimitiveProviderDefinedType):
+            return 
f'PDT({self._arg_as_string(arg.name)},{self._arg_as_string(arg.value)})'
+
         if isinstance(arg, Vertex):
             return f'{self._arg_as_string(arg.id)}'
 
@@ -956,7 +959,11 @@ class GremlinLang(object):
         # precedence over the @provider_defined decorator fallback below, 
allowing
         # explicit adapters to override decorator-derived behavior.
         if self.pdt_registry is not None:
-            adapter = self.pdt_registry.get_adapter_by_class(type(arg))
+            primitive_adapter = 
self.pdt_registry.get_primitive_adapter_by_class(type(arg))
+            if primitive_adapter is not None and primitive_adapter['to_value'] 
is not None:
+                value = primitive_adapter['to_value'](arg)
+                return 
self._arg_as_string(PrimitiveProviderDefinedType(primitive_adapter['type_name'],
 value))
+            adapter = 
self.pdt_registry.get_composite_adapter_by_class(type(arg))
             if adapter is not None and adapter['serialize'] is not None:
                 fields = adapter['serialize'](arg)
                 return 
self._arg_as_string(ProviderDefinedType(adapter['type_name'], fields))
diff --git a/gremlin-python/src/main/python/gremlin_python/structure/graph.py 
b/gremlin-python/src/main/python/gremlin_python/structure/graph.py
index 5d66e12bac..ac05c5c989 100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/graph.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/graph.py
@@ -204,19 +204,19 @@ class PrimitiveProviderDefinedType(object):
 
 class ProviderDefinedTypeRegistry(object):
     def __init__(self):
-        self._adapters_by_name = {}
-        self._adapters_by_class = {}
+        self._composite_adapters_by_name = {}
+        self._composite_adapters_by_class = {}
         self._primitive_adapters_by_name = {}
         self._primitive_adapters_by_class = {}
 
     def register(self, type_name, deserialize_fn, serialize_fn=None, 
target_class=None):
-        self._adapters_by_name[type_name] = {
+        self._composite_adapters_by_name[type_name] = {
             'deserialize': deserialize_fn,
             'serialize': serialize_fn,
             'target_class': target_class
         }
         if target_class is not None:
-            self._adapters_by_class[target_class] = {
+            self._composite_adapters_by_class[target_class] = {
                 'type_name': type_name,
                 'serialize': serialize_fn,
             }
@@ -293,7 +293,7 @@ class ProviderDefinedTypeRegistry(object):
             else:
                 hydrated_fields[k] = v
 
-        adapter = self._adapters_by_name.get(pdt.name)
+        adapter = self._composite_adapters_by_name.get(pdt.name)
         if adapter is None:
             return ProviderDefinedType(pdt.name, hydrated_fields) if changed 
else pdt
         try:
@@ -317,9 +317,9 @@ class ProviderDefinedTypeRegistry(object):
             logging.getLogger(__name__).warning(f"Primitive PDT hydration 
failed for '{pdt.name}': {e}")
             return pdt
 
-    def get_adapter_by_class(self, cls):
+    def get_composite_adapter_by_class(self, cls):
         """Return (type_name, serialize_fn) tuple for the given class, or 
None."""
-        return self._adapters_by_class.get(cls)
+        return self._composite_adapters_by_class.get(cls)
 
     def get_primitive_adapter_by_class(self, cls):
         """Return adapter dict for the given class, or None."""
diff --git a/gremlin-python/src/main/python/tests/integration/conftest.py 
b/gremlin-python/src/main/python/tests/integration/conftest.py
index de685fb4b9..0c4b48b06f 100644
--- a/gremlin-python/src/main/python/tests/integration/conftest.py
+++ b/gremlin-python/src/main/python/tests/integration/conftest.py
@@ -43,12 +43,19 @@ verbose_logging = False
 
 # Shared namedtuple used by remote_connection_with_registry fixture and its 
tests.
 RegistryPoint = namedtuple('RegistryPoint', ['x', 'y'])
+# Shared namedtuple used by remote_connection_with_primitive_registry fixture 
and its tests.
+RegistryUint32 = namedtuple('RegistryUint32', ['value'])
 
 
 @pytest.fixture
 def registry_point_class():
     return RegistryPoint
 
+
[email protected]
+def registry_uint32_class():
+    return RegistryUint32
+
 logging.basicConfig(format='%(asctime)s [%(levelname)8s] 
[%(filename)15s:%(lineno)d - %(funcName)10s()] - %(message)s',
                     level=logging.DEBUG if verbose_logging else logging.INFO)
 
@@ -241,6 +248,27 @@ def remote_connection_with_registry(request):
         return remote_conn
 
 
[email protected]
+def remote_connection_with_primitive_registry(request):
+    from gremlin_python.structure.graph import ProviderDefinedTypeRegistry
+
+    registry = ProviderDefinedTypeRegistry()
+    registry.register_primitive('Uint32',
+                                from_value=lambda v: 
RegistryUint32(value=int(v)),
+                                to_value=lambda u: str(u.value),
+                                target_class=RegistryUint32)
+    try:
+        remote_conn = DriverRemoteConnection(anonymous_url, 'gmodern', 
pdt_registry=registry)
+    except OSError:
+        pytest.skip('Gremlin Server is not running')
+    else:
+        def fin():
+            remote_conn.close()
+
+        request.addfinalizer(fin)
+        return remote_conn
+
+
 def json_interceptor(request):
         request['headers']['content-type'] = "application/json"
         request['payload'] = dumps({"gremlin": "g.inject(2)", "g": "g"})
diff --git 
a/gremlin-python/src/main/python/tests/integration/driver/test_client.py 
b/gremlin-python/src/main/python/tests/integration/driver/test_client.py
index 34fa004e12..7addce6d24 100644
--- a/gremlin-python/src/main/python/tests/integration/driver/test_client.py
+++ b/gremlin-python/src/main/python/tests/integration/driver/test_client.py
@@ -26,7 +26,7 @@ from gremlin_python.driver.client import Client
 from gremlin_python.driver.connection import GremlinServerError
 from gremlin_python.driver.request import RequestMessage
 from gremlin_python.driver.serializer import GraphBinarySerializersV4
-from gremlin_python.structure.graph import ProviderDefinedType
+from gremlin_python.structure.graph import ProviderDefinedType, 
PrimitiveProviderDefinedType
 from gremlin_python.process.graph_traversal import __, GraphTraversalSource
 from gremlin_python.process.traversal import TraversalStrategies, GValue
 from gremlin_python.process.strategies import OptionsStrategy
@@ -612,3 +612,54 @@ def test_pdt_in_collection(client):
     assert pdt_list[1].name == 'Point'
     assert pdt_list[1].fields['x'] == 3
     assert pdt_list[1].fields['y'] == 4
+
+
+def test_primitive_pdt_round_trip(client):
+    """Inject and retrieve a primitive Uint32 PDT (opaque string value)."""
+    results = client.submit(
+        "g.inject(PDT(\"Uint32\", \"4294967295\"))"
+    ).all().result()
+
+    assert len(results) == 1
+    pdt = results[0]
+    assert isinstance(pdt, PrimitiveProviderDefinedType)
+    assert pdt.name == 'Uint32'
+    assert pdt.value == '4294967295'
+
+
+def test_primitive_pdt_in_collection(client):
+    """Retrieve multiple primitive PDTs of different kinds as a list."""
+    results = client.submit(
+        "g.inject([PDT(\"Uint32\", \"42\"), PDT(\"TinkerId\", \"abc-123\")])"
+    ).all().result()
+
+    assert len(results) == 1
+    pdt_list = results[0]
+    assert isinstance(pdt_list, list)
+    assert len(pdt_list) == 2
+
+    assert isinstance(pdt_list[0], PrimitiveProviderDefinedType)
+    assert pdt_list[0].name == 'Uint32'
+    assert pdt_list[0].value == '42'
+
+    assert isinstance(pdt_list[1], PrimitiveProviderDefinedType)
+    assert pdt_list[1].name == 'TinkerId'
+    assert pdt_list[1].value == 'abc-123'
+
+
+def test_primitive_pdt_nested_in_composite(client):
+    """Inject and retrieve a composite PDT containing a nested primitive 
PDT."""
+    results = client.submit(
+        "g.inject(PDT(\"Measurement\", [\"unit\":\"meters\", 
\"quantity\":PDT(\"Uint32\", \"100\")]))"
+    ).all().result()
+
+    assert len(results) == 1
+    pdt = results[0]
+    assert isinstance(pdt, ProviderDefinedType)
+    assert pdt.name == 'Measurement'
+    assert pdt.fields['unit'] == 'meters'
+
+    quantity = pdt.fields['quantity']
+    assert isinstance(quantity, PrimitiveProviderDefinedType)
+    assert quantity.name == 'Uint32'
+    assert quantity.value == '100'
diff --git 
a/gremlin-python/src/main/python/tests/integration/driver/test_driver_remote_connection.py
 
b/gremlin-python/src/main/python/tests/integration/driver/test_driver_remote_connection.py
index cf853d43b8..f488442cc8 100644
--- 
a/gremlin-python/src/main/python/tests/integration/driver/test_driver_remote_connection.py
+++ 
b/gremlin-python/src/main/python/tests/integration/driver/test_driver_remote_connection.py
@@ -26,7 +26,7 @@ from gremlin_python.statics import long
 from gremlin_python.process.traversal import TraversalStrategy, P, Order, T, 
DT, GValue, Cardinality, Scope
 from gremlin_python.process.graph_traversal import __
 from gremlin_python.process.anonymous_traversal import traversal
-from gremlin_python.structure.graph import Vertex, Edge, Graph, 
ProviderDefinedType, provider_defined
+from gremlin_python.structure.graph import Vertex, Edge, Graph, 
ProviderDefinedType, PrimitiveProviderDefinedType, provider_defined
 from gremlin_python.process.strategies import SubgraphStrategy, SeedStrategy, 
ReservedKeysVerificationStrategy
 from gremlin_python.structure.io.util import HashableDict
 from gremlin_python.driver.connection import GremlinServerError
@@ -320,3 +320,20 @@ class TestDriverRemoteConnection(object):
         assert isinstance(result, TestPoint)
         assert result.x == 5
         assert result.y == 10
+
+    def test_primitive_pdt_round_trip_via_traversal(self, remote_connection):
+        g = traversal().with_(remote_connection)
+        pdt = PrimitiveProviderDefinedType('Uint32', '4294967295')
+        result = g.inject(pdt).next()
+        assert isinstance(result, PrimitiveProviderDefinedType)
+        assert result.name == 'Uint32'
+        assert result.value == '4294967295'
+
+    def test_primitive_pdt_registry_round_trip_via_traversal(self, 
remote_connection_with_primitive_registry,
+                                                             
registry_uint32_class):
+        g = traversal().with_(remote_connection_with_primitive_registry)
+        u = registry_uint32_class(value=42)
+        result = g.inject(u).next()
+        # Registry auto-dehydrates on send (to_value) and auto-hydrates on 
receive (from_value)
+        assert isinstance(result, registry_uint32_class)
+        assert result.value == 42
diff --git 
a/gremlin-python/src/main/python/tests/unit/structure/io/test_provider_defined_type.py
 
b/gremlin-python/src/main/python/tests/unit/structure/io/test_provider_defined_type.py
index 147e4b670e..13c5c1b17d 100644
--- 
a/gremlin-python/src/main/python/tests/unit/structure/io/test_provider_defined_type.py
+++ 
b/gremlin-python/src/main/python/tests/unit/structure/io/test_provider_defined_type.py
@@ -88,18 +88,18 @@ class TestProviderDefinedTypeRegistry(object):
                           deserialize_fn=lambda fields: Point(fields["x"], 
fields["y"]),
                           serialize_fn=lambda p: {"x": p.x, "y": p.y},
                           target_class=Point)
-        adapter = registry.get_adapter_by_class(Point)
+        adapter = registry.get_composite_adapter_by_class(Point)
         fields = adapter['serialize'](Point(1.0, 2.0))
         assert fields == {"x": 1.0, "y": 2.0}
 
     def test_dehydrate_no_adapter_returns_none(self):
         registry = ProviderDefinedTypeRegistry()
-        assert registry.get_adapter_by_class(str) is None
+        assert registry.get_composite_adapter_by_class(str) is None
 
     def test_dehydrate_no_serialize_fn_returns_none(self):
         registry = ProviderDefinedTypeRegistry()
         registry.register("com.example.Thing", deserialize_fn=lambda fields: 
fields, target_class=dict)
-        adapter = registry.get_adapter_by_class(dict)
+        adapter = registry.get_composite_adapter_by_class(dict)
         assert adapter['serialize'] is None
 
     def test_hydrate_inner_registered_in_unregistered_outer(self):
@@ -144,7 +144,7 @@ class TestProviderDefinedTypeRegistryBuild(object):
                 mock_entry_points.return_value = {'tinkerpop.pdt': [mock_ep]}
 
             registry = ProviderDefinedTypeRegistry.create()
-            assert "com.mock.Type" in registry._adapters_by_name
+            assert "com.mock.Type" in registry._composite_adapters_by_name
 
     def test_build_handles_failing_entry_point(self):
         from unittest.mock import patch, MagicMock
@@ -162,7 +162,7 @@ class TestProviderDefinedTypeRegistryBuild(object):
 
             registry = ProviderDefinedTypeRegistry.create()
             assert isinstance(registry, ProviderDefinedTypeRegistry)
-            assert len(registry._adapters_by_name) == 0
+            assert len(registry._composite_adapters_by_name) == 0
 
 
 class TestReaderAutoHydration(object):
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index 1416f35887..c328707d72 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -41,6 +41,10 @@ import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefined;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDTAdapter;
 import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry;
+import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType;
+import org.apache.tinkerpop.gremlin.server.pdt.Measurement;
+import org.apache.tinkerpop.gremlin.server.pdt.Uint32;
+import org.apache.tinkerpop.gremlin.server.pdt.Uint32Adapter;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
 import org.apache.tinkerpop.gremlin.util.ExceptionHelper;
 import org.apache.tinkerpop.gremlin.util.TimeUtil;
@@ -1395,6 +1399,80 @@ public class GremlinDriverIntegrateTest extends 
AbstractGremlinServerIntegration
         }
     }
 
+    @Test
+    public void shouldRoundTripRawPrimitivePdtViaTraversal() {
+        // Unregistered base case: with no adapter, a 
PrimitiveProviderDefinedType round-trips as-is.
+        final Cluster cluster = TestClientFactory.build().create();
+        try {
+            final GraphTraversalSource g = 
traversal().with(DriverRemoteConnection.using(cluster));
+            final PrimitiveProviderDefinedType pdt = new 
PrimitiveProviderDefinedType("UnregisteredPrimitive", "4294967295");
+            final Object result = g.inject(pdt).next();
+
+            assertTrue("Expected PrimitiveProviderDefinedType but got: " + 
result.getClass().getName(),
+                    result instanceof PrimitiveProviderDefinedType);
+            final PrimitiveProviderDefinedType r = 
(PrimitiveProviderDefinedType) result;
+            assertEquals("UnregisteredPrimitive", r.getName());
+            assertEquals("4294967295", r.getValue());
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void shouldRoundTripRegisteredPrimitivePdtViaTraversal() {
+        // Registered case: a raw provider object auto-dehydrates on send and 
auto-hydrates on receive.
+        final ProviderDefinedTypeRegistry registry = 
ProviderDefinedTypeRegistry.empty();
+        registry.register(new Uint32Adapter());
+
+        final Cluster cluster = TestClientFactory.build()
+                .serializer(new 
GraphBinaryMessageSerializerV4(TypeSerializerRegistry.INSTANCE, registry))
+                .create();
+        try {
+            final DriverRemoteConnection connection = 
DriverRemoteConnection.using(cluster);
+            connection.setPdtRegistry(registry);
+            final GraphTraversalSource g = traversal().with(connection);
+
+            final Object result = g.inject(new Uint32(42L)).next();
+
+            assertTrue("Expected Uint32 but got: " + 
result.getClass().getName(), result instanceof Uint32);
+            assertEquals(42L, ((Uint32) result).getValue());
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void shouldRoundTripRegistryNestedPrimitivePdtViaTraversal() {
+        // Registered nested case: a composite containing a primitive PDT 
field, both adapters registered,
+        // auto-dehydrates recursively on send and auto-hydrates recursively 
on receive.
+        // Outer composite is the @ProviderDefined "Measurement" (registered 
by class); inner primitive
+        // uses the Uint32 adapter. Exercises recursive de/hydration across 
both PDT kinds.
+        final ProviderDefinedTypeRegistry registry = 
ProviderDefinedTypeRegistry.empty();
+        registry.register(new Uint32Adapter());
+        registry.register(Measurement.class);
+
+        final Cluster cluster = TestClientFactory.build()
+                .serializer(new 
GraphBinaryMessageSerializerV4(TypeSerializerRegistry.INSTANCE, registry))
+                .create();
+        try {
+            final DriverRemoteConnection connection = 
DriverRemoteConnection.using(cluster);
+            connection.setPdtRegistry(registry);
+            final GraphTraversalSource g = traversal().with(connection);
+
+            final Object result = g.inject(new Measurement("meters", new 
Uint32(100L))).next();
+
+            assertTrue("Expected Measurement but got: " + 
result.getClass().getName(),
+                    result instanceof Measurement);
+            final Measurement m = (Measurement) result;
+            assertEquals("meters", m.unit);
+            assertTrue("Expected nested Uint32 but got: " + 
m.quantity.getClass().getName(),
+                    m.quantity instanceof Uint32);
+            assertEquals(100L, m.quantity.getValue());
+        } finally {
+            cluster.close();
+        }
+    }
+
     // --- PDT helper types ---
 
     static class TestPoint {
@@ -1424,4 +1502,5 @@ public class GremlinDriverIntegrateTest extends 
AbstractGremlinServerIntegration
         public TestAnnotatedPoint() {}
         TestAnnotatedPoint(final int x, final int y) { this.x = x; this.y = y; 
}
     }
+
 }
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerPrimitivePdtIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerPrimitivePdtIntegrateTest.java
index def25a2935..dcdae9d535 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerPrimitivePdtIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerPrimitivePdtIntegrateTest.java
@@ -23,7 +23,6 @@ import org.apache.tinkerpop.gremlin.driver.Cluster;
 import org.apache.tinkerpop.gremlin.driver.Result;
 import org.apache.tinkerpop.gremlin.server.pdt.TinkerId;
 import org.apache.tinkerpop.gremlin.server.pdt.Uint32;
-import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType;
 import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType;
 import org.junit.After;
 import org.junit.Before;
@@ -32,7 +31,6 @@ import org.junit.Test;
 import java.util.List;
 
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.Is.is;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java
index 0d57c515e5..817b6c6dc0 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java
@@ -23,12 +23,7 @@ import org.apache.tinkerpop.gremlin.structure.io.Buffer;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
 import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
-import org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDTAdapter;
-import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDTAdapter;
-import 
org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType;
-import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefined;
-import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType;
-import 
org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.*;
 import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory;
 import org.junit.Test;
 
@@ -37,6 +32,7 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
@@ -77,6 +73,24 @@ public class GraphBinaryWriterPdtTest {
         }
     }
 
+    @ProviderDefined(name = "AnnotatedName")
+    static class AnnotatedDual {
+        int x = 7;
+    }
+
+    static class AnnotatedDualAdapter implements 
CompositePDTAdapter<AnnotatedDual> {
+        @Override public String typeName() { return "AdapterName"; }
+        @Override public Class<AnnotatedDual> targetClass() { return 
AnnotatedDual.class; }
+        @Override public Map<String, Object> toFields(final AnnotatedDual obj) 
{
+            final Map<String, Object> m = new LinkedHashMap<>();
+            m.put("viaAdapter", obj.x * 10);
+            return m;
+        }
+        @Override public AnnotatedDual fromFields(final Map<String, Object> 
fields) {
+            return new AnnotatedDual();
+        }
+    }
+
     @Test
     public void shouldAutoConvertAnnotatedObjectToPdt() throws IOException {
         final Buffer buffer = bufferFactory.create(allocator.buffer());
@@ -121,6 +135,29 @@ public class GraphBinaryWriterPdtTest {
         assertEquals(42, result.value);
     }
 
+    /**
+     * A registered adapter takes precedence over the {@link ProviderDefined} 
annotation when dehydrating on
+     * the write path, consistent with GremlinLang.argAsString. AnnotatedDual 
is both annotated and has a
+     * registered CompositePDTAdapter; the adapter's type name and fields must 
win.
+     */
+    @Test
+    public void shouldPreferRegisteredAdapterOverAnnotationOnWritePath() 
throws IOException {
+        final ProviderDefinedTypeRegistry pdtRegistry = 
ProviderDefinedTypeRegistry.empty();
+        pdtRegistry.register(new AnnotatedDualAdapter());
+
+        final GraphBinaryWriter registryWriter = new 
GraphBinaryWriter(TypeSerializerRegistry.INSTANCE, pdtRegistry);
+
+        final Buffer buffer = bufferFactory.create(allocator.buffer());
+        registryWriter.write(new AnnotatedDual(), buffer);
+        buffer.readerIndex(0);
+
+        // Read with a registry-free reader to inspect the raw serialized form 
(no hydration).
+        final ProviderDefinedType result = reader.read(buffer);
+        assertEquals("AdapterName", result.getName());
+        assertEquals(70, result.getFields().get("viaAdapter"));
+        assertFalse(result.getFields().containsKey("x"));
+    }
+
     @Test
     public void shouldNotDoubleWrapProviderDefinedType() throws IOException {
         final Map<String, Object> fields = new LinkedHashMap<>();

Reply via email to