Skip to content

Commit fe2dafb

Browse files
committed
ENH: Allow asking of asynchronous questions via Child.ask
1 parent 2cc8bbf commit fe2dafb

File tree

2 files changed

+27
-7
lines changed

2 files changed

+27
-7
lines changed

octue/resources/child.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,26 +63,32 @@ def ask(
6363
record_messages=True,
6464
save_diagnostics="SAVE_DIAGNOSTICS_ON_CRASH", # This is repeated as a string here to avoid a circular import.
6565
question_uuid=None,
66+
push_endpoint=None,
67+
bigquery_table_id=None,
6668
timeout=86400,
6769
maximum_heartbeat_interval=300,
6870
):
69-
"""Ask the child a question and wait for its answer - i.e. send it input values and/or an input manifest and
70-
wait for it to analyse them and return output values and/or an output manifest. The input values and manifest
71-
must conform to the schema in the child's twine.
72-
73-
:param any|None input_values: any input values for the question
74-
:param octue.resources.manifest.Manifest|None input_manifest: an input manifest of any datasets needed for the question
71+
"""Ask the child either:
72+
- A synchronous (ask-and-wait) question and wait for it to return an output. Questions are synchronous if
73+
neither the `push_endpoint` or the `bigquery_table_id` argument is provided.
74+
- An asynchronous (fire-and-forget) question and return immediately. To make a question asynchronous, provide
75+
either the `push_endpoint` or `bigquery_table_id` argument.
76+
77+
:param any|None input_values: any input values for the question, conforming with the schema in the child's twine
78+
:param octue.resources.manifest.Manifest|None input_manifest: an input manifest of any datasets needed for the question, conforming with the schema in the child's twine
7579
:param list(dict)|None children: a list of children for the child to use instead of its default children (if it uses children). These should be in the same format as in an app's app configuration file and have the same keys.
7680
:param bool subscribe_to_logs: if `True`, subscribe to logs from the child and handle them with the local log handlers
7781
:param bool allow_local_files: if `True`, allow the input manifest to contain references to local files - this should only be set to `True` if the child will have access to these local files
7882
:param callable|None handle_monitor_message: a function to handle monitor messages (e.g. send them to an endpoint for plotting or displaying) - this function should take a single JSON-compatible python primitive as an argument (note that this could be an array or object)
7983
:param bool record_messages: if `True`, record messages received from the child in the `received_messages` property
8084
:param str save_diagnostics: must be one of {"SAVE_DIAGNOSTICS_OFF", "SAVE_DIAGNOSTICS_ON_CRASH", "SAVE_DIAGNOSTICS_ON"}; if turned on, allow the input values and manifest (and its datasets) to be saved by the child either all the time or just if it fails while processing them
8185
:param str|None question_uuid: the UUID to use for the question if a specific one is needed; a UUID is generated if not
86+
:param str|None push_endpoint: if answers to the question should be pushed to an endpoint, provide its URL here (the returned subscription will be a push subscription); if not, leave this as `None`
87+
:param str|None bigquery_table_id: if answers to the questions should be written to BigQuery, provide the ID of the table here (e.g. "your-project.your-dataset.your-table") (the returned subscription will be a BigQuery subscription); if not, leave this as `None`
8288
:param float timeout: time in seconds to wait for an answer before raising a timeout error
8389
:param float|int maximum_heartbeat_interval: the maximum amount of time (in seconds) allowed between child heartbeats before an error is raised
8490
:raise TimeoutError: if the timeout is exceeded while waiting for an answer
85-
:return dict: a dictionary containing the keys "output_values" and "output_manifest"
91+
:return dict|None: for a synchronous question, a dictionary containing the keys "output_values" and "output_manifest"; for an asynchronous question, `None`
8692
"""
8793
subscription, _ = self._service.ask(
8894
service_id=self.id,
@@ -93,9 +99,14 @@ def ask(
9399
allow_local_files=allow_local_files,
94100
save_diagnostics=save_diagnostics,
95101
question_uuid=question_uuid,
102+
push_endpoint=push_endpoint,
103+
bigquery_table_id=bigquery_table_id,
96104
timeout=timeout,
97105
)
98106

107+
if push_endpoint or bigquery_table_id:
108+
return None
109+
99110
return self._service.wait_for_answer(
100111
subscription=subscription,
101112
handle_monitor_message=handle_monitor_message,

tests/cloud/deployment/google/cloud_run/test_cloud_run_deployment.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,12 @@ def test_cloud_run_deployment(self):
3434
# Check that the output dataset and its files can be accessed.
3535
with answer["output_manifest"].datasets["example_dataset"].files.one() as (datafile, f):
3636
self.assertEqual(f.read(), "This is some example service output.")
37+
38+
def test_cloud_run_deployment_asynchronously(self):
39+
"""Test asking an asynchronous (BigQuery) question."""
40+
answer = self.child.ask(
41+
input_values={"n_iterations": 3},
42+
bigquery_table_id="octue-sdk-python.octue_sdk_python_test_dataset.question-events",
43+
)
44+
45+
self.assertIsNone(answer)

0 commit comments

Comments
 (0)