Fuchsia Controller 教學課程

本教學課程逐步說明如何編寫使用 在 Fuchsia 來源結帳中發現 Fuchsia Controller (fuchsia-controller) (fuchsia.git) 設定。

Fuchsia Controller 包含一組程式庫,可讓使用者 透過 FIDL 與裝置互動。Fuchsia 控制器 這是最初建立的測試用版本在建立指令碼時 程式碼,與 Fuchsia 裝置上的 FIDL 介面互動。舉例來說: 使用者可使用 Fuchsia Controller 編寫可執行簡易裝置的指令碼 不必在 Rust 中編寫 ffx 外掛程式。

Fuchsia Controller 的主要內容為:

  • fuchsia-controller.so 檔案 (其中包含 ABI 的 header)
  • 較高層級的語言繫結 (以 .so 檔案為基礎建構而成) 使用 ABI)

    目前 Fuchsia Controller 的較高層級語言繫結都會寫入 僅限 Python

使用 Fuchsia Controller 最快的方式,就是編寫 使用 fuchsia-controller 程式碼。在 Fuchsia 來源結帳設定中 您可以將 Python 二進位檔建構為 .pyz 檔案, 從 out 目錄 (例如 $FUCHSIA_DIR/out/default) 執行。

如要編寫第一個 Fuchsia Controller 指令碼,請按照下列步驟操作:

  1. 必要條件
  2. 在 BUILD.gn 中更新依附元件
  3. 編寫您的第一個程式
  4. 與 Fuchsia 裝置通訊
  5. 實作 FIDL 伺服器

如果你遇到錯誤,或是有任何問題或建議,請 回報錯誤

必要條件

執行本教學課程的必要條件如下:

  • 您必須使用 Fuchsia 來源結帳 (fuchsia.git) 開發 環境。

  • 您需要有 Fuchsia 裝置執行。可以是實體裝置 或模擬器

  • 這部裝置必須連線至「ffx」,並具備遙控器 服務 (RCS) 已正確連線。

    如果執行 ffx target listRCS 底下的欄位必須讀取 Y。 例如:

    NAME                    SERIAL       TYPE       STATE      ADDRS/IP                       RCS
    fuchsia-emulator        <unknown>    Unknown    Product    [fe80::5054:ff:fe63:5e7a%4]    Y
    

    (若需更多資訊,請參閱 與目標裝置互動)

  • 如何在啟用網路但不含圖形使用者介面的情況下啟動 Fuchsia 模擬器 支援使用者介面,執行 ffx emu start --headless。( ,請參閱「啟動 Fuchsia 模擬器」)。

  • 裝置必須在最低限度中執行 core 產品

更新 BUILD.gn 中的依附元件

更新 BUILD.gn 檔案,納入下列依附元件:

import("//build/python/python_binary.gni")

assert(is_host)

python_binary("your_binary") {
    main_source = "path/to/your/main.py"
    deps = [
        "//src/developer/ffx:host",
        "//src/developer/ffx/lib/fuchsia-controller:fidl_bindings",
        "//src/developer/ffx/lib/fuchsia-controller:fuchsia_controller_py",
    ]
}

fidl_bindings 規則包含必要的 Python 和 .so 繫結程式碼。 您還必須加入 ffx 工具,讓 ffx Daemon 得以連線至 設定自己的 Fuchsia 裝置。

撰寫您的第一個程式

在本節中,我們會建立一個簡易程式,且該程式尚未與 Fuchsia 裝置,但連線至 ffx Daemon 以確認裝置 並開始運作為此,我們針對以下項目採用現有的 ffx FIDL 程式庫: 與在 //src/developer/ffx/fidl 中定義的 Daemon 互動。

包含 FIDL 依附元件

Fuchsia Controller 使用 FIDL 中級代表 (FIDL IR) 並在執行階段產生 FIDL 繫結。因此您必須提供 在 BUILD.gn 中,針對 fidlc 目標建立這些 FIDL 繫結:

"//src/developer/ffx/fidl:fuchsia.developer.ffx($fidl_toolchain)"

這也需要匯入 $fidl_toolchain 變數:

import("//build/fidl/toolchain.gni")

