简体   繁体   中英

How should I catch exceptions raised by `with open(filename)` in Python?

The act of attempting to open a file in Python can throw an exception. If I'm opening the file using the with statement, can I catch exceptions thrown by the open call and the related __enter__ call without catching exceptions raised by the code within the with block?

try:
    with open("some_file.txt") as infile:
        print("Pretend I have a bunch of lines here and don't want the `except` below to apply to them")
        # ...a bunch more lines of code here...
except IOError as ioe:
    print("Error opening the file:", ioe)
    # ...do something smart here...

This question is different from this older one in that the older one is about writing a context manager, rather than using the familiar with open .

can I catch exceptions thrown by the open call and the related __enter__ call without catching exceptions raised by the code within the with block?

Yes:

#!/usr/bin/env python3
import contextlib

stack = contextlib.ExitStack()
try:
    file = stack.enter_context(open('filename'))
except OSError as e:
    print('open() or file.__enter__() failed', e)
else:
    with stack:
        print('put your with-block here')

with the default open() function, __enter__() shouldn't raise any interesting exceptions and therefore the code could be simplified:

#!/usr/bin/env python    
try:
    file = open('filename')
except OSError as e:
    print('open() failed', e)
else:
    with file:
        print('put your with-block here')

If the error has to do with opening the file (for instance, if the file doesn't exist), it will be raised by the call to open itself, not by __enter__ . In this case you can catch it by separating the open call from the with block:

try:
    myFile = open('some_file.txt')
except IOError:
    print("Oh my snakes and garters!")

with myFile:
    # do stuff

# This will be True
print(myFile.closed)

As the question you linked to (and other related ones) suggest, you can't really separate exceptions in __enter__ from exceptions in the with block. However, for that very reason, in most cases a context manager that tries to do complicated stuff that might raise an exception in __enter__ is probably a fragile design anyway. I think file.__enter__ just returns the file (as suggested by this page ), although I can't find any documentation guaranteeing this.

In Python 3, IOError is an alias of OSError. To verify, run the code:

IOError is OSError
---
True

OSError is the parent class of the file I/O exceptions.

      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
OSError.__subclasses__()
---
[ConnectionError,
 BlockingIOError,
 ChildProcessError,
 FileExistsError,
 FileNotFoundError,
 IsADirectoryError,
 NotADirectoryError,
 InterruptedError,
 PermissionError,
 ProcessLookupError,
 TimeoutError,
 io.UnsupportedOperation,
 signal.ItimerError,
 socket.herror,
 socket.gaierror,
 socket.timeout,
 ssl.SSLError,
 shutil.Error,
 shutil.SpecialFileError,
 shutil.ExecError,
 shutil.ReadError,
 urllib.error.URLError,
 gzip.BadGzipFile]

Hence, catch the OSError and check the exact class if detail is requied.

try:
    with open('hoge') as f:
        pass
except OSError as e:
    print(f"{type(e)}: {e}")
---
<class 'FileNotFoundError'>: [Errno 2] No such file or directory: 'hoge'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM