腾讯云客户端命令行工具tccli主流程解析
文章目录
- 环境信息
- 命令行文件
- 入口函数
- command.CLICommand 解析
- command_map 对象
- parser 对象
- 实际的Action函数,以`doDescribeTopics`为例
- 调用示例
- `format_output.py`文件
- Response类是其他格式的基类
- io.TextIOWrapper效果演示
- 问题
- 支持的输出格式
- 自定义解析格式
环境信息
- python: 3.8.5
- tccli: 3.0.666.1
只列出关键代码
命令行文件
from tccli.main import main
if __name__ == '__main__':sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])sys.exit(main())
入口函数
main.py
文件
def main():cli_version = __version__.rsplit(".", 1)[0]if sdkVersion < cli_version:sys.stderr.write("Version is inconsistent, python sdk version:%s tccli version:%s" % (sdkVersion, __version__))returntry:log.info("tccli %s" % ' '.join(sys.argv[1:]))CLICommand()()
main
函数主要是调用了CLICommand()()
。
command.CLICommand 解析
class CLICommand(BaseCommand):def __init__(self):self._command_map = Noneself._argument_map = Nonesuper(CLICommand, self).__init__()def __call__(self, args=None):if args is None:# 将所有参数存入至 args args = sys.argv[1:]if len(args) > 0 and args[0] == "as":args[0] = "autoscaling"command_map = self._get_command_map()parser = self._create_parser(command_map)# 解决接口版本(--version) 和 tccli版本(--version)字段冲突self._handle_service_version_argumnet(args, parser)self._handle_warning(args)parsed_args, remaining = parser.parse_known_args(args)return command_map[parsed_args.command](remaining, parsed_args)
command_map 对象
command_map
是一个OrderedDict
,当service
为dnspod
时,结果如下。说白了,就是{"func_name": func}
这样的字典,通过字符串方便找到真正的同名的函数。
下面的示例,就是 dnspod
这个service
下的 Action
。
使用tccli dnspod help
就能看到。
OrderedDict([('CreateDomain', <tccli.command.ActionCommand object at 0x7f6d7f0c16a0>), ('CreateDomainAlias', <tccli.command.ActionCommand object at 0x7f6d7f0a5370>), ('CreateDomainBatch', <tccli.command.ActionCommand object at 0x7f6d7f275970>), ('CreateDomainGroup', <tccli.command.ActionCommand object at 0x7f6d7eae5310>), ('CreateRecord', <tccli.command.ActionCommand object at 0x7f6d7f283160>), ('CreateRecordBatch', <tccli.command.ActionCommand object at 0x7f6d7e7de430>), ('DeleteDomain', <tccli.command.ActionCommand object at 0x7f6d7f2831c0>), ...])
parser 对象
这个实际上是CLIArgParser
类的实例。
CLIArgParser
来自argparser.py
文件
class CLIArgParser(BaseArgParser):
其父类BaseArgParser
继承自argparse.ArgumentParser
class BaseArgParser(argparse.ArgumentParser):
parse_known_args()
方法在BaseArgParser
重写了。
详细的不看了,这里直接看下相关的结果。
在CLICommand
的__call__
方法下添加打印代码。
print("parsed_args", parsed_args)
print("remaining", remaining)
sys.stdout.flush()
输出示例1
# tccli cls ModifyTopic help --detail
parsed_args Namespace(cli_input_json=None, cli_unfold_argument=False, command='cls', detail=True, endpoint=None, filter=None, generate_cli_skeleton=None, https_proxy=None, output=None, profile=None, region=None, role_arn=None, role_session_name=None, secretId=None, secretKey=None, service_version=None, timeout=None, token=None, use_cvm_role=False, waiter=None, warning=False)
remaining ['ModifyTopic', 'help']
示例2
# tccli cls DescribeTopics --Filters '[ {"Key": "topicName", "Values": [ "test-log" ] }]'
parsed_args Namespace(cli_input_json=None, cli_unfold_argument=False, command='cls', detail=False, endpoint=None, filter=None, generate_cli_skeleton=None, https_proxy=None, output=None, profile=None, region=None, role_arn=None, role_session_name=None, secretId=None, secretKey=None, service_version=None, timeout=None, token=None, use_cvm_role=False, waiter=None, warning=False)
remaining ['DescribeTopics', '--Filters', '[ {"Key": "topicName", "Values": [ "test-log" ] }]']
实际的Action函数,以doDescribeTopics
为例
此函数定义在cls/cls_client.py
文件当中。
按照如下调用来看,其把
- remaining 当作 args 传入
- parsed_args 当作 parsed_globals 传入
def doDescribeTopics(args, parsed_globals):...model = models.DescribeTopicsRequest()model.from_json_string(json.dumps(args))start_time = time.time()while True:rsp = client.DescribeTopics(model)result = rsp.to_json_string()...
这个也容易理解,一部分是“全局参数”,一部分是“Action函数”的参数。
其将“全局参数”转化为g_param
, 打印一下。结果如下:
g_param {'filter': None, 'output': 'json', 'secretId': 'xxxxxxxxx', 'secretKey': 'xxxxxxxxxxx', 'token': None, 'role_arn': None, 'role_session_name': None, 'use_cvm_role': False, 'detail': False, 'profile': 'default', 'region': 'ap-beijing', 'endpoint': 'cls.tencentcloudapi.com', 'timeout': None, 'generate_cli_skeleton': None, 'cli_input_json': None, 'cli_unfold_argument': False, 'https_proxy': None, 'warning': False, 'waiter': None, 'command': 'cls', 'service_version': None, 'version': 'v20201016'}
model
对象是v20201016/models.py
中DescribeTopicsRequest
的实例。其主要是实现了_deserialize
方法。
class DescribeTopicsRequest(AbstractModel):
from_json_string
方法来自abstract_model.py
中的AbstractModel
类。是DescribeTopicsRequest
的父类。
而在from_json_string
方法中又调用了上面的_deserialize
方法。
到此,告一段落吧。
调用示例
import sys
from tccli.main import mainargs = ['tccli', 'cls', 'DescribeTopics','--Filters','[ {"Key": "topicName", "Values": [ "test-log" ] }]']sys.argv = args
main()
如上代码,可以在自己的脚本中,调用tccli
中的函数了。
当然,由于其main
总是将结果输出至stdout
中,所以想将其结果保存至变量中的话,还需要自己写函数处理一下。这部分可以参考 python 将函数标准输出存至变量问题。
format_output.py
文件
此文件中定义了输出格式相关的操作。
Response类是其他格式的基类
__call__
方法内调用的self._format_response()
方法由各子类实现,如JSONResult
, TableResult
,TextResult
。
方法中,还有设置stream
的代码。
def __call__(self, command, response, stream=None):if stream is None:if not six.PY2:sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')stream = sys.stdouttry:self._format_response(command, response, stream)except IOError as e:passfinally:self._flush_stream(stream)
- 首先判断有没有
stream
传进来, 在当前的不修改源码的情况下,是没办法传stream
参数的。所以一定会进入判断内的逻辑 - 在
python
版本不是2.x
的情况下,将sys.stdout
指向io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')
的结果。- 这一步,主要是增加了buffer效果(同时修改了
encoding
),写入数据不会立马看到,示例如下
- 这一步,主要是增加了buffer效果(同时修改了
- 然后将
stream
指向sys.stdout
,
io.TextIOWrapper效果演示
In [51]: sys.stdout =old_outIn [52]: print("hahha")
hahhaIn [53]: print("hhehe")
hheheIn [54]: sys.stdout = io.TextIOWrapper(sys.stdout.buffer,...: encoding='utf-8')
In [55]: print("hahha")
In [56]: print("haha")
In [57]: sys.stdout.flush()hahhahaha
使用io.TextIOWrapper
之后,print
的结果不会马上显示,而是需要调用sys.stdout.flush()
函数才能看到。
问题
在诸如doDescribeRecordList
等函数中,如果使用了print()
语句,那么需要马上执行sys.stdout.flush()
方法,才能看到输出,否则最后只有相应的函数结果执行输出,而看不自己添加的print()
效果,这是为什么呢?
这个原因是因为后来FormatOutput.output
函数,改变了io
流,新的io
流与之前是不一样的了。验证代码如下:
import sys
import io
import timeold_stdout = sys.stdout
sys.stdout = io.TextIOWrapper(old_stdout.buffer, encoding='utf-8')
print("I'm sys.stdout.")# 没有这一行,脚本就会报错,暂时不明白
old_stdout = sys.stdout
sys.stdout = io.TextIOWrapper(old_stdout.buffer, encoding='utf-8')
print(time.ctime(), "heheh")
print(time.ctime(),"hahah")
print("over")
运行,上面的这个脚本,是不会看到I'm sys.stdout.
的,因为前后两次的io
流已经不一样了。
但是实际的tccli
中,虽然也是设置了两次sys.stdout,但是并没有像上面那样使用old_stdout = sys.stdout
这种语句,其脚本中使用了reload(sys)
。
main.py
脚本中设置一次sys.stdout
format_output.py
中设置一次sys.stdout
- 被其他函数所调用时
尝试将两次设置sys.stdout
放于不同的文件当中,也不行。
支持的输出格式
如代码中的情况,目前只支持3种方式。
这里思考,自定义一种格式的可能性?
自己编写一种格式,放在源代码中,然后,自己指定这种格式,是否可行?
[root@nano-kvm-11 ~]# tccli --output table1 dnspod DescribeDomain --Domain hongyuan.com
usage: tccli [options] <command> <subcommand> [<subcommand> ...] [parameters]
To tccli help text, you can run:tccli helptccli configure helptccli service[cvm] helptccli service[cvm] action[RunInstances] help
tccli: error: argument --output: Invalid choice, valid choices are:json | text
table Invalid choice: 'table1', maybe you meant:* table
自定义解析格式
在开始之前是有参数检查的,需要先跨过这一关。
[root@nano-kvm-11 ~]# tccli --output json1 dnspod DescribeDomain --Domain hongyuan.com
usage: tccli [options] <command> <subcommand> [<subcommand> ...] [parameters]
To tccli help text, you can run:tccli helptccli configure helptccli service[cvm] helptccli service[cvm] action[RunInstances] help
tccli: error: argument --output: Invalid choice, valid choices are:json | text
table Invalid choice: 'json1', maybe you meant:* json
腾讯云客户端命令行工具tccli主流程解析相关推荐
- linux使用mysql命令行工具_我使用过的Linux命令之mysql - MySQL客户端命令行工具
我使用过的Linux命令之mysql - MySQL客户端命令行工具 用途说明 mysql命令是用来连接MySQL服务器并执行用户命令行的工具,如果使用MySQL作为数据库,那这个命令就是经常需要用到 ...
- 知晓云 – 云函数命令行工具发布
九九重阳刚刚过去,一大波母螃蟹正在向你靠近,「知晓云」择此吉日出炉了一整锅热乎的新功能,欢迎各位同学敞开肚皮,尽情享用. No.0 云函数命令行工具发布 在云函数控制面板上可以很方便地创建.编辑.配置 ...
- (十二)洞悉linux下的Netfilteramp;iptables:iptables命令行工具源码解析【下】
iptables用户空间和内核空间的交互 iptables目前已经支持IPv4和IPv6两个版本了,因此它在实现上也需要同时兼容这两个版本.iptables-1.4.0在这方面做了很好的设计,主要是由 ...
- 客户端命令行工具 - 接口调试神器 HTTPie
http www.baidu.com (默认get请求) 一.http http://127.0.0.1:8080/admin/login mobile=13226317777password=abc ...
- 微软tfs服务器申请,TFS 的命令行工具
TFS 的命令行工具 06/09/2015 本文内容 Visual Studio Team Foundation Server (TFS) 命令行工具执行几类任务. 某些任务可以通过用户界面完成,其他 ...
- Redis 笔记(16)— info 指令和命令行工具(查看内存、状态、客户端连接数、监控服务器、扫描大key、采样服务器、执行批量命令等)
Info 命令返回关于 Redis 服务器的各种信息和统计数值.通过给定可选的参数 section ,可以让命令只返回某一部分的信息. 1. 显示模块 server : 一般 Redis 服务器信息, ...
- 通过命令行工具使用阿里云资源编排服务
资源编排ROS 是一种简单易用的云计算资源管理和自动化运维服务.用户通过模板描述多个云计算资源的依赖关系.配置等,并自动完成所有资源的创建和配置,以达到自动化部署.运维等目的. 了解更多 通过命令行工 ...
- 【云原生 • Kubernetes】命令行工具 kubectl 介绍及命令汇总
本文导读 1. kubectl 概述 2. kubectl 命令语法 3. kubectl help 获取更多信息 4. kubectl 命令大全 • 基础命令 • 部署命令 • 集群管理命令 • 故 ...
- 【重识云原生】第六章容器6.3.7节——命令行工具kubectl
<重识云原生系列>专题索引: 第一章--不谋全局不足以谋一域 第二章计算第1节--计算虚拟化技术总述 第二章计算第2节--主流虚拟化技术之VMare ESXi 第二章计算第3节--主流虚拟 ...
最新文章
- python递归题目_Python递归的问题?
- python推荐书籍-7本经典的Python书籍,你都读过了么?
- C语言程序设计第三次作业——选择结构(1)
- TRUNK配置详细讲解
- 内部类的分类及其定义
- ios逆向小试牛刀之操作手记
- MyBatis拦截器原理探究
- 非线性光纤光学_1.56 m波段高能量百飞秒光纤激光器
- 移位操作提高代码的可读性_本地记录或类,以提高流操作的可读性
- php中for循环流程图,PHP for循环
- java.sql.SQLException: Parameter index out of range (5 > number of parameters, which is 4).
- 用 Anaconda 完美解决 Python2 和 python3 共存问题
- MySQL Shell副本集和MGR快速搭建详解
- 1049. 最后一块石头的重量 II(JavaScript)
- 目录行距怎么设置_硕士论文格式设置方法
- [转载] 杜拉拉升职记——34 设定工作目标要符合“SMART”原则
- [PHP]算法- 二叉树的深度的PHP实现
- 机器学习中的数学:微积分与最优化
- 游戏感:虚拟感觉的游戏设计师指南——第十九章 游戏感的未来
- 离心泵水力设计——0设计参数
热门文章
- Python工具脚本,PDF文件批量转图片(pdf图片提取器)工具(exe)
- 《炬丰科技-半导体工艺》一步清洁取代RCA两步清洁法用于Pre-Gate清洁
- 学习笔记 - 月冲年冲
- 【一起来烧脑】一步学会CSS3体系
- 关于为什么出现粘包问题及如何解决!
- BPM-业务流程管理
- 期刊信息管理list.php,科学网—【信管学人国际期刊投稿目录列表】-Journal List for Scholars in Info Mgt - 陈晓宇的博文...
- CAD标注样式修改后为什么图中标注不变?
- QCW切割 --铁片
- 使用富文本编辑器wangEditor完成图片文件的上传