FAQ: Context Managers - Handling Exceptions II

This community-built FAQ covers the “Handling Exceptions II” exercise from the lesson “Context Managers”.

Paths and Courses
This exercise can be found in the following Codecademy content:
Learn Intermediate Python3

FAQs on the exercise Handling Exceptions II

There are currently no frequently asked questions associated with this exercise – that’s where you come in! You can contribute to this section by offering your own questions, answers, or clarifications on this exercise. Ask or answer a question by clicking reply (reply) below.

If you’ve had an “aha” moment about the concepts, formatting, syntax, or anything else with this exercise, consider sharing those insights! Teaching others and answering their questions is one of the best ways to learn and stay sharp.

Join the Discussion. Help a fellow learner on their journey.

Ask or answer a question about this exercise by clicking reply (reply) below!
You can also find further discussion and get answers to your questions over in Language Help.

Agree with a comment or answer? Like (like) to up-vote the contribution!

Need broader help or resources? Head to Language Help and Tips and Resources. If you are wanting feedback or inspiration for a project, check out Projects.

Looking for motivation to keep learning? Join our wider discussions in Community

Learn more about how to use this guide.

Found a bug? Report it online, or post in Bug Reporting

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

Why is it preferable to use

if isinstance(exc_value, AttributeError):

instead of

if exc_type == AttributeError:

for exception type handeling in the __exit__() function of a context manager?

1 Like

Since you already have the type here with exc_type you could consider https://docs.python.org/3/library/functions.html#issubclass instead for similar behaviour to isinstance.

If you want to match a type or any children of that type use isinstance instead of ==. The behaviour with isinstance/issubclass is similar to the behaviour of the except clause in a try statement where catching Exception catches any children of the Exception type too (which would be almost all runtime errors).

In many/most cases isinstance is used instead of type(obj) == because it allows types to replaced by a newer type that simply inherits from the old without breaking as much code (it handles inheritance which is normally considred good design). If you already have the type issubclass also handles inheritance.

As ever only catch the things you 100% want to catch so the most specific exception relevant to your problem and isinstance or issubclass would probably be the best way to go.

2 Likes

That’s a great answer. Thanks. But as good answers often do, it leads me to a new question. Why, then include the exc_type at all? Couldn’t we get that information from type(exc_value)?

2 Likes

Hmm, I think there’s a bit of a legacy issue with early versions of python when it comes to the three part value. In Python3 a single exception instance contains basically everything you need to know: type, value and traceback. The traceback for example is now bound to an exception too under .__traceback__. There are fewer tools that require all three parts but unfortunately they are still around, which I presume is because they can’t be removed without breaking too much legacy code.

You’d have to look into the history to find exactly why. I think trackbacks were a separate entity once upon a time (not bound in any way) and I think there was something funny about exception types being strings rather than real types which meant they were separate actions but that’s not something I know much about, if you’re really curious you might have to have a web search (it might go back to very early python versions).

Edit: Think I found the relevant details anyway:
The introduction of the .__traceback__ attribute: PEP 344 -- Exception Chaining and Embedded Tracebacks | Python.org
Note that some of the proposed future changes here (namely changing .__exit__ to a single parameter function never came about).

As for why types and values separated… read also the linked blog to Guido’s description of exceptions using strings prior to Python1.5 apparently (with legacy support for quite a long time afterwards apparently): python - Why does traceback.format_exception require a traceback parameter instead of using ex.__traceback__? - Stack Overflow

1 Like

I’m confused on how exactly “return True” suppresses an error. What is True being returned to and how does it repress the error?

Thanks in advance.

Why in the last example “of working with a TypeError”

class OpenFile:
 
 def __init__(self, file, mode):
   self.file = file
   self.mode = mode
 
 def __enter__(self):
   self.opened_file = open(self.file, self.mode)
   return self.opened_file
 
 def __exit__(self, exc_type, exc_val, traceback):
 
   if isinstance(exc_value, TypeError):
      # Handle TypeError here...
      print("The exception has been handled")
      return True
 
   self.file.close()

we place self.file.close() out of IF statement? If there is an TypeError we newer close the file. exit() loses its meaning…isn’t it?

 def __exit__(self, exc_type, exc_val, traceback):
 
   if isinstance(exc_value, TypeError):
      # Handle TypeError here...
      print("The exception has been handled")
      self.file.close()
      return True
 
   self.file.close()
1 Like

You are correct. I don’t know why the provided solution doesn’t include the additional close() method outside of the exception handler. I have to imagine that they were so focused on the exception handling that they forgot. We can verify the problem by trying to read the file outside of the context manager:

with PoemFiles('poem.txt', 'r') as file2:
    print(file2.read())
    print(" \n ---- Exception data below ---- \n ")

# this shouldn't work
file2.seek(0)
print(file2.read())

Outputs:

The moon is kind, warm and deep,
But she has promises to keep,
After cake and lots of sleep.
Sweet dreams come to her cheap.

As you’ve shown in your reply, we need to make sure to close the file when an exception is not raised:

    def __exit__(self, exc_type, exc_value, traceback):
        print(exc_type, exc_value, traceback, '\n')
        if isinstance(exc_value, AttributeError):
            self.opened_poem_file.close()
            return True
        self.opened_poem_file.close()

If we then try to read the file outside our context manager we get the error we expect:

Traceback (most recent call last):
  File "script.py", line 31, in <module>
    file2.seek(0)
ValueError: I/O operation on closed file.

Good eye my friend. I’ll reach out to see if this can be updated.

Cheers and God bless!

1 Like

This lesson has some issues. In the first codebox for example why does the exit method say self.file.close() instead of self.opened_file.close()? Seems like some Codecademy programmers should go practice the module on testing.

why do you use

isinstance(exc_value, AttributeError)

instead of

isinstance(exc_type, AttributeError)

Aren’t we trying to see if the error type is an instance of an AttributeError? Why would the error value be an instance of an error type? I think I’m misunderstanding something…