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.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- 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('&', '&')
|
|
344
|
+
.replace('<', '<')
|
|
345
|
+
.replace('>', '>')
|
|
346
|
+
.replace('"', '"')
|
|
347
|
+
.replace("'", '''))
|
|
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
|
+
]
|