Skip to content

Commit 77623fb

Browse files
committed
refactor: added type hints
1 parent 668768b commit 77623fb

File tree

3 files changed

+56
-55
lines changed

3 files changed

+56
-55
lines changed

src/paste/main.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from fastapi import File, UploadFile, HTTPException, status, Request, Form
1+
from fastapi import File, UploadFile, HTTPException, status, Request, Form, FastAPI
22
from fastapi.responses import PlainTextResponse, HTMLResponse, RedirectResponse, JSONResponse
33
import shutil
44
import os
@@ -16,15 +16,16 @@
1616
from pygments.lexers import get_lexer_by_name, guess_lexer
1717
from pygments.formatters import HtmlFormatter
1818
from pygments.util import ClassNotFound
19+
from typing import List, Optional
1920

2021
limiter = Limiter(key_func=get_remote_address)
21-
app = FastAPI(title="paste.py 🐍")
22+
app: FastAPI = FastAPI(title="paste.py 🐍")
2223
app.state.limiter = limiter
2324
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
2425

25-
origins = ["*"]
26+
origins: List[str] = ["*"]
2627

27-
BASE_URL = r"http://paste.fosscu.org"
28+
BASE_URL: str = r"http://paste.fosscu.org"
2829

2930
app.add_middleware(
3031
CORSMiddleware,
@@ -36,34 +37,33 @@
3637

3738
app.add_middleware(LimitUploadSize, max_upload_size=20_000_000) # ~20MB
3839

39-
large_uuid_storage = []
40+
large_uuid_storage: List[str] = []
4041

41-
BASE_DIR = Path(__file__).resolve().parent
42+
BASE_DIR: Path = Path(__file__).resolve().parent
4243

43-
templates = Jinja2Templates(directory=str(Path(BASE_DIR, "templates")))
44+
templates: Jinja2Templates = Jinja2Templates(directory=str(Path(BASE_DIR, "templates")))
4445

4546

4647
@app.post("/file")
4748
@limiter.limit("100/minute")
48-
async def post_as_a_file(request: Request, file: UploadFile = File(...)):
49+
async def post_as_a_file(request: Request, file: UploadFile = File(...)) -> PlainTextResponse:
4950
try:
50-
uuid = generate_uuid()
51+
uuid: str = generate_uuid()
5152
if uuid in large_uuid_storage:
5253
uuid = generate_uuid()
5354
# Extract file extension from the filename
5455
try:
55-
file_extension = Path(file.filename).suffix[1:]
56-
path = f"data/{uuid}.{file_extension}"
56+
file_extension: str = Path(file.filename).suffix[1:]
57+
path: str = f"data/{uuid}.{file_extension}"
5758
except Exception:
5859
path = f"data/{uuid}"
5960
finally:
60-
val = "/".join(path.split("/")[1:])
61+
val: str = "/".join(path.split("/")[1:])
6162
with open(path, "wb") as f:
6263
shutil.copyfileobj(file.file, f)
6364
large_uuid_storage.append(uuid)
6465
print(large_uuid_storage)
6566
except Exception:
66-
# return {"message": "There was an error uploading the file"}
6767
raise HTTPException(
6868
detail="There was an error uploading the file",
6969
status_code=status.HTTP_403_FORBIDDEN,
@@ -74,13 +74,13 @@ async def post_as_a_file(request: Request, file: UploadFile = File(...)):
7474

7575

7676
@app.get("/paste/{uuid}")
77-
async def get_paste_data(uuid):
78-
path = f"data/{uuid}"
77+
async def get_paste_data(uuid: str) -> HTMLResponse:
78+
path: str = f"data/{uuid}"
7979
try:
8080
with open(path, "rb") as f:
81-
content = f.read().decode("utf-8")
81+
content: str = f.read().decode("utf-8")
8282
# Get file extension from the filename
83-
file_extension = Path(path).suffix[1:]
83+
file_extension: str = Path(path).suffix[1:]
8484
if file_extension == "":
8585
# Guess lexer based on content
8686
lexer = guess_lexer(content)
@@ -93,7 +93,7 @@ async def get_paste_data(uuid):
9393
"text", stripall=True) # Default lexer
9494
formatter = HtmlFormatter(
9595
style="colorful", full=True, linenos="inline")
96-
highlighted_code = highlight(content, lexer, formatter)
96+
highlighted_code: str = highlight(content, lexer, formatter)
9797
return HTMLResponse(
9898
content=highlighted_code
9999
)
@@ -106,13 +106,13 @@ async def get_paste_data(uuid):
106106

107107

108108
@app.get("/", response_class=HTMLResponse)
109-
async def indexpage(request: Request):
109+
async def indexpage(request: Request) -> HTMLResponse:
110110
return templates.TemplateResponse("index.html", {"request": request})
111111

112112

113113
@app.delete("/paste/{uuid}", response_class=PlainTextResponse)
114-
async def delete_paste(uuid):
115-
path = f"data/{uuid}"
114+
async def delete_paste(uuid: str) -> PlainTextResponse:
115+
path: str = f"data/{uuid}"
116116
try:
117117
os.remove(path)
118118
return PlainTextResponse(f"File successfully deleted {uuid}")
@@ -127,23 +127,25 @@ async def delete_paste(uuid):
127127

128128

129129
@app.get("/web", response_class=HTMLResponse)
130-
async def web(request: Request):
130+
async def web(request: Request) -> HTMLResponse:
131131
return templates.TemplateResponse("web.html", {"request": request})
132132

133133

134134
@app.post("/web", response_class=PlainTextResponse)
135135
@limiter.limit("100/minute")
136-
async def web_post(request: Request, content: str = Form(...), extension: str = Form(None)):
136+
async def web_post(
137+
request: Request, content: str = Form(...), extension: Optional[str] = Form(None)
138+
) -> RedirectResponse:
137139
try:
138-
file_content = content.encode()
139-
uuid = generate_uuid()
140+
file_content: bytes = content.encode()
141+
uuid: str = generate_uuid()
140142
if uuid in large_uuid_storage:
141143
uuid = generate_uuid()
142144
if extension:
143145
uuid_ = uuid + extension
144146
else:
145147
uuid_ = uuid
146-
path = f"data/{uuid_}"
148+
path: str = f"data/{uuid_}"
147149
with open(path, "wb") as f:
148150
f.write(file_content)
149151
large_uuid_storage.append(uuid_)
@@ -165,11 +167,10 @@ async def health() -> dict[str, str]:
165167

166168

167169
@app.get("/languages.json", response_class=JSONResponse)
168-
async def get_languages():
170+
async def get_languages() -> JSONResponse:
169171
try:
170172
with open(Path(BASE_DIR, "languages.json"), "r") as file:
171-
languages_data = json.load(file)
172-
# print(languages_data)
173+
languages_data: dict = json.load(file)
173174
return JSONResponse(content=languages_data, status_code=status.HTTP_200_OK)
174175
except FileNotFoundError:
175176
raise HTTPException(

src/paste/middleware.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
class LimitUploadSize(BaseHTTPMiddleware):
99
def __init__(self, app: ASGIApp, max_upload_size: int) -> None:
1010
super().__init__(app)
11-
self.max_upload_size = max_upload_size
11+
self.max_upload_size: int = max_upload_size
1212

1313
async def dispatch(
1414
self, request: Request, call_next: RequestResponseEndpoint
1515
) -> Response:
1616
if request.method == "POST":
1717
if "content-length" not in request.headers:
1818
return Response(status_code=status.HTTP_411_LENGTH_REQUIRED)
19-
content_length = int(request.headers["content-length"])
19+
content_length: int = int(request.headers["content-length"])
2020
if content_length > self.max_upload_size:
2121
return Response(
2222
"File is too large",

tests/test_api.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,60 @@
22
from src.paste.main import app
33
import os
44

5-
client = TestClient(app)
5+
client: TestClient = TestClient(app)
66

7-
file = None
7+
file: str = None
88

99

10-
def test_get_health_route():
11-
data = {"status": "ok"}
10+
def test_get_health_route() -> None:
11+
data: dict = {"status": "ok"}
1212
response = client.get("/health")
1313
assert response.status_code == 200
1414
assert response.json() == data
1515

1616

17-
def test_get_homepage_route():
18-
response_expected_headers = "text/html; charset=utf-8"
17+
def test_get_homepage_route() -> None:
18+
response_expected_headers: str = "text/html; charset=utf-8"
1919
response = client.get("/")
2020
assert response.status_code == 200
2121
assert response.headers.get("Content-Type", "") == response_expected_headers
2222

2323

24-
def test_get_web_route():
25-
response_expected_headers = "text/html; charset=utf-8"
24+
def test_get_web_route() -> None:
25+
response_expected_headers: str = "text/html; charset=utf-8"
2626
response = client.get("/web")
2727
assert response.status_code == 200
2828
assert response.headers.get("Content-Type", "") == response_expected_headers
2929

3030

31-
def test_get_paste_data_route():
32-
data = "This is a test file."
31+
def test_get_paste_data_route() -> None:
32+
data: str = "This is a test file."
3333
response = client.get("/paste/test")
3434
assert response.status_code == 200
3535
assert data in response.text
3636

3737

38-
def test_post_web_route():
39-
data = "This is a test data"
40-
form_data = {"content": data, "extension": ".txt"}
38+
def test_post_web_route() -> None:
39+
data: str = "This is a test data"
40+
form_data: dict = {"content": data, "extension": ".txt"}
4141
response = client.post("/web", data=form_data)
4242
global file
4343
file = str(response.url).split("/")[-1]
4444
assert response.status_code == 200
4545
assert data in response.text
4646

4747

48-
def test_delete_paste_route():
49-
expected_response = f"File successfully deleted {file}"
48+
def test_delete_paste_route() -> None:
49+
expected_response: str = f"File successfully deleted {file}"
5050
response = client.delete(f"/paste/{file}")
5151
assert response.status_code == 200
5252
assert response.text == expected_response
5353

5454

55-
def test_post_file_route():
55+
def test_post_file_route() -> None:
5656
response = client.post("/file", files={"file": ("test.txt", b"test file content")})
5757
assert response.status_code == 201
58-
response_file_uuid = response.text
58+
response_file_uuid: str = response.text
5959
response = client.get(f"/paste/{response_file_uuid}")
6060
assert response.status_code == 200
6161
assert "test file content" in response.text
@@ -64,7 +64,7 @@ def test_post_file_route():
6464
assert f"File successfully deleted {response_file_uuid}" in response.text
6565

6666

67-
def test_post_file_route_failure():
67+
def test_post_file_route_failure() -> None:
6868
response = client.post("/file")
6969
assert response.status_code == 422 # Unprocessable Entity
7070
assert response.json() == {
@@ -80,18 +80,18 @@ def test_post_file_route_failure():
8080
}
8181

8282

83-
def test_post_file_route_size_limit():
84-
large_file_name = "large_file.txt"
85-
file_size = 20 * 1024 * 1024 # 20 MB in bytes
86-
additional_bytes = 100 # Adding some extra bytes to exceed 20 MB
87-
content = b"This is a line in the file.\n"
83+
def test_post_file_route_size_limit() -> None:
84+
large_file_name: str = "large_file.txt"
85+
file_size: int = 20 * 1024 * 1024 # 20 MB in bytes
86+
additional_bytes: int = 100 # Adding some extra bytes to exceed 20 MB
87+
content: bytes = b"This is a line in the file.\n"
8888
with open(large_file_name, "wb") as file:
8989
while file.tell() < file_size:
9090
file.write(content)
9191
file.write(b"Extra bytes to exceed 20 MB\n" * additional_bytes)
9292
file.close()
9393
f = open(large_file_name, "rb")
94-
files = {"file": f}
94+
files: dict = {"file": f}
9595
response = client.post("/file", files=files)
9696
f.close()
9797
# cleanup

0 commit comments

Comments
 (0)