中间件自动化测试框架cmdlinker

背景

作为一个中间件的测试工程师,如何对于中间件提供的命令进行自动化的回归,这一直是一个难题,市面上好像缺乏了对于命令进行自动化回归的合理解决方案。
常见方式有下面两种:

  • 直接写字符串的命令,然后使用各种编程语言的SSH库进行连接,然后执行命令字符串,获取执行结果,如果需要对传入的参数进行校验,还需要各种提取字符串,或者设置各种命令变量,比较繁琐
  • 编写命令对象,通过操作命令对象set/get方式,生成命令字符串给SSH库进行命令执行,如果需要对传入的参数进行校验,只需要通过命令对象get的方式进行获取,当然缺点就是,每次都需要进行大量的工作去编写命令对象,重复性劳动

通过对比之后,会发现,使用第二种方式更加直观、简洁,提取响应数据也更加方便,但是第二种方式对于大量重复性的去编写命令对象的方式,也让人十分恶心。如果有一个工具能够帮助大家去生成命令对象,然后通过调用命令对象执行请求响应的方式就能让大家从这种困境中走出来。

所以引入了cmdlinker这个开源python库,cmdlinker能够帮助我们做什么?

  • 通过编写命令对象yaml的方式,生成命令对象,无需手动进行命令对象代码的编写
  • 命令对象自带runner,可以直接获取命令执行的请求响应
  • 支持本地shell执行及远程ssh执行的方式

安装

pip3 install cmdlinker

文档地址

https://github.com/chineseluo/cmdlinker

案例演示:

使用docker命令举例,查询容器,获取容器配置信息,断言容器的ID是否包含请求的ID
编写yaml文件:

entry: "docker"
mapping_entry: "Docker"
module_name: "docker"
class_name: "Docker"
out_path: "./"
mode: "SSH"
parameters:- mapping_name: "ps"original_cmd: "ps"value: False # 是否需要值mutex: False # 是否互斥default: None- mapping_name: "inspect"original_cmd: "inspect"value: True # 是否需要值mutex: False # 是否互斥default: None- mapping_name: "f"original_cmd: "-f"value: True # 是否需要值mutex: False # 是否互斥default: None

执行生成命令:

CmdLinker init -f ./docker.yaml

生成python命令对象

