Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
149: Remote Debugging Basics
There comes a point in every project where print() statements just aren't enough. You've got a bug that only happens on your staging server or inside a Docker container, and trying to replicate the exact environment locally is a nightmare. This is where remote debugging comes in. Instead of guessing what's happening, we're going to let our local IDE "reach into" the running process on the remote machine.
For this, we'll use debugpy. It's the engine that powers the Python debugger in VS Code and is basically the industry standard for this workflow.
Setting up a buggy weather aggregator
Let's imagine we have a small script that aggregates weather data from a remote API. I've written this to run on a remote server (or a container), but it's throwing a KeyError that I can't quite pin down because the input data varies.
# weather_service.py
import requests
def process_weather(city):
# Imagine this is a real API call
data = requests.get(f"https://api.example.com/weather/{city}").json()
# This is where the crash happens
temp = data['main']['temp']
return f"The temperature in {city} is {temp} degrees."
if __name__ == "__main__":
cities = ["London", "New York", "UnknownCity"]
for city in cities:
print(process_weather(city))
In a real scenario, UnknownCity might return a different JSON structure (like an error message) that doesn't have a 'main' key. Since this is running on a remote box, I don't want to keep restarting the server and adding prints. I want to pause the code execution right there and inspect the data variable.
Injecting the debug listener
To make the script "debuggable" from the outside, we need to tell Python to open a port and wait for a debugger to attach to it. I'll add debugpy to the start of the script.
import debugpy
import requests
# Listen for a connection on port 5678
debugpy.listen(("0.0.0.0", 5678))
print("Waiting for debugger to attach...")
debugpy.wait_for_client() # The script will freeze here until we connect
print("Debugger attached! Continuing execution...")
def process_weather(city):
# ... (rest of the code)
I used 0.0.0.0 because that tells the listener to accept connections from any network interface, which is necessary when the debugger is on your laptop and the code is on a server.
The "Where is my breakpoint?" moment
Now, here is where I usually trip up the first time I do this. I ran the script on my remote server, saw the "Waiting for debugger..." message, and then I tried to connect via my IDE. But... nothing happened. The breakpoints weren't hitting.
I spent ten minutes checking my code before I realized the obvious: I forgot that the remote server has a firewall. Port 5678 was closed. If you're using AWS or a similar cloud provider, you have to explicitly open that TCP port in your security groups. Once I opened the port, the IDE connected instantly, and the script moved past the wait_for_client() call.
Hunting the KeyError in real-time
With the debugger attached, I set a breakpoint on the line temp = data['main']['temp']. When the loop hit UnknownCity, the execution paused. I looked at the data variable in my IDE's watch window and saw this:
{
"cod": 404,
"message": "city not found"
}
There's the culprit. There is no 'main' key because the API returned an error response. I can now fix this by adding a check for the cod key or using data.get('main'). I didn't have to restart the server or guess the payload; I saw the actual live state of the remote memory.
📋 Practical Task
Fixing the Remote API Timeout Bug
You have a script running on a remote server that processes a list of user IDs. It's occasionally crashing with a TypeError when a user profile is incomplete. Your goal is to set up remote debugging to find the offending data.
Your Task:
- Modify the provided script below to include a
debugpylistener on port5678. - Ensure the script pauses execution and waits for the debugger to attach before it enters the
forloop. - Identify which
user_idcauses the crash by inspecting theprofilevariable during the remote session.
# user_processor.py
import time
# TODO: Add debugpy setup here
def fetch_profile(user_id):
# Simulated database of users
db = {
1: {"name": "Alice", "email": "alice@example.com"},
2: {"name": "Bob"}, # Missing email!
3: {"name": "Charlie", "email": "charlie@example.com"},
}
return db.get(user_id)
def send_welcome_email(user_id):
profile = fetch_profile(user_id)
# This line is causing the TypeError
print(f"Sending email to {profile['email'].lower()}...")
if __name__ == "__main__":
users = [1, 2, 3]
for u in users:
send_welcome_email(u)
time.sleep(1)
There are no comments for now.