Add type hints to tutorial and Javascript examples

- Add return type annotations and parameter
  types
 - Import necessary typing modules (Optional,
 Union, Flask, Response)
 - Improve code readability and IDE support for
   examples
 - All existing tests continue to pass
This commit is contained in:
sebacry3 2025-09-26 11:08:04 -05:00
parent adf363679d
commit 57df4824ea
4 changed files with 26 additions and 17 deletions

View file

@ -1,18 +1,19 @@
from flask import jsonify from flask import jsonify
from flask import render_template from flask import render_template
from flask import request from flask import request
from flask import Response
from . import app from . import app
@app.route("/", defaults={"js": "fetch"}) @app.route("/", defaults={"js": "fetch"})
@app.route("/<any(xhr, jquery, fetch):js>") @app.route("/<any(xhr, jquery, fetch):js>")
def index(js): def index(js: str) -> str:
return render_template(f"{js}.html", js=js) return render_template(f"{js}.html", js=js)
@app.route("/add", methods=["POST"]) @app.route("/add", methods=["POST"])
def add(): def add() -> Response:
a = request.form.get("a", 0, type=float) a = request.form.get("a", 0, type=float)
b = request.form.get("b", 0, type=float) b = request.form.get("b", 0, type=float)
return jsonify(result=a + b) return jsonify(result=a + b)

View file

@ -1,4 +1,6 @@
import functools import functools
from typing import Callable, Union
from werkzeug.wrappers import Response
from flask import Blueprint from flask import Blueprint
from flask import flash from flask import flash
@ -16,7 +18,7 @@ from .db import get_db
bp = Blueprint("auth", __name__, url_prefix="/auth") bp = Blueprint("auth", __name__, url_prefix="/auth")
def login_required(view): def login_required(view: Callable) -> Callable:
"""View decorator that redirects anonymous users to the login page.""" """View decorator that redirects anonymous users to the login page."""
@functools.wraps(view) @functools.wraps(view)
@ -30,7 +32,7 @@ def login_required(view):
@bp.before_app_request @bp.before_app_request
def load_logged_in_user(): def load_logged_in_user() -> None:
"""If a user id is stored in the session, load the user object from """If a user id is stored in the session, load the user object from
the database into ``g.user``.""" the database into ``g.user``."""
user_id = session.get("user_id") user_id = session.get("user_id")
@ -44,7 +46,7 @@ def load_logged_in_user():
@bp.route("/register", methods=("GET", "POST")) @bp.route("/register", methods=("GET", "POST"))
def register(): def register() -> Union[str, Response]:
"""Register a new user. """Register a new user.
Validates that the username is not already taken. Hashes the Validates that the username is not already taken. Hashes the
@ -82,7 +84,7 @@ def register():
@bp.route("/login", methods=("GET", "POST")) @bp.route("/login", methods=("GET", "POST"))
def login(): def login() -> Union[str, Response]:
"""Log in a registered user by adding the user id to the session.""" """Log in a registered user by adding the user id to the session."""
if request.method == "POST": if request.method == "POST":
username = request.form["username"] username = request.form["username"]
@ -110,7 +112,7 @@ def login():
@bp.route("/logout") @bp.route("/logout")
def logout(): def logout() -> Union[str, Response]:
"""Clear the current session, including the stored user id.""" """Clear the current session, including the stored user id."""
session.clear() session.clear()
return redirect(url_for("index")) return redirect(url_for("index"))

View file

@ -1,3 +1,7 @@
import sqlite3
from typing import Optional, Union
from werkzeug.wrappers import Response
from flask import Blueprint from flask import Blueprint
from flask import flash from flask import flash
from flask import g from flask import g
@ -14,7 +18,7 @@ bp = Blueprint("blog", __name__)
@bp.route("/") @bp.route("/")
def index(): def index() -> str:
"""Show all the posts, most recent first.""" """Show all the posts, most recent first."""
db = get_db() db = get_db()
posts = db.execute( posts = db.execute(
@ -25,7 +29,7 @@ def index():
return render_template("blog/index.html", posts=posts) return render_template("blog/index.html", posts=posts)
def get_post(id, check_author=True): def get_post(id: int, check_author: bool = True) -> Optional[sqlite3.Row]:
"""Get a post and its author by id. """Get a post and its author by id.
Checks that the id exists and optionally that the current user is Checks that the id exists and optionally that the current user is
@ -59,7 +63,7 @@ def get_post(id, check_author=True):
@bp.route("/create", methods=("GET", "POST")) @bp.route("/create", methods=("GET", "POST"))
@login_required @login_required
def create(): def create() -> Union[str, Response]:
"""Create a new post for the current user.""" """Create a new post for the current user."""
if request.method == "POST": if request.method == "POST":
title = request.form["title"] title = request.form["title"]
@ -85,7 +89,7 @@ def create():
@bp.route("/<int:id>/update", methods=("GET", "POST")) @bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required @login_required
def update(id): def update(id: int) -> Union[str, Response]:
"""Update a post if the current user is the author.""" """Update a post if the current user is the author."""
post = get_post(id) post = get_post(id)
@ -112,7 +116,7 @@ def update(id):
@bp.route("/<int:id>/delete", methods=("POST",)) @bp.route("/<int:id>/delete", methods=("POST",))
@login_required @login_required
def delete(id): def delete(id: int) -> Union[str, Response]:
"""Delete a post. """Delete a post.
Ensures that the post exists and that the logged in user is the Ensures that the post exists and that the logged in user is the

View file

@ -5,8 +5,10 @@ import click
from flask import current_app from flask import current_app
from flask import g from flask import g
from flask import Flask
def get_db():
def get_db() -> sqlite3.Connection:
"""Connect to the application's configured database. The connection """Connect to the application's configured database. The connection
is unique for each request and will be reused if this is called is unique for each request and will be reused if this is called
again. again.
@ -20,7 +22,7 @@ def get_db():
return g.db return g.db
def close_db(e=None): def close_db(e=None) -> None:
"""If this request connected to the database, close the """If this request connected to the database, close the
connection. connection.
""" """
@ -30,7 +32,7 @@ def close_db(e=None):
db.close() db.close()
def init_db(): def init_db() -> None:
"""Clear existing data and create new tables.""" """Clear existing data and create new tables."""
db = get_db() db = get_db()
@ -39,7 +41,7 @@ def init_db():
@click.command("init-db") @click.command("init-db")
def init_db_command(): def init_db_command() -> None:
"""Clear existing data and create new tables.""" """Clear existing data and create new tables."""
init_db() init_db()
click.echo("Initialized the database.") click.echo("Initialized the database.")
@ -48,7 +50,7 @@ def init_db_command():
sqlite3.register_converter("timestamp", lambda v: datetime.fromisoformat(v.decode())) sqlite3.register_converter("timestamp", lambda v: datetime.fromisoformat(v.decode()))
def init_app(app): def init_app(app: Flask) -> None:
"""Register database functions with the Flask app. This is called by """Register database functions with the Flask app. This is called by
the application factory. the application factory.
""" """