-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #64 from DLHub-Argonne/async_client
Add Asynchronous Operations to Client
- Loading branch information
Showing
4 changed files
with
95 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
"""Tools for dealing with asynchronous execution""" | ||
from globus_sdk import GlobusAPIError | ||
from concurrent.futures import Future | ||
from threading import Thread | ||
from time import sleep | ||
import json | ||
|
||
|
||
class DLHubFuture(Future): | ||
"""Utility class for simplifying asynchronous execution in DLHub""" | ||
|
||
def __init__(self, client, task_id: str, ping_interval: float): | ||
""" | ||
Args: | ||
client (DLHubClient): Already-initialized client, used to check | ||
task_id (str): Set the task ID of the | ||
ping_interval (float): How often to ping the server to check status in seconds | ||
""" | ||
super().__init__() | ||
self.client = client | ||
self.task_id = task_id | ||
self.ping_interval = ping_interval | ||
|
||
# Once you create this, the task has already started | ||
self.set_running_or_notify_cancel() | ||
|
||
# Forcing the ping interval to be no less than 1s | ||
if ping_interval < 1: | ||
assert AttributeError('Ping interval must be at least 1 second') | ||
|
||
# Start a thread that polls status | ||
self._checker_thread = Thread(target=DLHubFuture._ping_server, args=(self,)) | ||
self._checker_thread.start() | ||
|
||
def _ping_server(self): | ||
while True: | ||
sleep(self.ping_interval) | ||
try: | ||
if not self.running(): | ||
break | ||
except GlobusAPIError: | ||
# Keep pinging even if the results fail | ||
continue | ||
|
||
def running(self): | ||
if super().running(): | ||
# If the task isn't already completed, check if it is still running | ||
status = self.client.get_task_status(self.task_id) | ||
# TODO (lw): What if the task fails on the server end? Do we have a "FAILURE" status? | ||
if status['status'] == 'COMPLETED': | ||
self.set_result(json.loads(status['result'])) | ||
return False | ||
return True | ||
return False | ||
|
||
def stop(self): | ||
"""Stop the execution of the function""" | ||
# TODO (lw): Should be attempt to cancel the execution of the task on DLHub? | ||
self.set_exception(Exception('Cancelled by user')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from dlhub_sdk.utils.futures import DLHubFuture | ||
from dlhub_sdk.client import DLHubClient | ||
from unittest import TestCase | ||
|
||
# ID of a task that has completed in DLHub | ||
completed_task = 'b8e51bc6-4081-4ec9-9e3b-b0e52198c08d' | ||
|
||
|
||
class TestFutures(TestCase): | ||
|
||
def test_future(self): | ||
client = DLHubClient() | ||
future = DLHubFuture(client, completed_task, 1) | ||
self.assertFalse(future.running()) | ||
self.assertTrue(future.done()) | ||
self.assertEquals({}, future.result()) |