terricain / aioboto3

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

ERROR:root:'ClientCreatorContext' object has no attribute 'create_bucket' #225

Closed jcf-dev closed 3 years ago

jcf-dev commented 3 years ago

Description

I'm trying to use this package in my FastAPI app (to make everything async); however, I can't get this working. Here is a sample of my code.

api.py

...
from typing import List, Optional
from files import s3, upload_file_to_bucket, create_bucket
...

@router.post('/api/storage/buckets/create', name='Create Bucket Endpoint', status_code=201)
async def create_bucket_api(bucket_name: str, region: Optional[str] = None):
    s3_client = await s3()
    bucket_created = await create_bucket(s3_client=s3_client,
                                         bucket_name=bucket_name,
                                         region=region)
    if bucket_created:
        return JSONResponse(content="Object has been uploaded to bucket successfully",
                            status_code=status.HTTP_201_CREATED)
    else:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                            detail="File could not be uploaded")

files.py

import logging

from aiobotocore.client import BaseClient
from fastapi import UploadFile

AWS_ACCESS_KEY_ID = os.getenv('POSTGRES_HOST')
AWS_SECRET_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_S3_BUCKET_NAME = os.getenv('AWS_S3_BUCKET_NAME')

async def s3() -> BaseClient:
    client = aioboto3.client(service_name='s3',
                             aws_access_key_id=AWS_ACCESS_KEY_ID,
                             aws_secret_access_key=AWS_SECRET_KEY,
                             endpoint_url='http://localstack:4566/')  # Use LocalStack Endpoint
    return client

async def create_bucket(s3_client: BaseClient, bucket_name: str, region: str = None):
    try:
        if region is None:
            await s3_client.create_bucket(Bucker=bucket_name)
        else:
            s3_client = await s3_client.client(region=region)
            location = {'LocationConstraint': region}
            await s3_client.create_bucket(Bucket=bucket_name, CreateBucketConfiguration=location)
    except Exception as e:
        logging.error(e)
        return False
    return True

async def upload_file_to_bucket(s3_client: BaseClient, file: UploadFile, bucket: str, folder: str,
                                filename: str = None):
    # If S3 object_name was not specified, use file_name
    if filename is None:
        filename = file.filename
    # Upload the file
    try:
        await s3_client.upload_fileobj(file.file, bucket, f'{folder}/{filename}')
    except Exception as e:
        logging.error(e)
        return False
    return True

I'm getting this error:

ERROR:root:'ClientCreatorContext' object has no attribute 'create_bucket
INFO: 172.20.0.1:33546 - "POST /api/storage/buckets/create?bucket_name=demp-bucket HTTP/1.1" 500 Internal Server Error
/usr/local/lib/python3.8/site-packages/starlette/exceptions.py:93: RuntimeWarning: coroutine 'AioSession._create_client' was never awaited await response(scope, receive, sender)

The error says that I need to await something. I've also tried using AioBaseClient. I would greatly appreciate it if you can point out the part I've been doing wrong. Thanks!

terricain commented 3 years ago

try changing

s3_client = await s3()
# to
async with (await s3()) as s3_client:
   blah everything else indented

you could drop the async def off the s3() function and then do

async with s3() as s3_client:
   blah everything else indented
terricain commented 3 years ago

For using fastapi, I've not used it but i'd say you probably want the s3 client as a dependency declared something like

async def get_s3():
    async with aioboto3.client('s3', blah) as client:
        yield client

Though this might create the client on every request, ideally you'd want to create the client once, and then just reference that in every request (as long as you dont close the client)

jcf-dev commented 3 years ago

Okay, now I get it. Thank you very much!