本記事で扱っている内容はNamedTemporaryFileのバグです。
概要
NamedTemporaryFileでDLしたファイルをS3へアップロードした後、再度参照しようとした際にclosed file関連でエラーが出たので、その時の対処法を記載する。
困った事
以下の手順で開発を進めていたが、手順4の箇所で以下のエラーに遭遇し解決策を模索していた。
ValueError: I/O operation on closed file
ValueError: seek of closed file
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpoirwooqn.xlsm'
- S3からxlsmをDL
- 情報を転記
- S3へxlsmをUP
- xlsmを参照して情報を修正
- S3へxlsmをUP(3とは独立)
元のソース
with tempfile.NamedTemporaryFile(mode='wb+', suffix='.xlsm') as tf:
# S3からテンプレートファイルをダウンロード
s3 = S3()
s3.download_report_file('template_hoge/hoge/', 'template', tf)
# テンプレートファイルにデータを入力してファイルを作成
for file_type in ['xlsm', 'xlsx']:
if file_type == 'xlsm':
wb = openpyxl.load_workbook(tf.name, keep_vba=True)
# ファイルに情報転記(省略)
wb.save(tf.name)
# ファイルをS3へアップロード
tf.seek(0)
s3.upload_report_file('hoge/hoge/', 'hoge', tf)
elif file_type == 'xlsx':
wb = openpyxl.load_workbook(tf.name)
# ファイルに情報転記(省略)
wb.save(tf.name)
# ファイルをS3へアップロード
tf.seek(0)
s3.upload_report_file('hoge/hoge/', 'hoge', tf)
解決策
class NonCloseableBufferedReader(BufferedReader):
def close(self):
self.flush()
with tempfile.NamedTemporaryFile(mode='wb+', suffix='.xlsm') as tf:
# S3からテンプレートファイルをダウンロード
s3 = S3()
s3.download_report_file('template_hoge/hoge/', 'template', tf)
# テンプレートファイルにデータを入力してファイルを作成
for file_type in ['xlsm', 'xlsx']:
if file_type == 'xlsm':
wb = openpyxl.load_workbook(tf.name, keep_vba=True)
# ファイルに情報転記(省略)
wb.save(tf.name)
# ファイルをS3へアップロード
tf.seek(0)
buffer = NonCloseableBufferedReader(tf)
s3.upload_report_file('hoge/hoge/', 'hoge', buffer)
buffer.detach()
assert not tf.closed
elif file_type == 'xlsx':
wb = openpyxl.load_workbook(tf.name)
# ファイルに情報転記(省略)
wb.save(tf.name)
# ファイルをS3へアップロード
tf.seek(0)
s3.upload_report_file('hoge/hoge/', 'hoge', tf)
回避策の説明
近日公開