Background:
The exception system in Python was originally built on classes. The entire exception hierarchy inherits from the base class BaseException. In practice, custom exceptions are created from Exception. Developing custom exceptions is important for large projects — it allows for more precise error handling and signaling of application-specific errors.
Problem:
Not all developers understand the differences between BaseException and Exception, some are lazy to create their own classes, and they use generic catch blocks, which leads to the absorption of unwanted exceptions (like SystemExit, KeyboardInterrupt) and hard-to-reach bugs. Often, exception classes are implemented incorrectly — they do not have meaningful names and do not inherit from the right class.
Solution:
Write your exceptions as descendants of Exception. Use meaningful names to avoid catching basic errors indiscriminately. Do not inherit from BaseException, but only from Exception or its derivatives, and do not catch everything through except: without specifying a particular class.
Code example:
class MyAppError(Exception): """Base class for application exceptions""" pass class ConfigFileNotFound(MyAppError): pass try: raise ConfigFileNotFound('Configuration file not found!') except ConfigFileNotFound as e: print(f'Error: {e}')
Key features:
What is dangerous about catching all exceptions using "except:" without specifying a type?
Such a block catches even system exceptions — KeyboardInterrupt, SystemExit, making it impossible to terminate the program properly with Ctrl+C and leading to "hanging" in critical situations. You should write "except Exception:" to skip base system events.
Is it acceptable to inherit your exception from BaseException?
Technically yes, but it's strongly discouraged — such exceptions are hard to detect, they bypass standard Exception handlers, which often leads to uncaught errors in the application.
Is it correct to use ValueError or TypeError instead of custom exceptions?
In small scripts, it's acceptable, but in large projects, it's better to create your own semantically meaningful exceptions. This speeds up diagnostics and error handling at higher levels of the application.
# Incorrect: raise ValueError('Something specific for the application') # Good: class MyAppValueError(MyAppError): pass raise MyAppValueError('Error description with application context')
In a large project, there was a global try/except: block that caught system exceptions. A number of errors (like SystemExit) were not logged, causing the application to enter unexpected states, and administrators spent a long time searching for symptoms.
Pros:
Cons:
Custom error classes were defined, only "except Exception: ..." was used everywhere with separate handlers for custom exceptions.
Pros:
Cons: