init
This commit is contained in:
@@ -8,6 +8,14 @@ The only dependencies for this project should be docker and docker-compose.
|
|||||||
|
|
||||||
### Quick Start
|
### Quick Start
|
||||||
|
|
||||||
|
1. 创建配置文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp config.toml.template config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 启动项目
|
||||||
|
|
||||||
Starting the project with hot-reloading enabled
|
Starting the project with hot-reloading enabled
|
||||||
(the first time it will take a while):
|
(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__":
|
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
|
loguru~=0.7.2
|
||||||
rich
|
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