實戰(zhàn):通過 Entry Points 自定義 I/O 處理器與執(zhí)行引擎)
開發(fā)工具CLI數(shù)據(jù)工程【免費下載鏈接】papermill Parameterize, execute, and analyze notebooks項目地址https://gitcode.com/gh_mirrors/pa/papermill點擊查看免費下載Papermill 在開箱即用地支持本地文件、S3、GCS、HDFS 等多種讀寫來源和默認的 nbclient 本地執(zhí)行引擎之外還提供了一套基于 Python Entry Points 的插件機制允許開發(fā)者在不修改 papermill 源碼的前提下為其接入任意存儲后端如 SFTP 服務器或自定義執(zhí)行邏輯如遠程執(zhí)行、逐單元格計時統(tǒng)計。本文將以倉庫docs/extending-overview.rst、docs/extending-entry-points.rst為核心脈絡結合papermill/iorw.py、papermill/engines.py等源碼實現(xiàn)完整講解 I/O Handler 與 Engine 的接口約定、entry point 注冊方式并給出兩個可復制運行的完整實戰(zhàn)示例使讀者能夠獨立為 papermill 開發(fā)并交付自己的插件。擴展機制總覽Papermill 的插件化設計用 papermill 運行一個 notebook 時背后其實只發(fā)生四件事見 docs/extending-overview.rst讀取 notebook 文件將文件內(nèi)容轉(zhuǎn)換為 notebook 的 Python 對象nbformat 的 NotebookNode執(zhí)行該 notebook將執(zhí)行后的 notebook 寫回文件。其中步驟 1、3、4 正是擴展點通過 entry points你可以編寫自己的工具來接管讀取I/O Handler、執(zhí)行Engine與寫回I/O Handler。步驟 2 由 papermill 內(nèi)部完成通常無需干預。所謂 entry points是 Python 打包規(guī)范中已安裝的分發(fā)包向外界通告自身組件供其他代碼發(fā)現(xiàn)和使用的機制——典型如console_scripts生成命令行包裝器以及 Pygments 通過它加載第三方語法高亮插件。Papermill 在運行時正是通過entrypoints庫掃描兩類 entry point 組papermill.io實現(xiàn)輸入/輸出I/O處理的 handlerpapermill.engine實現(xiàn)執(zhí)行邏輯的 engine。從源碼可以印證這一點papermill/iorw.py 中的PapermillIO.register_entry_points()調(diào)用entrypoints.get_group_all(papermill.io)并逐個注冊papermill/engines.py 中的PapermillEngines.register_entry_points()則調(diào)用entrypoints.get_group_all(papermill.engine)。兩個注冊方法都在模塊導入時被自動執(zhí)行iorw.py 與 engines.py因此第三方插件只要以正常方式安裝到環(huán)境中papermill 啟動時即會自動發(fā)現(xiàn)。如果覺得僅靠新增 handler 和 engine 仍不夠而是想改進項目本身的一些根本性設計則可以參與 papermill 的貢獻開發(fā)詳見下文參與 papermill 核心開發(fā)一節(jié)對應 docs/extending-developing.rst。開發(fā)新的 I/O HandlerI/O Handler 的接口約定四個必須實現(xiàn)的方法Papermill 中讀取輸入 notebook由 I/O Handler 管理正是它們讓 papermill 不僅能訪問本地文件系統(tǒng)還能訪問 S3 等遠程服務同樣將執(zhí)行后的 notebook 寫回也由 I/O Handler 負責。因此I/O Handler 是 papermill 與任意存儲后端對接的統(tǒng)一抽象層。編寫自己的 I/O Handler就是編寫一個實現(xiàn)了以下四個類方法的類CustomIO僅為示意名方法簽名作用readCustomIO.read(file_path)返回文件內(nèi)容字符串writeCustomIO.write(file_content, file_path)寫入文件無返回值pretty_pathCustomIO.pretty_path(path)返回美化后的路徑用于日志與展示listdirCustomIO.listdir(path)返回路徑列表用于目錄瀏覽原文檔特別提醒如果你的 handler 只用于寫例如只面向發(fā)布平臺不打算支持read等操作那么應當實現(xiàn)該方法并在被調(diào)用時拋出異常如NotImplementedError而不是省略它——這樣接口保持完整行為上又明確表達了此能力不受支持。這一約定與倉庫內(nèi)置 handler 的實現(xiàn)完全一致。例如 papermill/iorw.py 的LocalHandler實現(xiàn)了全部四個方法而只讀性質(zhì)更強的 handler 則主動對不支持的方法拋異常如GithubHandler.write拋PapermillException(write is not supported by GithubHandler)iorw.py、StreamHandler.listdir拋PapermillException(listdir is not supported by Stream Handler)iorw.py。這些實現(xiàn)可以作為編寫部分能力受限 handler 的現(xiàn)成參考。確保 handler 被 papermill 發(fā)現(xiàn)pyproject.toml 注冊開發(fā)完 handler 類之后需要在插件的pyproject.toml中聲明 papermill 的 entry point。方法是在文件中加入[project.entry-points.papermill.io]段[project.entry-points.papermill.io] sftp:// papermill_sftp:SFTPHandler這一行的語義是當傳入的文件路徑以sftp://開頭時papermill 就使用papermill_sftp包中導入的SFTPHandler類來處理該路徑的讀寫。等號左邊是路徑前綴等號右邊是類名及其導入來源格式為包名:類名。從源碼看這套匹配邏輯實現(xiàn)在PapermillIO.get_handler中papermill/iorw.py它按注冊順序遍歷self._handlers一旦path.startswith(scheme)命中即返回對應 handler若全部未命中則回退到localhandler即本地文件系統(tǒng)連本地 handler 都沒有時才拋出PapermillException。另外要注意register是 LIFO 順序插入iorw.py后注冊的 scheme 會優(yōu)先匹配設計同名前綴覆蓋時需留意這一點。傳統(tǒng)上papermill I/O handler 的 entry point 名采用 URL 前綴形式。倉庫內(nèi)置注冊的 handler注意這些是 papermill 內(nèi)部注冊并非通過 entry point 加載但接口與約定相同包括見 papermill/iorw.pylocal→LocalHandler本地文件系統(tǒng)s3://→S3HandlerAmazon S3adl://→ADLHandlerAzure Data Lakeabs://→ABSHandlerAzure Blob Storagehttp://、https://→HttpHandlerHTTP/HTTPS 讀寫gs://→GCSHandlerGoogle Cloud Storage寫入時帶限流重試hdfs://→HDFSHandlerHadoop 文件系統(tǒng)http://github.com/、https://github.com/→GithubHandlerGitHub 內(nèi)容讀取-→StreamHandler標準輸入/輸出流??梢酝茢嗳魏我?URL 風格前綴命名的新 handler 都能與這套體系自然共存。實戰(zhàn)示例完整的 SFTP I/O Handler下面按原文檔的演示從零構建一個可讀寫 SFTP 服務器的 handler目標是支持這樣的命令行用法papermill sftp://my_ftp_server.co.uk/input.ipynb sftp://my_ftp_server.co.uk/output.ipynb項目結構如下papermill_sftp |- pyproject.toml |- src |- papermill_sftp |- __init__.py在src/papermill_sftp/__init__.py中實現(xiàn) handler讀取時先把遠端文件下載到臨時目錄再讀入寫入時先寫到臨時文件再上傳pretty_path直接原樣返回路徑listdir暫不實現(xiàn)按接口約定拋異常。原文檔示例代碼中未顯式導入pathlib、tempfile、urllib.parse且cnopts需按你的 pysftp 環(huán)境配置這里補齊 import 并給出說明使代碼可直接運行import os import pathlib import tempfile import urllib.parse import pysftp sftp_username os.getenv(SFTP_USERNAME) sftp_password os.getenv(SFTP_PASSWORD) # 根據(jù)你的環(huán)境配置主機密鑰校驗選項例如 # cnopts pysftp.CnOpts() # cnopts.hostkeys None # 僅測試環(huán)境使用生產(chǎn)環(huán)境請校驗 host key cnopts pysftp.CnOpts() class SFTPHandler: classmethod def read(cls, path): Read a notebook from an SFTP server. parsed_url urllib.parse.urlparse(path) with tempfile.TemporaryDirectory() as tmpdir: tmp_file pathlib.Path(tmpdir) / pathlib.Path(parsed_url.path).name with pysftp.Connection( parsed_url.hostname, usernamesftp_username, passwordsftp_password, port(parsed_url.port or 22), cnoptscnopts, ) as sftp: sftp.get(parsed_url.path, str(tmp_file)) return tmp_file.read_text() classmethod def write(cls, file_content, path): Write a notebook to an SFTP server. parsed_url urllib.parse.urlparse(path) with tempfile.TemporaryDirectory() as tmpdir: tmp_file pathlib.Path(tmpdir) / output.ipynb tmp_file.write_text(file_content) with pysftp.Connection( parsed_url.hostname, usernamesftp_username, passwordsftp_password, port(parsed_url.port or 22), cnoptscnopts, ) as sftp: sftp.put(str(tmp_file), parsed_url.path) classmethod def pretty_path(cls, path): return path classmethod def listdir(cls, path): raise NotImplementedError配套的pyproject.toml完整內(nèi)容如下注意[project.entry-points.papermill.io]段以及 setuptools 的 src 布局聲明[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_meta [project] name papermill_sftp version 0.1 description An SFTP I/O handler for papermill. authors [ {name My Name, email my.emailgmail.com} ] dependencies [pysftp] [project.urls] Repository https://github.com/my_username/papermill_sftp.git [project.entry-points.papermill.io] sftp:// papermill_sftp:SFTPHandler [tool.setuptools] packages [papermill_sftp] package-dir { src}安裝該插件后papermill 執(zhí)行時會檢查輸入、輸出路徑是否以sftp://開頭若命中則調(diào)用papermill_sftp中的SFTPHandler完成讀與寫。整個交互鏈路為sftp://路徑 →PapermillIO.get_handler前綴匹配iorw.py→SFTPHandler.read/write→ 繼續(xù)走 papermill 的讀取/寫回流程。開發(fā)新的執(zhí)行引擎EngineEngine 基類與 execute_managed_notebook 接口Papermill 的 engine 是能夠執(zhí)行一個 notebook 的 Python 對象。默認實現(xiàn)NBClientEngine接收一個 notebook 對象并在本機執(zhí)行其背后是 nbclient 的PapermillNotebookClient見 papermill/engines.py。通過編寫自定義 engine你可以把執(zhí)行交給遠程服務器或在執(zhí)行后對 notebook 做后處理例如注入額外的輸出單元格。自定義 engine 需要繼承papermill.engines.Engine基類并實現(xiàn)類方法execute_managed_notebook其調(diào)用簽名要與父類保持一致class CustomEngine(papermill.engines.Engine): classmethod def execute_managed_notebook(cls, nb_man, kernel_name, **kwargs): pass這里需要澄清一個容易誤解的細節(jié)原文檔將nb_man描述為nbformat.NotebookNode對象但從源碼看papermill/engines.pyEngine.execute_notebook會先把 notebook 包裝進NotebookExecutionManager其內(nèi)部nb屬性才是NotebookNode再把該管理器實例傳給execute_managed_notebook。NotebookExecutionManager封裝了統(tǒng)一的執(zhí)行狀態(tài)管理notebook_start初始化并清空 papermill 元數(shù)據(jù)、cell_start/cell_exception/cell_complete逐單元格更新狀態(tài)與耗時、notebook_complete收尾并強制保存還內(nèi)置了進度條與自動保存autosave_cell_every默認 30 秒見 engines.py。因此自定義 engine 只需聚焦如何逐單元格執(zhí)行而元數(shù)據(jù)記錄、保存等橫切邏輯可全部復用。基類Engine的默認execute_managed_notebook直接拋出NotImplementedErrorengines.py強制子類實現(xiàn)。實戰(zhàn)示例記錄每個代碼單元格耗時的 Timing Engine原文檔提供了一個完整的演示實現(xiàn)一個自定義 engine把每個代碼單元格的執(zhí)行耗時作為額外輸出注入到該單元格的 outputs 開頭。因為 papermill 本身已在單元格元數(shù)據(jù)中記錄start_time/end_time我們直接復用默認引擎NBClientEngine再借助nbformat的new_output構造輸出節(jié)點。項目結構papermill_timing |- pyproject.toml |- src |- papermill_timing |- __init__.pysrc/papermill_timing/__init__.py內(nèi)容如下from datetime import datetime from papermill.engines import NBClientEngine from nbformat.v4 import new_output class CustomEngine(NBClientEngine): classmethod def execute_managed_notebook(cls, nb_man, kernel_name, **kwargs): # call the papermill execution engine: super().execute_managed_notebook(nb_man, kernel_name, **kwargs) for cell in nb_man.nb.cells: if cell.cell_type code and cell.execution_count is not None: start datetime.fromisoformat(cell.metadata.papermill.start_time) end datetime.fromisoformat(cell.metadata.papermill.end_time) output_message fExecution took {(end - start).total_seconds():.3f} seconds output_node new_output(display_data, data{text/plain: [output_message]}) cell.outputs [output_node] cell.outputs實現(xiàn)要點先調(diào)用super().execute_managed_notebook(...)走完默認的本地執(zhí)行流程此時cell.metadata.papermill.start_time/end_time已被NotebookExecutionManager.cell_start/cell_complete寫入這兩個回調(diào)分別見 engines.py 與 engines.py隨后遍歷代碼單元格計算耗時差并把新構造的display_data輸出節(jié)點插到原有 outputs 之前。確保 engine 被 papermill 發(fā)現(xiàn)pyproject.toml 注冊自定義 engine 需要以papermill.engine為前綴注冊為 entry point引用我們剛實現(xiàn)的類。以papermill_timing包中的CustomEngine為例[build-system] requires [setuptools61.0, wheel] build-backend setuptools.build_meta [project] name papermill_timing version 0.1 description A papermill engine that logs additional timing information about code. authors [ {name My Name, email my.emailgmail.com} ] dependencies [papermill, nbformat] [project.urls] Repository https://github.com/my_username/papermill_timing.git [project.entry-points.papermill.engine] timer_engine papermill_timing:CustomEngine [tool.setuptools] packages [papermill_timing] package-dir { src}注冊后用戶即可通過命令行參數(shù)--engine timer_engine選用該引擎該參數(shù)在 papermill/cli.py 中定義為執(zhí)行 notebook 時使用的引擎名稱papermill input.ipynb output.ipynb --engine timer_enginePapermillEngines.get_engine會按名稱在已注冊的引擎字典中查找找不到時拋出PapermillException(fNo engine named {name} found)engines.py。倉庫內(nèi)置注冊了None與nbclient兩個名稱均指向NBClientEngineengines.py自定義名稱與內(nèi)置名稱互不沖突。下圖左側(cè)為使用自定義 timing engine 執(zhí)行后的 notebook右側(cè)為使用標準 engine 執(zhí)行的結果每個代碼單元格頂部都多出了我們注入的耗時輸出配圖源自 docs/extending-entry-points.rst關于 nb_man 回調(diào)的更輕量寫法如果你的自定義 engine 不想走完整的 nbclient 執(zhí)行流程而只是想在默認流程之外做點事情可以像測試用例CellCallbackEngine那樣直接操縱回調(diào)在 papermill/tests/test_engines.py 中一個繼承Engine的最小實現(xiàn)只調(diào)用nb_man.cell_start(cell)與nb_man.cell_complete(cell)配合Engine.execute_notebook的包裝即可完成元數(shù)據(jù)的完整記錄與保存。這驗證了引擎實現(xiàn)者的核心職責只是決定什么時候執(zhí)行、執(zhí)行什么狀態(tài)機與持久化由NotebookExecutionManager統(tǒng)一兜底。參與 papermill 核心開發(fā)當擴展需求超出新增 I/O handler 與執(zhí)行 handler的范疇涉及項目根本性改進時可以選擇直接向 papermill 貢獻代碼詳見 docs/extending-developing.rst。倉庫內(nèi)已提供 CONTRIBUTING.md、CODE_OF_CONDUCT.md 與 DEVELOPMENT_GUIDE.md 供貢獻者查閱開始之前建議先通讀。開發(fā)過程中papermill/tests/下的測試套件例如 test_engines.py 中的TestEngineRegistration、test_iorw.py 中的test_entrypoint_register分別驗證了 engine 與 I/O handler 的 entry point 注冊邏輯可作為自己插件行為對標的回歸參考。小結Papermill 的擴展體系可以概括為兩條清晰的插件通道I/O Handlerpapermill.io組實現(xiàn)read/write/pretty_path/listdir四個類方法以 URL 前綴注冊即可讓 papermill 讀寫任意存儲后端默認回退到本地文件系統(tǒng)Enginepapermill.engine組繼承papermill.engines.Engine并實現(xiàn)execute_managed_notebook通過--engine 名稱在命令行選用可接管或增強執(zhí)行邏輯遠程執(zhí)行、結果后處理、重復運行模擬等。兩者都只需在插件自己的pyproject.toml中聲明 entry point、正常安裝即可被 papermill 自動發(fā)現(xiàn)完全不需要改動 papermill 本體。理解這些接口約定以及NotebookExecutionManager的狀態(tài)管理語義之后無論是接入私有存儲、對接遠程執(zhí)行集群還是為 notebook 注入自定義分析輸出都可以在數(shù)十分鐘內(nèi)落地為一個獨立的可復用插件包。贊分享開發(fā)工具CLI數(shù)據(jù)工程【免費下載鏈接】papermill Parameterize, execute, and analyze notebooks項目地址https://gitcode.com/gh_mirrors/pa/papermill點擊查看免費下載相關推薦解鎖AI創(chuàng)作新維度5個關鍵策略讓您的ComfyUI多GPU效率提升3倍解鎖AI創(chuàng)作新維度5個關鍵策略讓您的ComfyUI多GPU效率提升3倍 您是否曾因顯卡顯存不足而無法運行心儀的大型AI模型是否在生成高分辨率圖像或視頻時頻繁人工智能大模型深度學習本地部署終極指南如何擴展Papermill自定義引擎與翻譯器開發(fā)終極指南如何擴展Papermill自定義引擎與翻譯器開發(fā) 想要讓Papermill參數(shù)化執(zhí)行工具發(fā)揮更大威力嗎這篇完整教程將教你如何通過自定義引擎和翻開發(fā)工具CLI數(shù)據(jù)工程Woodpecker 自定義 Backend 開發(fā)指南通過 Backend 接口與 RunAgent 擴展 CI/CD 執(zhí)行引擎Woodpecker 自定義 Backend 開發(fā)指南通過 Backend 接口與 RunAgent 擴展 CI/CD 執(zhí)行引擎 本文面向需要為 WoodpeCI/CDDevOps上一篇PPT Master把手頭的文檔快速變成原生可編輯的 PPT下一篇Flowframes本地視頻插幀指南把24fps素材輸出成60fps流暢畫面創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考