Coverage for nexios\middlewares\errors\server_error_handler.py: 30%

151 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-21 20:31 +0100

1from nexios.middlewares.base import BaseMiddleware 

2from nexios.http import Request, Response 

3from nexios.config import get_config 

4import traceback, html, sys, inspect, typing, platform, json, datetime, uuid, os 

5from nexios.logging import DEBUG, create_logger 

6from nexios.__main__ import __version__ as nexios_version 

7 

8logger = create_logger(__name__, log_level=DEBUG) 

9STYLES = """ 

10 

11:root { 

12 --primary: #10b981; 

13 --primary-dark: #059669; 

14 --primary-light: #d1fae5; 

15 --secondary: #14b8a6; 

16 --background: #0f172a; 

17 --surface: #1e293b; 

18 --surface-light: #334155; 

19 --error: #ef4444; 

20 --text: #f1f5f9; 

21 --text-secondary: #94a3b8; 

22 --text-tertiary: #64748b; 

23 --border: #334155; 

24 --code-bg: #1e293b; 

25 --code-fg: #a5f3fc; 

26 --highlight: #eab308; 

27 --highlight-bg: rgba(234, 179, 8, 0.1); 

28} 

29 

30* { 

31 box-sizing: border-box; 

32 margin: 0; 

33 padding: 0; 

34} 

35 

36body { 

37 font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; 

38 background-color: var(--background); 

39 color: var(--text); 

40 margin: 0; 

41 padding: 0; 

42 line-height: 1.6; 

43 font-size: 15px; 

44} 

45 

46h1, h2, h3, h4, h5, h6 { 

47 font-weight: 600; 

48 line-height: 1.4; 

49} 

50 

51h1 { 

52 color: var(--primary); 

53 font-size: 24px; 

54 margin-bottom: 4px; 

55} 

56 

57h2 { 

58 color: var(--text); 

59 font-size: 18px; 

60 margin-top: 4px; 

61 margin-bottom: 16px; 

62 font-weight: 500; 

63} 

64 

65h3 { 

66 color: var(--primary); 

67 font-size: 16px; 

68 margin-top: 16px; 

69 margin-bottom: 10px; 

70 border-bottom: 1px solid var(--border); 

71 padding-bottom: 5px; 

72} 

73 

74.container { 

75 max-width: 1200px; 

76 margin: 0 auto; 

77 padding: 24px 16px; 

78} 

79 

80.section { 

81 margin-bottom: 24px; 

82 border: 1px solid var(--border); 

83 background: var(--surface); 

84 border-radius: 8px; 

85 overflow: hidden; 

86 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 

87} 

88 

89.section-title { 

90 background-color: var(--primary); 

91 color: var(--background); 

92 padding: 12px 16px; 

93 font-size: 16px; 

94 font-weight: 600; 

95 display: flex; 

96 justify-content: space-between; 

97 align-items: center; 

98} 

99 

100.section-content { 

101 padding: 16px; 

102} 

103 

104.traceback-container { 

105 background: var(--surface); 

106 border-radius: 8px; 

107 overflow: hidden; 

108} 

109 

110.traceback-title { 

111 background-color: var(--primary); 

112 color: var(--background); 

113 padding: 12px 16px; 

114 font-size: 16px; 

115 font-weight: 600; 

116 display: flex; 

117 justify-content: space-between; 

118 align-items: center; 

119} 

120 

121.frame-line { 

122 padding-left: 12px; 

123 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; 

124 color: var(--text); 

125} 

126 

127.frame-filename { 

128 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; 

129 font-weight: 600; 

130 color: var(--primary); 

131} 

132 

133.center-line { 

134 background-color: var(--primary-dark); 

135 color: white; 

136 padding: 6px 12px; 

137 font-weight: 600; 

138 border-radius: 4px; 

139} 

140 

141.lineno { 

142 margin-right: 8px; 

143 color: var(--text-tertiary); 

144 user-select: none; 

145} 

146 

147.frame-title { 

148 font-weight: 500; 

149 padding: 12px 16px; 

150 background-color: var(--surface-light); 

151 color: var(--text); 

152 font-size: 14px; 

153 border-radius: 4px; 

154 margin-bottom: 8px; 

155 display: flex; 

156 justify-content: space-between; 

157 align-items: center; 

158 border-left: 4px solid var(--primary); 

159} 

160 

161.collapse-btn { 

162 background: var(--primary); 

163 color: var(--background); 

164 border: none; 

165 width: 24px; 

166 height: 24px; 

167 font-size: 14px; 

168 cursor: pointer; 

169 border-radius: 4px; 

170 display: flex; 

171 align-items: center; 

172 justify-content: center; 

173 margin-left: 10px; 

174 transition: background-color 0.2s; 

175} 

176 

177.collapse-btn:hover { 

178 backgroun 

179""" 

