11"""High-level Action base class implementation."""
22
3+ from copy import deepcopy
34import uuid
45import asyncio
56from .utils import timestamp
@@ -17,6 +18,8 @@ def __init__(self, thing, name, target, input_):
1718 name -- name of the action
1819 input_ -- any action inputs
1920 """
21+ self ._task = None
22+
2023 self .id = uuid .uuid4 ().hex
2124 self .thing = thing
2225 self .name = name
@@ -102,23 +105,29 @@ async def start(self):
102105 self .thing .action_notify (self )
103106 # If the action function is async
104107 if asyncio .iscoroutinefunction (self .target_function ):
105- # Await the action function
106- self .output = await self .target_function (self .input )
108+ self ._task = asyncio .ensure_future (self .target_function (self .input ))
107109 # If the action function is not async
108110 else :
109111 # Run in the default ThreadPoolExecutor
110- self .output = await asyncio .get_event_loop ().run_in_executor (
112+ self ._task = asyncio .get_event_loop ().run_in_executor (
111113 None , self .target_function , self .input
112114 )
113- self .finish ( )
115+ self ._task . add_done_callback ( self . finish )
114116
115117 def cancel (self ):
116- # TODO: Implement coroutine cancellation
117- pass
118+ if self . _task :
119+ self . _task . cancel ()
118120
119- def finish (self ):
121+ def finish (self , future ):
120122 """Finish performing the action."""
121- self .status = "completed"
123+ try :
124+ self .output = future .result ()
125+ self .status = "completed"
126+ except asyncio .CancelledError :
127+ self .status = "cancelled"
128+ except Exception as e :
129+ self .output = str (e )
130+ self .status = "error"
122131 self .time_completed = timestamp ()
123132 self .thing .action_notify (self )
124133
@@ -137,6 +146,28 @@ def __init__(self, thing, name, invokeaction=None, metadata=None):
137146
138147 self .queue = []
139148
149+ def as_action_description (self ):
150+ """
151+ Get the action description.
152+
153+ Returns a dictionary describing the action.
154+ """
155+ description = deepcopy (self .metadata )
156+
157+ # Create forms
158+ if "forms" not in description :
159+ description ["forms" ] = []
160+
161+ description ["forms" ].append (
162+ {
163+ "op" : "invokeaction" ,
164+ "href" : self .href_prefix + self .href ,
165+ "htv:methodName" : "POST" ,
166+ }
167+ )
168+
169+ return description
170+
140171 def invokeaction (self , input_ ):
141172 action_obj = ActionObject (
142173 self .thing , self .name , self .invokeaction_forwarder , input_
0 commit comments