河南实力网站建设首选建工论坛网
张小明 2026/1/12 9:46:52
河南实力网站建设首选,建工论坛网,373网站怎么做这样的网站,荆州seo推广背景#xff1a;某些框架的“脚手架缺失”
可能对于很多人来说不是难题#xff0c;对于我来说 #xff0c;用习惯了django springboot3 等 遇到fastAPI这种 有工具的 也可以 通过一些标准库 不过总有一些时候 有一些比较轻量的框架没有脚手架pip install fastapi-scaff…背景某些框架的“脚手架缺失”可能对于很多人来说不是难题对于我来说 用习惯了django springboot3 等 遇到fastAPI这种 有工具的 也可以 通过一些标准库 不过总有一些时候 有一些比较轻量的框架没有脚手架pipinstallfastapi-scaff fastapi-scaff new backend-fastapi Starting new project... Done. Now run:1.cdbackend-fastapi2. modify config, eg: db3. pipinstall-r requirements.txt4. python runserver.py ----- More see README.md -----本文商业互吹价值在于 当轻量框架缺乏脚手架一个基于 Python 标准库的通用工具如何优雅地解决项目初始化之痛…真实价值 三天不练手生没事写点东西不至于太生疏对于IT工作者来说。聚焦脚本设计五个关键考量点不关注过程的直接跳过 完整脚本在本文最后1. 零依赖原则只用Python标准库意味着用户无需安装任何额外包真正做到“下载即用”。这个选择背后的考虑是脚手架工具本身应该是轻量级的不应该成为项目的负担。# 仅需三个标准库importosimportsysimportargparse2. 跨平台兼容性处理Windows与Linux/macOS的差异需要特别处理路径分隔符使用os.path.join()自动处理/和\文件编码依次尝试UTF-8、GBK编码读取文件控制台输出避免使用可能在某些终端显示异常的Unicode字符# 智能编码检测defread_file_safely(filename):encodings[utf-8,gbk,utf-16]forencodinginencodings:try:withopen(filename,r,encodingencoding)asf:returnf.read()exceptUnicodeDecodeError:continueraiseValueError(f无法解码文件{filename})3. 帮助信息与用户体验详细的帮助信息是命令行工具的门面。我设计了多级帮助简要用法运行python create_tree.py -h查看示例说明展示最常见的几种用法参数详解每个参数的作用和默认值# 清晰的帮助系统$ python create_tree.py -h 使用方法: create_tree.py[选项]选项: -f, --file FILE 指定tree.txt文件路径(默认: tree.txt)--dry-run 只预览不实际创建 -v, --verbose 显示详细过程 -y, --yes 跳过确认直接创建 -h, --help 显示此帮助信息 示例: create_tree.py --dry-run# 安全预览create_tree.py -v# 详细模式create_tree.py -y# 跳过确认直接创建4. 幂等性保证好的工具应该可以安全地反复运行。我确保了已存在的目录不会被重复创建已有的文件不会被意外覆盖随时可以重新运行以补全缺失的结构# 安全的目录创建os.makedirs(path,exist_okTrue)# 关键exist_okTrue# 安全的文件创建ifnotos.path.exists(filepath):withopen(filepath,w,encodingutf-8)asf:f.write()# 只创建空文件不覆盖内容5. 交互式确认机制为了防止误操作我添加了多层确认解析完成后显示预览用户确认无误后才开始创建支持-y参数跳过确认用于自动化脚本核心技术栈式解析算法脚本的核心是理解tree格式的层级结构。我采用了一种栈式解析算法模拟人类阅读树形结构的方式defparse_tree_structure(content):stack[]# 栈存储(路径, 层级)items[]forlineincontent.splitlines():levelcalculate_indent_level(line)nameextract_item_name(line)is_dirname.endswith(/)# 关键通过栈找到正确的父目录whilestackandstack[-1][1]level:stack.pop()# 构建完整路径parentstack[-1][0]ifstackelsefull_pathos.path.join(parent,name)items.append((full_path,is_dir))ifis_dir:stack.append((full_path,level))returnitems这种算法的优势在于线性时间复杂度只需遍历一次文本内存效率高栈的深度就是目录的最大嵌套层级容错性强即使输入格式略有偏差也能正确处理为什么选择tree格式而非Jinja2模板在技术选型时我仔细考虑过使用Jinja2模板引擎。Jinja2确实强大但最终我选择了更简单的tree格式原因如下Jinja2的过度设计问题# Jinja2方案需要这样fromjinja2importEnvironment,FileSystemLoader envEnvironment(loaderFileSystemLoader(templates))templateenv.get_template(project_structure.j2)outputtemplate.render(project_namemy_project)# 然后还需要解析输出创建文件...Jinja2方案带来的复杂性双重依赖需要Jinja2库和模板文件学习成本团队成员需要了解Jinja2语法维护负担模板文件需要额外维护tree格式的独特优势相比之下tree格式tree命令的输出具有以下优势1. 人类可读与机器可读的统一my-project/ ├── src/ │ └── main.py └── README.md这种格式既能让开发者直观理解结构又能被脚本精确解析。2. 广泛的工具支持Linux/macOS自带tree命令VS Code有目录树插件许多IDE可以导出目录结构3. 零学习成本任何开发者都能立即理解无需学习新的模板语法。4. 版本控制友好纯文本格式diff清晰合并冲突容易解决。实用主义的选择考虑到脚手架工具的核心需求是创建目录和空文件而不是生成复杂的内容简单的tree格式完全够用。这正是“技术内敛”理念的体现——用最简单的技术解决核心问题。使用方式三步完成项目初始化第一步创建结构蓝图使用任何工具生成项目结构描述# Linux/macOStree -I__pycache__|node_modulestree.txt# 或手动创建vimtree.txt第二步预览验证# 安全第一先预览python create_tree.py --dry-run -v# 输出示例解析结果预览 vue-fastapi-demo(目录) backend(目录) .env(文件) requirements.txt(文件) models(目录) __init__.py(文件) user.py(文件)...第三步一键创建# 确认无误后创建python create_tree.py# 或跳过确认直接创建适合CI/CDpython create_tree.py -y对症易懂炫技则空技术内敛体验外显。这十六个字不仅总结了这次工具开发的体会也指引着我在技术道路上的每一次选择。完整脚本#!/usr/bin/env python3 目录结构创建工具 - 稳定解析版 (修正) 专注解决 tree.txt 解析问题确保创建正确的目录结构 importosimportsysimportargparsedefparse_tree_structure(content): 核心解析函数正确解析 tree 命令的输出格式 返回列表每个元素为 (完整路径, 是否是目录) lines[line.rstrip(\n\r)forlineincontent.split(\n)]items[]ifnotlinesornotlines[0].strip():returnitems# 1. 处理根目录第一行root_linelines[0].strip()root_nameroot_line.rstrip(/)items.append((root_name,True))# 栈记录当前路径和层级[(路径, 层级), ...]stack[(root_name,0)]forline_num,lineinenumerate(lines[1:],start2):lineline.rstrip()ifnotline.strip():continue# 2. 计算当前行的缩进层级# tree 格式每层用 4 个字符indent0i0whileilen(line):charline[i]ifcharin \t:indent1i1elifi1len(line)andline[i:i2]in(│ ,├─,└─):# tree图形字符组合indent2i2elifcharin(│,├,└):# 单独的tree图形字符indent1i1else:break# 3. 清理行内容提取项目名称clean_lineline[i:].lstrip(─ )# 移除可能剩余的 ──# 如果有── 前缀移除它ifclean_line.startswith(── ):clean_lineclean_line[3:]# 移除注释if#inclean_line:clean_lineclean_line.split(#)[0].strip()clean_lineclean_line.strip()ifnotclean_line:continue# 4. 判断是目录还是文件item_nameclean_line is_dirFalseifitem_name.endswith(/):is_dirTrueitem_nameitem_name.rstrip(/)else:# 通过常见规则判断basenameitem_name# 判断条件优先级# 1. 以点开头的特殊文件如 .envifbasename.startswith(.):is_dirFalse# 2. 有常见扩展名的是文件elifany(basename.endswith(ext)forextin[.py,.txt,.js,.ts,.vue,.html,.json,.css,.md,.config.ts]):is_dirFalse# 3. 特殊文件名elifbasenamein[__init__,package,vite.config]:is_dirFalse# 4. 没有扩展名且不是特殊文件的可能是目录elif.notinbasename:is_dirTrue# 5. 其他情况默认是文件else:is_dirFalse# 5. 计算当前层级tree 格式通常每4字符一级levelindent//4# 6. 根据层级找到正确的父目录whilestackandstack[-1][1]level:stack.pop()ifstack:parent_path,_stack[-1]full_pathos.path.join(parent_path,item_name)else:full_pathos.path.join(root_name,item_name)items.append((full_path,is_dir))# 7. 如果是目录压入栈ifis_dir:stack.append((full_path,level))returnitemsdefpreview_structure(items,show_numbersTrue):预览解析结果使用清晰的层级显示ifnotitems:print(未解析到任何项目)return0,0print(\n*60)print(解析结果预览)print(*60)# 为每个项目生成编号item_map{}foridx,(path,is_dir)inenumerate(items):item_map[idx](path,is_dir)root_nameitems[0][0]ifitemselseforidx,(path,is_dir)inenumerate(items):# 计算缩进层级ifidx0:level0else:# 通过计算路径中分隔符的数量来确定层级levelpath.count(os.sep)-root_name.count(os.sep)indent *level# Windows兼容性如果控制台不支持Unicode使用简单字符try:# 尝试输出Unicode字符iconifis_direlsesys.stdout.write()# 测试Unicode支持except:icon[DIR]ifis_direlse[FILE]type_desc目录ifis_direlse文件# 显示名称nameos.path.basename(path)iflevel0orpath!root_nameelsepath# 显示编号如果需要ifshow_numbers:# 生成层级编号A, A.1, A.1.1, B, B.1 等ifidx0:num_strROOTelse:# 简单数字编号num_strf[{idx:2d}]print(f{indent}{num_str}{icon}{name}({type_desc}))else:print(f{indent}{icon}{name}({type_desc}))print(*60)# 统计信息dirssum(1for_,is_dirinitemsifis_dir)filessum(1for_,is_dirinitemsifnotis_dir)print(f\n 统计信息:)print(f 根目录:{root_name})print(f 目录数:{dirs})print(f 文件数:{files})print(f 总项目:{len(items)})# 特别检查关键结构backend_pathos.path.join(root_name,backend)frontend_pathos.path.join(root_name,frontend)backend_existsany(pathbackend_pathforpath,_initems)frontend_existsany(pathfrontend_pathforpath,_initems)ifbackend_existsandfrontend_exists:print(f\n✅ 关键结构检测:)print(f -{backend_path}/)print(f -{frontend_path}/)print( (backend 和 frontend 为平级目录))elifbackend_existsorfrontend_exists:print(f\n⚠️ 注意: 只检测到部分结构)returndirs,filesdefget_user_confirmation(prompt,default_noTrue):获取用户确认try:options (y/N)ifdefault_noelse (Y/n)responseinput(f{prompt}{options}: ).strip().lower()ifdefault_no:returnresponsein(y,yes,是,1)else:returnresponsenotin(n,no,否,0)except(KeyboardInterrupt,EOFError):returnFalsedefcreate_structure(items,verboseFalse,dry_runFalse):创建目录和文件结构created[]existing[]errors[]print(\n开始创建结构...( (模拟运行)ifdry_runelse))forpath,is_dirinitems:try:ifis_dir:# 创建目录ifdry_run:print(f[模拟] 创建目录:{path})else:ifnotos.path.exists(path):os.makedirs(path,exist_okTrue)ifverbose:print(f✓ 创建目录:{path})created.append((目录,path))else:ifverbose:print(f✓ 目录已存在:{path})existing.append((目录,path))else:# 创建文件dir_pathos.path.dirname(path)# 先确保父目录存在ifdir_pathandnotos.path.exists(dir_path)andnotdry_run:os.makedirs(dir_path,exist_okTrue)# 创建文件ifdry_run:print(f[模拟] 创建文件:{path})else:ifnotos.path.exists(path)oros.path.getsize(path)0:withopen(path,w,encodingutf-8)asf:f.write()# 创建空文件ifverbose:print(f✓ 创建文件:{path})created.append((文件,path))else:ifverbose:print(f✓ 文件已存在:{path})existing.append((文件,path))exceptExceptionase:error_msgf创建失败{path}:{str(e)}print(f✗{error_msg})errors.append(error_msg)returncreated,existing,errorsdefmain():# 设置参数解析器parserargparse.ArgumentParser(description从 tree.txt 创建目录结构 - 稳定解析版,add_helpFalse)parser.add_argument(-f,--file,defaulttree.txt,helptree.txt 文件路径 (默认: tree.txt))parser.add_argument(-v,--verbose,actionstore_true,help显示详细过程)parser.add_argument(--dry-run,actionstore_true,help模拟运行只预览不实际创建)parser.add_argument(-y,--yes,actionstore_true,help跳过确认直接创建)parser.add_argument(--no-numbers,actionstore_true,help不显示项目编号)parser.add_argument(-h,--help,actionstore_true,help显示帮助信息)# 解析参数argsparser.parse_args()ifargs.help:print( 目录结构创建工具 - 稳定解析版 使用方法: python tree_creator.py [选项] 选项: -f, --file FILE 指定 tree.txt 文件 (默认: tree.txt) -v, --verbose 显示详细过程 --dry-run 模拟运行只预览不创建 -y, --yes 跳过确认直接创建 --no-numbers 不显示项目编号 -h, --help 显示此帮助信息 示例: python tree_creator.py --dry-run # 只预览 python tree_creator.py -v # 详细模式 python tree_creator.py -y # 直接创建 )return0# 检查文件是否存在ifnotos.path.exists(args.file):print(f错误: 找不到文件 {args.file})print(f请确保文件存在或使用 -f 参数指定其他文件)return1print(f 读取文件:{args.file})# 读取文件try:withopen(args.file,r,encodingutf-8)asf:contentf.read()exceptUnicodeDecodeError:try:withopen(args.file,r,encodinggbk)asf:contentf.read()except:print(错误: 无法读取文件请检查文件编码)return1exceptExceptionase:print(f错误: 读取文件失败 -{e})return1ifnotcontent.strip():print(错误: 文件为空)return1print(f✅ 文件读取成功 ({len(content)}字符))# 解析结构print(\n 正在解析 tree 结构...)itemsparse_tree_structure(content)ifnotitems:print(错误: 无法解析出任何项目)print(请检查 tree.txt 格式是否正确)return1# 预览解析结果print(\n*60)print(第一步解析结果确认)print(*60)dirs,filespreview_structure(items,notargs.no_numbers)# 检查解析质量iffiles0anddirs5:print(\n⚠️ 警告: 解析出大量目录但没有文件)print(可能是解析逻辑将文件误判为目录)ifnotargs.yes:responseinput(是否继续? (y/N): ).lower()ifresponsenotin(y,yes):print(操作取消)return0# 如果是dry-run模式到此结束ifargs.dry_run:print(\n*60)print(模拟运行完成 - 未实际创建任何文件)print(*60)return0# 第二步确认是否继续print(\n*60)print(第二步创建确认)print(*60)ifnotargs.yes:print(f\n将在当前目录创建{len(items)}个项目:)print(f -{dirs}个目录)print(f -{files}个文件)print(f\n根目录:{items[0][0]})ifnotget_user_confirmation(是否创建上述目录结构,default_noTrue):print(操作取消)return0# 第三步创建结构print(\n*60)print(第三步创建目录结构)print(*60)created,existing,errorscreate_structure(items,verboseargs.verbose,dry_runFalse)# 第四步结果显示print(\n*60)print(第四步操作结果)print(*60)ifcreated:print(f\n✅ 成功创建{len(created)}个项目:)foritem_type,pathincreated[:10]:# 最多显示10个print(f{item_type}:{os.path.relpath(path)})iflen(created)10:print(f ... 还有{len(created)-10}个项目)ifexisting:print(f\n 跳过{len(existing)}个已存在的项目)iferrors:print(f\n❌ 遇到{len(errors)}个错误:)forerrorinerrors[:5]:# 最多显示5个错误print(f{error})print(f\n 总计:{len(created)len(existing)}个项目已就绪)# 最终检查root_pathitems[0][0]ifos.path.exists(root_path):print(f\n 根目录位置:{os.path.abspath(root_path)})print(*60)return1iferrorselse0if__name____main__:try:sys.exit(main())exceptKeyboardInterrupt:print(\n\n⏹️ 操作被用户中断)sys.exit(130)exceptExceptionase:print(f\n 程序执行出错:{e})importtraceback traceback.print_exc()sys.exit(1)