Python 高手编程系列四百四十四:常见模式

对于没有经验的开发者来说,创建一个用于分发的包可能是一项乏味的任务。如果不
考虑元数据可能在项目其他部分找到的事实,setuptools 或 distuitls 在 setup()
函数调用中接受的大多数元数据都可以手动输入,代码如下:
from setuptools import setup
setup(
name=“myproject”,
version=“0.0.1”,
description=“mypackage project short description”,
long_description=“”"
Longer description of mypackage project
possibly with some documentation and/or
usage examples
“”",
install_requires=[
‘dependency1’,
‘dependency2’,
‘etc’,
]
)
这么做当然可行,但从长远来看很难维护,并且未来可能会出现错误和不一致。
setuptools 和 distuitls 都不能从项目源代码中自动提取各种元数据信息,因此你需
要自己提供这些信息。在 Python 社区中有一些常见模式可以解决最常见的问题,例如依赖
管理、包含版本/自述文件等。至少应该知道其中一些模式,因为它们非常流行,已经被看
做一种打包惯例(packaging idioms)。
自动包含包中的版本字符串
PEP 440(版本标识和依赖规范,Version Identification and Dependency Specification)
文档规定了版本和依赖规范的标准。这是一份很长的文档,包含已接受的版本规范方案和
Python 打包工具中应该如何做版本匹配和比较。如果你正在使用或打算使用一种复杂的项
目版本编号方案,那么一定要阅读这份文档。如果你使用的是一种简单方案,其中包含用
点分开的一个、两个、三个或更多的数字,那么可以不必阅读 PEP 440。如果你不知道如
何选择合适的版本方案,我强烈建议遵循第 1 章中已经提到过的语义化版本。
另一个问题是将包或模块的版本标识符包含在什么位置。PEP 396 (模块版本号,Module
Version Numbers)正好解决了这个问题。注意,这份文档只是信息性的(informational),
并且状态为延期(deferred),所以它并不是标准路径(standards track)的一部分。不管怎
样,它描述的内容现在似乎成了事实上的标准。根据 PEP 396,如果一个包或模块要指定
一个版本,那么应该将其包含在包的根目录(init.py)或模块文件的__version__
属性中。另一个事实上的标准是,也要将包括版本元组的 VERSION 属性包含其中。这有
助于用户编写兼容代码,因为如果版本方案足够简单的话,这样的版本元组很容易比较。
因此,PyPI 上的很多包都遵循这两个标准。它们的__init__.py 文件包含如下所示
的版本属性,如下所示:

用元组表示版本,可以简单比较

VERSION = (0, 1, 1)

利用元组创建字符串,以避免出现不一致

version = “.”.join([str(x) for x in VERSION])
延期的 PEP 396 的另一个建议是,在 distutils 的 setup()函数中提供的版本应该
从__version__派生,反之亦然。Python 打包用户指南为单一来源的项目版本提供了多种
模式,每一种都有自己的优点和局限性。我个人最喜欢相当长的,并没有包含在 PyPA 的
指南中,但它的优点是仅限制 setup.py 脚本的复杂度。这个样板假定,版本标识符由包
的__init__模块的 VERSION 属性给出,并且提取这一数据包含在 setup()调用中。下面
是某个虚构的包的 setup.py 脚本中的片段,其中使用了以下这种方法:
from setuptools import setup
import os
def get_version(version_tuple):

additional handling of a,b,rc tags, this can

be simpler depending on your versioning scheme

if not isinstance(version_tuple[-1], int):
return ‘.’.join(
map(str, version_tuple[:-1])
) + version_tuple[-1]
return ‘.’.join(map(str, version_tuple))

path to the packages init module in project

source tree

init = os.path.join(
os.path.dirname(file), ‘src’, ‘some_package’,
init.py’
)
version_line = list(
filter(lambda l: l.startswith(‘VERSION’), open(init))
)[0]

VERSION is a tuple so we need to eval its line of code.

We could simply import it from the package but we

cannot be sure that this package is importable before

finishing its installation

VERSION = get_version(eval(version_line.split(‘=’)[-1]))
setup(
name=‘some-package’,
version=VERSION,

)
README 文件
Python 包索引可以在 PyPI 门户的包页面中显示一个项目的 readme 或者 long_
description 的值。你可以用 reStructuredText 标记来编写这个说明,它在上传时会转换为
HTML 格式。不幸的是,目前 PyPI 上的文档标记只能使用 reStructuredText。这在短期内也
不太可能改变。更有可能的是,如果 warehouse 项目完全取代了当前的 PyPI 实现,那么
将会支持其他标记语言。不幸的是,我们仍然不知道 warehouse 的最终发布时间。
但是,许多开发者想要使用不同的标记语言,原因有很多。最常见的选择是 Markdown,
它是 GitHub 上默认的标记语言 — 目前大多数开源的 Python 开发都是在 GitHub 上。因此,
GitHub 和 Markdown 的粉丝通常要么忽略这个问题,要么就提供两份独立的文档文本。提
供给 PyPI 的说明要么是项目 GitHub 页面上说明的简短版本,要么是在 PyPI 上无法正常显
示的普通的无格式 Markdown。
如果你想使用除了 reStructuredText 之外的标记语言来编写项目的 README,你仍然
可以用可读的形式将它作为 PyPI 页面上的项目说明。诀窍是在将包上传到 Python 包索引
时使用 pypandoc 包将你使用的其他脚本语言转换成 reStructuredText。同时准备 readme
文件的简单内容作为备用(fallback)也很重要,这样即使用户没有安装 pypandoc,安装
也不会失败,代码如下:
try:
from pypandoc import convert
def read_md(f):
return convert(f, ‘rst’)
except ImportError:
convert = None
print(
“warning: pypandoc module not found, could not convert
Markdown to RST”
)
def read_md(f):
return open(f, ‘r’).read() # noqa
README = os.path.join(os.path.dirname(file), ‘README.md’)
setup(
name=‘some-package’,
long_description=read_md(README),

)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值