Skip to content

Commit 41bc475

Browse files
committed
additional context can be passed to the user-defined callback function, more type hinting
1 parent 412cc9e commit 41bc475

File tree

7 files changed

+43
-25
lines changed

7 files changed

+43
-25
lines changed

examples/custom_save_fn.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@
1414
ENDPOINT = "echo"
1515

1616

17-
def intercept_result(future: Future):
17+
def intercept_result(context, future: Future):
1818
"""
1919
Will be invoked on every process completion
2020
"""
2121
data = None
2222
if future.done():
23-
with open("/path/to/saved/file") as f:
23+
fname = context.get("read_result_from", None)
24+
with open(fname) as f:
2425
data = f.read()
2526
# 1. get current result object
2627
res = future.result()
2728
# 2. update the report variable,
2829
# you may update only these: report,error,status
2930
res.report = data
30-
res.status = "success"
31+
if context.get("force_success", False):
32+
res.status = "success"
3133
# 3. set new result
3234
future._result = res
3335

@@ -42,7 +44,13 @@ def intercept_result(future: Future):
4244
app.testing = True
4345
c = app.test_client()
4446
# request new process
45-
data = {"args": ["hello", "world"]}
47+
data = {
48+
"args": ["hello", "world"],
49+
"callback_context": {
50+
"read_result_from": "/path/to/saved/file",
51+
"force_success": True,
52+
},
53+
}
4654
r = c.post(f"/{ENDPOINT}", json=data)
4755
# get result
4856
result_url = r.get_json()["result_url"]

