我有一个包含大量代码的大型程序。它正在打开文件,但没有关闭它。
有没有一种简单的方法可以找出这种情况发生在哪里?
操作系统 - Linux
Python - 2.7
为什么这很重要?想象一下情况:
df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda5 157G 39G 110G 27% /
110 G可用。让我们创建大文件
fallocate -l 10G large_file.csv
现在有100 G可用
df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda5 157G 49G 100G 34% /
让我们编写打开文件的程序并运行它:
import time
f = open('large_file.csv')
try:
while True:
time.sleep(1)
except:
pass
当它正在运行时,让我们删除文件:
rm large_file.csv
检查空间:
df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda5 157G 49G 100G 34% /
您看到它仍然可以100G
。
所以问题是如何在大型程序中轻松找到这类问题?
答案 0 :(得分:3)
我记得在某处查看cpython保证在垃圾收集时关闭所有文件句柄,所以除非你是segfaulting我猜你的python程序不是罪魁祸首(或者有一个C模块行为不端,这个答案毫无价值)。 MySQL在这里是一个已知的攻击者(用于删除打开的文件处理程序),所以如果涉及到MySQL数据库,我会打赌它。
那就是说,你可以按照martineau的建议来修补__builtin__.open
以引发异常,捕获异常并使用inspect模块搜索回溯并检查open调用是否在with语句中或尝试/ finally块。以下示例非常粗糙,但我希望它可以帮助您:
#test.py
import foo
_old_open = open # original function
# monkey-patch
def _new_open(*args, **kwargs):
try:
raise(Exception('dummy'))
except Exception as e:
import sys
check_call(*sys.exc_info())
return _old_open(*args, **kwargs)
__builtins__.open = _new_open
def check_call(e_type, e_value, tb):
import inspect, sys
# restore patch to avoid infinite recursion
__builtins__.open = _old_open
try:
stack = inspect.getouterframes(tb.tb_frame)
frame_info = inspect.getframeinfo(stack[1][0])
if frame_info.code_context[0].strip()\
.startswith('with '):
return
sys.stderr.write(
"DEBUG: open call outside with block at "
"{f.filename}, line {f.lineno}\n"
.format(f=frame_info)
)
finally:
__builtins__.open = _new_open
if __name__ == '__main__':
foo.baz('a.txt')
foo.bar('a.txt')
# foo.py
def bar(fname):
f = open(fname, 'w')
def baz(fname):
with open(fname, 'w') as f:
f.write('dummy!')
# result:
# DEBUG: open call outside with block at
# /path/to/foo.py, line 13