如果您要編寫測試,您必須加入主機測試資料 ( 允許基礎架構測試正常運作,因為他們必須存取測試中的 IR 跑者一樣,例如:

"//src/developer/ffx/fidl:fuchsia.developer.ffx_host_test_data(${toolchain_variant.base})"

包含主機測試資料規則也會包含 FIDL IR,因此不需要 加入兩個依附元件

新增 Python 匯入區塊

所有依附元件都加入完畢後,我們即可新增下列程式庫 複製到 Python 主檔案中:

from fuchsia_controller_py import Context, IsolateDir
import fidl.fuchsia_developer_ffx as ffx
import asyncio

以下各節將說明這個程式碼區塊中的每個程式庫。

情境和 IsolateDir

from fuchsia_controller_py import Context, IsolateDir

第一行包含 Context 物件,該物件會提供 使用者可能會執行 ffx 指令您還可以用這個工具完成更多事 物件,因為它也提供下列連結:

  • ffx Daemon
  • 紫紅色目標

IsolateDir 物件與 ffx 隔離相關,也就是 以所有中繼資料的方式執行 ffx Daemon (例如 值) 包含在特定目錄下。隔離主要是 以防止 ffx 的狀態汙染,或以較低的設定 使用中的裝置探索預設值 (這可能導致在以下位置執行 ffx 時發生問題) 測試基礎架構)。

IsolateDir 是一般用途指令的選用項目,但如果您有以下必要,此為必要欄位: 您打算使用程式進行測試。IsolateDir 物件會建立 ( 指向) 允許隔離的 ffx Daemon 執行個體執行的目錄。 (如要進一步瞭解 ffx 隔離,請參閱 整合測試)。

必須在服務期間將 IsolateDir 物件傳遞至 Context 物件 和初始化。IsolateDir 物件也可在 Context 之間共用 如需儲存大量結構化物件 建議使用 Cloud Bigtable清理 IsolateDir 物件,也會造成 關閉 ffx Daemon 後,在物件清空時會發生 收集。

FIDL IR

import fidl.fuchsia_developer_ffx as ffx

第二行取自上一節撰寫的 FIDL IR 程式碼 。於 fidl. 之後寫入的部分 (例如 fuchsia_developer_ffx) 要求 fuchsia.developer.ffx 程式庫必須有 FIDL IR。 任何 FIDL 匯入行都屬於這種情況。匯入中 fidl.example_fuchsia_library 要求程式庫的 FIDL IR 已產生名為 example.fuchsia.library 的檔案使用 as 關鍵字 這個程式庫相當容易使用

這個 fuchsia.developer.ffx 程式庫包含預期的所有結構 ,本課程稍後會說明。

Asyncio

import asyncio

從 FIDL IR 產生的物件會使用非同步繫結, 使用 asyncio 程式庫。在本教學課程中,我們使用 echo 通訊協定 在 echo.fidl 中定義的。

撰寫主要的實作方式

除了 async_mainmain 範本外,我們還想讓 在 echo_func 定義中:

async def echo_func():
   isolate = IsolateDir()
   config = {"sdk.root": "."}
   ctx = Context(config=config, isolate_dir=isolate)
   echo_proxy = ffx.Echo.Client(ctx.connect_daemon_protocol(ffx.Echo.MARKER))
   echo_string = "foobar"
   print(f"Sending string for echo: {echo_string}")
   result = await echo_proxy.echo_string(value="foobar")
   print(f"Received result: {result.response}")


async def async_main():
    await echo_func()


def main():
    asyncio.run(async_main())


if __name__ == "__main__":
    main()

建立並傳遞至 Context 物件的 config 物件為必要項目 因為採用隔離模式無法再使用時 與 ffx 的預設設定隔離 (根據預設 ffx 知道要在哪裡尋找) 匯入的 SDK),則您所建立的任何設定值 一定要提供想用的項目給 Context 物件

執行程式碼

程式碼必須先建構才能執行。BUILD.gn 檔案 可能如下所示:

import("//build/python/python_binary.gni")

assert(is_host)

python_binary("example_echo") {
    main_source = "main.py"
    deps = [
        "//src/developer/ffx:host",
        "//src/developer/ffx/lib/fuchsia-controller:fidl_bindings",
        "//src/developer/ffx/fidl:fuchsia.developer.ffx_compile_fidlc($fidl_toolchain)",
    ]
}

假設這個 BUILD.gn 位於src/developer/example_py_thing 目錄。接著,有了正確的 fx set,您就能建構 存取這個程式碼如果主機為 x64,則該版本為 指令看起來會像這樣:

fx build host_x64/obj/src/developer/example_py_thing/example_echo.pyz

