Skip to content

Commit

Permalink
fix: pylint issues and improve code quality
Browse files Browse the repository at this point in the history
  • Loading branch information
erfjab committed Nov 5, 2024
1 parent 90eeea4 commit ad165ab
Show file tree
Hide file tree
Showing 36 changed files with 748 additions and 352 deletions.
4 changes: 3 additions & 1 deletion db/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .base import Base, GetDB
"""Database module initialization."""

from .base import Base, get_db
from .crud import TokenManager
from .models import Token
75 changes: 38 additions & 37 deletions db/alembic/env.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# pylint: disable=all

"""
Alembic environment configuration for running database migrations.
This module configures and runs Alembic migrations for the database, supporting both
synchronous (offline) and asynchronous (online) migration modes.
"""

import asyncio
from logging.config import fileConfig

Expand All @@ -8,66 +17,56 @@

from db.base import Base

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Alembic Config object for accessing values within the .ini file.
config = context.config # pylint: disable=no-member
config.set_main_option("sqlalchemy.url", "sqlite+aiosqlite:///data/db.sqlite3")
# Interpret the config file for Python logging.
# This line sets up loggers basically.

# Set up loggers from config file if available
if config.config_file_name is not None:
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
# Metadata object for 'autogenerate' support in migrations
target_metadata = Base.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
Run migrations in 'offline' mode.
Configures the context with a URL instead of an Engine,
allowing migrations without DBAPI.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
context.configure( # pylint: disable=no-member
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)

with context.begin_transaction():
context.run_migrations()
with context.begin_transaction(): # pylint: disable=no-member
context.run_migrations() # pylint: disable=no-member


def do_run_migrations(connection: Connection) -> None:
context.configure(connection=connection, target_metadata=target_metadata)
"""
Configures the context for a migration and executes the migrations.
"""
context.configure(
connection=connection, target_metadata=target_metadata # pylint: disable=no-member
)

with context.begin_transaction():
context.run_migrations()
with context.begin_transaction(): # pylint: disable=no-member
context.run_migrations() # pylint: disable=no-member


async def run_async_migrations() -> None:
"""In this scenario we need to create an Engine
and associate a connection with the context.
"""

