配置?

介紹?

invoke提供了一個多方面的配置機制,允許您通過配置文件、環境變量的層次結構來配置核心行為和任務的行為, task namespaces 和cli標志。

配置查找、加載、解析和合并的最終結果是 Config 對象,其行為類似于(嵌套的)python字典。invoke在運行時引用此對象(確定以下方法的默認行為 Context.run )并將其作為 Context.config 或作為快捷屬性訪問 Context 本身。

配置層次結構?

簡言之,配置值相互覆蓋的順序如下:

  1. 內部默認值 對于通過配置可以控制的行為。見 默認配置值 有關詳細信息。

  2. Collection-driven configurations 通過在任務模塊中定義 Collection.configure . (見 Collection -基于配置 詳情見下文。)

    • 子集合的配置被合并到頂級集合中,最終結果構成整個配置設置的基礎。

  3. System-level configuration file 存儲在 /etc/ ,如 /etc/invoke.yaml . (見 配置文件 有關此項和其他配置文件項的詳細信息。)

  4. User-level configuration file 在正在運行的用戶的主目錄中找到,例如。 ~/.invoke.yaml .

  5. Project-level configuration file 生活在你的頂層 tasks.py . 例如,如果運行調用加載 /home/user/myproject/tasks.py (參見我們的文檔 the load process ),這可能是 /home/user/myproject/invoke.yaml .

  6. 環境變量 在調用shell環境中找到。

    • 這些名稱空間的層次性不如其他名稱空間強,shell環境名稱空間也不是完全由invoke擁有,因此我們必須依賴稍微冗長的前綴-請參見 環境變量 有關詳細信息。

  7. 運行時配置文件 其路徑被賦予 -f ,例如 inv -f /random/path/to/config_file.yaml . 此路徑也可以通過 INVOKE_RUNTIME_CONFIG EV V.

  8. Command-line flags 對于某些核心設置,例如 -e .

  9. 用戶代碼所做的修改 在運行時。

默認配置值?

下面是所有配置值和/或節invoke本身用于控制以下行為的列表 Context.runechopty 標志、任務重復數據消除等。

注解

這些值的存儲位置在 Config 類的返回值。 Config.global_defaults ;有關更多詳細信息,請參閱其api文檔。

為了方便起見,我們使用點式語法來引用嵌套的設置名稱,例如 foo.bar 引用將是什么(在python配置上下文中) {{'foo': {{'bar': <value here>}}}} . 通常,可以讀取或設置這些。 ConfigContext 使用看起來幾乎相同的屬性語法的對象: c.foo.bar .

  • 這個 tasks 配置樹保存與任務執行相關的設置。

    • tasks.dedupe Controls 任務重復數據消除 默認值為 True . 它也可以在運行時被重寫。 --no-dedupe .

    • tasks.auto_dash_names 控制任務和集合名稱是否在cli上將下劃線轉換為破折號。違約: True . 也見 破折號與下劃線 .

    • tasks.collection_name 控制由 collection discovery ,默認為 "tasks" .

    • tasks.executor_class 允許用戶重寫實例化并用于任務執行的類。

      必須是窗體的完全限定的點路徑 module(.submodule...).class ,除了 .class 將交給 importlib.import_moduleclass 應該是該結果模塊對象的屬性。

      默認為 None ,意思是用跑步 Program 對象的 executor_class 屬性。

      警告

      如果將此設置與 custom program binaries ,因為自定義程序可以指定自己的默認執行器類(使用此設置將覆蓋該類?。┎⒓僭O某些行為源于此。

    • tasks.search_root 允許重寫默認值 collection discovery 根搜索位置。它默認為 None ,它指示使用正在執行的進程的當前工作目錄。

  • 這個 run 樹控制的行為 Runner.run . 此樹的每個成員(例如 run.echorun.pty )直接映射到 Runner.run 同名的關鍵字參數;有關這些設置的作用及其默認值的詳細信息,請參見該方法的docstring。

  • 這個 runners 樹控件 _which_ 運行程序類映射到哪個執行上下文;如果您自己使用invoke,則這將只傾向于有一個成員, runners.local . 客戶端庫可以使用額外的鍵/值對來擴展它,例如 runners.remote .

  • 這個 sudo 樹控制的行為 Context.sudo

    • sudo.password 控制提交到sudo密碼提示的自動響應密碼。違約: None .

      警告

      雖然可以像其他任何設置一樣將此設置存儲在 configuration files --這樣做本質上是不安全的。我們強烈建議在運行時從某種機密管理系統中填充此配置值。

    • sudo.prompt 保存sudo密碼提示文本,這兩個文本都提供給 sudo -p ,并在執行時搜索 auto-response . 違約: [sudo] password: .

  • 頂級配置設置, debug ,控制是否記錄調試級別輸出;它默認為 False .

    debug 可以通過 -d cli標志,用于在運行cli分析之后啟用調試。它也可以通過 INVOKE_DEBUG 與常規env vars不同的是,環境變量從執行開始就受到重視,因此對于解析和/或配置加載的故障排除非常有用。

  • 一個小配置樹, timeouts ,保存各種超時控件。目前,對于invoke,這只包含 command 子鍵,用于控制子進程執行超時。

    • 客戶機代碼通常會向該樹添加更多內容,而調用本身也可能在將來添加更多內容。

配置文件?

加載?

對于上一節中提到的每個配置文件位置,我們搜索以 .yaml , .yml , .json.py按順序! ),加載我們找到的第一個,忽略可能存在的任何其他。

