Compare commits

..

5 Commits

Author SHA1 Message Date
Kanika Kapoor
19b565f885
Merge c048ec9faf4bfe02da004dce69471897933c3617 into 2fa14c588463bf576bce766f6ae996e7e8e10271 2026-02-01 09:54:31 +08:00
blueloveTH
2fa14c5884 fix #455 2026-01-30 14:42:45 +08:00
blueloveTH
cf965a1957 fix #456 2026-01-30 14:40:05 +08:00
blueloveTH
a59f916f5b Update ideas.md 2026-01-30 11:55:40 +08:00
blueloveTH
f9100dc504 Create ideas.md 2026-01-30 11:41:51 +08:00
9 changed files with 92 additions and 7 deletions

46
docs/gsoc2026/ideas.md Normal file
View File

@ -0,0 +1,46 @@
---
icon: light-bulb
order: 0
label: "Project Ideas"
---
## Idea Title
Build a Vibe Coding Agent in Python for Creating Mobile Games
## Project Size
Medium
## Related Skills
- CPython
- Agentic Programming
- Prompt Engineering
- PIXI.JS Framework
## Description
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.
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.
This agent is composed of two main components,
backend and frontend.
The backend part should be developed in CPython,
which is composed of the following modules:
+ Virtual Container. The agent needs to create a virtual linux container for each vibe coding project. This module provides management for users' sources and assets inside the container.
+ 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.
+ 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,
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.

View File

