refactor: modernize typing annotations and docstrings

This commit is contained in:
2025-12-23 05:57:08 +01:00
parent 0fc099c4e4
commit 1098710d06
6 changed files with 45 additions and 49 deletions

View File

@@ -2,7 +2,6 @@
import logging
import os
from pathlib import Path
from typing import Optional
from dotenv import load_dotenv
from flask import Flask
@@ -26,12 +25,12 @@ def setup_logging(app: Flask) -> None:
app.logger.setLevel(logging.DEBUG)
def create_app(config_name: Optional[str] = None) -> Flask:
def create_app(config_name: str | None = None) -> Flask:
"""
Application factory pattern for Flask.
Args:
config_name: Optional configuration name (development, production, etc.)
config_name: str | None configuration name (development, production, etc.)
Returns:
Flask application instance

View File

@@ -1,7 +1,7 @@
"""Blueprint for task-related routes."""
import logging
from datetime import datetime, date, timezone
from typing import Dict, Any, Tuple
from typing import Any
from flask import Blueprint, render_template, request, jsonify, Response
from sqlalchemy import text
@@ -93,7 +93,7 @@ def index() -> str:
@tasks_blueprint.route('/api/tasks', methods=['POST'])
def api_create_task() -> Tuple[Response, int]:
def api_create_task() -> tuple[Response, int]:
"""API endpoint to create a new task."""
try:
data = request.get_json()
@@ -142,7 +142,7 @@ def api_create_task() -> Tuple[Response, int]:
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['GET'])
def api_get_task(task_id: int) -> Tuple[Response, int]:
def api_get_task(task_id: int) -> tuple[Response, int]:
"""API endpoint to get a single task."""
try:
task = Task.query.get_or_404(task_id)
@@ -153,14 +153,14 @@ def api_get_task(task_id: int) -> Tuple[Response, int]:
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['PUT', 'PATCH'])
def api_update_task(task_id: int) -> Tuple[Response, int]:
def api_update_task(task_id: int) -> tuple[Response, int]:
"""API endpoint to update a task."""
try:
data = request.get_json()
if not isinstance(data, dict):
return jsonify({'error': 'Invalid JSON payload'}), 400
update_data: Dict[str, Any] = {}
update_data: dict[str, Any] = {}
# Validate and update title
if 'title' in data:
@@ -217,7 +217,7 @@ def api_update_task(task_id: int) -> Tuple[Response, int]:
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['DELETE'])
def api_delete_task(task_id: int) -> Tuple[Response, int]:
def api_delete_task(task_id: int) -> tuple[Response, int]:
"""API endpoint to delete a task."""
try:
delete_task(task_id)
@@ -229,7 +229,7 @@ def api_delete_task(task_id: int) -> Tuple[Response, int]:
@tasks_blueprint.route('/api/tasks/reorder', methods=['POST'])
def api_reorder_tasks() -> Tuple[Response, int]:
def api_reorder_tasks() -> tuple[Response, int]:
"""API endpoint to reorder tasks."""
try:
data = request.get_json()

View File

@@ -1,5 +1,4 @@
"""Error handlers for Flask application."""
from typing import Tuple
from flask import Flask, jsonify, Response
from flask_wtf.csrf import CSRFError
from werkzeug.exceptions import HTTPException
@@ -9,23 +8,23 @@ def register_error_handlers(app: Flask) -> None:
"""Register all error handlers with the Flask app."""
@app.errorhandler(400)
def bad_request(error: HTTPException) -> Tuple[Response, int]:
def bad_request(error: HTTPException) -> tuple[Response, int]:
"""Handle 400 errors with JSON response."""
return jsonify({'error': 'Bad request', 'message': str(error)}), 400
@app.errorhandler(404)
def not_found(error: HTTPException) -> Tuple[Response, int]:
def not_found(error: HTTPException) -> tuple[Response, int]:
"""Handle 404 errors with JSON response."""
return jsonify({'error': 'Not found', 'message': str(error)}), 404
@app.errorhandler(CSRFError)
def handle_csrf_error(e: CSRFError) -> Tuple[Response, int]:
def handle_csrf_error(e: CSRFError) -> tuple[Response, int]:
"""Handle CSRF errors with JSON response."""
app.logger.warning(f'CSRF error: {e}')
return jsonify({'error': 'CSRF token missing or invalid'}), 400
@app.errorhandler(500)
def internal_error(error: Exception) -> Tuple[Response, int]:
def internal_error(error: Exception) -> tuple[Response, int]:
"""Handle 500 errors with JSON response."""
app.logger.error(f'Server error: {error}')
return jsonify({'error': 'Internal server error'}), 500

View File

@@ -1,6 +1,6 @@
"""SQLAlchemy models for Flado."""
from datetime import datetime, timezone
from typing import Dict, Any
from typing import Any
from flask_sqlalchemy import SQLAlchemy
@@ -29,7 +29,7 @@ class Task(db.Model):
tags = db.relationship('Tag', secondary='task_tags',
back_populates='tasks', lazy='select')
def to_dict(self) -> Dict[str, Any]:
def to_dict(self) -> dict[str, Any]:
"""Convert task to dictionary for JSON serialization."""
return {
'id': self.id,

View File

@@ -1,7 +1,6 @@
"""Business logic helpers for Flado."""
import logging
from datetime import datetime, date, timezone
from typing import Optional, List
from sqlalchemy import or_, func
from sqlalchemy.orm import Query
@@ -11,13 +10,13 @@ from .models import db, Task, Tag
logger = logging.getLogger(__name__)
def get_tasks(filter_type: str = 'all', search_query: Optional[str] = None) -> Query:
def get_tasks(filter_type: str = 'all', search_query: str | None = None) -> Query:
"""
Retrieve tasks based on filter type.
Args:
filter_type: 'all', 'today', 'upcoming', 'completed', 'active'
search_query: Optional search string to filter by title/description
search_query: str | None search string to filter by title/description
Returns:
Query object of tasks
@@ -49,18 +48,18 @@ def get_tasks(filter_type: str = 'all', search_query: Optional[str] = None) -> Q
def create_task(
title: str,
description: Optional[str] = None,
due_date: Optional[date] = None,
tag_names: Optional[List[str]] = None
description: str | None = None,
due_date: date | None = None,
tag_names: list[str] | None = None
) -> Task:
"""
Create a new task.
Args:
title: Task title (required, already validated)
description: Optional task description (already validated)
due_date: Optional due date (date object, already validated)
tag_names: Optional list of tag names to associate (already validated)
description: str | None task description (already validated)
due_date: date | None due date (date object, already validated)
tag_names: list[str] | None list of tag names to associate (already validated)
Returns:
Created Task object
@@ -162,12 +161,12 @@ def delete_task(task_id: int) -> bool:
raise
def reorder_tasks(task_ids: List[int]) -> bool:
def reorder_tasks(task_ids: list[int]) -> bool:
"""
Reorder tasks by updating their positions.
Args:
task_ids: List of task IDs in desired order (already validated)
task_ids: list[int] of task IDs in desired order (already validated)
Returns:
True if reordered successfully
@@ -209,15 +208,15 @@ def reorder_tasks(task_ids: List[int]) -> bool:
raise
def get_or_create_tags(tag_names: List[str]) -> List[Tag]:
def get_or_create_tags(tag_names: list[str]) -> list[Tag]:
"""
Get existing tags or create new ones.
Args:
tag_names: List of tag names (already validated)
tag_names: list[str] of tag names (already validated)
Returns:
List of Tag objects
list[Tag] of tag objects
Note:
Does not commit the session. Caller is responsible for committing.
@@ -235,6 +234,6 @@ def get_or_create_tags(tag_names: List[str]) -> List[Tag]:
return tags
def get_all_tags() -> List[Tag]:
def get_all_tags() -> list[Tag]:
"""Get all tags."""
return Tag.query.order_by(Tag.name.asc()).all()

View File

@@ -1,9 +1,8 @@
"""Input validation utilities for Flado."""
import re
from typing import Optional, List, Tuple
def validate_title(title: Optional[str]) -> Tuple[bool, Optional[str]]:
def validate_title(title: str | None) -> tuple[bool, str | None]:
"""
Validate task title.
@@ -11,7 +10,7 @@ def validate_title(title: Optional[str]) -> Tuple[bool, Optional[str]]:
title: Title to validate
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if not title:
return False, "Title is required"
@@ -26,7 +25,7 @@ def validate_title(title: Optional[str]) -> Tuple[bool, Optional[str]]:
return True, None
def validate_description(description: Optional[str]) -> Tuple[bool, Optional[str]]:
def validate_description(description: str | None) -> tuple[bool, str | None]:
"""
Validate task description.
@@ -34,7 +33,7 @@ def validate_description(description: Optional[str]) -> Tuple[bool, Optional[str
description: Description to validate
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if description is None:
return True, None
@@ -46,7 +45,7 @@ def validate_description(description: Optional[str]) -> Tuple[bool, Optional[str
return True, None
def validate_date_format(date_str: Optional[str]) -> Tuple[bool, Optional[str]]:
def validate_date_format(date_str: str | None) -> tuple[bool, str | None]:
"""
Validate date string format (YYYY-MM-DD).
@@ -54,7 +53,7 @@ def validate_date_format(date_str: Optional[str]) -> Tuple[bool, Optional[str]]:
date_str: Date string to validate
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if not date_str:
return True, None
@@ -70,7 +69,7 @@ def validate_date_format(date_str: Optional[str]) -> Tuple[bool, Optional[str]]:
return True, None
def validate_tag_name(tag_name: Optional[str]) -> Tuple[bool, Optional[str]]:
def validate_tag_name(tag_name: str | None) -> tuple[bool, str | None]:
"""
Validate tag name.
@@ -78,7 +77,7 @@ def validate_tag_name(tag_name: Optional[str]) -> Tuple[bool, Optional[str]]:
tag_name: Tag name to validate
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if not tag_name:
return False, "Tag name is required"
@@ -97,15 +96,15 @@ def validate_tag_name(tag_name: Optional[str]) -> Tuple[bool, Optional[str]]:
return True, None
def validate_tag_names(tag_names: Optional[List[str]]) -> Tuple[bool, Optional[str]]:
def validate_tag_names(tag_names: list[str] | None) -> tuple[bool, str | None]:
"""
Validate list of tag names.
Args:
tag_names: List of tag names to validate
tag_names: list[str] of tag names to validate
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if tag_names is None:
return True, None
@@ -121,7 +120,7 @@ def validate_tag_names(tag_names: Optional[List[str]]) -> Tuple[bool, Optional[s
return True, None
def validate_hex_color(color: Optional[str]) -> Tuple[bool, Optional[str]]:
def validate_hex_color(color: str | None) -> tuple[bool, str | None]:
"""
Validate hex color format.
@@ -129,7 +128,7 @@ def validate_hex_color(color: Optional[str]) -> Tuple[bool, Optional[str]]:
color: Hex color string (e.g., #RRGGBB)
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if not color:
return True, None
@@ -144,15 +143,15 @@ def validate_hex_color(color: Optional[str]) -> Tuple[bool, Optional[str]]:
return True, None
def validate_task_ids(task_ids: Optional[List[int]]) -> Tuple[bool, Optional[str]]:
def validate_task_ids(task_ids: list[int] | None) -> tuple[bool, str | None]:
"""
Validate list of task IDs.
Args:
task_ids: List of task IDs to validate
task_ids: list[int] of task IDs to validate
Returns:
Tuple of (is_valid, error_message)
tuple[bool, str | None]: (is_valid, error_message)
"""
if not task_ids:
return False, "task_ids is required"