网站首页 > 教程文章 正文
前言
在前面的文章《Pytest测试框架一键动态切换测试环境实现思路及方案》中,我们介绍过通过Pytest的“pytest_addoption”的hook函数来给Pytest框架增加命令行参数:
def pytest_addoption(parser):
"""
添加命令行参数
parser.addoption为固定写法
default 设置一个默认值,此处设置默认值为test
choices 参数范围,传入其他值无效
help 帮助信息
"""
parser.addoption(
"--env", default="test", choices=["dev", "test", "pre"], help="enviroment parameter"
)
在“pytest_addoption”函数中,它实际上就是调用了Python标准库“argparse”的“addoption”方法:
所以,如何定义命令行参数的核心是掌握argparse模块的用法。
基于本篇文章的目标,我们需要拆解为两部分:
- 如何定义命令行参数?
- 如何将命令行参数的程序上传到Pypi?
一、如何定义命令行参数
正如上面分析的,定义命令行参数主要会用到argparse模块。这里主要用到的是argparse的ArgumentParser类,先来看一个示例:
import argparse
def main():
parser = argparse.ArgumentParser(prog="gtp", description="大刚测试开发实战项目")
# 不加 - 的前缀 表示必填参数,加 - 表示可选参数,- 为缩写 -- 为全称
parser.add_argument("-n", "--name", default="大刚", help="测试开发实战的作者姓名")
parser.add_argument("-a", "--age", default="18", help="测试开发实战的作者年龄")
args = parser.parse_args()
if args.name:
print(f"Welcome to '测试开发实战', author by {args.name}")
print(f"The author age is {args.age}")
if __name__ == '__main__':
main()
代码分析:
- 第5行,实例化argparse模块的ArgumentParser类,prog即program,也就是我们这个程序的名称,description为描述信息,会在帮助信息中显示;
- 第7-8行,调用parser对象的add_argument方法,增加命令行参数(添加多个命令行参数,就调用多次、定义多个参数即可),其中:不加 - 的前缀 表示必填参数,在命令行输入gtp的时候,必须带上这个参数名,加 - 表示可选参数,- 为缩写 -- 为全称,default为默认参数;
- 第9行,调用parser对象的parse_args方法,解析命令行参数,将参数字符串转换为对象;
- 第10-12行,打印输入的命令行参数的参数值;
由于工具我们没有上传或安装到本地,所以gtp这个命令无法直接使用,可以先通过Python来验证上述功能的正确性:
通过控制台执行结果可以发现:
- 第1次执行,没有带上任何参数,打印的就是我们default默认的姓名的参数值“大刚”和年龄的参数值"18";
- 第2次执行,带上了"-n"参数及参数值"周润发"、"-a"参数及参数值"72",打印的就是我们指定的参数值“周润发”和"72";
- 第3次执行,带上了"--name"参数及参数值"刘德华"、"--age"参数及参数值"68",打印的就是我们指定的参数值"刘德华"和"68";
以上参数全部生效,验证成功!
二、如何将命令行参数的程序上传到Pypi
搞定了命令行参数,我们就该考虑如何将我们自己编写的程序上传到Pypi(Python官方仓库)了。
1.创建目录结构
首先,需要创建固定形式的目录结构,如下:
2.准备项目文件
① README.rst
项目的描述文件,类似于帮助文档,一般包含怎样安装项目,怎样使用项目等,更多信息请参考:https://rest-sphinx-memo.readthedocs.io/en/latest/ReST.html,例如:
大刚测试开发实战demo项目: gtp工具,更多测试开发干活请关注公众号"测试开发实战"
参数说明:
--name 或 -n 姓名 执行后会打印此输入的名字
--age 或 -a 年龄 执行后会打印此输入的年龄
用法示例:
gtp --name "周润发" --age "72"
打印信息:
Welcome to '测试开发实战', author by 周润发
The author age is 72
② LICENSE.txt
项目许可证信息,上传到Python Package Index的每个包都包含许可证,开源License,如MIT,Apache license 2.0等,可以进入:https://choosealicense.com/网站获取MIT的许可信息,直接复制粘贴到LICENSE.txt文件中:
例如:
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
③ setup.py
setuptools的构建脚本,包括定义包名、版本、包含哪些代码模块、依赖哪些库、在哪些Python版本上运行。
大名鼎鼎的requests库的作者大神kennethreitz为大家准备了一个仓库作为一个setup.py的很好的模板
git clone https://github.com/kennethreitz/setup.py
当然也可以自己手写setup.py,建议直接编辑仓库里的setup.py, 只需要修改一些必要的配置即可。例如:
# coding: utf-8
from distutils.core import setup
from setuptools import find_packages
with open("README.rst", "r") as f:
long_description = f.read()
setup(name='gtp', # 包名
version='1.0.0', # 版本号
description='大刚测试开发实战项目',
long_description=long_description, # 首页会显示README的说明信息
author='大刚',
author_email='152xxxx2668@163.com',
url='https://github.com/xxxx',
install_requires=[], # 存放依赖库,并指明依赖版本
license='MIT License', # 许可信息
packages=find_packages(), # 要发布的包,多个包可以使用[a,b,c]定义指定包,不指定默认所有
platforms=["all"], # 平台
classifiers=[ # 允许包在哪个Python版本下运行
'Intended Audience :: Developers',
'Operating System :: OS Independent',
'Natural Language :: Chinese (Simplified)',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Topic :: Software Development :: Libraries'
],
)
④ main.py
项目的主程序文件,就是我们前面自定义命令行工具的代码:
import argparse
def main():
parser = argparse.ArgumentParser(prog="gtp", description="大刚测试开发实战项目")
# 不加 - 的前缀 表示必填参数,加 - 表示可选参数,- 为缩写 -- 为全称
parser.add_argument("-n", "--name", default="大刚", help="测试开发实战的作者姓名")
parser.add_argument("-a", "--age", default="18", help="测试开发实战的作者年龄")
args = parser.parse_args()
if args.name:
print(f"Welcome to '测试开发实战', author by {args.name}")
print(f"The author age is {args.age}")
if __name__ == '__main__':
main()
3.生成Python分发包
首先要确保您拥有setuptools并wheel 安装了最新版本:
pip install --upgrade setuptools wheel
打包命令:
# 打tar.gz格式的包
python setup.py sdist build
# 打wheels格式的包
python setup.py bdist_wheel
# 同时打tar.gz和wheel格式的包
python setup.py sdist bdist_wheel
打包完成后,本地会多出dist目录及相关文件。
4.本地安装命令行工具
① 安装工具
上传Pypi之前,我们可以先安装到本地试试:
python setup.py install
安装报错了:
原来是“setup.py install”方法被弃用了,在网上找了一下原因及解决办法:原因在于setuptools版本过高。通过检查并降级setuptools到58版本,成功解决了编译和安装算法时的报错。
pip install setuptools==58
再次运行安装,安装成功,:
② 验证工具
由于gtp项目名在pypi中已存在,后续已统一修改为“dgtest”
dgtest -h
执行报错了,系统无法识别该命令:
起初我以为是环境的问题,后来才知道是setup.py文件中缺少了“entry_points”参数。如果你的包只是用于被其他模块导入,可以不用添加此参数,但如作为命令行调用,就必须要增加。
entry_points参数的作用(来自于豆包):
在setup.py中添加entry_points参数:
卸载后再次安装:
python uninstall dgtest
python setup.py install
命令行可以正确识别到“dgtest”命令:
5.注册帐号、发布包到Pypi
① 注册帐号
在 PyPI上注册一个帐户,请访问:https://pypi.org/, 到PyPI上注册自己的用户, 点击“Register”, 填写用户名, 密码, 邮件地址等。
② 安装上传包环境
pip install twine
③ 发布包到PyPi
twine upload dist/*
发布成功会出现如下提示:
6.查看发布成功的项目
发布成功后,pypi主页会多出新发布的项目:
7.安装使用
注意:如果曾经设置过本地安装源为阿里或清华的镜像源(可以通过“pip3 config list”命令查看),那么在安装我们刚刚上传的包时就需要指定Pypi的镜像源:
pip install -i https://pypi.org/simple dgtest
验证命令行工具,工作正常:
三、踩坑记录
1.上传地址变化
之前的上传地址“https://pypi.python.org”已经关闭,现在已替换成“https://upload.pypi.org/legacy/”。所以如果原来有在“$HOME/.pypirc”的配置文件中配置过repository仓库地址的,需要将其改为最新上传地址。
2.双重认证
2023年年底,pypi采用了双因素认证。这个是需要登陆pypi的账户后,进行认证。有两种方式进行认证:
- 在手机端搜索2FA,下载一个认证器APP(一般情况下都是收费的,前面会免费试用几天),扫描身份认证页面的二维码,获取六位数验证码
- 通过代码生成六位数验证码:
import pyotp
key = 'EV4JWPKX5SRPTVC3MR*********' # 从二维码下方的“验证申请”处复制过来
totp = pyotp.TOTP(key)
print(totp.now())
3.API token
之前是上传的时候输入用户名密码,现在改为API token进行认证。具体方法:
- 在pypi账户设置中添加API token
- 复制API token,在上传时粘贴
不过第2步经常需要粘贴较为麻烦,也可以在本地创建一个名为“$HOME/.pypirc”的文件,将token内容复制进去。这样,下次在上传的时候就不用再一遍遍粘贴token了。“$HOME/.pypirc”文件内容形式如下:
[distutils]
index-servers=pypi
[pypi]
repository = https://upload.pypi.org/legacy/
username = __token__
password = 这里是你的token信息
4.命名重复
比如我取名为"gtp",出现了以下报错(推测是仓库中存在同名项目),所以需要修改项目名,重新上传:
注意:
- 修改项目名,同时也要修改所有涉及到包名的文件,例如:setup.py以及main.py,所以取名要慎重;
- 另外需要删除原来的build、dist目录等打包信息,重新进行打包。
5.文件重复
例如上面修改setup.py文件、加入了entry_points参数后,再次打包上传,会提示文件已存在:
修改setup.py中的“version”(版本号),重新打包上传即可:
小结
以上就是“如何定义命令行参数”,以及“如何将命令行参数的程序上传到Pypi”的全部过程。总体来说,并不复杂,只是步骤较多,很容易踩坑。尤其是几个事项一定要特别注意:
- setuptools包版本不能过高,否则运行“python setup.py install”时会报错终止;
- 如果你的包想要作为命令行调用,就必须要在setup.py中增加entry_points参数;
- 上传包的地址“https://pypi.python.org”已经关闭,现在已替换成“https://upload.pypi.org/legacy/”;
- Pypi已开启双重认证,原来上传时输入用户名密码的认证方式已经被弃用,现已采用API-Token的认证方式;
- 项目的命名最好独特一些,不要和别人重复,每重新打包上传之前,都要修改一次版本号,避免版本重复。
猜你喜欢
- 2024-12-07 Pytorch配置GPU加速深度学习环境
- 2024-12-07 APISIX从安装到放弃
- 2024-12-07 K8S+DevOps架构师实战课 | Kubernetes安装手册(非高可用版)
- 2024-12-07 frida环境搭建及hook入门
- 2024-12-07 docker源更换
- 2024-12-07 CentOS 更新yum源总结
- 2024-12-07 玩转大模型必备:milvus向量数据库私有化安装部署及使用介绍
- 2024-12-07 java自动化测试
- 2024-12-07 使用debian镜像的优点,nodejs,nginx,python...
- 2024-12-07 Could not retrieve mirrorlist http://mirrorlist.centos.org/?rele
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- md5 sha1 (32)
- mybatis plus page (35)
- semaphore 使用详解 (32)
- update from 语句 (32)
- vue @scroll (38)
- 堆栈区别 (33)
- 在线子域名爆破 (32)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)