Replacing asyncio.get_event_loop().run_until_complete with ayncio.run

Viewed 2186

I'm trying to run these asyncronyous tests, I run them by running this from the command line while in the root of this library node run-tests gateio --python-async

This code below causes a deprecation warning because I am using something (tox) that depends on python3.10 and it looks like get_event_loop is deprecated with python3.10

if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(main())

Deprecation Warning

[100%] Testing gateio WARN (testExchange @ run-tests.js:172)

WARN gateio (Python 3 Async): (explain @ run-tests.js:191)

    /path/ccxt/python/ccxt/test/test_async.py:558: DeprecationWarning: There is no current event loop
      asyncio.get_event_loop().run_until_complete(main())
    gateio using proxy ``
    EXCHANGE: gateio
    SYMBOL: BTC/USD
    CODE: ZRX
    gateio BTC/USD ticker None high: 65609.0 low: 64073.0 bid: 64958.24 ask: 64991.5 volume: 40525928.149905
    gateio fetched all 2176 tickers
    gateio fetched 10 OHLCVs
    gateio BTC/USD order book 2021-11-12T03:25:31.759Z bid: 64958.24 bidVolume: 0.3225 ask: 64991.5 askVolume: 0.3225
    gateio BTC/USD fetched 100 trades
    gateio BTC/USD fetch_orders() not supported
    gateio BTC/USD fetched 0 open orders
    gateio BTC/USD fetched 0 closed orders
    gateio ZRX fetch_transactions() not supported
    gateio fetched balance (explain @ run-tests.js:193)
WARN ["gateio"] (run-tests.js:272)
All done, 1 warnings (run-tests.js:276)


I've tried to update this line, to use asyncio.run instead, but then I just get an infinite loop and eventually the tests time out and fail

asyncio.run(main(), debug=True)

Timeout error

[100%] Testing gateio FAIL (testExchange @ run-tests.js:172)

FAILED gateio (Python 3 Async): (explain @ run-tests.js:190)

    timed out (explain @ run-tests.js:193)
FAIL ["gateio"] (run-tests.js:271)
All done, 1 failed (run-tests.js:276)


I'd like to know how to properly update this method so as to not get the deprecation warning and not get timed out

1 Answers

The issue here lies in how the event loop is managed. asyncio.run always creates a brand new event loop internally. The API you're using seems like it uses asyncio.get_event_loop() in the Exchange class which creates a different event loop from the one asyncio.run creates and passes that one around. This causes problems, as the loop that the Exchange class created is just sitting there idle, so trying to run anything with that loop won't really run. To fix this, you need to ensure you're using the loop that asyncio.run creates, which means refactoring the code a bit and using asyncio.get_running_loop(). asyncio.get_running_loop() will return the currently running loop or throw an exception if one is not running, it will never create a new one.

To specifically fix the issue you have (bear in mind, this change may break other areas of the code)

Change the constructor of the Exchange class in exchange.py to use asyncio.get_running_loop() instead of asyncio.get_event_loop() like so:

class Exchange(BaseExchange):

    def __init__(self, config={}):
        if 'asyncio_loop' in config:
            self.asyncio_loop = config['asyncio_loop']
        self.asyncio_loop = asyncio.get_running_loop() #this is the change here, note that this overwrites the loop from the config, so this could cause problems.
        self.aiohttp_trust_env = config.get('aiohttp_trust_env', self.aiohttp_trust_env)
        self.verify = config.get('verify', self.verify)
        self.own_session = 'session' not in config
        self.cafile = config.get('cafile', certifi.where())
        super(Exchange, self).__init__(config)
        self.throttle = None
        self.init_rest_rate_limiter()
        self.markets_loading = None
        self.reloading_markets = False

Next, you'll need to move your exchange init code into the main coroutine. This is so that when the constructor calls get_running_loop it will return the loop that asyncio.run created:

async def main():

  # instantiate all exchanges
  for id in ccxt.exchanges:
    if id == 'theocean':
      continue
    exchange = getattr(ccxt, id)
    exchange_config = {'verbose': argv.verbose}
    if sys.version_info[0] < 3:
      exchange_config.update({'enableRateLimit': True})
    if id in config:
      exchange_config = ccxt.Exchange.deep_extend(exchange_config, config[id])
    exchanges[id] = exchange(exchange_config)
    #other code as before...

This fix works, but bear in mind that the API itself may need to be refactored in a way that supports the way it handles the event loop to support the way asyncio.run creates and manages the event loop. In particular, the code in the Exchange class that can pull an event loop from a config may need to be refactored.

Related