-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdebug_ncurses_examples.py
More file actions
289 lines (235 loc) · 9.63 KB
/
debug_ncurses_examples.py
File metadata and controls
289 lines (235 loc) · 9.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#!/usr/bin/env python3
"""
Example: How to debug main_ncurses.py with PyCharm
This file demonstrates best practices for debugging ncurses applications
in PyCharm using main_pexpect.py
"""
import logging
# ============================================================================
# SETUP: Configure logging for debugging
# ============================================================================
# Create logger for debugging (works alongside PyCharm debugger)
logging.basicConfig(
filename='ncurses_debug.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('ncurses_debug')
# ============================================================================
# DEBUGGING TECHNIQUES
# ============================================================================
# Technique 1: Breakpoints in Business Logic (✅ GOOD)
# ----------------------------------------------------------------------------
def handle_user_input_good_example(self, key_code: int):
"""
Example of where to place breakpoints - in business logic
To debug in PyCharm:
1. Set breakpoint on line below (click in margin)
2. Run: main_pexpect.py ncurses (Debug mode)
3. When breakpoint hits, inspect variables
"""
# ← BREAKPOINT HERE ✅
logger.debug(f"Received key: {key_code}")
if key_code == ord('\n'): # Enter key
# ← BREAKPOINT HERE ✅ (inspect self.cursor, self.mode, etc.)
selected_option = self.get_selected_option()
logger.debug(f"Selected: {selected_option}")
# ← BREAKPOINT HERE ✅ (verify selection before processing)
self.process_selection(selected_option)
# Technique 2: DO NOT put breakpoints in draw functions (❌ BAD)
# ----------------------------------------------------------------------------
def draw_menu_bad_example(self, screen):
"""
Example of where NOT to place breakpoints - in drawing code
Why? NCurses display will be corrupted when paused
Solution: Use logging instead
"""
# ❌ DON'T PUT BREAKPOINT HERE - screen will freeze/corrupt
# ✅ DO THIS INSTEAD - use logging
logger.debug(f"Drawing menu with {len(self.options)} options")
for idx, option in enumerate(self.options):
# ❌ DON'T PUT BREAKPOINT HERE
logger.debug(f"Drawing option {idx}: {option}")
screen.addstr(idx, 0, option)
# Technique 3: Debug data loading/saving (✅ GOOD)
# ----------------------------------------------------------------------------
def load_game_data_debug_example(self):
"""
Example: Debug data loading with detailed logging
Breakpoints work well here since no UI is being drawn
"""
# ← BREAKPOINT HERE ✅ (before loading)
logger.info("Starting game data load")
try:
# ← BREAKPOINT HERE ✅ (inspect path)
roster_path = self.get_roster_path()
logger.debug(f"Loading roster from: {roster_path}")
# ← BREAKPOINT HERE ✅ (step into to see loading logic)
self.roster = self.load_roster(roster_path)
# ← BREAKPOINT HERE ✅ (verify roster loaded)
logger.info(f"Loaded {len(self.roster)} characters")
# Log details for each character (visible in log file)
for char in self.roster:
logger.debug(f" - {char.name} (Level {char.level})")
except Exception as e:
# ← BREAKPOINT HERE ✅ (catch errors)
logger.error(f"Failed to load roster: {e}", exc_info=True)
raise
# Technique 4: Conditional breakpoints (✅ ADVANCED)
# ----------------------------------------------------------------------------
def handle_menu_selection_advanced(self, cursor_position: int):
"""
Example: Use conditional breakpoints for specific cases
In PyCharm:
1. Right-click breakpoint
2. Add condition: cursor_position == 3
3. Breakpoint only hits when cursor is at position 3
"""
# ← CONDITIONAL BREAKPOINT: cursor_position == 3
logger.debug(f"Menu selection at position {cursor_position}")
# Your logic here
if cursor_position == 3:
# This specific case can be debugged
logger.debug("Special case: position 3 selected")
# Technique 5: Watch expressions (✅ ADVANCED)
# ----------------------------------------------------------------------------
def game_state_debug_example(self):
"""
Example: Use watch expressions to monitor state
In PyCharm Debug panel:
1. Right-click in Variables panel
2. Add Watch: len(self.party)
3. Add Watch: self.mode
4. See values update as you step through
"""
# ← BREAKPOINT HERE ✅
# Add watches:
# - len(self.party)
# - len(self.roster)
# - self.current_location
# - self.gold
logger.debug("Game state check:")
logger.debug(f" Party size: {len(self.party)}")
logger.debug(f" Roster size: {len(self.roster)}")
logger.debug(f" Mode: {self.mode}")
# ============================================================================
# REAL-WORLD DEBUGGING SCENARIOS
# ============================================================================
# Scenario 1: "Why isn't my character loading?"
# ----------------------------------------------------------------------------
def debug_character_loading():
"""
Problem: Characters not appearing in roster
Debug steps:
1. Breakpoint in load_game_data()
2. Inspect self.characters_dir
3. Step into get_roster()
4. Verify files are being read
5. Check Character objects are created correctly
"""
# In main_ncurses.py, add breakpoints at:
# Line ~230: self.roster = get_roster(self.characters_dir)
# Then step through to see what happens
pass
# Scenario 2: "Menu selection not working"
# ----------------------------------------------------------------------------
def debug_menu_selection():
"""
Problem: Selecting menu item doesn't do anything
Debug steps:
1. Breakpoint in _handle_castle() (or relevant handler)
2. Press Enter in the game
3. Breakpoint should hit
4. Inspect self.castle_cursor
5. Step through the if/elif chain to see which branch executes
"""
# In main_ncurses.py, add breakpoints at:
# Line ~XXX: def _handle_castle(self, c: int):
# Line ~XXX: if c in (ord('\n'), ord('\r')):
pass
# Scenario 3: "Game crashes on save"
# ----------------------------------------------------------------------------
def debug_save_crash():
"""
Problem: Game crashes when saving
Debug steps:
1. Breakpoint in save_character() or save_party()
2. Inspect the character object
3. Check file path is valid
4. Step through pickle.dump() to see where it fails
5. Catch exception and log details
"""
# In main_ncurses.py, add breakpoints at:
# Line ~XXX: def save_character(char, _dir):
# Wrap in try/except with logging
pass
# ============================================================================
# DEBUGGING CHECKLIST
# ============================================================================
"""
Before starting debug session:
□ Configuration set to main_pexpect.py with parameter 'ncurses'
□ "Emulate terminal in output console" is checked
□ TERM=xterm-256color in environment variables
□ Breakpoints placed in business logic (not drawing code)
□ Logging configured for detailed output
During debug session:
□ Use F8 to step over, F7 to step into
□ Inspect variables in Debug panel
□ Check log file for additional context
□ Use watches for important state variables
□ Test specific scenarios with conditional breakpoints
After finding the bug:
□ Fix the code
□ Test in PyCharm debugger
□ Test in real terminal to verify UI
□ Update tests if needed
"""
# ============================================================================
# COMMON PATTERNS
# ============================================================================
class DebugPatterns:
"""Common debugging patterns for ncurses apps"""
@staticmethod
def debug_with_logging(func):
"""Decorator to add automatic logging to functions"""
def wrapper(*args, **kwargs):
logger.debug(f"Entering {func.__name__}")
try:
result = func(*args, **kwargs)
logger.debug(f"Exiting {func.__name__} with result: {result}")
return result
except Exception as e:
logger.error(f"Error in {func.__name__}: {e}", exc_info=True)
raise
return wrapper
@staticmethod
def safe_curses_call(func):
"""Decorator to safely call curses functions with error handling"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logger.error(f"Curses error in {func.__name__}: {e}")
# Don't crash the whole app
return None
return wrapper
# ============================================================================
# USAGE EXAMPLES
# ============================================================================
if __name__ == "__main__":
print("This is an example file showing debugging techniques.")
print()
print("To debug main_ncurses.py with PyCharm:")
print()
print("1. Open PyCharm")
print("2. Run → Edit Configurations")
print("3. Create new Python configuration:")
print(" - Script: main_pexpect.py")
print(" - Parameters: ncurses")
print(" - ☑ Emulate terminal in output console")
print("4. Open main_ncurses.py")
print("5. Place breakpoints (see examples above)")
print("6. Click Debug button (🐞)")
print()
print("For more info, see: PYCHARM_DEBUG_GUIDE.md")