ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Textual 浏览器端能力解析:textual-serve、open_url 与文件交付 API 的跨平台实践

Textual 浏览器端能力解析:textual-serve、open_url 与文件交付 API 的跨平台实践 Textual 浏览器端能力解析textual-serve、open_url 与文件交付 API 的跨平台实践【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textual本文围绕 Textual 项目新增的浏览器端功能展开借助textual-serve可以把 Textual 应用跑在服务器上、让终端用户通过浏览器直接使用同时新增的App.open_url、App.deliver_text、App.deliver_binary三个 API让开发者无需关心用户究竟在用终端还是浏览器即可优雅地打开链接、把文件交付到用户手中。读完本文你将掌握这三个 API 的完整用法、参数语义以及从驱动层到 websocket 再到前端渲染的底层数据链路。什么是textual-servetextual-serve是一个独立的开源项目作用是把你的 Textual 应用服务化应用本身运行在你掌控的机器或服务器上浏览器只负责展示与交互。应用与浏览器之间通过基于websocket的协议通信因此浏览器端的最终用户只能接触到正在运行的这个 Textual 应用而无法通过浏览器访问到承载应用的机器本身。这种部署模式带来了很典型的实用场景把终端版 SQL IDE例如harlequin安装到网络中的一台机器上用textual-serve启动后分享 URL任何拿到 URL 的人都能用它查询该服务器可访问的数据库把终端版 API 客户端例如posting部署到服务器上把 URL 发给同事大家就能直接在浏览器里从这台服务器发出 HTTP 请求。追求体验对等的设计动机在浏览器中交互时应用其实并不运行在浏览器里——它运行在安装它的那台机器上这与典型的服务端驱动 Web 应用相似。这种架构给 Textual 团队提出了一个有趣的问题如何让浏览器端与终端端获得对等的体验原因很直观浏览器端的应用天然对非技术用户更友好不应该因为平台差异而让这些用户错过核心功能应用开发者也不应该被迫反复判断当前用户是在用浏览器还是终端并在代码里为两种环境写两套逻辑。为此Textual 提供了新的 API让开发者能以平台无关的方式为应用添加 Web 链接、向最终用户交付文件。目标很明确开发者只需编写一份代码就能在终端和浏览器中都获得合理的用户体验无需额外努力。打开网页链接App.open_url在浏览器里使用应用时点击并打开链接几乎是一项基本预期。Python 标准库提供了webbrowser模块在终端中运行 Textual 应用时直接调用它就能如愿打开默认浏览器。但问题在于当应用正被浏览器访问时webbrowser会在承载应用的那台服务器上尝试打开浏览器——这对最终用户毫无意义。为此 Textual 在App上新增了App.open_url方法定义见 src/textual/app.pydef open_url(self, url: str, *, new_tab: bool True) - None: Open a URL in the default web browser. Args: url: The URL to open. new_tab: Whether to open the URL in a new tab. if self._driver is not None: self._driver.open_url(url, new_tab)其行为完全由底层驱动决定开发者无需感知终端场景驱动基类的默认实现见 src/textual/driver.py内部调用webbrowser.open(url)在本地打开 URL浏览器场景WebDriver的实现见 src/textual/drivers/web_driver.py并不调用webbrowser而是把请求打包成一条open_url元数据消息写入 stdoutdef open_url(self, url: str, new_tab: bool True) - None: self.write_meta({type: open_url, url: url, new_tab: new_tab})这条元数据会被textual-serve捕获再通过 websocket 通知浏览器端在用户自己的浏览器中打开该链接——效果与普通 Web 链接无异。new_tab参数是否在新标签页打开仅在 Web 驱动下生效终端下会被忽略。把文件交给用户deliver_text与deliver_binary终端与浏览器下的难题在终端中运行应用时把文件交给用户相对简单写入磁盘并告知路径或者用$EDITOR打开内容即可——毕竟终端用户通常具备一定技术水平。但在浏览器中运行同样的应用问题就来了如果只是把文件写到磁盘最终用户需要能访问承载应用的机器、并在文件系统中找到它。这往往不可行——他们可能没有权限访问该机器甚至根本不知道如何操作。为此Textual 新增了两个方法App.deliver_text交付文本文件定义见 src/textual/app.pyApp.deliver_binary交付二进制文件定义见 src/textual/app.py。两个 API 的语义完全对称核心目标是无论应用被浏览器还是终端访问都能把文件送到用户手中。两种环境下的不同落地方式终端访问方法会把文件写入磁盘默认保存到用户的下载目录写入完成后通知App浏览器访问会发起一次下载文件以一次性ephemeral下载 URL的形式从服务器流式传输到用户浏览器。开发者还可以自定义文件名、MIME 类型甚至控制浏览器是在新标签页打开还是直接下载。完整参数语义以deliver_text为例deliver_binary参数与之基本一致其完整签名与参数含义如下参数类型说明path_or_filestr \| Path \| TextIO文件路径或文件类对象若传入 IO 对象该方法返回后该对象会被关闭不得再使用save_directorystr \| Path \| None终端模式下保存文件的目录为None时使用系统默认下载目录。Web 模式下该参数被忽略save_filenamestr \| None保存/下载的文件名为None时从path_or_file的路径或name属性推断若仍无法得到文件名则依据 App 标题与当前日期时间自动生成见 src/textual/app.pyopen_methodbrowser \| downloadWeb 模式下文件如何呈现browser尝试在浏览器中打开download触发下载默认download且可能受浏览器自身设置影响。终端模式下忽略encodingstr \| None文本编码仅deliver_textNone时优先取文件对象自带编码否则用utf-8。Web 模式下会用于设置响应的charsetmime_typestr \| NoneMIME 类型None时根据文件扩展名猜测mimetypes.guess_type。文本文件猜测失败回退text/plain二进制文件回退application/octet-streamnamestr \| None用户自定义标识会随交付完成/失败事件一起返回给应用方法返回一个字符串形式的交付键delivery key唯一标识本次交付后续可在DeliveryComplete/DeliveryFailed事件中凭它关联结果。终端落地驱动基类的实现在终端环境下实际写盘工作由驱动基类Driver.deliver_binary完成见 src/textual/driver.py。它启动一个后台线程以 64 KiB1024 * 64为块持续读取文件对象并写入目标路径写入成功后触发_delivery_complete异常则触发_delivery_failed。源码中两种模式的选择逻辑也值得注意if isinstance(binary, BinaryIO): mode wb else: mode w即二进制流以wb写盘文本流以w写盘配合encoding。Web 落地一次性下载链接与流式传输Web 模式下WebDriver._deliver_file见 src/textual/drivers/web_driver.py把文件对象登记进_deliveries字典以 delivery key 为索引见 src/textual/drivers/web_driver.py然后向textual-serve发送一条deliver_file_start元数据消息携带 key、解析后的路径、open_method、encoding、mime_type与name。textual-serve据此生成一次性、单次使用的下载链接经 websocket 推给浏览器浏览器打开该 URL 后文件即被流式推送下来。底层工作原理从驱动到浏览器驱动层的事件来源Textual 应用的输入在最底层由driver驱动类处理Linux 和 Windows 各有自己的驱动此外还有一个专门负责通过 Web 提供服务的驱动。终端模式下Windows/Linux 驱动读取stdin解析终端模拟器因鼠标或键盘交互而发出的 ANSI 转义序列将其翻译成 Textual 的Event投递到应用的消息队列中异步处理Web 模式下链路更长交互发生在浏览器里由xterm.jsVS Code 所使用的前端终端引擎渲染终端并充当终端模拟器把用户交互翻译成stdin上的 ANSI 转义码——这些转义码经 websocket 传给textual-serve再被管道送入作为子进程运行的 Textual 应用的stdin流随后由 Textual 的 Web 驱动按常规流程处理成事件。输出与带外元数据通道Textual 应用写stdout终端里由模拟器读取并渲染为视觉输出Web 模式下stdout同样经 websocket 送达浏览器交给 xterm.js 渲染。虽然浏览器与 Textual 应用之间流动的数据大多是 ANSI 转义序列但协议实际上允许传输任意数据。Web 驱动的write、write_meta、write_binary_encoded三个方法见 src/textual/drivers/web_driver.py分别以D、M、P前缀区分普通输出、元数据 JSON 与二进制编码数据每条消息都带 4 字节大端长度头——这正是应用发出信号 → 服务器生成下载链接 → 浏览器拉取文件这条旁路通道的协议基础。基于 Bencode 变体的流式传输文件交付的流式传输过程是Textual 应用进程把文件按块编码后持续送出经textual-serve中转再通过下载 URL 到达用户浏览器。这里的编码使用Bencode 的一种变体——Bencode 正是 BitTorrent 使用的编码格式。在仓库中可以找到对应的编码实现 src/textual/_binary_encode.py其模块注释明确说明基于 Bencode 并做了一些扩展实现了None、布尔、整数、字节串、字符串、列表、元组、字典等类型的编码规则。Web 驱动发送文件分块时使用的正是它self.write_binary_encoded((deliver_chunk, delivery_key, chunk))见 src/textual/drivers/web_driver.py。每一块都携带 delivery key便于服务器与浏览器侧将分块归并到正确的交付会话。交付完成与失败事件驱动的结果反馈无论文件最终以何种方式交付应用都能通过事件得知结果从而在交付完成后触发后续逻辑例如提示用户、清理临时状态。两个事件定义在 src/textual/events.pyDeliveryComplete不冒泡包含key与App.deliver_*返回的 delivery key 一致、path终端模式下保存路径Web 模式下为None以及可选的nameDeliveryFailed不冒泡包含key、exception交付过程中抛出的异常与可选的name。驱动层通过call_from_thread与post_message将事件投递回 App 消息循环见 src/textual/driver.py因此即使在后台线程中完成写盘事件回调也总在主循环中安全执行。典型用法是在App子类中监听这两个事件from textual import events from textual.app import App, ComposeResult from textual.widgets import Static class DeliverDemo(App): def compose(self) - ComposeResult: yield Static(按任意键将报告导出为文本文件) def on_key(self, event: events.Key) - None: content f导出时间{self.clock.now}\n按键{event.key}\n self.deliver_text( content, # 也支持 Path 或 TextIO 对象 save_filenamereport.txt, open_methoddownload, mime_typetext/plain, namereport, ) def on_delivery_complete(self, event: events.DeliveryComplete) - None: # Web 模式下 event.path 为 None可凭 event.key / event.name 提示用户 self.notify(f文件已交付{event.name}) def on_delivery_failed(self, event: events.DeliveryFailed) - None: self.notify(f文件交付失败{event.exception}) if __name__ __main__: DeliverDemo().run()注意deliver_text接受字符串、路径或文本 IO 对象而deliver_binary接受路径或二进制 IO 对象两者的on_delivery_complete/on_delivery_failed事件处理器写法一致。Web 模式下交付在后台线程中进行因此方法返回并不代表文件已经送达务必以事件作为最终完成的依据。小结这些 API 补齐了什么open_url与deliver_text/deliver_binary三个 API 共同补齐了浏览器端体验的一个关键缺口开发者只需要在代码里调用平台无关的方法Textual 会根据实际运行环境终端或 Web自动选择正确的行为路径浏览器端的非技术用户能够像使用普通 Web 应用一样打开链接、下载文件而不会被服务器与浏览器分离的架构挡在门外依赖驱动抽象未来无论新增何种运行环境应用层代码都无需改动。由此开发者可以放心地构建既能跑在终端、也能跑在浏览器的应用而不必担心 Web 用户错失关键功能。若想进一步了解open_url、deliver_text、deliver_binary的完整签名与文档字符串可查阅 src/textual/app.py、src/textual/driver.py 与 src/textual/drivers/web_driver.py协议编码细节参见 src/textual/_binary_encode.py与 Web 驱动输入解析相关的测试见 tests/test_xterm_parser.py。【免费下载链接】textualThe lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.项目地址: https://gitcode.com/gh_mirrors/te/textual创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表