Skip to content

Commit 63da840

Browse files
authored
[SYNPY-1634] Add walk method to containers (#1242)
* Add walk_async and walk methods to StorableContainer for entity traversal
1 parent 3ccb87f commit 63da840

File tree

10 files changed

+1109
-3
lines changed

10 files changed

+1109
-3
lines changed

docs/reference/experimental/async/folder.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ at your own risk.
1414
- store_async
1515
- delete_async
1616
- copy_async
17+
- walk_async
1718
- sync_from_synapse_async
1819
- flatten_file_list
1920
- map_directory_to_all_contained_files

docs/reference/experimental/async/project.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ at your own risk.
1313
- get_async
1414
- store_async
1515
- delete_async
16+
- walk_async
1617
- sync_from_synapse_async
1718
- flatten_file_list
1819
- map_directory_to_all_contained_files

docs/reference/experimental/sync/folder.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ at your own risk.
2525
- store
2626
- delete
2727
- copy
28+
- walk
2829
- sync_from_synapse
2930
- flatten_file_list
3031
- map_directory_to_all_contained_files

docs/reference/experimental/sync/project.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ at your own risk.
2424
- get
2525
- store
2626
- delete
27+
- walk
2728
- sync_from_synapse
2829
- flatten_file_list
2930
- map_directory_to_all_contained_files

synapseclient/core/async_utils.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,68 @@ def wrap_async_to_sync(coroutine: Coroutine[Any, Any, Any], syn: "Synapse") -> A
9393
return asyncio.run(coroutine)
9494

9595

96+
def wrap_async_generator_to_sync_generator(async_gen_func: Callable, *args, **kwargs):
97+
"""
98+
Wrap an async generator function to be called in a sync context, returning a sync generator.
99+
100+
This function takes an async generator function and its arguments, then yields items
101+
synchronously by running the async generator in the appropriate event loop.
102+
103+
Arguments:
104+
async_gen_func: The async generator function to wrap
105+
*args: Positional arguments to pass to the async generator function
106+
**kwargs: Keyword arguments to pass to the async generator function
107+
108+
Yields:
109+
Items from the async generator, yielded synchronously
110+
"""
111+
loop = None
112+
113+
try:
114+
loop = asyncio.get_running_loop()
115+
except RuntimeError:
116+
pass
117+
118+
if loop:
119+
nest_asyncio.apply(loop=loop)
120+
121+
# Create the async generator
122+
async_gen = async_gen_func(*args, **kwargs)
123+
124+
# Yield items from the async generator synchronously
125+
try:
126+
while True:
127+
try:
128+
item = loop.run_until_complete(async_gen.__anext__())
129+
yield item
130+
except StopAsyncIteration:
131+
break
132+
finally:
133+
# Ensure the generator is properly closed
134+
try:
135+
loop.run_until_complete(async_gen.aclose())
136+
except (RuntimeError, StopAsyncIteration):
137+
pass
138+
else:
139+
# No running loop, create a new one
140+
async def run_generator():
141+
async_gen = async_gen_func(*args, **kwargs)
142+
items = []
143+
try:
144+
async for item in async_gen:
145+
items.append(item)
146+
finally:
147+
try:
148+
await async_gen.aclose()
149+
except (RuntimeError, StopAsyncIteration):
150+
pass
151+
return items
152+
153+
items = asyncio.run(run_generator())
154+
for item in items:
155+
yield item
156+
157+
96158
# Adapted from
97159
# https://github.com/keflavich/astroquery/blob/30deafc3aa057916bcdca70733cba748f1b36b64/astroquery/utils/process_asyncs.py#L11
98160
def async_to_sync(cls):

0 commit comments

Comments
 (0)