Skip to content

Commit 63dc2ff

Browse files
committed
feat: Introduce several new Python projects, unit testing fundamentals, and update project dependencies.
1 parent d1c6102 commit 63dc2ff

11 files changed

Lines changed: 1404 additions & 9 deletions

File tree

11_testing/01_test_basics.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
================================================================================
3+
File: 01_test_basics.py
4+
Topic: Introduction to Unit Testing with pytest
5+
================================================================================
6+
7+
This file introduces unit testing, a professional practice used to ensure that
8+
code behaves as expected. We use 'pytest', the industry standard for Python.
9+
10+
Key Concepts:
11+
- Writing test functions (starting with test_)
12+
- Assertions (checking if conditions are True)
13+
- Running tests from the terminal
14+
================================================================================
15+
"""
16+
17+
import pytest
18+
19+
# A simple function we want to test
20+
def add(a: int, b: int) -> int:
21+
return a + b
22+
23+
def test_add_positive():
24+
"""Test addition of positive numbers."""
25+
assert add(2, 3) == 5
26+
27+
def test_add_negative():
28+
"""Test addition of negative numbers."""
29+
assert add(-1, -1) == -2
30+
31+
def test_add_zero():
32+
"""Test addition with zero."""
33+
assert add(5, 0) == 5
34+
35+
if __name__ == "__main__":
36+
print("Run this file using: pytest 11_testing/01_test_basics.py")

README.md

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ A practical, example-driven Python repository designed to help you learn Python
1010

1111
Each file is self-contained and focuses on a single concept, making the repository suitable for structured learning, revision, or reference.
1212

13-
### 🌐 **Visit the Interactive Website**
14-
Want to follow your progress? Visit the website at [pythonbyexample.page.gd](https://pythonbyexample.page.gd/)
15-
1613
> **If you like this project, please hit the ⭐ Star button and follow me on GitHub [@blshaer](https://github.com/blshaer)!**
1714
1815
---
@@ -25,6 +22,7 @@ Want to follow your progress? Visit the website at [pythonbyexample.page.gd](htt
2522
- [Prerequisites](#-prerequisites)
2623
- [File Structure](#-file-structure)
2724
- [Learning Path](#-learning-path)
25+
- [Projects](#-projects)
2826

2927
---
3028

@@ -119,21 +117,50 @@ This repository contains a complete Python tutorial designed for both beginners
119117

120118
## Getting Started
121119

120+
Follow these steps to clone the repository and set it up in your local editor:
121+
122122
1. **Clone the repository**
123+
Open your terminal and run:
124+
```bash
125+
git clone https://github.com/blshaer/python-by-example.git
126+
cd python-by-example
127+
```
128+
129+
2. **Set up a Virtual Environment (Recommended)**
130+
It's best practice to keep your projects isolated. Run these commands:
131+
```bash
132+
python -m venv venv
133+
# On Windows:
134+
.\venv\Scripts\activate
135+
# On macOS/Linux:
136+
source venv/bin/activate
137+
```
138+
139+
3. **Install Dependencies**
140+
Install the necessary tools for testing and formatting:
141+
```bash
142+
pip install -r requirements.txt
143+
```
144+
145+
4. **Open in your code editor**
146+
We recommend [Visual Studio Code](https://code.visualstudio.com/). Open the project directly:
123147
```bash
124-
git clone https://github.com/blshaer/python_review.git
125-
cd python_review
148+
code .
126149
```
127150

128-
2. **Start from basics**
151+
5. **Explore and Run**
152+
Navigate to any example, like `01_basics`, and run the file:
129153
```bash
130154
cd 01_basics
131155
python 01_print.py
132156
```
133157

134-
3. **Read the comments**Each section is thoroughly explained
158+
6. **Read the comments**Detailed explanations are provided inline for every concept!
135159

136-
4. **Experiment** — Modify the code and observe the results
160+
7. **Try the Tests** — Go to the new testing module to see how professional code is verified:
161+
```bash
162+
pytest 11_testing/01_test_basics.py
163+
```
137164

138165
---
139166

@@ -219,6 +246,7 @@ Key Concepts:
219246
<td>
220247

221248
10. [Best Practices](./10_best_practices/)
249+
11. [Testing](./11_testing/)
222250

223251
</td>
224252
</tr>
@@ -229,11 +257,26 @@ Key Concepts:
229257
Beginner → 01_basics → 02_control_flow → 03_loops
230258
Intermediate → 04_data_structures → 05_functions → 06_modules
231259
Advanced → 07_error_handling → 08_oop → 09_advanced
232-
Professional → 10_best_practices
260+
Professional → 10_best_practices → 11_testing
233261
```
234262

235263
---
236264

265+
## Projects
266+
267+
Put your skills to the test! Each project includes a **challenge description** (`README.md`) so you can try building it yourself before looking at the solution.
268+
269+
| # | Project | Difficulty | Concepts Applied |
270+
|:--|:--------|:-----------|:-----------------|
271+
| 01 | [**Number Guessing Game**](./projects/01_number_guessing_game/) | 🟢 Beginner | Loops, Conditionals, Random module |
272+
| 02 | [**Expense Tracker**](./projects/02_expense_tracker/) | 🟡 Intermediate | Dicts, File I/O (JSON), Functions |
273+
| 03 | [**Library Management System**](./projects/03_library_management/) | 🟠 Advanced | OOP, Inheritance, Custom Exceptions |
274+
| 04 | [**Real-time Weather CLI**](./projects/04_weather_cli/) | 🚀 Professional | API Requests, Decorators, Type Hints |
275+
276+
> 💡 **Tip:** Read the project `README.md` first and try to build it on your own before looking at `solution.py`!
277+
278+
---
279+
237280
## Progress Tracker
238281

239282
Use this checklist to track your learning progress:
@@ -248,6 +291,13 @@ Use this checklist to track your learning progress:
248291
- [ ] **08. OOP** — Classes, Init, Inheritance, Polymorphism
249292
- [ ] **09. Advanced Python** — Comprehensions, Generators, Decorators
250293
- [ ] **10. Best Practices** — PEP8, Type Hints, Virtual Environments
294+
- [ ] **11. Testing** — Unit Tests, Pytest, Assertions
295+
296+
### 🏗️ Projects
297+
- [ ] **Project 01** — Number Guessing Game (Beginner)
298+
- [ ] **Project 02** — Expense Tracker (Intermediate)
299+
- [ ] **Project 03** — Library Management System (Advanced)
300+
- [ ] **Project 04** — Real-time Weather CLI (Professional)
251301

252302
---
253303

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# 🎯 Project 01: Number Guessing Game
2+
3+
## Difficulty: 🟢 Beginner
4+
5+
## Description
6+
Build a command-line number guessing game where the computer picks a random number
7+
and the player tries to guess it. The game should provide hints like "Too high!" or
8+
"Too low!" after each guess.
9+
10+
## Requirements
11+
12+
1. Generate a random number between 1 and 100
13+
2. Ask the user for guesses until they get the correct number
14+
3. After each guess, tell the user if their guess is too high, too low, or correct
15+
4. Track the number of attempts and display it at the end
16+
5. Handle invalid input gracefully (non-numeric input)
17+
6. Ask the player if they want to play again after winning
18+
19+
## Concepts You'll Practice
20+
21+
- `while` loops
22+
- `if/elif/else` conditionals
23+
- `random` module
24+
- `try/except` error handling
25+
- `input()` function
26+
- f-strings
27+
28+
## Example Output
29+
30+
```
31+
🎯 Welcome to the Number Guessing Game!
32+
I'm thinking of a number between 1 and 100...
33+
34+
Enter your guess: 50
35+
📉 Too low! Try again.
36+
37+
Enter your guess: 75
38+
📈 Too high! Try again.
39+
40+
Enter your guess: 63
41+
🎉 Congratulations! You guessed it in 3 attempts!
42+
43+
Play again? (yes/no): no
44+
Thanks for playing! Goodbye! 👋
45+
```
46+
47+
## How to Run
48+
49+
```bash
50+
cd projects/01_number_guessing_game
51+
python solution.py
52+
```
53+
54+
## Bonus Challenges
55+
56+
- [ ] Add difficulty levels (Easy: 1-50, Medium: 1-100, Hard: 1-500)
57+
- [ ] Add a scoreboard that tracks best scores across games
58+
- [ ] Set a maximum number of attempts based on difficulty
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""
2+
================================================================================
3+
Project 01: Number Guessing Game
4+
Difficulty: 🟢 Beginner
5+
================================================================================
6+
7+
A command-line number guessing game where the computer picks a random number
8+
and the player tries to guess it with hints provided after each attempt.
9+
10+
Concepts Used:
11+
- while loops
12+
- if/elif/else conditionals
13+
- random module
14+
- try/except error handling
15+
- input() function
16+
- f-strings
17+
18+
================================================================================
19+
"""
20+
21+
import random
22+
23+
24+
# -----------------------------------------------------------------------------
25+
# Game Configuration
26+
# -----------------------------------------------------------------------------
27+
28+
DIFFICULTIES = {
29+
"easy": {"min": 1, "max": 50, "attempts": 10},
30+
"medium": {"min": 1, "max": 100, "attempts": 7},
31+
"hard": {"min": 1, "max": 500, "attempts": 10},
32+
}
33+
34+
35+
# -----------------------------------------------------------------------------
36+
# Helper Functions
37+
# -----------------------------------------------------------------------------
38+
39+
def display_welcome():
40+
"""Display the welcome banner."""
41+
print("""
42+
╔══════════════════════════════════════════╗
43+
║ 🎯 NUMBER GUESSING GAME 🎯 ║
44+
╚══════════════════════════════════════════╝
45+
""")
46+
47+
48+
def choose_difficulty() -> dict:
49+
"""Let the player choose a difficulty level."""
50+
print("Choose your difficulty:")
51+
print(" [1] 🟢 Easy (1-50, 10 attempts)")
52+
print(" [2] 🟡 Medium (1-100, 7 attempts)")
53+
print(" [3] 🔴 Hard (1-500, 10 attempts)")
54+
55+
while True:
56+
choice = input("\nYour choice (1/2/3): ").strip()
57+
if choice == "1":
58+
return DIFFICULTIES["easy"]
59+
elif choice == "2":
60+
return DIFFICULTIES["medium"]
61+
elif choice == "3":
62+
return DIFFICULTIES["hard"]
63+
else:
64+
print("❌ Invalid choice. Please enter 1, 2, or 3.")
65+
66+
67+
def get_guess(min_val: int, max_val: int) -> int:
68+
"""Get a valid integer guess from the player."""
69+
while True:
70+
try:
71+
guess = int(input(f"\nEnter your guess ({min_val}-{max_val}): "))
72+
if min_val <= guess <= max_val:
73+
return guess
74+
else:
75+
print(f"⚠️ Please enter a number between {min_val} and {max_val}.")
76+
except ValueError:
77+
print("⚠️ That's not a valid number. Try again!")
78+
79+
80+
def play_round():
81+
"""Play a single round of the guessing game."""
82+
# Setup
83+
config = choose_difficulty()
84+
min_val = config["min"]
85+
max_val = config["max"]
86+
max_attempts = config["attempts"]
87+
secret = random.randint(min_val, max_val)
88+
attempts = 0
89+
90+
print(f"\n🤔 I'm thinking of a number between {min_val} and {max_val}...")
91+
print(f" You have {max_attempts} attempts. Good luck!\n")
92+
93+
# Game loop
94+
while attempts < max_attempts:
95+
guess = get_guess(min_val, max_val)
96+
attempts += 1
97+
remaining = max_attempts - attempts
98+
99+
if guess < secret:
100+
print(f"📉 Too low! ({remaining} attempts remaining)")
101+
elif guess > secret:
102+
print(f"📈 Too high! ({remaining} attempts remaining)")
103+
else:
104+
print(f"\n🎉 Congratulations! You guessed it in {attempts} attempt(s)!")
105+
if attempts <= 3:
106+
print("🏆 Amazing! You're a natural!")
107+
elif attempts <= 5:
108+
print("👏 Well done!")
109+
else:
110+
print("😅 That was close!")
111+
return True
112+
113+
# Out of attempts
114+
print(f"\n💀 Game over! The number was {secret}.")
115+
return False
116+
117+
118+
def play_again() -> bool:
119+
"""Ask the player if they want to play again."""
120+
while True:
121+
answer = input("\n🔄 Play again? (yes/no): ").strip().lower()
122+
if answer in ("yes", "y"):
123+
print()
124+
return True
125+
elif answer in ("no", "n"):
126+
return False
127+
else:
128+
print("Please enter 'yes' or 'no'.")
129+
130+
131+
# -----------------------------------------------------------------------------
132+
# Main Game Loop
133+
# -----------------------------------------------------------------------------
134+
135+
def main():
136+
"""Main entry point for the game."""
137+
display_welcome()
138+
139+
wins = 0
140+
total = 0
141+
142+
while True:
143+
total += 1
144+
if play_round():
145+
wins += 1
146+
147+
print(f"\n📊 Score: {wins} wins out of {total} games")
148+
149+
if not play_again():
150+
break
151+
152+
print(f"\n{'='*40}")
153+
print(f" Final Score: {wins}/{total} games won")
154+
print(f" Thanks for playing! Goodbye! 👋")
155+
print(f"{'='*40}\n")
156+
157+
158+
if __name__ == "__main__":
159+
main()

0 commit comments

Comments
 (0)