fishertools 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. fishertools/__init__.py +82 -0
  2. fishertools/config/__init__.py +24 -0
  3. fishertools/config/manager.py +247 -0
  4. fishertools/config/models.py +96 -0
  5. fishertools/config/parser.py +265 -0
  6. fishertools/decorators.py +93 -0
  7. fishertools/documentation/__init__.py +38 -0
  8. fishertools/documentation/api.py +242 -0
  9. fishertools/documentation/generator.py +502 -0
  10. fishertools/documentation/models.py +126 -0
  11. fishertools/documentation/visual.py +583 -0
  12. fishertools/errors/__init__.py +29 -0
  13. fishertools/errors/exceptions.py +191 -0
  14. fishertools/errors/explainer.py +303 -0
  15. fishertools/errors/formatters.py +386 -0
  16. fishertools/errors/models.py +228 -0
  17. fishertools/errors/patterns.py +119 -0
  18. fishertools/errors/recovery.py +467 -0
  19. fishertools/examples/__init__.py +22 -0
  20. fishertools/examples/models.py +118 -0
  21. fishertools/examples/repository.py +770 -0
  22. fishertools/helpers.py +116 -0
  23. fishertools/integration.py +451 -0
  24. fishertools/learn/__init__.py +18 -0
  25. fishertools/learn/examples.py +550 -0
  26. fishertools/learn/tips.py +281 -0
  27. fishertools/learning/__init__.py +32 -0
  28. fishertools/learning/core.py +349 -0
  29. fishertools/learning/models.py +112 -0
  30. fishertools/learning/progress.py +314 -0
  31. fishertools/learning/session.py +500 -0
  32. fishertools/learning/tutorial.py +626 -0
  33. fishertools/legacy/__init__.py +76 -0
  34. fishertools/legacy/deprecated.py +261 -0
  35. fishertools/legacy/deprecation.py +149 -0
  36. fishertools/safe/__init__.py +16 -0
  37. fishertools/safe/collections.py +242 -0
  38. fishertools/safe/files.py +240 -0
  39. fishertools/safe/strings.py +15 -0
  40. fishertools/utils.py +57 -0
  41. fishertools-0.2.1.dist-info/METADATA +256 -0
  42. fishertools-0.2.1.dist-info/RECORD +81 -0
  43. fishertools-0.2.1.dist-info/WHEEL +5 -0
  44. fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
  45. fishertools-0.2.1.dist-info/top_level.txt +2 -0
  46. tests/__init__.py +6 -0
  47. tests/conftest.py +25 -0
  48. tests/test_config/__init__.py +3 -0
  49. tests/test_config/test_basic_config.py +57 -0
  50. tests/test_config/test_config_error_handling.py +287 -0
  51. tests/test_config/test_config_properties.py +435 -0
  52. tests/test_documentation/__init__.py +3 -0
  53. tests/test_documentation/test_documentation_properties.py +253 -0
  54. tests/test_documentation/test_visual_documentation_properties.py +444 -0
  55. tests/test_errors/__init__.py +3 -0
  56. tests/test_errors/test_api.py +301 -0
  57. tests/test_errors/test_error_handling.py +354 -0
  58. tests/test_errors/test_explainer.py +173 -0
  59. tests/test_errors/test_formatters.py +338 -0
  60. tests/test_errors/test_models.py +248 -0
  61. tests/test_errors/test_patterns.py +270 -0
  62. tests/test_examples/__init__.py +3 -0
  63. tests/test_examples/test_example_repository_properties.py +204 -0
  64. tests/test_examples/test_specific_examples.py +303 -0
  65. tests/test_integration.py +298 -0
  66. tests/test_integration_enhancements.py +462 -0
  67. tests/test_learn/__init__.py +3 -0
  68. tests/test_learn/test_examples.py +221 -0
  69. tests/test_learn/test_tips.py +285 -0
  70. tests/test_learning/__init__.py +3 -0
  71. tests/test_learning/test_interactive_learning_properties.py +337 -0
  72. tests/test_learning/test_learning_system_properties.py +194 -0
  73. tests/test_learning/test_progress_tracking_properties.py +279 -0
  74. tests/test_legacy/__init__.py +3 -0
  75. tests/test_legacy/test_backward_compatibility.py +236 -0
  76. tests/test_legacy/test_deprecation_warnings.py +208 -0
  77. tests/test_safe/__init__.py +3 -0
  78. tests/test_safe/test_collections_properties.py +189 -0
  79. tests/test_safe/test_files.py +104 -0
  80. tests/test_structure.py +58 -0
  81. tests/test_structure_enhancements.py +115 -0
