Skip to content

Latest commit

 

History

History
296 lines (213 loc) · 10.8 KB

File metadata and controls

296 lines (213 loc) · 10.8 KB

十三、包和分发

打包和分发 Python 代码可能是一项复杂且有时令人困惑的任务,尤其是当您的项目有很多依赖项或涉及比直接 Python 代码更奇特的组件时。然而,在许多情况下,以标准方式让其他人可以访问您的代码是非常简单的,我们将在本节中使用标准的distutils模块来了解如何做到这一点。distutils的主要优点是它包含在 Python 标准库中。除了最简单的打包需求之外,您可能还想看看安装工具,它的功能超出了distutils的功能,但相应地,它更容易混淆。

distutils 模块允许您编写一个简单的 Python 脚本,它知道如何将 Python 模块安装到任何 Python 安装中,包括托管在虚拟环境中的 Python 安装。按照惯例,这个脚本称为setup.py,它存在于项目结构的顶层。然后可以执行此脚本以执行实际安装。

使用 distutils 配置包

让我们看一个简单的distutils示例。我们将为我们在第 11 章与 PDB调试中编写的回文模块创建一个基本的setup.py安装脚本。

我们要做的第一件事是创建一个目录来保存我们的项目。我们称之为palindrome

$ mkdir palindrome
$ cd palindrome

让我们把我们的palindrome.py副本放在这个目录中:

"""palindrome.py - Detect palindromic integers"""

import unittest

def digits(x):
 """Convert an integer into a list of digits.

 Args:
 x: The number whose digits we want.

 Returns: A list of the digits, in order, of ``x``.

 >>> digits(4586378)
 [4, 5, 8, 6, 3, 7, 8]
 """

 digs = []
 while x != 0:
 div, mod = divmod(x, 10)
 digs.append(mod)
 x = div
 return digs

def is_palindrome(x):
 """Determine if an integer is a palindrome.

 Args:
 x: The number to check for palindromicity.

 Returns: True if the digits of ``x`` are a palindrome,
 False otherwise.

 >>> is_palindrome(1234)
 False
 >>> is_palindrome(2468642)
 True
 """
 digs = digits(x)
 for f, r in zip(digs, reversed(digs)):
 if f != r:
 return False
 return True

class Tests(unittest.TestCase):
 "Tests for the ``is_palindrome()`` function."
 def test_negative(self):
 "Check that it returns False correctly."
 self.assertFalse(is_palindrome(1234))

 def test_positive(self):
 "Check that it returns True correctly."
 self.assertTrue(is_palindrome(1234321))

 def test_single_digit(self):
 "Check that it works for single digit numbers."
 for i in range(10):
 self.assertTrue(is_palindrome(i))

if __name__ == '__main__':
 unittest.main()

最后让我们创建setup.py脚本:

from distutils.core import setup

setup(
 name = 'palindrome',
 version = '1.0',
 py_modules  = ['palindrome'],

 # metadata
 author = 'Austin Bingham',
 author_email = 'austin@sixty-north.com',
 description = 'A module for finding palindromic integers.',
 license = 'Public domain',
 keywords = 'palindrome',
 )

文件中的第一行从distutils.core模块导入我们需要的功能,即setup()函数。这个函数完成了安装代码的所有工作,所以我们需要告诉它我们正在安装的代码。当然,我们通过传递给函数的参数来实现这一点。

我们首先告诉setup()的是这个项目的名称。在本例中,我们选择了回文,但您可以选择任何您喜欢的名称。不过,一般来说,最简单的方法是将名称与项目名称保持一致。

我们传递给setup()的下一个参数是版本。同样,这可以是您想要的任何字符串。Python 不依赖该版本来遵循任何规则。

下一个论点py_modules可能是最有趣的。我们使用它来指定要安装的 Python 模块。此列表中的每个条目都是不带.py扩展名的模块名称。setup()功能将查找匹配的.py文件并安装它。因此,在我们的示例中,我们要求setup()安装palindrome.py,当然,这是我们项目中的一个文件。

我们在这里使用的其他论点都是不言自明的,主要是为了帮助人们正确使用您的模块,并知道如果他们有问题,应该联系谁。

在开始使用setup.py之前,我们首先需要创建一个虚拟环境,在其中安装模块。在回文目录中,创建一个名为palindrome_env的虚拟环境:

$ python3 -m venv palindrome_env

完成后,激活新环境。在 Linux 或 macOS 上,源激活脚本:

$ source palindrome_env/bin/activate

或在 Windows 上直接调用脚本:

> palindrome_env\bin\activate

用 distutils 安装

现在我们有了setup.py,我们可以用它做很多有趣的事情。首先,也许是最明显的,我们可以做的是将我们的模块安装到我们的虚拟环境中!我们通过将 install 参数传递给setup.py来实现这一点:

(palindrome_env)$ python setup.py install
running install
running build
running build_py
copying palindrome.py -> build/lib
running install_lib
copying build/lib/palindrome.py -> /Users/sixty_north/examples/palindrome/palindrome_env/lib/python3.5/site-packages
byte-compiling /Users/sixty_north/examples/palindrome/palindrome_env/lib/python3.5/site-packages/palindrome.py to palindrome.cpython-35.pyc
running install_egg_info
Writing /Users/sixty_north/examples/palindrome/palindrome_env/lib/python3.5/site-packages/palindrome-1.0-py3.5.egg-info

