如何开发一个Python命令行小工具并上传到Pypi?
off999 2024-12-05 15:18 25 浏览 0 评论
前言
在前面的文章《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的认证方式;
- 项目的命名最好独特一些,不要和别人重复,每重新打包上传之前,都要修改一次版本号,避免版本重复。
相关推荐
- 分区助手怎么用给c盘扩容(分区助手如何扩大c盘容量)
-
360分区助手扩大c盘的方法如下1、下载分区助手软件,安装一定要安装到C盘,安装在其他盘内不可用。折腾还得安装回来,安装后打开软件,选择主页面左侧“扩展分区向导”。2、直接点击“下一步”,默认选择扩...
- u盘不显示文件夹(u盘不显示文件夹但是有内存)
-
如果您的U盘不显示文件夹,可能是由于文件夹被隐藏或损坏造成的。您可以通过以下方法解决此问题:首先,打开文件管理器,点击"查看"选项卡,勾选"隐藏文件",然后查看U盘根目...
- 电脑自带磁盘分区工具(磁盘分区工具在哪里)
-
1、右击我的电脑,选择“管理”。2、接着,在计算机管理界面选择“存储——磁盘管理”3、接着系统会自动的查询压缩空间,然后选择选择好需要压缩空间的大小。接着点击“压缩”4、等待系统自动将磁盘空间压...
- win10激活密钥永久(win10激活密钥永久正版企业版最新)
-
要获得Windows10专业版永久激活密钥,可以通过微软官方零售渠道或可靠的第三方卖家购买正版产品密钥。使用第三方卖家时,务必注意其信誉和真实性。激活后,密钥将与您的Microsoft帐户关...
- wlan不可上网怎么办(wlan显示不可上网怎么回事)
-
当wlan不能上网时,可以尝试以下解决方案:1.检查路由器或无线网络设备是否正常运作,确保其连接和配置正确。2.检查电脑或移动设备是否连接到正确的无线网络,并确保输入正确的密码。3.尝试重新启动...
- ip地址是什么(腾达路由器的ip地址是什么)
-
IP地址:IP是英文InternetProtocol的缩写,意思是“网络之间互连的协议”,也就是为计算机网络相互连接进行通信而设计的协议。我们可以把ip地址类比成电话号码。扫地[sǎodì]&...
- win10 1703版本(window10 1703版本)
-
windows的版本是这样排序的:17为2017年,03为3月,所以此版本为2017年3月更新的版本。微软加入了不少新的功能:增强了Edge浏览器的稳定性。额外的安全和隐私保护。游戏模式更加稳定。日历...
- tplink路由器用户名(tplink路由器用户名和密码)
-
tp-link无线路由器的WiFi默认为TP-LINK_XXXX(XXXX为4位英文和数字组合)。查看tp-link无线路由器ID的方法如下:1、打开电脑浏览器,在地址栏中输入“192.168.0.1...
- 如何安装windows10家庭版(如何安装windows 10家庭版)
-
Windows10家庭版可以安装鲁大师。鲁大师是一款软件,可以用于检测电脑的系统效果和状态。然而,有人认为鲁大师被360收购后出现了很多问题,如难卸载、弹窗不断等。因此,是否需要安装鲁大师,还需要根据...
- 虚拟机安装win7镜像(虚拟机安装win7镜像软件)
-
下载VMware虚拟机win7映像文件,您可以选择官方或授权的渠道进行操作。首先,您需要确认需要下载的虚拟机镜像的操作系统和版本。通常,官方提供了一些预定义的虚拟机镜像,如Windows7等。一种可...
- cpu温度过高会怎样(cpu温度过高会造成什么影响)
-
CPU温度过高会导致一系列问题,包括但不限于以下几个方面:1.电脑运行不稳定:CPU温度过高会导致电脑运行不稳定,程序崩溃、电脑反应缓慢等问题。2.电脑硬件损坏:CPU温度过高容易导致电脑硬件损坏...
- win7进安全模式(win7进安全模式卡死)
-
1、重启或开机时,在进入Windows系统启动画面之前按下F8键,会出现系统多操作启动菜单,有三个版本的安全模式可以选择,回车就直接进入安全模式。2、重启电脑时,按住Ctrl键不放,会出现系统多操作启...
- 360手机助手下载的软件在哪里
-
在手机中打开安装好的360手机应用助手然后在360手机应用助手界面的右下角,选择“更多”,然后在这里再进入“设置”进入设置后,再选择“应用安装位置”设置最后我们选择SD卡即可根据以上步骤,就可以修改下...
- 组策略管理器怎么打开(组策略管理器怎么打开控制面板)
-
1.找不到2.本地组策略管理器可能找不到是因为它可能被禁用或者被删除了。另外,也有可能是因为你的操作系统版本不支持本地组策略管理器。3.如果你的操作系统版本不支持本地组策略管理器,你可以尝试使用...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
