Skip to content

Commit fe409b2

Browse files
committed
Fix notebook execution errors
- Fix singleton pattern notebook: Add missing imports and correct __new__ method - Fix repository pattern notebook: Use temp files instead of in-memory databases - All 60 notebook tests now pass with nbval validation - Notebooks are properly cleaned with no outputs and execution counts
1 parent b35cc62 commit fe409b2

File tree

3 files changed

+287
-369
lines changed

3 files changed

+287
-369
lines changed

debug_ci_environment.py

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Debug script to identify CI environment issues.
4+
This script runs comprehensive checks to identify differences between local and CI environments.
5+
"""
6+
7+
import sys
8+
import os
9+
import subprocess
10+
import json
11+
import traceback
12+
from pathlib import Path
13+
14+
15+
def run_command(cmd, check=False):
16+
"""Run a command and return the result."""
17+
try:
18+
result = subprocess.run(cmd, shell=True, check=check, capture_output=True, text=True)
19+
return result
20+
except subprocess.CalledProcessError as e:
21+
return e
22+
23+
24+
def check_python_environment():
25+
"""Check Python environment details."""
26+
print("🐍 Python Environment Check")
27+
print("=" * 40)
28+
29+
print(f"Python version: {sys.version}")
30+
print(f"Python executable: {sys.executable}")
31+
print(f"Python path: {sys.path}")
32+
33+
# Check installed packages
34+
result = run_command("python3 -m pip list")
35+
if result.returncode == 0:
36+
print("📦 Installed packages:")
37+
for line in result.stdout.split('\n')[:10]: # Show first 10 packages
38+
if line.strip():
39+
print(f" {line}")
40+
print(" ...")
41+
else:
42+
print(f"❌ Failed to list packages: {result.stderr}")
43+
44+
print()
45+
46+
47+
def check_file_system():
48+
"""Check file system and permissions."""
49+
print("📁 File System Check")
50+
print("=" * 40)
51+
52+
# Check current directory
53+
print(f"Current directory: {os.getcwd()}")
54+
55+
# Check important files exist
56+
important_files = [
57+
"src/patterns/singleton.py",
58+
"src/patterns/repository.py",
59+
"requirements.txt",
60+
"requirements-dev.txt",
61+
"notebooks/01_singleton_pattern.ipynb"
62+
]
63+
64+
for file_path in important_files:
65+
if os.path.exists(file_path):
66+
print(f"✅ {file_path} exists")
67+
else:
68+
print(f"❌ {file_path} missing")
69+
70+
# Check Python path setup
71+
src_path = os.path.join(os.getcwd(), 'src')
72+
if src_path in sys.path:
73+
print(f"✅ {src_path} is in Python path")
74+
else:
75+
print(f"❌ {src_path} not in Python path")
76+
sys.path.insert(0, src_path)
77+
print(f"🔧 Added {src_path} to Python path")
78+
79+
print()
80+
81+
82+
def check_imports():
83+
"""Check if critical imports work."""
84+
print("📥 Import Check")
85+
print("=" * 40)
86+
87+
import_tests = [
88+
("sqlite3", "import sqlite3"),
89+
("json", "import json"),
90+
("tempfile", "import tempfile"),
91+
("patterns.singleton", "from patterns.singleton import singleton"),
92+
("patterns.repository", "from patterns.repository import User, SqliteUserRepository, JsonFileUserRepository"),
93+
("patterns.factory", "from patterns.factory import ShapeFactory"),
94+
("patterns.observer", "from patterns.observer import WeatherStation"),
95+
]
96+
97+
for name, import_stmt in import_tests:
98+
try:
99+
exec(import_stmt)
100+
print(f"✅ {name} imports successfully")
101+
except Exception as e:
102+
print(f"❌ {name} import failed: {e}")
103+
traceback.print_exc()
104+
105+
print()
106+
107+
108+
def check_repository_functionality():
109+
"""Check repository pattern functionality in detail."""
110+
print("🗄️ Repository Functionality Check")
111+
print("=" * 40)
112+
113+
try:
114+
# Import required modules
115+
from patterns.repository import User, SqliteUserRepository, JsonFileUserRepository
116+
117+
# Test SQLite repository
118+
print("Testing SQLite repository...")
119+
sqlite_repo = SqliteUserRepository(':memory:')
120+
test_user = User(id=None, name='Test User', email='test@example.com')
121+
saved_user = sqlite_repo.save(test_user)
122+
123+
print(f"✅ SQLite: User saved with ID {saved_user.id}")
124+
125+
found_user = sqlite_repo.find_by_id(saved_user.id)
126+
if found_user:
127+
print(f"✅ SQLite: User found - {found_user.name}")
128+
else:
129+
print("❌ SQLite: User not found")
130+
131+
# Test JSON file repository with new file
132+
print("Testing JSON file repository (new file)...")
133+
import tempfile
134+
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
135+
temp_path = f.name
136+
137+
try:
138+
json_repo = JsonFileUserRepository(temp_path)
139+
print(f"✅ JSON: Repository created, users count: {len(json_repo._users)}")
140+
141+
saved_user2 = json_repo.save(test_user)
142+
print(f"✅ JSON: User saved with ID {saved_user2.id}")
143+
144+
finally:
145+
os.unlink(temp_path)
146+
147+
# Test JSON file repository with existing file
148+
print("Testing JSON file repository (existing file)...")
149+
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
150+
test_data = {
151+
'users': [
152+
{
153+
'id': 1,
154+
'name': 'Alice',
155+
'email': 'alice@example.com',
156+
'created_at': '2023-01-01T12:00:00',
157+
}
158+
],
159+
'next_id': 2,
160+
}
161+
json.dump(test_data, f)
162+
temp_path2 = f.name
163+
164+
try:
165+
json_repo2 = JsonFileUserRepository(temp_path2)
166+
print(f"✅ JSON existing: Repository loaded, users count: {len(json_repo2._users)}")
167+
print(f"✅ JSON existing: Next ID: {json_repo2._next_id}")
168+
print(f"✅ JSON existing: First user: {json_repo2._users[0].name}")
169+
170+
finally:
171+
os.unlink(temp_path2)
172+
173+
# Test corrupted file handling
174+
print("Testing corrupted file handling...")
175+
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
176+
f.write('invalid json content')
177+
temp_path3 = f.name
178+
179+
try:
180+
json_repo3 = JsonFileUserRepository(temp_path3)
181+
print(f"✅ Corrupted: Repository created, users count: {len(json_repo3._users)}")
182+
183+
finally:
184+
os.unlink(temp_path3)
185+
186+
print("✅ All repository functionality tests passed")
187+
188+
except Exception as e:
189+
print(f"❌ Repository functionality test failed: {e}")
190+
traceback.print_exc()
191+
192+
print()
193+
194+
195+
def check_notebook_validation():
196+
"""Check notebook validation."""
197+
print("📓 Notebook Validation Check")
198+
print("=" * 40)
199+
200+
notebooks_dir = Path("notebooks")
201+
if not notebooks_dir.exists():
202+
print("❌ Notebooks directory does not exist")
203+
return
204+
205+
notebooks = list(notebooks_dir.glob("*.ipynb"))
206+
print(f"Found {len(notebooks)} notebooks")
207+
208+
for notebook in notebooks:
209+
try:
210+
with open(notebook, 'r') as f:
211+
data = json.load(f)
212+
213+
# Check structure
214+
if 'cells' not in data or 'metadata' not in data:
215+
print(f"❌ {notebook.name}: Missing basic structure")
216+
continue
217+
218+
# Check for outputs
219+
has_outputs = any(
220+
cell.get('cell_type') == 'code' and cell.get('outputs')
221+
for cell in data.get('cells', [])
222+
)
223+
224+
if has_outputs:
225+
print(f"⚠️ {notebook.name}: Has outputs")
226+
else:
227+
print(f"✅ {notebook.name}: Clean (no outputs)")
228+
229+
except Exception as e:
230+
print(f"❌ {notebook.name}: Error - {e}")
231+
232+
print()
233+
234+
235+
def check_nbval_compatibility():
236+
"""Check nbval compatibility."""
237+
print("🔬 nbval Compatibility Check")
238+
print("=" * 40)
239+
240+
try:
241+
import nbval
242+
print(f"✅ nbval version: {nbval.__version__}")
243+
244+
# Try to run nbval on a notebook
245+
notebooks = list(Path("notebooks").glob("*.ipynb"))
246+
if notebooks:
247+
test_notebook = notebooks[0]
248+
print(f"Testing nbval on {test_notebook.name}...")
249+
250+
result = run_command(f"python3 -m pytest --nbval {test_notebook}")
251+
if result.returncode == 0:
252+
print("✅ nbval test passed")
253+
else:
254+
print(f"❌ nbval test failed: {result.stderr}")
255+
256+
except ImportError:
257+
print("❌ nbval not installed")
258+
except Exception as e:
259+
print(f"❌ nbval test error: {e}")
260+
261+
print()
262+
263+
264+
def main():
265+
"""Main debugging function."""
266+
print("🔍 CI Environment Debugging Script")
267+
print("=" * 50)
268+
print()
269+
270+
check_python_environment()
271+
check_file_system()
272+
check_imports()
273+
check_repository_functionality()
274+
check_notebook_validation()
275+
check_nbval_compatibility()
276+
277+
print("=" * 50)
278+
print("🏁 Debug script completed")
279+
280+
281+
if __name__ == "__main__":
282+
main()

0 commit comments

Comments
 (0)