持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
大家好~我是小方,欢迎大家关注笋货测试笔记体完记得俾个like呀
回顾
上一期,我们完成了权限控制的引入,数据工厂上的一些操作、查看、数据权限有了更好的把控,接着我们今天来进行项目的初始化操作
需求
我们的设想是数据工厂的脚本与平台进行解耦,在平台上可以创建多个脚本项目,然后脚本项目的所有代码都存放在git上,通过配置项目相关的信息,拉取git上的项目,再···可以回顾下之前的流程图~
那我们要怎么初始化项目呢?脚本项目都存放在git上,那肯定是git命令呀~
引入subprocess模块
git命令其实就是shell命令,那Python是如何执行shell命令呢?
-system方法
这里最简单的方法就是直接调用os.system
,执行shell命令
import os
os.system('ls')
但是这里会存在一个问题,无法获取shell命令的输出,也无法进行输入,没有超时设置,如果命令挂死,直接导致当前进程挂死
- subprocess模块
subprocess模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值,简单来说就是,帮你启动新的进程来执行shell命令,并不会对当前进程造成影响
这里最常见的用法就是subprocess.run
,先来看一下源码,可以看到run
方法内部也调用了Popen
说几个比较常用的参数:
- args: 表示要执行的命令,必须为字符串或者字符串列表
- timeout: 设置命令超时时间,如果超时,子进程就会被杀死,并抛出
TimeoutExpired
异常 - check: 如果设置为True,进程退出码不为0,则抛出
CalledProcessError
异常
先来个demo试试
- 执行成功
ret_success = subprocess.run('ls', shell=True, timeout=1, check=True)
print(f'输出成功对象:{ret_success}')
print(ret_success.returncode)
- 执行失败
ret_error = subprocess.run('cat a.txt', shell=True, check=True, timeout=1)
print(f'输出失败对象:{ret_error}')
print(ret_error.returncode)
- 执行超时
ret_timeout = subprocess.run(['sleep 2s', 'ls'], timeout=1, shell=True, check=True)
print(f'输出超时对象:{ret_timeout}')
print(ret_timeout.returncode)
subprocess.Popen
方法,Popen是subprocess的核心功能,子进程的创建、管理都由它来处理,刚上面也看到了subprocess.run
底层实现是由Popen类实现的,两者的用法大致相同,区别点是,subprocess.Popen
是异步,subprocess.run
是同步,执行下面这两段代码,你就知道差异了如果我们需要等子进程执行完才进行下一步操作,直接使用wait
即可,这里先不介绍subprocess.Popen
方法啦,有兴趣的同学可自行了解
sub = subprocess.Popen('sleep 5s;echo hello,this is async!!!', shell=True)
print('不用等待')
# 等待子进程终止
# sub.wait()
ret_success = subprocess.run('sleep 1s;echo hello,this is sync!!!', shell=True, timeout=2, check=True)
接下来我们来进行简单封装
新建app/utils/cmd_utils.py
,新增如下代码,代码比较简单,大家自己体会一下~
import subprocess
from app.utils.logger import Log
class CmdUtils(object):
log = Log("CmdUtils")
@staticmethod
def cmd(cmd_str: str, timeout: int = 10):
"""
执行shell命令
:param cmd_str: 字符串命令
:param timeout: 超时时间
:return:
"""
try:
subprocess.run(cmd_str, shell=True, check=True, timeout=timeout)
except Exception as e:
CmdUtils.log.error(f"{cmd_str} 命令执行失败, 错误信息: {str(e)}")
raise Exception(f"命令执行失败!!! ")
git clone逻辑
上面已经编写了CmdUtils
工具类,那我们来编写核心逻辑吧,先来回顾一下git的clone命令
git clone url
# 拉取指定分支代码
git clone -b branch url
# 通过账号密码拉取
git clone http://username:password@url
# 通过账号密码拉取指定分支
git clone -b branch http://username:password@url
新建app/core/git.py
,添加如下代码:
from config import FilePath
from app.utils.logger import Log
from app.utils.cmd_utils import CmdUtils
from urllib.parse import quote
class Git(object):
log = Log("git")
@staticmethod
def git_url(url, user, pwd):
git_url_list = url.split('/')
git_url_list[2] = f"{quote(user)}:{pwd}@" + git_url_list[2]
return '/'.join(git_url_list)
@staticmethod
def git_clone_http(git_branch, git_url, user, password):
"""
http克隆
:param git_branch: 分支名
:param git_url: 代码地址
:param user: git账号
:param password: git密码
:return:
"""
Git.log.info("开始克隆, 方式为http")
command_str = f"cd {FilePath.BASE_DIR}\n" \
f"git clone -b {git_branch} {Git.git_url(git_url, user, password)}\n"
CmdUtils.cmd(command_str)
Git.log.info("克隆结束")
这里要注意的是,我们拉下来的项目统一放在项目的根目录;邮箱中的@
要使用%40
代替,这里用了quote
方法进行编码
测试一下代码=。=
总结
今天讲了subprocess
模块相关的用法,目前就编写了http克隆的逻辑,还有ssh克隆的逻辑暂未编写,下一遍我们来讲讲ssh克隆代码,大家敬请期待~
- 项目地址
- 今日代码提交
- 后端:26d3c7b