Skip to content

Commit

Permalink
Merge pull request #3 from mkzeender/big-static-typing-changes
Browse files Browse the repository at this point in the history
Big static typing changes
  • Loading branch information
mkzeender authored Jul 28, 2024
2 parents 8055d22 + 581735f commit 6901eca
Show file tree
Hide file tree
Showing 32 changed files with 804 additions and 207 deletions.
5 changes: 5 additions & 0 deletions autohotpy/ahk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sys

from autohotpy import ahk as _ahk

sys.modules[__name__] = _ahk # type: ignore
47 changes: 47 additions & 0 deletions autohotpy/ahk.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ----------
# Primitives
# ----------
from typing import Any as Any
from autohotpy.static_typing.classes import (
Primitive as Primitive,
Number as Number,
VarRef as VarRef,
)

Integer = int
Float = float
String = str

# -------
# Objects
# -------
from autohotpy.static_typing.classes.object_ import Object as Object
from autohotpy.static_typing.classes.array import Array as Array
from autohotpy.static_typing.classes.buffer import Buffer as Buffer
from autohotpy.static_typing.classes.class_ import Class as Class

# from autohotpy.static_typing.classes.error import # TODO: error typing
from autohotpy.static_typing.classes.file import File as File
from autohotpy.static_typing.classes.func import (
Func as Func,
BoundFunc as BoundFunc,
Closure as Closure,
Enumerator as Enumerator,
)

# from autohotpy.static_typing.classes.gui # TODO: gui typing
from autohotpy.static_typing.classes.inputhook import InputHook as InputHook
from autohotpy.static_typing.classes.map import Map as Map
from autohotpy.static_typing.classes.menu import Menu as Menu, MenuBar as MenuBar
from autohotpy.static_typing.classes.regex_match_info import (
RegExMatchInfo as RegExMatchInfo,
)

# -----------
# COM objects
# -----------
from autohotpy.static_typing.classes.com_obj import (
ComValue as ComValue,
ComObject as ComObject,
ComObjArray as ComObjArray,
)
6 changes: 3 additions & 3 deletions autohotpy/ahk_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from autohotpy.proxies.ahk_script import AhkScript
from autohotpy.communicator import Communicator
from autohotpy.communicator.hotkey_factory import HotkeyFactory
from autohotpy.exceptions import AhkError, ExitApp
from autohotpy.exceptions import AhkException, ExitApp
from .communicator import ahkdll

from .global_state import thread_state
Expand Down Expand Up @@ -214,9 +214,9 @@ def _call_method_callback(self, data: dict) -> tuple[bool, Any]:
ret_val = func(*args, **kwargs)
success = True
except BaseException as e:
if isinstance(e, AhkError):
if isinstance(e, AhkException):
# e is a wrapper for the ahk Error object, so we unwrap it
ret_val = e.error
ret_val = e.wrapped_object
else:
ret_val = e
success = False
Expand Down
6 changes: 5 additions & 1 deletion autohotpy/communicator/communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ def value_from_data(self, data, factory: AhkObjFactory | None) -> Any:
if isinstance(data, dict):
if data["dtype"] == DTypes.AHK_OBJECT:
assert factory is not None
return factory.create(int(data["ptr"]))
return factory.create(
ptr=int(data["ptr"]),
type_name=data["type_name"],
immortal=bool(data["immortal"]),
)
if data["dtype"] == DTypes.INT:
return int(data["value"])
if data["dtype"] == DTypes.PY_OBJECT:
Expand Down
2 changes: 1 addition & 1 deletion autohotpy/communicator/references.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class RefWrapper:
value: Any
count: int = 0

def __eq__(self, other: RefWrapper | int) -> bool:
def __eq__(self, other: object) -> bool:
if isinstance(other, int):
return id(self.value) == other
elif isinstance(other, RefWrapper):
Expand Down
34 changes: 31 additions & 3 deletions autohotpy/communicator/script_inject/py_communicator.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ _py_setprop(obj, prop, value, params*) {
obj.%prop%[params*] := value
}

_py_instancecheck(inst, cls) {
return inst is cls
}

class _PyCommunicator {

static __New() {
Expand Down Expand Up @@ -98,16 +102,40 @@ class _PyCommunicator {
return map("dtype", _PyParamTypes.VARREF, "ptr", String(ptr))
}
if IsObject(val) {
ptr := ObjPtrAddRef(val)
return map("dtype", _PyParamTypes.AHK_OBJECT, "ptr", String(ptr))
immortal := this.IsImmortal(val)
if immortal {
ptr := ObjPtr(val)
} else {
ptr := ObjPtrAddRef(val)
}
; Msgbox immortal ", " Type(val) ", " (val is Func and val.IsBuiltIn)

return map("dtype", _PyParamTypes.AHK_OBJECT, "ptr", String(ptr), "type_name", Type(val), 'immortal', immortal)
}
if val is Integer {
return map("dtype", _PyParamTypes.INT, "value", String(val))
}

return val
}


static IsImmortal(_ahk_value) {
if (_ahk_value is Class) {
try {
return %_ahk_value.Prototype.__Class% == _ahk_value
} catch {
return false
}
} else if (_ahk_value is Func) {
try {
return %_ahk_value.Name% == _ahk_value
} catch {
return false
}
} else {
return false
}
}


}
136 changes: 104 additions & 32 deletions autohotpy/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
from __future__ import annotations
from functools import cached_property
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Protocol, cast

if TYPE_CHECKING:
from autohotpy.proxies.ahk_object import AhkObject
from autohotpy.static_typing.classes import error # type: ignore
from autohotpy.static_typing.classes import object_ # type: ignore


class BaseAhkException(Exception):
pass


class AhkError(BaseAhkException):
def __init__(self, err: AhkObject) -> None:
super().__init__()
self.error = err
class ExceptionLike(Protocol):
Message: str
What: str
Extra: str

@property
def args(self) -> tuple[Any, Any, Any]:
return self.error.Message, self.error.What, self.error.Extra

@args.setter
def args(self, val:tuple[Any, Any, Any]): # type: ignore # TODO: investigate this?
try:
self.error.Message = val[0]
self.error.What = val[1]
self.error.Extra = val[2]
except IndexError:
pass
_err_class_mapping: dict[str, type[AhkBaseException]] = {}

@cached_property
def msg(self):
err_type = self.error._ahk_instance.call_method(None, "Type", (self.error,))
return f"{err_type}: {self.args[0]}, {self.args[2]}"

def __str__(self):
return self.msg
class AhkBaseException(BaseException):
def __init_subclass__(cls, name: str = "") -> None:
if not name:
name = cls.__name__
_err_class_mapping[name] = cls
return super().__init_subclass__()


class ExitApp(BaseAhkException):
class ExitApp(SystemExit, AhkBaseException):
def __init__(self, reason: str, code: int, *args: object) -> None:
super().__init__(*args)
self.code = code
Expand All @@ -47,8 +35,92 @@ def __str__(self) -> str:
return f"Exit Code: {self.code}, Reason: {self.reason}"


def throw(exc_value: Exception | AhkObject):
if isinstance(exc_value, BaseException):
raise exc_value
else:
raise AhkError(exc_value)
class AhkException(AhkBaseException, Exception):
wrapped_object: Any


class AhkNonErrorException(AhkException):
wrapped_object: Any

def __str__(self) -> str:
return str(self.wrapped_object)


class Error(AhkException):
wrapped_object: ExceptionLike

# @cached_property
# def args(self) -> tuple[str, str, str]:
# return (
# self.wrapped_object.Message,
# self.wrapped_object.What,
# self.wrapped_object.Extra,
# )

# def args(self, val: tuple[str, str, str]) -> None:
# try:
# self.wrapped_object.Message = val[0]
# self.wrapped_object.What = val[1]
# self.wrapped_object.Extra = val[2]
# except IndexError:
# pass

@cached_property
def msg(self) -> str:
msg = self.wrapped_object.Message
if what := self.wrapped_object.What:
msg += f'\nIn function "{what}"'
if extra := self.wrapped_object.Extra:
msg += ",\n" + extra
return msg

def __str__(self) -> str:
return self.msg


class MemoryError(Error, MemoryError):
pass


class OSError(Error, OSError):
pass


class MemberError(Error, AttributeError):
pass


class PropertyError(MemberError):
pass


class MethodError(MemberError):
pass


class IndexError(Error, IndexError):
pass


class KeyError(IndexError, KeyError):
pass


class ValueError(Error, ValueError):
pass


def throw(err: Any):
from autohotpy.proxies.ahk_object import AhkObject

if isinstance(err, BaseException):
raise err
if isinstance(err, AhkObject):
clsname = err._ahk_type_name
if clsname in _err_class_mapping:
wrapper = _err_class_mapping[clsname]()
else:
wrapper = AhkNonErrorException()
if isinstance(wrapper, AhkException):
wrapper.wrapped_object = err
raise wrapper
12 changes: 1 addition & 11 deletions autohotpy/proxies/_cached_prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@
SelfT = TypeVar("SelfT", infer_variance=True)


ahkobject_slots = (
"_ahk_instance",
"_ahk_ptr",
"_ahk_bound_to",
"_ahk_method_name",
"_ahk_cached_name",
"_ahk_cached_ahk_type",
)


class CachedProp(Generic[SelfT, PropT]):
def __init__(self, func: Callable[[SelfT], PropT]):
self.func = func
Expand All @@ -30,7 +20,7 @@ def __set_name__(self, owner: type[SelfT], name: str) -> None:
self.name = name
self.qualname = owner.__qualname__ + "." + name
self.private_name = intern("_ahk_cached_" + name.strip("_"))
assert self.private_name in ahkobject_slots
assert not hasattr(owner, "__slots__") or self.private_name in owner.__slots__ # type: ignore

@overload
def __get__(self, obj: None, objtype: type[SelfT]) -> Self: ...
Expand Down
Loading

0 comments on commit 6901eca

Please sign in to comment.