Coverage for tests / unit / errormode / test_errormode_functionality.py: 61%
425 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 02:51 -0700
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 02:51 -0700
1from __future__ import annotations
3import warnings
5from muutils.errormode import ErrorMode
7import pytest
10def test_except():
11 with pytest.raises(ValueError):
12 ErrorMode.EXCEPT.process("test-except", except_cls=ValueError)
14 with pytest.raises(TypeError):
15 ErrorMode.EXCEPT.process("test-except", except_cls=TypeError)
17 with pytest.raises(RuntimeError):
18 ErrorMode.EXCEPT.process("test-except", except_cls=RuntimeError)
20 with pytest.raises(KeyError):
21 ErrorMode.EXCEPT.process("test-except", except_cls=KeyError)
23 with pytest.raises(KeyError):
24 ErrorMode.EXCEPT.process(
25 "test-except", except_cls=KeyError, except_from=ValueError("base exception")
26 )
29def test_warn():
30 with pytest.warns(UserWarning):
31 ErrorMode.WARN.process("test-warn", warn_cls=UserWarning)
33 with pytest.warns(Warning):
34 ErrorMode.WARN.process("test-warn", warn_cls=Warning)
36 with pytest.warns(DeprecationWarning):
37 ErrorMode.WARN.process("test-warn", warn_cls=DeprecationWarning)
40def test_ignore():
41 with warnings.catch_warnings(record=True) as w:
42 ErrorMode.IGNORE.process("test-ignore")
44 ErrorMode.IGNORE.process("test-ignore", except_cls=ValueError)
45 ErrorMode.IGNORE.process("test-ignore", except_from=TypeError("base exception"))
47 ErrorMode.IGNORE.process("test-ignore", warn_cls=UserWarning)
49 assert len(w) == 0, f"There should be no warnings: {w}"
52def test_except_custom():
53 class MyCustomError(ValueError):
54 pass
56 with pytest.raises(MyCustomError):
57 ErrorMode.EXCEPT.process("test-except", except_cls=MyCustomError)
60def test_warn_custom():
61 class MyCustomWarning(Warning):
62 pass
64 with pytest.warns(MyCustomWarning):
65 ErrorMode.WARN.process("test-warn", warn_cls=MyCustomWarning)
68def test_except_mode_chained_exception():
69 try:
70 # set up the base exception
71 try:
72 raise KeyError("base exception")
73 except Exception as base_exception:
74 # catch it, raise another exception with it as the cause
75 ErrorMode.EXCEPT.process(
76 "Test chained exception",
77 except_cls=RuntimeError,
78 except_from=base_exception,
79 )
80 # catch the outer exception
81 except RuntimeError as e:
82 assert str(e) == "Test chained exception"
83 # check that the cause is the base exception
84 assert isinstance(e.__cause__, KeyError)
85 assert repr(e.__cause__) == "KeyError('base exception')"
86 else:
87 assert False, "Expected RuntimeError with cause KeyError"
90def test_logging_global():
91 import muutils.errormode as errormode
93 log: list[str] = []
95 def log_func(msg: str):
96 log.append(msg)
98 ErrorMode.LOG.process("test-log-print")
100 errormode.GLOBAL_LOG_FUNC = log_func
102 ErrorMode.LOG.process("test-log")
103 ErrorMode.LOG.process("test-log-2")
105 assert log == ["test-log", "test-log-2"]
107 ErrorMode.LOG.process("test-log-3")
109 assert log == ["test-log", "test-log-2", "test-log-3"]
112def test_custom_showwarning():
113 """Test custom_showwarning function with traceback handling and frame extraction."""
114 from muutils.errormode import custom_showwarning
116 # Capture warnings
117 with warnings.catch_warnings(record=True) as w:
118 warnings.simplefilter("always")
120 # Call custom_showwarning directly
121 custom_showwarning("test warning message", UserWarning)
123 # Check that a warning was issued
124 assert len(w) == 1
125 assert issubclass(w[0].category, UserWarning)
126 assert "test warning message" in str(w[0].message)
128 # Check that the warning has traceback information
129 assert w[0].filename is not None
130 assert w[0].lineno is not None
133def test_custom_showwarning_with_category():
134 """Test custom_showwarning with different warning categories."""
135 from muutils.errormode import custom_showwarning
137 with warnings.catch_warnings(record=True) as w:
138 warnings.simplefilter("always")
140 custom_showwarning("deprecation test", DeprecationWarning)
142 assert len(w) == 1
143 assert issubclass(w[0].category, DeprecationWarning)
146def test_custom_showwarning_default_category():
147 """Test custom_showwarning uses UserWarning as default."""
148 from muutils.errormode import custom_showwarning
150 with warnings.catch_warnings(record=True) as w:
151 warnings.simplefilter("always")
153 # Call without specifying category
154 custom_showwarning("default category test", category=None)
156 assert len(w) == 1
157 assert issubclass(w[0].category, UserWarning)
160def test_ErrorMode_process_except_from():
161 """Test exception chaining with except_from parameter."""
162 base_exception = ValueError("base error")
164 try:
165 ErrorMode.EXCEPT.process(
166 "chained error message",
167 except_cls=RuntimeError,
168 except_from=base_exception,
169 )
170 except RuntimeError as e:
171 # Check the exception message
172 assert str(e) == "chained error message"
173 # Check that __cause__ is set correctly
174 assert e.__cause__ is base_exception
175 assert isinstance(e.__cause__, ValueError)
176 assert str(e.__cause__) == "base error"
177 else:
178 # TYPING: ty bug on python <= 3.9
179 pytest.fail("Expected RuntimeError to be raised") # ty: ignore[arg-type,invalid-argument-type]
182def test_ErrorMode_process_except_from_different_types():
183 """Test exception chaining with different exception types."""
184 # Test with KeyError -> TypeError
185 base = KeyError("key not found")
186 try:
187 ErrorMode.EXCEPT.process("type error", except_cls=TypeError, except_from=base)
188 except TypeError as e:
189 assert e.__cause__ is base
191 # Test with AttributeError -> ValueError
192 base2 = AttributeError("attribute missing")
193 try:
194 ErrorMode.EXCEPT.process(
195 "value error", except_cls=ValueError, except_from=base2
196 )
197 except ValueError as e:
198 assert e.__cause__ is base2
201def test_ErrorMode_process_custom_funcs():
202 """Test custom warn_func and log_func parameters."""
203 # Test custom warn_func
204 warnings_captured = []
206 def custom_warn(msg: str, category, source=None):
207 warnings_captured.append({"msg": msg, "category": category, "source": source})
209 ErrorMode.WARN.process(
210 "custom warn test", warn_cls=UserWarning, warn_func=custom_warn
211 )
213 assert len(warnings_captured) == 1
214 assert warnings_captured[0]["msg"] == "custom warn test"
215 assert warnings_captured[0]["category"] == UserWarning # noqa: E721
217 # Test custom log_func
218 logs_captured = []
220 def custom_log(msg: str):
221 logs_captured.append(msg)
223 ErrorMode.LOG.process("custom log test", log_func=custom_log)
225 assert len(logs_captured) == 1
226 assert logs_captured[0] == "custom log test"
229def test_ErrorMode_process_custom_warn_func_with_except_from():
230 """Test custom warn_func with except_from to augment message."""
231 warnings_captured = []
233 def custom_warn(msg: str, category, source=None):
234 warnings_captured.append(msg)
236 base_exception = ValueError("source exception")
238 ErrorMode.WARN.process(
239 "warning message",
240 warn_cls=UserWarning,
241 warn_func=custom_warn,
242 except_from=base_exception,
243 )
245 assert len(warnings_captured) == 1
246 # Check that the message is augmented with source
247 assert "warning message" in warnings_captured[0]
248 assert "Source of warning" in warnings_captured[0]
249 assert "source exception" in warnings_captured[0]
252def test_ErrorMode_serialize_load():
253 """Test round-trip serialization and loading."""
254 # Test EXCEPT
255 serialized = ErrorMode.EXCEPT.serialize()
256 loaded = ErrorMode.load(serialized)
257 assert loaded is ErrorMode.EXCEPT
259 # Test WARN
260 serialized = ErrorMode.WARN.serialize()
261 loaded = ErrorMode.load(serialized)
262 assert loaded is ErrorMode.WARN
264 # Test LOG
265 serialized = ErrorMode.LOG.serialize()
266 loaded = ErrorMode.load(serialized)
267 assert loaded is ErrorMode.LOG
269 # Test IGNORE
270 serialized = ErrorMode.IGNORE.serialize()
271 loaded = ErrorMode.load(serialized)
272 assert loaded is ErrorMode.IGNORE
275def test_ErrorMode_serialize_format():
276 """Test that serialize returns the expected format."""
277 assert ErrorMode.EXCEPT.serialize() == "ErrorMode.Except"
278 assert ErrorMode.WARN.serialize() == "ErrorMode.Warn"
279 assert ErrorMode.LOG.serialize() == "ErrorMode.Log"
280 assert ErrorMode.IGNORE.serialize() == "ErrorMode.Ignore"
283def test_ERROR_MODE_ALIASES():
284 """Test that all aliases resolve correctly."""
285 from muutils.errormode import ERROR_MODE_ALIASES
287 # Test EXCEPT aliases
288 assert ERROR_MODE_ALIASES["except"] is ErrorMode.EXCEPT
289 assert ERROR_MODE_ALIASES["e"] is ErrorMode.EXCEPT
290 assert ERROR_MODE_ALIASES["error"] is ErrorMode.EXCEPT
291 assert ERROR_MODE_ALIASES["err"] is ErrorMode.EXCEPT
292 assert ERROR_MODE_ALIASES["raise"] is ErrorMode.EXCEPT
294 # Test WARN aliases
295 assert ERROR_MODE_ALIASES["warn"] is ErrorMode.WARN
296 assert ERROR_MODE_ALIASES["w"] is ErrorMode.WARN
297 assert ERROR_MODE_ALIASES["warning"] is ErrorMode.WARN
299 # Test LOG aliases
300 assert ERROR_MODE_ALIASES["log"] is ErrorMode.LOG
301 assert ERROR_MODE_ALIASES["l"] is ErrorMode.LOG
302 assert ERROR_MODE_ALIASES["print"] is ErrorMode.LOG
303 assert ERROR_MODE_ALIASES["output"] is ErrorMode.LOG
304 assert ERROR_MODE_ALIASES["show"] is ErrorMode.LOG
305 assert ERROR_MODE_ALIASES["display"] is ErrorMode.LOG
307 # Test IGNORE aliases
308 assert ERROR_MODE_ALIASES["ignore"] is ErrorMode.IGNORE
309 assert ERROR_MODE_ALIASES["i"] is ErrorMode.IGNORE
310 assert ERROR_MODE_ALIASES["silent"] is ErrorMode.IGNORE
311 assert ERROR_MODE_ALIASES["quiet"] is ErrorMode.IGNORE
312 assert ERROR_MODE_ALIASES["nothing"] is ErrorMode.IGNORE
315def test_ErrorMode_from_any_with_string():
316 """Test from_any with string inputs."""
317 # Test base values
318 assert ErrorMode.from_any("except") is ErrorMode.EXCEPT
319 assert ErrorMode.from_any("warn") is ErrorMode.WARN
320 assert ErrorMode.from_any("log") is ErrorMode.LOG
321 assert ErrorMode.from_any("ignore") is ErrorMode.IGNORE
323 # Test with uppercase
324 assert ErrorMode.from_any("EXCEPT") is ErrorMode.EXCEPT
325 assert ErrorMode.from_any("WARN") is ErrorMode.WARN
327 # Test with whitespace
328 assert ErrorMode.from_any(" except ") is ErrorMode.EXCEPT
329 assert ErrorMode.from_any(" warn ") is ErrorMode.WARN
332def test_ErrorMode_from_any_with_aliases():
333 """Test from_any with alias strings."""
334 # Test EXCEPT aliases
335 assert ErrorMode.from_any("error") is ErrorMode.EXCEPT
336 assert ErrorMode.from_any("e") is ErrorMode.EXCEPT
337 assert ErrorMode.from_any("raise") is ErrorMode.EXCEPT
339 # Test WARN aliases
340 assert ErrorMode.from_any("warning") is ErrorMode.WARN
341 assert ErrorMode.from_any("w") is ErrorMode.WARN
343 # Test LOG aliases
344 assert ErrorMode.from_any("print") is ErrorMode.LOG
345 assert ErrorMode.from_any("l") is ErrorMode.LOG
346 assert ErrorMode.from_any("output") is ErrorMode.LOG
348 # Test IGNORE aliases
349 assert ErrorMode.from_any("silent") is ErrorMode.IGNORE
350 assert ErrorMode.from_any("i") is ErrorMode.IGNORE
351 assert ErrorMode.from_any("quiet") is ErrorMode.IGNORE
354def test_ErrorMode_from_any_with_prefix():
355 """Test from_any with ErrorMode. prefix."""
356 assert ErrorMode.from_any("ErrorMode.except") is ErrorMode.EXCEPT
357 assert ErrorMode.from_any("ErrorMode.warn") is ErrorMode.WARN
358 assert ErrorMode.from_any("ErrorMode.log") is ErrorMode.LOG
359 assert ErrorMode.from_any("ErrorMode.ignore") is ErrorMode.IGNORE
361 # Test with mixed case
362 assert ErrorMode.from_any("ErrorMode.Except") is ErrorMode.EXCEPT
363 assert ErrorMode.from_any("ErrorMode.WARN") is ErrorMode.WARN
366def test_ErrorMode_from_any_with_ErrorMode_instance():
367 """Test from_any with ErrorMode instance."""
368 assert ErrorMode.from_any(ErrorMode.EXCEPT) is ErrorMode.EXCEPT
369 assert ErrorMode.from_any(ErrorMode.WARN) is ErrorMode.WARN
370 assert ErrorMode.from_any(ErrorMode.LOG) is ErrorMode.LOG
371 assert ErrorMode.from_any(ErrorMode.IGNORE) is ErrorMode.IGNORE
374def test_ErrorMode_from_any_without_aliases():
375 """Test from_any with allow_aliases=False."""
376 # Base values should still work
377 assert ErrorMode.from_any("except", allow_aliases=False) is ErrorMode.EXCEPT
379 # Aliases should fail
380 with pytest.raises(KeyError):
381 ErrorMode.from_any("error", allow_aliases=False)
383 with pytest.raises(KeyError):
384 ErrorMode.from_any("e", allow_aliases=False)
387def test_ErrorMode_from_any_invalid_string():
388 """Test from_any with invalid string."""
389 with pytest.raises(KeyError):
390 ErrorMode.from_any("invalid_mode")
392 with pytest.raises(KeyError):
393 ErrorMode.from_any("not_a_mode")
396def test_ErrorMode_from_any_invalid_type():
397 """Test from_any with invalid type."""
398 with pytest.raises(TypeError):
399 ErrorMode.from_any(123) # type: ignore
401 with pytest.raises(TypeError):
402 ErrorMode.from_any(None) # type: ignore
404 with pytest.raises(TypeError):
405 ErrorMode.from_any([]) # type: ignore
408def test_ErrorMode_str_repr():
409 """Test __str__ and __repr__ methods."""
410 assert str(ErrorMode.EXCEPT) == "ErrorMode.Except"
411 assert str(ErrorMode.WARN) == "ErrorMode.Warn"
412 assert str(ErrorMode.LOG) == "ErrorMode.Log"
413 assert str(ErrorMode.IGNORE) == "ErrorMode.Ignore"
415 assert repr(ErrorMode.EXCEPT) == "ErrorMode.Except"
416 assert repr(ErrorMode.WARN) == "ErrorMode.Warn"
417 assert repr(ErrorMode.LOG) == "ErrorMode.Log"
418 assert repr(ErrorMode.IGNORE) == "ErrorMode.Ignore"
421def test_ErrorMode_process_unknown_mode():
422 """Test that an unknown error mode raises ValueError."""
423 # This is a edge case that shouldn't normally happen, but testing defensively
424 # We can't easily create an invalid ErrorMode, so we test the else branch
425 # by mocking or checking that all modes are handled
426 # All enum values should be handled in process, so this is more of a sanity check
427 pass
430def test_warn_with_except_from_builtin():
431 """Test WARN mode with except_from using built-in warnings.warn."""
432 import muutils.errormode as errormode
434 # Make sure we're using the default warn function
435 errormode.GLOBAL_WARN_FUNC = warnings.warn # type: ignore
437 with warnings.catch_warnings(record=True) as w:
438 warnings.simplefilter("always")
440 base_exception = ValueError("base error")
441 ErrorMode.WARN.process(
442 "test warning", warn_cls=UserWarning, except_from=base_exception
443 )
445 assert len(w) == 1
446 # Message should include source information
447 message_str = str(w[0].message)
448 assert "test warning" in message_str
449 assert "Source of warning" in message_str
450 assert "base error" in message_str
453def test_custom_showwarning_with_warning_instance():
454 """Test custom_showwarning when passed a Warning instance instead of string."""
455 from muutils.errormode import custom_showwarning
457 with warnings.catch_warnings(record=True) as w:
458 warnings.simplefilter("always")
460 # Create a warning instance
461 warning_instance = UserWarning("instance warning")
462 custom_showwarning(warning_instance, UserWarning)
464 assert len(w) == 1
465 assert "instance warning" in str(w[0].message)
468def test_log_with_custom_func():
469 """Test LOG mode with custom log function passed directly."""
470 logs = []
472 def my_logger(msg: str):
473 logs.append(f"LOGGED: {msg}")
475 ErrorMode.LOG.process("test message", log_func=my_logger)
477 assert len(logs) == 1
478 assert logs[0] == "LOGGED: test message"
481def test_multiple_log_functions():
482 """Test that different log functions can be used."""
483 log1 = []
484 log2 = []
486 def logger1(msg: str):
487 log1.append(msg)
489 def logger2(msg: str):
490 log2.append(msg)
492 ErrorMode.LOG.process("message 1", log_func=logger1)
493 ErrorMode.LOG.process("message 2", log_func=logger2)
495 assert log1 == ["message 1"]
496 assert log2 == ["message 2"]
499def test_warn_with_source_parameter():
500 """Test that warn_func receives proper parameters."""
501 calls = []
503 def tracking_warn(msg: str, category, source=None):
504 calls.append({"msg": msg, "category": category, "source": source})
506 ErrorMode.WARN.process(
507 "test message", warn_cls=DeprecationWarning, warn_func=tracking_warn
508 )
510 assert len(calls) == 1
511 assert calls[0]["msg"] == "test message"
512 assert calls[0]["category"] == DeprecationWarning # noqa: E721
515def test_ErrorMode_enum_values():
516 """Test that ErrorMode has the expected enum values."""
517 assert ErrorMode.EXCEPT.value == "except"
518 assert ErrorMode.WARN.value == "warn"
519 assert ErrorMode.LOG.value == "log"
520 assert ErrorMode.IGNORE.value == "ignore"
523def test_from_any_without_prefix():
524 """Test from_any with allow_prefix=False."""
525 # Should still work with plain values
526 assert ErrorMode.from_any("except", allow_prefix=False) is ErrorMode.EXCEPT
528 # Should fail with prefix
529 with pytest.raises(KeyError):
530 ErrorMode.from_any("ErrorMode.except", allow_prefix=False)
533def test_GLOBAL_WARN_FUNC():
534 """Test that GLOBAL_WARN_FUNC is used when no warn_func is provided."""
535 import muutils.errormode as errormode
537 # Save original
538 original_warn_func = errormode.GLOBAL_WARN_FUNC
540 try:
541 # Set custom global warn function
542 captured = []
544 def global_warn(msg: str, category, source=None):
545 captured.append(msg)
547 errormode.GLOBAL_WARN_FUNC = global_warn # type: ignore
549 # Use WARN mode without providing warn_func
550 ErrorMode.WARN.process("test with global", warn_cls=UserWarning)
552 assert len(captured) == 1
553 assert captured[0] == "test with global"
555 finally:
556 # Restore original
557 errormode.GLOBAL_WARN_FUNC = original_warn_func
560def test_GLOBAL_LOG_FUNC():
561 """Test that GLOBAL_LOG_FUNC is used when no log_func is provided."""
562 import muutils.errormode as errormode
564 # Save original
565 original_log_func = errormode.GLOBAL_LOG_FUNC
567 try:
568 # Set custom global log function
569 captured = []
571 def global_log(msg: str):
572 captured.append(msg)
574 errormode.GLOBAL_LOG_FUNC = global_log
576 # Use LOG mode without providing log_func
577 ErrorMode.LOG.process("test with global log")
579 assert len(captured) == 1
580 assert captured[0] == "test with global log"
582 finally:
583 # Restore original
584 errormode.GLOBAL_LOG_FUNC = original_log_func
587def test_custom_warn_func_signature():
588 """Test that custom warn_func follows the WarningFunc protocol."""
589 from muutils.errormode import WarningFunc
591 # Create a function that matches the protocol
592 def my_warn(msg: str, category: type[Warning], source=None) -> None:
593 pass
595 # This should work without errors
596 warn_func: WarningFunc = my_warn # type: ignore
598 # Use it with ErrorMode
599 ErrorMode.WARN.process("test", warn_cls=UserWarning, warn_func=warn_func)
602def test_ErrorMode_all_enum_members():
603 """Test that all ErrorMode enum members are accessible."""
604 # Verify all enum members exist
605 assert hasattr(ErrorMode, "EXCEPT")
606 assert hasattr(ErrorMode, "WARN")
607 assert hasattr(ErrorMode, "LOG")
608 assert hasattr(ErrorMode, "IGNORE")
610 # Test that they are unique
611 modes = [ErrorMode.EXCEPT, ErrorMode.WARN, ErrorMode.LOG, ErrorMode.IGNORE]
612 assert len(set(modes)) == 4
615def test_custom_showwarning_frame_extraction():
616 """Test that custom_showwarning correctly extracts frame information."""
617 import sys
618 from muutils.errormode import custom_showwarning
620 with warnings.catch_warnings(record=True) as w:
621 warnings.simplefilter("always")
623 # Call from this specific line so we can verify frame info
624 line_number = 0
626 def call_showwarning():
627 nonlocal line_number
628 line_number = sys._getframe().f_lineno + 1
629 custom_showwarning("frame test", UserWarning)
631 call_showwarning()
633 assert len(w) == 1
634 # The warning should have been issued with correct file and line info
635 assert w[0].filename == __file__
636 # Line number should be close to where we called it
637 assert isinstance(w[0].lineno, int)
640def test_exception_traceback_attached():
641 """Test that raised exceptions have traceback attached."""
642 try:
643 ErrorMode.EXCEPT.process("test traceback", except_cls=ValueError)
644 except ValueError as e:
645 # Check that exception has traceback
646 assert e.__traceback__ is not None
647 else:
648 # TYPING: ty bug on python <= 3.9
649 pytest.fail("Expected ValueError to be raised") # ty: ignore[arg-type,invalid-argument-type]
652def test_exception_traceback_with_chaining():
653 """Test that chained exceptions have correct traceback."""
654 base = RuntimeError("base")
656 try:
657 ErrorMode.EXCEPT.process("chained", except_cls=ValueError, except_from=base)
658 except ValueError as e:
659 # Check traceback exists
660 assert e.__traceback__ is not None
661 # Check cause is set
662 assert e.__cause__ is base
663 else:
664 # TYPING: ty bug on python <= 3.9
665 pytest.fail("Expected ValueError to be raised") # ty: ignore[arg-type,invalid-argument-type]
668def test_warn_with_default_warn_func():
669 """Test WARN mode with default warnings.warn function."""
670 import muutils.errormode as errormode
672 # Ensure we're using default
673 errormode.GLOBAL_WARN_FUNC = warnings.warn # type: ignore
675 with warnings.catch_warnings(record=True) as w:
676 warnings.simplefilter("always")
678 ErrorMode.WARN.process("default warn func test", warn_cls=UserWarning)
680 assert len(w) == 1
681 assert "default warn func test" in str(w[0].message)
684def test_from_any_strip_whitespace():
685 """Test that from_any strips whitespace correctly."""
686 # Leading/trailing spaces
687 assert ErrorMode.from_any(" except") is ErrorMode.EXCEPT
688 assert ErrorMode.from_any("warn ") is ErrorMode.WARN
689 assert ErrorMode.from_any(" log ") is ErrorMode.LOG
691 # Tabs and newlines
692 assert ErrorMode.from_any("\texcept\t") is ErrorMode.EXCEPT
693 assert ErrorMode.from_any("\nwarn\n") is ErrorMode.WARN
696def test_load_with_prefix():
697 """Test load method with ErrorMode. prefix."""
698 # load uses allow_prefix=True
699 loaded = ErrorMode.load("ErrorMode.Except")
700 assert loaded is ErrorMode.EXCEPT
702 loaded = ErrorMode.load("ErrorMode.warn")
703 assert loaded is ErrorMode.WARN
706def test_load_without_aliases():
707 """Test that load does not accept aliases."""
708 # load uses allow_aliases=False
709 with pytest.raises((KeyError, ValueError)):
710 ErrorMode.load("error") # alias should not work
712 with pytest.raises((KeyError, ValueError)):
713 ErrorMode.load("e") # alias should not work
716def test_ERROR_MODE_ALIASES_completeness():
717 """Test that ERROR_MODE_ALIASES contains all expected aliases."""
718 from muutils.errormode import ERROR_MODE_ALIASES
720 # Count aliases per mode
721 except_aliases = [k for k, v in ERROR_MODE_ALIASES.items() if v is ErrorMode.EXCEPT]
722 warn_aliases = [k for k, v in ERROR_MODE_ALIASES.items() if v is ErrorMode.WARN]
723 log_aliases = [k for k, v in ERROR_MODE_ALIASES.items() if v is ErrorMode.LOG]
724 ignore_aliases = [k for k, v in ERROR_MODE_ALIASES.items() if v is ErrorMode.IGNORE]
726 # Verify we have multiple aliases for each mode
727 assert len(except_aliases) >= 5 # except, e, error, err, raise
728 assert len(warn_aliases) >= 3 # warn, w, warning
729 assert len(log_aliases) >= 6 # log, l, print, output, show, display
730 assert len(ignore_aliases) >= 5 # ignore, i, silent, quiet, nothing
733def test_custom_exception_classes():
734 """Test process with various custom exception classes."""
736 class CustomError(Exception):
737 pass
739 class NestedCustomError(CustomError):
740 pass
742 # Test with custom exception
743 with pytest.raises(CustomError):
744 ErrorMode.EXCEPT.process("custom", except_cls=CustomError)
746 # Test with nested custom exception
747 with pytest.raises(NestedCustomError):
748 ErrorMode.EXCEPT.process("nested custom", except_cls=NestedCustomError)
751def test_custom_warning_classes():
752 """Test process with various custom warning classes."""
754 class CustomWarning(UserWarning):
755 pass
757 class NestedCustomWarning(CustomWarning):
758 pass
760 # Test with custom warning
761 with warnings.catch_warnings(record=True) as w:
762 warnings.simplefilter("always")
764 def custom_warn(msg: str, category, source=None):
765 warnings.warn(msg, category)
767 ErrorMode.WARN.process("custom", warn_cls=CustomWarning, warn_func=custom_warn)
769 assert len(w) == 1
770 assert issubclass(w[0].category, CustomWarning)
773def test_ignore_with_all_parameters():
774 """Test that IGNORE mode ignores all parameters."""
775 # None of these should raise or warn
776 ErrorMode.IGNORE.process("ignored message")
777 ErrorMode.IGNORE.process("ignored", except_cls=ValueError)
778 ErrorMode.IGNORE.process("ignored", warn_cls=UserWarning)
779 ErrorMode.IGNORE.process("ignored", except_from=ValueError("base"))
781 # Also test with custom functions (they should not be called)
782 called = []
784 def should_not_be_called(msg: str):
785 called.append(msg)
787 ErrorMode.IGNORE.process("ignored", log_func=should_not_be_called)
789 # log_func should not have been called
790 assert len(called) == 0
793def test_from_any_case_insensitivity():
794 """Test that from_any is case insensitive."""
795 # Test various cases
796 assert ErrorMode.from_any("EXCEPT") is ErrorMode.EXCEPT
797 assert ErrorMode.from_any("Except") is ErrorMode.EXCEPT
798 assert ErrorMode.from_any("eXcEpT") is ErrorMode.EXCEPT
800 assert ErrorMode.from_any("WARN") is ErrorMode.WARN
801 assert ErrorMode.from_any("Warn") is ErrorMode.WARN
803 # Test with aliases
804 assert ErrorMode.from_any("ERROR") is ErrorMode.EXCEPT
805 assert ErrorMode.from_any("Error") is ErrorMode.EXCEPT
806 assert ErrorMode.from_any("RAISE") is ErrorMode.EXCEPT
809# def test_logging_pass():
810# errmode: ErrorMode = ErrorMode.LOG
812# log: list[str] = []
813# def log_func(msg: str):
814# log.append(msg)
816# errmode.process(
817# "test-log",
818# log_func=log_func,
819# )
821# errmode.process(
822# "test-log-2",
823# log_func=log_func,
824# )
826# assert log == ["test-log", "test-log-2"]
829# def test_logging_init():
830# errmode: ErrorMode = ErrorMode.LOG
832# log: list[str] = []
833# def log_func(msg: str):
834# log.append(msg)
836# errmode.set_log_loc(log_func)
838# errmode.process("test-log")
839# errmode.process("test-log-2")
841# assert log == ["test-log", "test-log-2"]
843# errmode_2: ErrorMode = ErrorMode.LOG
844# log_2: list[str] = []
845# def log_func_2(msg: str):
846# log_2.append(msg)
848# errmode_2.set_log_loc(log_func_2)
850# errmode_2.process("test-log-3")
851# errmode_2.process("test-log-4")
853# assert log_2 == ["test-log-3", "test-log-4"]
854# assert log == ["test-log", "test-log-2"]
857# def test_logging_init_2():
858# log: list[str] = []
859# def log_func(msg: str):
860# log.append(msg)
862# errmode: ErrorMode = ErrorMode.LOG.set_log_loc(log_func)
864# errmode.process("test-log")
865# errmode.process("test-log-2")
867# assert log == ["test-log", "test-log-2"]