2025-08-19 08:06:37 -04:00

216 lines
5.6 KiB
Python

import inspect
from typing import Callable
class RegisteredActionsAccessor:
def __init__(self, registered_actions, namespace):
self.registered_actions = registered_actions
self.namespace = namespace
def __getattr__(self, name):
for category in ("test", "module"):
cat_actions = self.registered_actions[category]
if self.namespace in cat_actions:
if name in cat_actions[self.namespace]:
return cat_actions[self.namespace][name]
raise AttributeError(f"Couldn't find action {self.namespace}.{name}")
def __call__(self, *args, **kwargs):
# Provide a useful error message if people try something like
# actions.my_action() when they should do actions.user.my_action()
raise RuntimeError(f"actions.{self.namespace}() is not an available action")
class Actions:
"""
Implements something like talon.actions. You can use the register
function to add in an action definition from your test (e.g. a mock).
"""
def __init__(self):
self.registered_actions = {
"module": {},
"test": {},
}
# Some built in actions
self.register_module_action("", "key", lambda x: None)
self.register_module_action("", "insert", lambda x: None)
self.register_module_action("", "sleep", lambda x: None)
self.register_module_action("edit", "selected_text", lambda: "test")
def reset_test_actions(self):
self.registered_actions["test"] = {}
def register_module_action(self, namespace: str, name: str, func: Callable):
"""
Registers an action to the module category. This should
only be called by importing files containing module definitions.
It won't be reset between test runs (or test files). Use
register_test_action and reset_test_actions to temporarily override
actions.
"""
self._register_action("module", namespace, name, func)
def register_test_action(self, namespace: str, name: str, func: Callable):
"""
Registers the given action, use like:
actions.register("user.my_action", lambda: None)
"""
self._register_action("test", namespace, name, func)
def _register_action(
self, category: str, namespace: str, name: str, func: Callable
):
if namespace not in self.registered_actions[category]:
self.registered_actions[category][namespace] = {}
self.registered_actions[category][namespace][name] = func
def __getattr__(self, name):
try:
# If name exists as a direct property of this class, then
# use that
return object.__getattribute__(self, name)
except AttributeError:
pass
try:
# Else if name is an action like actions.key
# that has no namespace then return that.
default_accessor = RegisteredActionsAccessor(self.registered_actions, "")
return getattr(default_accessor, name)
except AttributeError:
# Otherwise treat name as an action namespace
# (like actions.user).
return RegisteredActionsAccessor(self.registered_actions, name)
class Module:
"""
Implements something like the Module class built in to Talon
"""
def list(self, *args, **kwargs):
pass
def setting(self, *args, **kwargs):
pass
def capture(self, rule=None):
def __funcwrapper(func):
def __inner(*args, **kwargs):
return func(*args, **kwargs)
return __inner
return __funcwrapper
def tag(self, name, desc=None):
pass
def action_class(self, target_class):
# Register all the methods on the class with our actions implementation
for name, func in inspect.getmembers(target_class, inspect.isfunction):
actions.register_module_action("user", name, func)
return target_class
class Context:
"""
Implements something like the Context class built in to Talon
"""
lists = {}
def action_class(self, path=None):
def __funcwrapper(clazz):
return clazz
return __funcwrapper
def capture(self, name: str, rule: str = None):
def __funcwrapper(func):
def __inner(*args, **kwargs):
return func(*args, **kwargs)
return __inner
return __funcwrapper
class ImgUI:
"""
Stub out ImgUI so we don't get crashes
"""
GUI = None
def open(self):
def __funcwrapper(func):
def __inner(*args, **kwargs):
return func(*args, **kwargs)
return __inner
return __funcwrapper
class UI:
"""
Stub out UI so we don't get crashes
"""
def register(*args, **kwargs):
pass
class Settings:
"""
Implements something like talon.settings
"""
class Registry:
"""
Implements something like Talon's registry
"""
class Resource:
"""
Implements something like the talon resource system
"""
def open(self, path: str, mode: str = "r"):
return open(path, mode, encoding="utf-8")
def watch(self, path: str):
return lambda f: f
class App:
"""
Implements something like the talon app variable
"""
platform = "mac"
actions = Actions()
app = App
clip = None
imgui = ImgUI()
ui = UI()
settings = Settings()
resource = Resource()
registry = Registry()
# Indicate to test files that they should load since we're running in test mode
test_mode = True