百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

如何开发一个Python命令行小工具并上传到Pypi?

off999 2024-12-05 15:18 16 浏览 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模块的用法。

基于本篇文章的目标,我们需要拆解为两部分:

  1. 如何定义命令行参数?
  2. 如何将命令行参数的程序上传到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的账户后,进行认证。有两种方式进行认证:

  1. 在手机端搜索2FA,下载一个认证器APP(一般情况下都是收费的,前面会免费试用几天),扫描身份认证页面的二维码,获取六位数验证码
  1. 通过代码生成六位数验证码:
import pyotp

key = 'EV4JWPKX5SRPTVC3MR*********' # 从二维码下方的“验证申请”处复制过来
totp = pyotp.TOTP(key)
print(totp.now())

3.API token

之前是上传的时候输入用户名密码,现在改为API token进行认证。具体方法:

  1. 在pypi账户设置中添加API token
  2. 复制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”的全部过程。总体来说,并不复杂,只是步骤较多,很容易踩坑。尤其是几个事项一定要特别注意:

  1. setuptools包版本不能过高,否则运行“python setup.py install”时会报错终止;
  2. 如果你的包想要作为命令行调用,就必须要在setup.py中增加entry_points参数;
  3. 上传包的地址“https://pypi.python.org”已经关闭,现在已替换成“https://upload.pypi.org/legacy/”;
  4. Pypi已开启双重认证,原来上传时输入用户名密码的认证方式已经被弃用,现已采用API-Token的认证方式;
  5. 项目的命名最好独特一些,不要和别人重复,每重新打包上传之前,都要修改一次版本号,避免版本重复。

相关推荐

让 Python 代码飙升330倍:从入门到精通的四种性能优化实践

花下猫语:性能优化是每个程序员的必修课,但你是否想过,除了更换算法,还有哪些“大招”?这篇文章堪称典范,它将一个普通的函数,通过四套组合拳,硬生生把性能提升了330倍!作者不仅展示了“术”,更传授...

7 段不到 50 行的 Python 脚本,解决 7 个真实麻烦:代码、场景与可复制

“本文整理自开发者AbdurRahman在Stackademic的真实记录,所有代码均经过最小化删减,确保在50行内即可运行。每段脚本都对应一个日常场景,拿来即用,无需额外依赖。一、在朋...

Python3.14:终于摆脱了GIL的限制

前言Python中最遭人诟病的设计之一就是GIL。GIL(全局解释器锁)是CPython的一个互斥锁,确保任何时刻只有一个线程可以执行Python字节码,这样可以避免多个线程同时操作内部数据结...

Python Web开发实战:3小时从零搭建个人博客

一、为什么选Python做Web开发?Python在Web领域的优势很突出:o开发快:Django、Flask这些框架把常用功能都封装好了,不用重复写代码,能快速把想法变成能用的产品o需求多:行业...

图解Python编程:从入门到精通系列教程(附全套速查表)

引言本系列教程展开讲解Python编程语言,Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,它也是互联网最热门的编程语言之一。Python生态丰富,库(模块)极其丰富,这使...

Python 并发编程实战:从基础到实战应用

并发编程是提升Python程序效率的关键技能,尤其在处理多任务场景时作用显著。本文将系统介绍Python中主流的并发实现方式,帮助你根据场景选择最优方案。一、多线程编程(threading)核...

吴恩达亲自授课,适合初学者的Python编程课程上线

吴恩达教授开新课了,还是亲自授课!今天,人工智能著名学者、斯坦福大学教授吴恩达在社交平台X上发帖介绍了一门新课程——AIPythonforBeginners,旨在从头开始讲授Python...

Python GUI 编程:tkinter 初学者入门指南——Ttk 小部件

在本文中,将介绍Tkinter.ttk主题小部件,是常规Tkinter小部件的升级版本。Tkinter有两种小部件:经典小部件、主题小部件。Tkinter于1991年推出了经典小部件,...

Python turtle模块编程实践教程

一、模块概述与核心概念1.1turtle模块简介定义:turtle是Python标准库中的2D绘图模块,基于Logo语言的海龟绘图理念实现。核心原理:坐标系系统:原点(0,0)位于画布中心X轴:向右...

Python 中的asyncio 编程入门示例-1

Python的asyncio库是用于编写并发代码的,它使用async/await语法。它为编写异步程序提供了基础,通过非阻塞调用高效处理I/O密集型操作,适用于涉及网络连接、文件I/O...

30天学会Python,开启编程新世界

在当今这个数字化无处不在的时代,Python凭借其精炼的语法架构、卓越的性能以及多元化的应用领域,稳坐编程语言排行榜的前列。无论是投身于数据分析、人工智能的探索,还是Web开发的构建,亦或是自动化办公...

Python基础知识(IO编程)

1.文件读写读写文件是Python语言最常见的IO操作。通过数据盘读写文件的功能都是由操作系统提供的,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个...

Python零基础到精通,这8个入门技巧让你少走弯路,7天速通编程!

Python学习就像玩积木,从最基础的块开始,一步步搭建出复杂的作品。我记得刚开始学Python时也是一头雾水,走了不少弯路。现在回头看,其实掌握几个核心概念,就能快速入门这门编程语言。来聊聊怎么用最...

一文带你了解Python Socket 编程

大家好,我是皮皮。前言Socket又称为套接字,它是所有网络通信的基础。网络通信其实就是进程间的通信,Socket主要是使用IP地址,协议,端口号来标识一个进程。端口号的范围为0~65535(用户端口...

Python-面向对象编程入门

面向对象编程是一种非常流行的编程范式(programmingparadigm),所谓编程范式就是程序设计的方法论,简单的说就是程序员对程序的认知和理解以及他们编写代码的方式。类和对象面向对象编程:把...

取消回复欢迎 发表评论: