Skip to main content

Fastapi mc style tools to make it easier to develop.

Project description

FastAPI-MCtools

  • MC(나)의 FastAPI로 개발할 때 자주 사용하는 코드들을 모아놓은 패키지입니다.
  • This is a package that contains frequently used codes when developing with FastAPI by MC(me).
  • This is kind of an utility package
  • 블로그 소개글 참고

Installation

pip install fastapi-mctools

Usage

CLI

1-1. run dev

  • run uvicorn server for development, this command wraps uvicorn command.
  • this command will find main.py and run uvicorn server
  • use this command when running in local environment and use --port and --host for setting port and host. default is 8000 and 127.0.0.1
mct run dev

1-2. run prod

  • run gunicorn server for production, this command wraps gunicorn command.
  • this command will find gunicorn.config.py and run gunicorn server
  • if not existing gunicorn.config.py, it will make basic gunicorn config file
mct run prod

2. startproject

  • create a new FastAPI project in ordinary way
mct startproject

3. gunicorn

  • make gunicorn basic config
mct gunicorn

4. types

  • check all functions if existing missing type hint
  • this command will find all functions in the file or directory and check if there is any missing type hint
  • use this if you are very strict about type hinting
mct types <path: directory or file>

5. shell

  • simple ipython shell
  • need to install ipython
mct shell
# in ipython shell
# need to import session that made by generator or async generator

# sync session
from app.session import get_db
db = next(get_db())

# async session
from app.session import get_db
db = await anxet(get_db())

DB Session

  • DB, AsyncDB
  • when you want to make db session in the fast way, use this
from fastapi_mctools.db import DB, AsyncDB

get_db = DB(db_url)
get_db = AsyncDB(db_url)

Request Logging Middleware

  • RequestLoggingMiddleware
  • when you simply want to log all requests, use this
  • you can use log configuration by dictConfig or fileConfig
# main.py
import logging
from fastapi import FastAPI
from fastapi_mctools.middlewares import RequestLoggingMiddleware

app = FastAPI()

logger = logging.getLogger('request')

app.add_middleware(RequestLoggingMiddleware, logger=logger)

SQLAlchemy ORM

  • sync_base, async_base
  • these are kind of repositories for frequently used ORM operations, such as CRUD
  • if not putting any column name, it will use all columns in the model
  • can use filterbackend for filtering data
from fastapi_mctools.orms.sqlalchemy import sync_base, async_base
from fastapi_mctools.orms.filters import FilterBackend

class UserCreate(async_base.ACreateBase):
    ...


class UserRead(async_base.AReadBase):
    ...


class UserUpdate(async_base.AUpdateBase):
    ...

class UserDelete(async_base.ADeleteBase):
    ...

class UserRepository(UserCreate, UserRead, UserUpdate, UserDelete):
    def __init__(self, model: SqlalchemyModel) -> None:
        super().__init__(model=model)


user_repository = UserRepository(model=User)

# AsyncSession from sqlalchemy.ext.asyncio
async def create_user(db: AsyncSession, data: dict) -> User:
    return await user_repository.create(db, **data)

async def read_user(db: AsyncSession, user_id: int) -> User:
    user =  await user_repository.get_by_id(db, user_id)
    users = await user_repository.get_by_filters(db, age=20) # This will return data for users whose age is 20.
    ...

async def read_user_with_filterbackend(db: AsyncSession, user_id: int) -> User:
    filter_backend = FilterBackend()
    filter_backend.set_model(User)
    filter_backend.add_filter({"age": 20})
    filter_backend.add_filter({"name": "%test%"})
    users = await user_repository.get_by_filters(db, filter_backend=filter_backend) # This will return data for users whose age is 20 and name contains "test"
    ...

# update and delete are similar to create and read

TestTools

  • db_managers
  • when you want to test with db and set up and tear down db in very simple way
# conftest.py
from fastapi_mctools.test_tools.db_managers import TestConfDBManager

db_test_manager = TestConfDBManager(TEST_DB_URL)

@pytest.fixture
async def db():
    # Base is your sqlalchemy base
    async for session in db_test_manager.get_async_db_session(base=Base, is_meta=True):
        yield session


# test_something.py

async def test_something(db):
    # db is your test db session
    ...

dependencies

  • Dependency
  • this is a class that can be used as a dependency manager that can handle multiple dependencies in an single instance
  • so you can only import one instance even if there are many dependencies
async def temp_dep_1():
    return 1

async def temp_dep_2():
    return 2

async def temp_dep_3():
    return 3

temp_dep = Dependency(
    TempDep1=temp_dep_1,
    TempDep2=temp_dep_2,
    TempDep3=temp_dep_3
)

# in your route

@app.get('/temp_1')
async def temp_1(result: temp_dep.TempDep1): # temp_dep[0] is possible
    return result

@app.get('/temp_2')
async def temp_2(result: temp_dep.TempDep2): # temp_dep[1] is possible
    return result