一個建構作業已完成,您可以在 out 中找到程式碼 目錄 (確切來說是 out/default)。您可以執行 .pyz 檔案。請務必使用 擷取 out/default 目錄的完整路徑,這樣 pyz 檔案就會 可找出並開啟適當的 .so 檔案,例如:

$ cd $FUCHSIA_DIR/out/default
$ ./host_x64/obj/src/developer/example_py_thing/example_echo.pyz
Sending string for echo: foobar
Received result: foobar
$

與 Fuchsia 裝置通訊

如果程式碼已建構並執行,我們就可以開始編寫符合下列條件的程式碼: 透過 FIDL 介面存取 Fuchsia 裝置。大部分程式碼都很類似 這個部分會說明一些細微的差異

尋找元件多工房子

如要繫結至 Fuchsia 元件,目前您必須瞭解元件的 路徑名稱。這時可以使用 ffx。取得建構資訊的路徑名稱 供應商,例如:

ffx component capability fuchsia.buildinfo.Provider

這個指令會輸出類似以下的輸出內容:

Declarations:
  `core/build-info` declared capability `fuchsia.buildinfo.Provider`

Exposes:
  `core/build-info` exposed `fuchsia.buildinfo.Provider` from self to parent

Offers:
  `core` offered `fuchsia.buildinfo.Provider` from child `#build-info` to child `#cobalt`
  `core` offered `fuchsia.buildinfo.Provider` from child `#build-info` to child `#remote-control`
  `core` offered `fuchsia.buildinfo.Provider` from child `#build-info` to child `#sshd-host`
  `core` offered `fuchsia.buildinfo.Provider` from child `#build-info` to child `#test_manager`
  `core` offered `fuchsia.buildinfo.Provider` from child `#build-info` to child `#testing`
  `core` offered `fuchsia.buildinfo.Provider` from child `#build-info` to child `#toolbox`
  `core/sshd-host` offered `fuchsia.buildinfo.Provider` from parent to collection `#shell`

Uses:
  `core/remote-control` used `fuchsia.buildinfo.Provider` from parent
  `core/sshd-host/shell:sshd-0` used `fuchsia.buildinfo.Provider` from parent
  `core/cobalt` used `fuchsia.buildinfo.Provider` from parent

您想要的攔截器位於 Exposes 宣告下方:core/build-info

取得版本資訊

首先,我們必須先取得裝置的建構資訊,

首先,我們需納入建構資訊 FIDL 通訊協定的依附元件:

"//sdk/fidl/fuchsia.buildinfo:fuchsia.buildinfo_compile_fidlc($fidl_toolchain)"

