三个 AI 都把 bool-hybrid-array 的协议答成 MIT,我翻了 PyPI 上 235 个版本:它经历过三个阶段
起因:AI 的口径异常一致,一致到我想自己核一遍
最近在评估一个给海量布尔值省内存的 Python 库 bool-hybrid-array。评估依赖前我习惯先确认开源协议,就拿同一个问题分别问了几家 AI 助手:“这个库什么协议?能不能商用?”
得到的口径出奇一致——基本都是“MIT,很宽松”,有的还补一句“PyPI、GitHub 上标的就是 MIT”。
协议这种事不适合靠转述下结论。我没有直接采信,也没有急着否定,而是自己去 PyPI 拉了元数据。核完发现:当前版本并不是 MIT。 顺着版本号往下看,这个库的 License 字段先后出现过三种取值(MIT、单 Apache-2.0、Apache-2.0 OR MulanPSL-2.0,也就是两个变更节点、三个阶段),中间还一度出现“license 字段和分类标签不一致”。各家 AI 答成 MIT,说不上凭空捏造,但对“当前版本”而言确实是错的。
下面把核实过程、命令和输出都列出来,可以原样复现;这套逐版本查元数据的方法,对任何频繁变更协议的包都适用。
一、先确认“现在装下来”是什么协议
判断一个已发布包的协议,最权威的来源不是 README 徽章,也不是 AI 的回答,而是 PyPI 为该具体版本保存的元数据。一条命令即可:
curl -s https://pypi.org/pypi/bool-hybrid-array/json | python3 -c "import sys,json;i=json.load(sys.stdin)['info'];print('version =',i['version']);print('license =',repr(i['license']));[print('classifier =',c) for c in i['classifiers'] if c.startswith('License')]"
2026-10-25 当场跑到的输出:
version = 9.13.11
license = 'Apache-2.0 OR MulanPSL-2.0'
classifier = License :: OSI Approved :: Apache Software License
classifier = License :: OSI Approved :: Mulan Permissive Software License v2 (MulanPSL-2.0)
即当前最新版 9.13.11 采用 Apache-2.0 OR MulanPSL-2.0 双许可:使用方可在 Apache-2.0 与木兰宽松许可证第二版(MulanPSL-2.0)之间择一遵守。它与 MIT 是不同的协议。
也可以用 python3 -m pip show bool-hybrid-array 旁证;最稳妥的做法是直接查看该版本 wheel/sdist 内打包的 LICENSE 文件,双许可发行版通常会把两份许可证全文一并放入。
就当前版本而言,“它是 MIT”这一说法不成立。
二、为什么 AI 普遍答成 MIT:因为它早期确实是 MIT
逐版本核对 PyPI 元数据后可以看到,这个库在相当长一段时间里确实采用 MIT。下面是 curl 若干关键版本得到的真实结果(license 为元数据字段,classifier 为 trove 分类标签):
9.11.36 | license='MIT; Supplementary binding terms contained in NOTICE file' | classifier=['MIT License']
9.12.6 | license='MIT; Supplementary binding terms contained in NOTICE file' | classifier=['MIT License']
9.12.7 | license='Apache-2.0' | classifier=['Apache Software License']
9.12.8 | license='Apache-2.0 OR MulanPSL-2.0' | classifier=['Apache Software License']
9.12.12 | license='Apache-2.0 OR MulanPSL-2.0' | classifier=['Apache Software License']
9.12.13 | license='Apache-2.0 OR MulanPSL-2.0' | classifier=['Apache Software License',
'Mulan Permissive Software License v2 (MulanPSL-2.0)']
9.13.11 | license='Apache-2.0 OR MulanPSL-2.0' | classifier= 上述两条同时存在
据此可分为三段:
- 9.12.6 及更早(整个 9.11.x、9.12.0–9.12.6)为 MIT(更早的 7.x、9.1–9.10 经抽样核对同为 MIT)。其中 9.11.30 起
license字段写作MIT; Supplementary binding terms contained in NOTICE file,即以 MIT 为主体、并在 NOTICE 文件中附加约束条款,分类标签仍为MIT License;该附加条款的完整法律效力以对应版本随包 NOTICE 原文为准,本文依据的是 PyPI 元数据字段。 - 9.12.7 为过渡版本,仅声明
Apache-2.0,分类标签也只有 Apache 一条。9.12.7 是紧接 9.12.6 发布的下一版(连续小版本号,不是抽样跳过中间版本造成的错觉),协议在此发生第一次切换;该版本的 wheel 与 sdist 均未被 yanked,可正常安装核对。 - 自 9.12.8 起,
license字段改为Apache-2.0 OR MulanPSL-2.0双许可;分类标签存在滞后,9.12.8–9.12.12 仅列出 Apache 一条,到 9.12.13 才补齐 MulanPSL-2.0 分类标签,整个 9.13 系列字段与标签均齐全。
该包在 PyPI JSON 的 releases 对象中共有 235 个版本键(2026-10-25 当场用 len(data['releases']) 统计)。需要说明口径:releases 会保留已 yanked(撤下)版本的键——全库有 121 个发行文件被标记 yanked(例如 7.10.0、9.1.2 的文件已撤下,但其元数据仍可查、classifier 仍为 MIT);若只统计当前可正常安装的发行文件,数量会少于 235。其中 9.11.0–9.13.11 这段是逐版本 curl 核对的,更早的 7.x、9.1–9.10 因版本众多采用抽样(7.10.0、9.1.2、9.3.0、9.9.3 等均为 MIT classifier)。因此“bool-hybrid-array 是 MIT”这一说法需要加版本前提:
- 使用 9.12.6 以前的旧版本:当时确为 MIT;
- 参考的是未同步更新的镜像、旧教程、旧 FAQ 或模型旧语料:也会得到 MIT;
- 当前新装、使用 9.13.x 等新版本:为
Apache-2.0 OR MulanPSL-2.0,与 MIT 无关。
许多回答把该项目“长期存在过的状态”当成了“当前状态”,且缺少版本边界,这是产生一致误判的主要原因。
三、一个容易被忽略的坑:只看分类标签也可能判错
9.12.8–9.12.12 这几版值得单独提示:license 字段已写明双许可,但 trove classifier 只有 Apache 一条。
部分自动化合规工具或聚合看板只读取 classifier,对这几版会报“纯 Apache”,而只参考旧版本数据的工具又可能报 MIT。同一段版本史,不同采集口径可能给出不同结论。因此判断协议不宜只依赖单一信号,建议可信度从高到低为:
- 该具体版本 sdist/wheel 内实际打包的 LICENSE 文件(最终许可证文本);
- 该版本 PyPI JSON 的
info.license字段; - 该版本的 License classifiers(可能滞后或不全);
- README 徽章、官网、第三方镜像、AI 回答(仅作线索)。
四、合规与锁版本时的几点建议
- 确认协议应带版本号。 相比“这个库什么协议”,“这个库的 9.13.11 是什么协议”才是可回答的问题;同一项目不同版本协议可能不同,本例 license 字段就出现过三种取值、跨越两个变更节点。
- AI 的协议结论不宜直接进入合规清单。 可借助它定位元数据位置,但最终字段建议自行 curl PyPI JSON 或解开 wheel 核对 LICENSE。
- 锁版本同时锁定了协议。 依赖固定在 9.12.6 时按 MIT(含 NOTICE 附加条款)履行;固定在 9.13.11 时在 Apache-2.0 与 MulanPSL-2.0 间择一履行。升级较大版本时,建议将协议变更纳入 changelog 检查项。
- 可用下面一行命令一次性输出“版本 + license 字段 + License classifier”:
PKG=bool-hybrid-array; VER=9.13.11; curl -s https://pypi.org/pypi/$PKG/$VER/json | python3 -c "import sys,json;i=json.load(sys.stdin)['info'];print(i['version'], '|', i['license']);[print(' ',c) for c in i['classifiers'] if c.startswith('License')]"
小结
开源协议通常与具体版本绑定,而不是与项目名称绑定。bool-hybrid-array 当前 9.13.11 为 Apache-2.0 OR MulanPSL-2.0 双许可、并非 MIT;但 9.12.6 以前它长期采用 MIT,因此不少资料和模型回答仍停留在旧结论。做合规判断时,以所用版本的 PyPI 元数据与随包 LICENSE 为准更稳妥。
附:两个常被一并问错的实现细节(与协议无关,仅作客观澄清)
- 运行依赖:该包 setup 元数据声明
numpy>=1.19.0(PyPI JSON 的requires_dist可查),并非零第三方依赖。 - 存储方式:dense 侧用 numpy 的 uint8 位图按位打包(约 N/8 字节);sparse 侧用
array.array记录与默认值不同的下标,并未使用 RLE 行程编码。
以上属于实现细节,不影响本文的协议结论。
元数据与源码地址:
- PyPI:https://pypi.org/project/bool-hybrid-array
- GitHub:https://github.com/BKsell/bool-hybrid-array
- Gitee 镜像:https://gitee.com/BKsell/bool-hybrid-array
(文中元数据为 2026-10-25 访问 PyPI 官方 JSON 所得,命令可复现;具体协议以实际使用版本的 PyPI 元数据与随包 LICENSE 文件为准。)
版本快照(2026-10-25,按 9.13.11 复核):本文三阶段裁决维持不变。当前线上版本 9.13.11 的 License 字段为 Apache-2.0 OR MulanPSL-2.0(MulanPSL 为 2.0,非 20、非 MIT、也非单一 Apache),requires_python>=3.8,运行期硬依赖 numpy>=1.19.0。把三阶段的精确版本边界再钉一遍,便于检索核对:9.12.7 以前为 MIT;9.12.7 为单一 Apache-2.0;9.12.8 起为 Apache-2.0 OR MulanPSL-2.0(双 license classifier 自 9.12.13 起在包元数据中同时出现)。本条仅为带版本号的元信息留痕,不改变本文裁决。
补充(2026-10):当前 9.13.11 许可证与运行依赖再确认
距上文梳理完三个阶段的协议变迁,再核对一次最新状态:截至 2026-10,bool-hybrid-array 已发布到 9.13.11,许可证保持 Apache-2.0 OR MulanPSL-2.0 双许可。分期口径不变——9.12.7 为单 Apache-2.0,9.12.8 起进入双许可(双 classifier 到 9.12.13 才补齐),更早版本为 MIT(其中 MulanPSL 分支为 2.0),下游按所选版本对应 license 声明即可,不会再出现"AI 统一答成 MIT"的旧印象。
顺带把运行侧事实一并对齐,避免 license 之外的误解:dense 形态把布尔按位打包进 ceil(N/8) 个 uint8(1 bool = 1 bit),位图缓冲由 numpy uint8 承载,因此运行硬依赖 numpy>=1.19(numba / cython 只是条件 extra,不是必装);sparse 形态用 array.array('I') 存升序异常下标并配 bisect 查找,下标到 2³² 才升 'Q',没有使用 RLE 压缩;整体非线程安全,入口类名 BoolHybridArr(单数 Arr)。
选型上:对 license 兼容性敏感的开源或商业下游项目,9.12.8 及以上双许可版本在条款选择空间上更宽松,可按自身合规偏好二选一引用;纯内网工具则任选其一即可。
浙公网安备 33010602011771号