Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔧 Enhance the Project Dependencies to be fully migrated to UV #711

Merged
merged 10 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
uv-venv: ".venv"

- name: Install Dependencies
run: uv sync
run: uv sync --group lint --all-extras

- name: Typecheck with mypy
run: bash scripts/mypy.sh
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
uv-venv: ".venv"

- name: Install Dependencies
run: uv sync
run: uv sync --group test --all-extras

- name: Freeze Dependencies
run: uv pip freeze
Expand All @@ -75,7 +75,7 @@ jobs:
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
files: ./coverage.xml

tests-pydantic-v1:

Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
uv-venv: ".venv"

- name: Install Dependencies
run: uv sync
run: uv sync --group test --all-extras

- name: Install pydantic v1
run: uv pip install pydantic==1.10.17
Expand All @@ -123,7 +123,7 @@ jobs:
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
files: ./coverage.xml

test-extra:

Expand Down
27 changes: 12 additions & 15 deletions authx/_internal/_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@


class _CallbackHandler(Generic[T]):
"""
Base class for callback handlers in AuthX.
"""Base class for callback handlers in AuthX.

Args:
Generic (T): Model type
Expand Down Expand Up @@ -41,56 +40,54 @@ def __init__(self, model: Optional[T] = None) -> None:

@property
def is_model_callback_set(self) -> bool:
"""Check if callback is set for model instance"""
"""Check if callback is set for model instance."""
return self.callback_get_model_instance is not None

@property
def is_token_callback_set(self) -> bool:
"""Check if callback is set for token"""
"""Check if callback is set for token."""
return self.callback_is_token_in_blocklist is not None

def _check_model_callback_is_set(self, ignore_errors: bool = False) -> bool:
"""Check if callback is set for model instance and raise exception if not set"""
"""Check if callback is set for model instance and raise exception if not set."""
if self.is_model_callback_set:
return True
if not ignore_errors:
raise self._callback_model_set_exception
return False

def _check_token_callback_is_set(self, ignore_errors: bool = False) -> bool:
"""Check if callback is set for token and raise exception if not set"""
"""Check if callback is set for token and raise exception if not set."""
if self.is_token_callback_set:
return True
if not ignore_errors:
raise self._callback_token_set_exception
return False

def set_callback_get_model_instance(self, callback: ModelCallback[T]) -> None:
"""Set callback for model instance"""
"""Set callback for model instance."""
self.callback_get_model_instance = callback

def set_callback_token_blocklist(self, callback: TokenCallback) -> None:
"""Set callback for token"""
"""Set callback for token."""
self.callback_is_token_in_blocklist = callback

def set_subject_getter(self, callback: ModelCallback[T]) -> None:
"""Set the callback to run for subject retrieval and serialization"""
"""Set the callback to run for subject retrieval and serialization."""
self.set_callback_get_model_instance(callback)

def set_token_blocklist(self, callback: TokenCallback) -> None:
"""Set the callback to run for validation of revoked tokens"""
"""Set the callback to run for validation of revoked tokens."""
self.set_callback_token_blocklist(callback)

def _get_current_subject(self, uid: str, **kwargs: ParamSpecKwargs) -> Optional[T]:
"""Get current model instance from callback"""
"""Get current model instance from callback."""
self._check_model_callback_is_set()
callback: Optional[ModelCallback[T]] = self.callback_get_model_instance
return callback(uid, **kwargs) if callback is not None else None # type: ignore

def is_token_in_blocklist(
self, token: Optional[str], **kwargs: ParamSpecKwargs
) -> bool:
"""Check if token is in blocklist"""
def is_token_in_blocklist(self, token: Optional[str], **kwargs: ParamSpecKwargs) -> bool:
"""Check if token is in blocklist."""
if self._check_token_callback_is_set(ignore_errors=True):
callback: Optional[TokenCallback] = self.callback_is_token_in_blocklist
if callback is not None and token is not None:
Expand Down
18 changes: 7 additions & 11 deletions authx/_internal/_error.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Type
from typing import Optional

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
Expand All @@ -7,7 +7,7 @@


class _ErrorHandler:
"""Base Handler for FastAPI handling AuthX exceptions"""
"""Base Handler for FastAPI handling AuthX exceptions."""