调用时,setup()打印出几行,告诉您其进度。对我们来说,最重要的一行是将palindrome.py复制到安装文件夹的实际位置:

copying build/lib/palindrome.py -> /Users/sixty_north/examples/palindrome/palindrome_env/lib/python3.5/site-packages

Python 安装的site-packages目录通常是安装像我们这样的第三方软件包的地方。看来安装工作正常。

让我们通过运行 Python 并查看模块是否可以导入来验证这一点。请注意,我们希望在执行此操作之前更改目录,否则,当我们导入回文时,Python 只需在当前目录中加载源文件:

(palindrome_env)$ cd ..
(palindrome_env)$ python
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 26 2016, 10:47:25)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import palindrome
>>> palindrome.__file__
'/Users/sixty_north/examples/palindrome/palindrome_env/lib/python3.5/site-packages/palindrome.py'

在这里,我们使用模块上的__file__属性来查看它是从何处导入的,我们看到它是从虚拟环境的site-packages导入的,这正是我们想要的。

退出 Python REPL 后,不要忘记切换回源目录:

(palindrome_env)$ cd palindrome

带蒸馏器的包

setup()的另一个有用特性是,它可以创建各种类型的“分发”格式。它将把您指定的所有模块打包成易于分发给其他人的包。您可以使用sdist命令(这是“源代码分发”的缩写)来完成此操作:

(palindrome_env)$ python setup.py sdist --format zip
running sdist
running check
warning: check: missing required meta-data: url

warning: sdist: manifest template 'MANIFEST.in' does not exist (using default file list)

warning: sdist: standard file not found: should have one of README, README.txt

writing manifest file 'MANIFEST'
creating palindrome-1.0
making hard links in palindrome-1.0...
hard linking palindrome.py -> palindrome-1.0
hard linking setup.py -> palindrome-1.0
creating dist
creating 'dist/palindrome-1.0.zip' and adding 'palindrome-1.0' to it
adding 'palindrome-1.0/palindrome.py'
adding 'palindrome-1.0/PKG-INFO'
adding 'palindrome-1.0/setup.py'
removing 'palindrome-1.0' (and everything under it)

如果我们看一下,我们将看到该命令创建了一个新目录dist,其中包含新生成的分发文件:

(palindrome_env) $ ls dist
palindrome-1.0.zip

如果我们解压缩该文件,我们将看到它包含我们项目的源代码,包括setup.py

(palindrome_env)$ cd dist
(palindrome_env)$ unzip palindrome-1.0.zip
Archive:  palindrome-1.0.zip
 inflating: palindrome-1.0/palindrome.py
 inflating: palindrome-1.0/PKG-INFO
 inflating: palindrome-1.0/setup.py

所以现在你可以将这个 zip 文件发送给任何想使用你的代码的人,他们可以使用setup.py将它安装到他们的系统中。非常方便!

注意,sdist命令可以生成各种类型的分布。要查看可用选项,您可以使用--help-formats选项:

(palindrome_env) $ python setup.py sdist --help-formats
List of available source distribution formats:
 --formats=bztar  bzip2'ed tar-file
 --formats=gztar  gzip'ed tar-file
 --formats=tar    uncompressed tar file
 --formats=zip    ZIP file
 --formats=ztar   compressed tar file

本节仅涉及distutils的基本内容。通过将--help传递给setup.py,您可以了解更多有关如何使用distutils的信息:

(palindrome_env) $ python setup.py --help
Common commands: (see '--help-commands' for more)

 setup.py build      will build the package underneath 'build/'
 setup.py install    will install the package

Global options:
 --verbose (-v)      run verbosely (default)
 --quiet (-q)        run quietly (turns verbosity off)
 --dry-run (-n)      don't actually do anything
 --help (-h)         show detailed help message
 --command-packages  list of packages that provide distutils commands

Information display options (just display information, ignore any commands)
 --help-commands     list all available commands
 --name              print package name
 --version (-V)      print package version
 --fullname          print <package name>-<version>
 --author            print the author's name
 --author-email      print the author's email address
 --maintainer        print the maintainer's name
 --maintainer-email  print the maintainer's email address
 --contact           print the maintainer's name if known, else the author's
 --contact-email     print the maintainer's email address if known, else the
 author's
 --url               print the URL for this package
 --license           print the license of the package
 --licence           alias for --license
 --description       print the package description
 --long-description  print the long package description
 --platforms         print the list of platforms
 --classifiers       print the list of classifiers
 --keywords          print the list of keywords
 --provides          print the list of packages/modules provided
 --requires          print the list of packages/modules required
 --obsoletes         print the list of packages/modules made obsolete

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
 or: setup.py --help [cmd1 cmd2 ...]
 or: setup.py --help-commands
 or: setup.py cmd --help

对于许多简单的项目,您会发现我们刚才介绍的内容几乎是您需要知道的全部。