Managing thirdparty package: defer.
This commit is contained in:
+26
-34
@@ -13,7 +13,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
# from defer import defer
|
from defer import defer
|
||||||
import tomli_w
|
import tomli_w
|
||||||
# from contextlib import ExitStack, nullcontext
|
# from contextlib import ExitStack, nullcontext
|
||||||
import traceback
|
import traceback
|
||||||
@@ -657,16 +657,15 @@ class App:
|
|||||||
"""
|
"""
|
||||||
[C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close]
|
[C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close]
|
||||||
"""
|
"""
|
||||||
if imgui.begin_menu("manual slop"):
|
with imscope.menu("manual slop") as (active):
|
||||||
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
if active and imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
||||||
self.runner_params.app_shall_exit = True
|
self.runner_params.app_shall_exit = True
|
||||||
imgui.end_menu()
|
with imscope.menu("Windows") as (active):
|
||||||
if imgui.begin_menu("Windows"):
|
if (active):
|
||||||
for w in self.show_windows.keys():
|
for w in self.show_windows.keys():
|
||||||
_, self.show_windows[w] = imgui.menu_item(w, "", self.show_windows[w])
|
_, self.show_windows[w] = imgui.menu_item(w, "", self.show_windows[w])
|
||||||
imgui.end_menu()
|
with imscope.menu("Project") as (active):
|
||||||
if imgui.begin_menu("Project"):
|
if active and imgui.menu_item("Save All", "", False)[0]:
|
||||||
if imgui.menu_item("Save All", "", False)[0]:
|
|
||||||
self._flush_to_project()
|
self._flush_to_project()
|
||||||
self._flush_to_config()
|
self._flush_to_config()
|
||||||
models.save_config(self.config)
|
models.save_config(self.config)
|
||||||
@@ -686,9 +685,8 @@ class App:
|
|||||||
self.ai_status = f"md written: {path.name}"
|
self.ai_status = f"md written: {path.name}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ai_status = f"error: {e}"
|
self.ai_status = f"error: {e}"
|
||||||
imgui.end_menu()
|
with imscope.menu("Layout") as (active):
|
||||||
if imgui.begin_menu("Layout"):
|
if active and imgui.menu_item("Save Current...", "", False)[0]:
|
||||||
if imgui.menu_item("Save Current...", "", False)[0]:
|
|
||||||
self._show_save_workspace_profile_modal = True
|
self._show_save_workspace_profile_modal = True
|
||||||
self._new_workspace_profile_name = ""
|
self._new_workspace_profile_name = ""
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
@@ -696,37 +694,32 @@ class App:
|
|||||||
if imgui.menu_item(profile.name, "", False)[0]:
|
if imgui.menu_item(profile.name, "", False)[0]:
|
||||||
self.controller._cb_load_workspace_profile(profile_id)
|
self.controller._cb_load_workspace_profile(profile_id)
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
if imgui.begin_menu("Delete Profile"):
|
with imscope.menu("Delete Profile") as (active):
|
||||||
for profile_id, profile in self.workspace_profiles.items():
|
if active:
|
||||||
if imgui.menu_item(profile.name, "", False)[0]:
|
for profile_id, profile in self.workspace_profiles.items():
|
||||||
self.controller._cb_delete_workspace_profile(profile_id, self._new_workspace_profile_scope)
|
if imgui.menu_item(profile.name, "", False)[0]:
|
||||||
imgui.end_menu()
|
self.controller._cb_delete_workspace_profile(profile_id, self._new_workspace_profile_scope)
|
||||||
imgui.end_menu()
|
|
||||||
|
|
||||||
# RAG status indicator
|
# RAG status indicator
|
||||||
if self.controller.rag_config and self.controller.rag_config.enabled:
|
if self.controller.rag_config and self.controller.rag_config.enabled:
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
status = self.controller.rag_status
|
status = self.controller.rag_status
|
||||||
if status == "indexing...":
|
if status == "indexing...": color = vec4(100, 255, 100)
|
||||||
color = vec4(100, 255, 100)
|
elif status == "error": color = vec4(255, 100, 100)
|
||||||
elif status == "error":
|
else: color = vec4(180, 180, 180)
|
||||||
color = vec4(255, 100, 100)
|
|
||||||
else:
|
|
||||||
color = vec4(180, 180, 180)
|
|
||||||
imgui.text_colored(color, f"[RAG: {status}]")
|
imgui.text_colored(color, f"[RAG: {status}]")
|
||||||
if imgui.is_item_hovered():
|
if imgui.is_item_hovered(): imgui.set_tooltip(f"RAG is enabled. Status: {status}. Click to rebuild index.")
|
||||||
imgui.set_tooltip(f"RAG is enabled. Status: {status}. Click to rebuild index.")
|
if imgui.is_item_clicked(): self.controller.event_queue.put('click', 'btn_rebuild_rag_index')
|
||||||
if imgui.is_item_clicked():
|
|
||||||
self.controller.event_queue.put('click', 'btn_rebuild_rag_index')
|
|
||||||
|
|
||||||
# Draw right-aligned window controls directly in the menu bar (Win32 only)
|
# Draw right-aligned window controls directly in the menu bar (Win32 only)
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
try:
|
try:
|
||||||
import ctypes
|
import ctypes
|
||||||
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
|
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
|
||||||
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p]
|
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p]
|
||||||
hwnd_capsule = imgui.get_main_viewport().platform_handle_raw
|
hwnd_capsule = imgui.get_main_viewport().platform_handle_raw
|
||||||
hwnd = ctypes.pythonapi.PyCapsule_GetPointer(hwnd_capsule, b"nb_handle")
|
hwnd = ctypes.pythonapi.PyCapsule_GetPointer(hwnd_capsule, b"nb_handle")
|
||||||
except Exception:
|
except Exception:
|
||||||
hwnd = 0
|
hwnd = 0
|
||||||
|
|
||||||
@@ -734,9 +727,8 @@ class App:
|
|||||||
btn_w = 40
|
btn_w = 40
|
||||||
# Use window width (points) instead of display_size (pixels) for correct scaling
|
# Use window width (points) instead of display_size (pixels) for correct scaling
|
||||||
window_w = imgui.get_window_width()
|
window_w = imgui.get_window_width()
|
||||||
bar_h = imgui.get_window_height()
|
bar_h = imgui.get_window_height()
|
||||||
right_x = window_w - (btn_w * 3)
|
right_x = window_w - (btn_w * 3)
|
||||||
|
|
||||||
# Drag area check using an explicit invisible button spanning the empty space
|
# Drag area check using an explicit invisible button spanning the empty space
|
||||||
curr_x = imgui.get_cursor_pos_x()
|
curr_x = imgui.get_cursor_pos_x()
|
||||||
drag_w = right_x - curr_x
|
drag_w = right_x - curr_x
|
||||||
|
|||||||
+79
-78
@@ -2,20 +2,6 @@ from __future__ import annotations
|
|||||||
from imgui_bundle import imgui
|
from imgui_bundle import imgui
|
||||||
from imgui_bundle import imgui_node_editor
|
from imgui_bundle import imgui_node_editor
|
||||||
|
|
||||||
def window(name: str, visible: bool = True, flags: int = 0): return _ScopeWindow(name, visible, flags)
|
|
||||||
class _ScopeWindow:
|
|
||||||
def __init__(self, name: str, visible: bool, flags: int):
|
|
||||||
self._name = name
|
|
||||||
self._visible = visible
|
|
||||||
self._flags = flags
|
|
||||||
self._result = None
|
|
||||||
def __enter__(self):
|
|
||||||
self._result = imgui.begin(self._name, self._visible, self._flags)
|
|
||||||
return self._result
|
|
||||||
def __exit__(self, *args):
|
|
||||||
imgui.end()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def child(id_str: str, size_x: float = 0, size_y: float = 0, flags: int = 0): return _ScopeChild(id_str, size_x, size_y, flags)
|
def child(id_str: str, size_x: float = 0, size_y: float = 0, flags: int = 0): return _ScopeChild(id_str, size_x, size_y, flags)
|
||||||
class _ScopeChild:
|
class _ScopeChild:
|
||||||
def __init__(self, id_str: str, size_x: float | imgui.ImVec2, size_y: float, flags: int):
|
def __init__(self, id_str: str, size_x: float | imgui.ImVec2, size_y: float, flags: int):
|
||||||
@@ -33,19 +19,35 @@ class _ScopeChild:
|
|||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def table(name: str, columns: int, flags: int = 0): return _ScopeTable(name, columns, flags)
|
def group(): return _ScopeGroup()
|
||||||
class _ScopeTable:
|
class _ScopeGroup:
|
||||||
def __init__(self, name: str, columns: int, flags: int):
|
def __enter__(self):
|
||||||
self._name = name
|
return imgui.begin_group()
|
||||||
self._columns = columns
|
def __exit__(self, *args):
|
||||||
self._flags = flags
|
imgui.end_group()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def id(str_id: str): return _ScopeId(str_id)
|
||||||
|
class _ScopeId:
|
||||||
|
def __init__(self, str_id: str):
|
||||||
|
self._id = str_id
|
||||||
|
def __enter__(self):
|
||||||
|
imgui.push_id(self._id)
|
||||||
|
def __exit__(self, *args):
|
||||||
|
imgui.pop_id()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def menu(label: str): return _ScopeMenu(label)
|
||||||
|
class _ScopeMenu:
|
||||||
|
def __init__(self, label: str):
|
||||||
|
self._label = label
|
||||||
self._active = False
|
self._active = False
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self._active = imgui.begin_table(self._name, self._columns, self._flags)
|
self._active = imgui.begin_menu(self._label)
|
||||||
return self._active
|
return self._active
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
if self._active:
|
if self._active:
|
||||||
imgui.end_table()
|
imgui.end_menu()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def menu_bar(): return _ScopeMenuBar()
|
def menu_bar(): return _ScopeMenuBar()
|
||||||
@@ -60,17 +62,14 @@ class _ScopeMenuBar:
|
|||||||
imgui.end_menu_bar()
|
imgui.end_menu_bar()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def menu(label: str): return _ScopeMenu(label)
|
def node(name: str): return _ScopeNode(name)
|
||||||
class _ScopeMenu:
|
class _ScopeNode:
|
||||||
def __init__(self, label: str):
|
def __init__(self, name: str):
|
||||||
self._label = label
|
self._name = name
|
||||||
self._active = False
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self._active = imgui.begin_menu(self._label)
|
return imgui_node_editor.begin(self._name)
|
||||||
return self._active
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
if self._active:
|
imgui_node_editor.end()
|
||||||
imgui.end_menu()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def popup(id_str: str): return _ScopePopup(id_str)
|
def popup(id_str: str): return _ScopePopup(id_str)
|
||||||
@@ -101,50 +100,42 @@ class _ScopePopupModal:
|
|||||||
imgui.end_popup()
|
imgui.end_popup()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def tooltip(): return _ScopeTooltip()
|
|
||||||
class _ScopeTooltip:
|
def style_color(col: int, val: Any): return _ScopeStyleColor(col, val)
|
||||||
|
class _ScopeStyleColor:
|
||||||
|
def __init__(self, col: int, val: Any):
|
||||||
|
self._col = col
|
||||||
|
self._val = val
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return imgui.begin_tooltip()
|
imgui.push_style_color(self._col, self._val)
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
imgui.end_tooltip()
|
imgui.pop_style_color()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def group(): return _ScopeGroup()
|
def style_var(var: int, val: Any): return _ScopeStyleVar(var, val)
|
||||||
class _ScopeGroup:
|
class _ScopeStyleVar:
|
||||||
|
def __init__(self, var: int, val: Any):
|
||||||
|
self._var = var
|
||||||
|
self._val = val
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return imgui.begin_group()
|
imgui.push_style_var(self._var, self._val)
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
imgui.end_group()
|
imgui.pop_style_var()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def node(name: str): return _ScopeNode(name)
|
def table(name: str, columns: int, flags: int = 0): return _ScopeTable(name, columns, flags)
|
||||||
class _ScopeNode:
|
class _ScopeTable:
|
||||||
def __init__(self, name: str):
|
def __init__(self, name: str, columns: int, flags: int):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self._columns = columns
|
||||||
|
self._flags = flags
|
||||||
|
self._active = False
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return imgui_node_editor.begin(self._name)
|
self._active = imgui.begin_table(self._name, self._columns, self._flags)
|
||||||
|
return self._active
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
imgui_node_editor.end()
|
if self._active:
|
||||||
return False
|
imgui.end_table()
|
||||||
|
|
||||||
def text_wrap(wrap_pos: float = 0.0): return _ScopeTextWrap(wrap_pos)
|
|
||||||
class _ScopeTextWrap:
|
|
||||||
def __init__(self, wrap_pos: float):
|
|
||||||
self._wrap_pos = wrap_pos
|
|
||||||
def __enter__(self):
|
|
||||||
imgui.push_text_wrap_pos(self._wrap_pos)
|
|
||||||
def __exit__(self, *args):
|
|
||||||
imgui.pop_text_wrap_pos()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def id(str_id: str): return _ScopeId(str_id)
|
|
||||||
class _ScopeId:
|
|
||||||
def __init__(self, str_id: str):
|
|
||||||
self._id = str_id
|
|
||||||
def __enter__(self):
|
|
||||||
imgui.push_id(self._id)
|
|
||||||
def __exit__(self, *args):
|
|
||||||
imgui.pop_id()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def tab_bar(id_str: str, flags: int = 0): return _ScopeTabBar(id_str, flags)
|
def tab_bar(id_str: str, flags: int = 0): return _ScopeTabBar(id_str, flags)
|
||||||
@@ -176,26 +167,22 @@ class _ScopeTabItem:
|
|||||||
imgui.end_tab_item()
|
imgui.end_tab_item()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def style_color(col: int, val: Any): return _ScopeStyleColor(col, val)
|
def text_wrap(wrap_pos: float = 0.0): return _ScopeTextWrap(wrap_pos)
|
||||||
class _ScopeStyleColor:
|
class _ScopeTextWrap:
|
||||||
def __init__(self, col: int, val: Any):
|
def __init__(self, wrap_pos: float):
|
||||||
self._col = col
|
self._wrap_pos = wrap_pos
|
||||||
self._val = val
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
imgui.push_style_color(self._col, self._val)
|
imgui.push_text_wrap_pos(self._wrap_pos)
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
imgui.pop_style_color()
|
imgui.pop_text_wrap_pos()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def style_var(var: int, val: Any): return _ScopeStyleVar(var, val)
|
def tooltip(): return _ScopeTooltip()
|
||||||
class _ScopeStyleVar:
|
class _ScopeTooltip:
|
||||||
def __init__(self, var: int, val: Any):
|
|
||||||
self._var = var
|
|
||||||
self._val = val
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
imgui.push_style_var(self._var, self._val)
|
return imgui.begin_tooltip()
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
imgui.pop_style_var()
|
imgui.end_tooltip()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def tree_node_ex(label: str, flags: int = 0): return _ScopeTreeNodeEx(label, flags)
|
def tree_node_ex(label: str, flags: int = 0): return _ScopeTreeNodeEx(label, flags)
|
||||||
@@ -211,3 +198,17 @@ class _ScopeTreeNodeEx:
|
|||||||
if self._opened:
|
if self._opened:
|
||||||
imgui.tree_pop()
|
imgui.tree_pop()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def window(name: str, visible: bool = True, flags: int = 0): return _ScopeWindow(name, visible, flags)
|
||||||
|
class _ScopeWindow:
|
||||||
|
def __init__(self, name: str, visible: bool, flags: int):
|
||||||
|
self._name = name
|
||||||
|
self._visible = visible
|
||||||
|
self._flags = flags
|
||||||
|
self._result = None
|
||||||
|
def __enter__(self):
|
||||||
|
self._result = imgui.begin(self._name, self._visible, self._flags)
|
||||||
|
return self._result
|
||||||
|
def __exit__(self, *args):
|
||||||
|
imgui.end()
|
||||||
|
return False
|
||||||
|
|||||||
Vendored
+68
@@ -0,0 +1,68 @@
|
|||||||
|
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
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
from .sugar import defer as defer
|
||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
from contextlib import ExitStack
|
||||||
|
from types import FrameType
|
||||||
|
from typing import Any, Callable, Optional, ParamSpec, TypeVar
|
||||||
|
|
||||||
|
|
||||||
|
P = ParamSpec("P")
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class _Defer:
|
||||||
|
def __init__(self, tracefn: Optional[Callable]) -> None:
|
||||||
|
self.tracefn = tracefn or (lambda *_: None)
|
||||||
|
|
||||||
|
self._stack = ExitStack()
|
||||||
|
self._stack.__enter__()
|
||||||
|
|
||||||
|
def push(self, fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs):
|
||||||
|
self._stack.callback(fn, *args, **kwargs)
|
||||||
|
|
||||||
|
def __call__(self, frame: FrameType, event: str, arg: Any):
|
||||||
|
self.tracefn(frame, event, arg)
|
||||||
|
|
||||||
|
match event:
|
||||||
|
case "call":
|
||||||
|
self._stack.__enter__()
|
||||||
|
case "return":
|
||||||
|
self._stack.__exit__(None, None, None)
|
||||||
|
self._stack = ExitStack()
|
||||||
|
case "exception":
|
||||||
|
self._stack.__exit__(*arg)
|
||||||
|
self._stack = ExitStack()
|
||||||
|
return self
|
||||||
Vendored
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import sys
|
||||||
|
from typing import Any, Callable, ParamSpec, TypeVar
|
||||||
|
|
||||||
|
from defer._defer import _Defer
|
||||||
|
from defer.sugar._parse import _ParseDefer
|
||||||
|
|
||||||
|
P = ParamSpec("P")
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class Defer:
|
||||||
|
def __call__(self, fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs):
|
||||||
|
if sys.gettrace() is None:
|
||||||
|
sys.settrace(_ParseDefer.IDENTITY)
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
if not isinstance(frame.f_trace, _Defer):
|
||||||
|
frame.f_trace = _Defer(frame.f_trace)
|
||||||
|
frame.f_trace.push(fn, *args, **kwargs)
|
||||||
|
frame.f_trace_lines = False
|
||||||
|
|
||||||
|
def __contains__(self, fn: Any):
|
||||||
|
breakpoint()
|
||||||
|
self(fn)
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
from types import FunctionType
|
||||||
|
|
||||||
|
|
||||||
|
class DeferErrror(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FreeVarsError(DeferErrror):
|
||||||
|
def __init__(self, fn: FunctionType) -> None:
|
||||||
|
super().__init__(
|
||||||
|
"deferred function must not have free variables",
|
||||||
|
)
|
||||||
|
self.add_note("free vars: " + str(list(fn.__code__.co_freevars)))
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from defer.defer import Defer
|
||||||
|
from defer.sugar._parse import _ParseDefer
|
||||||
|
|
||||||
|
|
||||||
|
def install():
|
||||||
|
if not isinstance(sys.gettrace(), _ParseDefer):
|
||||||
|
sys.settrace(_ParseDefer(sys.gettrace()))
|
||||||
|
|
||||||
|
|
||||||
|
defer = Defer()
|
||||||
|
install()
|
||||||
Vendored
+67
@@ -0,0 +1,67 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
if not (stmt := next(iter(exc.statements), None)):
|
||||||
|
return self
|
||||||
|
if isinstance(stmt, (AsyncFunctionDef, FunctionDef)):
|
||||||
|
if stmt.name.startswith("__") and stmt.name.endswith("__"):
|
||||||
|
return self
|
||||||
|
self.pending.append(exc)
|
||||||
|
return self
|
||||||
|
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
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
from ast import (
|
||||||
|
AST,
|
||||||
|
AsyncFunctionDef,
|
||||||
|
Call,
|
||||||
|
Compare,
|
||||||
|
FunctionDef,
|
||||||
|
In,
|
||||||
|
Lambda,
|
||||||
|
Load,
|
||||||
|
Module,
|
||||||
|
Name,
|
||||||
|
NodeTransformer,
|
||||||
|
arg,
|
||||||
|
arguments,
|
||||||
|
copy_location,
|
||||||
|
fix_missing_locations,
|
||||||
|
)
|
||||||
|
from ast import (
|
||||||
|
walk as ast_walk,
|
||||||
|
)
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class RewriteDefer(NodeTransformer):
|
||||||
|
def __init__(self, root: AST) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._dirty = False
|
||||||
|
self._root = root
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def transform(cls, node: FunctionDef | AsyncFunctionDef) -> Optional[Module]:
|
||||||
|
instance = cls(node)
|
||||||
|
node = instance.visit(node)
|
||||||
|
if not instance._dirty:
|
||||||
|
return None
|
||||||
|
return fix_missing_locations(Module(body=[node], type_ignores=[]))
|
||||||
|
|
||||||
|
def visit_Compare(self, node: Compare):
|
||||||
|
match node:
|
||||||
|
case Compare(ops=[In()], comparators=[Name(id="defer", ctx=Load())]):
|
||||||
|
names = [n for n in ast_walk(node.left) if isinstance(n, Name)]
|
||||||
|
fn = Lambda(
|
||||||
|
args=arguments(
|
||||||
|
args=[arg(arg=n.id, annotation=None) for n in names],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[],
|
||||||
|
posonlyargs=[],
|
||||||
|
),
|
||||||
|
body=node.left,
|
||||||
|
)
|
||||||
|
call = Call(
|
||||||
|
func=Name(id="defer", ctx=Load()), args=[fn, *names], keywords=[]
|
||||||
|
)
|
||||||
|
copy_location(call, node)
|
||||||
|
self._dirty = True
|
||||||
|
return call
|
||||||
|
case _:
|
||||||
|
return node
|
||||||
|
|
||||||
|
def visit_FunctionDef(self, node: FunctionDef | AsyncFunctionDef) -> Any:
|
||||||
|
if node is self._root:
|
||||||
|
return self.generic_visit(node)
|
||||||
|
return node
|
||||||
|
|
||||||
|
visit_AsyncFunctionDef = visit_FunctionDef
|
||||||
Reference in New Issue
Block a user