180 

181JS = """ 

182<script type="text/javascript"> 

183 function collapse(element){ 

184 const targetId = element.getAttribute("data-target-id"); 

185 const target = document.getElementById(targetId); 

186 

187 if (target.classList.contains("collapsed")){ 

188 element.innerHTML = "&#8210;"; // Minus symbol 

189 target.classList.remove("collapsed"); 

190 } else { 

191 element.innerHTML = "+"; // Plus symbol 

192 target.classList.add("collapsed"); 

193 } 

194 } 

195 

196 function toggleSection(sectionId) { 

197 const section = document.getElementById(sectionId); 

198 const button = document.querySelector(`[data-section="${sectionId}"]`); 

199  

200 if (section.classList.contains("collapsed")) { 

201 section.classList.remove("collapsed"); 

202 button.innerHTML = "&#8210;"; // Minus symbol 

203 } else { 

204 section.classList.add("collapsed"); 

205 button.innerHTML = "+"; // Plus symbol 

206 } 

207 } 

208 

209 document.addEventListener('DOMContentLoaded', function() { 

210 // Initialize all sections as expanded 

211 const sections = document.querySelectorAll('.section-content'); 

212 sections.forEach(section => { 

213 if (section.id !== 'traceback-section') { 

214 section.classList.add('collapsed'); 

215 const button = document.querySelector(`[data-section="${section.id}"]`); 

216 if (button) button.innerHTML = "+"; 

217 } 

218 }); 

219 }); 

220</script> 

221""" 

222TEMPLATE = """ 

223<!DOCTYPE html> 

224<html lang="en"> 

225 <head> 

226 <meta charset="UTF-8"> 

227 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 

228 <style type='text/css'> 

229 {styles} 

230 </style> 

231 <title>Nexios Debug - {error_type}</title> 

232 </head> 

233 <body> 

234 <div class="container"> 

235 <h1>Server Error</h1> 

236 <h1>Nexios Debug - {error_type}</h1> 

237 <!-- Traceback Section --> 

238 

239 <div class="section"> 

240 <div class="section-title"> 

241 <span>Request Information</span> 

242 <button class="collapse-btn" data-section="request-section" onclick="toggleSection('request-section')">+</button> 

243 </div> 

244 <div id="request-section" class="section-content"> 

245 {request_info} 

246 </div> 

247 </div> 

248  

249 <div class="section"> 

250 <div class="section-title"> 

251 <span>Traceback</span> 

252 <button class="collapse-btn" data-section="traceback-section" onclick="toggleSection('traceback-section')">&#8210;</button> 

253 </div> 

254 <div id="traceback-section" class="section-content"> 

255 <div>{exc_html}</div> 

256 </div> 

257 </div> 

258 

259 <!-- Request Information Section --> 

260  

261 

262 <!-- System Information Section --> 

263 <div class="section"> 

264 <div class="section-title"> 

265 <span>System Information</span> 

266 <button class="collapse-btn" data-section="system-section" onclick="toggleSection('system-section')">+</button> 

267 </div> 

268 <div id="system-section" class="section-content"> 

269 {system_info} 

270 </div> 

271 </div> 

272 

273 <!-- Suggestions Section --> 

274 <div class="section"> 

275 <div class="section-title"> 

276 <span>Debugging Suggestions</span> 

277 <button class="collapse-btn" data-section="suggestions-section" onclick="toggleSection('suggestions-section')">+</button> 

278 </div> 

279 <div id="suggestions-section" class="section-content"> 

280 {debugging_suggestions} 

281 </div> 

282 </div> 

283 

284 <!-- JSON Data Section --> 

285 <div class="section"> 

286 <div class="section-title"> 

287 <span>Error JSON Data</span> 

288 <button class="collapse-btn" data-section="json-section" onclick="toggleSection('json-section')">+</button> 

289 </div> 

290 <div id="json-section" class="section-content"> 

291 <div class="code-box"> 

292 <div class="code-header">Error Data (JSON)</div> 

293 <pre class="code-content">{error_json}</pre> 

294 </div> 

295 </div> 

296 </div> 

297 </div> 

298 {js} 

299 </body> 

300</html> 

301""" 

