├── requirements.txt ├── hello.js ├── coin_01.png ├── character.png ├── nodejs_hello.js ├── index.html ├── test_myGame.py ├── PREP.md ├── game.py ├── final_game.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | arcade -------------------------------------------------------------------------------- /hello.js: -------------------------------------------------------------------------------- 1 | function hello_message(person) { 2 | return 'Hello ' + person; 3 | } -------------------------------------------------------------------------------- /coin_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pauleveritt/visual_debugging/HEAD/coin_01.png -------------------------------------------------------------------------------- /character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pauleveritt/visual_debugging/HEAD/character.png -------------------------------------------------------------------------------- /nodejs_hello.js: -------------------------------------------------------------------------------- 1 | function hello_message(person) { 2 | return 'Hello ' + person; 3 | } 4 | 5 | console.log(hello_message('world')); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | Hello World 3 | 4 |

Hello World

5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /test_myGame.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | class TestMyGame(TestCase): 5 | def test_setup(self): 6 | from game import MyGame 7 | game = MyGame(100, 100, 'Test') 8 | assert game.is_stopped is True 9 | 10 | def test_not_stopped(self): 11 | from game import MyGame 12 | game = MyGame(100, 100, 'Test') 13 | game.is_stopped = False 14 | game.on_draw() 15 | assert game.is_stopped is True 16 | 17 | def test_stopped(self): 18 | from game import MyGame 19 | game = MyGame(100, 100, 'Test') 20 | game.is_stopped = True 21 | game.on_draw() 22 | assert game.is_stopped is True 23 | -------------------------------------------------------------------------------- /PREP.md: -------------------------------------------------------------------------------- 1 | ### Before 2 | 3 | - Delete Cython speedups 4 | * \helpers\pydev\_pydevd_bundle 5 | * Remove pydevd_cython.so 6 | - Uninstall ipython 7 | - Default theme, big font 8 | - Ensure Presentation Assistant is on 9 | - Turn off various tools and status bar 10 | - Close Chrome 11 | - Django/Flask template debugging projects open 12 | - Have the numpy/pandas project open 13 | - Delete the run configuration for game.py 14 | - Check value of break on any Python exception 15 | - Delete any watch expressions 16 | - Test exists, with a run configuration for all tests 17 | - Delete run configurations 18 | 19 | - Turn off Notifications and other macOS toolbar stuff 20 | - Close all the Safari tabs 21 | - 1280x720 22 | - Microphone -------------------------------------------------------------------------------- /game.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import arcade 4 | 5 | 6 | class MyGame(arcade.Window): 7 | def __init__(self, width: int, height: int, title: str) -> None: 8 | super().__init__(width, height, title) 9 | arcade.set_background_color(arcade.color.AMAZON) 10 | self.score: int = 0 11 | 12 | self.all_sprites_list = arcade.SpriteList() 13 | self.player_sprite = arcade.Sprite('character.png', 0.5) 14 | self.player_sprite.center_x = 50 15 | self.player_sprite.center_y = 50 16 | self.all_sprites_list.append(self.player_sprite) 17 | 18 | self.coin_list = arcade.SpriteList() 19 | for i in range(50): 20 | coin = arcade.Sprite('coin_01.png', 0.2) 21 | coin.center_x = random.randrange(600) 22 | coin.center_y = random.randrange(600) 23 | self.all_sprites_list.append(coin) 24 | self.coin_list.append(coin) 25 | 26 | def on_draw(self): 27 | arcade.start_render() 28 | output = f'Score: {self.score:02d}' 29 | arcade.draw_text(output, 100, 100, arcade.color.WHITE) 30 | self.all_sprites_list.draw() 31 | 32 | def update(self, delta_time: float) -> None: 33 | self.all_sprites_list.update() 34 | hit_list = arcade.check_for_collision_with_list( 35 | self.player_sprite, 36 | self.coin_list 37 | ) 38 | for coin in hit_list: 39 | coin.kill() 40 | self.score += 1 41 | 42 | def on_mouse_motion(self, x, y, dx, dy): 43 | self.player_sprite.center_x = x 44 | self.player_sprite.center_y = y 45 | 46 | 47 | def main(): 48 | MyGame(600, 600, 'My Awesome Game') 49 | arcade.run() 50 | 51 | 52 | if __name__ == '__main__': 53 | main() 54 | -------------------------------------------------------------------------------- /final_game.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import arcade 4 | 5 | 6 | class MyGame(arcade.Window): 7 | def __init__(self, width, height, title): 8 | super().__init__(width, height, title) 9 | arcade.set_background_color(arcade.color.AMAZON) 10 | self.score = 0 11 | 12 | self.all_sprites_list = arcade.SpriteList() 13 | self.player_sprite = arcade.Sprite('character.png', 0.5) 14 | self.player_sprite.center_x = 50 15 | self.player_sprite.center_y = 50 16 | self.all_sprites_list.append(self.player_sprite) 17 | self.is_stopped: bool = True 18 | 19 | self.coin_list = arcade.SpriteList() 20 | for i in range(50): 21 | coin = arcade.Sprite('coin_01.png', 0.2) 22 | coin.center_x = random.randrange(600) 23 | coin.center_y = random.randrange(600) 24 | self.all_sprites_list.append(coin) 25 | self.coin_list.append(coin) 26 | 27 | def on_draw(self): 28 | arcade.start_render() 29 | output = f'Score: {self.score:02d}' 30 | if self.is_stopped: 31 | arcade.draw_text(output, 100, 100, arcade.color.WHITE) 32 | self.is_stopped = True 33 | self.all_sprites_list.draw() 34 | 35 | def animate(self, delta_time): 36 | self.all_sprites_list.update() 37 | hit_list = arcade.check_for_collision_with_list( 38 | self.player_sprite, 39 | self.coin_list 40 | ) 41 | 42 | for coin in hit_list: 43 | coin.kill() 44 | self.score += 1 45 | 46 | def on_mouse_motion(self, x, y, dx, dy): 47 | self.is_stopped = False 48 | self.player_sprite.center_x = x 49 | self.player_sprite.center_y = y 50 | 51 | 52 | def main(): 53 | MyGame(600, 600, 'My Game') 54 | arcade.run() 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Visual Debugging Webinar 2 | 3 | - Debugging without PyCharm's debugger: print and pdb 4 | - First use of the debugger (and the Cython speedups) 5 | - Breakpoints 6 | - Using the console/IPython at a breakpoint 7 | - Conditional breakpoints 8 | - All flavors of stepping through code 9 | - Moving through stack frames 10 | - Setting watches 11 | - Attaching to processes 12 | - Debugging during testing 13 | - Extracting type information 14 | - Django/Flask template debugging 15 | - Viewing numpy/pandas data frames 16 | 17 | 18 | 19 | ### Old-Fashioned Debugging 20 | 21 | - Problem: Where is 0,0 22 | - print statements 23 | - import pdb; pdb.set_trace() 24 | 25 | 26 | ### Visual Debugging 27 | 28 | - Run program like normal, under the debugger 29 | - Install Cython speedups 30 | - Use breakpoint to solve first problem 31 | 32 | ### Poking Around 33 | 34 | - Problem: I want to use f-string `f'Score: {self.score:02d}'` but I'm not sure how 35 | - Interactive would help 36 | - Set breakpoint, then use Evaluate Expression 37 | - Or, Console 38 | - Install iPython and use it 39 | 40 | ### Breakpoints 41 | 42 | - Problem: What is that "delta_time" thing? 43 | - Set breakpoint, observe value, click "Continue" 44 | - Clear breakpoint, click "Continue" 45 | - Problem: wrong filename, what directory am I in? 46 | - Any Exception breakpoint 47 | 48 | ## Stepping 49 | 50 | - Problem: 50 coins at random positions 51 | - Add for loop, but is random doing the right thing? 52 | - Set breakpoint, start regular stepping 53 | - Set breakpoint outside, and step into 54 | - Ditto, step over 55 | - Problem: How does Arcade collisions work? 56 | - Add code then step into 57 | 58 | ### Watch Expressions 59 | 60 | - Problem: Are the number of coins actually decreasing? 61 | - Set breakpoint in for coin in hit_list 62 | - Add a watch expression: len(self.coin_list) 63 | - Cause collision with multiple coins 64 | - Step through and see the watch expression value 65 | 66 | ### Stack Frames 67 | 68 | - Problem: Which coin is in _set_center_x? 69 | - In for loop, step into setting the x 70 | - Move back up the stack 71 | - Observe the watch expression and self.score 72 | 73 | ### Debug During Testing 74 | 75 | - Problem: TDD for "Don't show score when mouse is moving" 76 | - Write a test to see if score goes up on collision 77 | - It fails, I'm confused 78 | - Use debugging 79 | - Fix tests, clear breakpoint, re-run 80 | 81 | ### Attach to Process 82 | 83 | - Run ``game.py`` from command line 84 | - Use "Attach to Process" and then debug it 85 | 86 | ### Extract Type Information 87 | 88 | - Preferences -> Build -> Python Debugger, checkbox for 89 | `Collect` 90 | 91 | ### Django and Flask 92 | 93 | - Open Flask project 94 | - Set a breakpoint in a template 95 | 96 | ### Extra Credit 97 | 98 | - NodeJS/Chrome debugging 99 | - Configuring Stepping 100 | - Keyboard shortcuts for stepping 101 | - Show Execution Point button in toolbar when you get lost 102 | - Mute Breakpoints button 103 | - Inspect watch value in separate window 104 | --------------------------------------------------------------------------------