Conversation
As defined in PEP-810. We implement this in much the same way as how we handle `async` annotations currently. The relevant nodes get an `is_lazy` field that defaults to being false.
Otherwise these will disappear every time we regenerate the AST.
Adds a new `isLazy` predicate to the relevant classes, and adds the relevant dbscheme (and up/downgrade) changes. On upgrades we do nothing, and on downgrades we remove the `is_lazy` bits.
There was a problem hiding this comment.
Pull request overview
Adds Python extractor + QL support for PEP 810 “lazy imports” by threading a new is_lazy boolean through the parser/DB scheme and exposing it as isLazy() on relevant import AST nodes.
Changes:
- Extend the tree-sitter grammar and TSG mapping to recognize
lazy import .../lazy from ... import ...and setis_lazy. - Add
is_lazyto the Python DB scheme forImportandImportStar, including upgrade/downgrade plumbing. - Expose
isLazy()in the generated QL AST classes and add extractor/parser tests and a change note.
Show a summary per file
| File | Description |
|---|---|
| python/ql/test/3/extractor-tests/lazy-imports/test.ql | New extractor test query validating Import.isLazy() output. |
| python/ql/test/3/extractor-tests/lazy-imports/test.py | New Python source for lazy vs non-lazy import extraction tests. |
| python/ql/test/3/extractor-tests/lazy-imports/test.expected | Expected results for the new extractor test. |
| python/ql/lib/upgrades/279cbb08d387ecd57ac177e87c94cfd5ca62f792/upgrade.properties | DB upgrade metadata for adding is_lazy. |
| python/ql/lib/upgrades/279cbb08d387ecd57ac177e87c94cfd5ca62f792/semmlecode.python.dbscheme | Upgrade dbscheme snapshot reflecting the new fields. |
| python/ql/lib/upgrades/279cbb08d387ecd57ac177e87c94cfd5ca62f792/old.dbscheme | Prior dbscheme snapshot for the upgrade. |
| python/ql/lib/semmlecode.python.dbscheme | Adds Import.is_lazy / ImportStar.is_lazy fields and updates @py_bool_parent. |
| python/ql/lib/semmle/python/AstGenerated.qll | Adds isLazy() predicates for Import and ImportStar AST nodes. |
| python/ql/lib/change-notes/2026-04-10-support-lazy-keyword.md | Change note documenting the new syntax support. |
| python/extractor/tsg-python/tsp/src/tree_sitter/array.h | Refactors array delete/swap helpers used by the embedded tree-sitter runtime. |
| python/extractor/tsg-python/tsp/src/node-types.json | Updates node types to include the is_lazy field and lazy token. |
| python/extractor/tsg-python/tsp/src/grammar.json | Generated grammar updates to parse optional lazy on import statements. |
| python/extractor/tsg-python/tsp/grammar.js | Grammar source changes adding optional lazy to import statements and soft-keyword handling. |
| python/extractor/tsg-python/python.tsg | Sets is_lazy attribute on import parse nodes when lazy is present. |
| python/extractor/tests/parser/lazy_imports_new.py | New parser test corpus for lazy imports + soft-keyword behavior. |
| python/extractor/tests/parser/lazy_imports_new.expected | Expected AST dump for the new parser test. |
| python/extractor/semmle/query_gen.py | Ensures generated QL libraries include overlay[local] module; header. |
| python/extractor/semmle/python/parser/tsg_parser.py | Updates placeholder node creation to treat is_lazy like other default-false fields. |
| python/extractor/semmle/python/parser/dump_ast.py | Avoids printing default-false is_lazy in AST dumps. |
| python/extractor/semmle/python/master.py | Adds is_lazy fields to Import and ImportFrom (ImportStar) schema definitions. |
| python/extractor/semmle/python/ast.py | Adds is_lazy slots/constructor params to Import and ImportFrom. |
| python/downgrades/eb5fc917c79bb23ce2de4a022f3e566d57a91be9/upgrade.properties | Downgrade metadata to remove is_lazy, including a py_bools rewrite step. |
| python/downgrades/eb5fc917c79bb23ce2de4a022f3e566d57a91be9/semmlecode.python.dbscheme | Downgrade dbscheme snapshot. |
| python/downgrades/eb5fc917c79bb23ce2de4a022f3e566d57a91be9/py_bools.ql | Downgrade query to drop py_bools rows for Import/ImportStar parents. |
| python/downgrades/eb5fc917c79bb23ce2de4a022f3e566d57a91be9/old.dbscheme | Prior dbscheme snapshot for the downgrade. |
Copilot's findings
- Files reviewed: 25/26 changed files
- Comments generated: 2
| /// Free any memory allocated for this array. Note that this does not free any | ||
| /// memory allocated for the array's contents. | ||
| #define array_delete(self) _array__delete((self), (void *)(self)->contents, sizeof(*self)) | ||
| #define array_delete(self) \ | ||
| do { \ | ||
| if ((self)->contents) ts_free((self)->contents); \ | ||
| (self)->contents = NULL; \ |
There was a problem hiding this comment.
The doc comment above array_delete says it “does not free any memory allocated for the array's contents”, but array_delete explicitly frees (self)->contents. If the intent is “does not free memory owned by the elements”, please reword this comment to avoid confusion.
There was a problem hiding this comment.
This is code that tree-sitter automatically adds. We have no control over it.
There was a problem hiding this comment.
I see the diff, so it is not marked as generated? Should we actually add that (not in this PR)?
There was a problem hiding this comment.
Good idea! (Somehow your comment only became visible when I unresolved this conversation. Weird.)
Said PEP adds support for annotating imports (
import ...andfrom ... import ...) with alazykeyword, the semantics of which are not relevant to this PR.We handle this in the parser in much the same way we do
asynccurrently -- import nodes get a newis_lazyfield that gets populated when thelazykeyword is present. On the QL side, this determines whether the newisLazypredicate method holds on the relevant nodes.Should be reviewed commit-by-commit.