302FRAME_TEMPLATE = """ 

303<div> 

304 <p class="frame-title"> 

305 File <span class="frame-filename">{frame_filename}</span>, 

306 line <i>{frame_lineno}</i>, 

307 in <b>{frame_name}</b> 

308 <button class="collapse-btn" data-target-id="{frame_filename}-{frame_lineno}" onclick="collapse(this)"> 

309 {collapse_button} 

310 </button> 

311 </p> 

312 <div id="{frame_filename}-{frame_lineno}" class="source-code {collapsed}">{code_context}</div> 

313 {locals_html} 

314</div> 

315""" 

316 

317LINE = """ 

318<p><span class="frame-line"> 

319<span class="lineno">{lineno}.</span> {line}</span></p> 

320""" 

321 

322CENTER_LINE = """ 

323<p class="center-line"><span class="frame-line"> 

324<span class="lineno">{lineno}.</span> {line}</span></p> 

325""" 

326 

327 

328ServerErrHandlerType = typing.Callable[[Request, Response, Exception], typing.Any] 

329 

330 

331class ServerErrorMiddleware(BaseMiddleware): 

332 def __init__(self, handler: typing.Optional[ServerErrHandlerType] = None): 

333 self.handler = handler 

334 

335 async def __call__( 

336 self, 

337 request: Request, 

338 response: Response, 

339 next_middleware: typing.Callable[[], typing.Awaitable[Response]], 

340 ) -> typing.Any: 

341 # Store the current request for error context 

342 self.current_request = request 

343 # Get debug mode from config 

344 self.debug = get_config().debug or True 

345 

346 try: 

347 return await next_middleware() 

348 except Exception as exc: 

349 if self.handler: 

350 response = await self.handler(request, response, exc) 

351 if self.debug: 

352 response = self.get_debug_response(request, response, exc) 

353 else: 

354 response = self.error_response(response) 

355 

356 err = traceback.format_exc() 

357 logger.error(err) 

358 return response 

359 

360 def error_response(self, res: Response): 

361 return res.text("Internal Server Error", status_code=500) 

362 

363 def get_debug_response( 

364 self, request: Request, response: Response, exc: Exception 

365 ) -> Response: 

366 accept = request.headers.get("accept", "") 

367 if "text/html" in accept: 

368 content: str = self.generate_html(exc) 

369 return response.html(content, status_code=500) 

370 content = self.generate_plain_text(exc) 

371 return response.text(content, status_code=500) 

372 

373 def format_line( 

374 self, index: int, line: str, frame_lineno: int, frame_index: int 

375 ) -> str: 

376 values: typing.Dict[str, typing.Any] = { 

377 # HTML escape - line could contain < or > 

378 "line": html.escape(line).replace(" ", "&nbsp"), 

379 "lineno": (frame_lineno - frame_index) + index, 

380 } 

381 

382 if index != frame_index: 

383 return LINE.format(**values) 

384 return CENTER_LINE.format(**values) 

385 

386 def _format_locals(self, frame_locals: typing.Dict[str, typing.Any]) -> str: 

387 """Format local variables for display in the error template.""" 

388 if not frame_locals: 

389 return "" 

390 

391 locals_html = "<div class='stack-locals'><h4>Local Variables:</h4>\n" 

392 for var_name, var_value in frame_locals.items(): 

393 try: 

394 # Skip internal variables 

395 if var_name.startswith("__") and var_name.endswith("__"): 

396 continue 

397 

398 # Format value safely 

399 value_str = html.escape(repr(var_value)) 

400 if len(value_str) > 500: # Truncate long values 

401 value_str = value_str[:500] + "..." 

402 

403 locals_html += f"<div><span style='color: #f39c12;'>{html.escape(var_name)}</span> = {value_str}</div>\n" 

404 except Exception: 

405 locals_html += f"<div><span style='color: #f39c12;'>{html.escape(var_name)}</span> = <error displaying value></div>\n" 

406 

407 locals_html += "</div>" 

408 return locals_html 

409 

410 def generate_frame_html(self, frame: inspect.FrameInfo, is_collapsed: bool) -> str: 

