init
This commit is contained in:
@@ -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):
|
||||
|
||||
|
||||
11
{{cookiecutter.project_slug}}/app/__main__.py
Normal file
11
{{cookiecutter.project_slug}}/app/__main__.py
Normal 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()
|
||||
37
{{cookiecutter.project_slug}}/app/core/config.py
Normal file
37
{{cookiecutter.project_slug}}/app/core/config.py
Normal 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()
|
||||
48
{{cookiecutter.project_slug}}/app/utils/stable.py
Normal file
48
{{cookiecutter.project_slug}}/app/utils/stable.py
Normal 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())
|
||||
5
{{cookiecutter.project_slug}}/config.toml.template
Normal file
5
{{cookiecutter.project_slug}}/config.toml.template
Normal file
@@ -0,0 +1,5 @@
|
||||
# Application configuration
|
||||
app_name = "{{ cookiecutter.project_name }}"
|
||||
debug = false
|
||||
|
||||
# Add your custom configuration here
|
||||
@@ -1,5 +0,0 @@
|
||||
import os
|
||||
|
||||
PROJECT_NAME = "{{cookiecutter.project_name}}"
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__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)
|
||||
|
||||
|
||||
26
{{cookiecutter.project_slug}}/pyproject.toml
Normal file
26
{{cookiecutter.project_slug}}/pyproject.toml
Normal 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"
|
||||
3
{{cookiecutter.project_slug}}/requirements-dev.txt
Normal file
3
{{cookiecutter.project_slug}}/requirements-dev.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
-r requirements.txt
|
||||
|
||||
pytest>=7.0.0
|
||||
@@ -1,2 +1,3 @@
|
||||
loguru
|
||||
rich
|
||||
loguru~=0.7.2
|
||||
pydantic~=2.10.5
|
||||
pydantic-settings[toml]~=2.6.1
|
||||
|
||||
7
{{cookiecutter.project_slug}}/tests/conftest.py
Normal file
7
{{cookiecutter.project_slug}}/tests/conftest.py
Normal 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))
|
||||
10
{{cookiecutter.project_slug}}/tests/test_config.py
Normal file
10
{{cookiecutter.project_slug}}/tests/test_config.py
Normal 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 }}"
|
||||
Reference in New Issue
Block a user