Creates an asynchronous Engine and associates a connection
with the Alembic migration context.
"""
connectable = async_engine_from_config(
config.get_section(config.config_ini_section, {}),
config.get_section(config.config_ini_section) or {},
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
Expand All @@ -79,12 +78,14 @@ async def run_async_migrations() -> None:


def run_migrations_online() -> None:
"""Run migrations in 'online' mode."""

"""
Run migrations in 'online' mode using asynchronous connections.
"""
asyncio.run(run_async_migrations())


if context.is_offline_mode():
# Run migrations based on the mode (offline or online)
if context.is_offline_mode(): # pylint: disable=no-member
run_migrations_offline()
else:
run_migrations_online()
16 changes: 6 additions & 10 deletions db/alembic/versions/3e5deef43bf0_init_commit.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
# pylint: skip-file
"""init commit
Revision ID: 3e5deef43bf0
Revises:
Create Date: 2024-10-11 15:47:37.464534
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
# Revision identifiers, used by Alembic.
revision: str = "3e5deef43bf0"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"""Create the tokens table."""
op.create_table( # pylint: disable=no-member
"tokens",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("token", sa.String(length=255), nullable=False),
Expand All @@ -30,10 +28,8 @@ def upgrade() -> None:
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("token"),
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("tokens")
# ### end Alembic commands ###
"""Drop the tokens table."""
op.drop_table("tokens") # pylint: disable=no-member
19 changes: 8 additions & 11 deletions db/alembic/versions/ab1ce3ef2a57_add_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,35 @@
Revision ID: ab1ce3ef2a57
Revises: 3e5deef43bf0
Create Date: 2024-10-13 01:42:55.733416
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
# Revision identifiers, used by Alembic.
revision: str = "ab1ce3ef2a57"
down_revision: Union[str, None] = "3e5deef43bf0"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade():
# Create settings table
op.create_table(
def upgrade() -> None:
"""Create the settings table."""
op.create_table( # pylint: disable=no-member
"settings",
sa.Column("key", sa.String(256), primary_key=True),
sa.Column("value", sa.String(2048), nullable=True),
sa.Column(
"created_at",
sa.DateTime(),
server_default=sa.func.current_timestamp(),
server_default=sa.func.current_timestamp(), # pylint: disable=not-callable
nullable=False,
),
sa.Column("updated_at", sa.DateTime(), nullable=True),
)


def downgrade():
# Drop settings table
op.drop_table("settings")
def downgrade() -> None:
"""Drop the settings table."""
op.drop_table("settings") # pylint: disable=no-member
21 changes: 17 additions & 4 deletions db/base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
"""
Database base module.
This module provides the asynchronous engine, session factory, and base class for
declarative models.
"""

from contextlib import asynccontextmanager
from typing import AsyncGenerator

from sqlalchemy.ext.asyncio import AsyncAttrs, AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeBase, sessionmaker


# Create an asynchronous engine
engine = create_async_engine(
"sqlite+aiosqlite:///data/db.sqlite3",
Expand All @@ -21,13 +27,20 @@
)


# Define a base class for declarative models
class Base(DeclarativeBase, AsyncAttrs):
pass
"""Base class for declarative models using SQLAlchemy."""

def save(self, session: AsyncSession) -> None:
"""Save the current instance to the database."""
session.add(self)

def delete(self, session: AsyncSession) -> None:
"""Delete the current instance from the database."""
session.delete(self)


@asynccontextmanager
async def GetDB() -> AsyncGenerator[AsyncSession, None]:
async def get_db() -> AsyncGenerator[AsyncSession, None]:
"""
Provide an asynchronous database session to the application.
"""
Expand Down
6 changes: 6 additions & 0 deletions db/crud/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
"""
This module initializes the CRUD operations for the application.
It includes managers for handling tokens and settings.
"""

from .token import TokenManager
from .setting import SettingManager
48 changes: 38 additions & 10 deletions db/crud/setting.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
"""
This module provides functionality for managing settings in the application.
It includes methods for upserting and retrieving settings from the database.
"""

from sqlalchemy.future import select
from db.base import GetDB
from db.base import get_db
from db.models import Setting
from models import SettingData, SettingUpsert, SettingKeys


class SettingManager:
"""Manager class for handling settings operations."""

@staticmethod
async def upsert(setting_upsert: SettingUpsert) -> SettingData | None:
async with GetDB() as db:
"""
Upsert a setting in the database.
If the setting exists and the value is None, it will be deleted.
If the setting does not exist, it will be created.
Args:
setting_upsert (SettingUpsert): The setting data to upsert.
Returns:
SettingData | None: The upserted setting data or None if deleted.
"""
async with get_db() as db:
existing_setting = await db.execute(
select(Setting).where(Setting.key == setting_upsert.key)
)
Expand All @@ -19,21 +38,30 @@ async def upsert(setting_upsert: SettingUpsert) -> SettingData | None:
await db.delete(setting)
await db.commit()
return None
else:
setting.value = setting_upsert.value
setting.value = setting_upsert.value
else:
if setting_upsert.value is None:
return None
setting = Setting(key=setting_upsert.key, value=setting_upsert.value)
db.add(setting)
if setting_upsert.value is not None:
setting = Setting(
key=setting_upsert.key, value=setting_upsert.value
)
db.add(setting)

await db.commit()
await db.refresh(setting)
return SettingData.from_orm(setting)

@staticmethod
async def get(key: SettingKeys) -> SettingData:
async with GetDB() as db:
async def get(key: SettingKeys) -> SettingData | None:
"""
Retrieve a setting by its key.
Args:
key (SettingKeys): The key of the setting to retrieve.
Returns:
SettingData | None: The retrieved setting data or None if not found.
"""
async with get_db() as db:
result = await db.execute(select(Setting).where(Setting.key == key))
setting = result.scalar_one_or_none()
return SettingData.from_orm(setting) if setting else None
Loading

0 comments on commit ad165ab

Please sign in to comment.