@app.get('/temp_3')
async def temp_3(result: temp_dep.TempDep3): # temp_dep[2] is possible
    return result
  • create_simple_form_dependency
  • this is developed to avoid the tedious task of repeatedly writing out the Form.
  • especially when you make api with file upload, you can use this to make it simple
from fastapi_mctools.dependencies import create_simple_form_dependency

simple_form_params ={
    "name": str,
    "age": int,
    "address": str,
    "email": str,
    "status": str,
    "memo": str,
}
form_data = create_simple_form_dependency(simple_form_params)

@app.post("/user")
def create_user(form_data: FastAPIForm = Depends(), file: UploadFile = File()):
    return form_data

Requests

  • APIClient
  • this is a class that can be used to make requests to external APIs only asynchronously
  • you can keep the session alive and reuse it
  • I normally use this in the lifespan of the app
from fastapi_mctools.utils.requests import APIClient

@asynccontextmanager
async def lifespan(app: FastAPI):
    try:
        api_client = APIClient()
        await api_client.start()
        states["api_client"] = api_client
        logger.info(f"Start App: states {states}, Debug: {app_settings.DEBUG}")
        yield
        await api_client.close()
    except Exception as e:
        raise e

Responses

  • ResponseInterFace
  • this is an interface that can unify the response format ex) {"results" : data, "message": message, "status": status}
  • it's not compatible with response_model in router.
from fastapi_mctools.utils.responses import ResponseInterFace


async def get_user(user_id: int) -> ResponseInterFace:
    user = await user_repository.get(user_id)
    if user:
        return ResponseInterFace(result=user, message="Success", temp_response_1="temp1", temp_response_2="temp2", status=200)
    return ResponseInterFace(result=None, message="Not Found", temp_response_1="temp1", temp_response_2="temp2", status=404)


from pydantic import BaseModel

class UserResponse(BaseModel)
    # Let's assume that the model is defined
    ...

@router.get("/user/{user_id}")
async def get_user(user_id: int):
    ...
    response = ResponseInterFace(result=user, message="Success", temp_response_1="temp1", temp_response_2="temp2", status=200)
    return UserResponse(**response)

time

  • time_checker
  • this is a decorator that can be used to check orm query time
from fastapi_mctools.utils.time import time_checker

@time_checker(debug=True, logger=logger)
class SomeRepository:
    def get(self, db: AsyncSession, id: int) -> SomeModel:
        ...

temp = SomeRepository()
temp.get(db, 1)  # SomeRepository.get took 0.01 ms

exceptions

  • HTTPException, handle_http_exception, handle_500_exception
  • HTTPException is a class that can be used instead of FastAPI's HTTPException, you can add more information to the exception response
  • handle_http_exception is exception handler that can be used to handle HTTPException, if you want to add more information with HTTPException, add this handler to app
  • handle_500_exception is exception handler that can be used to handle 500 error, it converts Exception to HTTPException and returns it
from fastapi_mctools.exceptions import HTTPException, handle_http_exception, handle_500_exception

async def temp_api():
    raise HTTPException(status_code=400, detail="Bad Request", more_info="more info", code="TEMP_ERROR")

app.add_exception_handler(HTTPException, handle_http_exception)

Lifespan

  • Lifespan is the class that can be used to manage the startup and shutdown of the app
  • Just make lifespan simple by using class
from fastapi_mctools.lifespan import Lifespan
from fastapi import FastAPI

async def async_hello(name):
    print(f"Hello async world, {name}")

def get_states():
    return {
        "state_1": "state_1",
        "state_2": "state_2"
    }

lifespan = Lifespan()
lifespan.add_startup(print, "Hello world")
lifespan.add_startup(async_hello, name="mingke")
lifespan.add_shutdown(print, "Goodbye world")
lifespan.states = get_states

app = FastAPI(lifespan = lifespan.lifespan)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_mctools-0.6.0.tar.gz (20.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_mctools-0.6.0-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_mctools-0.6.0.tar.gz.

File metadata

  • Download URL: fastapi_mctools-0.6.0.tar.gz
  • Upload date:
  • Size: 20.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.5

File hashes

Hashes for fastapi_mctools-0.6.0.tar.gz
Algorithm Hash digest
SHA256 12d1d8979c7a4927c6adb97f29370e1159e0430495b35331e9b9de8fc0e433fc
MD5 3fed96c7cbd99d7776cb6ba13938adf4
BLAKE2b-256 2062e6c6a3a56e07bcf2823798d8caa48abc11d33df9b8e5d40278f724d074df

See more details on using hashes here.

File details

Details for the file fastapi_mctools-0.6.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_mctools-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5cac8fc341b4c230cf48a6cf7f32de202d393910fedf35ad4ea09f68ff715461
MD5 d99a1a71c2e17e86f6acd4129784098a
BLAKE2b-256 6229b3e960c179b0f1cb7ffd1b10100d2b6217e4e6a7ea32a8f3da4dc89578d5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page