from loguru import logger
from typing import Union, Text
from cmdlinker.builtin.exception import CmdLinkerMutexException, CmdLinkerMulMutexException
from cmdlinker.builtin.ssh_utils import SSHClient
from cmdlinker.builtin.shell_utils import ShellClientclass Cmds:def __init__(self):self.index = 0self.CMD_LIST = []class Runner:def __init__(self):self._mutexs = []self._gcs = []self._not_mutexs = []self.pre: Runner = objectself.root: Runner = objectself.next: Union[Runner] = objectself.main_cmd: Text = Noneself.cmds: Cmds = Noneself.ssh_client: SSHClient = Noneself.sudo: bool = Trueself.timeout: int = 20def _exclude(self, cmd_obj):if not cmd_obj.mark:logger.debug(f"命令对象:{cmd_obj.__class__.__name__}未被使用")return Falseelse:return Truedef _get_execute_cmd(self, cmd_obj):if cmd_obj.need_value:return f"{cmd_obj.meta_data['original_cmd']} {cmd_obj.value}"else:return f"{cmd_obj.meta_data['original_cmd']}"def _get_log_desc(self):if self.pre == self.root:log_desc = "子"elif self.pre is None and self.root == self:log_desc = "根"else:log_desc = "父"return log_descdef cmd_checker(self):log_desc = self._get_log_desc()logger.info("==" * 20 + f"开启{log_desc}命令{self.__class__.__name__}合法性检查" + "==" * 20)for cmd_obj_str in vars(self).keys():cmd_obj = getattr(self, cmd_obj_str)if isinstance(cmd_obj, BaseCmd):if cmd_obj_str == "next":continueif not self._exclude(cmd_obj):continueif cmd_obj.mutex:self._mutexs.append(cmd_obj)logger.debug(f"{self.main_cmd}添加互斥对象:{cmd_obj},最新互斥对象列表:")else:self._not_mutexs.append(cmd_obj)logger.debug(f"{self.main_cmd}添加非互斥对象:{cmd_obj},最新非互斥对象列表:{self._not_mutexs}")if len(self._mutexs) > 1:raise CmdLinkerMulMutexException(self.__class__.__name__, self._mutexs)if len(self._mutexs) != 0 and len(self._not_mutexs) != 0:raise CmdLinkerMutexException(self.__class__.__name__, self._mutexs, self._not_mutexs)if self.pre == self.root:logger.debug(f"启用命令对象{self.__class__.__name__} 根命令: {self.root.__class__.__name__} 检查")self.pre.cmd_checker()elif self.pre is None and self.root == self:passelse:logger.debug(f"启用命令对象{self.__class__.__name__}父命令: {self.pre.__class__.__name__} 检查")self.pre.cmd_checker()self._not_mutexs = []self._mutexs = []logger.info("==" * 20 + f"{log_desc}命令{self.__class__.__name__}合法性检查通过" + "==" * 20)def runner(self):self.cmd_checker()cmd = self.exec_cmd()self.cmds.CMD_LIST = []return self.ssh_client.run_cmd(cmd, timeout=self.timeout)def collector(self):return self.cmds.CMD_LIST.sort(key=lambda cmd_obj: cmd_obj.index)def exec_cmd(self):self.cmds.CMD_LIST.sort(key=lambda cmd_obj: cmd_obj.index)cmd_list = [self.main_cmd] + [self._get_execute_cmd(cmd) for cmd in self.cmds.CMD_LIST]if self.sudo:cmd_list.insert(0, "sudo")logger.info(f"执行命令列表:{cmd_list}")cmd = " ".join(cmd_list)return cmdclass BaseCmd(Runner):def __init__(self):super().__init__()self.cmds: Cmds = Noneself.meta_data = Noneself.mutex = Noneself.need_value = Noneself.has_child_cmd = Noneself.gc = Noneself.child_cmd = Noneself.default_value = Noneself.value = Noneself.mark = Noneself.index = Noneself.level = 0self._mutexs = []self._gcs = []self._not_mutexs = []self.main_cmd = "docker"self.pre: object = objectself.root: object = objectself.next: Union[object] = objectself.ssh_client: SSHClient = Noneclass Ps(BaseCmd):def __init__(self, root_obj, pre_obj):super().__init__()self.pre: Docker = pre_objself.root: Docker = root_objself.next: Union[Ps] = selfself.meta_data = {'mapping_name': 'ps', 'original_cmd': 'ps', 'value': False, 'mutex': False, 'default': 'None','has_child_cmd': False, 'child_cmds': [], 'parent_cmd': 'Docker', 'root_cmd': 'Docker'}self.level = 2self.mutex = Falseself.need_value = Falseself.has_child_cmd = Falseself.child_cmds = []self.gc = Falseself.default_value = "None"self.value = self.default_valueself.mark = Falseclass Inspect(BaseCmd):def __init__(self, root_obj, pre_obj):super().__init__()self.pre: Docker = pre_objself.root: Docker = root_objself.next: Union[Inspect] = selfself.meta_data = {'mapping_name': 'inspect', 'original_cmd': 'inspect', 'value': True, 'mutex': False,'default': 'None', 'has_child_cmd': False, 'child_cmds': [], 'parent_cmd': 'Docker','root_cmd': 'Docker'}self.level = 2self.mutex = Falseself.need_value = Trueself.has_child_cmd = Falseself.child_cmds = []self.gc = Falseself.default_value = "None"self.value = self.default_valueself.mark = Falseclass Format(BaseCmd):def __init__(self, root_obj, pre_obj):super().__init__()self.pre: Docker = pre_objself.root: Docker = root_objself.next: Union[Format] = selfself.meta_data = {'mapping_name': 'format', 'original_cmd': '--format', 'value': True, 'mutex': False,'default': 'None', 'has_child_cmd': False, 'child_cmds': [], 'parent_cmd': 'Docker','root_cmd': 'Docker'}self.level = 2self.mutex = Falseself.need_value = Trueself.has_child_cmd = Falseself.child_cmds = []self.gc = Falseself.default_value = "None"self.value = self.default_valueself.mark = Falseclass Docker(Runner):def __init__(self, host=None, name=None, pwd=None, port="22", timeout="60", sudo=False):super().__init__()self.cmds: Cmds = Cmds()self.pre: object = Noneself.root: Docker = selfself.next: Union[Ps, Inspect, Format,] = Noneself.main_cmd = "docker"self._mutexs = []self._gcs = []self._not_mutexs = []self.mode = "SSH"self.ssh_client = SSHClient(host, name, pwd, port)self._ps: Ps = Ps(self, self)self._inspect: Inspect = Inspect(self, self)self._format: Format = Format(self, self)def _set_ps(self):self._ps.mark = Trueself._ps.index = self.cmds.indexself.cmds.index += 1self.next = self._psself.cmds.CMD_LIST.append(self._ps)self._ps.cmds = self.cmdsself._ps.ssh_client = self.ssh_clientdef tset_ps(self):"""传递TRANSMIT模式,可以获取子命令对象,可通过,root/pre/next,控制命令对象的根/父/子级"""self._set_ps()return self._psdef hset_ps(self, ):"""保持HOLD模式,该方法返回该对象本身,不返回子对象"""self._set_ps()return selfdef _set_inspect(self, value=None):self._inspect.mark = Trueself._inspect.index = self.cmds.indexself.cmds.index += 1self.next = self._inspectself.cmds.CMD_LIST.append(self._inspect)self._inspect.cmds = self.cmdsself._inspect.ssh_client = self.ssh_clientif self._inspect.need_value:self._inspect.value = valuedef tset_inspect(self, value=None):"""传递TRANSMIT模式,可以获取子命令对象,可通过,root/pre/next,控制命令对象的根/父/子级"""self._set_inspect(value=value)return self._inspectdef hset_inspect(self, value=None):"""保持HOLD模式,该方法返回该对象本身,不返回子对象"""self._set_inspect(value=value)return selfdef _set_format(self, value=None):self._format.mark = Trueself._format.index = self.cmds.indexself.cmds.index += 1self.next = self._formatself.cmds.CMD_LIST.append(self._format)self._format.cmds = self.cmdsself._format.ssh_client = self.ssh_clientif self._format.need_value:self._format.value = valuedef tset_format(self, value=None):"""传递TRANSMIT模式,可以获取子命令对象,可通过,root/pre/next,控制命令对象的根/父/子级"""self._set_format(value=value)return self._formatdef hset_format(self, value=None):"""保持HOLD模式,该方法返回该对象本身,不返回子对象"""self._set_format(value=value)return selfdef ps(self) -> Ps:return self._psdef inspect(self) -> Inspect:return self._inspectdef format(self) -> Format:return self._format

