Skip to content

Commit

Permalink
debugging prep
Browse files Browse the repository at this point in the history
  • Loading branch information
colevandersWands committed Dec 7, 2024
1 parent cf97cfe commit 37c86c0
Show file tree
Hide file tree
Showing 26 changed files with 1,097 additions and 83 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ __pycache__
*.cover
*.excalidraw
plann.txt
exercises_with_notes
7 changes: 4 additions & 3 deletions 4__python_self_study_2/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Python Self-Study 2

To prepare for the rest of the workshops in this series, you should study these
All of the workshop resources in this series will only use language features covered in [Python Self-Study 1](../2__python_self_study_1/). But as you become more comfortable programming you may want to explore further and practice solving harder challenges.

To prepare for the next steps in your Python journey, you should study these
language features using your favorite resources in the language of your choice.
You don't need to master them, just be familiar. Don’t forget to study with
Predictive Stepping!
You don't need to master them, just be familiar. Don’t forget to study using Documentation, Tests and Predictive Stepping!

- [ ] **Collections**: dictionaries, tuples, sets
- [ ] **Iteration**: `iter()`, `next()`, `enumerate()`
Expand Down
Binary file added 4_debugging/.assets/rubber-ducky.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
145 changes: 124 additions & 21 deletions 4_debugging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ different. But if you practice good habits, can avoid many bugs and be ready to
fix the bugs you can't avoid.

