From bf9227274e63198956d6351333543faeb12a6abd Mon Sep 17 00:00:00 2001 From: sreenithi Date: Mon, 29 Jun 2026 10:28:14 +0000 Subject: [PATCH 1/4] add limited api configs for grpcio pkg setup --- pyproject.toml | 3 +++ setup.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index feed2932bbcb9..e02aa672b095f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,9 @@ where = ["src/python/grpcio"] "*.pxi", ] +[tool.distutils.bdist_wheel] +py_limited_api = "cp310" + [tool.pyright] include = [ "src/python/grpcio/grpc/aio/_call.py", diff --git a/setup.py b/setup.py index 88b727156d7c7..151c108110a90 100644 --- a/setup.py +++ b/setup.py @@ -500,6 +500,7 @@ def _quote_build_define(argument): DEFINE_MACROS += (("PyMODINIT_FUNC", pymodinit),) DEFINE_MACROS += (("GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK", 1),) +DEFINE_MACROS += (("Py_LIMITED_API", "0x030A0000"),) def cython_extensions_and_necessity(): cython_module_files = [ @@ -535,6 +536,7 @@ def cython_extensions_and_necessity(): extra_objects=extra_objects, extra_compile_args=list(CFLAGS), extra_link_args=list(LDFLAGS), + py_limited_api=True, ) for (module_name, module_file) in zip( list(CYTHON_EXTENSION_MODULE_NAMES), cython_module_files From 5eeb5270e3130e1947a99e0c5e2ddd3b46c1fe5c Mon Sep 17 00:00:00 2001 From: sreenithi Date: Mon, 29 Jun 2026 10:29:08 +0000 Subject: [PATCH 2/4] remove Exception inheritance in AioRpcStatus Cython class --- src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pxd.pxi | 2 +- src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pyx.pxi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pxd.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pxd.pxi index 3780d8ddf2f95..076be94272ed1 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pxd.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pxd.pxi @@ -14,7 +14,7 @@ """Exceptions for the aio version of the RPC calls.""" -cdef class AioRpcStatus(Exception): +cdef class AioRpcStatus: cdef readonly: grpc_status_code _code str _details diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pyx.pxi index 70ffb94e5821b..fc1adbce8af1e 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/aio/rpc_status.pyx.pxi @@ -14,7 +14,7 @@ """Exceptions for the aio version of the RPC calls.""" -cdef class AioRpcStatus(Exception): +cdef class AioRpcStatus: # The final status of gRPC is represented by three trailing metadata: # `grpc-status`, `grpc-status-message`, and `grpc-status-details`. From a931364d034adb345d618262c98253be35a33b91 Mon Sep 17 00:00:00 2001 From: sreenithi Date: Mon, 29 Jun 2026 10:43:32 +0000 Subject: [PATCH 3/4] migrate `PyBytes_GET_SIZE` to Limited API friendly PyBytes_AsStringAndSize --- .../_cygrpc/private_key_offload.pyx.pxi | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/private_key_offload.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/private_key_offload.pyx.pxi index a8356bbd0589b..95544a7586d0a 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/private_key_offload.pyx.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/private_key_offload.pyx.pxi @@ -11,9 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from cpython.bytes cimport PyBytes_FromStringAndSize +from cpython.bytes cimport PyBytes_FromStringAndSize, PyBytes_AsStringAndSize from libcpp.utility cimport move -from cpython cimport PyObject, PyBytes_AsString, PyBytes_GET_SIZE +from cpython cimport PyObject, PyBytes_AsString import threading @@ -31,16 +31,22 @@ cdef class OnCompleteWrapper: def __call__(self, result): cdef StatusOr[string] cpp_result cdef string cpp_string - cdef shared_ptr[PrivateKeySignerPyWrapper.CompletionContext] locked_completion_context + cdef shared_ptr[PrivateKeySignerPyWrapper.CompletionContext] locked_completion_context cdef PrivateKeySignerPyWrapper.CompletionContext* local_completion_context + cdef char* c_buffer + cdef Py_ssize_t c_size + if isinstance(result, bytes): - # We got a signature - cpp_string = MakeStringForCython(PyBytes_AsString(result), PyBytes_GET_SIZE(result)) + # We got a signature, safely extract both the pointer and the size + PyBytes_AsStringAndSize(result, &c_buffer, &c_size) + cpp_string = MakeStringForCython(c_buffer, c_size) cpp_result = MakeStringResult(cpp_string) + elif isinstance(result, Exception): # If python returns an exception, convert to absl::Status cpp_string = MakeStringForCython(PyBytes_AsString(str(result).encode('utf-8'))) cpp_result = MakeInternalError(cpp_string) + else: # Any other return type is not valid cpp_string = MakeStringForCython(PyBytes_AsString(f"Invalid result type: {type(result)}".encode('utf-8'))) @@ -56,6 +62,9 @@ cdef PrivateKeySignerPyWrapper.PrivateKeySignerPyWrapperResult async_sign_wrappe cdef const char* data cdef size_t size cdef PrivateKeySignerPyWrapper.PrivateKeySignerPyWrapperResult cpp_result + cdef char* c_buffer + cdef Py_ssize_t c_size + with gil: # Cast the PyObject* pointer holding the user's python sign impl py_user_func = py_user_sign_fn @@ -72,16 +81,22 @@ cdef PrivateKeySignerPyWrapper.PrivateKeySignerPyWrapperResult async_sign_wrappe size = inp.length() py_bytes = PyBytes_FromStringAndSize(data, size) py_result = py_user_func(py_bytes, algorithm, py_on_complete_wrapper) + if isinstance(py_result, bytes): - # We got a signature - cpp_string = MakeStringForCython(PyBytes_AsString(py_result), PyBytes_GET_SIZE(py_result)) + # We got a signature, process it + + # This safely extracts both the pointer and the size via the Limited API + PyBytes_AsStringAndSize(py_result, &c_buffer, &c_size) + cpp_string = MakeStringForCython(c_buffer, c_size) cpp_result.sync_result = MakeStringResult(cpp_string) + elif callable(py_result): # Cancellation func cpp_result.is_sync = False Py_INCREF(py_result) cpp_result.async_result.py_user_cancel_fn = py_result cpp_result.async_result.cancel_wrapper = cancel_wrapper + else: # Any other return type is not valid cpp_string = MakeStringForCython(PyBytes_AsString(f"Invalid result type: {type(py_result)}".encode('utf-8'))) From dceac4c6ec1656e48a932fb06c0491bd1f8cde3f Mon Sep 17 00:00:00 2001 From: sreenithi <22791051+sreenithi@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:03:34 +0000 Subject: [PATCH 4/4] Automated change: Fix sanity tests --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 151c108110a90..a12014a1add4e 100644 --- a/setup.py +++ b/setup.py @@ -502,6 +502,7 @@ def _quote_build_define(argument): DEFINE_MACROS += (("Py_LIMITED_API", "0x030A0000"),) + def cython_extensions_and_necessity(): cython_module_files = [ os.path.join(PYTHON_STEM, name.replace(".", "/") + ".pyx")