【新】使用setuptools打包Python项目

如何使用setuptools打包Python项目

pypi 官网给出了4个打包 whl 格式的工具:HatchlingsetuptoolsFlitPDM

初步了解了一下,还是用 setuptools 。

本文主要内容包括:pyproject.toml 的简单配置、使用 python -m build 打包、使用 cython 编译模块、生成命令等内容。

本文仅为学习经验,深入学习请参考官方文档。

一、准备

安装 setuptools 和 build

pip install --upgrade setuptools    # 65.5.0
# 安装 build 以可以运行命令: python -m build
pip install --upgrade build    # 0.9.0

二、项目结构

以名为 pypackage 的项目为例(src 结构)

pypackage/
├── LICENSE           # LICENSE
├── pyproject.toml    # 项目配置信息
├── README.md         # 自述文件
├── src               # 源码 
│   └── pypackage
│       ├── __init__.py
│       └── __main.py
└── tests             # 相关的测试
    └── test_main.py

还有另一种结构(flat 结构)

pypackage/
├── LICENSE           # LICENSE
├── pyproject.toml    # 项目配置信息
├── README.md         # 自述文件
│── pypackage         # 名字为项目名
│   ├── __init__.py
│   └── __main.py
└── tests             # 相关的测试
    └── test_main.py

setuptools 具有自动检测项目代码目录的功能。另外需要说明的是,使用 src-layout 的形式时,会添加 src 下的所有目录,建议 src 下只保留一个与项目名同名的目录

三、简单打包

3.1 文件内容

src/pypackage/__init__.py

from .__main import say_hello

src/pypackage/__main.py

def say_hello(name: str):
    __private_say(name)

def __private_say(name: str):
    print(f"private say: Hello, {name}!")

tests/test_main.py

TEST_RELEASE = True
if not TEST_RELEASE:
    import sys
    import pathlib
    root_dir = pathlib.Path(__file__).absolute().parent.parent
    sys.path.insert(0, str(root_dir.joinpath("src")))

# using pytest
def test_hello():
    import pypackage 
    pypackage.say_hello("yluuu")

pyproject.toml

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "pypackage"
version = "0.0.1"
dependencies = [
    "requests",
    "rtoml; python_version<'3.8'",
]

3.2 打包项目

默认情况会根据依赖包创建一个隔离的虚拟环境来打包,但我是离线环境,所以不能用这种方式,需要添加 --no-isolation 选项

python -m build --no-isolation

打包如果成功会提示:Successfully built pypackage-0.0.1.tar.gz and pypackage-0.0.1-py3-none-any.whl,共生成了两个文件

3.3 安装测试

使用 pip 安装 whl 文件,查看安装目录 ~/miniconda3/lib/python3.7/site-packages/pypackage

-rw-rw-r--  1 yl yl   30 1031 08:19 __init__.py
-rw-rw-r--  1 yl yl  124 1031 08:19 __main.py
drwxrwxr-x  2 yl yl 4.0K 1031 08:19 __pycache__

在项目根目录下直接运行 pytest 命令,测试没有问题

 ~/test/pypackage ----------------- base py | yl@jisuan01 | 08:30:28 
> pytest
======================== test session starts =========================
platform linux -- Python 3.7.13, pytest-7.2.0, pluggy-1.0.0
rootdir: /home/yl/test/pypackage
collected 1 item                                                     

tests/test_main.py .                                           [100%]

========================= 1 passed in 0.01s ==========================

四、自定义打包

4.1 添加子模块

setuptools 有自动发现的功能:对于常用的两种项目形式(src-layout、flat-layout),setuptools 可以自动扫描并发现代码目录。

添加文件 src/pypackage/lib1/mod1.py

def func1():
    return 1

添加文件 src/pypackage/lib2/mod2.py

def func2():
    return 2

src 及 项目下的模块均可以自动被添加

adding 'pypackage/__init__.py'
adding 'pypackage/__main.py'
adding 'pypackage/lib1/mod1.py'    # 自动添加了
adding 'pypackage/lib2/mod2.py'    # 自动添加了
adding 'pypackage-0.0.1.dist-info/LICENSE'
adding 'pypackage-0.0.1.dist-info/METADATA'
adding 'pypackage-0.0.1.dist-info/WHEEL'
adding 'pypackage-0.0.1.dist-info/top_level.txt'
adding 'pypackage-0.0.1.dist-info/RECORD'

更多请参考: https://setuptools.pypa.io/en/latest/userguide/package_discovery.html

4.2 只生成 whl

添加选项 --wheel 即可

python -m build --no-isolation --wheel

4.3 包含/排除数据

如 添加 keep.parquet 排除 exclude.txt

pypackage/
├── ...
│── src
│   └── pypackage
│       ├── __init__.py
│       ├── __main.py
│       └── data 
│           ├── keep.parquet
│           └── exclude.txt
└── ...

