Skip to content

Commit 37e7d96

Browse files
committed
refactor: modernize type annotations and improve code style
- Replace legacy typing imports with modern Python 3.10+ syntax (Union -> |, Optional -> |, List -> list) - Update AsyncGenerator type hints to use collections.abc import - Add comprehensive docstring to settings.py explaining configuration priority and validation - Implement typed dependency annotations using Annotated for better type safety - Remove unused parameters and imports across multiple modules - Standardize function signatures and return type annotations
1 parent 9e8be41 commit 37e7d96

18 files changed

Lines changed: 84 additions & 48 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ src
6666
This application can be configured with environment variables.
6767

6868
You can create `.env` file in the root directory and place all
69-
environment variables here.
69+
environment variables here.
7070

7171
All environment variables should start with "FASTAPI_SUPER_TEMPLATE_" prefix.
7272

7373
For example if you see in your "FastAPI_super_template/settings.py" a variable named like
74-
`random_parameter`, you should provide the "FASTAPI_SUPER_TEMPLATE_RANDOM_PARAMETER"
74+
`random_parameter`, you should provide the "FASTAPI_SUPER_TEMPLATE_RANDOM_PARAMETER"
7575
variable to configure the value. This behaviour can be changed by overriding `env_prefix` property
7676
in `FastAPI_super_template.settings.Settings.Config`.
7777

@@ -83,9 +83,9 @@ FASTAPI_SUPER_TEMPLATE_ENVIRONMENT="dev"
8383
```
8484

8585
You can read more about BaseSettings class here: https://pydantic-docs.helpmanual.io/usage/settings/
86-
## OpenTelemetry
86+
## OpenTelemetry
8787

88-
If you want to start your project with OpenTelemetry collector
88+
If you want to start your project with OpenTelemetry collector
8989
you can add `-f ./deploy/docker-compose.otlp.yml` to your docker command.
9090

9191
Like this:
@@ -94,11 +94,11 @@ Like this:
9494
docker-compose -f docker-compose.yml -f deploy/docker-compose.otlp.yml --project-directory . up
9595
```
9696

97-
This command will start OpenTelemetry collector and jaeger.
97+
This command will start OpenTelemetry collector and jaeger.
9898
After sending a requests you can see traces in jaeger's UI
9999
at http://localhost:16686/.
100100

101-
This docker configuration is not supposed to be used in production.
101+
This docker configuration is not supposed to be used in production.
102102
It's only for demo purpose.
103103

104104
You can read more about OpenTelemetry here: https://opentelemetry.io/

deploy/docker-compose.otlp.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,3 @@ services:
2020
ports:
2121
# Jaeger UI
2222
- 16686:16686
23-

src/gunicorn_runner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
try:
88
import uvloop # (Found nested import)
99
except ImportError:
10-
uvloop = None # type: ignore # (variables overlap)
10+
uvloop = None
1111

1212

