Source code for fastapi_contrib.common.utils
import importlib
import sys
import pytz
from datetime import datetime
from functools import wraps
from time import time
from typing import Any
from fastapi import FastAPI
from fastapi_contrib.conf import settings
[docs]def resolve_dotted_path(path: str) -> Any:
"""
Retrieves attribute (var, function, class, etc.) from module by dotted path
.. code-block:: python
from datetime.datetime import utcnow as default_utcnow
utcnow = resolve_dotted_path('datetime.datetime.utcnow')
assert utcnow == default_utcnow
:param path: dotted path to the attribute in module
:return: desired attribute or None
"""
splitted = path.split(".")
if len(splitted) <= 1:
return importlib.import_module(path)
module, attr = ".".join(splitted[:-1]), splitted[-1]
module = importlib.import_module(module)
return getattr(module, attr)
[docs]def get_logger() -> Any:
"""
Gets logger that will be used throughout this whole library.
First it finds and imports the logger, then if it can be configured
using loguru-compatible config, it does so.
:return: desired logger (pre-configured if loguru)
"""
lib_logger = resolve_dotted_path(settings.logger)
# Check whether it is loguru-compatible logger
if hasattr(lib_logger, "configure"):
logger_config = {
"handlers": [
{
"sink": sys.stdout,
"level": settings.log_level,
"format": "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green>"
" | <level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:"
"<cyan>{line}</cyan> -"
" <level>{message}</level>",
}
]
}
lib_logger.configure(**logger_config)
return lib_logger
logger = get_logger()
[docs]def get_current_app() -> FastAPI:
"""
Retrieves FastAPI app instance from the path, specified in project's conf.
:return: FastAPI app
"""
# TODO: cache this
app = resolve_dotted_path(settings.fastapi_app)
return app
[docs]def async_timing(func):
"""
Decorator for logging timing of async functions.
Used in this library internally for tracking DB functions performance.
:param func: function to be decorated
:return: wrapped function
"""
@wraps(func)
async def wrap(*args, **kwargs):
time1 = time()
raised_exception = True
try:
ret = await func(*args, **kwargs)
raised_exception = False
if not settings.debug_timing:
return ret
finally:
time2 = time()
duration = (time2 - time1) * 1000.0
logger.debug(
"\t [TIMING] {:s} {:s} {:.3f} ms".format(
func.__module__.ljust(20),
func.__name__.ljust(20),
duration,
)
)
if not raised_exception:
return ret
return wrap
[docs]def get_now() -> datetime:
"""
Retrieves `now` function from the path, specified in project's conf.
:return: datetime of "now"
"""
# TODO: cache this
if settings.now_function:
return resolve_dotted_path(settings.now_function)()
return datetime.now(tz=get_timezone())
[docs]def get_timezone():
"""
Retrieves timezone name from settings and tries to create tzinfo from it.
:return: tzinfo object
"""
return pytz.timezone(settings.TZ)