注意: 雖然 JavaScript 對於本網站不是必需的,但您與內容的互動將受到限制。請開啟 JavaScript 以獲得完整的體驗。

Python Distutils-SIG: 設計提案

Python Distutils-SIG

(先決條件:在嘗試閱讀此設計文件之前,請先閱讀提議的介面;它很大程度上是介面文件的續篇。)

設計提案

Distutils 的觀點

setup.py 只需匯入一個模組,即 distutils.core。此模組負責解析 setup.py 的所有命令列引數(儘管選項的解釋分佈在各種 Distutils 命令中,並且可能分佈在客戶端 setup.py 中)。它還負責接收來自 setup.py 的控制,並將其適當地傳遞給 Distutils 命令。最重要的是,distutils.core 定義了 Distribution 類,它是 Distutils 的核心。客戶端 (setup.py) 主要用於為 Distribution 例項提供屬性,所有 Distutils 命令都操作該例項。distutils.core 還定義了 Command 類,這對於實現 Distutils 命令非常有用。

說到 Distutils 命令:每個命令都作為 Python 模組實現,例如 build 命令由 distutils.build 模組實現。每個命令模組都需要定義一個類,該類也以命令命名——例如 distutils.build.Build。這些命令類將繼承自 Command 類,該類(至少)將提供處理命令特定選項的方法。(Command 將提供一個建構函式,該建構函式接受 Distribution 類和此命令的可選引數列表,並透過檢查 Command 派生例項中的 getopt 樣式選項說明符來解析引數列表。)每個命令類必須提供一個 run 方法,該方法使用 Distribution 例項中的資訊和命令選項來“執行其操作”。編寫良好的命令類會將此任務分解為幾個定義明確(且有文件說明)的方法,以便客戶端 setup.py 可以繼承並覆蓋 Distutils 命令類的特定行為。這也意味著 Distribution 類必須有一種方法將覆蓋的命令類傳達給主排程程式。

客戶端的觀點

如上所述,客戶端 (setup.py) 只需匯入 distutils.core —— 其他所有 Distutils 相關的功能都由這個核心模組處理。然而,客戶端需要一種方法將其特定選項傳遞給 Distutils 核心(並傳遞給命令模組)。

有兩種可能的方案:一種簡短方便(但擴充套件性不強),另一種有點冗長笨拙(但更面向物件且擴充套件性強)。我們沒有理由不能兩者兼得;方便的介面可以只是一個包裝器,用於許多不需要大量花哨自定義的模組分發。

首先,這是一個簡單介面的示例,用於包含單個“純 Python”模組 (mymod.py) 的模組分發。

    from distutils.core import setup
    setup (name = "mymod",
           version = "1.2",
           author = "Greg Ward <gward@cnri.reston.va.us>",
           description = "A very simple, one-module distribution")
請注意,我們沒有明確列出 mymod.py:Distutils 假設這是一個以其唯一模組 (mymod) 命名的單模組分發。

那些喜歡定義子類的人可能更喜歡以不同的方式表達

    from distutils.core import Distribution, setup

    class MyDistribution (Distribution):
        name = "mymod"
        version = "1.2",
        author = "Greg Ward <gward@cnri.reston.va.us>",
        description = "A very simple, one-module distribution")

    setup (distclass = MyDistribution)
對於小型分發來說,這有點矯枉過正:我們僅為了提供屬性值而定義一個新類,而 distutils.core.setup 的主要目的正是讓我們這樣做。儘管如此,面向物件純粹主義者會喜歡這一點——而且無疑會有客戶端必須覆蓋行為而不僅僅是資料的情況,屆時面向物件介面將是必要的。