@ -135,7 +135,7 @@ void FuncDecl__dtor(FuncDecl* self);
typedef struct Function {
FuncDecl_ decl;
py_GlobalRef module; // maybe NULL, weak ref
py_Ref globals; // maybe NULL, strong ref
py_TValue globals; // maybe nil, strong ref
NameDict* closure; // maybe NULL, strong ref
PyObject* clazz; // weak ref; for super()
py_CFunction cfunc; // wrapped C function; for decl-based binding
@ -143,3 +143,8 @@ typedef struct Function {
void Function__ctor(Function* self, FuncDecl_ decl, py_GlobalRef module, py_Ref globals);
void Function__dtor(Function* self);
// https://github.com/pocketpy/pocketpy/issues/456
// Function may be created from `execdyn` and return
// Weakrefs like `.globals` and `.clazz` may invalidate

View File

@ -47,3 +47,9 @@ def ior(a, b): a |= b; return a
def ixor(a, b): a ^= b; return a
def ilshift(a, b): a <<= b; return a
def irshift(a, b): a >>= b; return a
class attrgetter:
def __init__(self, attr):
self.attr = attr
def __call__(self, obj):
return getattr(obj, self.attr)

View File

@ -10,7 +10,7 @@ const char kPythonLibs_datetime[] = "from time import localtime\nimport operator
const char kPythonLibs_functools[] = "class cache:\n def __init__(self, f):\n self.f = f\n self.cache = {}\n\n def __call__(self, *args):\n if args not in self.cache:\n self.cache[args] = self.f(*args)\n return self.cache[args]\n \nclass lru_cache:\n def __init__(self, maxsize=128):\n self.maxsize = maxsize\n self.cache = {}\n\n def __call__(self, f):\n def wrapped(*args):\n if args in self.cache:\n res = self.cache.pop(args)\n self.cache[args] = res\n return res\n \n res = f(*args)\n if len(self.cache) >= self.maxsize:\n first_key = next(iter(self.cache))\n self.cache.pop(first_key)\n self.cache[args] = res\n return res\n return wrapped\n \ndef reduce(function, sequence, initial=...):\n it = iter(sequence)\n if initial is ...:\n try:\n value = next(it)\n except StopIteration:\n raise TypeError(\"reduce() of empty sequence with no initial value\")\n else:\n value = initial\n for element in it:\n value = function(value, element)\n return value\n\nclass partial:\n def __init__(self, f, *args, **kwargs):\n self.f = f\n if not callable(f):\n raise TypeError(\"the first argument must be callable\")\n self.args = args\n self.kwargs = kwargs\n\n def __call__(self, *args, **kwargs):\n kwargs.update(self.kwargs)\n return self.f(*self.args, *args, **kwargs)\n\n";
const char kPythonLibs_heapq[] = "# Heap queue algorithm (a.k.a. priority queue)\ndef heappush(heap, item):\n \"\"\"Push item onto heap, maintaining the heap invariant.\"\"\"\n heap.append(item)\n _siftdown(heap, 0, len(heap)-1)\n\ndef heappop(heap):\n \"\"\"Pop the smallest item off the heap, maintaining the heap invariant.\"\"\"\n lastelt = heap.pop() # raises appropriate IndexError if heap is empty\n if heap:\n returnitem = heap[0]\n heap[0] = lastelt\n _siftup(heap, 0)\n return returnitem\n return lastelt\n\ndef heapreplace(heap, item):\n \"\"\"Pop and return the current smallest value, and add the new item.\n\n This is more efficient than heappop() followed by heappush(), and can be\n more appropriate when using a fixed-size heap. Note that the value\n returned may be larger than item! That constrains reasonable uses of\n this routine unless written as part of a conditional replacement:\n\n if item > heap[0]:\n item = heapreplace(heap, item)\n \"\"\"\n returnitem = heap[0] # raises appropriate IndexError if heap is empty\n heap[0] = item\n _siftup(heap, 0)\n return returnitem\n\ndef heappushpop(heap, item):\n \"\"\"Fast version of a heappush followed by a heappop.\"\"\"\n if heap and heap[0] < item:\n item, heap[0] = heap[0], item\n _siftup(heap, 0)\n return item\n\ndef heapify(x):\n \"\"\"Transform list into a heap, in-place, in O(len(x)) time.\"\"\"\n n = len(x)\n # Transform bottom-up. The largest index there's any point to looking at\n # is the largest with a child index in-range, so must have 2*i + 1 < n,\n # or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so\n # j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is\n # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.\n for i in reversed(range(n//2)):\n _siftup(x, i)\n\n# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos\n# is the index of a leaf with a possibly out-of-order value. Restore the\n# heap invariant.\ndef _siftdown(heap, startpos, pos):\n newitem = heap[pos]\n # Follow the path to the root, moving parents down until finding a place\n # newitem fits.\n while pos > startpos:\n parentpos = (pos - 1) >> 1\n parent = heap[parentpos]\n if newitem < parent:\n heap[pos] = parent\n pos = parentpos\n continue\n break\n heap[pos] = newitem\n\ndef _siftup(heap, pos):\n endpos = len(heap)\n startpos = pos\n newitem = heap[pos]\n # Bubble up the smaller child until hitting a leaf.\n childpos = 2*pos + 1 # leftmost child position\n while childpos < endpos:\n # Set childpos to index of smaller child.\n rightpos = childpos + 1\n if rightpos < endpos and not heap[childpos] < heap[rightpos]:\n childpos = rightpos\n # Move the smaller child up.\n heap[pos] = heap[childpos]\n pos = childpos\n childpos = 2*pos + 1\n # The leaf at pos is empty now. Put newitem there, and bubble it up\n # to its final resting place (by sifting its parents down).\n heap[pos] = newitem\n _siftdown(heap, startpos, pos)";
const char kPythonLibs_linalg[] = "from vmath import *";
const char kPythonLibs_operator[] = "# https://docs.python.org/3/library/operator.html#mapping-operators-to-functions\n\ndef le(a, b): return a <= b\ndef lt(a, b): return a < b\ndef ge(a, b): return a >= b\ndef gt(a, b): return a > b\ndef eq(a, b): return a == b\ndef ne(a, b): return a != b\n\ndef and_(a, b): return a & b\ndef or_(a, b): return a | b\ndef xor(a, b): return a ^ b\ndef invert(a): return ~a\ndef lshift(a, b): return a << b\ndef rshift(a, b): return a >> b\n\ndef is_(a, b): return a is b\ndef is_not(a, b): return a is not b\ndef not_(a): return not a\ndef truth(a): return bool(a)\ndef contains(a, b): return b in a\n\ndef add(a, b): return a + b\ndef sub(a, b): return a - b\ndef mul(a, b): return a * b\ndef truediv(a, b): return a / b\ndef floordiv(a, b): return a // b\ndef mod(a, b): return a % b\ndef pow(a, b): return a ** b\ndef neg(a): return -a\ndef matmul(a, b): return a @ b\n\ndef getitem(a, b): return a[b]\ndef setitem(a, b, c): a[b] = c\ndef delitem(a, b): del a[b]\n\ndef iadd(a, b): a += b; return a\ndef isub(a, b): a -= b; return a\ndef imul(a, b): a *= b; return a\ndef itruediv(a, b): a /= b; return a\ndef ifloordiv(a, b): a //= b; return a\ndef imod(a, b): a %= b; return a\n# def ipow(a, b): a **= b; return a\n# def imatmul(a, b): a @= b; return a\ndef iand(a, b): a &= b; return a\ndef ior(a, b): a |= b; return a\ndef ixor(a, b): a ^= b; return a\ndef ilshift(a, b): a <<= b; return a\ndef irshift(a, b): a >>= b; return a\n";
const char kPythonLibs_operator[] = "# https://docs.python.org/3/library/operator.html#mapping-operators-to-functions\n\ndef le(a, b): return a <= b\ndef lt(a, b): return a < b\ndef ge(a, b): return a >= b\ndef gt(a, b): return a > b\ndef eq(a, b): return a == b\ndef ne(a, b): return a != b\n\ndef and_(a, b): return a & b\ndef or_(a, b): return a | b\ndef xor(a, b): return a ^ b\ndef invert(a): return ~a\ndef lshift(a, b): return a << b\ndef rshift(a, b): return a >> b\n\ndef is_(a, b): return a is b\ndef is_not(a, b): return a is not b\ndef not_(a): return not a\ndef truth(a): return bool(a)\ndef contains(a, b): return b in a\n\ndef add(a, b): return a + b\ndef sub(a, b): return a - b\ndef mul(a, b): return a * b\ndef truediv(a, b): return a / b\ndef floordiv(a, b): return a // b\ndef mod(a, b): return a % b\ndef pow(a, b): return a ** b\ndef neg(a): return -a\ndef matmul(a, b): return a @ b\n\ndef getitem(a, b): return a[b]\ndef setitem(a, b, c): a[b] = c\ndef delitem(a, b): del a[b]\n\ndef iadd(a, b): a += b; return a\ndef isub(a, b): a -= b; return a\ndef imul(a, b): a *= b; return a\ndef itruediv(a, b): a /= b; return a\ndef ifloordiv(a, b): a //= b; return a\ndef imod(a, b): a %= b; return a\n# def ipow(a, b): a **= b; return a\n# def imatmul(a, b): a @= b; return a\ndef iand(a, b): a &= b; return a\ndef ior(a, b): a |= b; return a\ndef ixor(a, b): a ^= b; return a\ndef ilshift(a, b): a <<= b; return a\ndef irshift(a, b): a >>= b; return a\n\nclass attrgetter:\n def __init__(self, attr):\n self.attr = attr\n def __call__(self, obj):\n return getattr(obj, self.attr)\n";
const char kPythonLibs_typing[] = "class _Placeholder:\n def __init__(self, *args, **kwargs):\n pass\n def __getitem__(self, *args):\n return self\n def __call__(self, *args, **kwargs):\n return self\n def __and__(self, other):\n return self\n def __or__(self, other):\n return self\n def __xor__(self, other):\n return self\n\n\n_PLACEHOLDER = _Placeholder()\n\nSequence = _PLACEHOLDER\nList = _PLACEHOLDER\nDict = _PLACEHOLDER\nTuple = _PLACEHOLDER\nSet = _PLACEHOLDER\nAny = _PLACEHOLDER\nUnion = _PLACEHOLDER\nOptional = _PLACEHOLDER\nCallable = _PLACEHOLDER\nType = _PLACEHOLDER\nTypeAlias = _PLACEHOLDER\nNewType = _PLACEHOLDER\n\nClassVar = _PLACEHOLDER\n\nLiteral = _PLACEHOLDER\nLiteralString = _PLACEHOLDER\n\nIterable = _PLACEHOLDER\nGenerator = _PLACEHOLDER\nIterator = _PLACEHOLDER\n\nHashable = _PLACEHOLDER\n\nTypeVar = _PLACEHOLDER\nSelf = _PLACEHOLDER\n\nProtocol = object\nGeneric = object\nNever = object\n\nTYPE_CHECKING = False\n\n# decorators\noverload = lambda x: x\nfinal = lambda x: x\n\n# exhaustiveness checking\nassert_never = lambda x: x\n\nTypedDict = dict\nNotRequired = _PLACEHOLDER\n";
const char* load_kPythonLib(const char* name) {

View File

@ -512,7 +512,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// submit the call
if(!fn->cfunc) {
// python function
VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, false));
VM__push_frame(self, Frame__new(co, p0, fn->module, &fn->globals, argv, false));
return opcall ? RES_CALL : VM__run_top_frame(self);
} else {
// decl-based binding
@ -541,7 +541,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// submit the call
if(!fn->cfunc) {
// python function
VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, false));
VM__push_frame(self, Frame__new(co, p0, fn->module, &fn->globals, argv, false));
return opcall ? RES_CALL : VM__run_top_frame(self);
} else {
// decl-based binding
@ -557,7 +557,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// copy buffer back to stack
self->stack.sp = argv + co->nlocals;
memcpy(argv, self->vectorcall_buffer, co->nlocals * sizeof(py_TValue));
py_Frame* frame = Frame__new(co, p0, fn->module, fn->globals, argv, false);
py_Frame* frame = Frame__new(co, p0, fn->module, &fn->globals, argv, false);
pk_newgenerator(py_retval(), frame, p0, self->stack.sp);
self->stack.sp = p0; // reset the stack
return RES_RETURN;

View File

@ -538,7 +538,7 @@ py_GlobalRef pk_builtins__register() {
void function__gc_mark(void* ud, c11_vector* p_stack) {
Function* func = ud;
if(func->globals) pk__mark_value(func->globals);
pk__mark_value(&func->globals);
if(func->closure) {
NameDict* dict = func->closure;
for(int i = 0; i < dict->capacity; i++) {

View File

@ -143,7 +143,7 @@ void Function__ctor(Function* self, FuncDecl_ decl, py_GlobalRef module, py_Ref
PK_INCREF(decl);
self->decl = decl;
self->module = module;
self->globals = globals;
self->globals = globals != NULL ? *globals : *py_NIL();
self->closure = NULL;
self->clazz = NULL;
self->cfunc = NULL;

17
tests/661_exec_bug.py Normal file
View File

@ -0,0 +1,17 @@
# https://github.com/pocketpy/pocketpy/issues/456
module_code = '''
CONSTANT = 42
def hello(name):
return "Hello, " + name
'''
namespace = {}
exec(module_code, namespace)
assert namespace['CONSTANT'] == 42
assert namespace['hello']('world') == "Hello, world"
# print("Constant:", namespace['CONSTANT'])
# print("Function result:", namespace['hello']('world'))

View File

@ -50,3 +50,14 @@ assert op.ior(0b01, 0b11) == 0b11
assert op.ixor(0b01, 0b11) == 0b10
assert op.ilshift(0b01, 1) == 0b10
assert op.irshift(0b10, 1) == 0b01
# https://github.com/pocketpy/pocketpy/issues/455
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Alice", 30)
res = op.attrgetter('name')(person)
assert res == "Alice"