在我的职业生涯中,我编写、使用和看过很多随机脚本。有些人需要半自动化任务,所以他们诞生了。过了一会儿,它们变得越来越大。他们一生中可能会多次易手。我经常希望这些脚本有更多的命令行工具的感觉。但是,将一次性脚本的质量水平真正提高到合适的工具有多难?事实证明这在Python中并不难做到。构建框架脚本在本文中,我将从一小段Python代码开始。我将把它实现到脚手架模块中,并使用click库扩展它以接受命令行参数。#!/usr/bin/pythonfromglobimportglobfromos.pathimportjoin,basenamefromshutilimportmovefromdatetimeimportdatetimefromosimportlink,unlinkLATEST='latest.txt'ARCHIVE='/Users/mark/archive'INCOMING='/用户/mark/incoming'TPATTERN='%Y-%m-%d'deftransmogrify_filename(fname):bname=basename(fname)ts=datetime.now().strftime(TPATTERN)返回'-'.join([ts,bname])defset_current_latest(file):latest=join(ARCHIVE,LATEST)try:unlink(latest)除了:passlink(file,latest)defrotate_file(source):target=join(ARCHIVE,transmogrify_filename(source))move(source,target)set_current_latest(target)defrotoscope():file_no=0folder=join(INCOMING,'*.txt')打印(f'Lookingin{INCOMING}')forfileinglob(folder):rotate_file(file)print(f'Rotated:{file}')file_no=file_no+1print(f'Totalfilesrotated:{file_no}')if__name__=='__main__':print('Thisisrotoscope0.4.1.Bleep,bloop.')rotoscope()本文所有没有插入的代码示例都可以在https://codeberg.org/ofosos/rotoscope中找到代码的特定版本本存储库中的每个提交都描述了本文操作中的一些有意义的步骤。这段代码做了几件事:检查INCOMING指定的路径中是否存在文本文件(如果存在)、使用当前时间戳创建一个新文件名并将其移动到ARCHIVE删除当前的ARCHIVE/latest.txt链接和创建一个指向您刚刚添加的文件的新链接作为示例,这很简单,但可以让您了解该过程。使用Pyscaffold创建应用程序首先,您需要安装Scaffold、click和toxPython库。$python3-mpipinstallscaffold点击tox安装好scaffold后,切换到示例rotoscope项目所在目录,执行以下命令:$putuprotoscope-protoscope\--force--no-skeleton-nrotoscope\-d'四处移动一些文件。-lGLWT\-uhttp://codeberg.org/ofosos/rotoscope\--save-config--pre-commit--markdownPyscaffold将重写我的README.md,所以从Git恢复它:$gitcheckoutREADME.mdPyscaffold在文档中解释了如何搭建一个完整的示例项目,这里就不多说了,大家可以稍后探索。除此之外,Pyscaffold还可以为您的项目提供持续集成(CI)模板:打包:您的项目现在已启用PyPi,因此您可以将其上传到存储库并从那里安装。文档:您的项目现在有一个完整的文档文件夹层次结构,基于Sphinx,包括一个readthedocs.org构建器。测试:您的项目现在可以与tox一起使用,测试文件夹包含运行基于pytest的测试所需的所有样板。依赖管理:打包和测试基础设施都需要一种管理依赖的方法。setup.cfg文件解决了这个问题,它包含所有依赖项。Pre-commithooks:包括Python源代码格式化工具black和Python风格检查器flake8。查看test文件夹,在项目目录下运行tox命令,立即输出错误:打包基础设施找不到依赖库。现在创建一个Git标签(例如v0.2),此工具将其识别为可安装版本。查看自动生成的setup.cfg,并在提交更改之前根据需要对其进行编辑。对于这个例子,你可以修改LICENSE和项目描述,将这些更改添加到Git的暂存区,我不得不禁用预提交挂钩,然后提交它们。否则,我会遇到错误,因为Python样式检查器flake8会抱怨格式错误。$PRE_COMMIT_ALLOW_NO_CONFIG=1gitcommit如果这个脚本有一个用户可以从命令行调用的入口点就好了。现在,您只能通过找到.py文件并手动执行来运行它。幸运的是,Python的打包基础结构有一个很好的“罐装”方式来使配置更改变得容易。在setup.cfg的options.entry_points部分添加以下内容:你可以使用roto命令。就是这样,您可以免费从Pyscaffold获得所有打包、测试和文档设置。您还可以获得一个预提交挂钩,以确保(大部分)您根据设置的规则进行提交。CLIinstrumentation一些值现在被硬编码到脚本中,作为命令参数更方便。例如,最好将INCOMING常量作为命令行参数。首先导入click库,使用Click提供的命令装饰器装饰rotoscope()方法,添加一个Click传递给rotoscope函数的参数。Click提供了一组验证器,因此在参数中添加一个路径验证器。Click还方便地将嵌入式字符串用作命令行文档的一部分。所以你最终得到以下方法签名:@click.command()@click.argument('incoming',type=click.Path(exists=True))defrotoscope(incoming):"""Rotoscope0.4-Bleep,bloop.移动文件的简单示例。"""主要功能将调用rotoscope(),现在是一个点击命令,不需要传递任何参数。选项也可以使用环境变量自动填充。例如,将ARCHIVE常量更改为一个选项:@click.option('archive','--archive',default='/Users/mark/archive',envvar='ROTO_ARCHIVE',type=click.Path())使用相同的路径验证器。这一次,让Click填充环境变量,如果环境变量没有提供任何内容,则默认为旧常量的值。Click可以做更多的事情,它有彩色的控制台输出、提示和子命令,可以让你构建复杂的CLI工具。浏览Click文档以了解更多信息。现在添加一些测试。TestingClick提供了一些使用CLI运行器运行端到端测试的建议。你可以用它来实现一个完整的测试(在示例项目中,测试在tests文件夹中。)测试位于测试类的一个方法中。大多数约定与我在其他Python项目中使用的约定非常接近,但有一些细节,因为rotoscope使用点击。在测试方法中,我创建了一个CliRunner。测试使用它在隔离文件系统上运行此命令。然后测试在隔离文件系统中创建传入和存档目录以及一个虚拟传入/test.txt文件,它调用CliRunner就像调用命令行应用程序一样。运行完成后,测试会检查隔离的文件系统并验证incoming为空且archive包含两个文件(最新链接和存档文件)。fromosimportlistdir,mkdirfromclick.testingimportCliRunnerfromrotoscope.rotoscopeimportrotoscopeclassTestRotoscope:deftest_roto_good(self,tmp_path):runner=CliRunner()withrunner.isolated_filesystem(temp_dir=tmp_path)astd:mkdir"("传入)("archive")withopen("incoming/test.txt","w")asf:f.write("hello")result=runner.invoke(rotoscope,["incoming","--archive","archive"])assertresult.exit_code==0print(td)incoming_f=listdir("incoming")archive_f=listdir("archive")assertlen(incoming_f)==0assertlen(archive_f)==2to要在控制台上执行这些测试,请在项目的根目录中运行tox。在执行测试期间,我发现了代码中的错误。当我进行点击转换时,rotoscope只是取消链接最新的文件,无论它是否存在。测试在一个新的文件系统(不是我的主文件夹)上开始,但很快就失败了。我可以通过在隔离良好且自动化的测试环境中运行来防止此错误。这将避免很多“它在我的机器上运行良好”的问题。构建骨架脚本和模块本文到此结束,我们可以使用脚手架和点击来完成一些高级操作。有很多方法可以升级普通的Python脚本,甚至可以将您的简单实用程序变成功能齐全的CLI工具。