examples/run_script.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def test():
2525
```
2626
"""
2727
url = "http://localhost:4000/scripts/hacktheplanet"
28+
# request without any data
2829
resp = requests.post(url)
2930
resp_data = resp.json()
3031
print(resp_data)

examples/with_callback.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
ENDPOINT = "echo"
1515

1616

17-
def my_callback_fn(future: Future):
17+
def my_callback_fn(extra_callback_context, future: Future):
1818
"""
1919
Will be invoked on every process completion
2020
"""
2121
print("[i] Process running ?:", future.running())
2222
print("[i] Process completed ?:", future.done())
2323
print("[+] Result: ", future.result())
24+
print("[+] Context: ", extra_callback_context)
2425

2526

2627
shell2http.register_command(

examples/with_signals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323

2424
@my_signal.connect
25-
def my_callback_fn(future: Future):
25+
def my_callback_fn(extra_callback_context, future: Future):
2626
"""
2727
Will be invoked on every process completion
2828
"""

flask_shell2http/api.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
# system imports
1010
from http import HTTPStatus
11-
from typing import Callable, Any
11+
import functools
12+
from typing import Callable, Dict, Any
1213

1314
# web imports
1415
from flask import request, jsonify, make_response
@@ -75,7 +76,9 @@ def post(self):
7576
f"Requester: '{request.remote_addr}'."
7677
)
7778
# Check if request data is correct and parse it
78-
cmd, timeout, key = request_parser.parse_req(request, self.command_name)
79+
cmd, timeout, callback_context, key = request_parser.parse_req(
80+
request, self.command_name
81+
)
7982

8083
# run executor job in background
8184
future = self.executor.submit_stored(
@@ -84,8 +87,10 @@ def post(self):
8487
# callback that removes the temporary directory
8588
future.add_done_callback(request_parser.cleanup_temp_dir)
8689
if self.user_callback_fn:
87-
# user defined callback fn
88-
future.add_done_callback(self.user_callback_fn)
90+
# user defined callback fn with callback_context if any
91+
future.add_done_callback(
92+
functools.partial(self.user_callback_fn, callback_context)
93+
)
8994

9095
logger.info(f"Job: '{key}' --> added to queue for command: {cmd}")
9196
result_url = f"{request.base_url}?key={key}"
@@ -101,7 +106,7 @@ def post(self):
101106
def __init__(
102107
self,
103108
command_name: str,
104-
user_callback_fn: Callable[[Future], Any],
109+
user_callback_fn: Callable[[Dict, Future], Any],
105110
executor: Executor,
106111
):
107112
self.command_name: str = command_name

flask_shell2http/base_entrypoint.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# system imports
22
from collections import OrderedDict
3-
from typing import Callable, Any
3+
from typing import Callable, Dict, Any
44

55
# web imports
66
from flask_executor import Executor
@@ -74,7 +74,7 @@ def register_command(
7474
self,
7575
endpoint: str,
7676
command_name: str,
77-
callback_fn: Callable[[Future], Any] = None,
77+
callback_fn: Callable[[Dict, Future], Any] = None,
7878
) -> None:
7979
"""
8080
Function to map a shell command to an endpoint.
@@ -89,17 +89,19 @@ def register_command(
8989
For example,
9090
if you pass ``{ "args": ["Hello", "World"] }``
9191
in POST request, it gets converted to ``echo Hello World``.\n
92-
callback_fn (func):
92+
callback_fn (Callable[[Dict, Future], Any]):
9393
- An optional function that is invoked when a requested process
9494
to this endpoint completes execution.
9595
- This is added as a
9696
``concurrent.Future.add_done_callback(fn=callback_fn)``
97-
- A same callback function may be used for multiple commands.
97+
- The same callback function may be used for multiple commands.
98+
- if request JSON contains a `callback_context` attr, it will be passed
99+
as the first argument to this function.
98100
99101
Examples::
100102
101-
def my_callback_fn(future):
102-
print(future.result())
103+
def my_callback_fn(context: dict, future: Future) -> None:
104+
print(future.result(), context)
103105
104106
shell2http.register_command(endpoint="echo", command_name="echo")
105107
shell2http.register_command(
@@ -108,7 +110,7 @@ def my_callback_fn(future):
108110
callback_fn=my_callback_fn
109111
)
110112
"""
111-
uri = self.__construct_route(endpoint)
113+
uri: str = self.__construct_route(endpoint)
112114
# make sure the given endpoint is not already registered
113115
cmd_already_exists = self.__commands.get(uri)
114116
if cmd_already_exists:

flask_shell2http/classes.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,33 +139,34 @@ def __parse_multipart_req(args: List[str], files) -> (List[str], str):
139139
logger.debug(f"Request files saved under temp directory: '{tmpdir}'")
140140
return args, tmpdir
141141

142-
def parse_req(self, request, base_command: str) -> (str, str):
142+
def parse_req(self, request, base_command: str) -> (str, int, Dict, str):
143+
# default values if request is w/o any data
144+
# i.e. just run-script
143145
args: List[str] = []
146+
timeout: int = DEFAULT_TIMEOUT
144147
tmpdir = None
148+
callback_context = {}
145149
if request.is_json:
146150
# request does not contain a file
147151
args = request.json.get("args", [])
148152
timeout: int = request.json.get("timeout", DEFAULT_TIMEOUT)
153+
callback_context = request.json.get("callback_context", {})
149154
elif request.files:
150155
# request contains file
156+
callback_context = request.form.get("callback_context", {})
151157
received_args = request.form.getlist("args")
152158
timeout: int = request.form.get("timeout", DEFAULT_TIMEOUT)
153159
args, tmpdir = RequestParser.__parse_multipart_req(
154160
received_args, request.files
155161
)
156-
else:
157-
# request is w/o any data
158-
# i.e. just run-script
159-
args = []
160-
timeout: int = DEFAULT_TIMEOUT
161162

162163
cmd: List[str] = base_command.split(" ")
163164
cmd.extend(args)
164165
key: str = calc_hash(cmd)
165166
if tmpdir:
166167
self.__tmpdirs.update({key: tmpdir})
167168

168-
return cmd, timeout, key
169+
return cmd, timeout, callback_context, key
169170

170171
def cleanup_temp_dir(self, future: Future) -> None:
171172
key: str = future.result().key

0 commit comments

Comments
 (0)