Skip to content

Commit

Permalink
Add typehints
Browse files Browse the repository at this point in the history
  • Loading branch information
Siecje committed Jan 23, 2024
1 parent 9dc5066 commit b3e3cf7
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 83 deletions.
33 changes: 25 additions & 8 deletions htmd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import sys

import click
from flask import Flask
from flask_flatpages import FlatPages

from .utils import (
combine_and_minify_css,
Expand All @@ -16,7 +18,7 @@

@click.group()
@click.version_option()
def cli():
def cli() -> None:
pass # pragma: no cover


Expand All @@ -27,7 +29,7 @@ def cli():
default=False,
help='Include all templates.',
)
def start(all_templates):
def start(all_templates: bool) -> None: # noqa: FBT001
dir_templates = create_directory('templates/')
if all_templates:
copy_missing_templates()
Expand All @@ -49,7 +51,7 @@ def start(all_templates):


@cli.command('verify', short_help='Verify posts formatting is correct.')
def verify():
def verify() -> None:
# import is here to avoid looking for the config
# which doesn't exist until you run start
from . import site
Expand Down Expand Up @@ -91,7 +93,12 @@ def verify():
sys.exit(1)


def set_post_time(app, post, field, date_time):
def set_post_time(
app: Flask,
post: FlatPages,
field: str,
date_time: datetime.datetime,
) -> None:
file_path = (
Path(app.config['FLATPAGES_ROOT'])
/ (post.path + app.config['FLATPAGES_EXTENSION'])
Expand All @@ -113,7 +120,7 @@ def set_post_time(app, post, field, date_time):
file.write(line)


def set_posts_datetime(app, posts):
def set_posts_datetime(app: Flask, posts: [FlatPages]) -> None:
# Ensure each post has a published date
# set time for correct date field
for post in posts:
Expand Down Expand Up @@ -151,7 +158,11 @@ def set_posts_datetime(app, posts):
default=True,
help='If JavaScript should be minified',
)
def build(ctx, css_minify, js_minify):
def build(
ctx: click.Context,
css_minify: bool, # noqa: FBT001
js_minify: bool, # noqa: FBT001
) -> None:
ctx.invoke(verify)
# If verify fails sys.exit(1) will run

Expand Down Expand Up @@ -205,7 +216,13 @@ def build(ctx, css_minify, js_minify):
default=True,
help='If JavaScript should be minified',
)
def preview(_ctx, host, port, css_minify, js_minify):
def preview(
_ctx: click.Context,
host: str,
port: int,
css_minify: bool, # noqa: FBT001
js_minify: bool, # noqa: FBT001
) -> None:
from . import site
# reload for tests to refresh app.static_folder
# otherwise app.static_folder will be from another test
Expand All @@ -222,7 +239,7 @@ def preview(_ctx, host, port, css_minify, js_minify):


@cli.command('templates', short_help='Create any missing templates')
def templates():
def templates() -> None:
try:
copy_missing_templates()
except FileNotFoundError:
Expand Down
53 changes: 30 additions & 23 deletions htmd/site.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from collections.abc import Iterator
import os
from pathlib import Path
import sys
import tomllib
from typing import TypedDict

from bs4 import BeautifulSoup
from feedwerk.atom import AtomFeed
from flask import abort, Blueprint, Flask, render_template, url_for
from flask import abort, Blueprint, Flask, render_template, Response, url_for
from flask_flatpages import FlatPages, pygments_style_defs
from flask_frozen import Freezer
from htmlmin import minify
Expand All @@ -15,7 +17,7 @@
this_dir = Path(__file__).parent


def get_project_dir():
def get_project_dir() -> Path:
current_directory = Path.cwd()

while True:
Expand Down Expand Up @@ -103,7 +105,7 @@ def get_project_dir():
app.jinja_env.globals[key] = app.config[key]


def truncate_post_html(post_html):
def truncate_post_html(post_html: str) -> str:
return BeautifulSoup(post_html[:255], 'html.parser').prettify()


Expand Down Expand Up @@ -139,7 +141,7 @@ def truncate_post_html(post_html):


