这个手册指导你如何打包简单的python项目,其包括如何添加必须的文件和结构去创建一个包,如何编包和如何上传包到PYPI。
一个简单的项目
这个手册使用了一个非常简单的项目叫example_pkg。如果你不熟悉python的模块理念和‘import packages’的用法,建议先阅读“python的包和模块”。如果你已有项目而只想打包,我们仍建议跟着我们先使用这个实例项目,然后再尝试自己的项目。
在本地创建以下目录结构以创建常见项目:
[wlin@wlin ~]$ tree packaging_tutorial/
packaging_tutorial/
└── example_pkg
└── __init__.py
1 directory, 1 file
创建完这个结构以后,如果你想在根据运行本手册中用到的所有指令,则必须按照以下步骤进行配置:
[wlin@wlin ~]$ cd packaging_tutorial/
[wlin@wlin packaging_tutorial]$ echo 'name = "example_pkg"'
name = "example_pkg"
[wlin@wlin packaging_tutorial]$ echo 'name = "example_pkg"' > example_pkg/__init__.py
[wlin@wlin packaging_tutorial]$ cat example_pkg/__init__.py
name = "example_pkg"
这样做可以让我们直接验证这个包正确安装,而不需要使用PyPI
创建包文件
现在我们需要创建打包这个项目所需要的文件并为分发它做准备。创建以下目录结构:
[wlin@wlin packaging_tutorial]$ tree
.
├── example_pkg
│ └── __init__.py
├── LICENSE
├── README.md
└── setup.py
创建setup.py
setup.py是setuptools的编译脚本。它告诉setuptools你的包的名字,版本和所有包含的文件。打开setup.py输入以下内容。更新包名使之包含你的用户名,这就保证你的包名的唯一,避免了和其他参考该手册的人的包名冲突。
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="example-pkg-carawang",
version="0.0.1",
author="Cara Wang",
author_email="bookview@126.com",
description="A small example package",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/pypa/sampleproject",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
)
setup()需要配置一些参数。这个示例包仅仅用了相对最小的集合:
- name: 你分发时包的名字。这个名字可以是任意长度的包含数字,字母,连接线和下划线的字符串,但是其不能和Pypi上已存在的包重明。
- version:你的包的版本。关于如何定义版本,可参考PEP 440
- author和author_email:作为作者的你的名字和邮箱
- description: 关于你的包的一句简短的总结
- long_description: 关于包的详细说明。这会在PyPI这个的介绍界面显示。通常,这个长说明建议直接加载README.md,而它的格式就是Markdown
- url: 项目的家目录。对于许多项目来说,其可以是GitHub, GitLab, Bitbucket或者其他功能相似的代码托管服务。
- packages: 该项目中引入的所有的packages都需要包含的这个分发包中。比起手动添加所有包,我们建议使用‘find_packages’去自动发现所有包和其子包。在本例中,则就只有example_pkg一个包。
- classifiers: 给出了索引和pip会用到一些额外的元数据。这些数据会在你调用‘pip show package_name’时显示。在本例中,包支持Python 3, MIT许可以及OS系统。这三组数据也是我们必须提供的最小组合。其他的classifier,可参考手册
生成README.md
打开README.md并输入以下内容,你也可以自己编写。
# Example Package
This is a simple example package.
You can use [Github-flavored Markdown](https://guides.github.com/features/mastering-markdown/) to write your content.
生成许可
每一个上传到PyPI的包都必须有许可。这个许可告诉安装这个包的用户基于哪些条款可以使用你的包。可到发布许可的网站上选择相应的证书,并复制其内容保存为你的许可即可。
如我们要提供的MIT的证书,则我们的证书内容为:
Copyright (c) 2018 The Python Packaging Authority
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.
生成分发归档
接下来为这个包生成分发包。分发包是可以归档到PyPI上并使用pip进行安装的。
首先,升级你的setuptools和wheel到最新版本,如下:
[wlin@wlin packaging_tutorial]$ python3 -m pip install --user --upgrade setuptools wheel
Collecting setuptools
Downloading https://files.pythonhosted.org/packages/b2/86/095d2f7829badc207c893dd4ac767e871f6cd547145df797ea26baea4e2e/setuptools-41.2.0-py2.py3-none-any.whl (576kB)
|████████████████████████████████| 583kB 824kB/s
Collecting wheel
Downloading https://files.pythonhosted.org/packages/00/83/b4a77d044e78ad1a45610eb88f745be2fd2c6d658f9798a15e384b7d57c9/wheel-0.33.6-py2.py3-none-any.whl
Installing collected packages: setuptools, wheel
Successfully installed setuptools-41.2.0 wheel-0.33.6
然后,执行以下指令:
[wlin@wlin packaging_tutorial]$ ls -al setup.py
-rw-rw-r--. 1 wlin wlin 642 Sep 24 18:06 setup.py
[wlin@wlin packaging_tutorial]$ python3 setup.py sdist bdist_wheel
running sdist
running egg_info
writing example_pkg_carawang.egg-info/PKG-INFO
writing dependency_links to example_pkg_carawang.egg-info/dependency_links.txt
writing top-level names to example_pkg_carawang.egg-info/top_level.txt
reading manifest file 'example_pkg_carawang.egg-info/SOURCES.txt'
writing manifest file 'example_pkg_carawang.egg-info/SOURCES.txt'
running check
creating example-pkg-carawang-0.0.1
creating example-pkg-carawang-0.0.1/example_pkg
creating example-pkg-carawang-0.0.1/example_pkg_carawang.egg-info
copying files to example-pkg-carawang-0.0.1...
copying README.md -> example-pkg-carawang-0.0.1
copying setup.py -> example-pkg-carawang-0.0.1
copying example_pkg/__init__.py -> example-pkg-carawang-0.0.1/example_pkg
copying example_pkg_carawang.egg-info/PKG-INFO -> example-pkg-carawang-0.0.1/example_pkg_carawang.egg-info
copying example_pkg_carawang.egg-info/SOURCES.txt -> example-pkg-carawang-0.0.1/example_pkg_carawang.egg-info
copying example_pkg_carawang.egg-info/dependency_links.txt -> example-pkg-carawang-0.0.1/example_pkg_carawang.egg-info
copying example_pkg_carawang.egg-info/top_level.txt -> example-pkg-carawang-0.0.1/example_pkg_carawang.egg-info
Writing example-pkg-carawang-0.0.1/setup.cfg
Creating tar archive
removing 'example-pkg-carawang-0.0.1' (and everything under it)
running bdist_wheel
running build
running build_py
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64/wheel
creating build/bdist.linux-x86_64/wheel/example_pkg
copying build/lib/example_pkg/__init__.py -> build/bdist.linux-x86_64/wheel/example_pkg
running install_egg_info
Copying example_pkg_carawang.egg-info to build/bdist.linux-x86_64/wheel/example_pkg_carawang-0.0.1-py3.7.egg-info
running install_scripts
adding license file "LICENSE" (matched pattern "LICEN[CS]E*")
creating build/bdist.linux-x86_64/wheel/example_pkg_carawang-0.0.1.dist-info/WHEEL
creating 'dist/example_pkg_carawang-0.0.1-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'example_pkg/__init__.py'
adding 'example_pkg_carawang-0.0.1.dist-info/LICENSE'
adding 'example_pkg_carawang-0.0.1.dist-info/METADATA'
adding 'example_pkg_carawang-0.0.1.dist-info/WHEEL'
adding 'example_pkg_carawang-0.0.1.dist-info/top_level.txt'
adding 'example_pkg_carawang-0.0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
该指令生成了许多东西,最重要的是dist目录下的包文件,如下:
[wlin@wlin packaging_tutorial]$ ls -al dist/
total 16
drwxrwxr-x. 2 wlin wlin 4096 Sep 24 19:17 .
drwxrwxr-x. 6 wlin wlin 4096 Sep 24 19:17 ..
-rw-rw-r--. 1 wlin wlin 2425 Sep 24 19:17 example_pkg_carawang-0.0.1-py3-none-any.whl
-rw-rw-r--. 1 wlin wlin 1163 Sep 24 19:17 example-pkg-carawang-0.0.1.tar.gz
tar.gz文件是源码包,.whl是分发包。 新版的pip会优先安装.whl文件,备选安装源码包。你最好两种都上传。
上传分发包
最后,是时候上传包了。首先你需要在Test PyPI上申请一个帐号。Test PyPI是和PyPI一样但是又独立的提供测试和实验的平台。像我们这种实验性质的包,上传到Test PyPI上即可。等注册完帐号之后,则可以使用twine进行上传。如下:
先安装最新的twine
[wlin@wlin packaging_tutorial]$ python3 -m pip install --user --upgrade twine
Collecting twine
Using cached https://files.pythonhosted.org/packages/23/0e/9e833399b84e9232a7f86a8f4351d6687dbab0b03a2a6e352d431463fa68/twine-1.15.0-py2.py3-none-any.whl
Collecting requests!=2.15,!=2.16,>=2.5.0 (from twine)
Using cached https://files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl
Collecting tqdm>=4.14 (from twine)
Using cached https://files.pythonhosted.org/packages/e1/c1/bc1dba38b48f4ae3c4428aea669c5e27bd5a7642a74c8348451e0bd8ff86/tqdm-4.36.1-py2.py3-none-any.whl
Collecting readme-renderer>=21.0 (from twine)
Using cached https://files.pythonhosted.org/packages/c3/7e/d1aae793900f36b097cbfcc5e70eef82b5b56423a6c52a36dce51fedd8f0/readme_renderer-24.0-py2.py3-none-any.whl
Collecting requests-toolbelt!=0.9.0,>=0.8.0 (from twine)
Using cached https://files.pythonhosted.org/packages/60/ef/7681134338fc097acef8d9b2f8abe0458e4d87559c689a8c306d0957ece5/requests_toolbelt-0.9.1-py2.py3-none-any.whl
Collecting pkginfo>=1.4.2 (from twine)
Using cached https://files.pythonhosted.org/packages/e6/d5/451b913307b478c49eb29084916639dc53a88489b993530fed0a66bab8b9/pkginfo-1.5.0.1-py2.py3-none-any.whl
Requirement already satisfied, skipping upgrade: setuptools>=0.7.0 in /home/wlin/.local/lib/python3.7/site-packages (from twine) (41.2.0)
Collecting chardet<3.1.0,>=3.0.2 (from requests!=2.15,!=2.16,>=2.5.0->twine)
Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Collecting idna<2.9,>=2.5 (from requests!=2.15,!=2.16,>=2.5.0->twine)
Using cached https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl
Collecting certifi>=2017.4.17 (from requests!=2.15,!=2.16,>=2.5.0->twine)
Downloading https://files.pythonhosted.org/packages/18/b0/8146a4f8dd402f60744fa380bc73ca47303cccf8b9190fd16a827281eac2/certifi-2019.9.11-py2.py3-none-any.whl (154kB)
|████████████████████████████████| 163kB 830kB/s
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests!=2.15,!=2.16,>=2.5.0->twine)
Downloading https://files.pythonhosted.org/packages/81/b7/cef47224900ca67078ed6e2db51342796007433ad38329558f56a15255f5/urllib3-1.25.5-py2.py3-none-any.whl (125kB)
|████████████████████████████████| 133kB 11.9MB/s
Collecting docutils>=0.13.1 (from readme-renderer>=21.0->twine)
Using cached https://files.pythonhosted.org/packages/22/cd/a6aa959dca619918ccb55023b4cb151949c64d4d5d55b3f4ffd7eee0c6e8/docutils-0.15.2-py3-none-any.whl
Requirement already satisfied, skipping upgrade: six in /home/wlin/.local/lib/python3.7/site-packages (from readme-renderer>=21.0->twine) (1.12.0)
Collecting bleach>=2.1.0 (from readme-renderer>=21.0->twine)
Using cached https://files.pythonhosted.org/packages/ab/05/27e1466475e816d3001efb6e0a85a819be17411420494a1e602c36f8299d/bleach-3.1.0-py2.py3-none-any.whl
Collecting Pygments (from readme-renderer>=21.0->twine)
Downloading https://files.pythonhosted.org/packages/5c/73/1dfa428150e3ccb0fa3e68db406e5be48698f2a979ccbcec795f28f44048/Pygments-2.4.2-py2.py3-none-any.whl (883kB)
|████████████████████████████████| 890kB 10.6MB/s
Requirement already satisfied, skipping upgrade: webencodings in /home/wlin/.local/lib/python3.7/site-packages (from bleach>=2.1.0->readme-renderer>=21.0->twine) (0.5.1)
Installing collected packages: chardet, idna, certifi, urllib3, requests, tqdm, docutils, bleach, Pygments, readme-renderer, requests-toolbelt, pkginfo, twine
Successfully installed Pygments-2.4.2 bleach-3.1.0 certifi-2019.9.11 chardet-3.0.4 docutils-0.15.2 idna-2.8 pkginfo-1.5.0.1 readme-renderer-24.0 requests-2.22.0 requests-toolbelt-0.9.1 tqdm-4.36.1 twine-1.15.0 urllib3-1.25.5
WARNING: You are using pip version 19.1.1, however version 19.2.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
然后用twine进行上传, 如下:
[wlin@wlin packaging_tutorial]$ python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
Enter your username: carawang
Enter your password:
Uploading distributions to https://test.pypi.org/legacy/
Uploading example_pkg_carawang-0.0.1-py3-none-any.whl
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 5.60k/5.60k [00:02<00:00, 2.27kB/s]
Uploading example-pkg-carawang-0.0.1.tar.gz
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4.34k/4.34k [00:01<00:00, 3.06kB/s]
View at:
https://test.pypi.org/project/example-pkg-carawang/0.0.1/
如上所示,你可以在Test PyPI上访问自己的包了。
安装你新上传的包
你可以使用pip去安装你的包并验证其是否工作。启动一个virtualenv,如下:
[wlin@wlin packaging_tutorial]$ virtualenv test
Using base prefix '/usr/local'
New python executable in /home/wlin/packaging_tutorial/test/bin/python3.5
Also creating executable in /home/wlin/packaging_tutorial/test/bin/python
Installing setuptools, pip, wheel...
done.
[wlin@wlin packaging_tutorial]$ ls
build dist example_pkg example_pkg_carawang.egg-info LICENSE README.md setup.py test
[wlin@wlin packaging_tutorial]$ source test/bin/activate
(test) [wlin@wlin packaging_tutorial]$
用pip安装我们的包,如下:
[wlin@wlin packaging_tutorial]$ python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-pkg-carawang
Looking in indexes: https://test.pypi.org/simple/
Requirement already satisfied: example-pkg-carawang in /home/wlin/packaging_tutorial (0.0.1)
WARNING: You are using pip version 19.1.1, however version 19.2.3 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[wlin@wlin packaging_tutorial]$ ipython
Python 3.6.5 (default, Mar 28 2019, 14:44:43)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import example_pkg
In [2]: example_pkg.name
Out[2]: 'example_pkg'
注意:
例子中用的--index-url标志用来指定Test PyPI。如果不用--index-url, 则默认pip会去PyPI上查找。 另外,我们知道这个包没有依赖,所以我们指定--no-deps。
还需要注意的是,Test PyPI只是用来练习和测试的,所有不提供数据的永久存储。系统可能会删除帐号和包。所以,如果你正式发布你的包,请使用PyPI