{"record":{"id":"d2b4589107748b00","repo":"astral-sh/ruff","slug":"implement-ipy-escape-command-support","errorCode":null,"errorMessage":"Implement Ipy escape command support","messagePattern":"Implement Ipy escape command support","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/ty_python_semantic/src/types/infer/builder/type_expression.rs","lineNumber":958,"sourceCode":"                    format_args!(\n                        \"Set comprehensions are not allowed in {}s\",\n                        self.type_expression_context()\n                    ),\n                );\n                Type::unknown()\n            }\n\n            ast::Expr::Generator(generator) => {\n                if !self.in_string_annotation() {\n                    self.infer_generator_expression(generator, TypeContext::default());\n                }\n                self.report_invalid_type_expression(\n                    expression,\n                    format_args!(\n                        \"Generator expressions are not allowed in {}s\",\n                        self.type_expression_context()\n                    ),\n                );\n                Type::unknown()\n            }\n\n            ast::Expr::Await(await_expression) => {\n                if !self.in_string_annotation() {\n                    self.infer_await_expression(await_expression, TypeContext::default());\n                }\n                self.report_invalid_type_expression(\n                    expression,\n                    format_args!(\n                        \"`await` expressions are not allowed in {}s\",\n                        self.type_expression_context()\n                    ),\n                );\n                Type::unknown()\n            }\n\n            ast::Expr::Yield(yield_expression) => {","sourceCodeStart":940,"sourceCodeEnd":976,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs#L940-L976","documentation":"While checking, ty evaluates expressions in type-expression positions (annotations, base classes, subscripts of generics). If an IPython escape command (%magic, !shell, etc.) lands in such a position — possible when notebook cells are parsed with IPython syntax — inference reaches todo!(\"Implement Ipy escape command support\") and the checker panics rather than degrading. Per the 2025-08 comment in the source, this is the only remaining todo! in ruff/ty, kept loud on purpose.","triggerScenarios":"Running ty check on a .ipynb (or a .py parsed with jupyter magics enabled) where a cell places an escape command in a type slot, e.g. 'count: %time int = 0' or a magic on an annotation/decorator line.","commonSituations":"Notebooks converted to/from scripts where magics drift into annotation positions; cells mixing magics with typed signatures; CI pipelines type-checking raw notebooks.","solutions":["Move or remove the magic so no escape command sits in an annotation/type-expression position (put %time on its own line, outside the annotation).","Update ty and re-test — the todo may since have been implemented or converted into a diagnostic; check the changelog and issue tracker.","Exclude the affected notebook from checking until support lands (e.g. via respect-excludes / ignore patterns).","If it reproduces on current ty, file a [ty] issue with the minimal cell contents."],"exampleFix":"# before (notebook cell; magic inside an annotation slot)\ncount: %time int = 0\n\n# after\ncount: int = 0","handlingStrategy":"validation","validationCode":"import json, re\nfrom pathlib import Path\n\nMAGIC = re.compile(r'^\\s*[%!]\\w+')\n\n\ndef notebook_has_magics(path: Path) -> bool:\n    nb = json.loads(path.read_text())\n    lines = (\n        ''.join(cell.get('source', []))\n        for cell in nb.get('cells', [])\n        if cell.get('cell_type') == 'code'\n    )\n    return any(MAGIC.match(line) for chunk in lines for line in chunk.splitlines())\n\n\nfiles = [\n    p for p in map(Path, sys.argv[1:])\n    if p.suffix != '.ipynb' or not notebook_has_magics(p)\n]","typeGuard":null,"tryCatchPattern":"proc = subprocess.run(['ty', 'check', *files], capture_output=True, text=True)\nif proc.returncode != 0 and 'not yet implemented' in proc.stderr:\n    proc = subprocess.run(\n        ['ty', 'check', *[f for f in files if f.suffix != '.ipynb']],\n        capture_output=True,\n        text=True,\n    )","preventionTips":["Keep magics on their own lines; never inside annotations, decorators, or type arguments.","Pin a known-good ty version in CI and upgrade deliberately.","Check exit code and stderr for 'not yet implemented'/'panicked' so failures are loud, not silent."],"tags":["ty","typechecker","notebook","ipython","panic","unimplemented"],"backgroundTag":"unimplemented-language-feature","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}