资讯动态

Monty 沙箱类系统全解:用户类、宿主类实例与边界穿越机制

发布时间:2026/9/16 17:12:59 来源:尧图企业网站定制
Monty 沙箱类系统全解用户类、宿主类实例与边界穿越机制【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/montyMonty 是一个用 Rust 编写的、面向 AI 场景的最小化安全 Python 解释器。本文以 limitations/classes.md 为骨架系统梳理沙箱内用户定义类的支持面、与 CPython 的行为分歧、宿主与沙箱之间类/实例穿越的完整机制ClassInstance/ClassType包装器并辅以源码与测试用例佐证。读完本文你将掌握在 Monty 中安全使用类的边界能判断哪些写法可以复用、哪些会被拒绝或产生行为差异并理解宿主如何以结构化代理而非对象引用的方式传递类实例。概述沙箱内可以定义简单类Sandboxed Python 代码在 Monty 中可以定义简单类。class语句支持实例方法、__init__、__eq__、__repr__/__str__和类变量。类体拥有真实作用域类似 CPython 的 class-body code object因此类变量可以是任意表达式并且可以引用前面定义的类变量class Foo: count 0 def __init__(self, a: int) - None: self.a a def bar(self) - int: return self.a * 2 def __repr__(self) - str: return fFoo(a{self.a})该功能由 class__basic.py 与 class__repr.py 两个测试用例系统验证前者覆盖构造、实例属性读写、方法调用含默认参数与关键字参数、类变量共享、type()/isinstance()、自定义__eq__、绑定方法、无__init__的类、__init__抛异常后的半成品实例清理、实例互相引用形成的引用环可回收等场景后者覆盖__repr__/__str__在repr()/str()/f-string/print及嵌套容器中的派发以及默认 repr 的形态。宿主也可以把自己的类实例用ClassInstance策略包装器包裹以及 namedtuple 值送入沙箱——这是一套独立的机制其方法调用和惰性属性查找按包装器的 uuid 路由回宿主处理见 dataclass__basic.py 与下文「宿主类实例」。支持的功能面Supported surface以下清单界定了下文分歧条款的适用范围。以下特性是可用且与 CPython 匹配的实例方法、__init__完整的参数形态实例与类属性的读写包括setattr(Foo, ...)与「函数属性自动成为方法」绑定方法bound method类变量任意表达式在真实可挂起的类体作用域中求值类装饰器deco class Foo__repr__/__str__/__enter__/__exit__/__eq__/__hash__派发obj.__class__、Foo.__name__、Foo.__doc__/obj.__doc__Foo.__annotations__有序值为字符串化属于暂定能力见 typing.mdtype(obj)/isinstance(obj, Foo)以及 3 参数type()构造__enter__/__exit__的分歧细节见 with.md动态类创建type(name, bases, dict)3 参数type()形态可以在运行时创建类且遵循 CPython 的校验顺序与错误措辞但有以下分歧bases必须是空元组()。任何非空 bases 元组——哪怕是(object,)——都会抛出TypeError: type() bases are not supported这是解析期class Foo(Bar)被拒绝的运行时对应物。关键字参数一律被拒绝。CPython 会把多余的关键字转发给__init_subclass__Monty 没有__init_subclass__但错误信息与object.__init_subclass__产生的完全一致A.__init_subclass__() takes no keyword arguments。命名空间缺少__doc__时只会合成__doc__为None与 CPython 一致。CPython 还会设置__module__、__qualname__、__dict__、__weakref__等这些属性在 Monty 中会抛AttributeError对编译类同样如此。非字符串命名空间键会抛TypeErrornon-string key (int) in the namespace of class A。CPython 只发一个RuntimeWarning就接受它们Monty 没有 warnings 机制因此选择抛出而非静默接受。从源码看动态创建与编译类走同一条路径Class对象的name字段用EitherStr表示编译的class语句可以 intern 名称而运行时type()创建的名称因 intern 表在 prepare 后冻结而由堆持有见 class.rs。与 CPython 的主要分歧命名与类型表象默认repr未定义用户__repr__是Foo object at 0x..使用裸类名CPython 使用限定名module.Foo object at 0x..。class__repr.py 用宽松断言验证了Plain object at 0x前缀且地址与id(pl)一致。__init__/方法的参数个数错误只报方法名、不带类限定符例如__init__() missing 1 required positional argument: y而 CPython 会说Foo.__init__() missing ...。type(obj)返回类对象因此身份比较有效但其自身的repr是class Foo裸名CPython 会限定模块。类对象本身不是type实例。裸名type解析为内建type函数而非类型对象所以type(Foo) is type为FalseCPython 为Trueisinstance(Foo, type)抛TypeError: isinstance() arg 2 must be a type, a tuple of types, or a unionCPython 为True。没有元类。绑定方法报告为function而非method。type(obj.method)是class functionCPython 是class methodMonty 没有独立的method类型。绑定方法按身份比较与哈希。每次obj.method访问都会创建新对象所以obj.method obj.method为False两次访问哈希不同。CPython 按(instance, func)比较/哈希使不同访问相等。class__basic.py 验证了同一个绑定方法对象可以作为 dict 键且hash(m) hash(m)。绑定方法repr是裸的bound methodCPython 渲染为bound method Foo.m of __main__.Foo object at 0x..。赋值语义分歧给Foo.__name__赋值只是存一个普通类成员。与 CPython 中type.__name__是元类描述符、其 setter 会重命名类不同这里不会重命名类Foo.__name__读取与repr(Foo)保持原名只有实例能看到该成员。源码中__name__在命名空间查找之前就被合成返回正是为了模拟 CPython 中元类数据描述符遮蔽同名类成员的行为见 class.rs。给obj.__class__赋值只是存一个普通实例属性并不会重新指派对象的类。obj.__class__ X之后读取会得到X但type(obj)与isinstance仍报告原类留下一个内部不一致的对象。CPython 要么重指派类对兼容类要么抛TypeError: __class__ must be set to a class, not ... object。挂起suspend能力分歧沙箱内代码可以挂起让宿主处理外部/OS 调用但部分 dunder 例外__repr__/__str__不能挂起它们同步运行到完成因此调用外部/OS 函数的__repr__/__str__会抛出而非让出给宿主。__init__和普通方法可以在外部/OS 调用上挂起。只有普通函数__init__可以挂起。当__init__绑定到其他东西内建函数、另一个类、绑定方法……时按 CPython 的描述符绑定语义调用除非是普通函数否则不前置self并强制执行 CPython 的None返回契约但它同步运行到完成不能让出给宿主外部函数__init__抛NotImplementedError而非挂起。class__basic.py 用一组「异形__init__」测试验证了这一点__init__是另一个类时抛__init__() should return None, not _Helper是不可调用整数时抛int object is not callable是async def时抛__init__() should return None, not coroutine而内建print与已绑定方法则按无self前置的语义被调用。__eq__/__hash__/__index__不能挂起与__repr__/__str__一样同步运行到完成调用外部/OS 函数即抛出。__eq__抛出的异常会终止运行无法被比较外围的try捕获。排序 dunder 仍然不派发见下文未派发列表。实例恒为真值无__bool__/__len__派发。递归与栈安全递归/深层__repr__/__str__比 CPython 更早抛RecursionError。对self做 repr、或一长串实例的 repr 相互嵌套如长链表每层嵌套都会在原生 Rust 调用栈上重入解释器一次而普通 Python 级递归位于堆分配的帧栈上受常规 1000 层递归上限约束。原生栈溢出会 abort 进程这对与宿主共享进程的 in-process/wasm API 是致命的因此这种原生重入被独立限制在一个更低的固定深度超限即抛可捕获的RecursionError。无限__repr__递归抛RecursionError结果与 CPython 一致深度不同但 CPython 默认 1000 帧上限仍能渲染的「深而有限」链在 Monty 中可能抛RecursionError。同一上限也适用于同步回调求值如map()、filter()、sorted()/list.sort(key...)、min()/max(key...)以及异形__init__递归见 resource_limits.md 的「Recursion」一节。作用域与命名类体中的推导式可以看到类变量因为 Monty 会把推导式内联进外层作用域。CPython 中推导式有自己的作用域、跳过类作用域因此只有最左侧可迭代对象在类作用域求值推导式体看不到类变量引用类变量offset的[n offset for n in nums]在 CPython 中抛NameError在 Monty 中成功。同名冲突直接拒绝而非解决。当外层函数局部变量与类变量同名、且某个方法捕获了外层那个时CPython 保持二者独立类字典条目 vs. 闭包单元。Monty 将一个名字映射到单一槽位无法同时表示两者于是在编译期抛NotImplementedErrorclass member x that shadows a captured variable of the same name from an enclosing scope而非错误编译。不同名的情况正常工作。跨越宿主边界pydantic_monty/pydantic/monty沙箱类实例向外穿越沙箱定义的类实例以结构化方式向外穿越宿主收到一个只读的MontyClassProxy包含.name、.is_dataclass、.id和.attributes即实例__dict__的转换结果JS 包拼写为.name/.isDataclass/.id/.attributes。宿主不能调用其方法——方法只定义在沙箱内代理不持有活对象。实例及其类携带 worker 生成的 uuid存储在堆对象上因此在穿越与 dump/restore 间保持稳定。from pydantic_monty import Monty with Monty() as pool, pool.checkout() as session: result session.feed_run( class A:\n def __init__(self):\n self.x 1\nA() ) # result is MontyClassProxy(nameA, attributes{x: 1})把代理再传回沙箱作为输入或外部函数结果时交回的是原始对象——back is foo且isinstance(back, Foo)成立——但有以下分歧代理的attributes在回去时不会被应用在宿主侧编辑它们不会改变沙箱对象只会解析仍然存活的沙箱对象且其保持自身状态。沙箱对象已被释放的代理会抛RuntimeError: invalid input type: sandbox instance of Foo (id ...) no longer exists而非物化一份宿主备份副本。宿主发送的实例的代理restore 之后产生没有可解析的沙箱对象再传回去时会以宿主备份副本的身份重新进入该副本由它的attributes构建而不是宿主的原始对象。源码层面沙箱实例与类的 uuid 都是懒生成的Class::boundary_uuid与Instance::boundary_uuid在首次穿越时用get_or_insert_with(create_uuid)生成并存入堆对象见 class.rs 与 instance.rs这正是 uuid 跨穿越、跨 dump/restore 稳定的原因。类对象向外穿越沙箱定义的类对象A本身没有结构化的宿主表示转换为它的类型文本如class A。当实例穿越边界时不会咨询用户的__repr__——宿主拿到的是结构化代理而不是 repr 字符串。宿主类实例ClassInstance包装器宿主对象只有在显式包裹于宿主包的ClassInstance策略包装器时才能进入沙箱直接传裸 dataclass 或类实例作为输入Python 抛MontyConversionError、JS 抛TypeError。进入沙箱后它们是代理eager attrs急切属性在发送时被复制其余一切按包装器的iduuid 路由回宿主绝不用id()或任何地址派生的值。与真实 CPython 对象的分歧type(x)返回真实类的一个轻量替身因为类本身留在宿主侧。沙箱对每个宿主类 id 只保留一个这样的对象同类实例满足type(a) is type(b)x.__class__返回它同一 id 的ClassType作为值传入也会解析到它type(p) is Point相等与哈希只按类 id 判定。它用真实类名命名type(x).__name__是Pointrepr 是class Point——没有 CPython 的class mymod.Point那种模块限定错误信息也使用真实类名unhashable type: Point、Point object is not subscriptable——永远是裸名因此 CPython 的模块限定消息如mymod.Point object does not support the context manager protocol (missed __exit__ method)在 Monty 中只显示Point。但它不是类调用它会向宿主挂起一个__call__请求只有在宿主在ClassType包装器上授予了init时才成功见下文并且——与 Monty 类对象普遍一致——它暴露__name__加上宿主发送的任何 eager 类属性__module__、__qualname__、__doc__、__mro__、__bases__……抛AttributeError__class__同样如此——CPython 会用type回答它。返回宿主时若该类在会话中已注册则解析回真实类对象。源码实现上这是HostClassType堆对象每个类 uuid 在Heap::host_type_index中只有一条由该类的每个HostClass实例持有type(x)/x.__class__都返回它见 host_class.rs。isinstance(x, Point)只按精确类 id 匹配宿主从不发送 bases所以子类实例在沙箱中不算是Point的实例且issubclass不存在。repr()按顺序显示所有 eager attrsPoint(x1, y2)。沙箱代码设置新属性后该属性也会出现在 repr 中——CPython 的 dataclass repr 只显示声明的字段。dataclass__basic.py 特意不断言setattr后的 repr正因 Monty 会显示包括新增属性z在内的全部 eager attrs。该 repr 由write_dataclass_repr生成与原生dataclass实例共用避免两套渲染漂移见 host_class.rs。惰性属性查找在obj.attr、getattr()与hasattr()上咨询宿主——但从解释器自身发起的同步嵌套调用Rust 侧调用的__repr__、__eq__或排序键内部除外那里无法挂起属性读作不存在hasattr→Falsegetattr抛出或返回默认值。下划线前缀的名字从不咨询宿主dunder 探测保持本地。惰性属性读取会运行宿主代码property、JS getter、包装器的convert_value且只有宿主的AttributeError读作「不存在」。任何其他宿主异常都会在读取发生处的沙箱内原样抛出hasattr()/getattr(obj, name, default)不会吞掉它CPython 对抛异常的 property 也是同样处理。线上无法承载的值会在沙箱内抛TypeError: Cannot convert X to Monty value ...而 CPython 会返回它。惰性查找不缓存每次访问都是一次新的宿主往返两次访问之间宿主的变更可见。Eager attrs 是快照——发送后宿主侧的变更不可见沙箱setattr也不会影响宿主对象。dataclass__basic.py 验证了dimensions这类惰性类属性重复访问会重新咨询宿主、且不进入 repr 或相等比较。HostClass::py_getattr源码也注释明确惰性查找不缓存每次访问都是与宿主的全新往返见 host_class.rs。allowed_methodsall只暴露类上定义的函数会搜索其 MRO嵌套类、以属性形式存储的可调用对象或任何其他非函数类属性在被调用时抛AttributeErrorCPython 会调用它们。显式的名字集合则调用getattr返回的任何东西。在 JS 中all要求函数位于Object.prototype/Function.prototype之下的原型链上因此toString()、hasOwnProperty()、call()、bind()等都不存在且无论何种策略包括显式列表constructor、__proto__、prototype、arguments与caller一律被拒绝。作为值读取的方法不是绑定方法名字只在allowed_methods中时m x.greeting抛AttributeError、hasattr(x, greeting)为False——只有x.greeting(...)调用能到达宿主。若名字同时也在lazy_attrs中读取会以宿主函数代理的形式穿越调用时按名字经external_lookup解析并不绑定到实例。这与用户类的Instance处理方式形成对照普通实例方法读取会生成BoundMethod堆对象见 instance.rs。相等只使用 eager attrs同类 attrs 相等自定义__eq__之类的方法不被咨询。宿主实例恒不可哈希——与 CPython 中「定义了__eq__而未定义__hash__」的类规则一致——所以 CPython 中可哈希的 frozen dataclass 在沙箱中会抛TypeError: unhashable type: ...。dataclass__basic.py 与HostClass::py_hash返回None的实现见 host_class.rs互相印证。Frozen dataclass 在沙箱中并不 frozen线上没有 frozen 策略因此在沙箱副本上setattr对任何宿主实例都成功宿主对象从不被触碰。dataclasses.fields()/asdict()对宿主实例不工作dataclasses.is_dataclass(x)返回宿主发送的标志。返回宿主发送的实例宿主得到的是原始对象身份保留丢弃沙箱侧的任何属性变更。同一对象发送两次得到相等的同类 uuid attrs沙箱值但每次发送都分配自己的代理所以a is b为False。实例 id 按包装器、类 id 按进程宿主类——Python 在pydantic_monty.class_instance.type_id_cache中按module.qualname键控类 idJS 按类对象——所以同一进程内多个会话中同一宿主类的实例按类型相等。换一个全新进程id 会不同除非显式钉住ClassType(..., id...)或预填充缓存——在彼处恢复 dump 时这是必须的。因为 Python 的键是名字两个共享module.qualname的不同类对象notebook 单元格中重定义的类或工厂函数构建的类会得到相同的默认 id同时送进一个会话会抛ValueError而非静默别名——给其中一个显式id。JS 会把显式id校验为规范 uuid 并存储为小写所以wrapper.id可能与传入值的大小写不同。沙箱定义的 uuid 位于堆中可跨 dump/restore 存活。不建模继承宿主类的 bases 不会发送因此基类属性和方法不被咨询__bases__抛AttributeError。宿主保留其发送的每个包装器直到会话结束每个发送的包装器含嵌套的、每次initTrue构造、每次convert_value包裹都会向宿主侧实例存储加入一条记录max_memory不计入这些记录用相同 id 重发包装器会覆盖其记录而非新增。参见 pool-architecture.md 中的类实例存储说明。宿主类ClassType包装器宿主可以用ClassType策略包装器把裸类传入沙箱——ClassInstance的孪生作用于类对象本身eager_attrs随类型发送类常量lazy_attrs按需供给allowed_methods暴露 classmethod/staticmethod调用与惰性查找按类 uuid 路由到宿主与实例路由完全一致。当initTruepydantic_monty.ClassType(Point, initTrue)JS 为new ClassType(Point, { init: true })时沙箱代码还可以调用这个类构造以__call__方法调用的形式穿越、在宿主侧运行构造出的实例再用包装器的instance_*策略instance_eager_attrs、instance_lazy_attrs、instance_allowed_methodsJS 为instanceEagerAttrs……包好穿回。init纯粹是宿主侧策略——从不跨线上传递包装器在每次构造请求时检查它。分歧如下缺失/被拒的类属性抛 CPython 类型对象措辞的AttributeError: type object Point has no attribute x。与实例属性一样只有Type.attr语法咨询宿主下划线名字保持本地惰性类查找不缓存。实现上HostClassType::py_getattr对缺失的公开名返回CallResult::AttrLookup按类 uuid 路由对下划线名直接抛attribute_error_type见 host_class.rs。ClassType上的allowed_methods只暴露 classmethod 和 staticmethodall与显式集合都一样通过类调用实例方法Person.greet(other)抛AttributeError: type object Person has no attribute greet而 CPython 会把other作为self传入。JS 中all只暴露类自身的静态函数不继承基类的。没有init或为false时调用类抛TypeError: cannot instantiate host class PointCPython 会构造。这也包括对纯ClassInstance的type(x)()发送实例会为其类注册一个默认ClassTypeinit为 false。构造函数异常像外部函数错误一样传播进沙箱。会话 restore 后类注册消失构造与 classmethod 调用抛RuntimeError惰性类属性抛AttributeError宿主类穿回宿主作为值或作为type(x)时是只读的MontyClassTypeProxyname、id、is_dataclass、attributes而非原类把代理再传回则作为同一沙箱类型对象重新进入。JS 中未注册的类以纯{__monty_type__: Type, ...}标记返回已注册的解析为类对象。JS 构造函数没有关键字参数kwargs 以尾部 options-bag 参数到达与包装方法调用一致且该 bag 会丢弃__proto__关键字。Eager 类属性是发送时的快照发送后宿主侧变更不可见与 eager 实例属性一致。它们在类及其每个实例的每次穿越时都会被重发成本随 eager 类属性数量线性增长非空集合替换沙箱副本空集合则不动它因此重发无法清空重发还会覆盖类名与is_dataclass标志。HostClassType::update_from_wire正是这样实现name与is_dataclass以宿主为准覆盖attrs为Some时整体替换见 host_class.rs。在共享类型对象布局dump format version 8之前写入的 dump 在加载时被拒绝。沙箱用户代码中不存在的东西class Foo(Bar): ...——无继承、无 MRO、无super()解析期拒绝class inheritance and metaclasses运行时等价物type(Foo, (Bar,), {})抛TypeError见上文。元类、__init_subclass__、__set_name__及任何元类驱动的命名空间定制。__slots__、描述符__get__/__set__/__delete__。抽象基类abc.ABC、abstractmethod。方法装饰器——classmethod、staticmethod、property以及类体内def上的任何装饰器解析期拒绝。类装饰器和非方法函数上的装饰器受支持。类几乎不可内省__dict__、__bases__和dir()都不可用cls.__name__与cls.__annotations__可用后者值是字符串化的见 typing.md。因此类装饰器只能发现字段别无其他。装饰器应用产生的 traceback 指向整个class语句从第一个装饰器到类体的跨度类体省略为...N lines...CPython 则钉住抛出异常的单个装饰器。栈中每个装饰器都报告同一位置只有被调用帧能指出是哪个抛的。__init__、__repr__、__str__、__enter__、__exit__、__iter__、__next__、__contains__、__eq__、__hash__、__index__之外的 dunder 协议不对用户定义实例派发__new__、__call__、__getitem__、__setitem__、__add__、__ne__、__bool__等都不派发。__ne__恒为__eq__的取反与 CPython 默认派生一致自定义__ne__被忽略。__index__在索引场景派发但在算术运算符上不派发。定义它的类可以用于下标读取seq[obj]、作为切片界seq[obj:]、slice(obj)以及整数参数位置range(obj)、x.center(obj)、s.find(sub, obj)。序列重复不会咨询它所以ab * obj与[0] * obj抛TypeError: unsupported operand type(s) for *CPython 会重复——每个数值运算符自带强转不经过共享的 index 路径。下标赋值不派发__index__。lst[obj] x抛TypeError: list indices must be integers or slices, not FooCPython 会强转并赋值只有读取侧走 index 路径。slice()存储的是强转后的界而不是传入的对象。CPython 的slice()原样保留参数只在切片使用时才调用__index__因此slice(obj).start是objMonty 在构造时强转因此是结果int。__index__抛异常的界于是会在slice(...)处就抛而不是在使用时既非None、非int、也非可__index__的界会 upfront 被拒绝而非首次使用时。切片界饱和存储为i64。由于界在构造时强转见上超过i64的会被钳制到i64::MIN/i64::MAX而非保持精确slice(10**30).stop是9223372036854775807CPython 报告10**30。用这样的界切片仍与 CPython 一致——反正都会钳制到序列长度[1, 2, 3][10**30:]是[]——分歧只在读回属性时可见。这对字面量界与__index__返回的界同样适用。普通索引不受影响[1, 2, 3][10**30]与 CPython 一样抛IndexError。__iter__/__next__/__contains__会派发但与__repr__/__str__一样同步运行调用外部或 OS 函数者无法挂起并抛NotImplementedError。错误在 callee 内出错的调用处抛出因此那里的try/except能像捕获其他异常一样捕获它。还有两个相关协议不派发依赖它们的类不可迭代遗留的仅__getitem__回退CPython 从索引 0 迭代到IndexError的方式遍历定义了__getitem__但无__iter__的类Monty 则报告不可迭代。monty -t对这类类接受iter(obj)所以只在运行时失败见 iter.md。__reversed__对任何用户实例reversed(obj)抛TypeError: {cls} object is not reversible。对既无__reversed__也无__len____getitem__的类这与 CPython 一致对有它们的类则分歧。__next__只在类上查找绝不在实例__dict__上且在其内部任意位置抛出的StopIteration都会结束迭代包括从嵌套调用中传播出来的——CPython 的 PEP 479 保护只适用于生成器而 Monty 没有生成器。返回用户实例的__contains__恒为True。结果由 Monty 的真值判定强转而它把每个实例都报告为真值见上CPython 的PyObject_IsTrue会咨询返回对象的__bool__/__len__。其他返回类型与 CPython 强转一致。属性访问钩子从不派发__getattr__、__getattribute__、__setattr__、__delattr__、__del__。即使类定义了__getattr__缺失属性也总是抛默认AttributeError属性写入总是直达实例__dict__。object.__setattr__存在见下文且由于没有钩子可跳过它与普通obj.x v只在dataclass(frozenTrue)实例上有别前者能写入、后者拒绝——这正是 CPython 生成的__init__所用的逃生口。在类对象上它完全不写入而Foo.x v设置类成员。源码层面Instance::set_attr是普通写入的入口set_attr_unchecked直接写__dict__且是object.__setattr__与dataclass合成__init__调用的底层见 instance.rs。__name__、__doc__、__annotations__与obj.__class__之外的内省属性Foo.__dict__、obj.__dict__、Foo.__bases__、Foo.__mro__、Foo.__qualname__、Foo.__module__以及类未定义时显式调用obj.__repr__()/obj.__str__()全部抛AttributeError。类体中def、简单name [: T] expr变量赋值、pass、...、docstring 之外的语句如类体内的if/for/while或元组/多重赋值目标解析期拒绝。在类体作用域绑定的赋值表达式:类变量值、方法参数默认值、lambda 参数默认值中解析期拒绝。CPython 中海象目标会成为类成员class C: x (y : 5)给出C.yMonty 的类命名空间组装只记录直接赋名的名字因此该语法被保留而非静默丢弃绑定。lambda体内的海象f lambda: (z : 1)在 lambda 自身作用域绑定正常工作。类体内推导式中的海象也被拒绝CPython 也拒绝但措辞不同的SyntaxError。注解中的海象x: (y : int) 5在 Monty 中运行——注解表达式按源码文本捕获字符串化且从不求值所以海象从不绑定CPython 抛SyntaxError。这源于注解从不求值的特性一旦将来求值就会改变见 typing.md。del obj.attrdel语句整体不支持。objectobject名字可以解析但它是object.__setattr__的载体而非类型Monty 没有继承因此没有基类可做。isinstance(x, object)对每个值都是True与 CPython 一致。object()不能构造——抛TypeError: cannot create object instancesCPython 返回无特性实例。class Foo(object):仍被拒绝与任何 base 列表一样见上所以这个惯用法并不比class Foo:更有分量。只有__setattr__与__name__可解析。CPython 的object承载的其他成员——__doc__、__init__、__eq__、__getattribute__、__class__、__mro__、__bases__、__qualname__、__module__、__dict__——都抛AttributeErrorMonty 用泛化的type object has no attribute xCPython 说type object object has no attribute x。object.__setattr__只接受沙箱定义类的实例。其他任何东西都抛 CPython 的AttributeError: type object has no attribute name and no __dict__ for setting new attributes——包括类对象CPython 对此抛TypeError: cant apply this __setattr__ to type object。它 repr 为built-in function object.__setattr__CPython 说slot wrapper __setattr__ of object objects。FrozenInstanceError对沙箱内以dataclass(frozenTrue)声明的 dataclass 字段赋值时会抛出见 dataclasses.md宿主提供的实例在沙箱中从不 frozen见上文「宿主类实例」。它是AttributeError的子类因此except AttributeError:能捕获它与 CPythondataclasses模块一致。普通class从不 frozenobject.__setattr__无论如何都能绕过检查写入。该检查在Instance::set_attr中通过dataclasses::frozen_assignment_error实现在每次属性写入前确认类是否 frozen见 instance.rs。实践要点小结沙箱内写类用平铺的class Foo:定义配__init__、__repr__、__eq__即可获得与 CPython 高度一致的核心体验避开继承、元类、描述符、方法装饰器与属性访问钩子这些未实现面。向沙箱送对象务必显式用ClassInstance/ClassType包装并利用eager_attrs快照与lazy_attrs按需供给的取舍高频字段走 eager需实时性的走 lazy注意每次访问都是一次宿主往返。把沙箱结果拿回宿主识别只读的MontyClassProxy/MontyClassTypeProxy明白其上无法调用沙箱方法反向传回时依靠 uuid 解析恢复原始对象或宿主备份副本。dump/restore 与 id沙箱类/实例的 uuid 在堆中随 dump 保存宿主类的 id 在全新进程中需要显式钉住ClassType(..., id...)才能正确恢复。相关文档延伸阅读dataclasses.md、typing.md、with.md、iter.md、resource_limits.md、pool-architecture.md核心实现见 class.rs、instance.rs、host_class.rs。【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价