专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
OSC开源社区  ·  Bun ... ·  21 小时前  
码农翻身  ·  漫画 | 为什么大家都愿意进入外企? ·  昨天  
程序员的那些事  ·  清华大学:DeepSeek + ... ·  2 天前  
程序员的那些事  ·  OpenAI ... ·  昨天  
程序员小灰  ·  清华大学《DeepSeek学习手册》(全5册) ·  2 天前  
51好读  ›  专栏  ›  SegmentFault思否

Python 打包系统简单入门

SegmentFault思否  · 公众号  · 程序员  · 2018-10-10 08:00

正文

最近把pyenv、pipenv这种都研究了一下,然后我发现一个严重的问题:就是我虽然看了半天这些工具,但是我对Python自己的打包系统却完全没有了解。所以这篇文章就来研究一下Python自带的打包系统。

pip

先来详细介绍一下pip的用法,平时基本上我们用pip的时候也就是一个 pip install 。其实pip也有很多特性,在此先介绍一下常用的一些特性。此部分参考了pip文档,想了解更多的话可以看原文。

安装

最常用的命令就是安装了,除此以外还可以指定版本号:

  1. $ pip install SomePackage            # 不指定版本号,安装最新版

  2. $ pip install SomePackage==1.0.4     # 指定版本号

  3. $ pip install 'SomePackage>=1.0.4'     # 指定最小版本号

  4. $ pip install -r requirements.txt # 从需求文件安装

  5. $ pip install -e . # 从本地项目setup.py安装

使用代理服务器

当从官方的PyPI源安装比较慢的时候,可以考虑使用代理服务器,指定代理服务器的方法有三种:

  • 使用 -- proxy 参数在命令行指定,代理格式为 [ user : passwd@ ] proxy . server : port

  • 在配置文件中指定。

  • 设置 http_proxy , https_proxy no_proxy 环境变量。

使用需求文件(requirements.txt)

在需要很多pip包的项目中,用pip一个个安装包不是一个好办法,这时候可以考虑使用需求文件。

如果要生成需求文件,用下面的命令。这会将当前Python环境中的所有包的当前版本状态保存下来,将来安装的时候会精确还原到冻结的那个状态。

  1. pip freeze > requirements.txt

要从需求文件中安装,则是用下面的命令:

  1. pip install -r requirements.txt

官方文档还给出了一个带注释的实例需求文件:

  1. #

  2. ####### example-requirements.txt #######

  3. #

  4. ###### 没有版本标识符的包,会安装最新版 ######

  5. nose

  6. nose-cov

  7. beautifulsoup4

  8. #

  9. ###### 带版本标识符的包 ######

  10. #  版本标识符的资料 https://www.python.org/dev/peps/pep-0440/#version-specifiers

  11. docopt == 0.6.1             # Version Matching. Must be version 0.6.1

  12. keyring >= 4.1.1            # Minimum version 4.1.1

  13. coverage != 3.5             # Version Exclusion. Anything except version 3.5

  14. Mopidy-Dirble ~= 1.1        # Compatible release. Same as >= 1.1, == 1.*

  15. #

  16. ###### 还可以指定其他的需求文件 ######

  17. -r other-requirements.txt

  18. #

  19. #

  20. ###### 还可以指定本地货网络上的特定包 ######

  21. ./downloads/numpy-1.9.2-cp34-none-win32.whl

  22. http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl

  23. #

  24. ###### Additional Requirements without Version Specifiers ######

  25. #   和第一部分一样,这里这些部分没有顺序需求,可以随意改变位置

  26. rejected

  27. green

  28. #

版本标识符用来指定包的版本,有以下几个例子:

  1. SomeProject

  2. SomeProject == 1.3

  3. SomeProject >=1.2,<.>2.0

  4. SomeProject[foo, bar]

  5. SomeProject~=1.4.2

