Exception Hierarchy

In Python, all exceptions are derived from the BaseException class. This means that every exception in Python is an instance of BaseException or one of its subclasses.
The BaseException class is the top-level class in the exception hierarchy. It provides some common methods that all exceptions can use, such as __str__ and __repr__. However, you should not use BaseException directly in your code as it is too broad and can catch any type of exception. Instead, you should use more specific exception classes that are subclasses of BaseException. Python provides a number of built-in exception classes that you can use, and you can also create your own custom exception classes:
BaseException
 ├── BaseExceptionGroup
 ├── GeneratorExit
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception
      ├── ArithmeticError
      │    ├── FloatingPointError
      │    ├── OverflowError
      │    └── ZeroDivisionError
      ├── AssertionError
      ├── AttributeError
      ├── BufferError
      ├── EOFError
      ├── ExceptionGroup [BaseExceptionGroup]
      ├── ImportError
      │    └── ModuleNotFoundError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      ├── MemoryError
      ├── NameError
      │    └── UnboundLocalError
      ├── OSError
      │    ├── BlockingIOError
      │    ├── ChildProcessError
      │    ├── ConnectionError
      │    │    ├── BrokenPipeError
      │    │    ├── ConnectionAbortedError
      │    │    ├── ConnectionRefusedError
      │    │    └── ConnectionResetError
      │    ├── FileExistsError
      │    ├── FileNotFoundError
      │    ├── InterruptedError
      │    ├── IsADirectoryError
      │    ├── NotADirectoryError
      │    ├── PermissionError
      │    ├── ProcessLookupError
      │    └── TimeoutError
      ├── ReferenceError
      ├── RuntimeError
      │    ├── NotImplementedError
      │    └── RecursionError
      ├── StopAsyncIteration
      ├── StopIteration
      ├── SyntaxError
      │    └── IndentationError
      │         └── TabError
      ├── SystemError
      ├── TypeError
      ├── ValueError
      │    └── UnicodeError
      │         ├── UnicodeDecodeError
      │         ├── UnicodeEncodeError
      │         └── UnicodeTranslateError
      └── Warning
           ├── BytesWarning
           ├── DeprecationWarning
           ├── EncodingWarning
           ├── FutureWarning
           ├── ImportWarning
           ├── PendingDeprecationWarning
           ├── ResourceWarning
           ├── RuntimeWarning
           ├── SyntaxWarning
           ├── UnicodeWarning
           └── UserWarning
The hierarchy of built-in exception classes in Python (from the official Python docs).
This means that the FileNotFoundError is a subclass of an OSError, which is a subclass of the Exception class, which itself inherits from the BaseException.

Respecting the exception hierarchy

When using multiple except statements, it's important to respect the exception hierarchy in Python. This means that if you catch a base exception class, such as Exception, you should catch more specific exceptions before it. If you write an except statement of a more specific class after the base class, the block won’t be executed:
try:
    # some code that may raise an exception
except ValueError:
    # handle the ValueError
except Exception:
    # handle any other exception
In this example, the ValueError exception is caught first. If the code raises any other exception that is derived from Exception, the code in the second except block will execute. However, it is not recommended to catch Exception directly, as it is too broad and can catch any type of exception. It's better to catch more specific exceptions whenever possible:
try:
    ...
except TypeError:
    ...
except ValueError:
    ...
except Exception:
    ...
If the code in the try block raises a TypeError or a ValueError, the corresponding except block will execute. If the code raises any other exception that is derived from Exception, the code in the third except block will execute.
If we place the base exception as the first except block, it will prevent the rest from being ever executed:
try:
    # some code that may raise an exception
except Exception as e:               # This will catch all the errors
    print(f'An error occurred: {e}')
except ValueError:                   # This will never be executed
    print('A ValueError occurred')
except TypeError:                    # This will never be executed
    print('A TypeError occurred')
In this example, the try block may raise any type of exception. However, the first except statement catches the base Exception class, so it will catch any exception that is raised. The error message will be printed and the rest of the except statements will not be executed.

Challenge: Parse the File

You are working as a software engineer at a startup that deals with large amounts of data. Recently, the team decided to use JSON files to store and process the data. This data is vital to the operation of your startup, and it's your task to write a Python program that reads and parses these JSON files.
However, due to the large amount of data and multiple people working on it, some errors often occur:
  1. Sometimes the data file doesn't exist.
  1. Sometimes the data file is empty.
  1. Sometimes the data file contains data that is not valid JSON.
You need to ensure that your Python program can handle these exceptions without crashing and provide meaningful error messages so that the data team can quickly identify and fix the problem.
Your program should take as input a string that represents the filename and handle the following cases:
  • If the file doesn't exist, it should raise a FileNotFoundError and print an appropriate error message.
  • If the file is empty, it should raise a ValueError and print an appropriate error message.
  • If the file content is not valid JSON, it should raise a json.JSONDecodeError (which is a subclass of ValueError) and print an appropriate error message. When raising this exception, you should provide the doc and pos attributes as parameters.
💡
except json.JSONDecodeError as e: raise json.JSONDecodeError("some message", e.doc, e.pos)
Each of these exceptions should be handled independently of each other, and your program should provide a specific error message for each case.
Input
Output
nonexistent_file.json
FileNotFoundError: File 'nonexistent_file.json' does not exist.
empty_file.json
ValueError: File 'empty_file.json' is empty.
invalid_json.json
JSONDecodeError: Content of file 'invalid_json.json' is not valid JSON.: line 1 column 1 (char 0)
success.json
File 'success.json' has been successfully parsed as valid JSON.
 

Constraints

Time limit: 2 seconds

Memory limit: 512 MB

Output limit: 1 MB

To check your solution you need to sign in
Sign in to continue