Crafting Custom Exceptions for Enhanced Error Handling
In Python, built-in exceptions are useful, but sometimes you need to define your own specific error types to represent situations unique to your application. This challenge will guide you in creating and utilizing custom exceptions to make your code more readable, maintainable, and robust by clearly communicating the nature of errors.
Problem Description
Your task is to create a system for managing user accounts, including registration and login. To make error handling more precise, you need to define custom exceptions for common issues that might arise during these operations.
What needs to be achieved:
- Define three custom exception classes:
UserAlreadyExistsError: Raised when attempting to register a username that already exists.InvalidCredentialsError: Raised when login attempts with incorrect username or password.AccountNotFoundError: Raised when attempting to perform an operation on a non-existent account.
- Implement a simple
UserManagerclass that handles user registration and login. - Ensure that the
UserManagerraises the appropriate custom exceptions when specific error conditions are met.
Key requirements:
- Custom exceptions should inherit from Python's
Exceptionclass or a more specific built-in exception (e.g.,ValueError,LookupError) if appropriate for semantic clarity. - Each custom exception should have a descriptive error message.
- The
UserManagerclass should have at least two methods:register_user(username, password)andlogin_user(username, password). - The
login_usermethod should also consider theAccountNotFoundErrorcase if a username doesn't exist.
Expected behavior:
- When
register_useris called with a username that already exists,UserAlreadyExistsErrorshould be raised. - When
login_useris called with an incorrect username or password,InvalidCredentialsErrorshould be raised. - When
login_useris called with a username that has not been registered,AccountNotFoundErrorshould be raised.
Important edge cases to consider:
- Empty strings for username or password during registration or login. While not explicitly requiring custom exceptions for these, consider how your
UserManagermight handle them (e.g., by raising a standardValueErrororTypeError). For this challenge, focus on the specified custom exceptions.
Examples
Example 1: Successful Registration and Login
user_manager = UserManager()
user_manager.register_user("alice", "password123")
print("Alice registered successfully.")
if user_manager.login_user("alice", "password123"):
print("Alice logged in successfully.")
Output:
Alice registered successfully.
Alice logged in successfully.
Explanation: The user "alice" is registered with a password. Subsequently, a successful login is performed with the correct credentials, and no exceptions are raised.
Example 2: User Already Exists Error
user_manager = UserManager()
user_manager.register_user("bob", "securepass")
try:
user_manager.register_user("bob", "anotherpass")
except UserAlreadyExistsError as e:
print(f"Error: {e}")
Output:
Error: Username 'bob' already exists.
Explanation:
Attempting to register the username "bob" a second time triggers the UserAlreadyExistsError, and the custom error message is printed.
Example 3: Invalid Credentials and Account Not Found Errors
user_manager = UserManager()
user_manager.register_user("charlie", "charmain")
try:
user_manager.login_user("charlie", "wrongpassword")
except InvalidCredentialsError as e:
print(f"Error: {e}")
try:
user_manager.login_user("david", "anypassword")
except AccountNotFoundError as e:
print(f"Error: {e}")
Output:
Error: Invalid username or password for 'charlie'.
Error: Account for username 'david' not found.
Explanation:
The first try-except block catches an InvalidCredentialsError when "charlie" attempts to log in with an incorrect password. The second try-except block catches an AccountNotFoundError because the username "david" has not been registered.
Constraints
- The
UserManagershould store user data in memory (e.g., a dictionary). No persistent storage is required. - Usernames and passwords are case-sensitive.
- The system should handle up to 100 unique users for testing purposes.
Notes
- Consider what information each custom exception might carry. For instance,
UserAlreadyExistsErrorcould store the username that caused the conflict. - Inheriting from
ValueErrorforUserAlreadyExistsErrorandAccountNotFoundErrormight be semantically appropriate, as these often relate to invalid values or missing items. However, inheriting directly fromExceptionis also perfectly acceptable and common. - Focus on correctly defining and raising the custom exceptions. The internal implementation of how the
UserManagerstores and validates credentials is secondary to the exception handling aspect of this challenge.