Copilot commented on code in PR #3595:
URL: https://github.com/apache/thrift/pull/3595#discussion_r3407591011


##########
lib/py/test/thrift_TBinaryProtocol.py:
##########
@@ -22,7 +22,9 @@
 import uuid
 
 import _import_local_thrift  # noqa
+from thrift.Thrift import TApplicationException
 from thrift.protocol.TBinaryProtocol import TBinaryProtocol
+from thrift.protocol.TBinaryProtocol import TBinaryProtocolAcceleratedFactory

Review Comment:
   Importing `TBinaryProtocolAcceleratedFactory` at module import time can make 
the entire test module fail to import in environments where that symbol isn’t 
present, even though the test itself is guarded by `fastbinary` availability. 
Consider importing `TBinaryProtocolAcceleratedFactory` inside `setUp()` or the 
specific test with a `try/except ImportError` and skipping the test (or the 
module) when unavailable.



##########
lib/py/test/thrift_TBinaryProtocol.py:
##########
@@ -280,6 +299,26 @@ def test_TBinaryProtocol_write_read(self):
             print("Assertion fail")
             raise e
 
+    def test_accelerated_utf8_roundtrip_on_application_exception(self):
+        if not self._has_fastbinary:
+            self.skipTest("C extension not available")
+
+        original = TApplicationException(
+            type=TApplicationException.PROTOCOL_ERROR,
+            message=("snowman-\u2603-rocket-\U0001F680-" * 32),
+        )
+
+        otrans = TTransport.TMemoryBuffer()
+        oproto = 
TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(otrans)
+        oproto.trans.write(oproto._fast_encode(original, 
APPLICATION_EXCEPTION_TYPEARGS))
+
+        itrans = TTransport.TMemoryBuffer(otrans.getvalue())
+        iproto = 
TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(itrans)
+        decoded = iproto._fast_decode(None, iproto, 
APPLICATION_EXCEPTION_TYPEARGS)

Review Comment:
   This test calls private APIs (`_fast_encode` / `_fast_decode`) directly. 
That’s brittle across Thrift versions and can fail even when `fastbinary` is 
present but the protocol instance doesn’t expose these methods the same way. 
Consider guarding with `hasattr(oproto, \"_fast_encode\")` / `hasattr(iproto, 
\"_fast_decode\")` and skipping if absent, or using a public accelerated 
encode/decode entrypoint if the library provides one.



##########
lib/py/test/thrift_TBinaryProtocol.py:
##########
@@ -280,6 +299,26 @@ def test_TBinaryProtocol_write_read(self):
             print("Assertion fail")
             raise e
 
+    def test_accelerated_utf8_roundtrip_on_application_exception(self):
+        if not self._has_fastbinary:
+            self.skipTest("C extension not available")
+
+        original = TApplicationException(
+            type=TApplicationException.PROTOCOL_ERROR,
+            message=("snowman-\u2603-rocket-\U0001F680-" * 32),
+        )
+
+        otrans = TTransport.TMemoryBuffer()
+        oproto = 
TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(otrans)
+        oproto.trans.write(oproto._fast_encode(original, 
APPLICATION_EXCEPTION_TYPEARGS))

Review Comment:
   This test calls private APIs (`_fast_encode` / `_fast_decode`) directly. 
That’s brittle across Thrift versions and can fail even when `fastbinary` is 
present but the protocol instance doesn’t expose these methods the same way. 
Consider guarding with `hasattr(oproto, \"_fast_encode\")` / `hasattr(iproto, 
\"_fast_decode\")` and skipping if absent, or using a public accelerated 
encode/decode entrypoint if the library provides one.



##########
lib/py/src/ext/protocol.tcc:
##########
@@ -456,18 +456,32 @@ bool ProtocolBase<Impl>::encodeValue(PyObject* value, 
TType type, PyObject* type
 
   case T_STRING: {
     ScopedPyObject nval;
+    Py_ssize_t len;
 
     if (PyUnicode_Check(value)) {
+#if PY_VERSION_HEX >= 0x03030000
+      const char* str = PyUnicode_AsUTF8AndSize(value, &len);
+      if (!str) {
+        return false;
+      }
+      if (!detail::check_ssize_t_32(len)) {
+        return false;
+      }
+
+      impl()->writeI32(static_cast<int32_t>(len));
+      return writeBuffer(const_cast<char*>(str), static_cast<size_t>(len));

Review Comment:
   Using `const_cast<char*>` here is avoidable and potentially unsafe if 
`writeBuffer` ever writes into the buffer (even accidentally). Prefer changing 
`writeBuffer` to accept a `const char*` (or adding an overload) and pass `str` 
through without casting. If changing the signature isn’t feasible, a safe 
alternative is to copy into a mutable buffer before calling `writeBuffer` (with 
the tradeoff of allocating).



##########
lib/py/test/thrift_TBinaryProtocol.py:
##########
@@ -167,6 +169,16 @@ def testField(type, data):
     protocol.readStructEnd()
 
 
+APPLICATION_EXCEPTION_TYPEARGS = [
+    TApplicationException,
+    (
+        None,
+        (1, 11, "message", "UTF8", None),
+        (2, 8, "type", None, None),
+    ),
+]

Review Comment:
   `*_TYPEARGS` in Thrift’s accelerated paths are typically tuples (and may be 
used as cache keys or assumed immutable by downstream helpers). Using a list 
here is more error-prone and can break if internals assume tuple semantics. 
Consider making this a tuple (e.g., `(..., (...))`) to match common Thrift 
fastbinary conventions and avoid accidental mutation between tests.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to