For example, if Invoke is run on a system containing both /etc/invoke.yml and /etc/invoke.json, only the YAML file will be loaded. This helps keep things simple, both conceptually and in the implementation.

格式?

invoke的配置允許任意嵌套,因此我們的配置文件格式也是如此。下面的三個例子都得到了一個等價于 {{'debug': True, 'run': {{'echo': True}}}}

  • YAML

    debug: true
    run:
        echo: true
    
  • JSON

    {
        "debug": true,
        "run": {
            "echo": true
        }
    }
    
  • Python ::

    debug = True
    run = {
        "echo": True
    }
    

有關詳細信息,請參閱這些語言自己的文檔。

環境變量?

環境變量與其他配置設置方法稍有不同,因為它們不提供嵌套配置鍵的干凈方法,而且還隱式地在整個系統的已安裝應用程序庫中共享。

此外,出于實現方面的考慮,env vars必須由配置層次結構中它們下面的級別預先確定(換句話說-env vars只能用于重寫現有的配置值)。如果您需要調用來理解 FOOBAR 環境變量,必須首先聲明 foobar 在配置文件或任務集合中設置。

基本規則?

為了緩解shell名稱空間問題,我們只需在所有env vars前面加上 INVOKE_ .

嵌套是通過下劃線分隔來執行的,因此一個看起來像 {{'run': {{'echo': True}}}} 在python級別變成 INVOKE_RUN_ECHO=1 在一個典型的貝殼里。見 嵌套與帶下劃線的名稱 下面是關于這個的更多信息。

型鑄造?

由于EnvVAR只能用于重寫現有設置,所以給定的設置的前一個值被用來作為我們從shell中返回字符串的向導。

  • 如果當前值是字符串或Unicode對象,則將其替換為環境中的值,而不進行任何轉換;

    • 根據解釋器和環境的不同,這意味著設置默認為非Unicode字符串類型(例如 str 在python 2)中,可能最終被unicode字符串替換,反之亦然。這是有意的,因為它可以防止用戶意外地將自己限制在非unicode字符串中。

  • 如果當前值是 None ,它也被替換為環境中的字符串;

  • 布爾值設置如下: 0 以及空值/字符串(例如 SETTING=unset SETTING ,或等)評估為 False ,任何其他值的計算結果為 True .

  • 列表和元組當前不受支持,將引發異常;

    • 在將來,我們可以實現便利的轉換,例如在逗號上拆分以形成列表;然而,由于用戶總是能夠自己執行這樣的操作,所以它可能不是一個高優先級。

  • 所有其他類型(整數、長整型、浮點數等)都只是用作傳入值的構造函數。

    • 例如,a foobar 設置其默認值為整數 1 將通過 int 因此 FOOBAR=5 將產生python值 5 不是 "5" .

嵌套與帶下劃線的名稱?

由于環境變量鍵是單個字符串,我們必須使用某種形式的字符串解析來允許訪問嵌套的配置設置。如前所述,在基本用例中,這僅僅意味著使用下劃線字符: {{'run': {{'echo': True}}}} 變成 INVOKE_RUN_ECHO=1 .

但是,當設置名稱本身包含下劃線時,會引入歧義:is INVOKE_FOO_BAR=baz 相當于 {{'foo': {{'bar': 'baz'}}}} ,或 {{'foo_bar': 'baz'}} ?謝天謝地,因為EnvVAR只能用于修改Python級別或配置文件中聲明的設置,所以我們查看CONFIG的當前狀態來確定答案。

還有一個角落的案子 both 可能的解釋以有效的配置路徑存在(例如 {{'foo': {{'bar': 'default'}}, 'foo_bar': 'otherdefault'}} )在這種情況下,我們尊重 Zen of Python 拒絕猜測;相反會出現一個錯誤,建議用戶修改其配置布局或避免使用env vars進行設置。

Collection -基于配置?

