import inspect import warnings from types import FrameType as Frame from typing import Any, Dict, Optional, TypeVar, Union from varname import nameof from _pointers import force_update_locals from .base_pointers import BasePointer from .exceptions import NullPointerError, VariableLifetimeError from .util import NULL, Nullable, handle __all__ = "VarPointer", "to_var_ptr" T = TypeVar("T") class VarPointer(BasePointer[T]): def __init__(self, name: str, frame: Frame) -> None: self.name: Optional[str] = name self._frame = frame self._address = id(~self) def _get_scope(self) -> Dict[str, Any]: if not self.name: raise NullPointerError("pointer is NULL") frame = self._frame if self.name in frame.f_globals: return frame.f_globals if self.name in frame.f_locals: return frame.f_locals raise VariableLifetimeError(f'variable "{self.name}" no longer exists') @handle def move( self, target: Union[T, "BasePointer[T]"], *, unsafe: bool = False, ) -> None: if unsafe: warnings.warn("unsafe has no effect on variable pointers") if not self.name: raise NullPointerError("pointer is NULL") scope = self._get_scope() if (scope is self._frame.f_locals) and ( self._frame.f_locals is not self._frame.f_globals ): force_update_locals( self._frame, self.name, ( ~target if isinstance( target, BasePointer, ) else target ), ) else: scope[self.name] = ( ~target if isinstance( target, BasePointer, ) else target ) @property def address(self) -> Optional[int]: return self._address def _cleanup(self) -> None: pass def __repr__(self) -> str: return f"VarPointer(name={self.name!r})" def dereference(self) -> T: if not self.name: raise NullPointerError("pointer is NULL") return self._get_scope()[self.name] def assign(self, value: Nullable[Union["VarPointer[T]", T]], *, frame: int = 2) -> None: if value is NULL: self._address = 0 self.name = None return fframe = inspect.currentframe() assert fframe assert fframe.f_back self.name = nameof(value, frame=frame) self._address = id(value) def __irshift__( self, value: Nullable[Union["VarPointer[T]", T]], ): self.assign(value, frame=3) return self def to_var_ptr(value: T) -> VarPointer[T]: frame = inspect.currentframe() assert frame assert frame.f_back name = nameof(value, frame=2) return VarPointer(name, frame.f_back)