如何测试由保存的异常引起的引用循环



我说的是这个问题:https://bugs.python.org/issue36820.

小型总结:

保存异常会导致循环引用,因为异常的数据包括一个回溯,该回溯包含保存异常的变量所在的堆栈帧。

try:
1/0
except Exception as e:
ee = e

代码没有被破坏,因为Python最终会用垃圾收集器释放内存。但整个问题是可以避免的:

try:
1/0
except Exception as e:
ee = e
...
...
finally:
ee = None

在链接的bpo-36820中,有一个弱参考保持有效的演示。

我的问题是,是否存在一个不需要编辑函数本身的测试。类似的东西

  • 运行测试的函数
  • 检查是否创建了新周期

gc模块能做到这一点吗?

是的,使用gc模块,我们可以检查是否存在仅由回溯帧引用的(新(异常。

在实践中,迭代gc对象会创建一个额外的referrer(不能使用WeakSet,因为内置异常不支持weakref(,所以我们检查是否有两个referrer——框架和额外的refrer。

def get_exception_ids_with_reference_cycle(exclude_ids=None):
import gc
import types
exclude_ids = () if exclude_ids is None else exclude_ids
exceptions = [
o for o in gc.get_objects(generation=0)
if isinstance(o, Exception) and id(o) not in exclude_ids
]
exception_ids = [
id(e) for e in exceptions
if len(gc.get_referrers(e)) == 2 and all(
isinstance(r, types.FrameType) or r is exceptions
for r in gc.get_referrers(e)
)
]
return exception_ids

用法:

exception_ids = get_exception_ids_with_reference_cycle()
x()
print(bool(get_exception_ids_with_reference_cycle(exclude_ids=exception_ids)))

替代用途:

@contextlib.contextmanager
def make_helper():
exception_ids = get_exception_ids_with_reference_cycle()
yield lambda: bool(get_exception_ids_with_reference_cycle(exclude_ids=exception_ids))

with make_helper() as get_true_if_reference_cycle_was_created:
x()
print(get_true_if_reference_cycle_was_created())

我相信您可以使用gc模块来完成这样的操作。

import gc
# First, enable garbage collection
gc.enable()
# Save an exception to a variable
exception = Exception('test exception')
# Check for objects that are no longer being referenced by the program
if gc.garbage:
# Print the objects that are causing the cycle
print(gc.garbage)
# Use the gc.get_referrers method to find out what objects
# are causing the cycle
for obj in gc.garbage:
print(gc.get_referrers(obj))
# Modify your code to break the cycle
# (This will depend on your specific code and the objects
# involved in the cycle)

最新更新