fishertools 0.2.1__py3-none-any.whl → 0.4.0__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 (69) hide show
  1. fishertools/__init__.py +16 -5
  2. fishertools/errors/__init__.py +11 -3
  3. fishertools/errors/exception_types.py +282 -0
  4. fishertools/errors/explainer.py +87 -1
  5. fishertools/errors/models.py +73 -1
  6. fishertools/errors/patterns.py +40 -0
  7. fishertools/examples/cli_example.py +156 -0
  8. fishertools/examples/learn_example.py +65 -0
  9. fishertools/examples/logger_example.py +176 -0
  10. fishertools/examples/menu_example.py +101 -0
  11. fishertools/examples/storage_example.py +175 -0
  12. fishertools/input_utils.py +185 -0
  13. fishertools/learn/__init__.py +19 -2
  14. fishertools/learn/examples.py +88 -1
  15. fishertools/learn/knowledge_engine.py +321 -0
  16. fishertools/learn/repl/__init__.py +19 -0
  17. fishertools/learn/repl/cli.py +31 -0
  18. fishertools/learn/repl/code_sandbox.py +229 -0
  19. fishertools/learn/repl/command_handler.py +544 -0
  20. fishertools/learn/repl/command_parser.py +165 -0
  21. fishertools/learn/repl/engine.py +479 -0
  22. fishertools/learn/repl/models.py +121 -0
  23. fishertools/learn/repl/session_manager.py +284 -0
  24. fishertools/learn/repl/test_code_sandbox.py +261 -0
  25. fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
  26. fishertools/learn/repl/test_command_handler.py +224 -0
  27. fishertools/learn/repl/test_command_handler_pbt.py +189 -0
  28. fishertools/learn/repl/test_command_parser.py +160 -0
  29. fishertools/learn/repl/test_command_parser_pbt.py +100 -0
  30. fishertools/learn/repl/test_engine.py +190 -0
  31. fishertools/learn/repl/test_session_manager.py +310 -0
  32. fishertools/learn/repl/test_session_manager_pbt.py +182 -0
  33. fishertools/learn/test_knowledge_engine.py +241 -0
  34. fishertools/learn/test_knowledge_engine_pbt.py +180 -0
  35. fishertools/patterns/__init__.py +46 -0
  36. fishertools/patterns/cli.py +175 -0
  37. fishertools/patterns/logger.py +140 -0
  38. fishertools/patterns/menu.py +99 -0
  39. fishertools/patterns/storage.py +127 -0
  40. fishertools/readme_transformer.py +631 -0
  41. fishertools/safe/__init__.py +6 -1
  42. fishertools/safe/files.py +329 -1
  43. fishertools/transform_readme.py +105 -0
  44. fishertools-0.4.0.dist-info/METADATA +104 -0
  45. fishertools-0.4.0.dist-info/RECORD +131 -0
  46. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
  47. tests/test_documentation_properties.py +329 -0
  48. tests/test_documentation_structure.py +349 -0
  49. tests/test_errors/test_exception_types.py +446 -0
  50. tests/test_errors/test_exception_types_pbt.py +333 -0
  51. tests/test_errors/test_patterns.py +52 -0
  52. tests/test_input_utils/__init__.py +1 -0
  53. tests/test_input_utils/test_input_utils.py +65 -0
  54. tests/test_learn/test_examples.py +179 -1
  55. tests/test_learn/test_explain_properties.py +307 -0
  56. tests/test_patterns_cli.py +611 -0
  57. tests/test_patterns_docstrings.py +473 -0
  58. tests/test_patterns_logger.py +465 -0
  59. tests/test_patterns_menu.py +440 -0
  60. tests/test_patterns_storage.py +447 -0
  61. tests/test_readme_enhancements_v0_3_1.py +2036 -0
  62. tests/test_readme_transformer/__init__.py +1 -0
  63. tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
  64. tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
  65. tests/test_safe/test_files.py +726 -1
  66. fishertools-0.2.1.dist-info/METADATA +0 -256
  67. fishertools-0.2.1.dist-info/RECORD +0 -81
  68. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
  69. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,446 @@
