Skip to content

Commit

Permalink
chore: update ruff config and remove legacy tools
Browse files Browse the repository at this point in the history
  • Loading branch information
hairmare committed Mar 10, 2024
1 parent 34057ef commit 2dccb2e
Show file tree
Hide file tree
Showing 8 changed files with 535 additions and 478 deletions.
20 changes: 4 additions & 16 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.1
hooks:
- id: pyupgrade
args:
- --py311-plus
- repo: local
hooks:
- id: black
name: black
language: system
entry: black
types: [python]
- id: isort
name: isort
language: system
entry: isort
types: [python]
- id: flake8
name: flake8
language: system
entry: flake8
types: [python]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.3.2'
hooks:
- id: ruff
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
Expand Down
Empty file added app/py.typed
Empty file.
99 changes: 65 additions & 34 deletions app/server.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
"""Server that hosts our langing page thing with a cat."""

from __future__ import annotations

import json
import logging
import os
from os.path import basename, expanduser
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING: # pragma: no cover
from argparse import Namespace as ArgparseNamespace
from typing import Any, Iterable, Self
from wsgiref.types import StartResponse, WSGIEnvironment

import cherrypy # type: ignore
import cherrypy # type: ignore[import-untyped]
from cachelib.simple import SimpleCache
from configargparse import ArgumentParser # type: ignore
from configargparse import ArgumentParser # type: ignore[import-untyped]
from jinja2 import Environment, FileSystemLoader
from werkzeug.exceptions import HTTPException
from werkzeug.middleware.shared_data import SharedDataMiddleware
Expand All @@ -21,17 +28,17 @@
logger = logging.getLogger("catpage")


def get_config(parse=True):
def get_config(*, parse: bool = True) -> ArgparseNamespace:
"""Get ConfigargParse based configuration.
The config file in /etc gets overriden by the one in $HOME which gets
overriden by the one in the current directory. Everything can also be
set from environment variables.
"""
default_config_file = basename(__file__).replace(".py", ".conf")
default_config_file = Path(__file__).name.replace(".py", ".conf")
default_config_files = [
"/etc/" + default_config_file,
expanduser("~") + "/" + default_config_file,
Path("~").expanduser() / default_config_file,
default_config_file,
]
parser = ArgumentParser(
Expand All @@ -45,11 +52,16 @@ def get_config(parse=True):
default="https://rabe.ch/wp-content/uploads/2016/07/Header.gif",
)
parser.add_argument("--links", env_var="PAGE_LINKS", action="append", default=[])
parser.add_argument("--address", env_var="PAGE_ADDRESS", default="0.0.0.0")
parser.add_argument("--address", env_var="PAGE_ADDRESS", default="127.0.0.1")
parser.add_argument("--port", env_var="PAGE_PORT", default=8080)
parser.add_argument("--thread-pool", env_var="PAGE_THREADPOOL", default=30)

def add_bool_arg(parser, name, default=False):
def add_bool_arg(
parser: ArgumentParser,
name: str,
*,
default: bool = False,
) -> None:
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument("--" + name, dest=name, action="store_true")
group.add_argument("--no-" + name, dest=name, action="store_false")
Expand All @@ -58,10 +70,7 @@ def add_bool_arg(parser, name, default=False):
add_bool_arg(parser, "static", default=True)
add_bool_arg(parser, "dev")

if parse: # pragma: no cover
args = parser.parse_args()
else:
args = parser.parse_args([])
args = parser.parse_args() if parse else parser.parse_args([])
logger.info(parser.format_values())

if not args.links:
Expand All @@ -73,7 +82,7 @@ def add_bool_arg(parser, name, default=False):

if args.links:

def link_split(link):
def link_split(link: str) -> dict[str, str]:
split = link.split(";")
return {"name": split[0], "target": split[1]}

Expand All @@ -89,16 +98,18 @@ class Server:

DEFAULT_CACHE_TIMEOUT = 60 * 60 * 24 * 30

def __init__(self, config):
def __init__(self: Self, config: ArgparseNamespace) -> None:
"""Initialize server with cache and templates."""
self.page_title = config.title
self.page_background_image = config.background_image
self.links = config.links

self.cache = SimpleCache()

template_path = os.path.join(os.path.dirname(__file__), "templates")
template_path = Path(__file__).parent / "templates"
self.jinja_env = Environment(
loader=FileSystemLoader(template_path), autoescape=True
loader=FileSystemLoader(template_path),
autoescape=True,
)
self.url_map = Map(
[
Expand All @@ -108,10 +119,10 @@ def __init__(self, config):
Rule("/.env", endpoint="hack"),
Rule("/wp-login.php", endpoint="hack"),
Rule("/wp-admin", endpoint="hack"),
]
],
)