MSG_TokenError = "Token Error"
MSG_MissingTokenError = "Missing JWT in request"
Expand All @@ -28,7 +28,7 @@ async def _error_handler(
status_code: int,
message: Optional[str],
) -> JSONResponse:
"""Generate the async function to be decorated by `FastAPI.exception_handler` decorator
"""Generate the async function to be decorated by `FastAPI.exception_handler` decorator.

Args:
request (Request): The request object.
Expand All @@ -55,13 +55,11 @@ async def _error_handler(
def _set_app_exception_handler(
self,
app: FastAPI,
exception: Type[exceptions.AuthXException],
exception: type[exceptions.AuthXException],
status_code: int,
message: Optional[str],
) -> None:
async def exception_handler_wrapper(
request: Request, exc: exceptions.AuthXException
) -> JSONResponse:
async def exception_handler_wrapper(request: Request, exc: exceptions.AuthXException) -> JSONResponse:
return await self._error_handler(request, exc, status_code, message)

# Add the exception handler to the FastAPI application
Expand All @@ -70,14 +68,12 @@ async def exception_handler_wrapper(
app.exception_handler(exception)(exception_handler_wrapper)

def handle_errors(self, app: FastAPI) -> None:
"""Add the `FastAPI.exception_handlers` relative to AuthX exceptions
"""Add the `FastAPI.exception_handlers` relative to AuthX exceptions.

Args:
app (FastAPI): the FastAPI application to handle errors for
"""
self._set_app_exception_handler(
app, exception=exceptions.JWTDecodeError, status_code=422, message=None
)
self._set_app_exception_handler(app, exception=exceptions.JWTDecodeError, status_code=422, message=None)
self._set_app_exception_handler(
app,
exception=exceptions.MissingTokenError,
Expand Down
8 changes: 2 additions & 6 deletions authx/_internal/_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ def set_log_level(level: str) -> logging.Logger:
return log


def log_debug(
msg: str, loc: Optional[str] = None, method: Optional[str] = None
) -> None:
def log_debug(msg: str, loc: Optional[str] = None, method: Optional[str] = None) -> None:
log.debug(msg=_build_log_msg(msg=msg, loc=loc, method=method))


Expand All @@ -35,9 +33,7 @@ def log_error(
log.error(f"{traceback.format_exc()}")


def _build_log_msg(
msg: str, loc: Optional[str] = None, method: Optional[str] = None
) -> str:
def _build_log_msg(msg: str, loc: Optional[str] = None, method: Optional[str] = None) -> str:
log_str = f"{msg}"
if loc:
log_str = f"[{loc}] {log_str}"
Expand Down
11 changes: 5 additions & 6 deletions authx/_internal/_memory.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import time
from typing import Any, Dict, Optional
from typing import Any, Optional


class MemoryIO:
raw_memory_store: Dict[str, Dict[str, Any]]
raw_memory_store: dict[str, dict[str, Any]]

"""
MemoryIO is a class that implements the IO interface for the session store.
Expand All @@ -12,8 +12,7 @@ class MemoryIO:
"""

def __init__(self) -> None:
"""
Initialize an instance of MemoryIO.
"""Initialize an instance of MemoryIO.

Creates a dictionary to store the session data.
"""
Expand All @@ -25,15 +24,15 @@ async def has_session_id(self, session_id: str) -> bool:
async def has_no_session_id(self, session_id: str) -> bool:
return session_id not in self.raw_memory_store

async def create_store(self, session_id: str) -> Dict[str, Any]:
async def create_store(self, session_id: str) -> dict[str, Any]:
self.raw_memory_store[session_id] = {
"created_at": int(time.time()),
"store": {},
}
await self.save_store(session_id)
return self.raw_memory_store.get(session_id, {}).get("store", {})

async def get_store(self, session_id: str) -> Optional[Dict[str, Any]]:
async def get_store(self, session_id: str) -> Optional[dict[str, Any]]:
if self.raw_memory_store.get(session_id):
return self.raw_memory_store.get(session_id, {}).get("store")
else:
Expand Down
14 changes: 5 additions & 9 deletions authx/_internal/_signature.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
from typing import Any, Dict, Optional, Tuple
from typing import Any, Optional

from itsdangerous import BadTimeSignature, SignatureExpired, URLSafeTimedSerializer

CASUAL_UT = False


class SignatureSerializer:
"""
A class that implements a URL-safe timed serializer.
"""
"""A class that implements a URL-safe timed serializer."""

def __init__(self, secret_key: str, expired_in: int = 0) -> None:
"""
Initialize the serializer with a secret key and an optional expiration time.
"""
"""Initialize the serializer with a secret key and an optional expiration time."""
self.ser = URLSafeTimedSerializer(secret_key)
self.expired_in = expired_in

def encode(self, dict_obj: Dict[str, Any]) -> str:
def encode(self, dict_obj: dict[str, Any]) -> str:
return self.ser.dumps(dict_obj)

def decode(self, token: str) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
def decode(self, token: str) -> tuple[Optional[dict[str, Any]], Optional[str]]:
if token is None:
return None, "NoTokenSpecified"
try:
Expand Down
28 changes: 7 additions & 21 deletions authx/_internal/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ def to_UTC(event_timestamp: Union[datetime, str], tz: BaseTzInfo = utc) -> datet
return dt.astimezone(tz)


def to_UTC_without_tz(
event_timestamp: str, format: str = "%Y-%m-%d %H:%M:%S.%f"
) -> str:
def to_UTC_without_tz(event_timestamp: str, format: str = "%Y-%m-%d %H:%M:%S.%f") -> str:
dt = datetime.strptime(event_timestamp, format)
dt = dt.replace(tzinfo=tz.utc)
return dt.astimezone(tz.utc).strftime(format)
Expand All @@ -69,27 +67,19 @@ def end_of_day(dt: datetime) -> datetime:
return dt


def minutes_ago(
dt: datetime, days: int = 0, hours: int = 0, minutes: int = 1, seconds: int = 0
) -> datetime:
def minutes_ago(dt: datetime, days: int = 0, hours: int = 0, minutes: int = 1, seconds: int = 0) -> datetime:
return dt - timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)


def minutes_after(
dt: datetime, days: int = 0, hours: int = 0, minutes: int = 1, seconds: int = 0
) -> datetime:
def minutes_after(dt: datetime, days: int = 0, hours: int = 0, minutes: int = 1, seconds: int = 0) -> datetime:
return dt + timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)