@@ -0,0 +1,583 @@
1
+ """
2
+ Visual documentation generator with diagrams and charts.
3
+ """
4
+
5
+ from typing import List, Any, Optional
6
+ from .models import (
7
+ MermaidDiagram, FlowDiagram, Flowchart, StructureDiagram,
8
+ FunctionInfo, DiagramType
9
+ )
10
+
11
+
12
+ class VisualDocumentation:
13
+ """
14
+ Creates visual elements for documentation including diagrams and charts.
15
+
16
+ Generates architecture diagrams, data flow charts, and algorithm
17
+ flowcharts using Mermaid and other visualization tools.
18
+ """
19
+
20
+ def __init__(self, style: str = "modern"):
21
+ """
22
+ Initialize the visual documentation generator.
23
+
24
+ Args:
25
+ style: Visual style for diagrams ("modern", "classic", "minimal")
26
+ """
27
+ self.style = style
28
+
29
+ def create_architecture_diagram(self, modules: List[str]) -> MermaidDiagram:
30
+ """
31
+ Create an architecture diagram showing module relationships.
32
+
33
+ Args:
34
+ modules: List of module names to include
35
+
36
+ Returns:
37
+ MermaidDiagram: Generated architecture diagram
38
+ """
39
+ if not modules:
40
+ raise ValueError("At least one module must be provided")
41
+
42
+ # Generate Mermaid graph syntax for architecture
43
+ lines = ["graph TB"]
44
+
45
+ # Add nodes for each module
46
+ for i, module in enumerate(modules):
47
+ node_id = f"M{i}"
48
+ clean_name = module.replace(".", "_").replace("-", "_")
49
+ lines.append(f' {node_id}["{module}"]')
50
+
51
+ # Add relationships based on common patterns
52
+ for i, module in enumerate(modules):
53
+ node_id = f"M{i}"
54
+ # Connect related modules (simple heuristic based on naming)
55
+ for j, other_module in enumerate(modules):
56
+ if i != j:
57
+ other_id = f"M{j}"
58
+ # Connect if one module is a submodule of another
59
+ if module.startswith(other_module + ".") or other_module.startswith(module + "."):
60
+ lines.append(f" {other_id} --> {node_id}")
61
+
62
+ # Apply styling based on selected style
63
+ if self.style == "modern":
64
+ lines.extend([
65
+ " classDef default fill:#e1f5fe,stroke:#01579b,stroke-width:2px",
66
+ " classDef highlight fill:#f3e5f5,stroke:#4a148c,stroke-width:3px"
67
+ ])
68
+ elif self.style == "classic":
69
+ lines.extend([
70
+ " classDef default fill:#fff3e0,stroke:#e65100,stroke-width:2px",
71
+ " classDef highlight fill:#fce4ec,stroke:#880e4f,stroke-width:3px"
72
+ ])
73
+
74
+ content = "\n".join(lines)
75
+ return MermaidDiagram(
76
+ diagram_type=DiagramType.ARCHITECTURE,
77
+ content=content,
78
+ title="Module Architecture"
79
+ )
80
+
81
+ def generate_data_flow_diagram(self, function: FunctionInfo) -> FlowDiagram:
82
+ """
83
+ Generate a data flow diagram for a function.
84
+
85
+ Args:
86
+ function: Function information
87
+
88
+ Returns:
89
+ FlowDiagram: Generated data flow diagram
90
+ """
91
+ if not function.name:
92
+ raise ValueError("Function must have a name")
93
+
94
+ nodes = []
95
+ edges = []
96
+
97
+ # Input node
98
+ input_node = {
99
+ "id": "input",
100
+ "label": "Input Parameters",
101
+ "type": "input",
102
+ "details": ", ".join(function.parameters.keys()) if function.parameters else "None"
103
+ }
104
+ nodes.append(input_node)
105
+
106
+ # Function processing node
107
+ process_node = {
108
+ "id": "process",
109
+ "label": function.name,
110
+ "type": "process",
111
+ "details": function.docstring[:50] + "..." if function.docstring and len(function.docstring) > 50 else function.docstring or ""
112
+ }
113
+ nodes.append(process_node)
114
+
115
+ # Output node
116
+ output_node = {
117
+ "id": "output",
118
+ "label": "Return Value",
119
+ "type": "output",
120
+ "details": function.return_type or "Any"
121
+ }
122
+ nodes.append(output_node)
123
+
124
+ # Connect the nodes
125
+ edges.append({"from": "input", "to": "process", "label": "parameters"})
126
+ edges.append({"from": "process", "to": "output", "label": "result"})
127
+
128
+ # Add parameter-specific flows if we have detailed parameter info
129
+ if function.parameters:
130
+ for i, (param_name, param_type) in enumerate(function.parameters.items()):
131
+ param_id = f"param_{i}"
132
+ param_node = {
133
+ "id": param_id,
134
+ "label": param_name,
135
+ "type": "parameter",
136
+ "details": param_type
137
+ }
138
+ nodes.append(param_node)
139
+ edges.append({"from": param_id, "to": "process", "label": param_type})
140
+
141
+ return FlowDiagram(
142
+ nodes=nodes,
143
+ edges=edges,
144
+ title=f"Data Flow: {function.name}"
145
+ )
146
+
147
+ def create_algorithm_flowchart(self, code: str, title: Optional[str] = None) -> Flowchart:
148
+ """
149
+ Create a flowchart for an algorithm.
150
+
151
+ Args:
152
+ code: Python code to analyze
153
+ title: Optional title for the flowchart
154
+
155
+ Returns:
156
+ Flowchart: Generated algorithm flowchart
157
+ """
158
+ if not code.strip():
159
+ raise ValueError("Code cannot be empty")
160
+
161
+ steps = []
162
+ connections = []
163
+
164
+ # Simple code analysis - split by lines and identify key structures
165
+ lines = [line.strip() for line in code.split('\n') if line.strip()]
166
+
167
+ step_counter = 0
168
+
169
+ for i, line in enumerate(lines):
170
+ step_id = f"step_{step_counter}"
171
+
172
+ # Identify different types of statements
173
+ if line.startswith('def '):
174
+ step_type = "start"
175
+ description = f"Function: {line}"
176
+ elif line.startswith('if '):
177
+ step_type = "decision"
178
+ description = f"Decision: {line}"
179
+ elif line.startswith('elif '):
180
+ step_type = "decision"
181
+ description = f"Alternative: {line}"
182
+ elif line.startswith('else:'):
183
+ step_type = "decision"
184
+ description = "Else branch"
185
+ elif line.startswith('for ') or line.startswith('while '):
186
+ step_type = "loop"
187
+ description = f"Loop: {line}"
188
+ elif line.startswith('return '):
189
+ step_type = "end"
190
+ description = f"Return: {line}"
191
+ elif line.startswith('print(') or 'print(' in line:
192
+ step_type = "output"
193
+ description = f"Output: {line}"
194
+ else:
195
+ step_type = "process"
196
+ description = line
197
+
198
+ steps.append({
199
+ "id": step_id,
200
+ "type": step_type,
201
+ "description": description,
202
+ "line_number": i + 1
203
+ })
204
+
205
+ # Connect to previous step (simple linear flow)
206
+ if step_counter > 0:
207
+ connections.append({
208
+ "from": f"step_{step_counter - 1}",
209
+ "to": step_id,
210
+ "label": ""
211
+ })
212
+
213
+ step_counter += 1
214
+
215
+ # If no title provided, generate one
216
+ if title is None:
217
+ # Try to extract function name from first line
218
+ first_line = lines[0] if lines else ""
219
+ if first_line.startswith('def '):
220
+ func_name = first_line.split('(')[0].replace('def ', '')
221
+ title = f"Algorithm: {func_name}"
222
+ else:
223
+ title = "Algorithm Flowchart"
224
+
225
+ return Flowchart(
226
+ steps=steps,
227
+ connections=connections,
228
+ title=title
229
+ )
230
+
231
+ def visualize_data_structure(self, data: Any, title: Optional[str] = None) -> StructureDiagram:
232
+ """
233
+ Create a visual representation of a data structure.
234
+
235
+ Args:
236
+ data: Data structure to visualize
237
+ title: Optional title for the diagram
238
+
239
+ Returns:
240
+ StructureDiagram: Generated structure diagram
241
+ """
242
+ if data is None:
243
+ structure_type = "None"
244
+ visualization = "null"
245
+ elif isinstance(data, (list, tuple)):
246
+ structure_type = "list" if isinstance(data, list) else "tuple"
247
+ # Create a simple text-based visualization
248
+ if len(data) <= 10:
249
+ items = [str(item) for item in data]
250
+ visualization = f"[{', '.join(items)}]"
251
+ else:
252
+ items = [str(item) for item in data[:5]]
253
+ visualization = f"[{', '.join(items)}, ... ({len(data)} items total)]"
254
+ elif isinstance(data, dict):
255
+ structure_type = "dict"
256
+ if len(data) <= 5:
257
+ items = [f"'{k}': {repr(v)}" for k, v in data.items()]
258
+ visualization = f"{{{', '.join(items)}}}"
259
+ else:
260
+ items = [f"'{k}': {repr(v)}" for k, v in list(data.items())[:3]]
261
+ visualization = f"{{{', '.join(items)}, ... ({len(data)} keys total)}}"
262
+ elif isinstance(data, set):
263
+ structure_type = "set"
264
+ if len(data) <= 5:
265
+ items = [str(item) for item in data]
266
+ visualization = f"{{{', '.join(items)}}}"
267
+ else:
268
+ items = [str(item) for item in list(data)[:3]]
269
+ visualization = f"{{{', '.join(items)}, ... ({len(data)} items total)}}"
270
+ elif isinstance(data, str):
271
+ structure_type = "string"
272
+ if len(data) <= 50:
273
+ visualization = f'"{data}"'
274
+ else:
275
+ visualization = f'"{data[:47]}..." ({len(data)} chars)'
276
+ elif isinstance(data, (int, float, bool)):
277
+ structure_type = type(data).__name__
278
+ visualization = str(data)
279
+ else:
280
+ structure_type = type(data).__name__
281
+ visualization = f"<{structure_type} object>"
282
+
283
+ if title is None:
284
+ title = f"{structure_type.title()} Visualization"
285
+
286
+ return StructureDiagram(
287
+ structure_type=structure_type,
288
+ data=data,
289
+ visualization=visualization,
290
+ title=title
291
+ )
292
+
293
+ def create_example_visualization(self, code: str, result: Any) -> str:
294
+ """
295
+ Create visual representation of code example results.
296
+
297
+ Args:
298
+ code: Example code
299
+ result: Expected result
300
+
301
+ Returns:
302
+ str: HTML/SVG visualization of the example
303
+ """
304
+ if not code.strip():
305
+ raise ValueError("Code cannot be empty")
306
+
307
+ # Create a structured HTML visualization
308
+ html_parts = []
309
+
310
+ # Header
311
+ html_parts.append('<div class="example-visualization">')
312
+ html_parts.append('<div class="example-header">Code Example</div>')
313
+
314
+ # Code section
315
+ html_parts.append('<div class="code-section">')
316
+ html_parts.append('<pre class="code-block">')
317
+ html_parts.append(f'<code>{self._escape_html(code)}</code>')
318
+ html_parts.append('</pre>')
319
+ html_parts.append('</div>')
320
+
321
+ # Arrow
322
+ html_parts.append('<div class="arrow">↓</div>')
323
+
324
+ # Result section
325
+ html_parts.append('<div class="result-section">')
326
+ html_parts.append('<div class="result-label">Result:</div>')
327
+
328
+ # Visualize the result based on its type
329
+ result_viz = self.visualize_data_structure(result)
330
+ html_parts.append(f'<div class="result-content">{self._escape_html(result_viz.visualization)}</div>')
331
+ html_parts.append(f'<div class="result-type">Type: {result_viz.structure_type}</div>')
332
+ html_parts.append('</div>')
333
+
334
+ # Styling
335
+ html_parts.append(self._get_example_styles())
336
+
337
+ html_parts.append('</div>')
338
+
339
+ return '\n'.join(html_parts)
340
+
341
+ def _escape_html(self, text: str) -> str:
342
+ """Escape HTML special characters."""
343
+ return (text.replace('&', '&amp;')
344
+ .replace('<', '&lt;')
345
+ .replace('>', '&gt;')
346
+ .replace('"', '&quot;')
347
+ .replace("'", '&#x27;'))
348
+
349
+ def _get_example_styles(self) -> str:
350
+ """Get CSS styles for example visualization."""
351
+ if self.style == "modern":
352
+ return '''
353
+ <style>
354
+ .example-visualization {
355
+ border: 2px solid #01579b;
356
+ border-radius: 8px;
357
+ padding: 16px;
358
+ margin: 16px 0;
359
+ background: linear-gradient(135deg, #e1f5fe 0%, #f3e5f5 100%);
360
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
361
+ }
362
+ .example-header {
363
+ font-weight: bold;
364
+ color: #01579b;
365
+ margin-bottom: 12px;
366
+ font-size: 1.1em;
367
+ }
368
+ .code-section {
369
+ background: #263238;
370
+ border-radius: 4px;
371
+ padding: 12px;
372
+ margin: 8px 0;
373
+ }
374
+ .code-block {
375
+ color: #e8eaf6;
376
+ margin: 0;
377
+ font-family: 'Courier New', monospace;
378
+ }
379
+ .arrow {
380
+ text-align: center;
381
+ font-size: 1.5em;
382
+ color: #4a148c;
383
+ margin: 8px 0;
384
+ }
385
+ .result-section {
386
+ background: #f8f9fa;
387
+ border-radius: 4px;
388
+ padding: 12px;
389
+ border-left: 4px solid #4a148c;
390
+ }
391
+ .result-label {
392
+ font-weight: bold;
393
+ color: #4a148c;
394
+ margin-bottom: 8px;
395
+ }
396
+ .result-content {
397
+ font-family: 'Courier New', monospace;
398
+ background: #ffffff;
399
+ padding: 8px;
400
+ border-radius: 3px;
401
+ border: 1px solid #e0e0e0;
402
+ }
403
+ .result-type {
404
+ font-size: 0.9em;
405
+ color: #666;
406
+ margin-top: 4px;
407
+ font-style: italic;
408
+ }
409
+ </style>'''
410
+ elif self.style == "classic":
411
+ return '''
412
+ <style>
413
+ .example-visualization {
414
+ border: 1px solid #e65100;
415
+ padding: 12px;
416
+ margin: 12px 0;
417
+ background: #fff3e0;
418
+ font-family: serif;
419
+ }
420
+ .example-header {
421
+ font-weight: bold;
422
+ color: #e65100;
423
+ margin-bottom: 8px;
424
+ }
425
+ .code-section {
426
+ background: #f5f5f5;
427
+ padding: 8px;
428
+ margin: 6px 0;
429
+ border: 1px solid #ccc;
430
+ }
431
+ .code-block {
432
+ margin: 0;
433
+ font-family: monospace;
434
+ }
435
+ .arrow {
436
+ text-align: center;
437
+ font-size: 1.2em;
438
+ color: #880e4f;
439
+ margin: 6px 0;
440
+ }
441
+ .result-section {
442
+ background: #fce4ec;
443
+ padding: 8px;
444
+ border: 1px solid #880e4f;
445
+ }
446
+ .result-label {
447
+ font-weight: bold;
448
+ color: #880e4f;
449
+ margin-bottom: 6px;
450
+ }
451
+ .result-content {
452
+ font-family: monospace;
453
+ background: #ffffff;
454
+ padding: 6px;
455
+ border: 1px solid #ccc;
456
+ }
457
+ .result-type {
458
+ font-size: 0.85em;
459
+ color: #666;
460
+ margin-top: 3px;
461
+ }
462
+ </style>'''
463
+ else: # minimal
464
+ return '''
465
+ <style>
466
+ .example-visualization {
467
+ border: 1px solid #ccc;
468
+ padding: 8px;
469
+ margin: 8px 0;
470
+ background: #fafafa;
471
+ }
472
+ .example-header {
473
+ font-weight: bold;
474
+ margin-bottom: 6px;
475
+ }
476
+ .code-section {
477
+ background: #f0f0f0;
478
+ padding: 6px;
479
+ margin: 4px 0;
480
+ }
481
+ .code-block {
482
+ margin: 0;
483
+ font-family: monospace;
484
+ }
485
+ .arrow {
486
+ text-align: center;
487
+ margin: 4px 0;
488
+ }
489
+ .result-section {
490
+ background: #f9f9f9;
491
+ padding: 6px;
492
+ }
493
+ .result-label {
494
+ font-weight: bold;
495
+ margin-bottom: 4px;
496
+ }
497
+ .result-content {
498
+ font-family: monospace;
499
+ background: #ffffff;
500
+ padding: 4px;
501
+ border: 1px solid #ddd;
502
+ }
503
+ .result-type {
504
+ font-size: 0.8em;
505
+ color: #666;
506
+ margin-top: 2px;
507
+ }
508
+ </style>'''
509
+
510
+ def apply_consistent_styling(self, diagram: MermaidDiagram) -> MermaidDiagram:
511
+ """
512
+ Apply consistent styling to a diagram.
513
+
514
+ Args:
515
+ diagram: Diagram to style
516
+
517
+ Returns:
518
+ MermaidDiagram: Styled diagram
519
+ """
520
+ if not diagram.content:
521
+ raise ValueError("Diagram content cannot be empty")
522
+
523
+ # Apply styling based on diagram type and selected style
524
+ styled_content = diagram.content
525
+
526
+ # Add theme directive at the beginning if not present
527
+ if "%%{init:" not in styled_content:
528
+ if self.style == "modern":
529
+ theme_config = '''%%{init: {"theme": "base", "themeVariables": {
530
+ "primaryColor": "#e1f5fe",
531
+ "primaryTextColor": "#01579b",
532
+ "primaryBorderColor": "#01579b",
533
+ "lineColor": "#4a148c",
534
+ "secondaryColor": "#f3e5f5",
535
+ "tertiaryColor": "#fff"
536
+ }}}%%'''
537
+ elif self.style == "classic":
538
+ theme_config = '''%%{init: {"theme": "base", "themeVariables": {
539
+ "primaryColor": "#fff3e0",
540
+ "primaryTextColor": "#e65100",
541
+ "primaryBorderColor": "#e65100",
542
+ "lineColor": "#880e4f",
543
+ "secondaryColor": "#fce4ec",
544
+ "tertiaryColor": "#fff"
545
+ }}}%%'''
546
+ else: # minimal
547
+ theme_config = '''%%{init: {"theme": "base", "themeVariables": {
548
+ "primaryColor": "#f5f5f5",
549
+ "primaryTextColor": "#333",
550
+ "primaryBorderColor": "#666",
551
+ "lineColor": "#999",
552
+ "secondaryColor": "#fafafa",
553
+ "tertiaryColor": "#fff"
554
+ }}}%%'''
555
+
556
+ styled_content = theme_config + "\n" + styled_content
557
+
558
+ # Add diagram-specific styling
559
+ if diagram.diagram_type == DiagramType.ARCHITECTURE:
560
+ # Ensure architecture diagrams have proper node styling
561
+ if "classDef" not in styled_content:
562
+ if self.style == "modern":
563
+ styled_content += "\n classDef default fill:#e1f5fe,stroke:#01579b,stroke-width:2px"
564
+ elif self.style == "classic":
565
+ styled_content += "\n classDef default fill:#fff3e0,stroke:#e65100,stroke-width:2px"
566
+ else:
567
+ styled_content += "\n classDef default fill:#f5f5f5,stroke:#666,stroke-width:1px"
568
+
569
+ elif diagram.diagram_type == DiagramType.DATA_FLOW:
570
+ # Add flow-specific styling
571
+ if "linkStyle" not in styled_content:
572
+ if self.style == "modern":
573
+ styled_content += "\n linkStyle default stroke:#4a148c,stroke-width:2px"
574
+ elif self.style == "classic":
575
+ styled_content += "\n linkStyle default stroke:#880e4f,stroke-width:2px"
576
+ else:
577
+ styled_content += "\n linkStyle default stroke:#999,stroke-width:1px"
578
+
579
+ return MermaidDiagram(
580
+ diagram_type=diagram.diagram_type,
581
+ content=styled_content,
582
+ title=diagram.title
583
+ )
@@ -0,0 +1,29 @@
1
+ """
2
+ Error explanation system for fishertools.
3
+
4
+ This module provides tools to explain Python errors in simple, understandable terms
5
+ for beginners learning Python.
6
+ """
7
+
8
+ from .explainer import ErrorExplainer, explain_error
9
+ from .patterns import ErrorPattern
10
+ from .formatters import ConsoleFormatter, PlainFormatter, JsonFormatter, get_formatter
11
+ from .models import ErrorExplanation, ExplainerConfig
12
+ from .exceptions import (
13
+ FishertoolsError, ExplanationError, FormattingError,
14
+ ConfigurationError, PatternError, SafeUtilityError
15
+ )
16
+ from .recovery import (
17
+ ErrorRecoveryManager, ErrorSeverity, RecoveryStrategy, ErrorContext, RecoveryAction,
18
+ get_recovery_manager, handle_error_with_recovery, with_error_recovery
19
+ )
20
+
21
+ __all__ = [
22
+ "ErrorExplainer", "explain_error", "ErrorPattern",
23
+ "ConsoleFormatter", "PlainFormatter", "JsonFormatter", "get_formatter",
24
+ "ErrorExplanation", "ExplainerConfig",
25
+ "FishertoolsError", "ExplanationError", "FormattingError",
26
+ "ConfigurationError", "PatternError", "SafeUtilityError",
27
+ "ErrorRecoveryManager", "ErrorSeverity", "RecoveryStrategy", "ErrorContext", "RecoveryAction",
28
+ "get_recovery_manager", "handle_error_with_recovery", "with_error_recovery"
29
+ ]