File size: 2,913 Bytes
2969d1e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | """Origin-module lookup shared by the ``tools.browser_tool_*`` extraction modules.
Extracted code reads facade-owned state (caches, locks, session tables, facade-defined
helpers) *through* ``tools.browser_tool`` and must not import it at import time (cycle) —
hence the lazy :func:`origin_module` and the :data:`origin` proxy.
"""
import sys
from types import ModuleType
_ORIGIN_NAME = "tools.browser_tool"
class _NamespaceView:
"""Live view over a module namespace whose module object is gone (a test purged
``sys.modules``): the old code still runs with its own globals, so hit *those*."""
__slots__ = ("_g",)
def __init__(self, g):
object.__setattr__(self, "_g", g)
def __getattr__(self, name):
try:
return self._g[name]
except KeyError:
raise AttributeError(name) from None
def __setattr__(self, name, value):
self._g[name] = value
def _module_for_globals(g: dict):
"""The module object whose namespace is ``g`` (or a live view over it)."""
import tools
for mod in (sys.modules.get(_ORIGIN_NAME), getattr(tools, "browser_tool", None)):
if mod is not None and mod.__dict__ is g:
return mod
return _NamespaceView(g)
def origin_module(_depth: int = 2):
"""The ``tools.browser_tool`` instance the *calling* moved function belongs to, mirroring
what in-file code would see: (1) the nearest enclosing frame executing origin code (the
exact module copy, even after a test purged/reloaded ``sys.modules``); (2) an origin
module referenced from a calling frame's globals; (3) ``sys.modules`` / ``tools``
package attribute / fresh import. ``_depth`` is the frame of the moved function's caller."""
try:
start = sys._getframe(_depth)
except ValueError: # called directly by the interpreter (atexit callback)
start = None
frame = start
while frame is not None:
if frame.f_globals.get("__name__") == _ORIGIN_NAME:
return _module_for_globals(frame.f_globals)
frame = frame.f_back
frame = start
while frame is not None:
for value in list(frame.f_globals.values()):
if isinstance(value, ModuleType) and getattr(value, "__name__", None) == _ORIGIN_NAME:
return value
frame = frame.f_back
mod = sys.modules.get(_ORIGIN_NAME)
if mod is None:
import tools
mod = getattr(tools, "browser_tool", None)
if mod is None:
import tools.browser_tool as mod
return mod
class _OriginProxy:
"""Module-level ``_bt`` stand-in: every attribute read/write forwards to the origin
module resolved at that moment."""
__slots__ = ()
def __getattr__(self, name):
return getattr(origin_module(3), name)
def __setattr__(self, name, value):
setattr(origin_module(3), name, value)
origin = _OriginProxy()
|