@app.after_request
def format_html(response):
def format_html(response: Response) -> Response:
if response.mimetype == 'text/html':
if app.config.get('PRETTY_HTML', False):
response.data = BeautifulSoup(
Expand All @@ -152,7 +154,7 @@ def format_html(response):


@pages.route('/<path:path>/')
def page(path):
def page(path: str) -> Response:
try:
return render_template(path + '.html', active=path)
except TemplateNotFound:
Expand All @@ -164,18 +166,18 @@ def page(path):

# Will end up in the static directory
@app.route('/static/pygments.css')
def pygments_css():
def pygments_css() -> Response:
return pygments_style_defs('tango'), 200, {'Content-Type': 'text/css'}


@app.route('/')
def index():
def index() -> Response:
latest = sorted(posts, reverse=True, key=lambda p: p.meta.get('published'))
return render_template('index.html', active='home', posts=latest[:4])


@app.route('/feed.atom')
def feed():
def feed() -> Response:
name = app.config.get('SITE_NAME')
subtitle = app.config.get('SITE_DESCRIPTION') or 'Recent Blog Posts'
url = app.config.get('URL')
Expand Down Expand Up @@ -208,14 +210,14 @@ def feed():


@app.route('/all/')
def all_posts():
def all_posts() -> Response:
latest = sorted(posts, reverse=True, key=lambda p: p.meta.get('published'))
return render_template('all_posts.html', active='posts', posts=latest)


# If month and day are ints then Flask removes leading zeros
@app.route('/<year>/<month>/<day>/<path:path>/')
def post(year, month, day, path):
def post(year: str, month: str, day:str, path: str) -> Response:
if len(year) != 4 or len(month) != 2 or len(day) != 2: # noqa: PLR2004
abort(404)
post = posts.get_or_404(path)
Expand All @@ -225,19 +227,24 @@ def post(year, month, day, path):
return render_template('post.html', post=post)


def tag_in_list(list_of_tags, tag):
class TagDict(TypedDict):
tag: str
count: int


def tag_in_list(list_of_tags: [TagDict], tag: str) -> bool:
return any(i['tag'] == tag for i in list_of_tags)


def increment_tag_count(list_of_tags, tag):
def increment_tag_count(list_of_tags: [TagDict], tag: str) -> [TagDict]:
for i in list_of_tags:
if i['tag'] == tag:
i['count'] += 1
return list_of_tags


@app.route('/tags/')
def all_tags():
def all_tags() -> Response:
tags = []
for post in posts:
for tag in post.meta.get('tags', []):
Expand All @@ -249,7 +256,7 @@ def all_tags():


@app.route('/tags/<string:tag>/')
def tag(tag):
def tag(tag: str) -> Response:
tagged = [p for p in posts if tag in p.meta.get('tags', [])]
sorted_posts = sorted(
tagged,
Expand All @@ -260,7 +267,7 @@ def tag(tag):


@app.route('/author/<author>/')
def author(author):
def author(author: str) -> Response:
author_posts = [p for p in posts if author == p.meta.get('author', '')]
sorted_posts = sorted(
author_posts,
Expand All @@ -276,12 +283,12 @@ def author(author):


@app.route('/404.html')
def not_found():
def not_found() -> Response:
return render_template('404.html')


@app.route('/<int:year>/')
def year_view(year):
def year_view(year: int) -> Response:
year = str(year)
if len(year) != len('YYYY'):
abort(404)
Expand All @@ -299,7 +306,7 @@ def year_view(year):


@app.route('/<year>/<month>/')
def month_view(year, month):
def month_view(year: str, month: str) -> Response:
month_posts = [
p for p in posts if year == p.meta.get('published').strftime('%Y')
and month == p.meta.get('published').strftime('%m')
Expand All @@ -321,7 +328,7 @@ def month_view(year, month):


@app.route('/<year>/<month>/<day>/')
def day_view(year, month, day):
def day_view(year: str, month: str, day: str) -> Response:
day_posts = [
p for p in posts if year == p.meta.get('published').strftime('%Y')
and month == p.meta.get('published').strftime('%m')
Expand All @@ -340,21 +347,21 @@ def day_view(year, month, day):


@app.errorhandler(404)
def page_not_found(_e):
def page_not_found(_e: Exception | int) -> Response:
return render_template('404.html'), 404


# Telling Frozen-Flask about routes that are not linked to in templates
@freezer.register_generator
def year_view(): # noqa: F811
def year_view() -> Iterator[dict]: # noqa: F811
for post in posts:
yield {
'year': post.meta.get('published').year,
}


@freezer.register_generator
def month_view(): # noqa: F811
def month_view() -> Iterator[dict]: # noqa: F811
for post in posts:
yield {
'month': post.meta.get('published').strftime('%m'),
Expand All @@ -363,7 +370,7 @@ def month_view(): # noqa: F811


@freezer.register_generator
def day_view(): # noqa: F811
def day_view() -> Iterator[dict]: # noqa: F811
for post in posts:
yield {
'day': post.meta.get('published').strftime('%d'),
Expand Down
13 changes: 7 additions & 6 deletions htmd/utils.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from importlib.resources import as_file, files
from pathlib import Path
import shutil
from typing import BinaryIO

import click
from csscompressor import compress
from jsmin import jsmin


def create_directory(name):
def create_directory(name: str) -> Path:
directory = Path(name)
try:
directory.mkdir()
Expand All @@ -19,7 +20,7 @@ def create_directory(name):
return directory


def combine_and_minify_css(static_folder):
def combine_and_minify_css(static_folder: Path) -> None:
# Combine and minify all .css files in the static folder
css_files = sorted([
f for f in static_folder.iterdir()
Expand All @@ -41,7 +42,7 @@ def combine_and_minify_css(static_folder):
master.write(compress(combined))


def combine_and_minify_js(static_folder):
def combine_and_minify_js(static_folder: Path) -> None:
# Combine and minify all .js files in the static folder
js_files = sorted([
f for f in static_folder.iterdir()
Expand All @@ -66,7 +67,7 @@ def combine_and_minify_js(static_folder):
master.write(jsmin(combined))


def copy_file(source, destination):
def copy_file(source: BinaryIO, destination: Path) -> None:
if destination.exists() is False:
shutil.copyfile(source, destination)
click.echo(click.style(f'{destination} was created.', fg='green'))
Expand All @@ -75,14 +76,14 @@ def copy_file(source, destination):
click.echo(click.style(msg, fg='yellow'))


def copy_missing_templates():
def copy_missing_templates() -> None:
template_dir = files('htmd.example_site') / 'templates'
for template_file in sorted(template_dir.iterdir()):
file_name = template_file.name
copy_file(template_file, Path('templates') / file_name)


def copy_site_file(directory, filename):
def copy_site_file(directory: Path, filename: str) -> None:
if directory.name == '':
anchor = 'htmd.example_site'
else:
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ Repository = "https://github.com/siecje/htmd.git"

[tool.ruff.lint]
ignore = [
"D100", "D103", "D104", "D203", "D211", "D212", "D213",
"D100", "D101", "D103", "D104", "D203", "D211", "D212", "D213",
"INP001",
"RET504",
"S101",
"UP015",
"ANN",
]
select = ["ALL"]

Expand Down
Loading

0 comments on commit b3e3cf7

Please sign in to comment.