refactor: modernize typing annotations and docstrings
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user