411 code_context: str = "".join( # type:ignore 

412 self.format_line( 

413 index, 

414 line, 

415 frame.lineno, 

416 frame.index, # type:ignore 

417 ) 

418 for index, line in enumerate(frame.code_context or []) # type:ignore 

419 ) 

420 

421 # Format local variables if available 

422 locals_html = ( 

423 self._format_locals(frame.frame.f_locals) if hasattr(frame, "frame") else "" 

424 ) 

425 

426 values: typing.Dict[str, typing.Any] = { 

427 "frame_filename": html.escape(frame.filename), 

428 "frame_lineno": frame.lineno, 

429 "frame_name": html.escape(frame.function), 

430 "code_context": code_context, 

431 "collapsed": "collapsed" if is_collapsed else "", 

432 "collapse_button": "+" if is_collapsed else "&#8210;", 

433 "locals_html": locals_html, 

434 } 

435 return FRAME_TEMPLATE.format(**values) 

436 

437 def generate_plain_text(self, exc: Exception) -> str: 

438 return "".join(traceback.format_exception(type(exc), exc, exc.__traceback__)) 

439 

440 def _format_request_info(self, request: Request) -> str: 

441 """Format request information for display in the error template.""" 

442 method = request.method 

443 url = str(request.url) 

444 

445 # General request info 

446 _html = f""" 

447 <div class="info-grid"> 

448 <div class="info-block"> 

449 <h3>Request Details</h3> 

450 <div class="info-item"> 

451 <div class="info-label">Method:</div> 

452 <div class="info-value">{html.escape(method)}</div> 

453 </div> 

454 <div class="info-item"> 

455 <div class="info-label">URL:</div> 

456 <div class="info-value">{html.escape(url)}</div> 

457 </div> 

458 <div class="info-item"> 

459 <div class="info-label">Path:</div> 

460 <div class="info-value">{html.escape(request.path)}</div> 

461 </div> 

462  

463 </div> 

464  

465 <div class="info-block"> 

466 <h3>Headers</h3> 

467 <table class="key-value-table"> 

468 """ 

469 

470 # Add headers 

471 for name, value in request.headers.items(): 

472 _html += f""" 

473 <tr> 

474 <td>{html.escape(name)}</td> 

475 <td>{html.escape(value)}</td> 

476 </tr> 

477 """ 

478 

479 _html += """ 

480 </table> 

481 </div> 

482 </div> 

483 """ 

484 

485 # Add query parameters if available 

486 if hasattr(request, "query_params") and request.query_params: 

487 _html += """ 

488 <div class="info-block"> 

489 <h3>Query Parameters</h3> 

490 <table class="key-value-table"> 

491 """ 

492 

493 for name, value in request.query_params.items(): 

494 _html += f""" 

495 <tr> 

496 <td>{html.escape(name)}</td> 

497 <td>{html.escape(str(value))}</td> 

498 </tr> 

499 """ 

500 

501 _html += """ 

502 </table> 

503 </div> 

504 """ 

505 

506 return _html 

507 

508 def _format_system_info(self) -> str: 

509 """Format system information for display in the error template.""" 

510 python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" 

511 

512 _html = f""" 

513 <div class="info-grid"> 

514 <div class="info-block"> 

515 <h3>Nexios</h3> 

516 <div class="info-item"> 

517 <div class="info-label">Nexios Version:</div> 

518 <div class="info-value">{html.escape(nexios_version)}</div>  

519 </div> 

520 <div class="info-item"> 

521 <div class="info-label">Debug Mode:</div> 

522 <div class="info-value">{self.debug}</div> 

523 </div> 

524 </div> 

525  

526 <div class="info-block"> 

527 <h3>Python</h3> 

528 <div class="info-item"> 

529 <div class="info-label">Python Version:</div> 

530 <div class="info-value">{html.escape(python_version)}</div> 

531 </div> 

532 <div class="info-item"> 

533 <div class="info-label">Python Path:</div> 

534 <div class="info-value">{html.escape(sys.executable)}</div> 

535 </div> 

536 <div class="info-item"> 

537 <div class="info-label">Python Implementation:</div> 

538 <div class="info-value">{html.escape(platform.python_implementation())}</div> 

539 </div> 

540 </div> 

541 </div> 

542  

543 <div class="info-grid"> 

544 <div class="info-block"> 

545 <h3>System</h3> 

546 <div class="info-item"> 

547 <div class="info-label">Platform:</div> 

548 <div class="info-value">{html.escape(platform.platform())}</div> 

549 </div> 

550 <div class="info-item"> 

551 <div class="info-label">OS:</div> 

552 <div class="info-value">{html.escape(platform.system())} {html.escape(platform.release())}</div> 

553 </div> 

554 <div class="info-item"> 

555 <div class="info-label">Architecture:</div> 

556 <div class="info-value">{html.escape(platform.machine())}</div> 

557 </div> 

558 </div> 

559  

560 <div class="info-block"> 

561 <h3>Environment</h3> 

562 <div class="info-item"> 

563 <div class="info-label">Process ID:</div> 

564 <div class="info-value">{os.getpid()}</div> 

565 </div> 

566 <div class="info-item"> 

567 <div class="info-label">Current Directory:</div> 

568 <div class="info-value">{html.escape(os.getcwd())}</div> 

569 </div> 

570 <div class="info-item"> 

571 <div class="info-label">Python Path:</div> 

572 <div class="info-value">{html.escape(str(sys.path))}</div> 

573 </div> 

574 </div> 

575 </div> 

576 """ 