- [Learning Objectives](#learning-objectives)
- [Debuggin](./debugging.md)
- [Fixing Errors](#fixing-errors)
- [Avoiding Bugs](#avoiding-bugs)
- [Fixing Bugs](#fixing-bugs)
- [Errors vs. Bugs](#errors-vs-bugs)
- [Understand, Plan, Experiment](#understand-plan-experiment)
- [Surprises](#surprises)
- [Experiments](#experiments)
- [Philosophy of Debugging](./philosophy_of_debugging.md)
- [Prep Work](./prep_work.md)
- [Lesson Plan](./lesson_plan.md)

---

## Learning Objectives

<details><summary>Priorities: 🥚🐣🐥🐔 (click for more info)</summary>
Expand All @@ -47,38 +52,136 @@ master all of the skills introduced in this workshop.

</details>

### Fixing Errors

- 🥚 Understand the difference between an _error_ and a _bug_.
- **Error**: When the Python interpreter cannot execute your code and the program stops.
- **Bug**: When your program runs, but it's _behavior_ is not what you expected or wanted.
- 🥚 Use error messages and your debugger to find where errors occur in your code.
- 🥚 Search errors online using the _name_, the _message_ and your code for context.
- 🐣 Understand the difference between _syntax_ and _semantic_ (or _runtime_) errors.
- 🐣 Understand and explain why your error occurred: what about your code caused the program to stop?
- 🐣 Fixing errors in your program.

### Avoiding Bugs

- 🥚 Pair programming with someone you trust.
- 🥚 Always use the simplest and most understandable solution.
([KISS](https://github.com/dwmkerr/hacker-laws#the-kiss-principle))
- 🥚 Develop your code one small step at a time, writing and running tests for
each change before moving on.
- 🥚 Have others read and review your code, they will find mistakes you missed
and think of improvements you wouldn't.
- 🥚 Write less code. Keep your end goal in mind and avoid writing any code that
is not _absolutely necessary_ to reach your goal.
- 🥚 Keep a _Bug Log_; Write down bugs you've encountered and how you fixed them.
This log will help you avoid making the same mistakes, and double as
inspiration for how to fix new bugs.
- 🥚 Know that someone else will always use your [differently than you'd like](https://www.youtube.com/watch?v=CfCiW4UhqLo).
- _test as many edge cases as possible!_
- 🥚 Don't trust AI without checking it's work! Always take the time to understand, test and debug code AI writes for you.

### Fixing Bugs

- 🦆 You are not embarrassed to do some
[rubber duck debugging](https://rubberduckdebugging.com/).
- 🥚 You know that someone else will always use your program
[in a way you didn't imagine](https://www.youtube.com/watch?v=CfCiW4UhqLo).
- 🥚 You can study a program skeptically, always asking "_how can I break this
program?_".
- 🐣 You can identify steps of execution that surprise you. This will help
understand the gap between what a program _does_ do, and what it _should_ do.
- 🐣 You can clearly describe a bug by answering questions like these:
- on what line does the bug occur?
- what values were stored in memory when when the bug occurred?
- what language features are involved with the bug?
- what _should_ the program do? Name specific test cases and lines of code!
- What _does_ the program do? Name specific test cases and lines of code!
- 🐣 You can recognize these four types of bug: overt vs. covert, and persistent
vs. intermittent.
- 🐥 You can trace a program backwards from a surprising step to understand how
it happened (either mentally or on paper, VSCode's debugger only goes
forward).
- 🐥 You can trace a program backwards from a surprising step to understand how it happened
- Either mentally or on paper, almost all debuggers only go forward.

### Avoiding Bugs in the Fist Place
---

- 🥚 Pair programming with someone you trust.
- 🥚 Always use the simplest and most understandable solution.
([KISS](https://github.com/dwmkerr/hacker-laws#the-kiss-principle))
- 🥚 Develop your code one small step at a time, writing and running tests for
each change before moving on.
- 🥚 Have others read and review your code, they will find mistakes you missed
and think of improvements you wouldn't.
- 🥚 Write less code. Keep your end goal in mind and avoid writing any code that
is not _absolutely necessary_ to reach your goal.
- 🥚 Keep a Bug Log; Write down bugs you've encountered and how you fixed them.
This log will help you avoid making the same mistakes, and double as
inspiration for how to fix new bugs.
## Errors vs. Bugs

Python mistakes (or _errors_) are when your program is not able to finish because you wrote code that Python cannot execute.
Logic mistakes (or _bugs_) are when your code runs without an error, but does
not do what you expected. It will take practice to recognize the difference, [this video](https://www.youtube.com/watch?v=tV0tQisuxPo) has some funny examples to get you started -
You can think of errors vs. bugs in normal language:

- **Error**: A sentence can have incorrect grammar, so others can't understand it - _Chair train did doing water_.
- **bug**: A sentence can have correct grammar and still mean something different than you intended - _The panda eats shoots and leaves_.

While fixing errors "only" requires a solid understanding of Python syntax and
runtime. Fixing logic mistakes also requires an understanding of debugging strategies, testing and strategic thinking.

---

## Understand, Plan, Experiment

Fixing bugs requires carefully understanding what your code _does do_ before trying to
make it do what you _want_ it to do. After you understand the difference between
what it _does_ do and what it _should_ do, you can make small
experiments to discover how you can go from buggy code to working code.

Becoming a master debugger will take lots of experience, the more bugs you've
fixed the more solutions you know. Practicing this structured approach will help
you learn the most from each bug you encounter.

### What _does_ it do?

When you're already thinking of what the program _should_ do, it's too easy to
look at your buggy code and see what you _want_ to see. Not what's actually
there. Clearing your mind and telling yourself "I know nothing" is the first
step to understanding _exactly_ what the buggy code does and how it works.

Here some questions you should practice asking and answering when debugging. If you can't answer these questions then fixing your bug will be luck, not skill!

1. **What _should_ the code do?** The best way to understand what a function _should_ do is to write documentation and unit tests covering as many _edge cases_ as possible.
2. **What _does_ it do?** Run your unit tests and study the output to understand the buggy function's _behavior_. Can you see a pattern in which tests pass and which ones fail? When a test fails what did the function _actually_ return, and how does this compare to the _expected_ value?
- You might want to write _documentation_ for your buggy function before you start debugging it! This will force you to understand it's _behavior_ very well before moving on.
3. **How _does_ it work?** Spend the time to really understand the function's _strategy_ and _implementation_ before you start making any changes: Read the code, add comments for yourself, step through the function using paper or a debugger, ...

### Surprises

The whole point of debugging is that you _expect_ your code to do one thing, but
it _actually_ does something different. That means there must be at least one
line of code that surprises you! Finding the line(s) or step(s) that that surprise you is a huge step towards fixing your
bug. If there is a line of code that surprises,
then there's a good chance it has something to do with your bug.

As you study the function, mark any lines of code or steps of execution that surprise you. Careful! You're not trying to fix the bug yet, you're still trying to understand the function _before_ you make any changes. Take notes on:

- What values were stored in memory when the code surprised you?
- What actually happened that surprised you?
- What did you expect to happen instead?
- ... anything else that jumps out at you. You never know which details might be important!

### Experiments

After you've understood what the code _should_ do, what the code _does_ do and
you've found the lines that surprise you - it's time to start experimenting!
This is where the real debugging begins.

Fixing a bug doesn't happen by magic, and it usually doesn't happen in a
single moment of genius. Fixing a bug happens in small steps using trial and
error. You can think of it as a conversation with your code.

- You ask your code if you found the answer by making a small change and running the tests.
- The tests tells you if you found the answer by either passing or failing.

There are may

- Is something is missing? Try adding it in.
- Is there something extra that shouldn't be there? Try removing it.
- Is something int he wrong place? Try moving it somewhere else in the function.
- Is it in the right place, but the logic isn't correct? Try making small changes to the logic and running the tests after each change.

By taking notes on all the little experiments you make and what happened you can
carefully fix your bugs without causing more problems. (and learn a lot on the
way)

---
---

[![rubber ducky](./.assets/rubber-ducky.png)](https://rubberduckdebugging.com)
Empty file added 4_debugging/common_mistakes.md
Empty file.
37 changes: 37 additions & 0 deletions 4_debugging/exercises/1_buggy_tests/alternate_elements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A module for list manipulation focusing on alternating elements.
This is part of the debugging exercise series focusing on buggy tests.
Module contents:
- alternate_elements: Creates a new list with every other element
Created on 2024-12-06
Author: Claude AI
"""

def alternate_elements(items: list) -> list:
"""Returns a new list containing every other element from the input list.
Takes any list and returns a new list with elements at even indices
(0, 2, 4, etc.). The original list is not modified.
Parameters:
items: list, the input list to process
Returns -> list: new list containing every other element
Raises:
AssertionError: if input is not a list
Examples:
>>> alternate_elements([1, 2, 3, 4, 5])
[1, 3, 5]
>>> alternate_elements(['a', 'b', 'c'])
['a', 'c']
>>> alternate_elements([])
[]
"""
assert isinstance(items, list), "input must be a list"
return items[::2]
46 changes: 46 additions & 0 deletions 4_debugging/exercises/1_buggy_tests/count_between.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A module for counting numbers within a range.
This is part of the debugging exercise series focusing on buggy tests.
Module contents:
- count_between: Counts how many numbers fall between two values
Created on 2024-12-06
Author: Claude AI
"""

def count_between(numbers: list, lower: int, upper: int) -> int:
"""Counts how many numbers in the list fall between lower and upper bounds.
The bounds are inclusive, meaning a number equal to the lower or upper
bound is counted. The numbers list can contain integers or floats.
Parameters:
numbers: list of numbers to check
lower: int, lower bound (inclusive)
upper: int, upper bound (inclusive)
Returns -> int: count of numbers between bounds
Raises:
AssertionError: if numbers is not a list or bounds aren't integers
Examples:
>>> count_between([1, 2, 3, 4, 5], 2, 4)
3
>>> count_between([1.5, 2.5, 3.5], 2, 3)
1
>>> count_between([], 0, 10)
0
"""
assert isinstance(numbers, list), "first argument must be a list"
assert isinstance(lower, int), "lower bound must be an integer"
assert isinstance(upper, int), "upper bound must be an integer"

count = 0
for num in numbers:
if lower <= num <= upper:
count += 1
return count
36 changes: 36 additions & 0 deletions 4_debugging/exercises/1_buggy_tests/count_vowels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A module for counting vowels in a string.
This is part of the debugging exercise series focusing on buggy tests.
Module contents:
- count_vowels: Counts how many vowels are in a string
Created on 2024-12-06
Author: Claude AI
"""

def count_vowels(text: str) -> int:
"""Count the number of vowels (a,e,i,o,u) in a string.
Parameters:
text: str, the input string to check
Returns -> int: number of vowels in the text
>>> count_vowels("hello")
2
>>> count_vowels("APPLE")
2
>>> count_vowels("why")
0
"""
assert isinstance(text, str), "input must be a string"

vowels = "aeiou"
count = 0
for char in text.lower():
if char in vowels:
count += 1
return count
40 changes: 40 additions & 0 deletions 4_debugging/exercises/1_buggy_tests/remove_spaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A module containing string manipulation functions for removing spaces.
This is part of the debugging exercise series focusing on buggy tests.
Module contents:
- remove_spaces: Removes all spaces from a string
Created on 2024-12-06
Author: Claude AI
"""

def remove_spaces(text: str) -> str:
"""Removes all spaces from a string.
This function takes any string input and returns a new string with all
space characters removed. It preserves all other characters including
numbers, punctuation, and special characters.
Parameters:
text: str, the input string to process
Returns -> str: the input string with all spaces removed
Raises:
AssertionError: if input is not a string
Examples:
>>> remove_spaces("hello world")
'helloworld'
>>> remove_spaces(" spaces ")
'spaces'
>>> remove_spaces("no spaces")
'nospaces'
>>> remove_spaces("")
''
"""
assert isinstance(text, str), "input must be a string"
return text.replace(" ", "")
Loading

0 comments on commit 37c86c0

Please sign in to comment.