mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-06-07 14:00:40 +02:00
fix: creates property functions to avoid closure and late binding issue
When having multiple tasks, they all pointed to the one defined last.
This commit is contained in:
parent
8564df5adc
commit
0504a50a08
@ -95,6 +95,54 @@ class TaskManager:
|
|||||||
for name, method in inspect.getmembers(
|
for name, method in inspect.getmembers(
|
||||||
self.service, predicate=inspect.iscoroutinefunction
|
self.service, predicate=inspect.iscoroutinefunction
|
||||||
):
|
):
|
||||||
|
# create start and stop methods for each coroutine
|
||||||
|
setattr(self.service, f"start_{name}", self._make_start_task(name, method))
|
||||||
|
setattr(self.service, f"stop_{name}", self._make_stop_task(name))
|
||||||
|
|
||||||
|
def start_autostart_tasks(self) -> None:
|
||||||
|
if self.service._autostart_tasks is not None:
|
||||||
|
for service_name, args in self.service._autostart_tasks.items():
|
||||||
|
start_method = getattr(self.service, f"start_{service_name}", None)
|
||||||
|
if start_method is not None and callable(start_method):
|
||||||
|
start_method(*args)
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
f"No start method found for service '{service_name}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _make_stop_task(self, name: str) -> Callable[..., Any]:
|
||||||
|
"""
|
||||||
|
Factory function to create a 'stop_task' function for a running task.
|
||||||
|
|
||||||
|
The generated function cancels the associated asyncio task using 'name' for
|
||||||
|
identification, ensuring proper cleanup. Avoids closure and late binding issues.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name of the coroutine task, used for its identification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def stop_task() -> None:
|
||||||
|
# cancel the task
|
||||||
|
task = self.tasks.get(name, None)
|
||||||
|
if task is not None:
|
||||||
|
self._loop.call_soon_threadsafe(task["task"].cancel)
|
||||||
|
|
||||||
|
return stop_task
|
||||||
|
|
||||||
|
def _make_start_task( # noqa
|
||||||
|
self, name: str, method: Callable[..., Any]
|
||||||
|
) -> Callable[..., Any]:
|
||||||
|
"""
|
||||||
|
Factory function to create a 'start_task' function for a coroutine.
|
||||||
|
|
||||||
|
The generated function starts the coroutine as an asyncio task, handling
|
||||||
|
registration and monitoring.
|
||||||
|
It uses 'name' and 'method' to avoid the closure and late binding issue.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name of the coroutine, used for task management.
|
||||||
|
method (callable): The coroutine to be turned into an asyncio task.
|
||||||
|
"""
|
||||||
|
|
||||||
@wraps(method)
|
@wraps(method)
|
||||||
def start_task(*args: Any, **kwargs: Any) -> None:
|
def start_task(*args: Any, **kwargs: Any) -> None:
|
||||||
@ -136,9 +184,7 @@ class TaskManager:
|
|||||||
# Extend the list of positional arguments with None values to match
|
# Extend the list of positional arguments with None values to match
|
||||||
# the length of the parameter names list. This is done to ensure
|
# the length of the parameter names list. This is done to ensure
|
||||||
# that zip can pair each parameter name with a corresponding value.
|
# that zip can pair each parameter name with a corresponding value.
|
||||||
args_padded = list(args) + [None] * (
|
args_padded = list(args) + [None] * (len(parameter_names) - len(args))
|
||||||
len(parameter_names) - len(args)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create a dictionary of keyword arguments by pairing the parameter
|
# Create a dictionary of keyword arguments by pairing the parameter
|
||||||
# names with the values in 'args_padded'. Then merge this dictionary
|
# names with the values in 'args_padded'. Then merge this dictionary
|
||||||
@ -170,23 +216,4 @@ class TaskManager:
|
|||||||
else:
|
else:
|
||||||
logger.error(f"Task `{name}` is already running!")
|
logger.error(f"Task `{name}` is already running!")
|
||||||
|
|
||||||
def stop_task() -> None:
|
return start_task
|
||||||
# cancel the task
|
|
||||||
task = self.tasks.get(name, None)
|
|
||||||
if task is not None:
|
|
||||||
self._loop.call_soon_threadsafe(task["task"].cancel)
|
|
||||||
|
|
||||||
# create start and stop methods for each coroutine
|
|
||||||
setattr(self.service, f"start_{name}", start_task)
|
|
||||||
setattr(self.service, f"stop_{name}", stop_task)
|
|
||||||
|
|
||||||
def start_autostart_tasks(self) -> None:
|
|
||||||
if self.service._autostart_tasks is not None:
|
|
||||||
for service_name, args in self.service._autostart_tasks.items():
|
|
||||||
start_method = getattr(self.service, f"start_{service_name}", None)
|
|
||||||
if start_method is not None and callable(start_method):
|
|
||||||
start_method(*args)
|
|
||||||
else:
|
|
||||||
logger.warning(
|
|
||||||
f"No start method found for service '{service_name}'"
|
|
||||||
)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user