對於更復雜的模組分發,需要自定義大量屬性,將其分解開來可能更容易閱讀/維護。考慮一個包含兩個純 Python 模組 (mymodmy_othermod) 和一個 C 擴充套件 (myext) 的分發;C 擴充套件必須與兩個輔助 C 檔案和一個 C 庫連結。哦,這個分發需要 Python 1.5 和任何版本的 re 模組(忽略一個通常意味著另一個的事實)

    from distutils.core import Distribution, setup

    class MyDistribution (Distribution):
        name = "mydist",
        version = "1.3.4",
        author = "Greg Ward <gward@cnri.reston.va.us>"
        description = """\
    This is an example module distribution.  It provides no useful code,
    but is an interesting example of the Distutils in action."""

        # Dependencies
        requires = { 'python': '1.5',  # I like class-based exceptions
                     're': '*',        # and I love Perl-style regexps! ;-)
                   }
        # Actual files that need to be processed and installed in some form
        py_modules = ['mymod.py', 'my_othermod.py'],
        ext_modules = {'myext.c': 
                        {'other_c': ['extra1.c', 'extra2.c'],
                         'c_libraries': ['mylib']}
                      }

    setup (distclass = MyDistribution)

有幾點需要注意

  • 我不害怕使用深度巢狀的資料結構;如果您正在編寫和分發 Python 模組,這不應該是一個問題!
  • 每個屬性都有一個特定的型別(字串、列表、字典等)
  • 具有複雜型別(尤其是字典)的屬性將具有眾所周知且有文件說明的內部結構,例如。
        """ext_modules is a hash mapping names of C source files (each
        containing a Python extension module) to a nested hash of
        information about how to build that module.  The allowed keys to
        this nested hash are: 
          - other_c: other C files that must be compiled and linked with 
                     the main C file to create the module
          - c_libraries: C libraries that must be included in the link
          ...
       """
    

毫無疑問,ext_modules 巢狀雜湊將有更多選項,毫無疑問,其他 Distribution 屬性將具有複雜且有文件說明的結構。

最後,所有 Distribution 屬性的列表必須是眾所周知且有文件說明的!這些似乎分為幾個大類。這是一個最初的列表嘗試

  • 分發元資料
    • 名稱
    • 版本
    • 作者
    • 描述
  • 依賴項
    • requires
  • 要處理和安裝的檔案
    • py_modules
    • ext_modules
    • doc_files
  • 構建目錄(預設都在 ./blib 下)
    • build_lib - 放置獨立於平臺的庫檔案的位置
    • build_platlib - 放置依賴於平臺的庫檔案的位置
    • build_exe - 放置可執行程式(即指令碼)的位置
    • build_html - 放置已處理文件(HTML)的位置
  • 安裝目錄(預設在 sysconfig.LIBDEST 下)
    • install_lib
    • install_platlib
    • install_exe
    • install_html
……嗯,這是一個開始。

Distutils 的觀點再探

總而言之,讓我們回顧一下使用者執行 setup.py 時發生的事情。無論 setup.py 是以簡單形式(呼叫函式)還是通用形式(定義子類)編寫,都沒有太大關係,所以我不會將事情分成兩個流。

  • setup.py 匯入 distutils.core
  • distutils.core 啟動程式碼解析命令列引數:處理它知道的全域性選項,並保留其餘部分供客戶端 (setup.py) 處理;找出每個命令的命令和選項,將它們都儲存起來以便稍後處理
  • setup.py 呼叫 distutils.core.setup(可能帶有一個 distclass 引數,指定 Distribution 的子類,可能帶有一堆其他命名引數,指定 Distribution 例項的各種屬性)
  • distutils.core.setup 例項化 Distribution(或客戶端提供的子類),並使用其引數(除了 distclass)來覆蓋此例項的屬性
  • distutils.core.setup 載入命令模組(例如 distutils.build
  • distutils.core.setup 確定命令類(通常僅以命令命名,例如 distutils.build.Build,但也可能是客戶端作為 Distribution 例項的屬性之一提供的類)並例項化它
  • 命令類建構函式接受 Distribution 例項和 setup.py 命令列上特定於此命令的任何命令列引數作為引數
  • 命令類建構函式解析其選項以設定/覆蓋某些例項屬性
  • distutils.core.setup 呼叫命令物件上的 run 方法
  • 該方法執行命令應該執行的任何操作:構建模組、處理文件、安裝檔案等。
  • distutils.core.setup 確定下一個命令類(如果給定了多個命令),並像以前一樣進行