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