摘要:所以這篇文章就來研究一下自帶的打包系統。打包項目下面就進入本文的正題,的打包系統上。基本上我們不需要完全了解打包系統,只要學會簡單的幾個點就可以打包自己的類庫了。版本號下面是開發測測發布候選最終發布等情況的版本號實例。
最近把pyenv、pipenv這種都研究了一下,然后我發現一個嚴重的問題:就是我雖然看了半天這些工具,但是我對Python自己的打包系統卻完全沒有了解。所以這篇文章就來研究一下Python自帶的打包系統。
pip先來詳細介紹一下pip的用法,平時基本上我們用pip的時候也就是一個pip install。其實pip也有很多特性,在此先介紹一下常用的一些特性。此部分參考了pip文檔,想了解更多的話可以看原文。
安裝最常用的命令就是安裝了,除此以外還可以指定版本號:
$ pip install SomePackage # 不指定版本號,安裝最新版 $ pip install SomePackage==1.0.4 # 指定版本號 $ pip install "SomePackage>=1.0.4" # 指定最小版本號 $ pip install -r requirements.txt # 從需求文件安裝 $ pip install -e . # 從本地項目setup.py安裝使用代理服務器
當從官方的PyPI源安裝比較慢的時候,可以考慮使用代理服務器,指定代理服務器的方法有三種:
使用--proxy參數在命令行指定,代理格式為[user:passwd@]proxy.server:port。
在配置文件中指定。
設置http_proxy, https_proxy 和no_proxy環境變量。
使用需求文件(requirements.txt)在需要很多pip包的項目中,用pip一個個安裝包不是一個好辦法,這時候可以考慮使用需求文件。
如果要生成需求文件,用下面的命令。這會將當前Python環境中的所有包的當前版本狀態保存下來,將來安裝的時候會精確還原到凍結的那個狀態。
pip freeze > requirements.txt
要從需求文件中安裝,則是用下面的命令:
pip install -r requirements.txt
官方文檔還給出了一個帶注釋的實例需求文件:
# ####### example-requirements.txt ####### # ###### 沒有版本標識符的包,會安裝最新版 ###### nose nose-cov beautifulsoup4 # ###### 帶版本標識符的包 ###### # 版本標識符的資料 https://www.python.org/dev/peps/pep-0440/#version-specifiers docopt == 0.6.1 # Version Matching. Must be version 0.6.1 keyring >= 4.1.1 # Minimum version 4.1.1 coverage != 3.5 # Version Exclusion. Anything except version 3.5 Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.* # ###### 還可以指定其他的需求文件 ###### -r other-requirements.txt # # ###### 還可以指定本地貨網絡上的特定包 ###### ./downloads/numpy-1.9.2-cp34-none-win32.whl http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl # ###### Additional Requirements without Version Specifiers ###### # 和第一部分一樣,這里這些部分沒有順序需求,可以隨意改變位置 rejected green #
版本標識符用來指定包的版本,有以下幾個例子:
SomeProject SomeProject == 1.3 SomeProject >=1.2,<.2.0 SomeProject[foo, bar] SomeProject~=1.4.2
從6.0版本開始,pip也支持環境標記(也就是分號后面跟Python版本或者系統類型):
SomeProject ==5.4 ; python_version < "2.7" SomeProject; sys_platform == "win32"卸載
卸載某個包使用下面的命令:
$ pip uninstall SomePackage列出包
要列出所有已安裝的包:
$ pip list docutils (0.9.1) Jinja2 (2.6) Pygments (1.5) Sphinx (1.1.2)
要列出過時的包:
$ pip list --outdated docutils (Current: 0.9.1 Latest: 0.10) Sphinx (Current: 1.1.2 Latest: 1.1.3)
要列出某個已安裝的包的詳細信息:
$ pip show sphinx --- Name: Sphinx Version: 1.1.3 Location: /my/env/lib/pythonx.x/site-packages Requires: Pygments, Jinja2, docutils搜索
要搜索一個包,用下面的命令,搜索結果可能有很多:
$ pip search "query"更新
要更新一個包,使用-U或者--upgrade參數:
pip install -U
如果想更新所有的包,很遺憾,pip并沒有提供該功能,我在StackOverFlow上找到一個看起來比較簡單的解決辦法,就是在Python解釋器中執行下面的代碼:
import pkg_resources from subprocess import call packages = [dist.project_name for dist in pkg_resources.working_set] 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文件中應該有如下一行標識包名:
name = "yitian_first_package"編寫setup.py文件
用setuptools來編寫setup.py文件是一件非常簡單的事情,而且有很多例子可供參考,我挑選了Kenneth Reitz(requests、pipenv等類庫的作者)寫的例子,做了一些修改并翻譯了一些注釋:
#!/usr/bin/env python # -*- coding: utf-8 -*- # 注意 如果要使用上傳功能,需要安裝twine包: # $ pip install twine import io import os import sys from shutil import rmtree from setuptools import find_packages, setup, Command # 包的元信息 NAME = "yitian_first_package" DESCRIPTION = "項目的簡短描述,不超過200字符" URL = "https://github.com/techstay/python-study" EMAIL = "lovery521@gmail.com" AUTHOR = "易天" REQUIRES_PYTHON = ">=3.6.0" VERSION = "0.1.0" KEYWORDS = "sample setuptools development" # 項目依賴,也就是必須安裝的包 REQUIRED = [ "requests-html" ] # 項目的可選依賴,可以不用安裝 EXTRAS = { # "fancy feature": ["django"], } # 剩下部分不用怎么管 :) # ------------------------------------------------ # 除了授權和授權文件標識符! # 如果你改了License, 記得也相應修改Trove Classifier! here = os.path.abspath(os.path.dirname(__file__)) # 導入README文件作為項目長描述. # 注意 這需要README文件存在! try: with io.open(os.path.join(here, "README.md"), encoding="utf-8") as f: long_description = " " + f.read() except FileNotFoundError: long_description = DESCRIPTION # 當前面沒指定版本號的時候,將包的 __version__.py 模塊加載進來 about = {} if not VERSION: with open(os.path.join(here, NAME, "__version__.py")) as f: exec(f.read(), about) else: about["__version__"] = VERSION class UploadCommand(Command): """上傳功能支持""" description = "Build and publish the package." user_options = [] @staticmethod def status(s): """Prints things in bold.""" print("