#๐Ÿ”’ with statement vs try except

18 messages ยท Page 1 of 1 (latest)

pliant tendonBOT
#

@earnest vine

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

late cove
#

with isn't particularly about error handling but rather about context management. And part of that context management is usually some form of init/cleanup operation which is where it has an appeal over a try/finally block. For example, I'd probably prefer Figure 2 over 1 or 3 because with handles the closing for you.

late cove
# late cove `with` isn't particularly about error handling but rather about context manageme...

!e you can also definitely use a with within a try. the cleanup procedure should still be called.

class MyContext:
  def __init__(self):
    pass

  def __enter__(self):
    return self

  def __exit__(self, *exc_args):
    print("exit was called with args: ", exc_args)

class SomeException(Exception): ...

try:
  with MyContext():
    raise SomeException("uh oh")
except SomeException as exc:
  print("an exception happened", exc)
pliant tendonBOT
late cove
#

The __enter__ and __exit__ in there is how you define a context manager. You'll also notice that __exit__ (the "cleanup" part of the with) also receives exception details in case any occurred.

#

yeah

#

If an exception occurs within the with statement, it would call the object's __exit__ method with the exception details. If the context manager doesn't "suppress" the exception by returning True there, it would effectively reraise it and in this case would be picked up by the try/except.

#

!e

class MyContext:
  def __enter__(self):
    return self

  def __exit__(self, *exc_args):
    print("exit was called with args: ", exc_args)
    return True

class SomeException(Exception): ...

try:
  with MyContext() as ctx:
    # do something with ctx
    raise SomeException("uh oh")
except SomeException as exc:
  print("an exception happened", exc)
pliant tendonBOT
late cove
#

In this case, the except part doesn't get called since the __exit__ suppressed the exception.

#

Yeah, the exception would be caught by the with first, then if that doesn't suppress it, it would be caught by the try/except instead.

still patio
#

exactly that, and if you want to handle different exceptions in different ways you can just have them one after another like:

try:
    with open('example.txt') as file:
        content = file.read()
except FileNotFoundError as e:
    print('error opening file:', e)
except IOError as e:
    print('error reading file:', e)
print('file content is:', content)
```but for simple operations like this where you want to read the whole file in one go instead of processing it line by line or using the file handle for something special like passing it to something else, i would instead look into using `from pathlib import Path` for most file system operations that it can handle, such as `Path.read_text()` (which is applicable in your example) and its siblings `Path.write_text()`, `Path.read_bytes()` and `Path.write_bytes()`
#

@earnest vine what is also very nice with the using a context manager (with) it will clean up the resource (such as closing a file handle or a network socket) regardless if you get an exception or you return out of a function or method (in the case of classes)

#

it works almost like an if elif statement, so always handle the most specific exceptions first so that it's not cause by a less specific exception before it can reach another more specific one further down

#

one final little nugget, if you want to ignore one or more exceptions and don't want to handle any other exceptions with the same try block, instead of doing

try:
    with open('example.txt') as file:
        content = file.read()
except (FileNotFoundError, IOError):
    pass
```you can instead do
```py
from contextlib import suppress

with suppress(FileNotFoundError, IOError):
    with open('example.txt') as file:
        content = file.read()
```or with `pathlib` it would look like:
```py
from contextlib import suppress
from pathlib import Path
๏ปฟ
with suppress(FileNotFoundError, IOError):
    content = Path('example.txt').read_text()
```even if both of these are a bit contrived as you probably don't want to ignore an exception in this case, but in many other cases it can come in handy
pliant tendonBOT
#
Python help channel closed using Discord native close action

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.