本教學課程逐步說明如何編寫使用
在 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 指令碼,請按照下列步驟操作:
如果你遇到錯誤,或是有任何問題或建議,請 回報錯誤。
必要條件
執行本教學課程的必要條件如下:
您必須使用 Fuchsia 來源結帳 (
fuchsia.git
) 開發 環境。您需要有 Fuchsia 裝置執行。可以是實體裝置 或模擬器
這部裝置必須連線至「
ffx
」,並具備遙控器 服務 (RCS) 已正確連線。如果執行
ffx target list
,RCS
底下的欄位必須讀取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_main
和 main
範本外,我們還想讓
在 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
通訊協定啟動元件,其中包含
步驟如下:
連線至生命週期控制器:
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)
嘗試啟動元件的執行個體:
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()
實作伺服器時,請注意以下幾點:
- 方法定義可以是
sync
或async
。 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 頁面。