<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          2021年,你應(yīng)該知道的Python打包指南

          共 4244字,需瀏覽 9分鐘

           ·

          2021-01-06 18:19


          作者:frostming

          原題:Python打包指南2021

          來源:https://frostming.com/2020/12-25/python-packaging


          大家好,雕蟲小技欄目又和大家見面了,誰讓咱不會那些個屠龍之技,只好捉幾個蟲子玩玩了。寫這篇文章是因?yàn)檫^去的兩年關(guān)于pip和 Python 包管理有幾個重要的 PEP 發(fā)布,然而網(wǎng)上(中文世界)的打包發(fā)布教程很少有針對此的更新。再加上我成為 PyPA 的成員已經(jīng)尸位素餐快一年了,還是應(yīng)該來做點(diǎn)貢獻(xiàn)。

          setup.py 真難寫

          似乎從有 Python 打包以來就有了setuptools這個庫,你能搜到的教程,涉及打包發(fā)布的,都會讓你編寫那個可怕的setup.py。不知道誰能完全掌握那個東西的寫法,我到現(xiàn)在都還不太會。說幾個常用的配置:

          1. 指定依賴和可選依賴

            setup(
            ??install_requires=["flask",?"flask-migrate",?"sqlalchemy"],
            ??extras_require={"mysql":?["mysqlclient"],?"pgsql":?["psycopg2"]}
            )

            注意那兩個 key 分別是install_requiresextras_require,別寫錯了。此外,如果你需要根據(jù)條件增減依賴的話,不要用

            INSTALL_REQUIRES?=?["flask"]
            if?sys.platform?==?"win32":
            ??INSTALL_REQUIRES.append("pywin32")
            setup(install_requires=INSTALL_REQUIRES)

            而應(yīng)該使用Environment Markers

            INSTALL_REQUIRES?=?[
            ??"flask",
            ??"pywin32;?sys_platform?==?'win32'"
            ]
            setup(install_requires=INSTALL_REQUIRES)
          2. 發(fā)布可執(zhí)行程序到/bin

            setup(
            ??entry_points={
            ??????"console_scripts":?["mybin=mypackage.main:cli"]
            ??}
            )

            或者 ini 寫法

            setup(
            ??entry_points="""[console_scripts]
            ??mybin?=?mypackage.main:cli
            ??"""

            )

            任選其一。

          3. 包含 data 文件

            setup(
            ??include_package_data=True????#?從MANIFEST.in中讀取配置
            )

            或者

            setup(
            ??package_data={"":?["*.json"]}??#?包含所有json文件
            )
          4. 指定源代碼結(jié)構(gòu),如果你使用的是src/存放包的源碼這種項(xiàng)目結(jié)構(gòu),可以:

            setup(
            ??package_dir={"":?"src"}
            )

          打包上傳和安裝

          打包

          好了,這個萬惡的setup.py我已經(jīng)寫好了,咱要發(fā)布 PyPI 了。第一步,打包成可分發(fā)的文件:

          $?python?setup.py?sdist?bdist_wheel?--universal

          這條命令會同時生成源代碼包(Source Distribution),和二進(jìn)制包(Binary Distribution)。當(dāng)然,大部分的 Python 發(fā)布包中并不真的包含二進(jìn)制, 只是沿用了軟件工程中的一般叫法。

          其中bdist_wheel生成的二進(jìn)制包是 wheel 格式(需要安裝wheel才能打包),--universal的意思是這個二進(jìn)制包對所有 支持的 Python 版本和 ABI 都適用,「 一處打包,到處使用」,生成的文件名類似:my_package-0.1.0-py3-none-any.whl。

          如果你包中有 C 擴(kuò)展, 也就是打包出來的 wheel 會真的有二進(jìn)制文件時就不能加這個 flag 了,這時生成的文件名類似:my_package-0.1.0-cp38-cp38-win_amd64.whl。

          這個文件名不是亂來的,是要遵循一定規(guī)則,下載器能直接從這個文件名獲得這個包的基本信息:

          上傳

          可能有老的教程,讓你直接用python setup.py sdist bdist_wheel register upload打包上傳一步到位,這個方式已經(jīng)過時了不推薦使用。正確的方法應(yīng)該用twine工具:

          $?twine?upload?dist/*

          如果你要把上傳放到 CI 里自動執(zhí)行,最好生成一個 token 來使用,訪問 https://pypi.org/manage/account/token/ 按提示生成一個 token,使用的時候只要用命令指定下用戶名和密碼:

          twine?upload?--username?__token__?--password?${{?secrets.PYPI_TOKEN?}}?dist/*

          安裝

          把包上傳到 PyPI 以后,pip install my-package的時候是怎么安裝的呢?

          1. 訪問https://pypi.org/simple/my-package,解析所有鏈接

          2. 若是 whl 文件,判斷是否與當(dāng)前 Python 版本、ABI、平臺適配,加入到候選列表

          3. 標(biāo)簽中讀取data-requires-python屬性,判斷是否與當(dāng)前 Python 版本兼容,加入候選列表

          4. 若是源代碼包,直接加入候選列表

          最終在候選列表中優(yōu)先選擇 whl 文件為待安裝的包,將包下載到本地,候選包的選擇可以由pip install--only-binary--no-binary選項(xiàng)控制。

          現(xiàn)在準(zhǔn)備安裝了,如果待安裝的是 whl,那就非常簡單,直接解壓(whl 文件是一種 zip 格式),放到目標(biāo)目錄即可,解壓后產(chǎn)生的文件除了代碼或二進(jìn)制以外,還會包含一個my_package-0.1.0.dist-info/目錄,包含這個包的元數(shù)據(jù)信息,比如有哪些文件、文件 hash 值、entry_points 等等。

          如果待安裝的文件是源代碼包,那么需要把這個壓縮包解壓到一個臨時目錄,根據(jù)包指定的方式編譯構(gòu)建,生成 whl 文件,再用 whl 安裝同樣的方法放到目標(biāo)目錄中。而這個指定的編譯方式,在 PEP 517 提案之前,是調(diào)用python setup.py install命令。在 PEP 517 發(fā)布之后,則由 PEP 517 的 build backend 控制。

          setuptools 不再是唯一的選擇

          PEP 517 的內(nèi)容簡單來說,就是在項(xiàng)目根目錄下的pyproject.toml定義了兩個特殊屬性(注:其實(shí)還有第三個屬性backend-path,當(dāng)你的 backend 是在本地時使用。):

          [build-system]
          requires?=?["setuptools?>=?40.8.0",?"wheel"]
          build-backend?=?"setuptools.build_meta:__legacy__"

          上面這個就是setuptools的 PEP 517 的配置,這樣可以讓老的項(xiàng)目,能直接用 PEP 517 的方式構(gòu)建。如果你的項(xiàng)目中并沒有pyproject.toml文件,pip能自動填充為此缺省配置。其中requires意為這個 backend 依賴的包列表,build-backend則為 backend 的具體位置。這個 backend 需要實(shí)現(xiàn)幾個約定的接口:

          1. get_requires_for_build_wheel,構(gòu)建 wheel 需要的依賴列表,這個一般沒有特殊要求都是空

          2. get_requires_for_build_sdist,構(gòu)建 sdist 需要的依賴列表,同上

          3. prepare_metadata_for_build_wheel,生成一個 wheel 要用的dist-info/文件夾

          4. build_wheel,生成 wheel 文件

          5. build_sdist,生成 sdist 文件

          有了這些接口,pip以及其他可能的 frontend 就能從源代碼構(gòu)建一個 wheel 出來。因此,pyproject.toml必須被包含在源代碼包中。

          有了 PEP 517 的協(xié)議規(guī)范以后,backend 和 frontend 就能自由組合,不再是非setuptools不可了,實(shí)現(xiàn)了 PEP 517 的 backend 有:

          所以我可以不用寫 setup.py 了

          setup.py作為一個元數(shù)據(jù)的定義格式是有問題的:

          1. 必須由 Python 運(yùn)行,無法靜態(tài)解析

          2. 由于第 1 點(diǎn),有注入惡意代碼的操作可行性

          所以需要指定一個元數(shù)據(jù)的配置格式,這個格式規(guī)范最近也定下來了,它就是 PEP 621,也是使用pyproject.toml來定義的。而且,PDM已經(jīng)支持這個配置格式了,僅此一家。


          閱讀鏈接


          回復(fù)關(guān)鍵字“簡明python ”,立即獲取入門必備書籍簡明python教程》電子版

          回復(fù)關(guān)鍵字爬蟲”,立即獲取爬蟲學(xué)習(xí)資料

          python入門與進(jìn)階
          每天與你一起成長

          推薦閱讀


          點(diǎn)贊和在看就是最大的支持??

          瀏覽 29
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩免费视频在线观看 | 亚洲高清有码无码视频 | 国产精品成人久久 | 超碰在线青娱乐 | 九九精品九九视频 |