GitHub: https://github.com/celine-yeh/celine-simplerequest
我們開發時經常會使用別人開發的套件,透過 pip 來下載,而今天我們開發了一個套件,只要將套件打包發布到 PyPI 或 TestPyPI 就能讓所有人都可以用 pip 來下載和安裝你的套件。
PyPI 為 Python Package Index 的簡寫,是 Python 的第三方套件集中地,pip 能夠利用簡單的指令和步驟幫助我們從 PyPI 上下載、安裝套件。
TestPyPI 可以視為 PyPI 的測試區,他與 PyPI 是分開的,不用擔心會相互影響。
而一個套件主要有三個步驟:Configure、Package、Distribute。
Configure:配置你的套件,包括文件與目錄的組織、打包參數的資訊等,基本的目錄組織如下:
setup.py
is the build script for setuptools. It tells setuptools about your package (such as the name and version) as well as which code files to include.
一個套件會包含很多的資訊,例如名字、版本等等。而此文件就是用來設定、提供這些資訊的。執行該檔案可以進行安裝或打包。
安裝一個套件到環境中。
將套件打包。
而 setup.py
是用以下 distutils 或 setuptools 這兩種打包工具的功能寫成。
是 Python Standard Library 中負責建立 Python 第三方庫的安裝器,能夠進行 Python 模組的安裝和發布。distutils 對於簡單的發布很有用,但功能缺少。大部分會使用更便利的 setuptools 。
是 distutils 的加強版,不包括在 Python Standard Library 中。
支援了 Egg 與 Wheel 打包格式,也支援 install_requires 依賴包、find_packages 自動搜索包等功能。
最大的優勢是它在包管理能力方面的增強。它可以使用一種更加透明的方法來查詢、下載並安裝依賴包,並可以在一個包的多個版本中自由進行切換,這些版本都安裝在同一個系統上。
Package:就像是我們要傳一整個資料夾給別人時,我們通常會壓縮成一個壓縮檔,方便傳輸,而套件亦同,我們需要先打包成一個檔案,通常有下列幾種格式。
簡寫為 sdist,即源碼包,基本上就是你寫的所有程式碼與檔案,但不包括 .pyc 。打包後會在 dist/ 下看到 .tar.gz 的檔案。
包含了檔案和 metadata,只需要移到目標系統的正確位置下,就能夠安裝,安裝流程得到簡化,使用者只需要下載文件,解壓到特定目錄即可。編譯建構是由發行者來完成,且只需完成一次。
而 Binary Distributions 是 Built Distributions 的一種,包括了 Egg 和 Wheel。二進制發布格式不用每次都在终端重新編譯生成,採用預編譯格式,安裝速度較快,而 sdist 再安裝前則需要多一個建置的步驟。
Python 第一個主流的打包格式。透過 easy_install 安裝。包含了 .pyc 檔案。
為了替代 Egg 而出現。可以透過 pip 安裝。
不包含 .pyc 檔案,預編譯的 .pyc 檔案偶爾會導致各種奇怪的問題,我們更希望他能在每次安裝時在本地生成更新。
擁有更豐富的軟體包元資訊,甚至包中的每個檔案都有版本記錄。
whl 文件有一點與 egg 文件相似,實際上它們都是偽裝的 zip 文件。如果你將 whl 文件名擴展改為 zip,你就可以使用你的 zip 應用程式打開它,並且可以查看它包含的文件和文件夾。兩者都是單安裝文件,安裝程序會自動幫忙安裝依賴包。
wheel 是一個安裝包,内含構建的產物,而 egg 則是可移植的包,需要在目標機器上構建產物。
Distribute:
申請 TestPyPI 或 PyPI 帳號,建置 ~/.pypirc 用來設定登入資訊的設定檔。
將 dist/ 下的打包檔上傳至 TestPyPI 。
建立並啟動虛擬環境。
建出以下檔案。
simplehttp/
套件的部分說明,一個 python 檔案表示一個模組,而一個包含 __init__
的資料夾就可以視為一個套件,外部的使用者可以透過 import simplehttp
來使用,若 simplehttp/
下還有其他模組,如:
則可以透過 simplehttp.mod
來呼叫。
設定 setup.py
檔,更多的參數可以參考官方文件。
特別說明 packages
的參數,表示要打包的套件有哪些,可以以陣列列舉或是使用 find_packages 來自動抓取所有 packages 。
而 classifiers
內可以放入一些資訊,包括了支援的版本。
此時已經可以將套件打包上傳至 TestPyPI ,要特別注意的是,一但上傳該套件,則此套件此版本不可再上傳,會出現命名重複的錯誤,因次在每次上傳前,強烈建議先在本地安裝測試過再行上傳。
安裝 setup.py
。
安裝成功後,測試套件是否可以運作。
這裡要求套件只能使用 Python Standard Library,不能調用其他第三方套件。第一項要求的功能是:
編輯 __init__.py
,上面 import 的部分,因為 urllib 在 Python2 和 3 之間的使用方法不同,所以需要分開 import 。
撰寫單元測試 unittest ,方便日後程式碼的維護開發。
在專案資料夾下新增檔案。
編輯 test_simplehttp.py
。
此時可以執行測試查看結果,自動找出資料夾底下所有的測試(預設會找 test*.py
)。
最後出現 OK 即為單元測試成功。
此時若執行打包,會發現 find_packages 會將 tests 也視為一個 package 一同打包進去,因此需要修改 setup.py
,排除掉 tests 。
第二項功能需求:
修改 simplehttp/__init__.py
,新增函式 post_json() 。
其中 encode('utf-8')
可以參考此文章, Python3 中不能提交 str 類型,需為 bytes 類型。
修改 tests/test_simplehttp.py
,新增單元測試。
有另外一項需求是參數包含中文。
先新增這項 TestCase 至 tests/test_simplehttp.py
。
此時執行 python2.7 會出現錯誤。
修改 simplehttp/__init__.py
,get_json() 和 post_json() 都需修改,將 unicode 進行 utf-8 編碼。
再新增一項 TestCase 至 tests/test_simplehttp.py
。
新的功能需要顯示出自定義的異常。
可以先看這兩篇文章:自訂例外、例外兼容 Python2 與 Python3 的寫法。
上面 simplehttp.HttpError: HTTP Status Code: 400
這段可以拆成三個部分:
[package name].[exception class]: [exception str]
。
修改 simplehttp/__init__.py
,再 import 下方輸入程式碼,另外再新增一個 Class 繼承 Exception 。
上半段的 if else 函式是例外兼容 Python2 與 Python3 的寫法。
修改函式,發生錯誤時呼叫異常。
新增一項 TestCase 至 tests/test_simplehttp.py
。
別人使用我們的套件時,並不清楚每一個函式的用途,以及需要的參數有哪些,因此加入 DocString 就可以用來說明。
如此一來使用者可以透過呼叫來查看說明文件。
每次更新程式碼時,都要先本地測試,之後提交到 github ,然後再打包發佈到 PyPI ,過程相當繁瑣,而 github + travis-ci 可以解決這個問題,建構一個自動部署環境。
先進入 travis-ci 網站,可以直接以 Github 登入,並至 travis-ci.com/profile 激活 repo 。
在專案資料夾下,新增 .travis.yml
檔,參考 Building a Python Project 與 PyPI deployment。
其中 on: tags: true
內的句子表示:只有在 Github 發布 Release 版本時才會進行上傳動作。
上方的 deploy 少了 password 的原因是:若直接將密碼打在檔案中十分危險,因此需要用加密,加密的方法可以參考 PyPI deployment / Travis CI。
之後 push 到 github 時,都可以進入 Travis CI 網站查看建置狀況與結果。