Skip to content

Commit 6859548

Browse files
authored
fix: compatibility changes (#205)
Compatibility changes with upstream libraries
1 parent ce52c5f commit 6859548

File tree

14 files changed

+1389
-1501
lines changed

14 files changed

+1389
-1501
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repos:
1717
- id: mixed-line-ending
1818
- id: trailing-whitespace
1919
- repo: https://github.com/charliermarsh/ruff-pre-commit
20-
rev: "v0.9.2"
20+
rev: "v0.9.10"
2121
hooks:
2222
# Run the linter.
2323
- id: ruff
@@ -27,7 +27,7 @@ repos:
2727
- id: ruff-format
2828
types_or: [ python, pyi ]
2929
- repo: https://github.com/codespell-project/codespell
30-
rev: v2.4.0
30+
rev: v2.4.1
3131
hooks:
3232
- id: codespell
3333
exclude: "uv.lock|package.json|package-lock.json"

package-lock.json

Lines changed: 722 additions & 541 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ classifiers = [
1616
]
1717
dependencies = [
1818
"litestar[jinja,jwt,redis,structlog]",
19-
"advanced-alchemy[uuid]",
19+
"advanced-alchemy[uuid]>=0.33.2",
2020
"asyncpg",
2121
"python-dotenv",
2222
"passlib[argon2]",

src/app/config/_utils.py

Lines changed: 108 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,74 +9,159 @@
99
from collections.abc import Callable
1010

1111
BASE_DIR: Final[Path] = Path(__file__).parent.parent
12-
TRUE_VALUES: Final[frozenset[str]] = frozenset({"True", "true", "1", "yes", "Y", "T"})
12+
TRUE_VALUES: Final[frozenset[str]] = frozenset({"True", "true", "1", "yes", "YES", "Y", "y", "T", "t"})
1313

1414
T = TypeVar("T")
15-
ParseTypes = bool | int | str | list[str]
15+
ParseTypes = bool | int | str | list[str] | Path | list[Path]
16+
17+
18+
class UnsetType:
19+
"""Placeholder for an Unset type.
20+
21+
This helps differentiate None from the default
22+
"""
23+
24+
25+
_UNSET = UnsetType()
26+
27+
28+
@overload
29+
def get_env(key: str, default: bool, type_hint: UnsetType = _UNSET) -> Callable[[], bool]: ...
30+
31+
32+
@overload
33+
def get_env(key: str, default: int, type_hint: UnsetType = _UNSET) -> Callable[[], int]: ...
34+
35+
36+
@overload
37+
def get_env(key: str, default: str, type_hint: UnsetType = _UNSET) -> Callable[[], str]: ...
38+
39+
40+
@overload
41+
def get_env(key: str, default: Path, type_hint: UnsetType = _UNSET) -> Callable[[], Path]: ...
1642

1743

1844
@overload
19-
def get_env(key: str, default: bool) -> Callable[[], bool]: ...
45+
def get_env(key: str, default: list[Path], type_hint: UnsetType = _UNSET) -> Callable[[], list[Path]]: ...
2046

2147

2248
@overload
23-
def get_env(key: str, default: int) -> Callable[[], int]: ...
49+
def get_env(key: str, default: list[str], type_hint: UnsetType = _UNSET) -> Callable[[], list[str]]: ...
2450

2551

2652
@overload
27-
def get_env(key: str, default: str) -> Callable[[], str]: ...
53+
def get_env(key: str, default: None, type_hint: UnsetType = _UNSET) -> Callable[[], None]: ...
2854

2955

3056
@overload
31-
def get_env(key: str, default: list[str]) -> Callable[[], list[str]]: ...
57+
def get_env(key: str, default: ParseTypes | None, type_hint: type[T]) -> Callable[[], T]: ...
3258

3359

34-
def get_env(key: str, default: ParseTypes) -> Callable[[], ParseTypes]:
35-
return lambda: get_config_val(key, default)
60+
def get_env(
61+
key: str, default: ParseTypes | None, type_hint: type[T] | UnsetType = _UNSET
62+
) -> Callable[[], ParseTypes | T | None]:
63+
return lambda: get_config_val(key=key, default=default, type_hint=type_hint)
3664

3765

3866
@overload
39-
def get_config_val(key: str, default: bool) -> bool: ...
67+
def get_config_val(key: str, default: bool, type_hint: UnsetType = _UNSET) -> bool: ...
4068

4169

4270
@overload
43-
def get_config_val(key: str, default: int) -> int: ...
71+
def get_config_val(key: str, default: int, type_hint: UnsetType = _UNSET) -> int: ...
4472

4573

4674
@overload
47-
def get_config_val(key: str, default: str) -> str: ...
75+
def get_config_val(key: str, default: str, type_hint: UnsetType = _UNSET) -> str: ...
4876

4977

5078
@overload
51-
def get_config_val(key: str, default: list[str]) -> list[str]: ...
79+
def get_config_val(key: str, default: Path, type_hint: UnsetType = _UNSET) -> Path: ...
5280

5381

54-
def get_config_val(
55-
key: str,
56-
default: ParseTypes,
57-
) -> ParseTypes:
82+
@overload
83+
def get_config_val(key: str, default: list[Path], type_hint: UnsetType = _UNSET) -> list[Path]: ...
84+
85+
86+
@overload
87+
def get_config_val(key: str, default: list[str], type_hint: UnsetType = _UNSET) -> list[str]: ...
88+
89+
90+
@overload
91+
def get_config_val(key: str, default: None, type_hint: UnsetType = _UNSET) -> None: ...
92+
93+
94+
@overload
95+
def get_config_val(key: str, default: ParseTypes | None, type_hint: type[T]) -> T: ...
96+
97+
98+
def get_config_val( # noqa: C901, PLR0912, PLR0911
99+
key: str, default: ParseTypes | None, type_hint: type[T] | UnsetType = _UNSET
100+
) -> ParseTypes | T | None:
58101
"""Parse environment variables.
59102
60103
Args:
61104
key: Environment variable key
62105
default: Default value if key not found in environment
106+
type_hint: Optional type hint to use instead of inferring from `default`
107+
108+
Raises:
109+
ValueError: Raised when the configuration value cannot be parsed.
63110
64111
Returns:
65112
Parsed value of the specified type
66113
"""
67-
value = os.getenv(key)
68-
if value is None:
114+
str_value = os.getenv(key)
115+
if str_value is None:
116+
if type_hint != _UNSET:
117+
return cast("T", default)
69118
return default
119+
value: str = str_value
70120
if type(default) is bool:
71-
return value in TRUE_VALUES
121+
bool_value = value in TRUE_VALUES
122+
if type_hint != _UNSET:
123+
return cast("T", bool_value)
124+
return bool_value
72125
if type(default) is int:
73-
return int(value)
74-
if type(default) is list:
126+
int_value = int(value)
127+
if type_hint != _UNSET:
128+
return cast("T", int_value)
129+
return int_value
130+
if type(default) is Path:
131+
path_value = Path(value)
132+
if type_hint != _UNSET:
133+
return cast("T", path_value)
134+
return path_value
135+
if type(default) is list[Path]:
136+
if value.startswith("[") and value.endswith("]"):
137+
try:
138+
path_list = [Path(s) for s in json.loads(value)]
139+
if type_hint != _UNSET:
140+
return cast("T", path_list)
141+
except (SyntaxError, ValueError) as e:
142+
msg = f"{key} is not a valid list representation."
143+
raise ValueError(msg) from e
144+
else:
145+
return value
146+
path_list = [Path(host.strip()) for host in value.split(",")]
147+
if type_hint != _UNSET:
148+
return cast("T", path_list)
149+
return path_list
150+
if type(default) is list[str]:
75151
if value.startswith("[") and value.endswith("]"):
76152
try:
77-
return cast("list[str]", json.loads(value))
153+
str_list = cast("list[str]", json.loads(value))
154+
if type_hint != _UNSET:
155+
return cast("T", str_list)
78156
except (SyntaxError, ValueError) as e:
79157
msg = f"{key} is not a valid list representation."
80158
raise ValueError(msg) from e
81-
return [host.strip() for host in value.split(",")]
159+
else:
160+
return value
161+
str_list = [host.strip() for host in value.split(",")]
162+
if type_hint != _UNSET:
163+
return cast("T", str_list)
164+
return str_list
165+
if type_hint != _UNSET:
166+
return cast("T", value)
82167
return value

src/app/config/app.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@
6767
)
6868

6969
saq = SAQConfig(
70-
dsn=settings.redis.URL,
7170
web_enabled=settings.saq.WEB_ENABLED,
7271
worker_processes=settings.saq.PROCESSES,
7372
use_server_lifespan=settings.saq.USE_SERVER_LIFESPAN,
7473
queue_configs=[
7574
QueueConfig(
75+
dsn=settings.redis.URL,
7676
name="system-tasks",
7777
tasks=["app.domain.system.tasks.system_task", "app.domain.system.tasks.system_upkeep"],
7878
scheduled_tasks=[
@@ -85,6 +85,7 @@
8585
],
8686
),
8787
QueueConfig(
88+
dsn=settings.redis.URL,
8889
name="background-tasks",
8990
tasks=["app.domain.system.tasks.background_worker_task"],
9091
scheduled_tasks=[
@@ -125,14 +126,19 @@ def _is_tty() -> bool:
125126
},
126127
},
127128
loggers={
128-
"granian.access": {
129+
"_granian": {
129130
"propagate": False,
130-
"level": settings.log.GRANIAN_ACCESS_LEVEL,
131+
"level": settings.log.ASGI_ERROR_LEVEL,
131132
"handlers": ["queue_listener"],
132133
},
133-
"granian.error": {
134+
"granian.server": {
135+
"propagate": False,
136+
"level": settings.log.ASGI_ERROR_LEVEL,
137+
"handlers": ["queue_listener"],
138+
},
139+
"granian.access": {
134140
"propagate": False,
135-
"level": settings.log.GRANIAN_ERROR_LEVEL,
141+
"level": settings.log.ASGI_ACCESS_LEVEL,
136142
"handlers": ["queue_listener"],
137143
},
138144
"saq": {
@@ -154,7 +160,7 @@ def _is_tty() -> bool:
154160
),
155161
),
156162
middleware_logging_config=LoggingMiddlewareConfig(
157-
request_log_fields=["method", "path", "path_params", "query"],
158-
response_log_fields=["status_code"],
163+
request_log_fields=settings.log.REQUEST_FIELDS,
164+
response_log_fields=settings.log.RESPONSE_FIELDS,
159165
),
160166
)

0 commit comments

Comments
 (0)