12

从上下文管理器的数据模型文档中:

请注意,__exit__()方法不应重新引发传入的异常;这是调用者的责任。


我有一个临时文件,我想释放它的文件描述符,close但不向磁盘写入任何内容。我直观的解决方案是传递异常,但在文档中不鼓励这样做——当然是有充分理由的。

class Processor(object):
    ...
    def write(self, *args, **kwargs):
        if something_bad_happens:
            raise RuntimeError('This format expects %s columns: %s, got %s.' % (
                               (len(self.cols), self.cols, len(args))))
        self.writer.writerow(args)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        # the RuntimeError from write will be set as type, value and so on ..
        # I'd like to close the stream here (release the file descriptor), 
        # but I do not leave a trace of the associated file - 
        # (one can always 'manually' delete with `os.remove` but maybe there's a 
        # better way ..?)
        self.output_pipe.close()

此外,在这种特殊情况下,我不希望在调用者中进行错误处理,原因有两个:

  • 保持调用者中的代码最少(见下文)
  • 调用者对异常感到满意(快速失败是我们想要的)

上下文管理器的使用如下:

class Worker(object):
    ...
    def run(self):
        # output setup so it will emit a three column CSV
        with self.output().open('w') as output:
            output.write('John', 'CA', 92101)
            output.write('Jane', 'NY', 10304)
            # should yield an error, since only three 'columns' are allowed 
            output.write('Hello', 'world')

更新:我的问题有点表述不当,因为我的问题实际上归结为:在嵌套的上下文管理器中,如何将异常传递给最外层的 CM?

4

2 回答 2

16
  • __exit__返回 True 时,任何传递给它的异常都会被吞没。
  • __exit__返回 False 时,将重新引发异常。

def __exit__(self, type, value, traceback):
    self.output_pipe.close()  # always close the file
    if type is not None: # an exception has occurred
        os.unlink(...)   # remove the file
        return False     # reraise the exception

您当然可以省略,return False因为 Python 将None默认返回(即 Falsish)。


顺便说一句,self.output()Processor? 如果是这样,

with self.output().open('w') as output:

应该

with self.output() as output:

无论如何,如果您可以安排后者是正确的语法,那就更好了。您可能需要更改__enter__为:

def __enter__(self):
    return self.output_pipe.open('w')
于 2013-08-23T09:12:23.110 回答
3

没有必要例外raise已经提出了异常,并且您的上下文管理器只是被告知了这一点。

测试是否没有异常:

if type is None:
    # if no exception is raised, proceed as usual:
    self.output_pipe.close()

如果您的上下文管理器在那一刻返回True,您将抑制异常;只是退出函数而不是返回None,异常仍然“引发”。

请注意,该tempfile模块包括两种类型的临时文件对象,它们充当上下文管理器,它们已经自我删除,独立于平台。在 POSIX 系统上,您可以在创建文件后立即取消链接;文件描述符保持活动状态,直到您关闭文件。Windows 也提供了“关闭时删除”选项。该tempfile.NamedTemporaryFile()课程为您的平台使用正确的选项。

于 2013-08-23T08:53:34.403 回答