pyproject.toml 添加

[tool.setuptools]
include-package-data = true

[tool.setuptools.package-data]
pypackage = ["data/*.parquet"]

[tool.setuptools.exclude-package-data]
pypackage = ["data/*.txt"]

其实 include-package-data 默认就是 true 的

打包记录

adding 'pypackage/__init__.py'
adding 'pypackage/__main.py'
adding 'pypackage/data/keep.parquet'    # 这里
adding 'pypackage/lib1/mod1.py'
adding 'pypackage/lib2/mod2.py'
adding 'pypackage-0.0.1.dist-info/LICENSE'
adding 'pypackage-0.0.1.dist-info/METADATA'
adding 'pypackage-0.0.1.dist-info/WHEEL'
adding 'pypackage-0.0.1.dist-info/top_level.txt'
adding 'pypackage-0.0.1.dist-info/RECORD'

更多请参考: https://setuptools.pypa.io/en/latest/userguide/datafiles.html

4.4 编译模块

使用 cython

pip install cython --upgrade

创建 setup.py

# 方式一:按 setuptools 文档,使用 Extension 类,使用 cython 编译时无法设置 language_level TODO 待解决
# from setuptools import setup, Extension

# setup(
#     ext_modules=[
#         Extension(
#             name="pypackage.lib1.mod1",
#             sources=["src/pypackage/lib1/mod1.pyx"],
#         )
#     ]
# )

# 方式二:直接调用 cython,可以设置 language_level
# 备注: 子模块尽量添加 __init__.py 以确保编译后的库文件可以正常打包到子模块中。
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize(module_list=[
        "src/pypackage/lib1/mod1.pyx"
    ], language_level=3)
)

另外在 pyproject.toml 中排除 c 文件打包

[tool.setuptools.exclude-package-data]
pypackage = ["info/*.txt"]
"pypackage.lib1" = ["*.c"]

src/pypackage/lib1/mod1.py 重命名为 src/pypackage/lib1/mod1.pyx,并在 src/pypackage/lib1 下添加 __init__.py 文件

然后使用 python -m build 时会自动调用 setup.py 生成库文件并打包

单独编译模块时运行 python setup.py build_ext

备注:测试过程中的坑,都在上面的注释里了

adding 'pypackage/__init__.py'
adding 'pypackage/__main.py'
adding 'pypackage/info/keep.parquet'
adding 'pypackage/lib1/__init__.py'
adding 'pypackage/lib1/mod1.cpython-37m-x86_64-linux-gnu.so'
adding 'pypackage/lib2/mod2.py'
adding 'pypackage-0.0.1.dist-info/LICENSE'
adding 'pypackage-0.0.1.dist-info/METADATA'
adding 'pypackage-0.0.1.dist-info/WHEEL'
adding 'pypackage-0.0.1.dist-info/top_level.txt'
adding 'pypackage-0.0.1.dist-info/RECORD'

4.5 生成可执行命令

__main.py 中添加以下内容作为可执行命令的入口

def hello():
    __private_say("pypackage")

然后在 pypackage.toml 中添加名为 pypackage-hello 的命令(在命令行中可直接运行 pypackage-hello

[project.scripts]
pypackage-hello = "pypackage.__main:hello"

完成。重新打包安装后效果如下:

> pypackage-hello                                             
private say: Hello, pypackage!
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是使用setuptoolsPython项目打包成whl文件的步骤: 1. 确认已经安装了setuptools和wheel模块。如果没有安装,可以在命令行中输入以下命令进行安装: ``` pip3 install setuptools wheel ``` 2. 在项目根目录下创建一个名为`setup.py`的文件,内容如下: ``` from setuptools import setup, find_packages setup( name='your-project-name', version='0.1', packages=find_packages(), install_requires=[ 'dependency1', 'dependency2', # ... 其他依赖项 ], classifiers=[ 'Programming Language :: Python :: 3', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', ], ) ``` 其中,`name`是你的项目名称,`version`是你的项目版本号,`packages`是你的项目中包含的所有Python模块,`install_requires`是你的项目依赖的所有Python包,`classifiers`是一些元数据。 3. 在命令行中进入项目根目录,然后使用以下命令构建一个源码分发包: ``` python3 setup.py sdist ``` 这个命令会在项目根目录下生成一个`dist`目录,其中包含一个源码分发包。 4. 接下来,使用以下命令构建一个二进制分发包: ``` python3 setup.py bdist_wheel ``` 这个命令会在项目根目录下生成一个`dist`目录,其中包含一个whl文件。 5. 最后,你可以使用以下命令来安装你的项目: ``` pip3 install your-project-name-0.1-py3-none-any.whl ``` 其中,`your-project-name-0.1`是你的项目名称和版本号,`py3-none-any`表示这是一个纯Python的whl文件。 以上就是使用setuptoolsPython项目打包成whl文件的步骤。希望对你有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值