| 390 | * ══════════════════════════════════════════════════════════════════ */ |
| 391 | |
| 392 | static const char *py_classify_receiver(CBMExtractCtx *ctx, TSNode object_node) { |
| 393 | char *text = cbm_node_text(ctx->arena, object_node, ctx->source); |
| 394 | if (!text) { |
| 395 | return NULL; |
| 396 | } |
| 397 | const char *tail = text; |
| 398 | const char *dot = strrchr(tail, '.'); |
| 399 | if (dot) { |
| 400 | tail = dot + SKIP_ONE; |
| 401 | } |
| 402 | /* python-socketio */ |
| 403 | if (strcmp(tail, "sio") == 0 || strcmp(tail, "socketio") == 0 || strcmp(tail, "socket") == 0) { |
| 404 | return "socketio"; |
| 405 | } |
| 406 | /* Django Channels */ |
| 407 | if (strcmp(tail, "channel_layer") == 0) { |
| 408 | return "django_channels"; |
| 409 | } |
| 410 | /* FastAPI/Starlette WebSocket */ |
| 411 | if (strcmp(tail, "websocket") == 0 || strcmp(tail, "ws") == 0) { |
| 412 | return "websocket"; |
| 413 | } |
| 414 | /* kafka-python */ |
| 415 | if (strcmp(tail, "producer") == 0) { |
| 416 | return "kafka"; |
| 417 | } |
| 418 | if (strcmp(tail, "consumer") == 0) { |
| 419 | return "kafka"; |
| 420 | } |
| 421 | return NULL; |
| 422 | } |
| 423 | |
| 424 | /* Table-driven Python method→direction classification. |
| 425 | * NULL transport matches any transport not matched by earlier rows. */ |
no test coverage detected