From 1098710d06574ce34ba6ea06108663ecf33641d0 Mon Sep 17 00:00:00 2001 From: Kharec Date: Tue, 23 Dec 2025 05:57:08 +0100 Subject: [PATCH] refactor: modernize typing annotations and docstrings --- flado/app.py | 5 ++--- flado/blueprints.py | 14 +++++++------- flado/errors.py | 9 ++++----- flado/models.py | 4 ++-- flado/services.py | 29 ++++++++++++++--------------- flado/validators.py | 33 ++++++++++++++++----------------- 6 files changed, 45 insertions(+), 49 deletions(-) diff --git a/flado/app.py b/flado/app.py index 67c62dd..7b41afd 100644 --- a/flado/app.py +++ b/flado/app.py @@ -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 diff --git a/flado/blueprints.py b/flado/blueprints.py index a759224..302316c 100644 --- a/flado/blueprints.py +++ b/flado/blueprints.py @@ -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/', 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/', 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/', 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() diff --git a/flado/errors.py b/flado/errors.py index 8666442..55f425f 100644 --- a/flado/errors.py +++ b/flado/errors.py @@ -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 diff --git a/flado/models.py b/flado/models.py index c4e64c7..b00e1c8 100644 --- a/flado/models.py +++ b/flado/models.py @@ -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, diff --git a/flado/services.py b/flado/services.py index f449a70..39ee67e 100644 --- a/flado/services.py +++ b/flado/services.py @@ -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() diff --git a/flado/validators.py b/flado/validators.py index be14428..b93a816 100644 --- a/flado/validators.py +++ b/flado/validators.py @@ -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"