Skip to content

Commit

Permalink
Merge pull request #77 from python-discord/update-repo
Browse files Browse the repository at this point in the history
Update deps and migrate to ruff
  • Loading branch information
ChrisLovering authored Sep 5, 2023
2 parents 8fe4776 + 4f74aa4 commit 846e47b
Show file tree
Hide file tree
Showing 10 changed files with 638 additions and 746 deletions.
19 changes: 4 additions & 15 deletions .github/workflows/quackstack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,12 @@ jobs:
with:
python_version: '3.10'

# We will not run `flake8` here, as we will use a separate flake8
# action. As pre-commit does not support user installs, we set
# PIP_USER=0 to not do a user install.
- name: Run pre-commit hooks
run: export PIP_USER=0; SKIP=flake8 pre-commit run --all-files
run: SKIP=ruff pre-commit run --all-files

# Run flake8 and have it format the linting errors in the format of
# the GitHub Workflow command to register error annotations. This
# means that our flake8 output is automatically added as an error
# annotation to both the run result and in the "Files" tab of a
# pull request.
#
# Format used:
# ::error file={filename},line={line},col={col}::{message}
- name: Run flake8
run: "flake8 \
--format='::error file=%(path)s,line=%(row)d,col=%(col)d::[flake8] %(code)s: %(text)s'"
# Run `ruff` using github formatting to enable automatic inline annotations.
- name: Run ruff
run: "ruff check --format=github ."

build:
name: Build & Push Docker image
Expand Down
28 changes: 9 additions & 19 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.5.0
rev: v4.4.0
hooks:
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
args: [--fix=lf]
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.5.1
hooks:
- id: python-check-blanket-noqa
- repo: local
hooks:
- id: flake8
name: Flake8
description: This hook runs flake8 within our project's poetry environment.
entry: poetry run flake8
language: system
types: [python]
require_serial: true
- repo: https://github.com/pycqa/isort
rev: 5.8.0
hooks:
- id: isort
name: isort (python)
- id: ruff
name: ruff
description: Run ruff linting
entry: poetry run ruff check --force-exclude
language: system
'types_or': [python, pyi]
require_serial: true
args: [--fix, --exit-non-zero-on-fix]
Empty file added api/__init__.py
Empty file.
33 changes: 16 additions & 17 deletions api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from os import getenv
from pathlib import Path
from time import time
from typing import Optional, Union

from fastapi import FastAPI, Request, Response
from fastapi.exceptions import HTTPException
Expand Down Expand Up @@ -32,7 +31,7 @@

def dicthash(data: dict) -> str:
"""Take a dictionary and convert it to a SHA-1 hash."""
return sha1(dumps(data).encode()).hexdigest()
return sha1(dumps(data).encode()).hexdigest() # noqa: S324


def make_file_path(dh: str, request_url: URL) -> str:
Expand All @@ -41,10 +40,10 @@ def make_file_path(dh: str, request_url: URL) -> str:
return f"{fqdn}/static/{dh}.png"


@app.get("/duck", response_model=DuckResponse, status_code=201)
async def get_duck(request: Request, response: Response, seed: Optional[int] = None) -> DuckResponse:
@app.get("/duck", status_code=201)
async def get_duck(request: Request, response: Response, seed: int | None = None) -> DuckResponse:
"""Create a new random duck, with an optional seed."""
dh = sha1(str(time()).encode()).hexdigest()
dh = sha1(str(time()).encode()).hexdigest() # noqa: S324
file = CACHE / f"{dh}.png"

