Skip to content

Commit ccc043c

Browse files
[SYNPY-1590] Patching the async generator to return results as it gets them, and removing param passing to several submission related APIs which do not support it (#1305)
* Patching the async generator to return results as it gets them, and removing param passing to several submission related APIs which do not support it --------- Co-authored-by: Jenny Medina <medina.stsci@gmail.com>
1 parent 210bf36 commit ccc043c

12 files changed

Lines changed: 121 additions & 221 deletions

File tree

docs/tutorials/python/submission.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Before working with Submissions, it's helpful to understand how they fit into Sy
1414
## How Submissions Work
1515

1616
When you submit an Entity to an Evaluation:
17+
1718
- The Submission creates an immutable record linking your Entity to that Evaluation
1819
- The Evaluation owns this Submission record (not you as the submitter)
1920
- Organizers can add scores and feedback through a SubmissionStatus object
@@ -60,6 +61,13 @@ As an organizer of a Synapse challenge, you will
6061
* You have an existing entity with which to make a submission (can be a [File](./file.md) or Docker Repository)
6162
* You have the correct permissions on the Evaluation queue for your desired tutorial section (participant or organizer)
6263

64+
65+
Script setup:
66+
67+
```python
68+
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=13-30}
69+
```
70+
6371
## 1. Participating in a Synapse challenge
6472

6573
### 1. Make a submission to an existing evaluation queue on Synapse
@@ -77,29 +85,36 @@ As an organizer of a Synapse challenge, you will
7785
### 3. Count your submissions
7886

7987
```python
80-
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=72-88}
88+
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=72-82}
8189
```
8290

8391
### 4. Fetch all of your submissions from an existing evaluation queue on Synapse
8492

8593
```python
86-
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=90-101}
94+
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=82-95}
8795
```
8896

8997
### 5. Check the status of your submission
9098

9199
```python
92-
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=103-125}
100+
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=97-119}
93101
```
94102

95103
### 6. Cancel your submission
96104

97105
```python
98-
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=126-143}
106+
{!docs/tutorials/python/tutorial_scripts/submission_participant.py!lines=120-137}
99107
```
100108

101109
## 2. Organizing a Synapse challenge
102110

111+
112+
Script setup:
113+
114+
```python
115+
{!docs/tutorials/python/tutorial_scripts/submission_organizer.py!lines=12-31}
116+
```
117+
103118
### 1. Annotate a submission to score it
104119

105120
```python

docs/tutorials/python/tutorial_scripts/submission_organizer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"""
1111

1212
from synapseclient import Synapse
13-
from synapseclient.models import Submission, SubmissionBundle, SubmissionStatus
13+
from synapseclient.models import SubmissionBundle, SubmissionStatus
1414

1515
syn = Synapse()
1616
syn.login()

docs/tutorials/python/tutorial_scripts/submission_participant.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@
8080

8181
print(f"Total submissions in evaluation: {submission_count}")
8282

83-
# Get count of submissions with specific status (optional)
84-
scored_count = Submission.get_submission_count(
85-
evaluation_id=EVALUATION_ID, status="SCORED"
86-
)
87-
88-
print(f"SCORED submissions in evaluation: {scored_count}")
8983

9084
# ==============================================================================
9185
# 4. Fetch all of your submissions from an existing evaluation queue

synapseclient/api/evaluation_services.py

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -476,20 +476,16 @@ async def get_evaluation_submissions(
476476
client = Synapse.get_client(synapse_client=synapse_client)
477477

478478
uri = f"/evaluation/{evaluation_id}/submission/all"
479-
query_params = {}
480479

481480
if status:
482-
query_params["status"] = status
481+
uri = uri + "?status=" + status
483482

484-
async for item in rest_get_paginated_async(
485-
uri=uri, params=query_params, synapse_client=client
486-
):
483+
async for item in rest_get_paginated_async(uri=uri, synapse_client=client):
487484
yield item
488485

489486

490487
async def get_user_submissions(
491488
evaluation_id: str,
492-
user_id: Optional[str] = None,
493489
*,
494490
synapse_client: Optional["Synapse"] = None,
495491
) -> AsyncGenerator[dict[str, Any], None]:
@@ -501,8 +497,6 @@ async def get_user_submissions(
501497
502498
Arguments:
503499
evaluation_id: The ID of the evaluation queue.
504-
user_id: Optionally specify the ID of the user whose submissions will be returned.
505-
If omitted, this returns the submissions of the caller.
506500
synapse_client: If not passed in and caching was not disabled by `Synapse.allow_client_caching(False)`
507501
this will use the last created instance from the Synapse class constructor.
508502
@@ -514,20 +508,13 @@ async def get_user_submissions(
514508
client = Synapse.get_client(synapse_client=synapse_client)
515509

516510
uri = f"/evaluation/{evaluation_id}/submission"
517-
query_params = {}
518-
519-
if user_id:
520-
query_params["userId"] = user_id
521511

522-
async for item in rest_get_paginated_async(
523-
uri=uri, params=query_params, synapse_client=client
524-
):
512+
async for item in rest_get_paginated_async(uri=uri, synapse_client=client):
525513
yield item
526514

527515

528516
async def get_submission_count(
529517
evaluation_id: str,
530-
status: Optional[str] = None,
531518
synapse_client: Optional["Synapse"] = None,
532519
) -> dict:
533520
"""
@@ -537,8 +524,6 @@ async def get_submission_count(
537524
538525
Arguments:
539526
evaluation_id: The ID of the evaluation queue.
540-
status: Optionally filter submissions by a submission status, such as SCORED, VALID,
541-
INVALID, OPEN, CLOSED or EVALUATION_IN_PROGRESS.
542527
synapse_client: If not passed in and caching was not disabled by `Synapse.allow_client_caching(False)`
543528
this will use the last created instance from the Synapse class constructor.
544529
@@ -550,12 +535,8 @@ async def get_submission_count(
550535
client = Synapse.get_client(synapse_client=synapse_client)
551536

552537
uri = f"/evaluation/{evaluation_id}/submission/count"
553-
query_params = {}
554538

555-
if status:
556-
query_params["status"] = status
557-
558-
response = await client.rest_get_async(uri, params=query_params)
539+
response = await client.rest_get_async(uri)
559540

560541
return response
561542

@@ -812,14 +793,11 @@ async def get_evaluation_submission_bundles(
812793
client = Synapse.get_client(synapse_client=synapse_client)
813794

814795
uri = f"/evaluation/{evaluation_id}/submission/bundle/all"
815-
query_params = {}
816796

817797
if status:
818-
query_params["status"] = status
798+
uri = uri + "?status=" + status
819799

820-
async for item in rest_get_paginated_async(
821-
uri=uri, params=query_params, synapse_client=client
822-
):
800+
async for item in rest_get_paginated_async(uri=uri, synapse_client=client):
823801
yield item
824802

825803

@@ -847,9 +825,6 @@ async def get_user_submission_bundles(
847825
client = Synapse.get_client(synapse_client=synapse_client)
848826

849827
uri = f"/evaluation/{evaluation_id}/submission/bundle"
850-
query_params = {}
851828

852-
async for item in rest_get_paginated_async(
853-
uri=uri, params=query_params, synapse_client=client
854-
):
829+
async for item in rest_get_paginated_async(uri=uri, synapse_client=client):
855830
yield item

synapseclient/client.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,17 @@ class Synapse(object):
294294
Example: Getting started
295295
Logging in to Synapse using an authToken
296296
297-
```python
298-
import synapseclient
299-
syn = synapseclient.login(authToken="authtoken")
300-
```
297+
```python
298+
import synapseclient
299+
syn = synapseclient.login(authToken="authtoken")
300+
```
301301
302302
Using environment variable or `.synapseConfig`
303303
304-
```python
305-
import synapseclient
306-
syn = synapseclient.login()
307-
```
304+
```python
305+
import synapseclient
306+
syn = synapseclient.login()
307+
```
308308
309309
Example: Adding an additional `user_agent` value
310310
This example shows how to add an additional `user_agent` to the HTTP headers

synapseclient/core/async_utils.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ def wrap_async_generator_to_sync_generator(async_gen_func: Callable, *args, **kw
133133
f"For Jupyter/IPython notebooks: You can use 'async for' directly in cells.\n"
134134
f"For other async contexts: Ensure you're in an async function and use 'async for'."
135135
)
136-
elif loop:
136+
137+
if loop:
137138
nest_asyncio.apply(loop=loop)
138139

139140
# Create the async generator
@@ -143,7 +144,7 @@ def wrap_async_generator_to_sync_generator(async_gen_func: Callable, *args, **kw
143144
try:
144145
while True:
145146
try:
146-
item = loop.run_until_complete(async_gen.__anext__())
147+
item = loop.run_until_complete(anext(async_gen))
147148
yield item
148149
except StopAsyncIteration:
149150
break
@@ -154,23 +155,26 @@ def wrap_async_generator_to_sync_generator(async_gen_func: Callable, *args, **kw
154155
except (RuntimeError, StopAsyncIteration):
155156
pass
156157
else:
157-
# No running loop, create a new one
158-
async def run_generator():
159-
async_gen = async_gen_func(*args, **kwargs)
160-
items = []
161-
try:
162-
async for item in async_gen:
163-
items.append(item)
164-
finally:
158+
# No running loop, create a new one and yield items one by one
159+
async def async_wrapper():
160+
async for item in async_gen_func(*args, **kwargs):
161+
yield item
162+
163+
loop = asyncio.new_event_loop()
164+
async_gen = async_wrapper()
165+
try:
166+
while True:
165167
try:
166-
await async_gen.aclose()
167-
except (RuntimeError, StopAsyncIteration):
168-
pass
169-
return items
170-
171-
items = asyncio.run(run_generator())
172-
for item in items:
173-
yield item
168+
item = loop.run_until_complete(anext(async_gen))
169+
yield item
170+
except StopAsyncIteration:
171+
break
172+
finally:
173+
try:
174+
loop.run_until_complete(async_gen.aclose())
175+
except (RuntimeError, StopAsyncIteration):
176+
pass
177+
loop.close()
174178

175179

176180
# Adapted from

0 commit comments

Comments
 (0)