1313
class UvicornWorker(BaseUvicornWorker):
@@ -42,7 +42,7 @@ def __init__( # (Too many args)
4242
host: str,
4343
port: int,
4444
workers: int,
45-
**kwargs: Any,
45+
**kwargs: dict[str, Any],
4646
) -> None:
4747
self.options = {
4848
"bind": f"{host}:{port}",

src/log.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
import sys
3-
from typing import Any, Union
3+
from typing import Any
44

55
from loguru import logger
66
from opentelemetry.trace import INVALID_SPAN, INVALID_SPAN_CONTEXT, get_current_span
@@ -26,14 +26,14 @@ def emit(self, record: logging.LogRecord) -> None: # pragma: no cover
2626
:param record: record to log.
2727
"""
2828
try:
29-
level: Union[str, int] = logger.level(record.levelname).name
29+
level: str | int = logger.level(record.levelname).name
3030
except ValueError:
3131
level = record.levelno
3232

3333
# Find caller from where originated the logged message
3434
frame, depth = logging.currentframe(), 2
3535
while frame.f_code.co_filename == logging.__file__:
36-
frame = frame.f_back # type: ignore
36+
frame = frame.f_back
3737
depth += 1
3838

3939
logger.opt(depth=depth, exception=record.exc_info).log(
@@ -97,5 +97,5 @@ def configure_logging() -> None: # pragma: no cover
9797
logger.add(
9898
sys.stdout,
9999
level=settings.log_level.value,
100-
format=record_formatter, # type: ignore
100+
format=record_formatter,
101101
)

src/services/kafka/dependencies.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
from typing import Annotated
2+
13
from aiokafka import AIOKafkaProducer
24
from fastapi import Request
35
from taskiq import TaskiqDepends
6+
from taskiq_dependencies import Depends
47

58

69
def get_kafka_producer(
7-
request: Request = TaskiqDepends(),
10+
request: Annotated[Request, Depends(TaskiqDepends)],
811
) -> AIOKafkaProducer: # pragma: no cover
912
"""
1013
Returns kafka producer.

src/services/rabbit/dependencies.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
from typing import Annotated
2+
13
from aio_pika import Channel
24
from aio_pika.pool import Pool
35
from fastapi import Request
46
from taskiq import TaskiqDepends
7+
from taskiq_dependencies import Depends
58

69

710
def get_rmq_channel_pool(
8-
request: Request = TaskiqDepends(),
11+
request: Annotated[Request, Depends(TaskiqDepends)],
912
) -> Pool[Channel]: # pragma: no cover
1013
"""
1114
Get channel pool from the state.

src/services/redis/dependency.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
from typing import AsyncGenerator
1+
from collections.abc import AsyncGenerator
2+
from typing import Annotated
23

34
from redis.asyncio import Redis
45
from starlette.requests import Request
56
from taskiq import TaskiqDepends
7+
from taskiq_dependencies import Depends
68

79

810
async def get_redis_pool(
9-
request: Request = TaskiqDepends(),
10-
) -> AsyncGenerator[Redis, None]: # pragma: no cover
11+
request: Annotated[Request, Depends(TaskiqDepends)],
12+
) -> AsyncGenerator[Redis]: # pragma: no cover
1113
"""
1214
Returns connection pool.
1315

src/settings.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
1+
"""File with environment variables and general configuration logic.
2+
3+
`SECRET_KEY`, `ENVIRONMENT` etc. map to env variables with the same names.
4+
5+
Pydantic priority ordering:
6+
7+
1. (Most important, will overwrite everything) - environment variables
8+
2. `.env` file in root folder of project
9+
3. Default values
10+
11+
For project name, version, description we use pyproject.toml
12+
For the rest, we use file `.env` (gitignored), see `.env.example`
13+
14+
`SQLALCHEMY_DATABASE_URI` is meant to be validated at the runtime,
15+
do not change unless you know what are you doing.
16+
The validator is to build full URI (TCP protocol) to databases to avoid typo bugs.
17+
18+
See https://pydantic-docs.helpmanual.io/usage/settings/
19+
20+
Note, complex types like lists are read as json-encoded strings.
21+
"""
22+
123
import enum
224
from pathlib import Path
325
from tempfile import gettempdir
4-
from typing import List, Optional
526

627
from pydantic_settings import BaseSettings, SettingsConfigDict
728
from yarl import URL
@@ -43,15 +64,15 @@ class Settings(BaseSettings):
4364
# Variables for Redis
4465
redis_host: str = "src-redis"
4566
redis_port: int = 6379
46-
redis_user: Optional[str] = None
47-
redis_pass: Optional[str] = None
48-
redis_base: Optional[int] = None
67+
redis_user: str | None = None
68+
redis_pass: str | None = None
69+
redis_base: int | None = None
4970

5071
# Variables for RabbitMQ
5172
rabbit_host: str = "src-rmq"
5273
rabbit_port: int = 5672
5374
rabbit_user: str = "guest"
54-
rabbit_pass: str = "guest"
75+
rabbit_pass: str
5576
rabbit_vhost: str = "/"
5677

5778
rabbit_pool_size: int = 2
@@ -62,14 +83,14 @@ class Settings(BaseSettings):
6283
prometheus_dir: Path = TEMP_DIR / "prom"
6384

6485
# Sentry's configuration.
65-
sentry_dsn: Optional[str] = None
86+
sentry_dsn: str | None = None
6687
sentry_sample_rate: float = 1.0
6788

6889
# Grpc endpoint for opentelemetry.
6990
# E.G. http://localhost:4317
70-
opentelemetry_endpoint: Optional[str] = None
91+
opentelemetry_endpoint: str | None = None
7192

72-
kafka_bootstrap_servers: List[str] = ["src-kafka:9092"]
93+
kafka_bootstrap_servers: list[str] = ["src-kafka:9092"]
7394

7495
@property
7596
def redis_url(self) -> URL:

src/static/docs/swagger-ui-bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/static/docs/swagger-ui.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)