Compare commits

...

3 Commits

Author SHA1 Message Date
Kanika Kapoor
565fe1d017
Merge c048ec9faf4bfe02da004dce69471897933c3617 into 6dd3a6dbb3dd26342ba143724330ab802baf897c 2026-02-24 23:50:09 +05:30
blueloveTH
6dd3a6dbb3 up 2026-02-24 15:23:56 +08:00
Kanika Kapoor
c048ec9faf Fix context manager __exit__ not being called on exception (#395)
Problem: When an exception occurs in a WITH block, __exit__ was not called,
preventing proper cleanup of context managers.

Solution:
1. Wrap WITH block body in try-except structure
2. On normal exit: call __exit__(None, None, None)
3. On exception: call __exit__ with exception info before re-raising

Changes:
- compiler.c: Wrap WITH body in try-except, ensure __exit__ called in both paths
- ceval.c: Update OP_WITH_EXIT to accept three arguments (exc_type, exc_val, exc_tb)
- tests/520_context.py: Add test to verify __exit__ called on exceptions
2025-12-27 01:12:15 +05:30
5 changed files with 77 additions and 18 deletions

View File

@ -7,7 +7,7 @@ label: "Application Guide"
Welcome to the Google Summer of Code 2026 application guide for pocketpy. Welcome to the Google Summer of Code 2026 application guide for pocketpy.
We are recruiting a student who is passionate about vibe coding and mobile game development. We are recruiting a student who is passionate about vibe coding and mobile game development.
See [Project Ideas (TBA)](./ideas.md) for more details about the project. See [Project Ideas](./ideas.md) for more details about the project.
## Prerequisites ## Prerequisites
@ -16,7 +16,7 @@ To apply for this project, you need to satisfy the following prerequisites:
+ You are a student enrolled in an accredited institution (university, college, etc.) pursuing a degree in computer science or a related field. And this is your first time participating in Google Summer of Code. + You are a student enrolled in an accredited institution (university, college, etc.) pursuing a degree in computer science or a related field. And this is your first time participating in Google Summer of Code.
+ You have interest in vibe coding and mobile game development. + You have interest in vibe coding and mobile game development.
+ You are experienced in [Python](https://www.python.org/) and backend technologies, such as [FastAPI](https://fastapi.tiangolo.com/) or [Flask](https://flask.palletsprojects.com/). + You are experienced in [Python](https://www.python.org/) and backend technologies, such as [FastAPI](https://fastapi.tiangolo.com/) or [Flask](https://flask.palletsprojects.com/).
+ You are glad to learn mobile app development using frameworks like [Flutter](https://flutter.dev/). + You are glad to learn game engines like [Godot](https://godotengine.org/).
## Application steps ## Application steps

View File

@ -7,7 +7,7 @@ label: "Project Ideas"
## Idea Title ## Idea Title
Build a Vibe Coding Agent in Python for Creating Mobile Games Build a Vibe Coding Agent in Python for Creating Games
## Project Size ## Project Size
@ -18,14 +18,14 @@ Medium
- CPython - CPython
- Agentic Programming - Agentic Programming
- Prompt Engineering - Prompt Engineering
- PIXI.JS Framework - Game Engines
## Description ## Description
pocketpy is an organization dedicated to creating game development tools in Python language. pocketpy is an organization dedicated to creating game development tools in Python language.
Nowadays, vibe coding has become a popular approach for rapid game development, allowing developers to create games quickly and efficiently by leveraging language models and agentic programming techniques. Nowadays, vibe coding has become a popular approach for rapid game development, allowing developers to create games quickly and efficiently by leveraging language models and agentic programming techniques.
For Google Summer of Code 2026, we are looking for a student to develop a vibe coding agent that can assist developers in creating mobile games. For Google Summer of Code 2026, we are looking for a student to develop a vibe coding agent that can assist developers in creating games.
This agent is composed of two main components, This agent is composed of two main components,
backend and frontend. backend and frontend.
@ -36,11 +36,6 @@ which is composed of the following modules:
+ AI Service Provider. This module is responsible for communicating with AI service providers, such as OpenAI, to generate code and assets based on user prompts. + AI Service Provider. This module is responsible for communicating with AI service providers, such as OpenAI, to generate code and assets based on user prompts.
+ Persistent Memory. This module stores the state of each vibe coding project, including project progress, user preferences, and other relevant information. + Persistent Memory. This module stores the state of each vibe coding project, including project progress, user preferences, and other relevant information.
+ Agentic Core. This module uses Persistent Memory and AI Service Provider to implement the agentic programming logic, enabling the agent to understand user prompts and generate appropriate code and assets. + Agentic Core. This module uses Persistent Memory and AI Service Provider to implement the agentic programming logic, enabling the agent to understand user prompts and generate appropriate code and assets.
+ PIXI.JS Integration. We decide to use [PIXI.JS](https://pixijs.com/) as the default rendering engine for user projects. This is because PIXI.JS is fully source-driven, + Game Engine Integration.
which makes it easier for the agent to generate and modify game code.
The frontend part is optional. Knowing this could help students better understand the whole project.
We aims to create a mobile app using Flutter framework. This app invodes backend services via RESTful APIs,
and provides a user-friendly interface for users to control and run their vibe coding projects.
For more details, we will discuss with the selected student during the community bonding period. For more details, we will discuss with the selected student during the community bonding period.

View File

@ -2801,6 +2801,8 @@ static Error* compile_stmt(Compiler* self) {
case TK_WITH: { case TK_WITH: {
check(EXPR(self)); // [ <expr> ] check(EXPR(self)); // [ <expr> ]
Ctx__s_emit_top(ctx()); Ctx__s_emit_top(ctx());
// Save context manager for later __exit__ call
Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, prev()->line);
Ctx__enter_block(ctx(), CodeBlockType_WITH); Ctx__enter_block(ctx(), CodeBlockType_WITH);
NameExpr* as_name = NULL; NameExpr* as_name = NULL;
if(match(TK_AS)) { if(match(TK_AS)) {
@ -2809,17 +2811,33 @@ static Error* compile_stmt(Compiler* self) {
as_name = NameExpr__new(prev()->line, name, name_scope(self)); as_name = NameExpr__new(prev()->line, name, name_scope(self));
} }
Ctx__emit_(ctx(), OP_WITH_ENTER, BC_NOARG, prev()->line); Ctx__emit_(ctx(), OP_WITH_ENTER, BC_NOARG, prev()->line);
// [ <expr> <expr>.__enter__() ]
if(as_name) { if(as_name) {
bool ok = vtemit_store((Expr*)as_name, ctx()); bool ok = vtemit_store((Expr*)as_name, ctx());
vtdelete((Expr*)as_name); vtdelete((Expr*)as_name);
if(!ok) return SyntaxError(self, "invalid syntax"); if(!ok) return SyntaxError(self, "invalid syntax");
} else { } else {
// discard `__enter__()`'s return value
Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE); Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
} }
// Wrap body in try-except to ensure __exit__ is called even on exception
Ctx__enter_block(ctx(), CodeBlockType_TRY);
Ctx__emit_(ctx(), OP_BEGIN_TRY, BC_NOARG, prev()->line);
check(compile_block_body(self)); check(compile_block_body(self));
Ctx__emit_(ctx(), OP_END_TRY, BC_NOARG, BC_KEEPLINE);
// Normal exit: call __exit__(None, None, None)
Ctx__emit_(ctx(), OP_LOAD_NONE, BC_NOARG, prev()->line);
Ctx__emit_(ctx(), OP_LOAD_NONE, BC_NOARG, prev()->line);
Ctx__emit_(ctx(), OP_LOAD_NONE, BC_NOARG, prev()->line);
Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev()->line); Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev()->line);
int jump_patch = Ctx__emit_(ctx(), OP_JUMP_FORWARD, BC_NOARG, BC_KEEPLINE);
Ctx__exit_block(ctx());
// Exception handler: call __exit__ with exception info, then re-raise
Ctx__emit_(ctx(), OP_PUSH_EXCEPTION, BC_NOARG, BC_KEEPLINE);
Ctx__emit_(ctx(), OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE); // exc_type
Ctx__emit_(ctx(), OP_ROT_TWO, BC_NOARG, BC_KEEPLINE); // reorder: [cm, None, exc]
Ctx__emit_(ctx(), OP_LOAD_NONE, BC_NOARG, BC_KEEPLINE); // exc_tb
Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev()->line);
Ctx__emit_(ctx(), OP_RE_RAISE, BC_NOARG, BC_KEEPLINE);
Ctx__patch_jump(ctx(), jump_patch);
Ctx__exit_block(ctx()); Ctx__exit_block(ctx());
} break; } break;
/*************************************************/ /*************************************************/

View File

@ -1122,14 +1122,35 @@ __NEXT_STEP:
DISPATCH(); DISPATCH();
} }
case OP_WITH_EXIT: { case OP_WITH_EXIT: {
// [expr] // Stack: [cm, exc_type, exc_val, exc_tb]
py_push(TOP()); // Call cm.__exit__(exc_type, exc_val, exc_tb)
py_Ref exc_tb = TOP();
py_Ref exc_val = SECOND();
py_Ref exc_type = THIRD();
py_Ref cm = FOURTH();
// Save all values from stack
py_TValue saved_cm = *cm;
py_TValue saved_exc_type = *exc_type;
py_TValue saved_exc_val = *exc_val;
py_TValue saved_exc_tb = *exc_tb;
self->stack.sp -= 4;
// Push cm and get __exit__ method
py_push(&saved_cm);
if(!py_pushmethod(__exit__)) { if(!py_pushmethod(__exit__)) {
TypeError("'%t' object does not support the context manager protocol", TOP()->type); TypeError("'%t' object does not support the context manager protocol", saved_cm.type);
goto __ERROR; goto __ERROR;
} }
if(!py_vectorcall(0, 0)) goto __ERROR;
POP(); // Push arguments: exc_type, exc_val, exc_tb
PUSH(&saved_exc_type);
PUSH(&saved_exc_val);
PUSH(&saved_exc_tb);
// Call __exit__(exc_type, exc_val, exc_tb)
if(!py_vectorcall(3, 0)) goto __ERROR;
py_pop(); // discard return value
DISPATCH(); DISPATCH();
} }
/////////// ///////////

View File

@ -27,4 +27,29 @@ assert path == ['enter', 'in', 'exit']
path.clear() path.clear()
# Test that __exit__ is called even when an exception occurs
class B:
def __init__(self):
self.path = []
def __enter__(self):
path.append('enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
path.append('exit')
if exc_type is not None:
path.append('exception')
return False # propagate exception
try:
with B():
path.append('before_raise')
raise ValueError('test')
path.append('after_raise') # should not be reached
except ValueError:
pass
assert path == ['enter', 'before_raise', 'exit', 'exception'], f"Expected ['enter', 'before_raise', 'exit', 'exception'], got {path}"