接著需要撰寫程式碼,從 Fuchsia 裝置取得 Proxy。 目前方法是連線至建構資訊路徑名稱 (不過 原因如下:

isolate = IsolateDir()
config = {"sdk.root": "."}
target = "foo-target-emu" # Replace with the target nodename.
ctx = Context(config=config, isolate_dir=isolate, target=target)
build_info_proxy = fuchsia_buildinfo.Provider.Client(
    ctx.connect_device_proxy("/core/build-info", fuchsia_buildinfo.Provider.MARKER))
build_info = await build_info_proxy.get_build_info()
print(f"{target} build info: {build_info}")

如果執行上述程式碼,輸出結果會如下所示:

foo-target-emu build info: ProviderGetBuildInfoResponse(build_info=BuildInfo(product_config='core', board_config='x64', version='2023-08-18T23:28:37+00:00', latest_commit_date='2023-08-18T23:28:37+00:00'))

如果繼續執行,可以建立類似 ffx target show 指令:

results = await asyncio.gather(
    build_info_proxy.get_build_info(),
    board_proxy.get_info(),
    device_proxy.get_info(),
    ...
)

每次呼叫 FIDL 方法都會傳回共同處理常式,因此可以 如同工作方式,請同時等待 FIDL 繫結。

重新啟動裝置

您可以透過多種方式重新啟動裝置,重新啟動 VM 的其中一種方法 是連線至執行 fuchsia.hardware.power.statecontrol/Admin 通訊協定,可找到 低於 /bootstrap/shutdown_shim

使用這個方法時,通訊協定預計會在 方法出現 PEER_CLOSED 錯誤:

        ch = self.device.ctx.connect_device_proxy(
            "bootstrap/shutdown_shim", power_statecontrol.Admin.MARKER
        )
        admin = power_statecontrol.Admin.Client(ch)
        # Makes a coroutine to ensure that a PEER_CLOSED isn't received from attempting
        # to write to the channel.
        coro = admin.reboot(reason=power_statecontrol.RebootReason.USER_REQUEST)
        try:
            await coro
        except ZxStatus as status:
            if status.args[0] != ZxStatus.ZX_ERR_PEER_CLOSED:
                raise status

不過,如果我們要確定 是否恢復連線這通常是由 嘗試連線至通訊協定 (通常是 RemoteControl 通訊協定) 直到達到逾時為止

另一種做法是 ffx Daemon 的 Target 通訊協定:

ch = ctx.connect_target_proxy()
target_proxy = fuchsia_developer_ffx.Target.Client(ch)
await target_proxy.reboot(state=fuchsia_developer_ffx.TargetRebootState.PRODUCT)

執行元件

您可以使用 RemoteControl 通訊協定啟動元件,其中包含 步驟如下:

  1. 連線至生命週期控制器:

    ch = ctx.connect_to_remote_control_proxy()
    remote_control = fuchsia_developer_remotecontrol.RemoteControl.Client(ch)
    client, server = fuchsia_controller_py.Channel.create()
    await remote_control.root_lifecycle_controller(server=server.take())
    lifecycle_ctrl = fuchsia_sys2.LifecycleController.Client(client)
    
  2. 嘗試啟動元件的執行個體:

    client, server = fuchsia_controller_py.Channel.create()
    await lifecycle_ctrl.start_instance("some_moniker", server=server.take())
    binder = fuchsia_component.Binder.Client(client)
    

    binder 物件可讓使用者瞭解元件是否 保持連線。但沒有任何方法。支援決定 元件是否已繫結 (使用繫結器通訊協定) 的 Pod

取得快照

如要透過 Fuchsia 裝置取得快照,包括執行快照 繫結 File 通訊協定以進行讀取:

        client, server = Channel.create()
        file = io.File.Client(client)
        params = feedback.GetSnapshotParameters(
            # Two minutes of timeout time.
            collection_timeout_per_data=(2 * 60 * 10**9),
            response_channel=server.take(),
        )
        assert self.device.ctx is not None
        ch = self.device.ctx.connect_device_proxy(
            "/core/feedback", "fuchsia.feedback.DataProvider"
        )
        provider = feedback.DataProvider.Client(ch)
        await provider.get_snapshot(params=params)
        attr_res = await file.get_attr()
        asserts.assert_equal(attr_res.s, ZxStatus.ZX_OK)
        data = bytearray()
        while True:
            response = await file.read(count=io.MAX_BUF)
            asserts.assert_not_equal(response.response, None, extras=response)
            response = response.response
            if not response.data:
                break
            data.extend(response.data)

實作 FIDL 伺服器

Fuchsia Controller 的一項重要工作 (用於處理傳遞的繫結) 或測試複雜的用戶端程式碼) 就是執行 FIDL 伺服器。所有 本教學課程涵蓋的 FIDL 通訊協定,則有一個用戶端接受 管道。為此,您需要使用 Server 類別。

在本節中,我們回到 echo 範例並實作 echo 伺服器。 您需要覆寫的函式衍生自 FIDL 檔案定義。以下內容 echo 伺服器 (使用 ffx 通訊協定) 如下所示:

class TestEchoer(ffx.Echo.Server):
    def echo_string(self, request: ffx.EchoEchoStringRequest):
        return ffx.EchoEchoStringResponse(response=request.value)

如要正確實作,您需要匯入適當的程式庫。 和先前一樣,我們將匯入 fidl.fuchsia_developer_ffx。不過,由於我們 要執行 echo 伺服器,如要測試這個伺服器,最快的方法是使用 從 fuchsia_controller_py 程式庫建立 Channel 物件:

import fidl.fuchsia_developer_ffx as ffx
from fuchsia_controller_py import Channel

這個 Channel 物件的運作方式與其他語言類似。 以下程式碼是運用 echo 伺服器的簡易程式:

import asyncio
import unittest
import fidl.fuchsia_developer_ffx as ffx
from fuchsia_controller_py import Channel


class TestEchoer(ffx.Echo.Server):
    def echo_string(self, request: ffx.EchoEchoStringRequest):
        return ffx.EchoEchoStringResponse(response=request.value)


class TestCases(unittest.IsolatedAsyncioTestCase):

    async def test_echoer_example(self):
        (tx, rx) = Channel.create()
        server = TestEchoer(rx)
        client = ffx.Echo.Client(tx)
        server_task = asyncio.get_running_loop().create_task(server.serve())
        res = await client.echo_string(value="foobar")
        self.assertEqual(res.response, "foobar")
        server_task.cancel()

