- 科学计算
- 科研
- 数据分析
【免费下载链接】astropy
Astronomy and astrophysics core library
导读
SAMP(Simple Application Messaging Protocol,简单应用消息协议)是 IVOA(国际虚拟天文台联盟)定义的进程间通信协议,它通过一个本地运行的 Hub 进程,让 TOPCAT、SAO DS9、Aladin 等桌面天文工具与 Python 代码互相收发消息。本文以 docs/samp/example_hub.rst 为骨架,结合astropy.samp子包的源码实现,系统讲解在 astropy 中启动与停止 SAMP Hub 的三种途径——复用已有应用自带的 Hub、使用samp_hub命令行工具、以及用SAMPHubServer类编程式启动(含全部命令行参数与构造参数的表格式解析),并深入说明锁文件、超时线程、Web Profile 等底层机制,帮助你根据实际场景选择正确方案。
为什么需要先启动一个 SAMP Hub
astropy.samp是 SAMP V1.3 协议的 Python 实现,除 Standard Profile 外还支持 Web Profile。协议的基本工作方式是:本地主机上必须有一个正在运行的 SAMP "Hub" 进程,各客户端程序先连接到这个 Hub,再通过 Hub 互相转发消息(详见 docs/samp/index.rst)。
Hub 在整个会话中的角色类似于消息中枢:客户端通过它完成注册(register)、声明元数据(declare_metadata)、订阅消息类型(declare_subscriptions)、发送通知(notify)与同步调用(call)等。在大多数 SAMP 会话中,同时只会有一个 Hub 在运行。
值得留意的是,从 astropy 8.0 版本起,astropy.samp已被标记为弃用,官方建议迁移到pyvo.samp;但当前仓库中的实现依然完整可用,本文所述内容均基于仓库现状(弃用警告见 astropy/samp/init.py)。
启动 SAMP Hub 一共有三种方式,按适用场景从易到难逐一说明。
方式一:使用现有应用自带的 Hub
如果你只是想在日常桌面工具之间互传数据,最简单的做法是直接启动一个自带 Hub 的 SAMP 应用,例如:
- TOPCAT(表格数据探索工具)
- SAO DS9(图像显示与星表叠加工具)
- Aladin Desktop(图像与星表可视化工具)
TOPCAT 和 Aladin 在找不到已运行 Hub 时会自动拉起一个 Hub(这一点在 docs/samp/example_table_image.rst 中有明确说明),因此先启动这类应用、再运行你的 Python 客户端即可,无需额外操心 Hub 的启动与停止。
这种方式的优点是完全不用接触 Hub 进程管理:Hub 的生命周期随宿主应用走,关闭应用即停止 Hub。缺点是你必须在桌面会话中保持该应用处于运行状态,且 Hub 的监听地址、端口等参数由该应用决定,不由你控制。
方式二:使用samp_hub命令行工具
astropy自带一个独立的命令行程序samp_hub,可以在自己的进程里运行一个常驻 Hub。它是三种方式中最常用、也最接近“开箱即用”的途径。
基本用法
直接运行即可启动一个 Hub:
$ samp_hub查看全部可用选项:
$ samp_hub -h停止服务:在前台终端中按下Control-C。
samp_hub的主函数位于 astropy/samp/hub_script.py,它使用argparse解析命令行参数,将解析结果透传给SAMPHubServer构造器后调用hub.start(False)以非阻塞方式启动,随后进入轮询循环:
- 当 Hub 仍在运行(
hub.is_running为真)时,以 0.01 秒间隔循环检查; - 捕获
KeyboardInterrupt时调用hub.stop()优雅停机; - 捕获
OSError时打印 I/O 错误并以状态码 1 退出。
全部命令行选项详解
以下选项均直接来自 hub_script.py 中的argparse定义,按功能分组列出。
基础连接选项
| 选项 | 参数 | 说明 |
|---|---|---|
-k,--secret | CODE | 自定义 secret 码,写入 lockfile;不指定时由uuid.uuid1()自动生成 |
-d,--addr | ADDR | 监听地址(或 IP);默认为127.0.0.1(无网络时)或本机主机名(有网络时) |
-p,--port | PORT | 监听端口号;默认为 0,即由操作系统挑选空闲端口 |
-f,--lockfile | FILE | 自定义 lockfile 文件路径 |
-w,--no-web-profile | — | 以禁用 Web Profile 的方式运行 Hub(默认启用 Web Profile) |
-P,--pool-size | SIZE | 与客户端通信的 socket 连接池大小,默认 20 |
超时选项组(Timeout group)
| 选项 | 参数 | 说明 |
|---|---|---|
-t,--timeout | SECONDS | Hub 不活动超时(秒):超过该时长无任何活动则 Hub 自动关闭;默认 0,即永不过期 |
-c,--client-timeout | SECONDS | 客户端不活动超时(秒):超过该时长无活动的客户端将被 Hub 强制注销,并收到samp.hub.disconnect类型通知;默认 0,即客户端永不失效 |
日志选项组(Logging options)
| 选项 | 参数 | 说明 |
|---|---|---|
-L,--log-level | LEVEL | Hub 日志级别,可取值即logging模块的全部级别名(CRITICAL、ERROR、WARNING、INFO、DEBUG 等),默认INFO |
-O,--log-output | FILE | 将日志消息重定向到指定文件;默认为空,即输出到标准输出/标准错误 |
高级选项组(Advanced group)
| 选项 | 参数 | 说明 |
|---|---|---|
-l,--label | LABEL | 为 Hub 分配一个人类可读名称,写入 lockfile 的hub.label字段 |
-m,--multi | — | 以多实例模式启动 Hub:使用随机命名的自定义 lockfile(见下文锁文件机制) |
其中-m/--multi属于非标准的 Hub 行为,它允许多个 Hub 同时运行,每个 Hub 的 lockfile 放在用户主目录的.samp-1目录下,命名格式为samp-hub-<PID>-<ID>(PID 为 Hub 进程 ID,ID 为内部整数)。
命令行工具的测试验证
仓库中的测试 astropy/samp/tests/test_hub_script.py 演示了该工具的典型用法:以-m(多实例模式)与-w(禁用 Web Profile)启动,设置--log-level CRITICAL,并让hub_script(timeout=3)在 3 秒后自动停机,最后断言日志级别确实被设置为 CRITICAL。这印证了samp_hub各选项与hub_script的对应关系:-w对应web_profile=False,--log-level会直接作用于 astropy 全局日志器astropy.log。
方式三:编程式启动 Hub(高级用法)
对于需要把 Hub 嵌入到自己程序里的场景(例如嵌入 GUI 应用),可以绕过命令行,直接创建SAMPHubServer实例并调用其方法。在交互式 Python 提示符或脚本中均可:
>>> from astropy.samp import SAMPHubServer >>> hub = SAMPHubServer() >>> hub.start()停止 Hub:
>>> hub.stop()为什么文档不推荐普通用户使用这种方式
原文档明确提示:编程式启动一般不建议普通用户使用,因为它无法正确处理 Web SAMP 客户端连接;该方式主要留给需要在 GUI 中嵌入 Hub 的开发者。
从源码看,这一警告是有依据的:当web_profile=True(默认)且未提供web_profile_dialog时,Hub 在 Web 客户端请求连接时依赖web_profile_text_dialog在终端弹出文本式确认对话框(见 astropy/samp/hub.py 的_serve_forever循环与 astropy/samp/web_profile.py 中的web_profile_text_dialog实现)。而hub.start()默认以非阻塞线程方式运行(wait=False),在脱离交互式终端的环境里,这种终端交互确认并不适用。若确需保留 Web Profile,开发者应通过web_profile_dialog参数传入自定义 GUI 对话框实例,详见进阶文档 docs/samp/advanced_embed_samp_hub.rst(该文档还给出完整的 Tk 应用示例:继承WebProfileDialog、定时调用handle_queue()、实现show_dialog()后调用consent()/reject())。
SAMPHubServer 构造参数详解
以下参数定义见 astropy/samp/hub.py 的__init__签名,与samp_hub命令行选项一一对应:
| 参数 | 默认值 | 说明 |
|---|---|---|
secret | None | lockfile 使用的 secret 码;不指定时用uuid.uuid1()生成 |
addr | None | 监听地址或 IP;无网络时默认127.0.0.1,有网络时默认本机主机名(源码中通过internet_on()判断并回退,见 hub.py) |
port | 0 | XML-RPC 服务端口;0 表示由操作系统分配空闲端口 |
lockfile | None | 自定义 lockfile 名称 |
timeout | 0 | Hub 不活动超时(秒),大于 0 时自动停机;0 表示永不过期 |
client_timeout | 0 | 客户端不活动超时(秒),大于 0 时自动注销不活动客户端;0 表示永不失效 |
mode | 'single' | 'single'使用标准.samplockfile(桌面会话单实例);'multiple'使用.samp-1目录下的samp-hub-<UUID>非标准 lockfile |
label | '' | 人类可读的 Hub 名称,写入 lockfile 的hub.label字段 |
web_profile | True | 是否启用 Web Profile 支持 |
web_profile_dialog | None | 自定义确认对话框实例,替换终端文本式确认;实例会被注入queue_request与queue_result两个队列 |
web_port | 21012 | Web SAMP 端口;SAMP 规范要求 Web Profile 固定使用 21012,除测试外不应改动 |
pool_size | 20 | 与客户端通信的 socket 连接池大小 |
start() 与 stop() 的底层行为
start(wait=False)的完整流程(hub.py):
- 若 Hub 已在运行或 lockfile 已存在,抛出
SAMPHubError; - 若启用 Web Profile,先启动 Web Profile HTTP 服务(
_start_web_profile_server); - 启动 Standard Profile 的 XML-RPC 服务;
- 通过
create_lock_file创建 lockfile(写入 Hub 参数); - 以 Hub 自身的身份注册为 SAMP 客户端并声明元数据(
_setup_hub_as_client),使 Hub 也能收发消息; - 启动后台线程(
_start_threads); - 若
wait=True,则阻塞并join主线程——这一模式正是samp_hub独立脚本使用的阻塞式运行方式;wait=False(默认)则在后台线程中运行,适用于交互式 Python 会话与 GUI 嵌入。
stop()(hub.py)会先向所有订阅samp.hub.event.shutdown的客户端广播停机通知(_notify_shutdown),将_is_running置为False,校验 lockfile 中的 secret 后删除 lockfile,最后以 10 秒超时等待所有线程退出并重置内部状态。仓库测试 astropy/samp/tests/test_hub.py 验证了 Hub 的“启动→停止→再次启动”全生命周期,包括启用 Web Profile 的重复启动场景。
超时机制的实现
timeout与client_timeout分别由两个独立的守护线程负责(hub.py):
_timeout_test_hub:每 0.05 秒轮询一次,当距上次活动时间超过timeout时发出SAMPWarning警告并调用stop();_timeout_test_client:遍历各客户端最后活动时间,对超过client_timeout的客户端发出警告、广播samp.hub.disconnect通知并强制注销。
两个线程仅在对应超时值大于 0 时才会被创建(见_start_threads),因此默认配置下不存在额外的线程开销。
lockfile 机制:Hub 如何被发现
Hub 的“存在性”通过 lockfile 对外宣告,相关逻辑集中在 astropy/samp/lockfile_helpers.py。要点包括:
- 单实例模式(
mode='single')下,lockfile 位于用户主目录的.samp文件;多实例模式(mode='multiple')下位于.samp-1目录,文件名为samp-hub-<UUID>; - 若环境变量
SAMP_HUB设置为std-lockurl:前缀的地址,Hub 会改用该地址(须为file:协议); - 启动前会先清理失效 Hub 的垃圾 lockfile(
remove_garbage_lock_files),并检查是否已有 Hub 在运行——若检测到其他 Hub 正在运行,则放弃启动; - lockfile 内容由
SAMPHubServer.params属性生成(hub.py),包含samp.secret、samp.hub.xmlrpc.url、samp.profile.version、hub.id、hub.label等键。客户端正是通过读取这些键来连接 Hub。
相关配置项
astropy.samp在 astropy/samp/init.py 中定义了两个配置项:
use_internet(默认True):是否允许astropy.samp使用网络;n_retries(默认10):通信失败时的重试次数。
这些配置会影响 Hub 在初始化时对监听地址的选择(addr依赖internet_on())以及消息投递的可靠性。
三种方式的对比与选型建议
| 方式 | 适用场景 | 优点 | 注意事项 |
|---|---|---|---|
| 使用已有应用自带 Hub | 日常桌面工具互操作 | 零配置、生命周期随应用 | 需保持应用运行;参数不可控 |
samp_hub命令行 | 独立常驻 Hub、脚本化工作流 | 参数完整、进程隔离、Ctrl-C 即停 | 需要单独管理进程 |
SAMPHubServer编程式 | 嵌入 GUI、深度集成 | 可在代码内控制全部行为 | 默认 Web Profile 交互不便;普通用户不建议 |
选型建议:仅需在 TOPCAT、DS9、Aladin 之间互传数据时用方式一;需要稳定、可复现的 Hub 环境时用方式二(samp_hub),例如 docs/samp/example_table_image.rst 中发送表格与图像前先执行$ samp_hub的典型流程;只有当你确实要把 Hub 嵌入自己的应用程序(如 GUI)时,才使用方式三,并务必阅读进阶文档 docs/samp/advanced_embed_samp_hub.rst 处理 Web Profile 的连接确认对话框。
进一步阅读
- docs/samp/example_hub.rst:本文的原始骨架文档;
- docs/samp/advanced_embed_samp_hub.rst:在 GUI 中嵌入 Hub 的完整示例(Tk + WebProfileDialog);
- docs/samp/example_table_image.rst:启动 Hub 后收发表格(
table.load.votable)与图像(image.load.fits)的实战流程; - docs/samp/example_clients.rst:
SAMPClient与SAMPIntegratedClient的客户端实现细节; - astropy/samp/hub_script.py 与 astropy/samp/hub.py:
samp_hub与SAMPHubServer的源码实现; - astropy/samp/tests/test_hub_script.py 与 astropy/samp/tests/test_hub.py:对应的自动化测试用例。
- 科学计算
- 科研
- 数据分析
【免费下载链接】astropy
Astronomy and astrophysics core library
相关推荐
Sapling 的 Rebase 完全指南:用 `sl rebase` 移动提交的三种方式与底层原理
Sapling 的 Rebase 完全指南:用 sl rebase 移动提交的三种方式与底层原理 Sapling( sl )中所谓的 "rebase" 本质上是
开发工具CLI后端TaskingAI 记录(Record)创建完全指南:text / web / file 三种方式与底层分块原理
TaskingAI 记录(Record)创建完全指南:text / web / file 三种方式与底层分块原理 本指南基于 TaskingAI 仓库中的官方
人工智能大模型AI AgentRAG后端前端komorebi 边框样式详解:system / rounded / square 三种模式与底层绘制原理
komorebi 边框样式详解:system / rounded / square 三种模式与底层绘制原理 导读 : border style 是 komore
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考