Collection 對象可以包含配置映射,通過 Collection.configure ,和(根據 the hierarchy )這通常形成系統中的最低配置級別。

當集合是 nested ,默認情況下,配置是“向下”合并的:當發生沖突時,更接近根的外部命名空間將獲勝,而更接近被調用任務的內部命名空間將獲勝。

注解

這里的“內部”任務是指從根目錄到包含被調用任務的目錄路徑上的任務。忽略同級子集合。

這意味著什么的一個簡單例子:

from invoke import Collection, task

# This task & collection could just as easily come from
# another module somewhere.
@task
def mytask(c):
    print(c['conflicted'])
inner = Collection('inner', mytask)
inner.configure({'conflicted': 'default value'})

# Our project's root namespace.
ns = Collection(inner)
ns.configure({'conflicted': 'override value'})

呼叫的結果 inner.mytask ::

$ inv inner.mytask
override value

實際配置使用示例?

前幾節中有一些小示例;本節提供了一組更逼真的示例,展示了配置系統的工作原理。

安裝程序?

我們將從硬編碼其值的半現實任務開始,并逐步使用各種配置機制。建筑用的小模塊 Sphinx 文檔可能是這樣開始的:

from invoke import task

@task
def clean(c):
    c.run("rm -rf docs/_build")

@task
def build(c):
    c.run("sphinx-build docs docs/_build")

那么也許你重構了構建目標:

target = "docs/_build"

@task
def clean(c):
    c.run("rm -rf {}".format(target))

@task
def build(c):
    c.run("sphinx-build docs {}".format(target))

我們還可以允許運行時參數化:

default_target = "docs/_build"

@task
def clean(c, target=default_target):
    c.run("rm -rf {}".format(target))

@task
def build(c, target=default_target):
    c.run("sphinx-build docs {}".format(target))

這個任務模塊只適用于一組用戶,但是如果我們想允許重用呢?可能有人想將此模塊與其他默認目標一起使用。使用配置數據(通過上下文arg提供)配置這些設置通常是更好的解決方案 1.

通過任務集合配置?

配置 settinggetting api允許將其他“硬編碼”默認值移動到配置結構中,下游用戶可以自由重新定義該結構。讓我們把這個應用到我們的例子中。首先,我們添加一個顯式命名空間對象:

from invoke import Collection, task

default_target = "docs/_build"

@task
def clean(c, target=default_target):
    c.run("rm -rf {}".format(target))

@task
def build(c, target=default_target):
    c.run("sphinx-build docs {}".format(target))

ns = Collection(clean, build)

然后,我們可以將默認的構建目標值移到集合的默認配置中,并通過上下文引用它。此時,我們還將Kwarg默認值更改為 None 因此,我們可以確定是否給出了運行時值。結果:

@task
def clean(c, target=None):
    if target is None:
        target = c.sphinx.target
    c.run("rm -rf {}".format(target))

@task
def build(c, target=None):
    if target is None:
        target = c.sphinx.target
    c.run("sphinx-build docs {}".format(target))

ns = Collection(clean, build)
ns.configure({'sphinx': {'target': "docs/_build"}})

結果并沒有比我們開始時復雜得多,接下來我們將看到,用戶以各種方式覆蓋您的默認值已經很簡單了。

配置重寫?

當然,最低級別的重寫只是修改本地 Collection 已導入分布式模塊的樹。例如,如果上述模塊作為 myproject.docs 某人可以定義一個 tasks.py 就這樣:

from invoke import Collection, task
from myproject import docs

@task
def mylocaltask(c):
    # Some local stuff goes here
    pass

# Add 'docs' to our local root namespace, plus our own task
ns = Collection(mylocaltask, docs)

然后他們可以把這個添加到底部:

# Our docs live in 'built_docs', not 'docs/_build'
ns.configure({'sphinx': {'target': "built_docs"}})

現在我們有一個 docs 其生成目標默認為的子命名空間 built_docs 而不是 docs/_build . 運行時用戶仍然可以通過標志覆蓋這個(例如)。 inv docs.build --target='some/other/dir' )和以前一樣。

如果您更喜歡配置文件,而不是在python中調整命名空間樹,那么這也同樣有效;與其將上面的行添加到前面的代碼片段中,不如將其放到 tasks.py 已命名 invoke.yaml ::

sphinx:
    target: built_docs

對于本例,這種本地到項目的conf文件最有意義,但不要忘記 config hierarchy 提供其他配置方法,這些方法可能適合您的需要。

腳注

1

復制和修改文件中斷代碼重用;重寫模塊級 default_path 變量不能很好地處理并發;用不同的默認參數包裝任務可以工作,但是很脆弱,并且添加了樣板。