#python #file
#python #файл
Вопрос:
У меня большая программа с большим объемом кода. И это открывает файл, но не закрывает его.
Вопрос:
Есть ли простой способ узнать, где это происходит?
Подробнее:
OS — 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
доступен.
Итак, вопрос в том, как легко найти проблемы такого типа в большой программе?
Комментарии:
1. Для этого и предназначена
lsof
команда.2. Просто используйте
with
инструкцию илиtry finally
для работы с файлами, чтобы вы никогда не забывали их закрывать.3. @BenjaminBannier: вы можете разорвать связь с открытым файлом; файл все еще существует до закрытия . Пропала запись каталога. Это сделано специально .
4. Возможно, вы могли бы заменить значение по умолчанию
open
в__builtin__
модуле оболочкой, которая отслеживает, где они происходят.5. @Vor: вы можете заменить значение по умолчанию
open
в__builtin__
модуле, как предложил martineau, чтобы вызвать исключение, перехватить исключение и использоватьinspect
модуль для поиска AST из трассировки, чтобы узнатьopen
, предшествует ли вызовуwith
try/finally
блок или (getouterframes
) , если нет, вы можете найти соответствующие блоки кода. Это интересная проблема, но, к сожалению, мне не хватает времени, чтобы опубликовать правильный ответ.
Ответ №1:
Я помню, как где-то читал, что cpython гарантированно закрывает все дескрипторы файлов при сборе мусора, поэтому, если вы не нарушаете сегментацию, я предполагаю, что ваша программа на python не является виновником (или неправильно работает модуль C, и этот ответ бесполезен). MySQL здесь является известным нарушителем (для удаления обработчиков открытых файлов), поэтому, если задействована база данных MySQL, я бы поставил на это.
Тем не менее, вы можете использовать monkey-patch __builtin__.open
, как предложил martineau, чтобы вызвать исключение, перехватить исключение и использовать модуль inspect для поиска трассировки и проверки, находится ли вызов open внутри оператора with или блока try / 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
Комментарии:
1. Большое вам спасибо, это очень умный подход