项目文件夹

文件
2026-07-13 12:34:57 +08:00

532 行
14 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MyBoot CLI 工具
提供项目模板初始化功能
"""
import click
import sys
from pathlib import Path
@click.group()
def cli():
"""MyBoot 命令行工具 - 项目模板初始化"""
pass
@cli.command()
@click.option('--name', prompt='项目名称', help='项目名称')
@click.option('--dir', default='.', help='项目目录(默认为当前目录)')
@click.option('--template', type=click.Choice(['basic', 'api', 'full']), default='basic',
help='项目模板: basic(基础), api(API项目), full(完整项目)')
@click.option('--force', is_flag=True, help='如果目录已存在则覆盖')
def init(name: str, dir: str, template: str, force: bool):
"""初始化新的 MyBoot 项目"""
project_dir = Path(dir) / name
# 检查目录是否存在
if project_dir.exists() and not force:
click.echo(f"❌ 错误: 目录 '{project_dir}' 已存在", err=True)
click.echo(" 使用 --force 选项可以覆盖现有目录")
sys.exit(1)
click.echo(f"📦 正在初始化项目: {name}")
click.echo(f" 模板: {template}")
click.echo(f" 目录: {project_dir}")
click.echo()
try:
# 创建目录结构
dirs = ['app', 'app/api', 'app/service', 'app/model', 'app/jobs', 'app/client', 'conf', 'tests']
for d in dirs:
(project_dir / d).mkdir(parents=True, exist_ok=True)
# 创建 __init__.py
init_file = project_dir / d / '__init__.py'
if not init_file.exists():
init_file.write_text('', encoding='utf-8')
click.echo("✓ 创建目录结构")
# 创建配置文件
config_content = f"""# {name} 配置文件
# 应用配置
app:
name: "{name}"
version: "0.1.0"
# 服务器配置
server:
port: 8000
reload: true
workers: 1
keep_alive_timeout: 5
graceful_timeout: 30
response_format:
enabled: true
exclude_paths:
- "/docs"
# 日志配置
logging:
level: "INFO"
# 任务调度配置
scheduler:
enabled: true
timezone: "UTC"
max_workers: 10
"""
config_file = project_dir / 'conf' / 'config.yaml'
config_file.write_text(config_content, encoding='utf-8')
click.echo("✓ 创建配置文件: conf/config.yaml")
# 创建主应用文件(放在根目录)
app_content = f'''"""主应用文件"""
from myboot.core.application import create_app
app = create_app(name="{name}")
if __name__ == "__main__":
app.run()
'''
main_file = project_dir / 'main.py'
main_file.write_text(app_content, encoding='utf-8')
click.echo("✓ 创建主应用文件: main.py")
# 根据模板创建不同的文件
if template in ['api', 'full']:
# 创建示例路由
api_content = '''"""API 路由示例"""
from myboot.core.decorators import rest_controller, get
@rest_controller('/api')
class HelloController:
"""Hello 控制器"""
@get('/')
def hello(self):
"""Hello World 接口 - GET /api"""
return {"message": "Hello, MyBoot!", "status": "success"}
@get('/health')
def health(self):
"""健康检查接口 - GET /api/health"""
return {"status": "healthy", "service": "running"}
'''
routes_file = project_dir / 'app' / 'api' / 'routes.py'
routes_file.write_text(api_content, encoding='utf-8')
click.echo("✓ 创建示例路由: app/api/routes.py")
if template == 'full':
# 创建示例服务
service_content = '''"""服务层示例"""
from typing import Dict, Any
class UserService:
"""用户服务示例"""
def get_user(self, user_id: int) -> Dict[str, Any]:
"""获取用户信息"""
return {
"id": user_id,
"name": "示例用户",
"email": "user@example.com"
}
def create_user(self, name: str, email: str) -> Dict[str, Any]:
"""创建用户"""
return {
"id": 1,
"name": name,
"email": email,
"status": "created"
}
'''
service_file = project_dir / 'app' / 'service' / 'user_service.py'
service_file.write_text(service_content, encoding='utf-8')
click.echo("✓ 创建示例服务: app/service/user_service.py")
# 创建示例模型
model_content = '''"""数据模型示例"""
from pydantic import BaseModel, ConfigDict, EmailStr
from typing import Optional
class User(BaseModel):
"""用户模型"""
model_config = ConfigDict(
json_schema_extra={
"example": {
"name": "张三",
"email": "zhangsan@example.com"
}
}
)
id: Optional[int] = None
name: str
email: EmailStr
status: str = "active"
'''
model_file = project_dir / 'app' / 'model' / 'user.py'
model_file.write_text(model_content, encoding='utf-8')
click.echo("✓ 创建示例模型: app/model/user.py")
# 创建示例客户端
client_content = '''"""客户端示例"""
from typing import Dict, Any
import requests
class ApiClient:
"""API 客户端示例"""
def __init__(self, base_url: str):
"""初始化客户端"""
self.base_url = base_url
def get(self, endpoint: str) -> Dict[str, Any]:
"""GET 请求"""
response = requests.get(f"{self.base_url}/{endpoint}")
response.raise_for_status()
return response.json()
def post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
"""POST 请求"""
response = requests.post(f"{self.base_url}/{endpoint}", json=data)
response.raise_for_status()
return response.json()
'''
client_file = project_dir / 'app' / 'client' / 'api_client.py'
client_file.write_text(client_content, encoding='utf-8')
click.echo("✓ 创建示例客户端: app/client/api_client.py")
# 创建示例定时任务
job_content = '''"""定时任务示例"""
from myboot.core.decorators import cron
@cron("0 */5 * * * *") # 每5分钟执行一次
def cleanup_task():
"""清理任务示例"""
print("执行清理任务...")
@cron("0 0 * * * *") # 每小时执行一次
def hourly_task():
"""每小时任务示例"""
print("执行每小时任务...")
'''
job_file = project_dir / 'app' / 'jobs' / 'tasks.py'
job_file.write_text(job_content, encoding='utf-8')
click.echo("✓ 创建示例任务: app/jobs/tasks.py")
# 创建 README
readme_content = f"""# {name}
MyBoot 项目
## 快速开始
### 安装依赖
```bash
pip install myboot
```
### 运行应用
```bash
# 使用默认设置启动
python main.py
# 或者使用 myboot 命令(如果已安装)
myboot run
```
### 开发模式
```bash
# 启用自动重载
python main.py --reload
# 或者
myboot dev --reload
```
## 项目结构
```
{name}/
├── main.py # 应用入口
├── pyproject.toml # 项目配置文件
├── .gitignore # Git 忽略文件
├── app/ # 应用代码
│ ├── api/ # API 路由
│ ├── service/ # 业务逻辑层
│ ├── model/ # 数据模型
│ ├── jobs/ # 定时任务
│ └── client/ # 客户端(第三方API调用等)
├── conf/ # 配置文件
│ └── config.yaml # 主配置文件
└── tests/ # 测试代码
```
## 配置说明
配置文件位于 `conf/config.yaml`,支持以下配置:
- **app**: 应用配置(名称、版本等)
- **server**: 服务器配置(端口、工作进程等)
- **logging**: 日志配置
- **scheduler**: 任务调度配置
## 更多信息
- [MyBoot 文档](https://github.com/your-org/myboot)
- [快速开始指南](https://github.com/your-org/myboot/docs)
"""
readme_file = project_dir / 'README.md'
readme_file.write_text(readme_content, encoding='utf-8')
click.echo("✓ 创建 README: README.md")
# 创建 .gitignore
gitignore_content = """# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual Environment
venv/
ENV/
env/
.venv
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Logs
*.log
logs/
# OS
.DS_Store
Thumbs.db
# MyBoot
.pytest_cache/
.coverage
htmlcov/
"""
gitignore_file = project_dir / '.gitignore'
gitignore_file.write_text(gitignore_content, encoding='utf-8')
click.echo("✓ 创建 .gitignore")
# 创建 pyproject.toml
project_name = name.lower().replace(' ', '-').replace('_', '-')
pyproject_content = f"""[project]
name = "{project_name}"
version = "0.1.0"
description = "{name} - MyBoot 项目"
authors = [
{{name = "Your Name", email = "your.email@example.com"}}
]
readme = "README.md"
license = {{text = "MIT"}}
requires-python = ">=3.9"
keywords = ["myboot", "web", "api"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"myboot>=0.1.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"pytest-cov>=4.0",
"pytest-asyncio>=0.21.0",
"black>=23.0",
"isort>=5.12",
"flake8>=6.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["app"]
[tool.black]
line-length = 88
target-version = ['py39', 'py310', 'py311', 'py312']
include = '\\.pyi?$'
[tool.isort]
profile = "black"
line_length = 88
known_first_party = ["app"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--verbose",
]
"""
pyproject_file = project_dir / 'pyproject.toml'
pyproject_file.write_text(pyproject_content, encoding='utf-8')
click.echo("✓ 创建 pyproject.toml")
# 创建 Docker 支持文件(issue #1
dockerfile_content = f"""# 多阶段构建:构建层安装依赖,运行层只保留运行所需内容
FROM python:3.12-slim AS builder
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml ./
# 如有 uv.lock 一并复制可获得可复现构建(无锁文件时此层退化为解析安装)
RUN uv venv /opt/venv && \\
VIRTUAL_ENV=/opt/venv uv pip install --no-cache .
FROM python:3.12-slim
ENV VIRTUAL_ENV=/opt/venv \\
PATH="/opt/venv/bin:$PATH" \\
PYTHONUNBUFFERED=1
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
COPY . .
EXPOSE 8000
# 配置可用环境变量覆盖(__ 为层级分隔符),如 SERVER__PORT=8080
CMD ["python", "main.py"]
"""
dockerfile = project_dir / 'Dockerfile'
dockerfile.write_text(dockerfile_content, encoding='utf-8')
click.echo("✓ 创建 Dockerfile")
dockerignore_content = """__pycache__/
*.py[cod]
.venv/
venv/
.git/
.gitignore
.pytest_cache/
.coverage
htmlcov/
*.log
logs/
.vscode/
.idea/
Dockerfile
docker-compose.yaml
"""
dockerignore_file = project_dir / '.dockerignore'
dockerignore_file.write_text(dockerignore_content, encoding='utf-8')
click.echo("✓ 创建 .dockerignore")
compose_content = f"""services:
{project_name}:
build: .
ports:
- "8000:8000"
environment:
# MyBoot 配置支持环境变量覆盖,__ 为层级分隔符
- LOGGING__LEVEL=INFO
# - SERVER__WORKERS=4
# - CONFIG_FILE=/app/conf/config.prod.yaml
restart: unless-stopped
"""
compose_file = project_dir / 'docker-compose.yaml'
compose_file.write_text(compose_content, encoding='utf-8')
click.echo("✓ 创建 docker-compose.yaml")
click.echo()
click.echo(f"✅ 项目 '{name}' 初始化完成!")
click.echo()
click.echo("下一步:")
click.echo(f" cd {name}")
click.echo(" python main.py")
click.echo()
click.echo("Docker 部署:")
click.echo(f" docker build -t {project_name} .")
click.echo(f" docker run -p 8000:8000 {project_name}")
click.echo()
except Exception as e:
click.echo(f"❌ 初始化失败: {e}", err=True)
import traceback
if '--debug' in sys.argv:
traceback.print_exc()
sys.exit(1)
@cli.command()
def info():
"""显示 MyBoot 信息"""
click.echo("🎯 MyBoot - 类似 Spring Boot 的企业级Python快速开发框架")
click.echo()
click.echo("✨ 主要特性:")
click.echo(" • 快速启动和自动配置")
click.echo(" • 约定优于配置")
click.echo(" • 高性能 Hypercorn 服务器")
click.echo(" • Web API 开发")
click.echo(" • 定时任务调度")
click.echo(" • 日志管理")
click.echo(" • 配置管理")
click.echo()
click.echo("🚀 快速开始:")
click.echo(" myboot init # 初始化新项目")
click.echo(" myboot init --template api # 使用 API 模板")
click.echo(" myboot init --template full # 使用完整模板")
click.echo()
if __name__ == '__main__':
cli()