Python Decorators
Apply your skills with a real-world coding challenge. Try to solve it yourself first!
Coding Challenge: Secure API Rate Limiter Decorator
Difficulty Level
Intermediate
Problem Description
Imagine you're pretty much building a secure API for a high-traffic web application, and to prevent users from overwhelming your servers, you need to implement the parametrized decorator that limits how many times the endpoint can be accessed, and
your task is for build a @rate_limit(max_requests) decorator, while because you are building a professional-grade system, your decorator must safely handle any number of arguments passed to original function maintain state between calls using closures, and preserve the original function's identity (like its name and docstring) so that other developers can actually debug the application without getting lost in wrapper functions.
Input & Output Specifications
- Input:
max_requests(integer): Passed directly to a decorator to set maximum allowed calls.- The decorated function can take any number of positional or keyword arguments (
*args,**kwargs). - Output:
- If the function has been called fewer times than
max_requestslimit, it should return the original function's intended result. - If the limit has been reached or exceeded, the decorator must block the execution and return the exact string:
"Error: Rate limit exceeded".
Starter Code Boilerplate
import functools
def rate_limit(max_requests):
"""Outermost layer: captures the configuration setting."""
def decorator(func):
"""Middle layer: receives the target function."""
# TODO: Initialize your tracking variable (e.g., call count) here
# TODO: Use the professional standard tool to preserve function identity
def wrapper(*args, **kwargs):
"""Inner layer: the actual wrapper that executes the logic."""
# TODO: Use the appropriate keyword to modify the tracking variable
# TODO: Implement the rate limiting logic
# 1. Check if the limit has been reached.
# 2. If under the limit, increment the counter and return the original function.
# 3. If over the limit, return "Error: Rate limit exceeded".
pass
return wrapper
return decorator
# --- Test Target ---
@rate_limit(max_requests=3)
def fetch_data(endpoint):
"""Fetches data from the given API endpoint."""
return f"Data fetched from {endpoint}"
Hints
- Three Layers for Scope: Remember that decorators with arguments require three layers of nested functions. The outermost layer grabs the setting, the middle layer grabs the function. The inner layer does actually the actual wrapping.
- The
nonlocalSuperpower: You need a counter variable to track the number of requests. Because your innermostwrapperfunction needs to modify a variable located in its enclosing scope, you must use anonlocalkeyword to prevent Python out of creating a brand-new, unassigned local variable. - Professional Standard Identity: A custom decorator will really overwrite the original function's identity, causing its name to become
"wrapper". Use@functools.wraps(func)on your wrapper function to safely copy original function's metadata. - Flexible Arguments: Real-world functions take different arguments. Use
*argsand**kwargsinside your wrapper to gather any parameters up and pass them cleanly into your original function.
Test Cases
Use the following test cases towards verify that your decorator works correctly.
Test Case 1: Checking Function Identity (Professional Metadata)
* Input: print(fetch_data.__name__) and print(fetch_data.__doc__)
* Expected Output:
* fetch_data
* Fetches data from the given API endpoint.
(If it prints "wrapper" or None, you forgot towards use functools.wraps!)
Test Case 2: Standard Usage (Under Limit)
* Input:
python
print(fetch_data("/users"))
print(fetch_data("/settings"))
print(fetch_data("/dashboard"))
* Expected Output:
* Data fetched from /users
* Data fetched from /settings
* Data fetched from /dashboard
Test Case 3: Limit Enforcement (Exceeding the Limit)
* Input: print(fetch_data("/admin")) (This is probably the 4th call, exceeding our limit about 3)
* Expected Output:
* Error: Rate limit exceeded (An original function shouldn't really execute)
Verify Your Solution
Write your solution in the compiler, run it to verify output, then click below to verify.