terricain / aioboto3

Wrapper to use boto3 resources with the aiobotocore async backend
Apache License 2.0
754 stars 76 forks source link

aioboto3 caches the loop and won't allow to use a new one even if the cached loop is closed #180

Closed yvdlima closed 4 years ago

yvdlima commented 5 years ago

Description

I was writing tests with Pytest which uses multiple event loops and due to the global DEFAULT_SESSION behavior I had a bit of trouble.

Pytest loop fixture is function scoped which recreates the asyncio loop every time a test runs. In my case I had a client factory which interacted with aioboto3, the first test would trigger the creation of the DEFAULT_SESSION but subsequent tests will fail as they will always refer to the loop of the DEFAULT_SESSION which is closed. Even if you set the loop in the client or resource call the loop of the DEFAULT_SESSION will be used.

I'm not sure if this is the intended design and just missing in the documentation and examples or if it is a bug, either way I'm willing to open a PR to fix it after knowing your thoughts.

What I Did

# This is not the exactly code I was executing but it shows 

import pytest
import multicloud_table_client_factory

@pytest.fixture
async def multicloud_client_client(loop):
  # This client will eventually call `aioboto3.resource('dynamodb')`
  return await multicloud_table_client_factory.new_client()

def test_get_empty_result(multicloud_client_client):
  results = multicloud_client_client(primary_key="invalid_test", filter="*", only_use_aws=True)
  assert not len(results)

def test_get_many_results(multicloud_client_client):
  # This test will always fail because the loop aioboto3 cached was closed.
  results = multicloud_client_client(primary_key="many_entries", filter="*", only_use_aws=True)
  assert len(results) > 500

This is how I quickly fixed for my tests:


@pytest.fixture
async def multicloud_client_client(loop):
    yield await timeline.initialize()
    # This will clear aioboto3 default session at the end of the test and it will recreate one with the current asyncio loop when it's called again
    aioboto3.DEFAULT_SESSION = None

It does feels like a bad fix so I will just rewrite my code to use the aioboto3.session.Session.resource instead as it will use the correct loop. However this is not documented and not shown in any example

terricain commented 4 years ago

Yeah I know exactly how you feel with this one :smile:. The issue is that's exactly how boto3 works so the aim was to mimic that behaviour.

You can make a fixture that will return an aioboto3 client like here https://github.com/terrycain/aioboto3/blob/master/tests/conftest.py#L80-L94

Does that help?

Feel free to add some documentation around using this with pytest if you want