@@ -61,6 +61,8 @@ static void ShareConsumer_dealloc(ShareConsumerHandle *self) {
6161 if (self -> rkshare ) {
6262 CallState cs ;
6363 CallState_begin ((Handle * )self , & cs );
64+ /* TODO KIP-932: Use rd_kafka_share_destroy_flags() once
65+ * available in the librdkafka public API. */
6466 rd_kafka_share_destroy (self -> rkshare );
6567 self -> rkshare = NULL ;
6668 CallState_end ((Handle * )self , & cs );
@@ -197,12 +199,12 @@ static PyObject *ShareConsumer_subscription(ShareConsumerHandle *self) {
197199
198200
199201/**
200- * @brief Consume a batch of messages from the share consumer.
202+ * @brief Poll for a batch of messages from the share consumer.
201203 *
202204 */
203- static PyObject * ShareConsumer_consume_batch (ShareConsumerHandle * self ,
204- PyObject * args ,
205- PyObject * kwargs ) {
205+ static PyObject * ShareConsumer_poll (ShareConsumerHandle * self ,
206+ PyObject * args ,
207+ PyObject * kwargs ) {
206208 double tmout = -1.0f ;
207209 static char * kws [] = {"timeout" , NULL };
208210 rd_kafka_message_t * * rkmessages = NULL ;
@@ -299,20 +301,7 @@ static PyObject *ShareConsumer_consume_batch(ShareConsumerHandle *self,
299301
300302 /* Handle error from rd_kafka_share_consume_batch() */
301303 if (error ) {
302- const char * error_str = rd_kafka_error_string (error );
303- int is_fatal = rd_kafka_error_is_fatal (error );
304- int is_retriable = rd_kafka_error_is_retriable (error );
305-
306- if (is_fatal ) {
307- PyErr_Format (PyExc_RuntimeError , "Fatal error: %s" ,
308- error_str );
309- } else {
310- PyErr_Format (KafkaException ,
311- "Error: %s (retriable: %s)" , error_str ,
312- is_retriable ? "yes" : "no" );
313- }
314-
315- rd_kafka_error_destroy (error );
304+ cfl_PyErr_from_error_destroy (error );
316305 free (rkmessages );
317306 return NULL ;
318307 }
@@ -348,6 +337,8 @@ static PyObject *ShareConsumer_close(ShareConsumerHandle *self) {
348337 Py_RETURN_NONE ;
349338
350339 CallState_begin ((Handle * )self , & cs );
340+ /* TODO KIP-932: rd_kafka_share_consumer_close() return type will change
341+ * to rd_kafka_error_t *. Update error handling accordingly. */
351342 err = rd_kafka_share_consumer_close (self -> rkshare );
352343 rd_kafka_share_destroy (self -> rkshare );
353344 self -> rkshare = NULL ;
@@ -364,6 +355,36 @@ static PyObject *ShareConsumer_close(ShareConsumerHandle *self) {
364355}
365356
366357
358+ /**
359+ * @brief Context manager entry — returns self.
360+ */
361+ static PyObject * ShareConsumer_enter (ShareConsumerHandle * self ) {
362+ Py_INCREF (self );
363+ return (PyObject * )self ;
364+ }
365+
366+ /**
367+ * @brief Context manager exit — calls close().
368+ */
369+ static PyObject * ShareConsumer_exit (ShareConsumerHandle * self , PyObject * args ) {
370+ PyObject * exc_type , * exc_value , * exc_traceback ;
371+
372+ if (!PyArg_UnpackTuple (args , "__exit__" , 3 , 3 , & exc_type , & exc_value ,
373+ & exc_traceback ))
374+ return NULL ;
375+
376+ /* Cleanup: call close() */
377+ if (self -> rkshare ) {
378+ PyObject * result = ShareConsumer_close (self );
379+ if (!result )
380+ return NULL ;
381+ Py_DECREF (result );
382+ }
383+
384+ Py_RETURN_NONE ;
385+ }
386+
387+
367388/**
368389 * @brief ShareConsumer methods.
369390 */
@@ -399,14 +420,10 @@ static PyMethodDef ShareConsumer_methods[] = {
399420 " :raises RuntimeError: if called on a closed share consumer\n"
400421 "\n" },
401422
402- {"consume_batch" , (PyCFunction )ShareConsumer_consume_batch ,
403- METH_VARARGS | METH_KEYWORDS ,
404- ".. py:function:: consume_batch([timeout=-1])\n"
423+ {"poll" , (PyCFunction )ShareConsumer_poll , METH_VARARGS | METH_KEYWORDS ,
424+ ".. py:function:: poll([timeout=-1])\n"
405425 "\n"
406- " Consume a batch of messages from the share consumer.\n"
407- "\n"
408- " This is the ONLY consumption method for ShareConsumer.\n"
409- " Share consumers do NOT have a poll() method - they are batch-only.\n"
426+ " Poll for a batch of messages from the share consumer.\n"
410427 "\n"
411428 " The application must check each Message object's error() method\n"
412429 " to distinguish between proper messages (error() returns None)\n"
@@ -421,9 +438,8 @@ static PyMethodDef ShareConsumer_methods[] = {
421438 " Default: -1 (infinite)\n"
422439 " :returns: List of Message objects (possibly empty on timeout)\n"
423440 " :rtype: list(Message)\n"
424- " :raises RuntimeError: if called on a closed share consumer or on "
425- "fatal error\n"
426- " :raises KafkaException: on non-fatal errors\n"
441+ " :raises KafkaException: on error\n"
442+ " :raises RuntimeError: if called on a closed share consumer\n"
427443 " :raises KeyboardInterrupt: if Ctrl+C pressed during consumption\n"
428444 "\n" },
429445
@@ -438,6 +454,15 @@ static PyMethodDef ShareConsumer_methods[] = {
438454 " :raises KafkaException: on error\n"
439455 "\n" },
440456
457+ /* TODO KIP-932: Add set_sasl_credentials once librdkafka exposes
458+ * rd_kafka_sasl_set_credentials() (or the underlying rd_kafka_t *)
459+ * for rd_kafka_share_t handles. */
460+
461+ {"__enter__" , (PyCFunction )ShareConsumer_enter , METH_NOARGS ,
462+ "Context manager entry." },
463+ {"__exit__" , (PyCFunction )ShareConsumer_exit , METH_VARARGS ,
464+ "Context manager exit. Automatically closes the share consumer." },
465+
441466 {NULL }};
442467
443468
@@ -530,10 +555,6 @@ PyTypeObject ShareConsumerType = {
530555 "assigned to multiple consumers. Messages are delivered to only one "
531556 "consumer.\n"
532557 "\n"
533- ".. note::\n"
534- " ShareConsumer only supports batch consumption via consume_batch().\n"
535- " There is NO poll() method for single messages.\n"
536- "\n"
537558 ":param dict config: Configuration properties. At a minimum, "
538559 "``group.id`` **must** be set and ``bootstrap.servers`` **should** be "
539560 "set.\n"
0 commit comments