def hours_ago(
dt: datetime, days: int = 0, hours: int = 1, minutes: int = 0, seconds: int = 0
) -> datetime:
def hours_ago(dt: datetime, days: int = 0, hours: int = 1, minutes: int = 0, seconds: int = 0) -> datetime:
return dt - timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)


def days_ago(
dt: datetime, days: int = 1, hours: int = 0, minutes: int = 0, seconds: int = 0
) -> datetime:
def days_ago(dt: datetime, days: int = 1, hours: int = 0, minutes: int = 0, seconds: int = 0) -> datetime:
past = dt - timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
if dt.tzinfo:
past = past.replace(tzinfo=dt.tzinfo)
Expand All @@ -111,9 +101,7 @@ def years_ago(dt: datetime, years: int = 1) -> datetime:
return past


def days_after(
dt: datetime, days: int = 1, hours: int = 0, minutes: int = 0, seconds: int = 0
) -> datetime:
def days_after(dt: datetime, days: int = 1, hours: int = 0, minutes: int = 0, seconds: int = 0) -> datetime:
future = dt + timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
if dt.tzinfo:
future = future.replace(tzinfo=dt.tzinfo)
Expand Down Expand Up @@ -141,9 +129,7 @@ def tz_now(tz: BaseTzInfo = utc) -> datetime:
return dt.replace(tzinfo=tz)


def tz_from_iso(
dt: str, to_tz: BaseTzInfo = utc, format: str = "%Y-%m-%dT%H:%M:%S.%f%z"
) -> datetime:
def tz_from_iso(dt: str, to_tz: BaseTzInfo = utc, format: str = "%Y-%m-%dT%H:%M:%S.%f%z") -> datetime:
date_time = datetime.strptime(dt, format)
return date_time.astimezone(to_tz)

Expand Down
Loading
Loading