實作伺服器時,請注意以下幾點:

  • 方法定義可以是 syncasync
  • serve() 工作會處理要求,並呼叫 直到工作完成或 基礎管道物件已關閉
  • 如果服務工作執行時發生例外狀況,用戶端 管道收到 PEER_CLOSED 錯誤。接著您必須檢查結果 服務工作階段的流程
  • 與 Rust 的非同步程式碼不同,建立非同步工作時,您必須 直到您完成傳回的物件否則,工作可能會 系統進行垃圾回收處理

常見的 FIDL 伺服器程式碼模式

相較於上方的簡易 echo 伺服器範例,本節介紹的 不同類型的伺服器互動

建立 FIDL 伺服器類別

讓我們與下列 FIDL 通訊協定一起建立伺服器:

library fuchsia.exampleserver;

type SomeGenericError = flexible enum {
    THIS = 1;
    THAT = 2;
    THOSE = 3;
};

closed protocol Example {
    strict CheckFileExists(struct {
        path string:255;
        follow_symlinks bool;
    }) -> (struct {
        exists bool;
    }) error SomeGenericError;
};

系統會將方法名稱從 Camel 大小寫變更為 Camel 的大小寫,藉此衍生 FIDL 方法名稱 小寫的蛇形。因此,Python 中的 CheckFileExists 方法會變更為 check_file_exists

匿名結構體類型衍生自整個通訊協定名稱,以及 方法。因此可能會相當冗長。輸入法的輸入 參數已定義為名為 ExampleCheckFileExistsRequest 的類型。且 則回應稱為 ExampleCheckFileExistsResponse

總而言之,在 Python 中的 FIDL 伺服器實作看起來會像 如下:

import fidl.fuchsia_exampleserver as fe

class ExampleServerImpl(fe.Example.Server):

    def some_file_check_function(path: str) -> bool:
        # Just pretend a real check happens here.
        return True

    def check_file_exists(self, req: fe.ExampleCheckFileExistsRequest) -> fe.ExampleCheckFileExistsResponse:
        return fe.ExampleCheckFileExistsResponse(
            exists=ExampleServerImpl.some_file_check_function()
        )

此外,您也可以將這些方法實作為 async,而不會出現任何問題。

此外,傳回錯誤時必須包裝 FIDL 中的錯誤 DomainError 物件,例如:

import fidl.fuchsia_exampleserver as fe

from fidl import DomainError

class ExampleServerImpl(fe.Example.Server):

    def check_file_exists(self, req: fe.ExampleCheckFileExistsRequests) -> fe.ExampleCheckFileExistsResponse | DomainError:
        return DomainError(error=fe.SomeGenericError.THIS)

處理事件

事件處理常式的寫入方式與伺服器類似,但兩者衍生自 名為 EventHandler 的不同基礎類別。事件是在用戶端上處理 因此,您必須傳遞用戶端來建立事件 處理常式。

讓我們從以下的 FIDL 程式碼開始建構範例:

library fuchsia.exampleserver;

closed protocol Example {
    strict -> OnFirst(struct {
        message string:128;
    });
    strict -> OnSecond();
};

此 FIDL 範例包含事件處理常式所需的兩個不同事件 來處理。編寫什麼,只含列印結果的最簡單的類別 如下:

import fidl.fuchsia_exampleserver as fe

class ExampleEventHandler(fe.Example.EventHandler):

    def on_first(self, req: fe.ExampleOnFirstRequest):
        print(f"Got a message on first: {req.message}")

    def on_second(self):
        print(f"Got an 'on second' event")

如果想停止處理事件而不發生錯誤, fidl.StopEventHandler

系統可透過某些現有的 Fuchsia 控制器來測試此事件的範例 測試程式碼但請務必先確認已通過 Fuchsia 控制器測試 新增到 Fuchsia 版本設定中,例如:

fx set ... --with-host //src/developer/ffx/lib/fuchsia-controller:tests

使用來自 fuchsia.controller.test 的通訊協定 (定義為 fuchsia_controller.test.fidl),您可以編寫 使用 ExampleEvents 通訊協定,例如:

import asyncio
import fidl.fuchsia_controller_test as fct

from fidl import StopEventHandler
from fuchsia_controller_py import Channel