577 

578 return _html 

579 

580 def _generate_error_json(self, exc: Exception, exc_type_str: str) -> str: 

581 """Generate a JSON representation of the error for debugging.""" 

582 error_data: typing.Dict[str, typing.Any] = { 

583 "error": { 

584 "type": exc_type_str, 

585 "message": str(exc), 

586 "traceback": traceback.format_exc(), 

587 }, 

588 "system": { 

589 "nexios_version": nexios_version, 

590 "python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}", 

591 "platform": platform.platform(), 

592 "debug_mode": self.debug, 

593 }, 

594 "timestamp": datetime.datetime.now().isoformat(), 

595 "error_id": str(uuid.uuid4()), 

596 } 

597 

598 try: 

599 return json.dumps(error_data, indent=2) 

600 except: 

601 # If JSON serialization fails, provide a simplified version 

602 return json.dumps( 

603 { 

604 "error": { 

605 "type": exc_type_str, 

606 "message": str(exc), 

607 "note": "Full error data could not be serialized to JSON", 

608 } 

609 }, 

610 indent=2, 

611 ) 

612 

613 def _generate_debugging_suggestions(self, exc: Exception, exc_type_str: str) -> str: 

614 """Generate debugging suggestions based on the error type.""" 

615 suggestions: typing.List[typing.Dict[str, str]] = [] 

616 

617 # Common error types and suggestions 

618 if "ImportError" in exc_type_str or "ModuleNotFoundError" in exc_type_str: 

619 suggestions.append( 

620 { 

621 "title": "Missing Module", 

622 "text": "This error typically occurs when Python cannot find a required module. Check that all dependencies are installed correctly. Try running 'pip install -r requirements.txt'.", 

623 } 

624 ) 

625 

626 elif "SyntaxError" in exc_type_str: 

627 suggestions.append( 

628 { 

629 "title": "Syntax Error", 

630 "text": "There's a syntax error in your code. Check the line indicated in the traceback for mismatched parentheses, missing colons, or incorrect indentation.", 

631 } 

632 ) 

633 

634 elif "AttributeError" in exc_type_str: 

635 suggestions.append( 

636 { 

637 "title": "Attribute Error", 

638 "text": "You're trying to access an attribute or method that doesn't exist on the object. Check for typos or make sure the object is of the expected type before accessing its attributes.", 

639 } 

640 ) 

641 

642 elif "KeyError" in exc_type_str: 

643 suggestions.append( 

644 { 

645 "title": "Key Error", 

646 "text": "You're trying to access a dictionary key that doesn't exist. Make sure the key exists before trying to access it, or use dictionary.get(key) method with a default value.", 

647 } 

648 ) 

649 

650 elif "NameError" in exc_type_str: 

651 suggestions.append( 

652 { 

653 "title": "Name Error", 

654 "text": "You're trying to use a variable that hasn't been defined. Check for typos or make sure to define the variable before using it.", 

655 } 

656 ) 

657 

658 elif "TypeError" in exc_type_str: 

659 suggestions.append( 

660 { 

661 "title": "Type Error", 

662 "text": "An operation is being performed on an object of an inappropriate type. Check the types of your variables and make sure they match what the operation expects.", 

663 } 

664 ) 

665 

666 elif "ValueError" in exc_type_str: 

667 suggestions.append( 

668 { 

669 "title": "Value Error", 

670 "text": "An operation is receiving an argument with the right type but an inappropriate value. Check the value of the arguments you're passing to functions.", 

671 } 

672 ) 

