Py 3.14 changes fix by youknowone · Pull Request #6755 · RustPython/RustPython
5936-5959: Conditional-annotation indices can desync from __annotate__ enumeration.
next_conditional_annotation_index is incremented for every simple annotated assignment compiled (including those nested in if/for/while/try/match), but compile_module_annotate enumerates only top-level annotations via collect_simple_annotations. This can shift indices and cause executed annotations to be omitted from __annotate__ (e.g., a conditional annotation before a top-level one).
Consider aligning enumeration with compilation order by recursively collecting simple annotations from nested bodies (or, alternatively, only increment the index for annotations that compile_module_annotate will enumerate).
🔧 Possible fix: recursively collect annotations in AST order
- fn collect_simple_annotations(body: &[Stmt]) -> Vec<(&str, &Expr)> { - let mut annotations = Vec::new(); - for stmt in body { - if let Stmt::AnnAssign(StmtAnnAssign { - target, - annotation, - simple, - .. - }) = stmt - && *simple - && let Expr::Name(ExprName { id, .. }) = target.as_ref() - { - annotations.push((id.as_str(), annotation.as_ref())); - } - } - annotations - } + fn collect_simple_annotations(body: &[Stmt]) -> Vec<(&str, &Expr)> { + fn walk<'a>(stmts: &'a [Stmt], out: &mut Vec<(&'a str, &'a Expr)>) { + for stmt in stmts { + match stmt { + Stmt::AnnAssign(StmtAnnAssign { target, annotation, simple, .. }) + if *simple && matches!(target.as_ref(), Expr::Name(_)) => + { + if let Expr::Name(ExprName { id, .. }) = target.as_ref() { + out.push((id.as_str(), annotation.as_ref())); + } + } + Stmt::If(StmtIf { body, elif_else_clauses, .. }) => { + walk(body, out); + for clause in elif_else_clauses { + walk(&clause.body, out); + } + } + Stmt::For(StmtFor { body, orelse, .. }) + | Stmt::While(StmtWhile { body, orelse, .. }) => { + walk(body, out); + walk(orelse, out); + } + Stmt::With(StmtWith { body, .. }) => walk(body, out), + Stmt::Try(StmtTry { body, handlers, orelse, finalbody, .. }) => { + walk(body, out); + for handler in handlers { + if let ExceptHandler::ExceptHandler(ExceptHandlerExceptHandler { body, .. }) = handler { + walk(body, out); + } + } + walk(orelse, out); + walk(finalbody, out); + } + Stmt::Match(StmtMatch { cases, .. }) => { + for case in cases { + walk(&case.body, out); + } + } + _ => {} + } + } + } + let mut annotations = Vec::new(); + walk(body, &mut annotations); + annotations + }