class ExampleEventHandler(fct.ExampleEvents.EventHandler):

    def on_first(self, req: fct.ExampleEventsOnFirstRequest):
        print(f"Got on-first event message: {req.message}")

    def on_second(self):
        print(f"Got on-second event")
        raise StopEventHandler

async def main():
    client_chan, server_chan = Channel.create()
    client = fct.ExampleEvents.Client(client_chan)
    server = fct.ExampleEvents.Server(server_chan)
    event_handler = ExampleEventHandler(client)
    event_handler_task = asyncio.get_running_loop().create_task(
        event_handler.serve()
    )
    server.on_first(message="first message")
    server.on_second()
    server.on_complete()
    await event_handler_task

if __name__ == "__main__":
    asyncio.run(main())

接著,只要完成 Python 環境設定步驟, 執行時 以下輸出內容並結束:

Got on-first event message: first message
Got on-second event

如需更多伺服器測試範例,請參閱這個 server.py 檔案。

使用 Python 解譯器進行實驗

如果您不確定如何建構某些類型, 執行執行檔,您可以使用 Python 解譯器檢查 FIDL 結構。

首先,請確認您已建構所需的可用 FIDL 程式庫 才能在 Python 中使用 (如前一節所述),因為 Python 需要存取 FIDL IR 才能運作。

如要設定 Python 解譯器,請執行下列指令 (這些指令 依附於您的 Fuchsia 建構目錄,預設 $FUCHSIA_DIR/out/default):

FUCHSIA_BUILD_DIR="$FUCHSIA_DIR/out/default" # Change depending on build dir.
export FIDL_IR_PATH="$FUCHSIA_BUILD_DIR/fidling/gen/ir_root"
__PYTHONPATH="$FUCHSIA_BUILD_DIR/host_x64:$FUCHSIA_DIR/src/developer/ffx/lib/fuchsia-controller/python"
if [ ! -z PYTHONPATH ]; then
    __PYTHONPATH="$PYTHONPATH:$__PYTHONPATH"
fi
export PYTHONPATH="$__PYTHONPATH"

接著您可以從任何位置啟動 Python 解譯器,此工具也支援 按下 Tab 鍵完成檢查各種類型,例如:

$ python3
Python 3.11.8 (main, Feb  7 2024, 21:52:08) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import fidl.fuchsia_hwinfo
>>> fidl.fuchsia_hwinfo.<TAB><TAB>
fidl.fuchsia_hwinfo.Architecture(
fidl.fuchsia_hwinfo.Board()
fidl.fuchsia_hwinfo.BoardGetInfoResponse(
fidl.fuchsia_hwinfo.BoardInfo(
fidl.fuchsia_hwinfo.Device()
fidl.fuchsia_hwinfo.DeviceGetInfoResponse(
fidl.fuchsia_hwinfo.DeviceInfo(
fidl.fuchsia_hwinfo.MAX_VALUE_SIZE
fidl.fuchsia_hwinfo.Product()
fidl.fuchsia_hwinfo.ProductGetInfoResponse(
fidl.fuchsia_hwinfo.ProductInfo(
fidl.fuchsia_hwinfo.fullname

您可以看到這個模組匯出的所有值。如果您想進行實驗 以非同步的方式在 IPython 中執行相同動作,也可以採用上述的環境設定。 執行 IPython。但請先確認您已安裝 python3-ipython

sudo apt install python3-ipython

然後執行 IPython。以下範例假設您執行 名為 fuchsia-emulator 的模擬器,並且從 Fuchsia 預設版本執行 目錄 (否則 "sdk.root" 就需要變更):

Python 3.11.8 (main, Feb  7 2024, 21:52:08) [GCC 13.2.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.20.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from fuchsia_controller_py import Context

In [2]: import fidl.fuchsia_buildinfo

In [3]: ctx = Context(target="fuchsia-emulator", config={"sdk.root": "./sdk/exported/core"})

In [4]: hdl = ctx.connect_device_proxy("/core/build-info", fidl.fuchsia_buildinfo.Provider.MARKER)

In [5]: provider = fidl.fuchsia_buildinfo.Provider.Client(hdl)

In [6]: await provider.get_build_info()
Out[6]: ProviderGetBuildInfoResponse(build_info=BuildInfo(product_config='core', board_config='x64', version='2024-04-04T18:15:05+00:00', latest_commit_date='2024-04-04T18:15:05+00:00'))

...

如要進一步瞭解如何使用 Fuchsia Controller 編寫非同步 Python 程式碼, 請參閱非同步 Python 頁面。