Process a single Python import_from_statement node (from X import Y [as Z]).
| 246 | |
| 247 | // Process a single Python import_from_statement node (from X import Y [as Z]). |
| 248 | static void process_py_import_from(CBMExtractCtx *ctx, TSNode node) { |
| 249 | CBMArena *a = ctx->arena; |
| 250 | // `from __future__ import annotations` is a dedicated node type whose first |
| 251 | // child is the literal `__future__` keyword (an identifier, not a |
| 252 | // dotted_name). Emit a single import for `__future__` and return. |
| 253 | if (strcmp(ts_node_type(node), "future_import_statement") == 0) { |
| 254 | CBMImport imp = {.local_name = cbm_arena_strdup(a, "__future__"), |
| 255 | .module_path = cbm_arena_strdup(a, "__future__")}; |
| 256 | cbm_imports_push(&ctx->result->imports, a, imp); |
| 257 | return; |
| 258 | } |
| 259 | TSNode module_node = resolve_py_module_node(node); |
| 260 | char *mod_path = |
| 261 | ts_node_is_null(module_node) ? NULL : cbm_node_text(a, module_node, ctx->source); |
| 262 | |
| 263 | uint32_t nc = ts_node_child_count(node); |
| 264 | bool emitted = false; |
| 265 | for (uint32_t j = 0; j < nc; j++) { |
| 266 | TSNode child = ts_node_child(node, j); |
| 267 | const char *ck = ts_node_type(child); |
| 268 | if (strcmp(ck, "identifier") == 0 || strcmp(ck, "dotted_name") == 0) { |
| 269 | if (!ts_node_is_null(module_node) && |
| 270 | ts_node_start_byte(child) == ts_node_start_byte(module_node)) { |
| 271 | continue; |
| 272 | } |
| 273 | emit_py_import_from_name(ctx, child, mod_path); |
| 274 | emitted = true; |
| 275 | } else if (strcmp(ck, "aliased_import") == 0) { |
| 276 | emit_py_aliased_import(ctx, child, mod_path); |
| 277 | emitted = true; |
| 278 | } else if (strcmp(ck, "wildcard_import") == 0) { |
| 279 | // `from os.path import *` — the module itself is the import. |
| 280 | if (mod_path && mod_path[0]) { |
| 281 | CBMImport imp = {.local_name = path_last(a, mod_path), .module_path = mod_path}; |
| 282 | cbm_imports_push(&ctx->result->imports, a, imp); |
| 283 | emitted = true; |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | // Defensive: a from-import with a module but no recognized name child |
| 288 | // (grammar variant) still records the module as an import. |
| 289 | if (!emitted && mod_path && mod_path[0]) { |
| 290 | CBMImport imp = {.local_name = path_last(a, mod_path), .module_path = mod_path}; |
| 291 | cbm_imports_push(&ctx->result->imports, a, imp); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | static void parse_python_imports(CBMExtractCtx *ctx) { |
| 296 | TSTreeCursor cursor = ts_tree_cursor_new(ctx->root); |
no test coverage detected