This commit is contained in:
2025-01-20 15:38:15 +08:00
parent a0b76f1d51
commit b9cde3e874
14 changed files with 161 additions and 9 deletions

View File

@@ -8,6 +8,14 @@ The only dependencies for this project should be docker and docker-compose.
### Quick Start
1. 创建配置文件
```bash
cp config.toml.template config.toml
```
2. 启动项目
Starting the project with hot-reloading enabled
(the first time it will take a while):

View File

@@ -0,0 +1,11 @@
from app.core import config
def main():
"""Main entry point for the application."""
print(f"Welcome to {config.APP_NAME}!")
print(f"Debug mode: {config.DEBUG}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,37 @@
from pathlib import Path
from typing import Optional, Tuple, Type
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
SettingsConfigDict,
TomlConfigSettingsSource,
)
class Settings(BaseSettings):
"""Application settings."""
# 应用配置
APP_NAME: str = "{{ cookiecutter.project_name }}"
DEBUG: bool = False
SECRET_KEY: str = "{{ cookiecutter.secret_key }}"
# 路径配置
BASE_DIR: Path = Path(__file__).parent.parent.parent
model_config = SettingsConfigDict(toml_file=BASE_DIR / "config.toml")
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (TomlConfigSettingsSource(settings_cls),)
# 创建全局设置实例
config = Settings()

View File

@@ -0,0 +1,48 @@
import asyncio
import datetime
from functools import wraps
from loguru import logger
def retry_async(max_retries=3, initial_delay=1, exceptions=(Exception,)):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
attempt = 0
delay = initial_delay
while attempt < max_retries:
try:
return await func(*args, **kwargs)
except exceptions as e:
logger.info(
f"{e=}, attempt {attempt + 1}/{max_retries}, will retry in {delay} seconds"
)
logger.exception(f"{e=}")
attempt += 1
if attempt >= max_retries:
logger.info("Max retries reached, aborting")
raise e
await asyncio.sleep(delay)
delay *= 2 # 延迟时间加倍
return wrapper
return decorator
if __name__ == "__main__":
@retry_async()
async def main():
now = datetime.datetime.now()
logger.info(f"{now=}")
# Fail twice then succeed
if main.counter < 2:
main.counter += 1
raise Exception(now)
return now
main.counter = 0
asyncio.run(main())

View File

@@ -0,0 +1,5 @@
# Application configuration
app_name = "{{ cookiecutter.project_name }}"
debug = false
# Add your custom configuration here

View File

@@ -1,5 +0,0 @@
import os
PROJECT_NAME = "{{cookiecutter.project_name}}"
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

View File

@@ -1,4 +1,5 @@
from core import BASE_DIR
from app.core import config
if __name__ == "__main__":
print(BASE_DIR)
print(config.BASE_DIR)

View File

@@ -0,0 +1,26 @@
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"
[project]
name = "{{ cookiecutter.project_slug }}"
version = "0.1.0"
description = "{{ cookiecutter.project_short_description }}"
authors = [
{name = "{{ cookiecutter.github_username }}", email = "{{ cookiecutter.email }}"}
]
dependencies = [
"loguru~=0.7.2",
"pydantic~=2.10.5",
"pydantic-settings[toml]~=2.6.1"
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0"
]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
addopts = "-ra -q"

View File

@@ -0,0 +1,3 @@
-r requirements.txt
pytest>=7.0.0

View File

@@ -1,2 +1,3 @@
loguru
rich
loguru~=0.7.2
pydantic~=2.10.5
pydantic-settings[toml]~=2.6.1

View File

@@ -0,0 +1,7 @@
import os
import sys
from pathlib import Path
# Add the src directory to the Python path
src_path = Path(__file__).parent.parent / "app"
sys.path.insert(0, str(src_path))

View File

@@ -0,0 +1,10 @@
from pathlib import Path
import pytest
from app.core.config import Settings
def test_settings_default_values():
settings = Settings()
assert settings.APP_NAME == "{{ cookiecutter.project_name }}"