673 

674 elif "IndexError" in exc_type_str: 

675 suggestions.append( 

676 { 

677 "title": "Index Error", 

678 "text": "You're trying to access an index that's out of range. Make sure the index is valid before accessing it, or use a try/except block to handle the error.", 

679 } 

680 ) 

681 

682 elif "FileNotFoundError" in exc_type_str: 

683 suggestions.append( 

684 { 

685 "title": "File Not Found", 

686 "text": "The system cannot find the file specified. Check the file path and make sure the file exists.", 

687 } 

688 ) 

689 

690 elif "PermissionError" in exc_type_str: 

691 suggestions.append( 

692 { 

693 "title": "Permission Error", 

694 "text": "You don't have permission to access the specified file or directory. Check the file permissions or run the application with higher privileges.", 

695 } 

696 ) 

697 

698 # Add a general debugging strategy for all errors 

699 suggestions.append( 

700 { 

701 "title": "General Debugging Steps", 

702 "text": "1. Check the traceback to find where the error occurred.<br>2. Review the variables at that point using the local variables section.<br>3. Add logging statements around the error to track variable values.<br>4. Use a debugger to step through the code execution.", 

703 } 

704 ) 

705 

706 # Format the suggestions as HTML 

707 _html = "" 

708 for suggestion in suggestions: 

709 _html += f""" 

710 <div class="suggestion"> 

711 <div class="suggestion-title">{html.escape(suggestion["title"])}</div> 

712 <div>{suggestion["text"]}</div> 

713 </div> 

714 """ 

715 

716 return _html 

717 

718 def generate_html(self, exc: Exception, limit: int = 7) -> str: 

719 """Generate an enhanced HTML page for displaying error information.""" 

720 # Generate a unique error ID for tracking 

721 error_id = str(uuid.uuid4()) 

722 timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 

723 

724 # Get exception information 

725 traceback_obj = traceback.TracebackException.from_exception( 

726 exc, capture_locals=True 

727 ) 

728 

729 # Get exception type name 

730 if sys.version_info >= (3, 13): 

731 exc_type_str = traceback_obj.exc_type_str 

732 else: 

733 exc_type_str = traceback_obj.exc_type.__name__ 

734 

735 # Format the error message 

736 error = f"{html.escape(exc_type_str)}: {html.escape(str(traceback_obj))}" 

737 

738 # Generate traceback HTML 

739 exc_html = "" 

740 is_collapsed = False 

741 exc_traceback = exc.__traceback__ 

742 if exc_traceback is not None: 

743 frames = inspect.getinnerframes(exc_traceback, limit) 

744 for frame in reversed(frames): 

745 exc_html += self.generate_frame_html(frame, is_collapsed) 

746 is_collapsed = True 

747 

748 # Get request information if available 

749 try: 

750 request_info = self._format_request_info(self.current_request) 

751 except Exception as e: 

752 request_info = f"<div class='info-block'><h3>Error retrieving request information</h3><p>{html.escape(str(e))}</p></div>" 

753 

754 # Get system information 

755 try: 

756 system_info = self._format_system_info() 

757 except Exception as e: 

758 system_info = f"<div class='info-block'><h3>Error retrieving system information</h3><p>{html.escape(str(e))}</p></div>" 

759 

760 # Generate debugging suggestions 

761 try: 

762 debugging_suggestions = self._generate_debugging_suggestions( 

763 exc, exc_type_str 

764 ) 

765 except Exception as e: 

766 debugging_suggestions = f"<div class='info-block'><h3>Error generating debugging suggestions</h3><p>{html.escape(str(e))}</p></div>" 

767 

768 # Generate JSON representation of the error 

769 try: 

770 error_json = html.escape(self._generate_error_json(exc, exc_type_str)) 

771 except Exception as e: 

772 error_json = html.escape(f"Error generating JSON data: {str(e)}") 

773 

774 # Put everything together in the template 

775 return TEMPLATE.format( 

776 styles=STYLES, 

777 js=JS, 

778 error=error, 

779 error_type=html.escape(exc_type_str), 

780 error_id=error_id, 

781 timestamp=timestamp, 

782 exc_html=exc_html, 

783 request_info=request_info, 

784 system_info=system_info, 

785 debugging_suggestions=debugging_suggestions, 

786 error_json=error_json, 

787 )