DuckBuilder(seed).generate().image.save(file)
Expand All @@ -54,7 +53,7 @@ async def get_duck(request: Request, response: Response, seed: Optional[int] = N
return DuckResponse(file=file_path)


@app.post("/duck", response_model=DuckResponse, status_code=201)
@app.post("/duck", status_code=201)
async def post_duck(request: Request, response: Response, duck: DuckRequest = None) -> DuckResponse:
"""Create a new duck with a given set of options."""
dh = dicthash(duck.dict())
Expand All @@ -64,19 +63,19 @@ async def post_duck(request: Request, response: Response, duck: DuckRequest = No
try:
DuckBuilder().generate(options=duck.dict()).image.save(file)
except ValueError as e:
raise HTTPException(400, e.args[0])
raise HTTPException(400, e.args[0]) from e
except KeyError as e:
raise HTTPException(400, f"Invalid configuration option provided: '{e.args[0]}'")
raise HTTPException(400, f"Invalid configuration option provided: '{e.args[0]}'") from e

file_path = make_file_path(dh, request.url)
response.headers["Location"] = file_path
return DuckResponse(file=file_path)


@app.get("/manduck", response_model=DuckResponse, status_code=201)
async def get_man_duck(request: Request, response: Response, seed: Optional[int] = None) -> DuckResponse:
@app.get("/manduck", status_code=201)
async def get_man_duck(request: Request, response: Response, seed: int | None = None) -> DuckResponse:
"""Create a new man_duck, with an optional seed."""
dh = sha1(str(time()).encode()).hexdigest()
dh = sha1(str(time()).encode()).hexdigest() # noqa: S324

ducky = DuckBuilder(seed).generate()
ducky = ManDuckBuilder(seed).generate(ducky=ducky)
Expand All @@ -87,7 +86,7 @@ async def get_man_duck(request: Request, response: Response, seed: Optional[int]
return DuckResponse(file=file_path)


@app.post("/manduck", response_model=DuckResponse, status_code=201)
@app.post("/manduck", status_code=201)
async def post_man_duck(request: Request, response: Response, manduck: ManDuckRequest) -> DuckResponse:
"""Create a new man_duck with a given set of options."""
dh = dicthash(manduck.dict())
Expand All @@ -97,18 +96,18 @@ async def post_man_duck(request: Request, response: Response, manduck: ManDuckRe
try:
ducky = ManDuckBuilder().generate(options=manduck.dict())
except ValueError as e:
raise HTTPException(400, e.args[0])
raise HTTPException(400, e.args[0]) from e
except KeyError as e:
raise HTTPException(400, f"Invalid configuration option provided: '{e.args[0]}'")
raise HTTPException(400, f"Invalid configuration option provided: '{e.args[0]}'") from e
ducky.image.save(CACHE / f"{dh}.png")

file_path = make_file_path(dh, request.url)
response.headers["Location"] = file_path
return DuckResponse(file=file_path)


@app.get("/details/{type}", response_model=Union[ManDuckDetails, DuckyDetails])
async def get_details(type: str) -> Union[ManDuckDetails, DuckyDetails]:
@app.get("/details/{type}", response_model=ManDuckDetails | DuckyDetails)
async def get_details(type: str) -> ManDuckDetails | DuckyDetails: # noqa: A002
"""Get details about accessories which can be used to build ducks/man-ducks."""
details = {
"ducky": DuckyDetails(
Expand All @@ -127,7 +126,7 @@ async def get_details(type: str) -> Union[ManDuckDetails, DuckyDetails]:
variation_2=list(ManDuckBuilder.EQUIPMENTS["variation_2"]),
),
variations=list(ManDuckBuilder.VARIATIONS),
)
),
}

return details.get(type, Response("Requested type is not available", 400))
9 changes: 4 additions & 5 deletions api/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import Optional

from pydantic import BaseModel

Expand All @@ -25,15 +24,15 @@ class DressColors(BaseModel):
"""Valid options for a man ducky dress colors."""

shirt: PartOption
pants: Optional[PartOption]
pants: PartOption | None


class Accessories(BaseModel):
"""Valid accessories for a duck."""

hat: Optional[str]
outfit: Optional[str]
equipment: Optional[str]
hat: str | None
outfit: str | None
equipment: str | None


class DuckRequest(BaseModel):
Expand Down
Loading

0 comments on commit 846e47b

Please sign in to comment.