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,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration tests for fishertools.
|
|
3
|
+
|
|
4
|
+
These tests verify that all components work together correctly,
|
|
5
|
+
from exception handling to formatted output.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
import io
|
|
10
|
+
import sys
|
|
11
|
+
from contextlib import redirect_stdout
|
|
12
|
+
from unittest.mock import patch
|
|
13
|
+
|
|
14
|
+
import fishertools
|
|
15
|
+
from fishertools import explain_error
|
|
16
|
+
from fishertools.errors import ErrorExplainer, ErrorExplanation
|
|
17
|
+
from fishertools.errors.formatters import get_formatter
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestMainAPIIntegration:
|
|
21
|
+
"""Test the main API integration and complete workflow."""
|
|
22
|
+
|
|
23
|
+
def test_explain_error_complete_workflow(self):
|
|
24
|
+
"""Test complete workflow from exception to formatted output."""
|
|
25
|
+
# Create a test exception
|
|
26
|
+
test_exception = ValueError("invalid literal for int() with base 10: 'abc'")
|
|
27
|
+
|
|
28
|
+
# Capture output
|
|
29
|
+
output_buffer = io.StringIO()
|
|
30
|
+
|
|
31
|
+
with redirect_stdout(output_buffer):
|
|
32
|
+
explain_error(test_exception)
|
|
33
|
+
|
|
34
|
+
output = output_buffer.getvalue()
|
|
35
|
+
|
|
36
|
+
# Verify output contains expected sections
|
|
37
|
+
assert "Ошибка Python: ValueError" in output
|
|
38
|
+
assert "Что это означает" in output
|
|
39
|
+
assert "Как исправить" in output
|
|
40
|
+
assert "Пример" in output
|
|
41
|
+
assert "invalid literal for int()" in output
|
|
42
|
+
|
|
43
|
+
def test_explain_error_with_different_formats(self):
|
|
44
|
+
"""Test explain_error with different output formats."""
|
|
45
|
+
test_exception = TypeError("'str' object cannot be interpreted as an integer")
|
|
46
|
+
|
|
47
|
+
# Test console format (default)
|
|
48
|
+
output_buffer = io.StringIO()
|
|
49
|
+
with redirect_stdout(output_buffer):
|
|
50
|
+
explain_error(test_exception, format_type='console')
|
|
51
|
+
console_output = output_buffer.getvalue()
|
|
52
|
+
assert "🚨 Ошибка Python:" in console_output
|
|
53
|
+
|
|
54
|
+
# Test plain format
|
|
55
|
+
output_buffer = io.StringIO()
|
|
56
|
+
with redirect_stdout(output_buffer):
|
|
57
|
+
explain_error(test_exception, format_type='plain')
|
|
58
|
+
plain_output = output_buffer.getvalue()
|
|
59
|
+
assert "Ошибка Python:" in plain_output
|
|
60
|
+
assert "🚨" not in plain_output # No emojis in plain format
|
|
61
|
+
|
|
62
|
+
# Test JSON format
|
|
63
|
+
output_buffer = io.StringIO()
|
|
64
|
+
with redirect_stdout(output_buffer):
|
|
65
|
+
explain_error(test_exception, format_type='json')
|
|
66
|
+
json_output = output_buffer.getvalue()
|
|
67
|
+
assert '"error_type": "TypeError"' in json_output
|
|
68
|
+
assert '"simple_explanation"' in json_output
|
|
69
|
+
|
|
70
|
+
def test_explain_error_parameter_validation(self):
|
|
71
|
+
"""Test that explain_error validates parameters correctly."""
|
|
72
|
+
# Test invalid exception parameter
|
|
73
|
+
with pytest.raises(TypeError, match="должен быть экземпляром Exception"):
|
|
74
|
+
explain_error("not an exception")
|
|
75
|
+
|
|
76
|
+
# Test invalid language parameter
|
|
77
|
+
with pytest.raises(ValueError, match="должен быть одним из"):
|
|
78
|
+
explain_error(ValueError("test"), language='invalid')
|
|
79
|
+
|
|
80
|
+
# Test invalid format_type parameter
|
|
81
|
+
with pytest.raises(ValueError, match="должен быть одним из"):
|
|
82
|
+
explain_error(ValueError("test"), format_type='invalid')
|
|
83
|
+
|
|
84
|
+
def test_explain_error_with_optional_parameters(self):
|
|
85
|
+
"""Test explain_error with various optional parameters."""
|
|
86
|
+
test_exception = IndexError("list index out of range")
|
|
87
|
+
|
|
88
|
+
# Test with colors disabled
|
|
89
|
+
output_buffer = io.StringIO()
|
|
90
|
+
with redirect_stdout(output_buffer):
|
|
91
|
+
explain_error(test_exception, use_colors=False)
|
|
92
|
+
output = output_buffer.getvalue()
|
|
93
|
+
assert "IndexError" in output
|
|
94
|
+
|
|
95
|
+
# Test with original error hidden
|
|
96
|
+
output_buffer = io.StringIO()
|
|
97
|
+
with redirect_stdout(output_buffer):
|
|
98
|
+
explain_error(test_exception, show_original_error=False)
|
|
99
|
+
output = output_buffer.getvalue()
|
|
100
|
+
# Should still contain explanation but format might differ
|
|
101
|
+
assert "Что это означает" in output
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestComponentIntegration:
|
|
105
|
+
"""Test integration between different components."""
|
|
106
|
+
|
|
107
|
+
def test_explainer_formatter_integration(self):
|
|
108
|
+
"""Test that ErrorExplainer works correctly with formatters."""
|
|
109
|
+
explainer = ErrorExplainer()
|
|
110
|
+
test_exception = KeyError("'missing_key'")
|
|
111
|
+
|
|
112
|
+
# Get explanation
|
|
113
|
+
explanation = explainer.explain(test_exception)
|
|
114
|
+
|
|
115
|
+
# Test with different formatters
|
|
116
|
+
console_formatter = get_formatter('console')
|
|
117
|
+
plain_formatter = get_formatter('plain')
|
|
118
|
+
json_formatter = get_formatter('json')
|
|
119
|
+
|
|
120
|
+
console_output = console_formatter.format(explanation)
|
|
121
|
+
plain_output = plain_formatter.format(explanation)
|
|
122
|
+
json_output = json_formatter.format(explanation)
|
|
123
|
+
|
|
124
|
+
# All should contain the error information
|
|
125
|
+
assert "KeyError" in console_output
|
|
126
|
+
assert "KeyError" in plain_output
|
|
127
|
+
assert '"error_type": "KeyError"' in json_output
|
|
128
|
+
|
|
129
|
+
# Console should have formatting
|
|
130
|
+
assert "🚨" in console_output or "═══" in console_output
|
|
131
|
+
|
|
132
|
+
# Plain should be simpler
|
|
133
|
+
assert "🚨" not in plain_output
|
|
134
|
+
|
|
135
|
+
# JSON should be valid JSON structure
|
|
136
|
+
import json
|
|
137
|
+
json_data = json.loads(json_output)
|
|
138
|
+
assert json_data['error_type'] == 'KeyError'
|
|
139
|
+
|
|
140
|
+
def test_pattern_matching_integration(self):
|
|
141
|
+
"""Test that pattern matching works with real exceptions."""
|
|
142
|
+
explainer = ErrorExplainer()
|
|
143
|
+
|
|
144
|
+
# Test common exceptions that should have specific patterns
|
|
145
|
+
test_cases = [
|
|
146
|
+
(TypeError("'str' object cannot be interpreted as an integer"), "TypeError"),
|
|
147
|
+
(ValueError("invalid literal for int()"), "ValueError"),
|
|
148
|
+
(AttributeError("'str' object has no attribute 'append'"), "AttributeError"),
|
|
149
|
+
(IndexError("list index out of range"), "IndexError"),
|
|
150
|
+
(KeyError("'missing_key'"), "KeyError"),
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
for exception, expected_type in test_cases:
|
|
154
|
+
explanation = explainer.explain(exception)
|
|
155
|
+
assert explanation.error_type == expected_type
|
|
156
|
+
assert explanation.simple_explanation is not None
|
|
157
|
+
assert explanation.fix_tip is not None
|
|
158
|
+
assert explanation.code_example is not None
|
|
159
|
+
|
|
160
|
+
def test_fallback_explanation_integration(self):
|
|
161
|
+
"""Test that fallback explanations work for unknown exceptions."""
|
|
162
|
+
explainer = ErrorExplainer()
|
|
163
|
+
|
|
164
|
+
# Create a custom exception that shouldn't have a specific pattern
|
|
165
|
+
class CustomException(Exception):
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
custom_exception = CustomException("This is a custom error")
|
|
169
|
+
explanation = explainer.explain(custom_exception)
|
|
170
|
+
|
|
171
|
+
# Should get fallback explanation
|
|
172
|
+
assert explanation.error_type == "CustomException"
|
|
173
|
+
assert "Произошла ошибка типа CustomException" in explanation.simple_explanation
|
|
174
|
+
assert explanation.fix_tip is not None
|
|
175
|
+
assert explanation.code_example is not None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class TestModuleAccessibility:
|
|
179
|
+
"""Test that all modules and functions are accessible through the main API."""
|
|
180
|
+
|
|
181
|
+
def test_main_api_accessibility(self):
|
|
182
|
+
"""Test that main API functions are accessible."""
|
|
183
|
+
# Primary API
|
|
184
|
+
assert hasattr(fishertools, 'explain_error')
|
|
185
|
+
assert callable(fishertools.explain_error)
|
|
186
|
+
|
|
187
|
+
# Safe utilities
|
|
188
|
+
assert hasattr(fishertools, 'safe_get')
|
|
189
|
+
assert hasattr(fishertools, 'safe_divide')
|
|
190
|
+
assert hasattr(fishertools, 'safe_read_file')
|
|
191
|
+
|
|
192
|
+
# Learning tools
|
|
193
|
+
assert hasattr(fishertools, 'generate_example')
|
|
194
|
+
assert hasattr(fishertools, 'show_best_practice')
|
|
195
|
+
|
|
196
|
+
# All should be callable
|
|
197
|
+
assert callable(fishertools.safe_get)
|
|
198
|
+
assert callable(fishertools.safe_divide)
|
|
199
|
+
assert callable(fishertools.generate_example)
|
|
200
|
+
assert callable(fishertools.show_best_practice)
|
|
201
|
+
|
|
202
|
+
def test_module_imports_accessibility(self):
|
|
203
|
+
"""Test that modules can be imported and used."""
|
|
204
|
+
# Test errors module
|
|
205
|
+
from fishertools import errors
|
|
206
|
+
assert hasattr(errors, 'explain_error')
|
|
207
|
+
assert hasattr(errors, 'ErrorExplainer')
|
|
208
|
+
|
|
209
|
+
# Test safe module
|
|
210
|
+
from fishertools import safe
|
|
211
|
+
assert hasattr(safe, 'safe_get')
|
|
212
|
+
assert hasattr(safe, 'safe_divide')
|
|
213
|
+
|
|
214
|
+
# Test learn module
|
|
215
|
+
from fishertools import learn
|
|
216
|
+
assert hasattr(learn, 'generate_example')
|
|
217
|
+
assert hasattr(learn, 'show_best_practice')
|
|
218
|
+
|
|
219
|
+
# Test legacy module
|
|
220
|
+
from fishertools import legacy
|
|
221
|
+
assert legacy is not None
|
|
222
|
+
|
|
223
|
+
def test_direct_imports_work(self):
|
|
224
|
+
"""Test that direct imports from fishertools work."""
|
|
225
|
+
# These should all work without errors
|
|
226
|
+
from fishertools import explain_error
|
|
227
|
+
from fishertools import safe_get, safe_divide
|
|
228
|
+
from fishertools import generate_example, show_best_practice
|
|
229
|
+
|
|
230
|
+
# Test that they're the same objects as module attributes
|
|
231
|
+
assert explain_error is fishertools.explain_error
|
|
232
|
+
assert safe_get is fishertools.safe_get
|
|
233
|
+
assert generate_example is fishertools.generate_example
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class TestErrorHandlingIntegration:
|
|
237
|
+
"""Test error handling across the integrated system."""
|
|
238
|
+
|
|
239
|
+
def test_graceful_error_handling(self):
|
|
240
|
+
"""Test that the system handles errors gracefully."""
|
|
241
|
+
# Test with a problematic exception that might cause issues
|
|
242
|
+
test_exception = Exception("Test exception with unicode: тест")
|
|
243
|
+
|
|
244
|
+
# Should not raise an exception
|
|
245
|
+
output_buffer = io.StringIO()
|
|
246
|
+
with redirect_stdout(output_buffer):
|
|
247
|
+
explain_error(test_exception)
|
|
248
|
+
|
|
249
|
+
output = output_buffer.getvalue()
|
|
250
|
+
assert len(output) > 0 # Should produce some output
|
|
251
|
+
assert "Exception" in output
|
|
252
|
+
|
|
253
|
+
def test_system_robustness(self):
|
|
254
|
+
"""Test system robustness with edge cases."""
|
|
255
|
+
# Test with exception with empty message
|
|
256
|
+
empty_exception = ValueError("")
|
|
257
|
+
output_buffer = io.StringIO()
|
|
258
|
+
with redirect_stdout(output_buffer):
|
|
259
|
+
explain_error(empty_exception)
|
|
260
|
+
output = output_buffer.getvalue()
|
|
261
|
+
assert "ValueError" in output
|
|
262
|
+
|
|
263
|
+
# Test with exception with very long message
|
|
264
|
+
long_message = "x" * 1000
|
|
265
|
+
long_exception = RuntimeError(long_message)
|
|
266
|
+
output_buffer = io.StringIO()
|
|
267
|
+
with redirect_stdout(output_buffer):
|
|
268
|
+
explain_error(long_exception)
|
|
269
|
+
output = output_buffer.getvalue()
|
|
270
|
+
assert "RuntimeError" in output
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class TestBackwardCompatibility:
|
|
274
|
+
"""Test that backward compatibility is maintained."""
|
|
275
|
+
|
|
276
|
+
def test_legacy_module_access(self):
|
|
277
|
+
"""Test that legacy modules are still accessible."""
|
|
278
|
+
# These should work for backward compatibility
|
|
279
|
+
import fishertools.utils
|
|
280
|
+
import fishertools.decorators
|
|
281
|
+
import fishertools.helpers
|
|
282
|
+
|
|
283
|
+
# Modules should be importable
|
|
284
|
+
assert fishertools.utils is not None
|
|
285
|
+
assert fishertools.decorators is not None
|
|
286
|
+
assert fishertools.helpers is not None
|
|
287
|
+
|
|
288
|
+
def test_legacy_functions_accessible(self):
|
|
289
|
+
"""Test that legacy functions are accessible through fishertools."""
|
|
290
|
+
# Legacy modules should be in __all__
|
|
291
|
+
assert 'utils' in fishertools.__all__
|
|
292
|
+
assert 'decorators' in fishertools.__all__
|
|
293
|
+
assert 'helpers' in fishertools.__all__
|
|
294
|
+
|
|
295
|
+
# Should be accessible as attributes
|
|
296
|
+
assert hasattr(fishertools, 'utils')
|
|
297
|
+
assert hasattr(fishertools, 'decorators')
|
|
298
|
+
assert hasattr(fishertools, 'helpers')
|