如何通过Python脚本生成带动态元数据的复杂项目模板文件
最优解决方案:使用模板引擎
这是目前项目脚手架工具的通用实现方案,同时解决了你提到的两种思路的缺陷:既可以将模板文件独立存放、单独维护,又支持动态变量、甚至复杂逻辑的填充,完全适配多文件、大体积模板的场景。
方案1:Python标准库 string.Template(无第三方依赖,适合简单场景)
不需要额外安装依赖,足以覆盖大多数基础模板生成需求:
- 第一步:在你的预存模板文件中,将需要动态替换的元数据用
$变量名格式占位即可,示例setup.py模板内容:
from setuptools import setup, find_packages setup( name="$project_name", version="$version", packages=find_packages(), install_requires=$dependencies, license="$license_type", description="$description" )
- 第二步:读取模板、替换变量后写入目标路径,代码示例:
import os from string import Template from shutil import copy # 预存模板的文件夹路径 TEMPLATE_DIR = "./py_project_templates" # 用户传入的动态元数据 context = { "project_name": "helloworld", "version": "0.1.0", "dependencies": ["requests>=2.28.0", "loguru>=0.7.0"], "license_type": "MIT", "description": "这是一个示例项目" } # 生成项目的目标路径 TARGET_DIR = "./generated_project" # 遍历模板文件夹所有文件 for root, _, files in os.walk(TEMPLATE_DIR): # 计算目标目录的相对路径,保持目录结构一致 rel_path = os.path.relpath(root, TEMPLATE_DIR) target_root = os.path.join(TARGET_DIR, rel_path) os.makedirs(target_root, exist_ok=True) for file_name in files: src_path = os.path.join(root, file_name) dst_path = os.path.join(target_root, file_name) # 二进制文件(比如图片、字体等)不需要渲染,直接复制 if file_name.endswith((".png", ".jpg", ".ico")): copy(src_path, dst_path) continue # 文本文件走模板替换逻辑 with open(src_path, "r", encoding="utf-8") as f: template = Template(f.read()) with open(dst_path, "w", encoding="utf-8") as f: f.write(template.substitute(context))
注:如果模板中需要保留原义的
$字符,写$$即可完成转义。
方案2:Jinja2模板引擎(适合复杂模板场景)
如果你的模板需要支持条件判断、循环、过滤器等复杂逻辑(比如根据用户选择是否生成Dockerfile、动态生成依赖列表),可以使用Python生态通用的Jinja2模板引擎:
- 安装依赖:
pip install jinja2
- 模板文件使用Jinja2语法占位,示例
requirements.txt模板:
pytest>=7.4.0 {% if use_requests %} requests>=2.31.0 {% endif %} {% if use_pydantic %} pydantic>=2.0.0 {% endif %}
示例setup.py模板:
from setuptools import setup, find_packages setup( name="{{ project_name }}", version="{{ version }}", packages=find_packages(), install_requires=[ {% for dep in dependencies %} "{{ dep }}", {% endfor %} ], {% if license_type == "MIT" %} license="MIT", classifiers=["License :: OSI Approved :: MIT License"] {% endif %} )
- 渲染代码示例:
import os from shutil import copy from jinja2 import Environment, FileSystemLoader TEMPLATE_DIR = "./py_project_templates" env = Environment(loader=FileSystemLoader(TEMPLATE_DIR)) context = { "project_name": "helloworld", "version": "0.1.0", "dependencies": ["requests>=2.28.0", "loguru>=0.7.0"], "license_type": "MIT", "use_requests": True, "use_pydantic": False } TARGET_DIR = "./generated_project" for root, _, files in os.walk(TEMPLATE_DIR): rel_path = os.path.relpath(root, TEMPLATE_DIR) target_root = os.path.join(TARGET_DIR, rel_path) os.makedirs(target_root, exist_ok=True) for file_name in files: src_path = os.path.join(root, file_name) dst_path = os.path.join(target_root, file_name) # 二进制文件直接复制 if file_name.endswith((".png", ".jpg", ".ico")): copy(src_path, dst_path) continue template = env.get_template(os.path.join(rel_path, file_name)) with open(dst_path, "w", encoding="utf-8") as f: f.write(template.render(context))
内容的提问来源于stack exchange,提问作者God-status
相关产品推荐
相关产品推荐

