Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions fire/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def main(argv):
"""

import asyncio
import concurrent.futures
import inspect
import json
import os
Expand Down Expand Up @@ -649,6 +650,30 @@ def _GetMember(component, args):
raise FireError('Could not consume arg:', arg)


def _RunCoroutine(coro):
"""Runs a coroutine, blocking until it completes.

Args:
coro: The coroutine to run.

Returns:
The value the coroutine returns.

Raises:
Exception: Whatever the coroutine raises.
"""
try:
asyncio.get_running_loop()
except RuntimeError:
return asyncio.run(coro)
# asyncio.run refuses to be called while this thread already runs a loop,
# which happens when Fire is called from async code. Drive the coroutine
# from a loop belonging to a worker thread instead, so the call still
# blocks here until the coroutine completes.
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
return pool.submit(asyncio.run, coro).result()


def _CallAndUpdateTrace(component, args, component_trace, treatment='class',
target=None):
"""Call the component by consuming args from args, and update the FireTrace.
Expand Down Expand Up @@ -678,14 +703,7 @@ def _CallAndUpdateTrace(component, args, component_trace, treatment='class',

# Call the function.
if inspectutils.IsCoroutineFunction(fn):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
# No event loop running, create a new one
component = asyncio.run(fn(*varargs, **kwargs))
else:
# Event loop is already running
component = loop.run_until_complete(fn(*varargs, **kwargs))
component = _RunCoroutine(fn(*varargs, **kwargs))
else:
component = fn(*varargs, **kwargs)

Expand Down
18 changes: 18 additions & 0 deletions fire/fire_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"""Tests for the fire module."""

import asyncio
import os
import sys
from unittest import mock
Expand Down Expand Up @@ -716,6 +717,23 @@ def testFireAsyncio(self):
self.assertEqual(fire.Fire(tc.py3.WithAsyncio,
command=['double', '--count', '10']), 20)

def testFireAsyncioFromRunningLoop(self):
async def main():
return fire.Fire(tc.py3.WithAsyncio,
command=['double', '--count', '10'])

self.assertEqual(asyncio.run(main()), 20)

def testFireAsyncioFromRunningLoopRaises(self):
async def raiser(count=0):
raise ValueError('boom')

async def main():
return fire.Fire(raiser, command=['--count', '3'])

with self.assertRaises(ValueError):
asyncio.run(main())


if __name__ == '__main__':
testutils.main()
Loading