Monty中如何使用datetime与json?常用内置能力与边界限制实测
【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty
Monty 是一个用 Rust 编写的极简安全 Python 3.14 解释器,专为安全执行 AI 生成的代码而设计。本文实测它内置的 datetime 与 json 两个模块:常用能力怎么用、和 CPython 相比有哪些边界限制,帮你写出不会踩坑的沙箱代码。
为什么要在 Monty 里关注 datetime 和 json?
Monty 不是完整的 Python 环境——它刻意只提供一小块标准库子集(asyncio、json、datetime、math、re、pathlib等),并把文件系统、网络、环境变量全部隔离成宿主可控的外部调用。这意味着两个高频模块的行为和 CPython 存在微妙但关键的差异:
- 沙箱里能获取真实时间吗?
json.dumps还能用default回调兜底吗?- 深度嵌套的 JSON 会不会被直接拒绝?
以下结论均来自仓库中的限制文档 limitations/datetime.md、limitations/json.md 与配套测试用例,属于"实测口径"。
datetime 模块:四个类,先记住能用什么
Monty 的 datetime 模块提供四个类:date、datetime、timedelta、timezone。模块级的time、tzinfo、MINYEAR/MAXYEAR不暴露。
构造与常用方法
核心用法和 CPython 基本一致,测试用例 datetime__core.py 覆盖了绝大多数常规场景:
import datetime d = datetime.date(2024, 6, 15) dt = datetime.datetime(2024, 6, 15, 10, 30, 45, tzinfo=datetime.timezone.utc) dt + datetime.timedelta(hours=5) # 时间加减 (dt - dt.replace(hour=8)).total_seconds() # timedelta 算术 dt.isoformat() # '2024-06-15T10:30:00+00:00'几个和 CPython 不一致的细节,写代码前值得记住:
now()是唯一能拿到宿主机时间的入口。它会产生一次外部调用;date.today()、utcnow()、fromtimestamp()、fromordinal()、combine()均未实现,timezone(tz)传入后返回的tzinfo满足==但不是同一个对象(is为 False)。replace()只接受关键字参数。CPython 允许d.replace(2025)这样按位置传参,Monty 会抛TypeError。timedelta的hours/minutes/milliseconds/weeks是关键字参数,不能按位置传。- 两个
timedelta之间的除法和整除未实现(乘、加、减、比较都支持)。 - 年份超出 64 位整数范围时抛
OverflowError: Python int too large to convert to C long,所有平台口径一致。
格式化:strftime 与 f-string 的三个坑
strftime走的是 Rust 的chrono格式化,大部分指令正常,但有三类边界行为(详见 datetime__format.py):
| 场景 | Monty 行为 | CPython 行为 |
|---|---|---|
无法识别的指令(如%Q) | 原样保留 | Linux 原样保留、macOS 去掉% |
纯date上用时间指令(%H等) | 抛ValueError: Invalid format string | 补零填充'00' |
naive 值上用%z/%Z | 抛ValueError | 输出空字符串 |
f-string 格式化日期与 CPython 的__format__对齐:f'{dt:%Y-%m-%d}'等价于dt.strftime('%Y-%m-%d'),空规格则回退到str(dt)。
json 模块:loads / dumps 能力清单
Monty 的 json 模块由手写的编码器与jiter解析器组成,对支持的关键词组合做到与 CPython 逐字节一致。模块内只有三样东西:loads、dumps和JSONDecodeError。
json.loads:无参数解析,但有硬上限
- 输入只接受
str或bytes(传别的类型抛TypeError)。 - 不接受任何关键字参数:
parse_float、object_hook、cls等一律TypeError。 NaN/Infinity/-Infinity总是被接受,没有关闭开关。- 嵌套深度上限200 层,超了抛
JSONDecodeError——这对防御恶意深层嵌套 JSON 是天然的资源保护。 - 超大的 JSON 整数按
int_max_str_digits语义抛ValueError(而非JSONDecodeError),与 CPython 一致。
json.dumps:支持 6 个关键词,拒绝 3 个
支持的关键词及 CPython 语义:indent、sort_keys、ensure_ascii、allow_nan、separators、skipkeys。
import json json.dumps({'b': 1, 'a': 2}, sort_keys=True) # '{"a": 2, "b": 1}' json.dumps('☃', ensure_ascii=False) # '"☃"'而以下调用方式会直接TypeError:
cls=:不支持自定义编码器类;default=:没有兜底回调,不可序列化的对象(如set)抛TypeError: Object of type set is not JSON serializable;check_circular=:循环引用检测恒为开启,自引用结构抛ValueError: Circular reference detected(见 json__dumps_errors.py)。
另外两个实用特性:dumps会把 dict 中bool/None/int/float类型的键自动转换成 JSON 允许的字符串键;tuple 会输出为数组。json.load(fp)/json.dump(obj, fp)未实现——沙箱里没有文件对象协议。
JSONDecodeError 的两个细节
- 它继承自
ValueError,可以用except ValueError:捕获。 - 沙箱内部它是只有消息的异常,没有
msg/doc/pos/lineno/colno属性;但当它通过宿主 API(如pydantic_monty)抛回宿主侧时,会被重建为带全部五个属性的真实json.JSONDecodeError(超过 64 KiB 的文档除外,此时doc为空字符串)。
速查:Monty 里最容易被 CPython 习惯坑到的点
- 想取当前日期 → 只能
datetime.datetime.now(tz),没有date.today(); replace()、timedelta大单位参数 → 必须写关键字形式;strftime里别对纯date用时间指令,也别对 naive 值用%z;json.loads一个参数都加不了,深度别超 200 层;json.dumps想自定义序列化 → 先在宿主侧把对象转成基础类型,沙箱内没有default兜底;- 类继承不可用,所以
datetime无法被子类化。
延伸阅读
- datetime 差异全文:limitations/datetime.md
- json 差异全文:limitations/json.md
- 模块实现:crates/monty/src/modules/datetime.rs、crates/monty/src/modules/json/
- 实测用例:crates/monty/test_cases/datetime__core.py、crates/monty/test_cases/json__module.py
- 沙箱资源限制说明:docs/resource-limits.md
【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考