StringIO и совместимость с оператором 'with' (менеджер контекста)

У меня есть унаследованный код с унаследованной функцией, которая принимает имя файла в качестве аргумента и обрабатывает содержимое файла. Ниже приведен рабочий факсимильный код.

То, что я хочу сделать, - это не писать на диск с некоторым контентом, который я генерирую, чтобы использовать эту устаревшую функцию, поэтому я мог бы использовать StringIO для создания объекта вместо физического имени файла. Однако это не работает, как вы можете видеть ниже.

Я думал, что StringIO - это способ пойти с этим. Может ли кто-нибудь сказать мне, есть ли способ использовать эту унаследованную функцию и передать ей что-то в аргументе, который не является файлом на диске, но может быть рассмотрен как функция устаревшей функции? Унаследованная функция имеет диспетчер контекста with, выполняющий работу с параметром filename.

Единственное, с чем я столкнулся в google, было: http://bugs.python.org/issue1286, но это меня не помогло...

код

from pprint import pprint
import StringIO

    # Legacy Function
def processFile(filename):
    with open(filename, 'r') as fh:
        return fh.readlines()

    # This works
print 'This is the output of FileOnDisk.txt'
pprint(processFile('c:/temp/FileOnDisk.txt'))
print

    # This fails
plink_data = StringIO.StringIO('StringIO data.')
print 'This is the error.'
pprint(processFile(plink_data))

Выход

Это результат в FileOnDisk.txt:

['This file is on disk.\n']

Это ошибка:

Traceback (most recent call last):
  File "C:\temp\test.py", line 20, in <module>
    pprint(processFile(plink_data))
  File "C:\temp\test.py", line 6, in processFile
    with open(filename, 'r') as fh:
TypeError: coercing to Unicode: need string or buffer, instance found

Ответ 1

A StringIO экземпляр уже открыт. С другой стороны, команда open принимает только имена файлов, чтобы вернуть открытый файл. Экземпляр StringIO не подходит в качестве имени файла.

Кроме того, вам не нужно закрывать экземпляр StringIO, поэтому нет необходимости использовать его в качестве менеджера контекста.

Если ваш старый код может принимать имя файла, то экземпляр StringIO не подходит. Используйте tempfile module для создания временного имени файла.

Вот пример использования contextmanager для обеспечения последующего очистки файла temp:

import os
import tempfile
from contextlib import contextmanager

@contextmanager
def tempinput(data):
    temp = tempfile.NamedTemporaryFile(delete=False)
    temp.write(data)
    temp.close()
    try:
        yield temp.name
    finally:
        os.unlink(temp.name)

with tempinput('Some data.\nSome more data.') as tempfilename:
    processFile(tempfilename)

Ответ 2

вы можете определить свою собственную открытую функцию

fopen = open
def open(fname,mode):
    if hasattr(fname,"readlines"): return fname
    else: return fopen(fname,mode)

однако с необходимостью вызывать __exit__ после его завершения, а StringIO не имеет метода выхода...

вы можете определить пользовательский класс для использования с этим открытым

class MyStringIO:
     def __init__(self,txt):
         self.text = txt
     def readlines(self):
          return self.text.splitlines()
     def __exit__(self):
          pass