Skip to content

Commit 9263c52

Browse files
authored
Clean up modules (#16)
* Move get_current_user_id* dependencies to user/auth * Rename the export module to function/export * Rename database/connections.py to database/utils.py * Rename provision/databases.py to provision/utils.py * Move provision settings to provision/settings.py * Clean up env_prefix for settings * Finish provision settings extraction * Factor out driver settings * Remove async from non-async dependencies * Add missing yama/user/auth/__init__.py
1 parent 6041066 commit 9263c52

23 files changed

+112
-104
lines changed

backend/yama/__main__.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from typer import Typer
55

66
from yama.api.settings import Settings as APISettings
7-
from yama.database.connections import sqlalchemy_async_connection
8-
from yama.database.provision.databases import setup_database, teardown_database
7+
from yama.database.provision.settings import Settings as DatabaseProvisionSettings
8+
from yama.database.provision.utils import setup_database, teardown_database
99
from yama.database.settings import Settings as DatabaseSettings
10+
from yama.database.utils import sqlalchemy_async_connection
1011

1112
app = Typer()
1213
database_app = Typer()
@@ -28,24 +29,22 @@ def api() -> None:
2829
@database_app.command()
2930
def up() -> None:
3031
async def f() -> None:
31-
settings = DatabaseSettings()
32-
33-
if settings.provision is None:
34-
raise ValueError("Provision settings are required")
32+
database_settings = DatabaseSettings()
33+
database_provision_settings = DatabaseProvisionSettings()
3534

3635
async with sqlalchemy_async_connection(
37-
host=settings.host,
38-
port=settings.port,
39-
username=settings.provision.username,
40-
password=settings.provision.password,
41-
database=settings.provision.database,
36+
host=database_settings.host,
37+
port=database_settings.port,
38+
username=database_provision_settings.username,
39+
password=database_provision_settings.password,
40+
database=database_provision_settings.database,
4241
) as conn:
4342
autocommit_conn = await conn.execution_options(isolation_level="AUTOCOMMIT")
4443

4544
await setup_database(
4645
autocommit_conn,
47-
database=settings.database,
48-
migrate_executable=settings.provision.migrate_executable,
46+
database=database_settings.database,
47+
migrate_executable=database_provision_settings.migrate_executable,
4948
)
5049

5150
asyncio.run(f())
@@ -54,21 +53,21 @@ async def f() -> None:
5453
@database_app.command()
5554
def down() -> None:
5655
async def f() -> None:
57-
settings = DatabaseSettings()
58-
59-
if settings.provision is None:
60-
raise ValueError("Provision settings are required")
56+
database_settings = DatabaseSettings()
57+
database_provision_settings = DatabaseProvisionSettings()
6158

6259
async with sqlalchemy_async_connection(
63-
host=settings.host,
64-
port=settings.port,
65-
username=settings.provision.username,
66-
password=settings.provision.password,
67-
database=settings.provision.database,
60+
host=database_settings.host,
61+
port=database_settings.port,
62+
username=database_provision_settings.username,
63+
password=database_provision_settings.password,
64+
database=database_provision_settings.database,
6865
) as conn:
6966
autocommit_conn = await conn.execution_options(isolation_level="AUTOCOMMIT")
7067

71-
await teardown_database(autocommit_conn, database=settings.database)
68+
await teardown_database(
69+
autocommit_conn, database=database_settings.database
70+
)
7271

7372
asyncio.run(f())
7473

backend/yama/api/routes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
from fastapi import FastAPI
66
from pydantic import BaseModel
77

8-
from yama.database.connections import sqlalchemy_async_engine
98
from yama.database.settings import Settings as DatabaseSettings
9+
from yama.database.utils import sqlalchemy_async_engine
10+
from yama.file.driver.settings import Settings as FileDriverSettings
1011
from yama.file.routes import files_file_error_handler
1112
from yama.file.routes import router as file_router
1213
from yama.file.settings import Settings as FileSettings
@@ -20,6 +21,7 @@
2021
async def lifespan(_app: FastAPI) -> AsyncIterator[dict[str, Any]]:
2122
database_settings = DatabaseSettings() # pyright: ignore[reportCallIssue]
2223
file_settings = FileSettings() # pyright: ignore[reportCallIssue]
24+
file_driver_settings = FileDriverSettings() # pyright: ignore[reportCallIssue]
2325
user_settings = UserSettings() # pyright: ignore[reportCallIssue]
2426

2527
async with sqlalchemy_async_engine(
@@ -34,6 +36,7 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[dict[str, Any]]:
3436
yield {
3537
"engine": engine,
3638
"file_settings": file_settings,
39+
"file_driver_settings": file_driver_settings,
3740
"user_settings": user_settings,
3841
}
3942

backend/yama/api/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
class Settings(BaseSettings):
5-
model_config = SettingsConfigDict(env_prefix="yama_api__")
5+
model_config = SettingsConfigDict(env_prefix="yama__api__")
66

77
host: str
88
port: int

backend/yama/database/dependencies.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine
66

77

8-
# `get_engine()` is a lifetime dependency that provides
9-
# an `AsyncEngine` created by the lifespan
10-
async def get_engine(request: Request) -> AsyncEngine:
8+
# get_engine is a lifetime dependency that provides an AsyncEngine created by the
9+
# lifespan.
10+
def get_engine(*, request: Request) -> AsyncEngine:
1111
return request.state.engine # type: ignore[no-any-return]
1212

1313

1414
async def get_connection(
15-
engine: Annotated[AsyncEngine, Depends(get_engine)],
15+
*, engine: Annotated[AsyncEngine, Depends(get_engine)]
1616
) -> AsyncIterator[AsyncConnection]:
1717
async with engine.connect() as connection:
1818
yield connection
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from pathlib import Path
2+
3+
from pydantic_settings import BaseSettings, SettingsConfigDict
4+
5+
6+
class Settings(BaseSettings):
7+
model_config = SettingsConfigDict(
8+
env_prefix="yama__database__provision__",
9+
)
10+
11+
database: str
12+
username: str
13+
password: str
14+
migrate_executable: Path

backend/yama/database/settings.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
1-
from pathlib import Path
2-
31
from pydantic_settings import BaseSettings, SettingsConfigDict
42

53

6-
class ProvisionSettings(BaseSettings):
7-
database: str
8-
username: str
9-
password: str
10-
migrate_executable: Path
11-
12-
134
class Settings(BaseSettings):
145
model_config = SettingsConfigDict(
15-
env_prefix="yama_database__", env_nested_delimiter="__"
6+
env_prefix="yama__database__", env_nested_delimiter="__"
167
)
178

189
host: str
1910
port: int
2011
database: str
2112
username: str
2213
password: str
23-
provision: ProvisionSettings | None = None
File renamed without changes.

backend/yama/file/dependencies.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from yama.file.settings import Settings
44

55

6-
# `get_settings()` is a lifetime dependency that provides
7-
# `Settings` created by the lifespan
8-
async def get_settings(request: Request) -> Settings:
6+
# get_settings is a lifetime dependency that provides Settings created by the lifespan.
7+
def get_settings(*, request: Request) -> Settings:
98
return request.state.file_settings # type: ignore[no-any-return]
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
from typing import Annotated, assert_never
22

3-
from fastapi import Depends
3+
from fastapi import Depends, Request
44

5-
from yama.file.dependencies import get_settings
5+
from yama.file.driver.settings import Settings
66
from yama.file.driver.utils import Driver, FileSystemDriver
7-
from yama.file.settings import FileSystemDriverSettings, Settings
87

98

10-
async def get_driver(*, settings: Annotated[Settings, Depends(get_settings)]) -> Driver:
11-
match settings.driver:
12-
case FileSystemDriverSettings(file_system_dir=file_system_dir):
13-
return FileSystemDriver(file_system_dir=file_system_dir)
9+
# get_settings is a lifetime dependency that provides Settings created by the lifespan.
10+
def get_settings(*, request: Request) -> Settings:
11+
return request.state.file_driver_settings # type: ignore[no-any-return]
12+
13+
14+
def get_driver(*, settings: Annotated[Settings, Depends(get_settings)]) -> Driver:
15+
match settings.type:
16+
case "file-system":
17+
return FileSystemDriver(file_system_dir=settings.file_system_dir)
1418
case _:
1519
assert_never(settings.driver)

backend/yama/file/driver/settings.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from pathlib import Path
2+
from typing import Literal
3+
4+
from pydantic_settings import BaseSettings, SettingsConfigDict
5+
6+
7+
class Settings(BaseSettings):
8+
model_config = SettingsConfigDict(env_prefix="yama__file__driver__")
9+
10+
type: Literal["file-system"]
11+
file_system_dir: Path

backend/yama/file/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
)
4444
from yama.file.settings import Settings
4545
from yama.file.utils import FilesFileError
46-
from yama.user.dependencies import get_current_user_id, get_current_user_id_or_none
46+
from yama.user.auth.dependencies import get_current_user_id, get_current_user_id_or_none
4747
from yama.user.dependencies import get_settings as get_user_settings
4848
from yama.user.settings import Settings as UserSettings
4949

backend/yama/file/settings.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from pathlib import Path
2-
from typing import Literal, TypeAlias
31
from uuid import UUID
42

53
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -8,21 +6,12 @@
86
MAX_FILE_PATH_LENGTH = 4095
97

108

11-
class FileSystemDriverSettings(BaseSettings):
12-
type: Literal["file-system"]
13-
file_system_dir: Path
14-
15-
16-
DriverSettings: TypeAlias = FileSystemDriverSettings
17-
18-
199
class Settings(BaseSettings):
2010
model_config = SettingsConfigDict(
21-
env_prefix="yama_file__", env_nested_delimiter="__"
11+
env_prefix="yama__file__", env_nested_delimiter="__"
2212
)
2313

2414
chunk_size: int = 1024 * 1024 * 10 # 10 MiB
2515
max_file_size: int = 1024 * 1024 * 512 # 512 MiB
26-
driver: DriverSettings
2716
files_base_url: str
2817
root_file_id: UUID
File renamed without changes.

backend/yama/function/export/__init__.py

Whitespace-only changes.

backend/yama/export/settings.py renamed to backend/yama/function/export/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
class Settings(BaseSettings):
7-
model_config = SettingsConfigDict(env_prefix="yama_export__")
7+
model_config = SettingsConfigDict(env_prefix="yama__export__")
88

99
latexmk_executable: Path = Path("latexmk")
1010
pandoc_executable: Path = Path("pandoc")
File renamed without changes.

backend/yama/user/auth/__init__.py

Whitespace-only changes.

backend/yama/user/auth/dependencies.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
from typing import Annotated, Literal
2+
from uuid import UUID
23

3-
from fastapi import Form, HTTPException
4+
from fastapi import Depends, Form, HTTPException
5+
from fastapi.security import OAuth2PasswordBearer
46
from pydantic import ValidationError
57

6-
from yama.user.auth.models import GrantIn, GrantInAdapter
8+
from yama.user.auth.models import INVALID_TOKEN_EXCEPTION, GrantIn, GrantInAdapter
9+
from yama.user.auth.utils import InvalidTokenError, access_token_to_user_id
10+
from yama.user.dependencies import get_settings
11+
from yama.user.settings import Settings
712

813

914
def get_grant_in(
@@ -26,3 +31,31 @@ def get_grant_in(
2631
)
2732
except ValidationError:
2833
raise HTTPException(400, "Invalid grant.")
34+
35+
36+
_get_oauth2_token = OAuth2PasswordBearer(tokenUrl="/auth")
37+
_get_oauth2_token_or_none = OAuth2PasswordBearer(tokenUrl="/auth", auto_error=False)
38+
39+
40+
def get_current_user_id(
41+
*,
42+
token: Annotated[str, Depends(_get_oauth2_token)],
43+
settings: Annotated[Settings, Depends(get_settings)],
44+
) -> UUID:
45+
try:
46+
return access_token_to_user_id(token, settings=settings)
47+
except InvalidTokenError:
48+
raise INVALID_TOKEN_EXCEPTION
49+
50+
51+
def get_current_user_id_or_none(
52+
*,
53+
token: Annotated[str | None, Depends(_get_oauth2_token_or_none)],
54+
settings: Annotated[Settings, Depends(get_settings)],
55+
) -> UUID | None:
56+
if token is None:
57+
return None
58+
try:
59+
return access_token_to_user_id(token, settings=settings)
60+
except InvalidTokenError:
61+
return None

backend/yama/user/dependencies.py

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,8 @@
1-
from typing import Annotated
2-
from uuid import UUID
1+
from fastapi import Request
32

4-
from fastapi import Depends, Request
5-
from fastapi.security import OAuth2PasswordBearer
6-
7-
from yama.user.auth.models import INVALID_TOKEN_EXCEPTION
8-
from yama.user.auth.utils import InvalidTokenError, access_token_to_user_id
93
from yama.user.settings import Settings
104

115

126
# get_settings is a lifetime dependency that provides Settings created by the lifespan.
13-
async def get_settings(*, request: Request) -> Settings:
7+
def get_settings(*, request: Request) -> Settings:
148
return request.state.user_settings # type: ignore[no-any-return]
15-
16-
17-
_get_oauth2_token = OAuth2PasswordBearer(tokenUrl="/auth")
18-
_get_oauth2_token_or_none = OAuth2PasswordBearer(tokenUrl="/auth", auto_error=False)
19-
20-
21-
def get_current_user_id(
22-
*,
23-
token: Annotated[str, Depends(_get_oauth2_token)],
24-
settings: Annotated[Settings, Depends(get_settings)],
25-
) -> UUID:
26-
try:
27-
return access_token_to_user_id(token, settings=settings)
28-
except InvalidTokenError:
29-
raise INVALID_TOKEN_EXCEPTION
30-
31-
32-
def get_current_user_id_or_none(
33-
*,
34-
token: Annotated[str | None, Depends(_get_oauth2_token_or_none)],
35-
settings: Annotated[Settings, Depends(get_settings)],
36-
) -> UUID | None:
37-
if token is None:
38-
return None
39-
try:
40-
return access_token_to_user_id(token, settings=settings)
41-
except InvalidTokenError:
42-
return None

backend/yama/user/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from sqlalchemy.ext.asyncio import AsyncConnection
77

88
from yama.database.dependencies import get_connection
9-
from yama.user.dependencies import get_current_user_id
9+
from yama.user.auth.dependencies import get_current_user_id
1010
from yama.user.models import Handle, UserCreateIn, UserDb, UserOut, UserType
1111
from yama.user.utils import (
1212
hash_password,

backend/yama/user/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class AuthSettings(BaseSettings):
2121

2222
class Settings(BaseSettings):
2323
model_config = SettingsConfigDict(
24-
env_prefix="yama_user__", env_nested_delimiter="__"
24+
env_prefix="yama__user__", env_nested_delimiter="__"
2525
)
2626

2727
public_user_id: UUID

0 commit comments

Comments
 (0)