fix defer

This commit is contained in:
2026-05-13 05:55:46 -04:00
parent 9266add6a1
commit 1a529ed750
6 changed files with 66 additions and 106 deletions
-9
View File
@@ -1,9 +0,0 @@
import sys
import os
try:
from imgui_bundle import hello_imgui
rp = hello_imgui.RunnerParams()
print(f"Default borderless: {rp.app_window_params.borderless}")
except Exception as e:
print(f"Error: {e}")
-68
View File
@@ -1,68 +0,0 @@
import sys
from ast import (
AsyncFunctionDef,
FunctionDef,
)
from collections import deque
from types import FrameType
from typing import Any, Callable, Optional, cast
from executing.executing import Executing, Source
from defer.errors import FreeVarsError
from defer.sugar.transformer import RewriteDefer
class _ParseDefer:
IDENTITY = lambda *_: None # noqa: E731
def __init__(self, tracefn: Optional[Callable]) -> None:
self.tracefn = tracefn or self.IDENTITY
self.pending: deque[Executing] = deque()
def __call__(self, frame: FrameType, event: str, arg: Any):
self.tracefn(frame, event, arg)
if any(
frame.f_code.co_filename.startswith(path)
for path in {
sys.base_exec_prefix,
sys.base_prefix,
sys.exec_prefix,
sys.prefix,
"<frozen ",
"<string>",
"<pytest ",
}
):
return self
if event != "line":
return self
exc = Source.executing(frame)
if not (stmt := next(iter(exc.statements), None)):
return self
if isinstance(stmt, (AsyncFunctionDef, FunctionDef)):
self.pending.append(exc)
return self
if not self.pending or frame.f_back is not self.pending[-1].frame.f_back:
return self
stmts = self.pending.pop().statements
node = cast(FunctionDef | AsyncFunctionDef, next(iter(stmts)))
fn = frame.f_locals[node.name]
if fn.__module__ in sys.stdlib_module_names:
return self
if fn.__code__.co_freevars:
raise FreeVarsError(fn)
if not (ast := RewriteDefer.transform(node)):
return self
locals = frame.f_locals.copy()
del locals[node.name]
exec(compile(ast, frame.f_code.co_filename, "exec"), frame.f_globals, locals)
frame.f_locals[node.name].__code__ = locals[node.name].__code__
return self
BIN
View File
Binary file not shown.
View File
-1
View File
@@ -10,4 +10,3 @@ def install():
defer = Defer() defer = Defer()
install()
+65 -27
View File
@@ -4,15 +4,35 @@ from ast import (
FunctionDef, FunctionDef,
) )
from collections import deque from collections import deque
from os.path import isabs
from types import FrameType from types import FrameType
from typing import Any, Callable, Optional, cast from typing import Any, Callable, Optional, cast
from executing.executing import Executing, Source
from defer.errors import FreeVarsError from defer.errors import FreeVarsError
from defer.sugar.transformer import RewriteDefer from defer.sugar.transformer import RewriteDefer
def _is_dunder(name: str) -> bool:
return name.startswith("__") and name.endswith("__")
def _is_excluded_path(filename: str) -> bool:
if not filename:
return True
return (
filename.startswith(sys.base_exec_prefix)
or filename.startswith(sys.base_prefix)
or filename.startswith(sys.exec_prefix)
or filename.startswith(sys.prefix)
or ".venv" in filename
or filename.startswith("<frozen ")
or filename.startswith("<string>")
or filename.startswith("<pytest ")
or "thirdparty" in filename
or "__future__" in filename
)
class _ParseDefer: class _ParseDefer:
IDENTITY = lambda *_: None # noqa: E731 IDENTITY = lambda *_: None # noqa: E731
@@ -21,47 +41,65 @@ class _ParseDefer:
self.pending: deque[Executing] = deque() self.pending: deque[Executing] = deque()
def __call__(self, frame: FrameType, event: str, arg: Any): def __call__(self, frame: FrameType, event: str, arg: Any):
try:
self._do_call(frame, event, arg)
except Exception:
pass
return self
def _do_call(self, frame: FrameType, event: str, arg: Any):
self.tracefn(frame, event, arg) self.tracefn(frame, event, arg)
if any( try:
frame.f_code.co_filename.startswith(path) filename = frame.f_code.co_filename
for path in { except Exception:
sys.base_exec_prefix, return
sys.base_prefix,
sys.exec_prefix, if _is_excluded_path(filename):
sys.prefix, return
"<frozen ",
"<string>", if not isabs(filename):
"<pytest ", return
}
):
return self
if event != "line": if event != "line":
return self return
try:
from executing.executing import Executing, Source
except ImportError:
return
try:
exc = Source.executing(frame)
except Exception:
return
if not (stmt := next(iter(exc.statements), None)): if not (stmt := next(iter(exc.statements), None)):
return self return
if isinstance(stmt, (AsyncFunctionDef, FunctionDef)): if isinstance(stmt, (AsyncFunctionDef, FunctionDef)):
if stmt.name.startswith("__") and stmt.name.endswith("__"): if _is_dunder(stmt.name):
return self return
self.pending.append(exc) self.pending.append(exc)
return self return
return self if not self.pending or frame.f_back is not self.pending[-1].frame.f_back:
return
stmts = self.pending.pop().statements stmts = self.pending.pop().statements
node = cast(FunctionDef | AsyncFunctionDef, next(iter(stmts))) node = cast(FunctionDef | AsyncFunctionDef, next(iter(stmts)))
fn = frame.f_locals[node.name] fn_name = node.name
if fn_name not in frame.f_locals:
return
fn = frame.f_locals[fn_name]
if fn.__module__ in sys.stdlib_module_names: if fn.__module__ in sys.stdlib_module_names:
return self return
if fn.__code__.co_freevars: if fn.__code__.co_freevars:
raise FreeVarsError(fn) raise FreeVarsError(fn)
if not (ast := RewriteDefer.transform(node)): if not (ast := RewriteDefer.transform(node)):
return self return
locals = frame.f_locals.copy() locals = frame.f_locals.copy()
del locals[node.name] del locals[fn_name]
exec(compile(ast, frame.f_code.co_filename, "exec"), frame.f_globals, locals) exec(compile(ast, frame.f_code.co_filename, "exec"), frame.f_globals, locals)
frame.f_locals[node.name].__code__ = locals[node.name].__code__ frame.f_locals[fn_name].__code__ = locals[fn_name].__code__
return self