def dispatch_request(self, request):
def dispatch_request(self: Self, request: Request) -> Response | HTTPException:
"""Dispatch requests to handlers."""
adapter = self.url_map.bind_to_environ(request.environ)
try:
Expand All @@ -120,7 +131,7 @@ def dispatch_request(self, request):
except HTTPException as ex:
return ex

def on_site(self, request): # pylint: disable=unused-argument
def on_site(self: Self, _: Request) -> Response:
"""Return main / page."""
return self.render_template(
"index.html",
Expand All @@ -130,35 +141,49 @@ def on_site(self, request): # pylint: disable=unused-argument
version=__version__,
)

def on_service_worker(self, request): # pylint: disable=unused-argument
def on_service_worker(self: Self, _: Request) -> Response:
"""Return a service worker for SPA reasons."""
return self.render_template(
"sw.js",
mimetype="application/javascript",
background_url=self.page_background_image,
)

def on_api(self, request): # pylint: disable=unused-argument
def on_api(self: Self, _: Request) -> Response:
"""Return links as JSON request."""
return Response(
json.dumps({"version": __version__, "links": self.links}),
mimetype="application/json",
)

def on_hack(self, request): # pylint: disable=unused-argument
def on_hack(self: Self, _: Request) -> Response:
"""Rickroll people trying to break in."""
return redirect("https://www.youtube.com/watch?v=dQw4w9WgXcQ")

def wsgi_app(self, environ, start_response):
def wsgi_app(
self: Self,
environ: WSGIEnvironment,
start_response: StartResponse,
) -> Iterable[bytes]:
"""Return a wsgi app."""
request = Request(environ)
response = self.dispatch_request(request)
return response(environ, start_response)

def __call__(self, environ, start_response):
def __call__(
self: Self,
environ: WSGIEnvironment,
start_response: StartResponse,
) -> Iterable[bytes]:
"""Forward calls to wsgi_app."""
return self.wsgi_app(environ, start_response)

def render_template(self, template_name, mimetype="text/html", **context):
def render_template(
self: Self,
template_name: str,
mimetype: str = "text/html",
**context: Any, # noqa: ANN401
) -> Response:
"""Render template to cache and keep in cache forver."""
if not self.cache.has(template_name):
tpl = self.jinja_env.get_template(template_name)
Expand All @@ -167,33 +192,39 @@ def render_template(self, template_name, mimetype="text/html", **context):
return Response(self.cache.get(template_name), mimetype=mimetype)


def create_app(config):
def create_app(config: ArgparseNamespace) -> Server:
"""Create the app server."""
app = Server(config)
if config.static:
app.wsgi_app = SharedDataMiddleware(
app.wsgi_app = SharedDataMiddleware( # type: ignore[method-assign]
app.wsgi_app,
{"/static": os.path.join(os.path.dirname(__file__), "static")},
{"/static": str(Path(__file__).parent / "static")}, # type: ignore[arg-type]
cache_timeout=Server.DEFAULT_CACHE_TIMEOUT,
)
return app


def run_devserver(app, config): # pragma: no cover
def run_devserver(
app: Server,
config: ArgparseNamespace,
) -> None: # pragma: no cover
"""Run a simple werkzeug devserver."""
logger.info("Starting development server")

run_simple(config.address, config.port, app, use_debugger=True, use_reloader=True)
run_simple(config.address, config.port, app, use_debugger=True, use_reloader=True) # type: ignore[arg-type]


def run_webserver(app, config): # pragma: no cover
def run_webserver(
app: Server,
config: ArgparseNamespace,
) -> None: # pragma: no cover
"""Run the production cherrypy server."""
logger.info("Starting production server")

cherrypy.tree.graft(app, "/")
cherrypy.server.unsubscribe()

server = cherrypy._cpserver.Server() # pylint: disable=protected-access
server = cherrypy._cpserver.Server() # noqa: SLF001

server.socket_host = config.address
server.socket_port = config.port
Expand All @@ -205,7 +236,7 @@ def run_webserver(app, config): # pragma: no cover
cherrypy.engine.block()


def main(): # pragma: no cover
def main() -> None: # pragma: no cover
"""Start dev or prod server."""
logger.info("Starting cat-page server version %s", __version__)
cfg = get_config()
Expand Down
6 changes: 4 additions & 2 deletions docs/gen_ref_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())

readme = Path("README.md").open("r")
with mkdocs_gen_files.open("index.md", "w") as index_file:
with Path("README.md").open("r") as readme, mkdocs_gen_files.open(
"index.md",
"w",
) as index_file:
index_file.writelines(readme.read())
Loading

0 comments on commit 2dccb2e

Please sign in to comment.