从6.0版本开始,pip也支持环境标记(也就是分号后面跟Python版本或者系统类型):

  1. SomeProject ==5.4 ; python_version < '2.7'

  2. SomeProject; sys_platform == 'win32'

卸载

卸载某个包使用下面的命令:

  1. $ pip uninstall SomePackage

列出包

要列出所有已安装的包:

  1. $ pip list

  2. docutils (0.9.1)

  3. Jinja2 (2.6)

  4. Pygments (1.5)

  5. Sphinx (1.1.2)

要列出过时的包:

  1. $ pip list --outdated

  2. docutils (Current: 0.9.1 Latest: 0.10)

  3. Sphinx (Current: 1.1.2 Latest: 1.1.3)

要列出某个已安装的包的详细信息:

  1. $ pip show sphinx

  2. ---

  3. Name: Sphinx

  4. Version: 1.1.3

  5. Location: /my/env/lib/pythonx.x/site-packages

  6. Requires: Pygments, Jinja2, docutils

搜索

要搜索一个包,用下面的命令,搜索结果可能有很多:

  1. $ pip search "query"

更新

要更新一个包,使用 - U 或者 -- upgrade 参数:

  1. pip install -U

如果想更新所有的包,很遗憾,pip并没有提供该功能,我在StackOverFlow上找到一个看起来比较简单的解决办法,就是在Python解释器中执行下面的代码:

  1. import pkg_resources

  2. from subprocess import call

  3. packages = [dist.project_name for dist in pkg_resources.working_set]

  4. call("pip install --upgrade " + ' '.join(packages), shell=True)

以上就是pip的一些简单用法,详情可参考官方文档。

打包项目

下面就进入本文的正题,Python的打包系统上。基本上我们不需要完全了解打包系统,只要学会简单的几个点就可以打包自己的类库了。打包需要distutils、setuptools、wheel等类库,不过基本上我们只需要写好其中最重要的setup.py,就可以完成打包工作了。distutils是官方的类库,在当年有很广泛的使用,不过到了现在很难用。distutuils类库的核心就是setup函数,我们需要将项目的各种信息作为参数传递给setup函数,然后就可以用相关命令创建项目分发包了。关于distutils的用法,可以参考官方文档。

当然现在项目基本都不用distutils了,有更好用的第三方替代品,那就是setuptools,它可以算作是distutils的加强版,功能更加强大、使用更加简单,这就是这里要介绍的。其实从文档就可以看出来,distutils毕竟时间比较早,有些接口设计的不太合理甚至有些反人类,setuptools的文档就简单多了。

准备项目

为了做演示,首先需要准备一个项目,一个项目应该包括README和LICENSE等文件,README文件是Markdown格式的文本文件,用于描述项目自身;LICENSE文件是授权文件,列出项目使用者应该遵循的各种条款。下图是我的项目结构。

此外还可能存在几个文件:

  • setup.cfg。对应的配置文件,一般情况下可以不要。

  • MANIFEST.in。清单文件,当项目中需要一些没办法自动包括到源代码分发包的文件时,可能需要用到它。

具体文件内容就不列出了。需要注意 my_package / __init__ . py 文件中应该有如下一行标识包名:

  1. name = 'yitian_first_package'

编写setup.py文件