执行测试

进行请求响应断言(PS:自行替换下面的host/name/pwd/port等参数验证)

if __name__ == '__main__':docker = Docker(host="192.168.1.2", name="root", pwd="xxxx")ps_res = docker.hset_ps().hset_format("{{.ID}}").runner()inspect_res = docker.hset_inspect(ps_res["stdout"]).runner()assert ps_res["stdout"].strip() in inspect_res["stdout"][0]["Id"]

检查日志

C:\Users\Dell\AppData\Local\Programs\Python\Python39\python.exe E:\开源项目\CmdLinker\tests\docker.py 
2025-01-03 16:38:03.116 | INFO     | __main__:cmd_checker:53 - ========================================开启根命令Docker合法性检查========================================
2025-01-03 16:38:03.116 | DEBUG    | __main__:cmd_checker:66 - docker添加非互斥对象:<__main__.Ps object at 0x000001ADA171E9A0>,最新非互斥对象列表:[<__main__.Ps object at 0x000001ADA171E9A0>]
2025-01-03 16:38:03.116 | DEBUG    | __main__:_exclude:31 - 命令对象:Inspect未被使用
2025-01-03 16:38:03.116 | DEBUG    | __main__:cmd_checker:66 - docker添加非互斥对象:<__main__.Format object at 0x000001ADA171EAF0>,最新非互斥对象列表:[<__main__.Ps object at 0x000001ADA171E9A0>, <__main__.Format object at 0x000001ADA171EAF0>]
2025-01-03 16:38:03.116 | INFO     | __main__:cmd_checker:81 - ========================================根命令Docker合法性检查通过========================================
2025-01-03 16:38:03.116 | INFO     | __main__:exec_cmd:97 - 执行命令列表:['sudo', 'docker', 'ps', '--format {{.ID}}']
2025-01-03 16:38:03.693 | INFO     | cmdlinker.builtin.logger_operation:console_output:18 - 
================== cmd request  details ==================
execute cmd on host : 192.168.1.2
execute cmd : sudo docker ps --format {{.ID}}2025-01-03 16:38:03.694 | WARNING  | cmdlinker.builtin.ssh_utils:run_cmd:67 - 尝试对response stdout进行json转换失败,原样输出
2025-01-03 16:38:03.694 | INFO     | cmdlinker.builtin.logger_operation:console_output:18 - 
================== cmd response  details ==================
status_code : 0
execute_time : 578ms
stdout   : 7393aa6334e6stderr   : 2025-01-03 16:38:03.694 | INFO     | __main__:cmd_checker:53 - ========================================开启根命令Docker合法性检查========================================
2025-01-03 16:38:03.694 | DEBUG    | __main__:cmd_checker:66 - docker添加非互斥对象:<__main__.Ps object at 0x000001ADA171E9A0>,最新非互斥对象列表:[<__main__.Ps object at 0x000001ADA171E9A0>]
2025-01-03 16:38:03.694 | DEBUG    | __main__:cmd_checker:66 - docker添加非互斥对象:<__main__.Inspect object at 0x000001ADA171EA60>,最新非互斥对象列表:[<__main__.Ps object at 0x000001ADA171E9A0>, <__main__.Inspect object at 0x000001ADA171EA60>]
2025-01-03 16:38:03.694 | DEBUG    | __main__:cmd_checker:66 - docker添加非互斥对象:<__main__.Format object at 0x000001ADA171EAF0>,最新非互斥对象列表:[<__main__.Ps object at 0x000001ADA171E9A0>, <__main__.Inspect object at 0x000001ADA171EA60>, <__main__.Format object at 0x000001ADA171EAF0>]
2025-01-03 16:38:03.694 | INFO     | __main__:cmd_checker:81 - ========================================根命令Docker合法性检查通过========================================
2025-01-03 16:38:03.694 | INFO     | __main__:exec_cmd:97 - 执行命令列表:['sudo', 'docker', 'inspect 7393aa6334e6\n']
2025-01-03 16:38:03.922 | INFO     | cmdlinker.builtin.logger_operation:console_output:18 - 
================== cmd request  details ==================
execute cmd on host : 47.97.37.176
execute cmd : sudo docker inspect 7393aa6334e62025-01-03 16:38:03.922 | INFO     | cmdlinker.builtin.logger_operation:console_output:18 - 
================== cmd response  details ==================
status_code : 0
execute_time : 228ms
stdout   : [{"Id": "7393aa6334e6a091aae54790f5a6e3505f6dbab9515c5643978afbdc221975dd","Created": "2024-06-26T07:04:49.182101334Z","Path": "docker-entrypoint.sh","Args": ["--requirepass","yourpassword"],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 27544,"ExitCode": 0,"Error": "","StartedAt": "2024-06-26T07:04:49.54446658Z","FinishedAt": "0001-01-01T00:00:00Z"},"Image": "sha256:7614ae9453d1d87e740a2056257a6de7135c84037c367e1fffa92ae922784631","ResolvConfPath": "/var/lib/docker/containers/7393aa6334e6a091aae54790f5a6e3505f6dbab9515c5643978afbdc221975dd/resolv.conf","HostnamePath": "/var/lib/docker/containers/7393aa6334e6a091aae54790f5a6e3505f6dbab9515c5643978afbdc221975dd/hostname","HostsPath": "/var/lib/docker/containers/7393aa6334e6a091aae54790f5a6e3505f6dbab9515c5643978afbdc221975dd/hosts","LogPath": "/var/lib/docker/containers/7393aa6334e6a091aae54790f5a6e3505f6dbab9515c5643978afbdc221975dd/7393aa6334e6a091aae54790f5a6e3505f6dbab9515c5643978afbdc221975dd-json.log","Name": "/some-redis","RestartCount": 0,"Driver": "overlay2","Platform": "linux","MountLabel": "","ProcessLabel": "","AppArmorProfile": "","ExecIDs": null,"HostConfig": {"Binds": null,"ContainerIDFile": "","LogConfig": {"Type": "json-file","Config": {}},"NetworkMode": "default","PortBindings": {"6379/tcp": [{"HostIp": "","HostPort": "6379"}]},"RestartPolicy": {"Name": "no","MaximumRetryCount": 0},"AutoRemove": false,"VolumeDriver": "","VolumesFrom": null,"CapAdd": null,"CapDrop": null,"CgroupnsMode": "host","Dns": [],"DnsOptions": [],"DnsSearch": [],"ExtraHosts": null,"GroupAdd": null,"IpcMode": "private","Cgroup": "","Links": null,"OomScoreAdj": 0,"PidMode": "","Privileged": false,"PublishAllPorts": false,"ReadonlyRootfs": false,"SecurityOpt": null,"UTSMode": "","UsernsMode": "","ShmSize": 67108864,"Runtime": "runc","ConsoleSize": [0,0],"Isolation": "","CpuShares": 0,"Memory": 0,"NanoCpus": 0,"CgroupParent": "","BlkioWeight": 0,"BlkioWeightDevice": [],"BlkioDeviceReadBps": null,"BlkioDeviceWriteBps": null,"BlkioDeviceReadIOps": null,"BlkioDeviceWriteIOps": null,"CpuPeriod": 0,"CpuQuota": 0,"CpuRealtimePeriod": 0,"CpuRealtimeRuntime": 0,"CpusetCpus": "","CpusetMems": "","Devices": [],"DeviceCgroupRules": null,"DeviceRequests": null,"KernelMemory": 0,"KernelMemoryTCP": 0,"MemoryReservation": 0,"MemorySwap": 0,"MemorySwappiness": null,"OomKillDisable": false,"PidsLimit": null,"Ulimits": null,"CpuCount": 0,"CpuPercent": 0,"IOMaximumIOps": 0,"IOMaximumBandwidth": 0,"MaskedPaths": ["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths": ["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]},"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/da926ce47de0e966f295ddf249be649da1f2c1ea162be28fb84a5c5d28db3d96-init/diff:/var/lib/docker/overlay2/cb066f8e71551530105d92d22aee4ac7d10beeff480389676e102be0d701e534/diff:/var/lib/docker/overlay2/b69aadb0a11079daa266a36401ba0ce147450516b500c645eff85176f8e7919b/diff:/var/lib/docker/overlay2/a57e79ac2a99e28c990210b22b831dcd8fad2c98d810de241fc8a3d99b64953a/diff:/var/lib/docker/overlay2/482ff82ce4a7763a75994358255c4e604abab9fcb77fd1f5f2d815a0a98a323b/diff:/var/lib/docker/overlay2/8e951ea76637a9804eb44f89eb8353a5a71da11d304056b22e2d1886e7da9c2d/diff:/var/lib/docker/overlay2/e88bc247f216bd19bd2329b2feeda9cc387df6241598b863b201cafe7ff0db3a/diff","MergedDir": "/var/lib/docker/overlay2/da926ce47de0e966f295ddf249be649da1f2c1ea162be28fb84a5c5d28db3d96/merged","UpperDir": "/var/lib/docker/overlay2/da926ce47de0e966f295ddf249be649da1f2c1ea162be28fb84a5c5d28db3d96/diff","WorkDir": "/var/lib/docker/overlay2/da926ce47de0e966f295ddf249be649da1f2c1ea162be28fb84a5c5d28db3d96/work"},"Name": "overlay2"},"Mounts": [{"Type": "volume","Name": "502c8ea157173ae65d24a2e0173c0e990e5ba1398013ad01a048e7a594a62608","Source": "/var/lib/docker/volumes/502c8ea157173ae65d24a2e0173c0e990e5ba1398013ad01a048e7a594a62608/_data","Destination": "/data","Driver": "local","Mode": "","RW": true,"Propagation": ""}],"Config": {"Hostname": "7393aa6334e6","Domainname": "","User": "","AttachStdin": false,"AttachStdout": false,"AttachStderr": false,"ExposedPorts": {"6379/tcp": {}},"Tty": false,"OpenStdin": false,"StdinOnce": false,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOSU_VERSION=1.12","REDIS_VERSION=6.2.6","REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.6.tar.gz","REDIS_DOWNLOAD_SHA=5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab"],"Cmd": ["--requirepass","yourpassword"],"Image": "redis","Volumes": {"/data": {}},"WorkingDir": "/data","Entrypoint": ["docker-entrypoint.sh"],"OnBuild": null,"Labels": {}},"NetworkSettings": {"Bridge": "","SandboxID": "b98966d73f26b520343cc69867d8ac56dd3ac65655375c2edb047cabc9eec9c8","HairpinMode": false,"LinkLocalIPv6Address": "","LinkLocalIPv6PrefixLen": 0,"Ports": {"6379/tcp": [{"HostIp": "0.0.0.0","HostPort": "6379"}]},"SandboxKey": "/var/run/docker/netns/b98966d73f26","SecondaryIPAddresses": null,"SecondaryIPv6Addresses": null,"EndpointID": "63a20510e954d1b81181d319b2d004e68918a2926d3db1e71e846197b79c72e1","Gateway": "172.17.0.1","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","MacAddress": "02:42:ac:11:00:02","Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "7fdaf78d05fd8a3d1c3c2ae0151022ae8c326582524327e18403fdab4a26f16d","EndpointID": "63a20510e954d1b81181d319b2d004e68918a2926d3db1e71e846197b79c72e1","Gateway": "172.17.0.1","IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:02","DriverOpts": null}}}}
]
stderr   : 
Process finished with exit code 0

我们可以很方便的使用返回的status_code/stdout/stderr进行请求响应断言


OpenSourceTest cmdlinker 社区
欢迎小伙伴加群,讨论cmdlinker相关问题,或提出优化建议!
QQ群(自动化测试-夜行者):816489363

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/66139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

民宿酒店预订系统小程序+uniapp全开源+搭建教程

一.介绍 一.系统介绍 基于ThinkPHPuniappuView开发的多门店民宿酒店预订管理系统&#xff0c;快速部署属于自己民宿酒店的预订小程序&#xff0c;包含预订、退房、WIFI连接、吐槽、周边信息等功能。提供全部无加密源代码&#xff0c;支持私有化部署。 二.搭建环境 系统环境…

量子计算:定义、使用方法和示例

什么是量子计算&#xff1f; 量子计算是计算机科学的一个领域&#xff0c;它运用量子理论的原理。量子理论阐释了原子及亚原子层面上能量和物质的行为表现。 量子计算会用到诸如电子或光子之类的亚原子粒子。量子比特使得这些粒子能够同时处于多种状态&#xff08;即 1 和 0&…

使用Python的xml.etree.ElementTree模块解析和操作 XML 数据

Date: 2025.01.04 17:30:45 author: lijianzhan 简述&#xff1a;xml.etree.ElementTree 是 Python 标准库的一部分&#xff0c;而且是 Python 标准库中用于解析和操作 XML 数据的模块&#xff0c;因此不需要通过 pip install 安装。只要你安装了 Python&#xff08;无论是 Pyt…

一文讲清楚HTTP常见的请求头和应用

文章目录 一文讲清楚HTTP常见的请求头和应用1. 啥是个HTTP请求头2. 常见的请求头&#xff0c;作用和示例3.协商缓存4.会话状态 一文讲清楚HTTP常见的请求头和应用 1. 啥是个HTTP请求头 一句话&#xff0c;说白了就是限定HTTP传输的一些规则参数&#xff0c;比如Accept&#xf…

Unity3D ILRuntime开发原则与接口绑定详解

引言 ILRuntime是一款基于C#的热更新框架&#xff0c;使用IL2CPP技术将C#代码转换成C代码&#xff0c;支持动态编译和执行代码&#xff0c;适用于Unity3D的所有平台&#xff0c;包括Android、iOS、Windows、Mac等。本文将详细介绍ILRuntime在Unity3D中的开发原则及接口绑定技术…

Wend看源码-Java-Executor异步执行器学习

摘要 本文主要介绍了Java.util.concurrent包所提供的 Executor 异步执行器框架&#xff0c;涵盖了相关的接口和类。 并发执行器类图 图1 java 并发执行器相关类图 Executor 接口 Executor 接口提供了一种将任务的提交与任务的实际执行机制分离开来的方法。它只有一个方法 exe…

道可云人工智能元宇宙每日资讯|崂山区政务服务虚拟大厅启用

道可云元宇宙每日简报&#xff08;2024年12月31日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 崂山区政务服务虚拟大厅启用 近日&#xff0c;崂山区政务服务“虚拟大厅”在青岛正式上线&#xff0c;成为该市首个采用虚拟现实、人工智能、大数据及3D虚拟数字人…

Tailwind CSS 实战:社交媒体信息流开发

在社交媒体的世界里&#xff0c;信息流就像是一条永不停歇的河流&#xff0c;承载着用户的分享与互动。记得在一个社交平台项目中&#xff0c;我们通过重新设计信息流的展示方式&#xff0c;让用户的平均浏览时长提升了 45%。今天&#xff0c;我想和大家分享如何使用 Tailwind …

单片机-独立按键矩阵按键实验

1、按键介绍 按键管脚两端距离长的表示默认是导通状态&#xff0c;距离短的默认是断开状态&#xff0c; 如果按键按下&#xff0c;初始导通状态变为断开&#xff0c;初始断开状态变为导通 我们开发板是采用软件消抖&#xff0c;一般来说一个简单的按键消抖就是先读取按键的状…

在arm平台Euler系统上编译安装ffmpeg

问题背景 arm平台上&#xff0c;fmpeg不一定能用yum install轻松安装&#xff0c;可能要编译安装。 环境配置 以下是我的版本配置&#xff0c;读者可以下载同版本的tag分支&#xff0c;以便复现。 ffmpeg version 5.1.2 Copyright (c) 2000-2022 the FFmpeg developersbuil…

DCGAN模型详解

模型背景 在深度学习领域迅速发展的背景下,生成对抗网络(GAN)作为一种革命性的生成模型应运而生。 Ian Goodfellow等人于2014年首次提出GAN概念 ,开创了生成模型的新纪元。这一创新源于对深度学习在图像生成方面潜力的探索,旨在解决非监督学习中的关键问题:如何让机器创造…

Elasticsearch: 高级搜索

这里写目录标题 一、match_all匹配所有文档1、介绍&#xff1a; 二、精确匹配1、term单字段精确匹配查询2、terms多字段精确匹配3、range范围查询4、exists是否存在查询5、ids根据一组id查询6、prefix前缀匹配7、wildcard通配符匹配8、fuzzy支持编辑距离的模糊查询9、regexp正则…

HTML5实现喜庆的新年快乐网页源码

HTML5实现喜庆的新年快乐网页源码 前言一、设计来源1.1 主界面1.2 关于新年界面1.3 新年庆祝活动界面1.4 新年活动组织界面1.5 新年祝福订阅界面1.6 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现喜庆的新年快乐网页源码&#xff0c;春节新年网…

LeetCode热题100-两数之和【JavaScript讲解】

题目: 根据题目想思路&#xff1a; 使用哈希表进行索引查找。利用nums[j] target - nums[i]获得我们需要的值&#xff0c;在数组中进行查询。查询成功后返回这两个下标&#xff1b;否则当前元素的值和索引存储到哈希表中&#xff0c;以便后续查找。 题解&#xff1a; /*** …

沙箱模拟支付宝支付3--支付的实现

1 支付流程实现 演示案例 主要参考程序员青戈的视频【支付宝沙箱支付快速集成版】支付宝沙箱支付快速集成版_哔哩哔哩_bilibili 对应的源码在 alipay-demo: 使用支付宝沙箱实现支付功能 - Gitee.com 以下是完整的实现步骤 1.首先导入相关的依赖 <?xml version"1…

250103-逻辑运算符

1. 逻辑与运算符 && 2. 逻辑非运算符 &#xff01; 实例 if(!(p1.agep2.age&&p1.name.equals(p2.name)))

NLP CH3复习

CH3 3.1 几种损失函数 3.2 激活函数性质 3.3 哪几种激活函数会发生梯度消失 3.4 为什么会梯度消失 3.5 如何解决梯度消失和过拟合 3.6 梯度下降的区别 3.6.1 梯度下降&#xff08;GD&#xff09; 全批量&#xff1a;在每次迭代中使用全部数据来计算损失函数的梯度。计算成本…

探索 AIGC 的基础知识:人工智能生成内容的全景视图

在数字化时代&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;正以前所未有的速度改变着我们的创作方式。本文将深入探讨 AIGC 的定义、构成要素、应用场景以及其带来的优势&#xff0c;帮助读者全面理解这一前沿技术。 1. AIGC 的定义与范围 人工智能生成内容&…

【办公类-47-02】20250103 课题资料快速打印(单个docx转PDF,多个pdf合并一个PDF 打印)

背景需求&#xff1a; 2023区级大课题《运用Python优化3-6岁幼儿学习活动材料的实践研究》需要做阶段资料 本来应该2024年6月就提交电子稿和打印稿。可是python学具的教学实验实在太多了&#xff0c;不断生成&#xff0c;我忙着做教学&#xff0c;都没有精力去整理。 2025年…

Android布局layout的draw简洁clipPath实现圆角矩形布局,Kotlin

Android布局layout的draw简洁clipPath实现圆角矩形布局&#xff0c;Kotlin 通常&#xff0c;如果要把一个相对布局&#xff0c;FrameLayout&#xff0c;或者线性布局等这样的布局变成具有圆角或者圆形的布局&#xff0c;需要增加一个style&#xff0c;给它设置圆角&#xff0c;…