1
+ """
2
+ Unit tests for exception type identification module.
3
+
4
+ Tests the exception type identification functionality including:
5
+ - Identifying exception types from exception objects
6
+ - Getting exception type information
7
+ - Checking if exception types are supported
8
+ - Retrieving exception type mappings
9
+ """
10
+
11
+ import pytest
12
+ from fishertools.errors.exception_types import (
13
+ identify_exception_type,
14
+ get_exception_type_info,
15
+ is_supported_exception_type,
16
+ get_exception_type_mapping,
17
+ get_supported_exception_types,
18
+ ExceptionTypeInfo,
19
+ EXCEPTION_TYPE_MAPPING
20
+ )
21
+
22
+
23
+ class TestIdentifyExceptionType:
24
+ """Tests for identify_exception_type function."""
25
+
26
+ def test_identify_type_error(self):
27
+ """Test identifying TypeError."""
28
+ exc = TypeError("unsupported operand type")
29
+ assert identify_exception_type(exc) == "TypeError"
30
+
31
+ def test_identify_value_error(self):
32
+ """Test identifying ValueError."""
33
+ exc = ValueError("invalid literal for int()")
34
+ assert identify_exception_type(exc) == "ValueError"
35
+
36
+ def test_identify_index_error(self):
37
+ """Test identifying IndexError."""
38
+ exc = IndexError("list index out of range")
39
+ assert identify_exception_type(exc) == "IndexError"
40
+
41
+ def test_identify_key_error(self):
42
+ """Test identifying KeyError."""
43
+ exc = KeyError("missing_key")
44
+ assert identify_exception_type(exc) == "KeyError"
45
+
46
+ def test_identify_attribute_error(self):
47
+ """Test identifying AttributeError."""
48
+ exc = AttributeError("'str' object has no attribute 'append'")
49
+ assert identify_exception_type(exc) == "AttributeError"
50
+
51
+ def test_identify_file_not_found_error(self):
52
+ """Test identifying FileNotFoundError."""
53
+ exc = FileNotFoundError("No such file or directory")
54
+ assert identify_exception_type(exc) == "FileNotFoundError"
55
+
56
+ def test_identify_permission_error(self):
57
+ """Test identifying PermissionError."""
58
+ exc = PermissionError("Permission denied")
59
+ assert identify_exception_type(exc) == "PermissionError"
60
+
61
+ def test_identify_zero_division_error(self):
62
+ """Test identifying ZeroDivisionError."""
63
+ exc = ZeroDivisionError("division by zero")
64
+ assert identify_exception_type(exc) == "ZeroDivisionError"
65
+
66
+ def test_identify_name_error(self):
67
+ """Test identifying NameError."""
68
+ exc = NameError("name 'undefined_var' is not defined")
69
+ assert identify_exception_type(exc) == "NameError"
70
+
71
+ def test_identify_unknown_exception(self):
72
+ """Test identifying unknown exception type."""
73
+ class CustomException(Exception):
74
+ pass
75
+
76
+ exc = CustomException("custom error")
77
+ assert identify_exception_type(exc) == "CustomException"
78
+
79
+ def test_identify_with_non_exception_raises_type_error(self):
80
+ """Test that non-Exception objects raise TypeError."""
81
+ with pytest.raises(TypeError):
82
+ identify_exception_type("not an exception")
83
+
84
+ with pytest.raises(TypeError):
85
+ identify_exception_type(42)
86
+
87
+ with pytest.raises(TypeError):
88
+ identify_exception_type(None)
89
+
90
+ def test_identify_with_exception_subclass(self):
91
+ """Test identifying exception subclasses."""
92
+ # FileNotFoundError is a subclass of OSError
93
+ exc = FileNotFoundError("file not found")
94
+ assert identify_exception_type(exc) == "FileNotFoundError"
95
+
96
+
97
+ class TestGetExceptionTypeInfo:
98
+ """Tests for get_exception_type_info function."""
99
+
100
+ def test_get_info_type_error(self):
101
+ """Test getting info for TypeError."""
102
+ exc = TypeError("unsupported operand type")
103
+ info = get_exception_type_info(exc)
104
+
105
+ assert isinstance(info, ExceptionTypeInfo)
106
+ assert info.name == "TypeError"
107
+ assert info.exception_class == TypeError
108
+ assert "Type mismatch" in info.description
109
+ assert len(info.common_causes) > 0
110
+
111
+ def test_get_info_value_error(self):
112
+ """Test getting info for ValueError."""
113
+ exc = ValueError("invalid literal")
114
+ info = get_exception_type_info(exc)
115
+
116
+ assert info.name == "ValueError"
117
+ assert info.exception_class == ValueError
118
+ assert "Invalid value" in info.description
119
+
120
+ def test_get_info_index_error(self):
121
+ """Test getting info for IndexError."""
122
+ exc = IndexError("list index out of range")
123
+ info = get_exception_type_info(exc)
124
+
125
+ assert info.name == "IndexError"
126
+ assert "Sequence index" in info.description
127
+
128
+ def test_get_info_key_error(self):
129
+ """Test getting info for KeyError."""
130
+ exc = KeyError("missing_key")
131
+ info = get_exception_type_info(exc)
132
+
133
+ assert info.name == "KeyError"
134
+ assert "Dictionary key" in info.description
135
+
136
+ def test_get_info_attribute_error(self):
137
+ """Test getting info for AttributeError."""
138
+ exc = AttributeError("'str' object has no attribute 'append'")
139
+ info = get_exception_type_info(exc)
140
+
141
+ assert info.name == "AttributeError"
142
+ assert "Attribute or method" in info.description
143
+
144
+ def test_get_info_file_not_found_error(self):
145
+ """Test getting info for FileNotFoundError."""
146
+ exc = FileNotFoundError("No such file")
147
+ info = get_exception_type_info(exc)
148
+
149
+ assert info.name == "FileNotFoundError"
150
+ assert "File or directory" in info.description
151
+
152
+ def test_get_info_permission_error(self):
153
+ """Test getting info for PermissionError."""
154
+ exc = PermissionError("Permission denied")
155
+ info = get_exception_type_info(exc)
156
+
157
+ assert info.name == "PermissionError"
158
+ assert "Permission denied" in info.description
159
+
160
+ def test_get_info_zero_division_error(self):
161
+ """Test getting info for ZeroDivisionError."""
162
+ exc = ZeroDivisionError("division by zero")
163
+ info = get_exception_type_info(exc)
164
+
165
+ assert info.name == "ZeroDivisionError"
166
+ assert "Division by zero" in info.description
167
+
168
+ def test_get_info_name_error(self):
169
+ """Test getting info for NameError."""
170
+ exc = NameError("name 'x' is not defined")
171
+ info = get_exception_type_info(exc)
172
+
173
+ assert info.name == "NameError"
174
+ assert "Name not defined" in info.description
175
+
176
+ def test_get_info_unknown_exception(self):
177
+ """Test getting info for unknown exception type."""
178
+ class CustomException(Exception):
179
+ pass
180
+
181
+ exc = CustomException("custom error")
182
+ info = get_exception_type_info(exc)
183
+
184
+ assert info.name == "CustomException"
185
+ assert info.exception_class == CustomException
186
+ assert "unexpected error" in info.description.lower()
187
+
188
+ def test_get_info_with_non_exception_raises_type_error(self):
189
+ """Test that non-Exception objects raise TypeError."""
190
+ with pytest.raises(TypeError):
191
+ get_exception_type_info("not an exception")
192
+
193
+ with pytest.raises(TypeError):
194
+ get_exception_type_info(42)
195
+
196
+ def test_info_has_common_causes(self):
197
+ """Test that exception info includes common causes."""
198
+ exc = TypeError("unsupported operand type")
199
+ info = get_exception_type_info(exc)
200
+
201
+ assert isinstance(info.common_causes, list)
202
+ assert len(info.common_causes) > 0
203
+ assert all(isinstance(cause, str) for cause in info.common_causes)
204
+
205
+
206
+ class TestIsSupportedExceptionType:
207
+ """Tests for is_supported_exception_type function."""
208
+
209
+ def test_supported_type_error(self):
210
+ """Test that TypeError is supported."""
211
+ exc = TypeError("unsupported operand type")
212
+ assert is_supported_exception_type(exc) is True
213
+
214
+ def test_supported_value_error(self):
215
+ """Test that ValueError is supported."""
216
+ exc = ValueError("invalid literal")
217
+ assert is_supported_exception_type(exc) is True
218
+
219
+ def test_supported_index_error(self):
220
+ """Test that IndexError is supported."""
221
+ exc = IndexError("list index out of range")
222
+ assert is_supported_exception_type(exc) is True
223
+
224
+ def test_supported_key_error(self):
225
+ """Test that KeyError is supported."""
226
+ exc = KeyError("missing_key")
227
+ assert is_supported_exception_type(exc) is True
228
+
229
+ def test_supported_attribute_error(self):
230
+ """Test that AttributeError is supported."""
231
+ exc = AttributeError("'str' object has no attribute 'append'")
232
+ assert is_supported_exception_type(exc) is True
233
+
234
+ def test_supported_file_not_found_error(self):
235
+ """Test that FileNotFoundError is supported."""
236
+ exc = FileNotFoundError("No such file")
237
+ assert is_supported_exception_type(exc) is True
238
+
239
+ def test_supported_permission_error(self):
240
+ """Test that PermissionError is supported."""
241
+ exc = PermissionError("Permission denied")
242
+ assert is_supported_exception_type(exc) is True
243
+
244
+ def test_supported_zero_division_error(self):
245
+ """Test that ZeroDivisionError is supported."""
246
+ exc = ZeroDivisionError("division by zero")
247
+ assert is_supported_exception_type(exc) is True
248
+
249
+ def test_supported_name_error(self):
250
+ """Test that NameError is supported."""
251
+ exc = NameError("name 'x' is not defined")
252
+ assert is_supported_exception_type(exc) is True
253
+
254
+ def test_unsupported_custom_exception(self):
255
+ """Test that custom exceptions are not supported."""
256
+ class CustomException(Exception):
257
+ pass
258
+
259
+ exc = CustomException("custom error")
260
+ assert is_supported_exception_type(exc) is False
261
+
262
+ def test_unsupported_runtime_error(self):
263
+ """Test that RuntimeError is not in supported list."""
264
+ exc = RuntimeError("runtime error")
265
+ assert is_supported_exception_type(exc) is False
266
+
267
+ def test_is_supported_with_non_exception_raises_type_error(self):
268
+ """Test that non-Exception objects raise TypeError."""
269
+ with pytest.raises(TypeError):
270
+ is_supported_exception_type("not an exception")
271
+
272
+ with pytest.raises(TypeError):
273
+ is_supported_exception_type(42)
274
+
275
+
276
+ class TestGetExceptionTypeMapping:
277
+ """Tests for get_exception_type_mapping function."""
278
+
279
+ def test_mapping_is_dict(self):
280
+ """Test that mapping returns a dictionary."""
281
+ mapping = get_exception_type_mapping()
282
+ assert isinstance(mapping, dict)
283
+
284
+ def test_mapping_contains_all_supported_types(self):
285
+ """Test that mapping contains all supported exception types."""
286
+ mapping = get_exception_type_mapping()
287
+
288
+ expected_types = [
289
+ TypeError, ValueError, IndexError, KeyError, AttributeError,
290
+ FileNotFoundError, PermissionError, ZeroDivisionError, NameError
291
+ ]
292
+
293
+ for exc_type in expected_types:
294
+ assert exc_type in mapping
295
+
296
+ def test_mapping_values_are_exception_type_info(self):
297
+ """Test that all mapping values are ExceptionTypeInfo instances."""
298
+ mapping = get_exception_type_mapping()
299
+
300
+ for exc_type, info in mapping.items():
301
+ assert isinstance(info, ExceptionTypeInfo)
302
+ assert info.exception_class == exc_type
303
+
304
+ def test_mapping_is_copy(self):
305
+ """Test that returned mapping is a copy, not the original."""
306
+ mapping1 = get_exception_type_mapping()
307
+ mapping2 = get_exception_type_mapping()
308
+
309
+ # Should be equal but not the same object
310
+ assert mapping1 == mapping2
311
+ assert mapping1 is not mapping2
312
+
313
+ def test_mapping_has_correct_structure(self):
314
+ """Test that mapping entries have correct structure."""
315
+ mapping = get_exception_type_mapping()
316
+
317
+ for exc_type, info in mapping.items():
318
+ assert hasattr(info, 'exception_class')
319
+ assert hasattr(info, 'name')
320
+ assert hasattr(info, 'description')
321
+ assert hasattr(info, 'common_causes')
322
+
323
+ assert isinstance(info.name, str)
324
+ assert isinstance(info.description, str)
325
+ assert isinstance(info.common_causes, list)
326
+
327
+
328
+ class TestGetSupportedExceptionTypes:
329
+ """Tests for get_supported_exception_types function."""
330
+
331
+ def test_returns_list(self):
332
+ """Test that function returns a list."""
333
+ types = get_supported_exception_types()
334
+ assert isinstance(types, list)
335
+
336
+ def test_contains_all_supported_types(self):
337
+ """Test that list contains all supported exception type names."""
338
+ types = get_supported_exception_types()
339
+
340
+ expected_names = [
341
+ "TypeError", "ValueError", "IndexError", "KeyError", "AttributeError",
342
+ "FileNotFoundError", "PermissionError", "ZeroDivisionError", "NameError"
343
+ ]
344
+
345
+ for name in expected_names:
346
+ assert name in types
347
+
348
+ def test_all_items_are_strings(self):
349
+ """Test that all items in the list are strings."""
350
+ types = get_supported_exception_types()
351
+ assert all(isinstance(t, str) for t in types)
352
+
353
+ def test_no_duplicates(self):
354
+ """Test that there are no duplicate exception type names."""
355
+ types = get_supported_exception_types()
356
+ assert len(types) == len(set(types))
357
+
358
+ def test_correct_count(self):
359
+ """Test that the list has the correct number of supported types."""
360
+ types = get_supported_exception_types()
361
+ # Should have 9 supported exception types
362
+ assert len(types) == 9
363
+
364
+
365
+ class TestExceptionTypeMapping:
366
+ """Tests for the EXCEPTION_TYPE_MAPPING constant."""
367
+
368
+ def test_mapping_exists(self):
369
+ """Test that EXCEPTION_TYPE_MAPPING is defined."""
370
+ assert EXCEPTION_TYPE_MAPPING is not None
371
+ assert isinstance(EXCEPTION_TYPE_MAPPING, dict)
372
+
373
+ def test_mapping_has_all_types(self):
374
+ """Test that mapping has all expected exception types."""
375
+ expected_types = [
376
+ TypeError, ValueError, IndexError, KeyError, AttributeError,
377
+ FileNotFoundError, PermissionError, ZeroDivisionError, NameError
378
+ ]
379
+
380
+ for exc_type in expected_types:
381
+ assert exc_type in EXCEPTION_TYPE_MAPPING
382
+
383
+ def test_mapping_values_are_info_objects(self):
384
+ """Test that all values are ExceptionTypeInfo objects."""
385
+ for exc_type, info in EXCEPTION_TYPE_MAPPING.items():
386
+ assert isinstance(info, ExceptionTypeInfo)
387
+ assert info.exception_class == exc_type
388
+ assert isinstance(info.name, str)
389
+ assert isinstance(info.description, str)
390
+ assert isinstance(info.common_causes, list)
391
+
392
+
393
+ class TestExceptionTypeIdentificationIntegration:
394
+ """Integration tests for exception type identification."""
395
+
396
+ def test_identify_and_get_info_consistency(self):
397
+ """Test that identify_exception_type and get_exception_type_info are consistent."""
398
+ exceptions = [
399
+ TypeError("test"),
400
+ ValueError("test"),
401
+ IndexError("test"),
402
+ KeyError("test"),
403
+ AttributeError("test"),
404
+ FileNotFoundError("test"),
405
+ PermissionError("test"),
406
+ ZeroDivisionError("test"),
407
+ NameError("test"),
408
+ ]
409
+
410
+ for exc in exceptions:
411
+ exc_type_name = identify_exception_type(exc)
412
+ info = get_exception_type_info(exc)
413
+
414
+ assert exc_type_name == info.name
415
+
416
+ def test_supported_check_consistency(self):
417
+ """Test that is_supported_exception_type is consistent with mapping."""
418
+ mapping = get_exception_type_mapping()
419
+
420
+ for exc_type in mapping.keys():
421
+ exc = exc_type("test")
422
+ assert is_supported_exception_type(exc) is True
423
+
424
+ def test_all_supported_types_in_list(self):
425
+ """Test that all supported types are in the supported types list."""
426
+ supported_list = get_supported_exception_types()
427
+ mapping = get_exception_type_mapping()
428
+
429
+ for info in mapping.values():
430
+ assert info.name in supported_list
431
+
432
+ def test_exception_type_info_completeness(self):
433
+ """Test that all exception type info objects are complete."""
434
+ mapping = get_exception_type_mapping()
435
+
436
+ for exc_type, info in mapping.items():
437
+ # Check all required fields are present and non-empty
438
+ assert info.exception_class is not None
439
+ assert info.name and len(info.name) > 0
440
+ assert info.description and len(info.description) > 0
441
+ assert info.common_causes and len(info.common_causes) > 0
442
+
443
+ # Check all common causes are non-empty strings
444
+ for cause in info.common_causes:
445
+ assert isinstance(cause, str)
446
+ assert len(cause) > 0