用setuptools来编写setup.py文件是一件非常简单的事情,而且有很多例子可供参考,我挑选了Kenneth Reitz(requests、pipenv等类库的作者)写的例子,做了一些修改并翻译了一些注释:

  1. #!/usr/bin/env python

  2. # -*- coding: utf-8 -*-

  3. # 注意 如果要使用上传功能,需要安装twine包:

  4. #   $ pip install twine

  5. import io

  6. import os

  7. import sys

  8. from shutil import rmtree

  9. from setuptools import find_packages, setup, Command

  10. # 包的元信息

  11. NAME = 'yitian_first_package'

  12. DESCRIPTION = '项目的简短描述,不超过200字符'

  13. URL = 'https://github.com/techstay/python-study'

  14. EMAIL = '[email protected]'

  15. AUTHOR = '易天'

  16. REQUIRES_PYTHON = '>=3.6.0'

  17. VERSION = '0.1.0'

  18. KEYWORDS = 'sample setuptools development'

  19. # 项目依赖,也就是必须安装的包

  20. REQUIRED = [

  21.    'requests-html'

  22. ]

  23. # 项目的可选依赖,可以不用安装

  24. EXTRAS = {

  25.    # 'fancy feature': ['django'],

  26. }

  27. # 剩下部分不用怎么管 :)

  28. # ------------------------------------------------

  29. # 除了授权和授权文件标识符!

  30. # 如果你改了License, 记得也相应修改Trove Classifier!

  31. here = os.path.abspath(os.path.dirname(__file__))

  32. # 导入README文件作为项目长描述.

  33. # 注意 这需要README文件存在!

  34. try:

  35.    with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f:

  36.        long_description = '\n' + f.read()

  37. except FileNotFoundError:

  38.    long_description = DESCRIPTION

  39. # 当前面没指定版本号的时候,将包的 __version__.py 模块加载进来

  40. about = {}

  41. if not VERSION:

  42.     with open(os.path.join(here, NAME, '__version__.py')) as f:

  43.        exec(f.read(), about)

  44. else:

  45.    about['__version__'] = VERSION

  46. class UploadCommand(Command):

  47.    """上传功能支持"""

  48.    description = 'Build and publish the package.'

  49.    user_options = []

  50.    @staticmethod

  51.    def status(s):

  52.        """Prints things in bold."""

  53.        print('\033[1m{0}\033[0m'.format(s))

  54.    def initialize_options(self):

  55.        pass

  56.    def finalize_options(self):

  57.        pass

  58.    def run(self):

  59.        try:

  60.            self.status('Removing previous builds…')

  61.            rmtree(os.path.join(here, 'dist'))

  62.        except OSError:

  63.            pass

  64.         self.status('Building Source and Wheel (universal) distribution…')

  65.        os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))

  66.        self.status('Uploading the package to PyPI via Twine…')

  67.        os.system('twine upload dist/*')

  68.        self.status('Pushing git tags…')

  69.        os.system('git tag v{0}'.format(about['__version__']))

  70.        os.system('git push --tags')

  71.        sys.exit()

  72. # 神奇的操作,一个函数完事

  73. setup(

  74.    name =NAME,

  75.    version=about['__version__'],

  76.    description=DESCRIPTION,

  77.    long_description=long_description,

  78.    long_description_content_type='text/markdown',

  79.    author=AUTHOR,

  80.    author_email=EMAIL,

  81.    python_requires=REQUIRES_PYTHON,

  82.    url=URL,

  83.    keywords=KEYWORDS,

  84.    # 项目中要包括和要排除的文件,setuptools可以自动搜索__init__.py文件来找到包

  85.    packages=find_packages(exclude=('tests',)),

  86.    # 如果项目中包含任何不在包中的单文件模块,需要添加py_modules让setuptools能找到它们:

  87.    # py_modules=['yitian_first_package'],

  88.    # entry_points={

  89.    #     'console_scripts': ['mycli=mymodule:cli'],

  90.    # },

  91.    install_requires=REQUIRED,

  92.    extras_require=EXTRAS,

  93.    # 老旧的distutils需要手动添加项目中需要的非代码文件,setuptools可以用下面参数自动添加(仅限包目录下)

  94.    include_package_data=True,

  95.    # 如果是包的子目录下,则需要手动添加

  96.    package_data={

  97.        'yitian_first_package': ['static/*.html']

  98.    },

  99.    license='MIT',

  100.    classifiers=[

  101.        # Trove classifiers

  102.        # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers

  103.        'License :: OSI Approved :: MIT License',

  104.        







请到「今天看啥」查看全文