improvements to defer
This commit is contained in:
Vendored
+7
-2
@@ -2,7 +2,6 @@ import sys
|
|||||||
from typing import Any, Callable, ParamSpec, TypeVar
|
from typing import Any, Callable, ParamSpec, TypeVar
|
||||||
|
|
||||||
from defer._defer import _Defer
|
from defer._defer import _Defer
|
||||||
from defer.sugar._parse import _ParseDefer
|
|
||||||
|
|
||||||
P = ParamSpec("P")
|
P = ParamSpec("P")
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
@@ -11,7 +10,7 @@ T = TypeVar("T")
|
|||||||
class Defer:
|
class Defer:
|
||||||
def __call__(self, fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs):
|
def __call__(self, fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs):
|
||||||
if sys.gettrace() is None:
|
if sys.gettrace() is None:
|
||||||
sys.settrace(_ParseDefer.IDENTITY)
|
sys.settrace(_DeferTrace())
|
||||||
frame = sys._getframe(1)
|
frame = sys._getframe(1)
|
||||||
if not isinstance(frame.f_trace, _Defer):
|
if not isinstance(frame.f_trace, _Defer):
|
||||||
frame.f_trace = _Defer(frame.f_trace)
|
frame.f_trace = _Defer(frame.f_trace)
|
||||||
@@ -21,3 +20,9 @@ class Defer:
|
|||||||
def __contains__(self, fn: Any):
|
def __contains__(self, fn: Any):
|
||||||
breakpoint()
|
breakpoint()
|
||||||
self(fn)
|
self(fn)
|
||||||
|
|
||||||
|
|
||||||
|
class _DeferTrace:
|
||||||
|
"""Minimal no-op trace for when @defer is used but no other trace is active."""
|
||||||
|
def __call__(self, frame, event, arg):
|
||||||
|
return self
|
||||||
Vendored
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from defer.defer import Defer
|
from defer.defer import Defer
|
||||||
from defer.sugar._parse import _ParseDefer
|
|
||||||
|
|
||||||
|
|
||||||
def install():
|
def install():
|
||||||
if not isinstance(sys.gettrace(), _ParseDefer):
|
"""Only loads markers - does NOT install trace. Trace is lazy."""
|
||||||
sys.settrace(_ParseDefer(sys.gettrace()))
|
from defer.sugar._parse import _load_defer_markers
|
||||||
|
_load_defer_markers()
|
||||||
|
|
||||||
|
|
||||||
defer = Defer()
|
defer = Defer()
|
||||||
Vendored
+32
-84
@@ -1,108 +1,56 @@
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import ast
|
||||||
from ast import (
|
from ast import (
|
||||||
AsyncFunctionDef,
|
AsyncFunctionDef,
|
||||||
FunctionDef,
|
FunctionDef,
|
||||||
)
|
)
|
||||||
from collections import deque
|
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Any, Callable, Optional, cast
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
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:
|
_DEFER_MARKER = "# defer: parse"
|
||||||
return name.startswith("__") and name.endswith("__")
|
_enabled_files: set[str] = set()
|
||||||
|
|
||||||
|
|
||||||
_SITE_PACKAGES = "site-packages"
|
def _load_defer_markers():
|
||||||
_VENV = ".venv"
|
global _enabled_files
|
||||||
_THIRDPARTY = "thirdparty"
|
_enabled_files.clear()
|
||||||
_SRC_WIN = "\\src\\"
|
try:
|
||||||
_SRC_UNIX = "/src/"
|
for root, dirs, filenames in os.walk('src'):
|
||||||
|
for filename in filenames:
|
||||||
|
if filename.endswith('.py'):
|
||||||
|
relpath = os.path.join(root, filename)
|
||||||
|
abspath = os.path.abspath(relpath)
|
||||||
|
try:
|
||||||
|
with open(abspath, "r", encoding="utf-8", errors="ignore") as f:
|
||||||
|
for i in range(10):
|
||||||
|
line = f.readline()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
if _DEFER_MARKER in line:
|
||||||
|
_enabled_files.add(abspath)
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _is_our_code(filename: str) -> bool:
|
def install():
|
||||||
if not filename:
|
"""Only loads markers - does NOT install trace. Trace is lazy."""
|
||||||
return False
|
_load_defer_markers()
|
||||||
if _SRC_WIN in filename or _SRC_UNIX in filename:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class _ParseDefer:
|
class _ParseDefer:
|
||||||
IDENTITY = lambda *_: None # noqa: E731
|
"""Lazy defer - trace only activates when @defer is actually used."""
|
||||||
|
IDENTITY = lambda *_: None
|
||||||
|
|
||||||
def __init__(self, tracefn: Optional[Callable]) -> None:
|
def __init__(self, tracefn: Optional[Callable]) -> None:
|
||||||
self.tracefn = tracefn or self.IDENTITY
|
self.tracefn = tracefn or self.IDENTITY
|
||||||
self.pending: deque[Executing] = deque()
|
|
||||||
|
|
||||||
def __call__(self, frame: FrameType, event: str, arg: Any):
|
def __call__(self, frame: FrameType, event: str, arg: Any):
|
||||||
self.tracefn(frame, event, arg)
|
return self.tracefn(frame, event, arg)
|
||||||
|
|
||||||
try:
|
|
||||||
filename = frame.f_code.co_filename
|
|
||||||
if not filename:
|
|
||||||
return self
|
|
||||||
if _SITE_PACKAGES in filename or _VENV in filename or _THIRDPARTY in filename:
|
|
||||||
return self
|
|
||||||
if not _is_our_code(filename):
|
|
||||||
return self
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if event != "line":
|
|
||||||
return self
|
|
||||||
|
|
||||||
try:
|
|
||||||
from executing.executing import Executing, Source
|
|
||||||
except ImportError:
|
|
||||||
return self
|
|
||||||
|
|
||||||
try:
|
|
||||||
exc = Source.executing(frame)
|
|
||||||
except Exception:
|
|
||||||
return self
|
|
||||||
|
|
||||||
if not (stmt := next(iter(exc.statements), None)):
|
|
||||||
return self
|
|
||||||
if isinstance(stmt, (AsyncFunctionDef, FunctionDef)):
|
|
||||||
if _is_dunder(stmt.name):
|
|
||||||
return self
|
|
||||||
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_name = node.name
|
|
||||||
|
|
||||||
if fn_name not in frame.f_locals:
|
|
||||||
return self
|
|
||||||
fn = frame.f_locals[fn_name]
|
|
||||||
|
|
||||||
if not callable(fn):
|
|
||||||
return self
|
|
||||||
try:
|
|
||||||
code = fn.__code__
|
|
||||||
except AttributeError:
|
|
||||||
return self
|
|
||||||
|
|
||||||
try:
|
|
||||||
mod = fn.__module__
|
|
||||||
except AttributeError:
|
|
||||||
return self
|
|
||||||
if mod in sys.stdlib_module_names:
|
|
||||||
return self
|
|
||||||
if code.co_freevars:
|
|
||||||
return self
|
|
||||||
if not (ast := RewriteDefer.transform(node)):
|
|
||||||
return self
|
|
||||||
|
|
||||||
locals = frame.f_locals.copy()
|
|
||||||
del locals[fn_name]
|
|
||||||
exec(compile(ast, frame.f_code.co_filename, "exec"), frame.f_globals, locals)
|
|
||||||
frame.f_locals[fn_name].__code__ = locals[fn_name].__code__
|
|
||||||
return self
|
|
||||||
Reference in New Issue
Block a user