diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e18410..147d068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Next version +- add database class for easier sqlite3 access + ## 1.1.0 (2019-04-14) - change how config files are passed along diff --git a/yaboli/__init__.py b/yaboli/__init__.py index a9df9c2..b138c88 100644 --- a/yaboli/__init__.py +++ b/yaboli/__init__.py @@ -7,6 +7,7 @@ from .bot import * from .client import * from .command import * from .connection import * +from .database import * from .events import * from .exceptions import * from .message import * @@ -22,6 +23,7 @@ __all__ += bot.__all__ __all__ += client.__all__ __all__ += command.__all__ __all__ += connection.__all__ +__all__ += database.__all__ __all__ += events.__all__ __all__ += exceptions.__all__ __all__ += message.__all__ diff --git a/yaboli/database.py b/yaboli/database.py new file mode 100644 index 0000000..84af548 --- /dev/null +++ b/yaboli/database.py @@ -0,0 +1,40 @@ +import asyncio +import logging +import sqlite3 +from typing import Any, Awaitable, Callable, TypeVar + +from .util import asyncify + +logger = logging.getLogger(__name__) + +__all__ = ["Database", "operation"] + +T = TypeVar('T') + +def operation(func: Callable[..., T]) -> Callable[..., Awaitable[T]]: + async def wrapper(self: Any, *args: Any, **kwargs: Any) -> T: + async with self as db: + while True: + try: + return await asyncify(func, self, db, *args, **kwargs) + except sqlite3.OperationalError as e: + logger.warn(f"Operational error encountered: {e}") + await asyncio.sleep(5) + return wrapper + +class Database: + def __init__(self, database: str) -> None: + self._connection = sqlite3.connect(database, check_same_thread=False) + self._lock = asyncio.Lock() + + self.initialize(self._connection) + + def initialize(self, db: Any) -> None: + pass + + async def __aenter__(self) -> Any: + await self._lock.__aenter__() + return self._connection + + async def __aexit__(self, *args: Any, **kwargs: Any) -> Any: + return await self._lock.__aexit__(*args, **kwargs) diff --git a/yaboli/util.py b/yaboli/util.py index 6439799..e8395d9 100644 --- a/yaboli/util.py +++ b/yaboli/util.py @@ -1,8 +1,16 @@ +import asyncio import datetime +import functools import re +from typing import Any, Callable -__all__ = ["mention", "atmention", "normalize", "similar", "plural", - "format_time", "format_delta"] +__all__ = ["asyncify", "mention", "atmention", "normalize", "similar", + "plural", "format_time", "format_delta"] + +async def asyncify(func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any: + func_with_args = functools.partial(func, *args, **kwargs) + loop = asyncio.get_running_loop() + return await loop.run_in_executor(None, func_with_args) # Name/nick related functions