1+ """Concrete definition of an Action. Implemention of async and sync versions, action decorator."""
2+
3+ from __future__ import annotations
4+
15import warnings
26
37from enum import Enum
48from inspect import getfullargspec , iscoroutinefunction
59from types import FunctionType , MethodType
6- from typing import Any
10+ from typing import TYPE_CHECKING , Any
711
812import jsonschema
913
1216from hololinked import SchemaValidatorClasses
1317from hololinked .constants import JSON
1418from hololinked .core .exceptions import StateMachineError
19+ from hololinked .core .interfaces .metadata import ActionMetadata
1520from hololinked .param .parameterized import ParameterizedFunction
1621from hololinked .utils import (
1722 get_input_model_from_signature ,
2429from .dataklasses import ActionInfoValidator
2530
2631
32+ if TYPE_CHECKING :
33+ from hololinked .core .interfaces import ActionMetadata
34+ from hololinked .core .thing import Thing
35+
36+
2737class Action :
2838 """
2939 Object that models an action.
@@ -91,7 +101,7 @@ def execution_info(self, value: ActionInfoValidator) -> None:
91101 raise TypeError ("execution_info must be of type ActionInfoValidator" )
92102 self ._execution_info = value # type: ActionInfoValidator
93103
94- def to_affordance (self , owner_inst = None ) :
104+ def to_metadata (self , owner_inst : Thing | None = None , format : str = "wot" ) -> ActionMetadata :
95105 """
96106 Generates a `ActionAffordance` TD fragment for this Action.
97107
@@ -105,9 +115,12 @@ def to_affordance(self, owner_inst=None):
105115 ActionAffordance
106116 the affordance TD fragment for this action
107117 """
108- from .. td import ActionAffordance
118+ from hololinked . ddl import MetadataFormats
109119
110- return ActionAffordance .generate (self , owner_inst or self .owner )
120+ return MetadataFormats .get (format ).action .from_descriptor (
121+ self ,
122+ owner_inst or self .owner ,
123+ )
111124
112125
113126class BoundAction :
@@ -132,7 +145,7 @@ def __init__(self, obj: FunctionType, descriptor: Action, owner_inst, owner) ->
132145
133146 def __post_init__ (self ):
134147 # never called, neither possible to call, only type hinting
135- from .thing import Thing , ThingMeta
148+ from .thing import ThingMeta
136149
137150 # owner class and instance
138151 self .owner : ThingMeta
@@ -145,6 +158,7 @@ def __post_init__(self):
145158 def validate_call (self , args , kwargs : dict [str , Any ]) -> None :
146159 """
147160 Validate the call to the action, like payload, state machine state etc.
161+
148162 Errors are raised as exceptions.
149163
150164 Parameters
@@ -153,6 +167,13 @@ def validate_call(self, args, kwargs: dict[str, Any]) -> None:
153167 positional arguments to the action
154168 kwargs: dict
155169 keyword arguments to the action
170+
171+ Raises
172+ ------
173+ StateMachineError
174+ if the action cannot be executed in the current state of the owning thing
175+ RuntimeError
176+ if the action explicity accepts only keyword arguments but some positional arguments are given
156177 """
157178 if self .execution_info .isparameterized and len (args ) > 0 :
158179 raise RuntimeError ("parameterized functions cannot have positional arguments" )
@@ -202,7 +223,7 @@ def __getattribute__(self, name):
202223 return self .obj .__doc__
203224 return super ().__getattribute__ (name )
204225
205- def to_affordance (self ) :
226+ def to_metadata (self , owner_inst : Thing | None = None , format : str = "wot" ) -> ActionMetadata :
206227 """
207228 Generates a `ActionAffordance` TD fragment for this Action.
208229
@@ -216,17 +237,19 @@ def to_affordance(self):
216237 ActionAffordance
217238 the affordance TD fragment for this action
218239 """
219- return Action .to_affordance (self .descriptor , self .owner_inst or self .owner )
240+ return Action .to_metadata (self .descriptor , owner_inst or self .owner_inst or self .owner , format = format )
220241
221242
222243class BoundSyncAction (BoundAction ):
223244 """
224- non async(io) action call. The call is passed to the method as-it-is to allow local
245+ Non-async(io) action call.
246+
247+ The call is passed to the method as-it-is to allow local
225248 invocation without state machine checks. Use `external_call` to have validation.
226249 """
227250
228251 def external_call (self , * args , ** kwargs ):
229- """Validated call to the action with state machine and payload checks"""
252+ """Validated call to the action with state machine and payload checks. """
230253 self .validate_call (args , kwargs )
231254 return self .__call__ (* args , ** kwargs )
232255
@@ -238,12 +261,14 @@ def __call__(self, *args, **kwargs):
238261
239262class BoundAsyncAction (BoundAction ):
240263 """
241- async(io) action call. The call is passed to the method as-it-is to allow local
264+ async(io) action call.
265+
266+ The call is passed to the method as-it-is to allow local
242267 invocation without state machine checks. Use `external_call` to have validation.
243268 """
244269
245270 async def external_call (self , * args , ** kwargs ):
246- """Validated call to the action with state machine and payload checks"""
271+ """Validated call to the action with state machine and payload checks. """
247272 self .validate_call (args , kwargs )
248273 return await self .__call__ (* args , ** kwargs )
249274
@@ -263,8 +288,9 @@ def action(
263288 ** kwargs ,
264289) -> Action :
265290 """
266- Decorate on your methods to make them accessible remotely or create 'actions' out of them. When used with hardware,
267- actions generally command the hardware to do something.
291+ Decorate on your methods to make them accessible remotely or create 'actions' out of them.
292+
293+ When used with hardware, actions generally command the hardware to do something.
268294
269295 Parameters
270296 ----------
@@ -298,6 +324,13 @@ def action(
298324 Action
299325 returns the callable object wrapped in an `Action` object. When accessed at instance level,
300326 a `BoundSyncAction` or `BoundAsyncAction` object is returned.
327+
328+ Raises
329+ ------
330+ TypeError
331+ if the decorated object is not a function or method, or if the input/output schema is of invalid type
332+ ValueError
333+ if the decorated function is a dunder method, or if unknown keyword arguments are provided
301334 """
302335
303336 def inner (obj ):
0 commit comments